精华内容
下载资源
问答
  • ie tab插件 chrome

    热门讨论 2018-02-10 11:40:24
    IE Tab 这款插件对于Chrome的爱好者来说应该都不会感到陌生了。IE Tab插件的功能就是在Chrome浏览器中嵌入IE浏览器的内核,如果用户在用使用Chrome浏览过程中遇到只能兼容IE的网站,用户不需要从新在打开IE浏览器...
  • IE Tab Multi 1.0.2.4 CRX

    2018-08-30 11:12:58
    IE Tab Multi 1.0.2.4 CRX 亲测WIN8可用 其他系统未测试。
  • 最新版ie tab for chorme 离线插件

    热门讨论 2018-03-16 11:42:35
    最新版ie tab for chorme 离线插件
  • 微信小程序tab分页demo

    2016-12-22 15:46:47
    微信小程序tab分页demo,目前使用固定数据展示,具体请结合api请求服务器数据
  • 自己整理的所有的底部导航栏Tab切换方法

    千次下载 热门讨论 2016-07-03 01:32:03
    自己整理的所有的底部导航栏Tab切换方法
  • tab页切换(项目全靠你啦)

    热门讨论 2016-01-06 23:04:39
    tab页切换
  • 微信小程序 tab 顶部选项卡下载

    千次下载 热门讨论 2016-12-01 12:33:06
    微信小程序 tab 顶部选项卡
  • html5 滑动页面切换tab

    2016-02-18 16:32:31
    html5 滑动页面切换tab,点击tab也可以切换,非常实用。
  • Android下划线能滑动的Tab标签页

    热门讨论 2016-05-14 23:05:27
    Android下划线能滑动的Tab标签页,点击对应的Tab标签或者滑动屏幕则可跳转到对应的页面,并且底部的横线也会滑动到对应的Tab标签下面
  • Android tab 栏居中滚动

    2015-06-11 22:19:43
    ANDROID淘宝电影,日期选择功能的实现,
  • 效果与Chrome 比较接近,实现了标签新增、删除、移动、自适应宽度等特性。
  • 自由切换IE/Chrome 内核
  • 三星Galaxy Tab S6与Tab S7的任何区别

    千次阅读 2020-08-31 23:46:22
    Samsung’s Galaxy Tab S7 was recently announced at Samsung’s Unpacked event which featured products like the Note20 Ultra, Galaxy Buds Live, and more products! Last year, Samsung released the Galaxy ...

    Samsung’s Galaxy Tab S7 was recently announced at Samsung’s Unpacked event which featured products like the Note20 Ultra, Galaxy Buds Live, and more products! Last year, Samsung released the Galaxy Tab S6, which was a great 2-in-1 device which consumers liked. The Tab S7 improves on the Tab S6, with adding a few new amazing features, so should you pick up the new Tab S7 or buy last years Tab S6?

    三星的Galaxy Tab S7最近在三星的Unpacked活动中宣布,其特色产品包括Note20 Ultra,Galaxy Buds Live等产品! 去年,三星发布了Galaxy Tab S6,这是一款深受消费者喜爱的二合一设备。 Tab S7在Tab S6的基础上进行了改进,增加了一些新的令人惊奇的功能,那么您应该选择新的Tab S7还是购买去年的Tab S6?

    Galaxy Tab S7新功能 (Galaxy Tab S7 New Features)

    The display is a major improvement for the Galaxy Tab S7, and this is because it finally has 120HZ, a feature that the iPad Pro has had for 3 years now. 120HZ makes the display much smoother than 60HZ, and there is noticeably less delay when you’re swiping through than on a normal 60HZ display.

    显示屏是Galaxy Tab S7的一项重大改进,这是因为它最终具有120HZ的功能,这是iPad Pro已有3年的功能了。 120HZ可使显示比60HZ平滑得多,并且滑动时的延迟明显小于普通60HZ显示器。

    There are also now 2 sizes, an 11" Tab S7 and a 12.4" Tab S7+. The larger Tab S7+ is meant for people who want to do more laptop tasks on their tablet, and could use the extra screen room to fit more things. I feel like the 11" Tab S7 is a really nice size for a tablet, but 12.4" is better while using it with a keyboard as as laptop.

    现在也有2种尺寸,一个11“ Tab S7和一个12.4” Tab S7 +。 较大的Tab S7 +适用于希望在平板电脑上执行更多笔记本电脑任务,并可以使用额外的屏幕空间容纳更多东西的人们。 我觉得11“ Tab S7对于平板电脑来说确实是一个不错的尺寸,但是12.4”更好地用于键盘和笔记本电脑时。

    The Tab S7 also now packs the SnapDragon 865+, which is a decent processor, but nothing close to the A12Z on the iPad Pro and the Quad-Core chips on the Surface Pro lineup. One advantage that you do get with the SnapDragon 865+ is 5G,which this tablet does have. This is the first tablet I have seen to have 5G in it, which is great! You will also get 45W fast charging with the Tab S7 this year, compared to the 18W one on the Tab S6.

    Tab S7现在还包装了SnapDragon 865+,这是一款不错的处理器,但与iPad Pro上的A12Z和Surface Pro系列中的四核芯片几乎没有任何区别。 SnapDragon 865+所带来的优势之一就是这款平板电脑确实具有5G。 这是我见过的首款内置5G平板电脑,真是太好了! 与Tab S6上的18W相比,今年Tab S7还将为您提供45W快速充电。

    The S Pen has been improved a bit from last years Tab S6, with a new 9ms latency. This latency has been improved a lot over the Tab S6’s latency, and it is now on par with the Apple Pencil. There is definitely a difference, but only if you play it in super slow-mo, and are looking very hard for a difference. It’s a good improvement, but you probably won’t notice it. Also, the S-Pen still sits on the back of the tablet, with a weird placement and also a raise when you put the tablet on a surface.

    与去年的Tab S6相比,S Pen有了一些改进,新的延迟时间为9ms。 与Tab S6的延迟相比,此延迟已大大改善,并且现在可以与Apple Pencil媲美。 绝对会有区别,但前提是您必须以超级慢动作播放,并且正努力寻找区别。 这是一个很好的改进,但是您可能不会注意到它。 同样,S笔仍坐在平板电脑的背面,放置位置很奇怪,并且在将平板电脑放在表面时也会抬高。

    Image for post
    Samsung Galaxy Tab S7 in Black (Credit: Samsung)
    黑色的Samsung Galaxy Tab S7(来源:三星)

    Galaxy Tab S6的优势 (Galaxy Tab S6 Advantages)

    The Samsung Galaxy Tab S6 actually has a better quality panel than the standard Tab S7, not the S7+, which is larger, more expensive, and has the best tablet display. The Tab S6 features a Super AMOLED display compared to the LCD on the Tab S7. However, the Tab S7 does comes with 120Hz, which is a reason why they even put the lower quality LCD display in there. Panel-wise, the Tab S6 is better than the S7.

    三星Galaxy Tab S6实际上具有比标准Tab S7更好的面板质量,而不是S7 +,后者更大,更昂贵并且具有最佳的平板电脑显示屏。 与Tab S7上的LCD相比,Tab S6具有Super AMOLED显示屏。 但是,Tab S7确实具有120Hz,这就是为什么他们甚至在其中放置质量较低的LCD显示器的原因。 在面板上,Tab S6比S7更好。

    The main reason why you would consider the Galaxy Tab S6 is because of its cheaper price. You can find a Tab S6 brand new for around $500, whereas the Galaxy Tab S7 will run you $650, before you have to buy the keyboard. Speaking of the keyboard, that will cost you $229 to buy on the Tab S7+. which is a lot of money. A smart financial decision would be to go for the Tab S6 and get the keyboard case with it (costs around $150), which is a package for the same price as the Tab S7 itself. If you don’t want the keyboard, the Galaxy Buds Live could also be a good choice, as they are a good companion tool to this tablet.

    您之所以会考虑Galaxy Tab S6的主要原因是因为它的价格更便宜。 您可以找到价格约为500美元的全新Tab S6,而Galaxy Tab S7的价格为650美元,而您不必购买键盘。 说到键盘,在Tab S7 +上的购买价格为229美元。 这是很多钱。 一个明智的财务决策是购买Tab S6并带上它的键盘盒(价格约为150美元),该软件包的价格与Tab S7本身的价格相同。 如果您不想要键盘,Galaxy Buds Live也可能是一个不错的选择,因为它们是这款平板电脑的不错的辅助工具。

    Image for post
    Samsung Galaxy Tab S7 with the S-Pen (Credit: Samsung)
    带有S笔的Samsung Galaxy Tab S7(来源:三星)

    替代选择 (Alternative Options)

    One alternative option to these tablets is the iPad Pro, which is known as the best 2-in-1 option by a lot of people. It has premium build quality, great accessories, amazing display, great cameras, and the list keeps going on. The iPad Pro is a great device which is a blend of both iOS and MacOS, and Apple calls it iPadOS. iPadOS is a touch-based interface but it also incorporates MacOS elements like a full desktop version of safari. The problem, again, is that you can’t run some computer software's like Final Cut Pro, VSCode, to name a few. The iPad Pro is also very pricey, as it goes over $1500 for the 12.9" iPad Pro with a Magic Keyboard and Apple Pencil 2.

    这些平板电脑的一种替代选择是iPad Pro,它被许多人称为最佳2合1选项。 它具有出色的制造质量,出色的配件,出色的显示效果,出色的相机,而且清单还在不断增加。 iPad Pro是一款出色的设备,将iOS和MacOS融为一体,Apple称之为iPadOS。 iPadOS是基于触摸的界面,但它也包含MacOS元素,例如完整的Safari浏览器桌面版。 同样,问题在于您无法运行某些计算机软件,例如Final Cut Pro,VSCode等。 iPad Pro的价格也非常昂贵,因为带有魔术键盘和Apple Pencil 2的12.9英寸iPad Pro的价格超过1500美元。

    The Surface Pro 7 is a traditional laptop, running Windows, but it converts into a tablet right after you take the keyboard off. The Tab S6/S7 and the iPad Pro are mostly tablet and a little bit of laptop, whereas the Surface Pro 7 is meant to be a laptop, and tablet next. The Surface Pro 7 is for someone who wants a little bit of a tablet experience, but mainly they want to run their applications on there as well. This laptop is built decent, design is outdated, and in general isn’t the most up-to-date 2-in-1. It costs a pretty penny as well, and it is usually in the $1000 price range after adding the keyboard and a few options. This puts it right in between the Tab S7 and iPad Pro price wise.

    Surface Pro 7是一台传统的笔记本电脑,运行Windows,但是在您卸下键盘后立即将其转换为平板电脑。 Tab S6 / S7和iPad Pro主要是平板电脑和少量笔记本电脑,而Surface Pro 7则是笔记本电脑,其次是平板电脑。 Surface Pro 7适用于需要一点平板体验的人,但主要是他们也希望在那里运行应用程序。 这款笔记本电脑的机身不错,设计已经过时,而且通常不是最新的2合1笔记本电脑。 它的价格也很便宜,添加键盘和一些选项后,价格通常在1000美元左右。 这使它恰好介于Tab S7和iPad Pro的价格之间。

    The Surface Pro X is the renovated, freshly designed, 2-in-1 introduced by Microsoft. It has thin bezels, thin design, new storage place for the Surface Pen, ARM processor, better display, and more. Think of this tablet as the Surface Pro 7, except with newer internals and externals. After adding the keyboard, this will run you around $1200. That is a lot of money to be spending on a 2-in-1, but thankfully, it can do everything your normal Windows laptop can do.

    Surface Pro X是Microsoft推出的翻新的,全新设计的2合1电脑。 它具有薄边框,薄设计,Surface Pen,ARM处理器的新存储位置,更好的显示效果等等。 将此平板电脑视为Surface Pro 7,但内部和外部的更新除外。 添加键盘后,这将使您花费1200美元左右。 花在二合一上的钱不菲,但值得庆幸的是,它可以完成普通Windows笔记本电脑可以做的所有事情。

    Image for post
    Stunning OLED Display on the Tab S7 (Credit: PhoneDog)
    Tab S7上令人惊叹的OLED显示屏(来源:PhoneDog)

    判决 (Verdict)

    The Galaxy Tab S7 is a decent step-up over the Galaxy Tab S6, in a few areas like display and performance. The Galaxy Tab S6, however, is still amazing and has pretty recent internals, a nice bezel less design, but both of them have the awkward S-Pen placement. I think that the Tab S6 is a great option for someone who doesn’t need the latest, and is better off saving $150 to get last years model. There are design and software flaws on both of these tablets, so buying one over the other won’t give you Windows or take away the back S-Pen placement.

    Galaxy Tab S7在显示和性能等几个方面比Galaxy Tab S6有了不错的提升。 然而,Galaxy Tab S6仍然令人赞叹,并且具有相当新的内部构造,没有边框的漂亮设计,但是它们两个都具有笨拙的S笔放置。 我认为Tab S6对于不需要最新版本的人来说是一个不错的选择,最好节省150美元以购买去年的型号。 这两款平板电脑均存在设计和软件缺陷,因此,购买另一款平板电脑不会给您Windows或带走S-Pen背面的位置。

    Aside from just these two tablets, I feel like there are still other great options left on the market. The iPad Pro and the Surface lineup both feel like they could get many more things done with their superior software. I think that the hardware is really nice with the 120hz screen, great build quality, decent S-Pen, but Android can’t take advantage of all of that hardware, so Samsung should make something like iPadOS or make a Windows option for people who want it. As for the Tab S6 compared to the S7, there are a few improvements, but the largest one for me is that new 120Hz display.

    除了这两种平板电脑,我觉得市场上还有其他不错的选择。 iPad Pro和Surface阵容都感觉他们可以使用其出色的软件完成更多的工作。 我认为该硬件在120hz屏幕上真的非常好,出色的构建质量,像样的S笔,但是Android无法利用所有这些硬件,因此三星应该制造iPadOS之类的东西,或者为那些愿意使用Windows的人选择想要它。 至于与S7相比的Tab S6,有一些改进,但对我来说最大的改进是新的120Hz显示屏。

    If you are looking for the best and latest in technology, the Galaxy Tab S7 is for you. If you want to save $150 and get a very similar package to the Tab S7, the S6 is definitely worth the buy. I’d highly recommend looking at other options before buying though!

    如果您正在寻找最好和最新的技术,Galaxy Tab S7将非常适合您。 如果您想节省150美元并获得与Tab S7非常相似的套装,S6绝对值得购买。 我强烈建议您在购买之前先看看其他选择!

    翻译自: https://medium.com/the-latest-technology/samsung-galaxy-tab-s6-vs-tab-s7-any-difference-7be15bee4720

    展开全文
  • MFC tab控件的美化

    热门讨论 2013-10-19 14:17:31
    vc MFC tab控件的美化,标签页美化。
  • Fragment嵌套Fragment实现多tab页面

    千次下载 热门讨论 2016-03-16 08:04:09
    原来做的一个练手的项目,里面主要是Fragment的嵌套,需要的朋友可以看下,如果遇到不能运行的状况可酌量删除些代码。
  • IE Tab Multi 的表现与原版IE是最相近的。 你可以不用再另外打开IE访问只能在IE中访问的网站并运行ActiveX控件。 这个扩展只支持Windows. 这个扩展支持Chrome 6 以及更高的版本 *在Chrome浏览器中用多标签的IE *In-...
  • win32对话框tab control控件使用demo

    热门讨论 2014-02-26 22:44:40
    vc6 如鹏网win32对话框向导工程 tab control控件使用demo
  • 三种实现Android主界面Tab的方式

    千次阅读 2019-05-13 10:15:58
    在平时的Android开发中,我们经常会使用Tab来进行主界面的布局。由于手机屏幕尺寸的限制,合理使用Tab可以极大的利用屏幕资源,给用户带来良好的体验。学会Tab的使用方法已经成为学习Android开发必不可少的技能了。...

    转载于:https://www.cnblogs.com/caobotao/p/5103673.html

    在平时的Android开发中,我们经常会使用Tab来进行主界面的布局。由于手机屏幕尺寸的限制,合理使用Tab可以极大的利用屏幕资源,给用户带来良好的体验。学会Tab的使用方法已经成为学习Android开发必不可少的技能了。我们经常使用的微信、QQ就是使用Tab的方式进行主界面的布局的。

      

    下面我们通过三种方式实现旧版的微信主界面以演示Tab的使用方式。

    最终效果:

    第一种:单纯使用ViewPager

    MainActivity.java

    public class MainActivity extends Activity implements OnClickListener {
        //声明ViewPager
        private ViewPager mViewpager;
    
        //声明四个Tab
        private LinearLayout mTabWeixin;
        private LinearLayout mTabFrd;
        private LinearLayout mTabAddress;
        private LinearLayout mTabSetting;
    
        //声明四个ImageButton
        private ImageButton mWeixinImg;
        private ImageButton mFrdImg;
        private ImageButton mAddressImg;
        private ImageButton mSettingImg;
    
        //声明ViewPager的适配器
        private PagerAdapter mAdpater;
        //用于装载四个Tab的List
        private List<View> mTabs = new ArrayList<View>();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //去掉TitleBar
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            setContentView(R.layout.activity_main);
            initViews();//初始化控件
            initDatas();//初始化数据
            initEvents();//初始化事件
    
        }
    
        private void initEvents() {
            //设置四个Tab的点击事件
            mTabWeixin.setOnClickListener(this);
            mTabFrd.setOnClickListener(this);
            mTabAddress.setOnClickListener(this);
            mTabSetting.setOnClickListener(this);
    
            //添加ViewPager的切换Tab的监听事件
            mViewpager.addOnPageChangeListener(new OnPageChangeListener() {
                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    
                }
    
                @Override
                public void onPageSelected(int position) {
                    //获取ViewPager的当前Tab
                    int currentItem = mViewpager.getCurrentItem();
                    //将所以的ImageButton设置成灰色
                    resetImgs();
                    //将当前Tab对应的ImageButton设置成绿色
                    switch (currentItem) {
                        case 0:
                            mWeixinImg.setImageResource(R.mipmap.tab_weixin_pressed);
                            break;
                        case 1:
                            mFrdImg.setImageResource(R.mipmap.tab_find_frd_pressed);
                            break;
                        case 2:
                            mAddressImg.setImageResource(R.mipmap.tab_address_pressed);
                            break;
                        case 3:
                            mSettingImg.setImageResource(R.mipmap.tab_settings_pressed);
                            break;
                    }
                }
    
                @Override
                public void onPageScrollStateChanged(int state) {
    
                }
            });
        }
    
        private void initDatas() {
            //初始化ViewPager的适配器
            mAdpater = new PagerAdapter() {
                @Override
                public int getCount() {
                    return mTabs.size();
                }
    
                @Override
                public boolean isViewFromObject(View view, Object object) {
                    return view == object;
                }
    
                @Override
                public Object instantiateItem(ViewGroup container, int position) {
                    View view = mTabs.get(position);
                    container.addView(view);
                    return view;
                }
    
                @Override
                public void destroyItem(ViewGroup container, int position, Object object) {
                    container.removeView(mTabs.get(position));
                }
            };
            //设置ViewPager的适配器
            mViewpager.setAdapter(mAdpater);
        }
    
        //初始化控件
        private void initViews() {
            mViewpager = (ViewPager) findViewById(R.id.id_viewpager);
    
            mTabWeixin = (LinearLayout) findViewById(R.id.id_tab_weixin);
            mTabFrd = (LinearLayout) findViewById(R.id.id_tab_frd);
            mTabAddress = (LinearLayout) findViewById(R.id.id_tab_address);
            mTabSetting = (LinearLayout) findViewById(R.id.id_tab_setting);
    
            mWeixinImg = (ImageButton) findViewById(R.id.id_tab_weixin_img);
            mFrdImg = (ImageButton) findViewById(R.id.id_tab_frd_img);
            mAddressImg = (ImageButton) findViewById(R.id.id_tab_address_img);
            mSettingImg = (ImageButton) findViewById(R.id.id_tab_setting_img);
    
            //获取到四个Tab
            LayoutInflater inflater = LayoutInflater.from(this);
            View tab1 = inflater.inflate(R.layout.tab1, null);
            View tab2 = inflater.inflate(R.layout.tab2, null);
            View tab3 = inflater.inflate(R.layout.tab3, null);
            View tab4 = inflater.inflate(R.layout.tab4, null);
    
            //将四个Tab添加到集合中
            mTabs.add(tab1);
            mTabs.add(tab2);
            mTabs.add(tab3);
            mTabs.add(tab4);
    
        }
    
        @Override
        public void onClick(View v) {
            //先将四个ImageButton都设置成灰色
            resetImgs();
            switch (v.getId()) {
                case R.id.id_tab_weixin:
                    //设置viewPager的当前Tab
                    mViewpager.setCurrentItem(0);
                    //将当前Tab对应的ImageButton设置成绿色
                    mWeixinImg.setImageResource(R.mipmap.tab_weixin_pressed);
                    break;
                case R.id.id_tab_frd:
                    mViewpager.setCurrentItem(1);
                    mFrdImg.setImageResource(R.mipmap.tab_find_frd_pressed);
                    break;
                case R.id.id_tab_address:
                    mViewpager.setCurrentItem(2);
                    mAddressImg.setImageResource(R.mipmap.tab_address_pressed);
                    break;
                case R.id.id_tab_setting:
                    mViewpager.setCurrentItem(3);
                    mSettingImg.setImageResource(R.mipmap.tab_settings_pressed);
                    break;
            }
        }
    
        //将四个ImageButton设置成灰色
        private void resetImgs () {
            mWeixinImg.setImageResource(R.mipmap.tab_weixin_normal);
            mFrdImg.setImageResource(R.mipmap.tab_find_frd_normal);
            mAddressImg.setImageResource(R.mipmap.tab_address_normal);
            mSettingImg.setImageResource(R.mipmap.tab_settings_normal);
        }
    }

     

    顶部布局文件

    top.xm

     

     

    四个Tab对应页面的布局文件

    tabl1.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="30sp"
            android:text="The Weixin Tab!"/>
    </LinearLayout>

     

    tab2.xml

     

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="30sp"
            android:text="The Friend Tab!"/>
    </LinearLayout>

     

    tab3.xm

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="30sp"
            android:text="The Address Tab!"/>
    </LinearLayout>

    tab4.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="30sp"
            android:text="The Setting Tab!"/>
    </LinearLayout>

    底部布局文件

    bottom.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="55dp"
                  android:gravity="center"
                  android:background="@color/material_blue_grey_800"
                  android:orientation="horizontal">
    
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+id/id_tab_weixin"
            android:gravity="center"
            android:orientation="vertical">
    
            <ImageButton
                android:id="@+id/id_tab_weixin_img"
                android:clickable="false"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/tab_weixin_pressed"
                android:background="#00000000"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#ffffff"
                android:text="微信"/>
        </LinearLayout>
        <LinearLayout
            android:id="@+id/id_tab_frd"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">
    
            <ImageButton
                android:id="@+id/id_tab_frd_img"
                android:clickable="false"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/tab_find_frd_normal"
                android:background="#00000000"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#ffffff"
                android:text="朋友"/>
        </LinearLayout>
        <LinearLayout
            android:id="@+id/id_tab_address"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">
    
            <ImageButton
                android:id="@+id/id_tab_address_img"
                android:clickable="false"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/tab_address_normal"
                android:background="#00000000"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#ffffff"
                android:text="通讯录"/>
        </LinearLayout>
        <LinearLayout
            android:id="@+id/id_tab_setting"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">
    
            <ImageButton
                android:id="@+id/id_tab_setting_img"
                android:clickable="false"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/tab_settings_normal"
                android:background="#00000000"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#ffffff"
                android:text="设置"/>
        </LinearLayout>
    
    </LinearLayout>

    主布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <include layout="@layout/top"/>
    
        <android.support.v4.view.ViewPager
            android:id="@+id/id_viewpager"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">
    
        </android.support.v4.view.ViewPager>
    
        <include layout="@layout/bottom"/>
    </LinearLayout>

     

    完整源码 : 点击下载

    单纯使用ViewPager的方式可以实现左右滑动切换页面和点击Tab切换页面的效果。但是大家发现,这种方式需要在Activity完成所有的代码实现,包括初始化Tab及其对应页面的初始化控件、数据、事件及业务逻辑的处理。这样会使得Activity看起来非常臃肿,进而造成代码的可读性和可维护性变得极差。

    谷歌在Android 3.0时推出了Fragment。可以分别使用Fragment来管理每个Tab对应的页面的布局及功能的实现。然后将Fragment与Android关联,这样Android只需要管理Fragment就行了,起到了调度器的作用,不再关心每个Fragment里的内容及功能实现是什么。这样就极大的解放了Activity,使代码变得简单、易读。

    下面我们通过使用Fragment的方式来实现Tab。

    第二种:单纯使用Fragment

    MainActivity.java

    public class MainActivity extends FragmentActivity implements OnClickListener {
        //声明四个Tab的布局文件
        private LinearLayout mTabWeixin;
        private LinearLayout mTabFrd;
        private LinearLayout mTabAddress;
        private LinearLayout mTabSetting;
    
        //声明四个Tab的ImageButton
        private ImageButton mWeixinImg;
        private ImageButton mFrdImg;
        private ImageButton mAddressImg;
        private ImageButton mSettingImg;
    
        //声明四个Tab分别对应的Fragment
        private Fragment mFragWeinxin;
        private Fragment mFragFrd;
        private Fragment mFragAddress;
        private Fragment mFragSetting;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            setContentView(R.layout.activity_main);
            initViews();//初始化控件
            initEvents();//初始化事件
            selectTab(0);//默认选中第一个Tab
        }
    
        private void initEvents() {
            //初始化四个Tab的点击事件
            mTabWeixin.setOnClickListener(this);
            mTabFrd.setOnClickListener(this);
            mTabAddress.setOnClickListener(this);
            mTabSetting.setOnClickListener(this);
        }
    
        private void initViews() {
            //初始化四个Tab的布局文件
            mTabWeixin = (LinearLayout) findViewById(R.id.id_tab_weixin);
            mTabFrd = (LinearLayout) findViewById(R.id.id_tab_frd);
            mTabAddress = (LinearLayout) findViewById(R.id.id_tab_address);
            mTabSetting = (LinearLayout) findViewById(R.id.id_tab_setting);
    
            //初始化四个ImageButton
            mWeixinImg = (ImageButton) findViewById(R.id.id_tab_weixin_img);
            mFrdImg = (ImageButton) findViewById(R.id.id_tab_frd_img);
            mAddressImg = (ImageButton) findViewById(R.id.id_tab_address_img);
            mSettingImg = (ImageButton) findViewById(R.id.id_tab_setting_img);
        }
    
        //处理Tab的点击事件
        @Override
        public void onClick(View v) {
            //先将四个ImageButton置为灰色
            resetImgs();
            switch (v.getId()) {
                case R.id.id_tab_weixin:
                    selectTab(0);//当点击的是微信的Tab就选中微信的Tab
                    break;
                case R.id.id_tab_frd:
                    selectTab(1);
                    break;
                case R.id.id_tab_address:
                    selectTab(2);
                    break;
                case R.id.id_tab_setting:
                    selectTab(3);
                    break;
            }
    
        }
    
        //进行选中Tab的处理
        private void selectTab(int i) {
            //获取FragmentManager对象
            FragmentManager manager = getSupportFragmentManager();
            //获取FragmentTransaction对象
            FragmentTransaction transaction = manager.beginTransaction();
            //先隐藏所有的Fragment
            hideFragments(transaction);
            switch (i) {
                //当选中点击的是微信的Tab时
                case 0:
                    //设置微信的ImageButton为绿色
                    mWeixinImg.setImageResource(R.mipmap.tab_weixin_pressed);
                    //如果微信对应的Fragment没有实例化,则进行实例化,并显示出来
                    if (mFragWeinxin == null) {
                        mFragWeinxin = new WeixinFragment();
                        transaction.add(R.id.id_content, mFragWeinxin);
                    } else {
                        //如果微信对应的Fragment已经实例化,则直接显示出来
                        transaction.show(mFragWeinxin);
                    }
                    break;
                case 1:
                    mFrdImg.setImageResource(R.mipmap.tab_find_frd_pressed);
                    if (mFragFrd == null) {
                        mFragFrd = new FrdFragment();
                        transaction.add(R.id.id_content, mFragFrd);
                    } else {
                        transaction.show(mFragFrd);
                    }
                    break;
                case 2:
                    mAddressImg.setImageResource(R.mipmap.tab_address_pressed);
                    if (mFragAddress == null) {
                        mFragAddress = new AddressFragment();
                        transaction.add(R.id.id_content, mFragAddress);
                    } else {
                        transaction.show(mFragAddress);
                    }
                    break;
                case 3:
                    mSettingImg.setImageResource(R.mipmap.tab_settings_pressed);
                    if (mFragSetting == null) {
                        mFragSetting = new SettingFragment();
                        transaction.add(R.id.id_content, mFragSetting);
                    } else {
                        transaction.show(mFragSetting);
                    }
                    break;
            }
            //不要忘记提交事务
            transaction.commit();
        }
    
        //将四个的Fragment隐藏
        private void hideFragments(FragmentTransaction transaction) {
            if (mFragWeinxin != null) {
                transaction.hide(mFragWeinxin);
            }
            if (mFragFrd != null) {
                transaction.hide(mFragFrd);
            }
            if (mFragAddress != null) {
                transaction.hide(mFragAddress);
            }
            if (mFragSetting != null) {
                transaction.hide(mFragSetting);
            }
        }
    
        //将四个ImageButton置为灰色
        private void resetImgs() {
            mWeixinImg.setImageResource(R.mipmap.tab_weixin_normal);
            mFrdImg.setImageResource(R.mipmap.tab_find_frd_normal);
            mAddressImg.setImageResource(R.mipmap.tab_address_normal);
            mSettingImg.setImageResource(R.mipmap.tab_settings_normal);
        }
    }

    “微信”、“朋友”、“通讯录”、“设置”所对应的Fragment

    WeixinFragment.java

    public class WeixinFragment extends Fragment{
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.tab1, container, false);
            return view;
        }
    }

     

    FrdFragmen.java

    public class FrdFragment extends Fragment{
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.tab2, container, false);
            return view;
        }
    }

    AddressFragmen.java

    public class AddressFragment extends Fragment{
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.tab3, container, false);
            return view;
        }
    }

    SettingFragment.java

    public class SettingFragment extends Fragment{
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.tab4, container, false);
            return view;
        }
    }

    顶部布局文件

    top.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:background="@android:drawable/title_bar"
                  android:gravity="center"
                  android:layout_width="match_parent"
                  android:layout_height="45dp">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="微信"
            android:textColor="#ffffff"
            android:textSize="20sp"
            android:textStyle="bold"/>
    
    </LinearLayout>

    四个Tab对应页面的布局文件

    tab1.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="30sp"
            android:text="The Weixin Tab!"/>
    </LinearLayout>

    tab2.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="30sp"
            android:text="The Friend Tab!"/>
    </LinearLayout>

     

    tab3.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="30sp"
            android:text="The Address Tab!"/>
    </LinearLayout>

     

    tab4.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="30sp"
            android:text="The Setting Tab!"/>
    </LinearLayout>

     

    底部布局文件

    bottom.xml

     

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="55dp"
                  android:gravity="center"
                  android:background="@color/material_blue_grey_800"
                  android:orientation="horizontal">
    
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+id/id_tab_weixin"
            android:gravity="center"
            android:orientation="vertical">
    
            <ImageButton
                android:id="@+id/id_tab_weixin_img"
                android:clickable="false"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/tab_weixin_pressed"
                android:background="#00000000"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#ffffff"
                android:text="微信"/>
        </LinearLayout>
        <LinearLayout
            android:id="@+id/id_tab_frd"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">
    
            <ImageButton
                android:id="@+id/id_tab_frd_img"
                android:clickable="false"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/tab_find_frd_normal"
                android:background="#00000000"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#ffffff"
                android:text="朋友"/>
        </LinearLayout>
        <LinearLayout
            android:id="@+id/id_tab_address"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">
    
            <ImageButton
                android:id="@+id/id_tab_address_img"
                android:clickable="false"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/tab_address_normal"
                android:background="#00000000"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#ffffff"
                android:text="通讯录"/>
        </LinearLayout>
        <LinearLayout
            android:id="@+id/id_tab_setting"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">
    
            <ImageButton
                android:id="@+id/id_tab_setting_img"
                android:clickable="false"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/tab_settings_normal"
                android:background="#00000000"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#ffffff"
                android:text="设置"/>
        </LinearLayout>
    
    </LinearLayout>

     

    主布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <include layout="@layout/top"/>
    
        <FrameLayout
            android:id="@+id/id_content"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">
    
        </FrameLayout>
    
        <include layout="@layout/bottom"/>
    </LinearLayout>

     

    完整源码 : 点击下载

    可以看出,使用Fragment实现了Activity与Tab对应的页面分离,特别是当Tab对应的页面的布局和逻辑比较复杂时更能体会到使用Fragment的好处。但是单纯使用Fragment只能通过点击Tab来切换页面,并不能实现左右滑动进行切换。

    下面我们通过使用 ViewPager + Fragment 的方式实现Tab,这也是开发中使用比较广泛的一种方式。

    第三种:使用 ViewPager + Fragment

    MainActivity.java

    public class MainActivity extends FragmentActivity implements OnClickListener {
        //声明ViewPager
        private ViewPager mViewPager;
        //适配器
        private FragmentPagerAdapter mAdapter;
        //装载Fragment的集合
        private List<Fragment> mFragments;
    
        //四个Tab对应的布局
        private LinearLayout mTabWeixin;
        private LinearLayout mTabFrd;
        private LinearLayout mTabAddress;
        private LinearLayout mTabSetting;
    
        //四个Tab对应的ImageButton
        private ImageButton mImgWeixin;
        private ImageButton mImgFrd;
        private ImageButton mImgAddress;
        private ImageButton mImgSetting;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            setContentView(R.layout.activity_main);
            initViews();//初始化控件
            initEvents();//初始化事件
            initDatas();//初始化数据
        }
    
        private void initDatas() {
            mFragments = new ArrayList<>();
            //将四个Fragment加入集合中
            mFragments.add(new WeixinFragment());
            mFragments.add(new FrdFragment());
            mFragments.add(new AddressFragment());
            mFragments.add(new SettingFragment());
    
            //初始化适配器
            mAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
                @Override
                public Fragment getItem(int position) {//从集合中获取对应位置的Fragment
                    return mFragments.get(position);
                }
    
                @Override
                public int getCount() {//获取集合中Fragment的总数
                    return mFragments.size();
                }
    
            };
            //不要忘记设置ViewPager的适配器
            mViewPager.setAdapter(mAdapter);
            //设置ViewPager的切换监听
            mViewPager.addOnPageChangeListener(new OnPageChangeListener() {
                @Override
                //页面滚动事件
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    
                }
    
                //页面选中事件
                @Override
                public void onPageSelected(int position) {
                    //设置position对应的集合中的Fragment
                    mViewPager.setCurrentItem(position);
                    resetImgs();
                    selectTab(position);
                }
    
                @Override
                //页面滚动状态改变事件
                public void onPageScrollStateChanged(int state) {
    
                }
            });
        }
    
        private void initEvents() {
            //设置四个Tab的点击事件
            mTabWeixin.setOnClickListener(this);
            mTabFrd.setOnClickListener(this);
            mTabAddress.setOnClickListener(this);
            mTabSetting.setOnClickListener(this);
    
        }
    
        //初始化控件
        private void initViews() {
            mViewPager = (ViewPager) findViewById(R.id.id_viewpager);
    
            mTabWeixin = (LinearLayout) findViewById(R.id.id_tab_weixin);
            mTabFrd = (LinearLayout) findViewById(R.id.id_tab_frd);
            mTabAddress = (LinearLayout) findViewById(R.id.id_tab_address);
            mTabSetting = (LinearLayout) findViewById(R.id.id_tab_setting);
    
            mImgWeixin = (ImageButton) findViewById(R.id.id_tab_weixin_img);
            mImgFrd = (ImageButton) findViewById(R.id.id_tab_frd_img);
            mImgAddress = (ImageButton) findViewById(R.id.id_tab_address_img);
            mImgSetting = (ImageButton) findViewById(R.id.id_tab_setting_img);
    
        }
    
        @Override
        public void onClick(View v) {
            //先将四个ImageButton置为灰色
            resetImgs();
    
            //根据点击的Tab切换不同的页面及设置对应的ImageButton为绿色
            switch (v.getId()) {
                case R.id.id_tab_weixin:
                    selectTab(0);
                    break;
                case R.id.id_tab_frd:
                    selectTab(1);
                    break;
                case R.id.id_tab_address:
                    selectTab(2);
                    break;
                case R.id.id_tab_setting:
                    selectTab(3);
                    break;
            }
        }
    
        private void selectTab(int i) {
            //根据点击的Tab设置对应的ImageButton为绿色
            switch (i) {
                case 0:
                    mImgWeixin.setImageResource(R.mipmap.tab_weixin_pressed);
                    break;
                case 1:
                    mImgFrd.setImageResource(R.mipmap.tab_find_frd_pressed);
                    break;
                case 2:
                    mImgAddress.setImageResource(R.mipmap.tab_address_pressed);
                    break;
                case 3:
                    mImgSetting.setImageResource(R.mipmap.tab_settings_pressed);
                    break;
            }
            //设置当前点击的Tab所对应的页面
            mViewPager.setCurrentItem(i);
        }
    
        //将四个ImageButton设置为灰色
        private void resetImgs() {
            mImgWeixin.setImageResource(R.mipmap.tab_weixin_normal);
            mImgFrd.setImageResource(R.mipmap.tab_find_frd_normal);
            mImgAddress.setImageResource(R.mipmap.tab_address_normal);
            mImgSetting.setImageResource(R.mipmap.tab_settings_normal);
        }
    }

     

    “微信”、“朋友”、“通讯录”、“设置”所对应的Fragment

    WeixinFragment.java

    public class WeixinFragment extends Fragment {
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.tab1, container, false);
            return view;
        }
    }

     

    FrdFragment.java

    public class FrdFragment extends Fragment {
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.tab2, container, false);
            return view;
        }
    }

    AddressFragment.java

    public class AddressFragment extends Fragment {
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.tab3, container, false);
            return view;
        }
    }

    SettingFragment.java

    public class SettingFragment extends Fragment {
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.tab4, container, false);
            return view;
        }
    }

    顶部布局文件

    top.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:background="@android:drawable/title_bar"
                  android:gravity="center"
                  android:layout_width="match_parent"
                  android:layout_height="45dp">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="微信"
            android:textColor="#ffffff"
            android:textSize="20sp"
            android:textStyle="bold"/>
    
    </LinearLayout>

    四个Tab对应页面的布局文件

    tab1.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="30sp"
            android:text="The Weixin Tab!"/>
    </LinearLayout>

    tab2.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="30sp"
            android:text="The Friend Tab!"/>
    </LinearLayout>

    tab3.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="30sp"
            android:text="The Address Tab!"/>
    </LinearLayout>

    tab4.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="30sp"
            android:text="The Setting Tab!"/>
    </LinearLayout>

    底部布局文件

    bottom.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="55dp"
                  android:gravity="center"
                  android:background="@color/material_blue_grey_800"
                  android:orientation="horizontal">
    
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+id/id_tab_weixin"
            android:gravity="center"
            android:orientation="vertical">
    
            <ImageButton
                android:id="@+id/id_tab_weixin_img"
                android:clickable="false"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/tab_weixin_pressed"
                android:background="#00000000"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#ffffff"
                android:text="微信"/>
        </LinearLayout>
        <LinearLayout
            android:id="@+id/id_tab_frd"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">
    
            <ImageButton
                android:id="@+id/id_tab_frd_img"
                android:clickable="false"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/tab_find_frd_normal"
                android:background="#00000000"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#ffffff"
                android:text="朋友"/>
        </LinearLayout>
        <LinearLayout
            android:id="@+id/id_tab_address"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">
    
            <ImageButton
                android:id="@+id/id_tab_address_img"
                android:clickable="false"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/tab_address_normal"
                android:background="#00000000"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#ffffff"
                android:text="通讯录"/>
        </LinearLayout>
        <LinearLayout
            android:id="@+id/id_tab_setting"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">
    
            <ImageButton
                android:id="@+id/id_tab_setting_img"
                android:clickable="false"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/tab_settings_normal"
                android:background="#00000000"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#ffffff"
                android:text="设置"/>
        </LinearLayout>
    
    </LinearLayout>

    主布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <include layout="@layout/top"/>
    
        <android.support.v4.view.ViewPager
            android:id="@+id/id_viewpager"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">
    
        </android.support.v4.view.ViewPager>
    
        <include layout="@layout/bottom"/>
    </LinearLayout>

    完整源码 :点击下载

    使用 ViewPager + Fragment 的方式综合了使用ViewPager和使用Fragment的优势,即:既能使用Fragment管理Tab对应页面的布局及业务逻辑的实现,使得Activity与Tab对应的页面分离,又能使用ViewPager实现左右滑动切换页面的效果。这种方式需要为ViewPager设置FragmentPagerAdapter适配器,关于适配器的知识可参考我之前写的一篇文章:Android必学之数据适配器BaseAdapter

     

    作者:caobotao

    出处: http://www.cnblogs.com/caobotao/p/5103673.html

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。

    展开全文
  • JS实现Tab内容切换,页面不刷新,内容切换,网址对应变化,效果非常好。
  • Bootstrap Ace模板 tab页效果

    热门讨论 2017-03-22 20:08:06
    Bootstrap Ace模板 整合tab页效果,可以多tab展示内容。压缩文件里两个目录:ace和ace-tab。ace是模板的自带示例,ace-tab是个人修改整合了tab页的示例。
  • vue实现Tab切换功能

    千次阅读 2019-11-01 15:56:02
    在项目开发中,我们经常会碰到Tab切换的功能,而在Vue中想实现这样的功能也应该有很多种,常用的三种应该是 Tab路由切换、Tab动态组件切换、通过v-show设置Tab显示隐藏。每种方法实现起来其实都不难,看看官网介绍或...

    在项目开发中,我们经常会碰到Tab切换的功能,而在Vue中想实现这样的功能也应该有很多种,常用的三种应该是 Tab路由切换Tab动态组件切换通过v-show设置Tab显示隐藏。每种方法实现起来其实都不难,看看官网介绍或看几篇博客应该就能实现。

    但这里面其实还有很多细节需要我们去做,如

    1. Tab切换时,切换过的Tab组件状态怎样缓存
    2. 在项目中经常会有 页面A -> 页面B -> 页面C 则从 页面C 返回 页面B 时 页面B 使用缓存数据,而页面A 跳到 页面B 时,则页面B每次都请求最新数据。比如我们在某APP内点击 最新新闻(页面A) 选项 跳转到 新闻列表(页面B) 选择某一条新闻 跳转到 新闻详情(页面C) 页面,我们希望,从新闻详情返回到新闻列表时,直接用刚才请求的数据,而不每次都重新发送请求,而从 最新新闻 跳转到 新闻列表时,则都请求最新的数据
    3. 父组件如何给子组件传递参数
    4. 页面内Tab来回切换后,如何直接返回到上一级页面
    5. 页面循环切换时,前进或后退如何保证页面结构正确(具体下面会讲到)

    Tab路由切换带缓存

    想要通过路由进行切换,就需要使用嵌套路由,即整个大页面是一个路由,点击不同Tab时,再通过嵌套路由来切换不同的路由。
    想要Tab切换时保存当前状态可以使用keep-alive包裹,keep-alive具体使用参考这篇文章-vue中动态添加和删除组件缓存 keep-alive
    包裹Tab的组件页面我们也要动态的缓存,这里也需要用到keep-alive,只是这个keep-alive需要添加到App.vue内,各个组件的动态缓存我们使用的是keep-alive的include属性。缓存最大数使用max属性

    router-link介绍

    • 通过to属性链接目标路由,当被点击时,内部会立刻把 to 的值传到 router.push(),既然是通过router.push()的方式跳转,那么就会往history记录中添加,这样当返回时,可能就会先从Tab3返回到Tab2再返回到Tab1再返回,这种体验很不好,怎样一步返回呢,就是在router-link中添加replace属性,这样当点击时,会调用 router.replace() 而不是 router.push(),于是导航后不会留下 history 记录,这样就可一步返回了,如:<router-link :to="{ path: '/abc'}" replace></router-link>
    • 通过 命名的路由 传递参数,如:<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
    • 通过 带查询参数 传递参数,如:<router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link>,结果路由为:/register?plan=private
    • router-link设置点击事件时需要添加 natvie, 如@click.native="TabClick()"

    思路

    1. 通过router配置嵌套路由
    2. 通过使用keep-alive的include属性有条件的缓存组件
    3. 通过store响应式的修改include属性对应的值
    4. 通过组件内导航钩子beforeRouteEnter、beforeRouteLeave给store提交mutations修改

    实例演示

    1:page1->news->page2 然后再依次返回在这里插入图片描述

    通过演示我们发现

    1. 从page2返回到news时,总是能返回到我们之前保存的状态
    2. 从news返回到page1后,再从page1跳转news,不管news之前是什么状态,都会初始化显示购物的页面

    2:page1(1)->news(2)->page2(3)->page1(4)->news(5)->page2(6) 然后再依次返回
    在这里插入图片描述
    这个视频里有几个问题需要我们去思考

    1. 第四步跳转到第五步,为什么Tab选中为购物、内容选中为鞋包,为什么news组件及内部路由组件都缓存着
    2. 第三步返回到第二步,为什么Tab选中为购物、内容选中为母婴,但从右边缓存的组件看,为什么shopping组件也被缓存了

    这两个问题我们后边会具体介绍

    部分代码示例

    1:在router中配置各个路由

    这里需要注意,配置children子路由时path不能加 / ,在router-link的to后面写的路由需要以 / 开头,以 / 开头的嵌套路径会被当作根路径

    export default new Router({
      routes: [
          {
              path: '/page1',
              name: 'page1',
              component: () => import(/* webpackChunkName: "test" */ './views/news/page1.vue')
          }, {
              path: '/page2',
              name: 'page2',
              component: () => import(/* webpackChunkName: "test" */ './views/news/page2.vue')
          }, {
              path: '/news',
              name: 'newsIndex',
              component: () => import(/* webpackChunkName: "test" */ './views/news/news.vue'),
              children: [{
                  path: 'sports',
                  name: 'sports',
                  component: () => import(/* webpackChunkName: "test" */ './views/news/sports.vue'),
              }, {
                  path: 'shopping',
                  name: 'shopping',
                  component: () => import(/* webpackChunkName: "test" */ './views/news/shopping.vue'),
              }, {
                  path: 'learn',
                  name: 'learn',
                  component: () => import(/* webpackChunkName: "test" */ './views/news/learn.vue'),
              }]
          }
      ]
    })
    
    2:在App.vue组件通过computed计算属性响应式的获取store里的keepAliveArr计算属性,并赋值给keep-alive的include属性,并设置最多可缓存5个组件
      <template>
          <div id="app">
              <keep-alive :include="keepAliveArr" :max="5">
                  <router-view></router-view>
              </keep-alive>
          </div>
      </template>
      <script>
          export default {
              computed: {
                  keepAliveArr() {
                      return this.$store.getters.keepAliveArr
                  }
              }
          }
    
    3:在store的mutations中提供状态更改的方法,并通过store的计算属性供外部访问
      import Vue from 'vue'
      import Vuex from 'vuex'
      Vue.use(Vuex);
    
      export default new Vuex.Store({
          state: {
              //缓存组件数组
              keepAliveArr: []
          },
          mutations: {
              UPDATE_KEEP_ALIVE(state, payload) {
                  //当payload.type不为空则代表清除指定缓存组件,否则添加指定组件
                  if (payload.type) {
                      let index = state.keepAliveArr.indexOf(payload.keepAlive);
                      if (index !== -1) {
                          state.keepAliveArr.splice(index, 1); //删除数组的缓存的组件
                      }
                  } else {
                      let index = state.keepAliveArr.indexOf(payload.keepAlive);
                      if (index === -1) {
                          state.keepAliveArr.push(payload.keepAlive); //添加需要缓存的组件
                      }
                  }
              }
          },
          getters: {
              keepAliveArr: state => state.keepAliveArr
          }
      })
    
    4:在组件内通过导航钩子beforeRouteEnter、beforeRouteLeave给store提交mutations修改缓存组件keepAliveArr的值

    这里 page1为news的上一个页面,page2为下一个页面,通过beforeRouteEnter钩子,不管从哪个页面进入都提交mutations,缓存当前news页面,当离开时判断,如果是返回上一个页面则删除当前news页面缓存,当删除news页缓存时,内部通过keep-alive保存的 购物、体育、学习三个组件缓存的状态也会一并删除,即内部的在激活的和被停用的组件都会执行销毁的生命周期

    注意:如果我们的页面比较简单,最深跳转到page2,即: page1->news->page2,然后再一级一级返回的话,那么beforeRouteEnter这两个if判断可以不写

    4.1:beforeRouteEnter中两个if判断解释

    4.1.1:第一个if判断

    当循环跳转时,即

     page1(1)->news(2)->page2(3)->page1(4)->news(5)
    

    因为page1跳转的路径 永远是 /news/shopping,news组件又通过keep-alive保存当前状态,所以在第二步news内如果 点击了 Tab体育 或者 Tab学习时,此时currentTab不为0,但当通过 第三步->第四步->第五步 再次跳转到news 时,由于page1路径 永远是 /news/shopping,而news状态还保存在内存里不会重新 创建,此时Tab指示器显示的和下面具体内容就会不一致,所以这里判断如果是这种情况就 强制切换 到 /news/shopping 页

    4.1.2: 第二个if判断

    当循环跳转时又依次返回时。即

     page1(1)->news(2 Tab选择 学习)->page2(3)->page1(4)->news(5 Tab选择 体育)->page2(6)
    

    现在开始返回, 返回到第五步Tab体育时,是没有问题的,因为 news状态是缓存 的, 而第五步Tab体育页返回第四步page1时,这里 beforeRouteLeave中我们已经把news页设置不缓存 了,再继续返回到第三步page2,再返到第二步Tab学习页时,因为 最初 我们是从第二步Tab学习的路由往 下一页page2页跳的,所以这里返回也是返回到Tab学习的路由页即 /news/learn ,但因为整个news已经不缓存了,所以这里返回从第三步返回到第二步时,其实 news所有的生命周期都会执行 ,此时 currentTab的值为0,如果不通过这个判断,那么Tab指示器显示的和下面具体内容也会不一致,所以这种情况我也 强制让切换 到 /news/shopping 页

    4.2:组件内导航守卫的to.path 和 to.fullPath 区别?
    • to.path: 是我们在router路由里定义的路由,如/news/shopping
    • to.fullPath: 是包括我们跳转路由时传递的参数,如/news/shopping?content=购物
       beforeRouteEnter(to, from, next) {
           next(vm => { //添加组件缓存
               vm.$store.commit("UPDATE_KEEP_ALIVE", {
                   keepAlive: 'news'
               });
               let path = '';
               //当循环跳转时,替换路由为shopping页
               if (vm.currentTab !== 0 && from.path === '/page1') {
                   vm.currentTab = 0;
                   path = '/news/shopping';
                   vm.$router.replace({
                       path,
                       query: {
                           content: '购物'
                       }
                   });
               }
               //当循环跳转后,循环返回时,替换路由为shopping页
               if (vm.currentTab === 0 && to.path !== '/news/shopping') {
                   vm.currentTab = 0;
                   path = '/news/shopping';
                   vm.$router.replace({
                       path,
                       query: {
                           content: '购物'
                       }
                   });
               }
           })
       },
    
       beforeRouteLeave(to, from, next) {
           if (to.path === '/page1') {//删除缓存
               this.$store.commit("UPDATE_KEEP_ALIVE", {
                   type: 1,
                   keepAlive: 'goods'
               })
           }
           next()
       },
    

    总结

    通过上面四步就可以实现Tab路由切换并带组件状态缓存,这个keep-alive嵌套keep-alive需要注意的事项,大家可以参考这篇文章-vue中动态添加和删除组件缓存 keep-alive

    Tab动态组件切换

    大家可以参考这篇文章-vue中动态添加和删除组件缓存 keep-alive

    通过v-show设置Tab显示隐藏

    这个就不写了,大家只要慢慢写应该都能实现,只是用这种方式实现不太优雅。

    Tab路由切换的完成代码

    news代码

      <template>
          <div class="list-container">
              <div class="btn" @click="btnJumpClick">跳转到page2详情页</div>
              <nav class="tab-root">
                  <!--通过query向子路由传递参数-->
                  <router-link v-for="(item,index) in routerList"
                               :key="index"
                               class="tab-button"
                               :to="{path:item.url,query:{content:item.content}}"
                               replace
                               :class="{ active: currentTab === index }"
                               @click.native="currentTab = index">{{item.tab}}
                  </router-link>
              </nav>
              <keep-alive :include="cached" :max="3">
                  <router-view class="view"></router-view>
              </keep-alive>
          </div>
      </template>
    
      <script>
          export default {
              name: "news",
              data() {
                  return {
                      currentTab: 0,
                      cached: 'shopping,sports,learn',
                      about: '/news/shopping',
                      routerList: [{
                          tab: '购物',
                          url: '/news/shopping',
                          content: '购物'
                      }, {
                          tab: '运动',
                          url: '/news/sports',
                          content: '运动'
                      }, {
                          tab: '学习',
                          url: '/news/learn',
                          content: '学习'
                      }]
                  }
              },
              activated() {
                  console.log("--news--activated--");
              },
              deactivated() {
                  console.log("--news--deactivated--");
              },
              beforeRouteEnter(to, from, next) {
                  next(vm => { //添加组件缓存
                      vm.$store.commit("UPDATE_KEEP_ALIVE", {
                          keepAlive: 'news'
                      });
                      let path = '';
                      //当循环跳转时,替换路由为shopping页
                      if (vm.currentTab !== 0 && from.path === '/page1') {
                          vm.currentTab = 0;
                          path = '/news/shopping';
                          vm.$router.replace({
                              path,
                              query: {
                                  content: '购物'
                              }
                          });
                      }
                      //当循环跳转后,循环返回时,替换路由为shopping页
                      if (vm.currentTab === 0 && to.path !== '/news/shopping') {
                          vm.currentTab = 0;
                          path = '/news/shopping';
                          vm.$router.replace({
                              path,
                              query: {
                                  content: '购物'
                              }
                          });
                      }
                  })
              },
    
              beforeRouteLeave(to, from, next) {
                  if (to.path === '/page1') {//删除缓存
                      this.$store.commit("UPDATE_KEEP_ALIVE", {
                          type: 1,
                          keepAlive: 'news'
                      })
                  }
                  next()
              },
              methods: {
                  btnJumpClick() {
                      this.$router.push({
                          path: '/page2'
                      })
                  },
              }
          }
      </script>
    
      <style scoped lang="scss">
          .list-container {
            .btn {
                width: 100%;
                height: 40px;
                background: #f00;
                font-size: 20px;
                color: white;
            }
            .tab-root {
                display: flex;
                border-bottom: 1px solid #eee;
            }
            .tab-button {
                background: #fff;
                line-height: 40px;
                height: 40px;
                text-align: center;
                flex: 1;
                font-size: 15px;
                font-weight: normal;
            }
            .tab-button.active {
                font-size: 17px;
                font-weight: 500;
                border-bottom: 2px solid #f00;
            }
          }
    
    

    shopping代码

      <template>
          <div class="recommends-tab">
              <ul class="recommends-sidebar">
                  <li v-for="recommend in recommends"
                      :key="recommend.id"
                      :class="{ selected: recommend === selectedRecommend }"
                      @click="selectedRecommend = recommend">
                      {{ recommend.title }}
                  </li>
              </ul>
              <div class="selected-recommend-container">
                  <div class="selected-recommend">
                      <div v-html="selectedRecommend.content"></div>
                  </div>
              </div>
          </div>
      </template>
    
      <script>
          export default {
              name: "shopping",
              props:{
                  componentTabName:String
              },
              data() {
                  return {
                      recommends: [
                          {
                              id: 1,
                              title: '母婴',
                              content: '<p>儿童玩具、尿裤湿巾、奶粉辅食</p>'
                          },
                          {
                              id: 2,
                              title: '鞋包',
                              content: '<p>功能箱包、人气热卖、服饰配件</p>'
                          },
                          {
                              id: 3,
                              title: '水果',
                              content: '<p>瓜果桃李、海鲜水产、熟食凉菜</p>'
                          }
                      ],
                      selectedRecommend: {}
                  }
              },
              beforeMount() {
                  //获取通过路由传递过来的参数
                  console.log(this.$route.query.content);
              },
          }
      </script>
    
      <style scoped lang="scss">
          .recommends-tab {
              display: flex;
          }
          .recommends-sidebar {
              width: 20%;
              text-align: center;
              background: #eee;
              height: 100vh;
          }
          .recommends-sidebar li {
              height: 30px;
              line-height: 30px;
          }
          .recommends-sidebar li.selected {
              background: #fff;
              color: red;
          }
          .selected-recommend-container {
              padding-left: 10px;
          }
      </style>
    

    page1跳转代码

      btnLuYouClick() {
          this.$router.push({
              path: '/news/shopping',
              query: {
                  content: '购物'
              }
          });
      }
    

    page2跳转代码

      btnJumpClick() {
          this.$router.push({
              path: '/page1'
          })
      },
    

    sports和learn代码比较简单就不粘贴了

    上面的代码应该已经够用,如果需要全部详细代码的就留言吧,我再单独发你。

    展开全文
  • Vue 组件封装之 Tab 切换

    千次阅读 多人点赞 2019-07-25 17:22:27
    实战 Vue 第5天:封装一个tab切换组件前言使用现存组件面临的问题封装 tab 组件的思路封装 tab 组件代码总结 前言 以上 tab 切换功能在前端开发中司空见惯。各种现存的组件也随手拈来。在 Vue 中,有配套的 element-...

    一、Tab 切换组件

    组件说明:
    实现 tab 切换。

    效果展示:
    实现 tab 切换,改变激活样式,切换到对应的页面
    在这里插入图片描述
    以上 tab 切换功能在前端开发中司空见惯。各种现存的组件也随手拈来。在 Vue 中,有配套的 element-ui 组件,也有 vue-ant-design。

    element-ui 中 el-tabs 效果如下:
    在这里插入图片描述
    vue-ant-design 中 a-tabs 效果如下:
    在这里插入图片描述
    但是使用现存组件面临的问题如下

    1. element-ui 中 el-tabs 使用问题
      tab 标签文本没有居中,整体靠左,通过复写样式也不行,因为下面的高亮下划线是通过 JS 动态控制。
    2. vue-ant-design 中 a-tabs 使用问题
      tab 标签文本间的距离太大。
    • 基于以上问题,所以打算自己封装一个 tab 组件。

    二、使用案例

    该组件有两种使用方式。

    1. 只有一个主页面,切换时更新数据源即可,用法如下。
    <template>
        <el-tab defaultKey="1" @on-click="changeTab">
          <el-tab-panes actKey="1" label="全部"></el-tab-panes>
          <el-tab-panes actKey="2" label="推荐"></el-tab-panes>
          <el-tab-panes actKey="3" label="最新"></el-tab-panes>
        </el-tab>
        <div>只有我一个页面,更新数据源即可</div>
    </template>
    <script>
     export default{
        data(){
          return{
           
           }
        },
        methods:{
           changeTab(item,index){
                 //调数据
            }
        }
    }
    </script>
    
    1. 有几个tab,就有几个主页面,切换时切换到不同的页面,用法如下。
    <template>
        <el-tab defaultKey="1" @on-click="changeTab">
          <el-tab-panes actKey="1" label="全部">
            <div>页面1</div>
          </el-tab-panes>
          <el-tab-panes actKey="2" label="推荐">
            <div>页面2</div>
          </el-tab-panes>
          <el-tab-panes actKey="3" label="最新">
            <div>页面3</div>
          </el-tab-panes>
        </el-tab>
    </template>
    <script>
     export default{
        data(){
          return{
           
           }
        },
        methods:{
           changeTab(item,index){
                 //调数据
            }
        }
    }
    </script>
    

    三、API 使用指南

    属性说明类型默认值
    defaultKey默认选中的tabString1
    actKey每个tab的唯一keyString
    label每个tab的标题String
    on-clicktab 被选中时触发点击按钮的回调函数(e: Object): void

    四、源代码

    实现思路
    (1) tab 标签文本样式高亮;
    (2) tab 下面的下划线调整到相应位置;
    (3) tab 对应的内容切换。
    在这里插入图片描述

    1. tabs 组件
    <template>
      <div>
        <div class="tabs">
          <div  ref="line" class="tab-line"></div>
          <div :class="[activeKey == item.actKey? 'active-tab' : 'tab']"  @click="changeTab($event,item,index)" v-for="(item,index) in childList">{{item.label}}</div>
        </div>
        <slot></slot>
      </div>
    </template>
    <script>
      let self;
      export default {
        name: "ElTab",
        data(){
          return {
            childList:[],
            activeKey:this.defaultKey,//将初始化tab赋值给activeKey
            slideWidth:0
          }
        },
       //获取子组件传过来的激活tab
        props:{
          defaultKey:{
            type: String,
            default: "1"
          }
        },
        created(){
          self = this;
        },
        mounted(){
          //循环tab标签
          this.childList = this.$children;
          //设置滑动距离。平分设备宽度
          this.slideWidth = window.innerWidth/this.childList.length;
          //设置状态线初始化滑动位置
          this.$refs.line.style.width = this.slideWidth+"px";
        },
        methods:{
          //切换tab触发事件
          changeTab:(event,item,index)=>{
            self.activeKey = item.actKey;
            self.$refs.line.style.transform = "translateX("+self.slideWidth*index+"px)";
            self.$refs.line.style.transition = "transform .3s";
            self.$emit('on-click',item,index);//将切换tab的事件暴露给父组件
          },
          //初始化时tab状态设置与相应内容显示
          updateNav:()=>{
            self.$children.map((item,index)=>{
              if(item.actKey == self.activeKey){
                item.show = true;
                self.$nextTick(function() {
                  self.$refs.line.style.transform = "translateX("+self.slideWidth*index+"px)";
                  self.$refs.line.style.transition = "transform 0s";
                });
              }else {
                item.show = false;
              }
            })
          }
        },
        watch: {
          //监听当前tab,显示相应内容
          activeKey() {
            self.$children.map((item)=>{
              if(item.actKey == self.activeKey){
                item.show = true;
              }else {
                item.show = false;
              }
            })
          }
        }
      }
    </script>
    <style>
      .active-tab{
        color:#158ef3;
        height: 50px;
        font-weight: bold;
        line-height: 50px;
        font-size: 16px;
      }
      .tab{
        color:#333;
        height: 50px;
        line-height: 50px;
        font-size: 16px;
      }
      .tabs{
        display: flex;
        justify-content: space-around;
        align-items: center;
        height: 50px;
        border-bottom: 1px solid #f6f6f6;
      }
      .tab-line{
        position: absolute;
        left: 0;
        border-bottom: 2px solid #158ef3;
        height: 50px;
      }
    </style>
    
    1. tab-pane 组件
    <template>
      <div v-if="show">
        <slot></slot>
      </div>
    </template>
    <script>
      export default {
        name: "ElTabPanes",
        data(){
          return {
            show: false //初始时将所有内容隐藏
          }
        },
        props:{
          actKey:{
            type: String,
            default: "1"
          }, label:{
            type: String,
            default: ""
          },
        },
        mounted(){
          this.$parent.updateNav();
        },
      }
    </script>
    

    五、总结

    最后总结一下封装一个 tabs 的核心思路和方法。

    1. 设置初始化 tab 标签

    在 tab-pane 子组件中将所有的内容隐藏(show 属性设置为 false),在 tabs 父组件内接收由开发者自定义的 activeKey,定义一个方法,将 activeKey与子组件的 actKey 比较,如果相同,则该 tab 为初始化时激活的 tab 标签,将相应 tab 的 show 属性设置为 true,并修改 tab 样式。该方法在 tab-pane 子组件中调用。

    将 tab-pane 子组件中所有的开发者添加的内容隐藏:
    在这里插入图片描述
    tabs 父组件提供的方法:
    在这里插入图片描述

    tab-pane 子组件调用:
    在这里插入图片描述

    1. tabs 组件内部循环 tab-pane 子组件的标签。接收 activeKey,点击时将 tab-pane 子组件的 actKey 赋值给 activeKey。然后每个 tab 的样式通过当前 activeKey 与 actKey 比较,判断是否是当前 tab 标签。如果是,则样式设置为激活样式。
    <div :class="[activeKey == item.actKey? 'active-tab' : 'tab']"  @click="changeTab($event,item.actKey,index)" v-for="(item,index) in childList">{{item.label}}</div>
    
    changeTab:(event,tab,index)=>{
                    self.activeKey = tab;
                    self.$refs.line.style.transform = "translateX("+self.slideWidth*index+"px)";
                    self.$refs.line.style.transition = "transform .3s";
                    self.$emit('on-click',event,tab)
                },
    
    1. 在 tab-pane 上方添加状态线,状态线的样式需要注意一下。
    <div  ref="line" class="tab-line"></div>
    
        .tab-line{
            height: 2px;
            background: #409eff;
            position: absolute;
            left: 0;
            margin-top: 20px;
        }
    
    1. 切换 tab 时,改变相应的内容。
      这一步最关键,需要在 tabs 组件中使用 watch 监听当前状态,如果子组件中的 actKey 等于当前状态,则显示相应的内容。
         activeKey() {
                    self.$children.map((item)=>{
                        if(item.actKey == self.activeKey){
                            item.show = true;
                        }else {
                            item.show = false;
                        }
                    })
                }
    
    展开全文
  • vue+elementUI点击菜单添加tab

    万次阅读 多人点赞 2019-09-27 19:42:05
    这个功能主要是靠elementUI的tab+Vuex来实现的,下面就来介绍如何实现的 思路:首先我们正常点击菜单跳转页面是靠路由实现的,然后我们需要点击菜单生成tab页和设置显示的tab页,所以我们需要用Vuex定义一个数组...
  • 声明:这也是我学习时在网上下载的,鉴于分享精神,并且觉得很不错才上传上来的。。。。。 android 很漂亮的tab 情景模式选择 有界面
  • 在移动应用中,大部分应用都是底部会有几个 tab 切换页。只是在底部切换到相应的页面,并且 tab 样式状态改变很容易实现,但是如果是其他页面跳转到 tab 页,tab 样式改变要如何实现昵?如下图,假设点击提交之后要...
  • 使用angular7/ng-zorro实现Tab页签 + 路由重用路由重用定义重用策略注册服务提供商定义路由信息监听路由事件common-tools 路由重用 定义重用策略 RouteReuseStrategy【官方说明】 提供一种自定义何时重用路由的方法...
  • 原 layui之Tab选项卡 - 页面元素

    千次阅读 2019-03-19 08:06:19
    Tab广泛应用于Web页面,因此layui也对其进行了良好的支持。Layui内置多种Tab风格,支持删除选项卡、并提供响应式支持。 这是一个最基本的例子: <div class="layui-tab"> <ul class="layui-tab-title"&...
  • vue+elementUI实现tab路由切换

    千次阅读 2020-04-30 20:19:06
    vue+elementUI实现tab路由切换 这个tab其实实现很久了,已经在我好几个项目中使用了,很早之前就想记录下来的,一直拖拖拖到现在emmmm… 最开始写这个tab的时候去看了很多篇实现tab的文章,先后也试了很多他们写的,...
  • 本文是在之前看到的一篇博客的基础上改进的,去掉了关闭当前功能,修改为关闭其他tab。现在将代码关键部分实现代码贴出来: 首先是样式  &lt;style&gt; .rightmenu { position: absolute; width: 80px;...
  • Fragment+FrameLayout实现Tab切换

    千次阅读 2018-05-09 17:49:31
    view=inflater.inflate(R.layout.fragment_tab1, container, false ); Button button=view.findViewById(R.id.tab1_btn); button.setOnClickListener( new View.OnClickListener() { @Override public ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 870,886
精华内容 348,354
关键字:

tab