网络知识 娱乐 Android TabLayout 使用进阶(含源码)

Android TabLayout 使用进阶(含源码)

TabLayout 使用进阶

  • 前言
  • 正文
    • 一、控件基础使用
      • ① 设置标题
      • ② 设置图标
      • ③ 设置下划线
    • 二、分类页面 (TabLayout + ViewPager + Fragment)
      • ① 创建Fragment
      • ② Fragment适配器
      • ③ 编码运行
    • 三、App主页面 (TabLayout + TabItem + ViewPager + Fragment)
      • ① 选中图标
      • ② 创建Fragment
      • ③ 编码运行
    • 四、商品分类页面
      • ① 添加第三方依赖库
      • ② 创建页面
      • ③ 创建适配器
      • ④ 编码运行
    • 五、个人主页面
      • ① 新建页面
      • ② 创建Fragment
      • ③ 图片模糊
      • ④ 编码运行
    • 六、源码

前言

  对于Android开发来说,画页面算是必不可少的,因此你会接触很多不同的UI布局,你需要去绘制出来,在这过程中你已经接触过TabLayout。

演示效果图,这个图大概一分钟,请耐心看完。

扫描二维码下载APK试用

正文

为了方便讲解,我依然是新建一个TabLayoutDemo项目来说明。

一、控件基础使用

首先在现在的版本中,TabLayout已经迁移到androidx下了。因此先在你的app下的build.gradle中的dependencies{}闭包下添加如下依赖:

	implementation 'com.google.android.material:material:1.2.1'

添加之后点击Sync Now同步一下。

同样为了方便演示我这里的MainActivity的布局中只放按钮,方便根据不同的使用方式进入不同的页面进行相应的演示。下面修改activity_main.xml。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:text="基础使用"
        android:onClick="mode1"
        android:layout_width="match_parent"
        android:layout_height="50dp"/>

</LinearLayout>

在MainActivity中新增一个方法。

	/**
     * 基础使用
     * @param view
     */
    public void mode1(View view) {

    }

这个命名就不是很规范,实际中不要这么做,我这样是为了方便演示。

而基础的使用也需要进入一个新的Activity,那么很简单,在com.llw.tablayoutdemo下新建一个mode1,这个包下新建一个BasicUseActivity,布局是activity_basic_use.xml。

接下来从MainActivity通过点击按钮进入BasicUseActivity。

	public void mode1(View view) {
        startActivity(new Intent(this, BasicUseActivity.class));
    }

下面修改activity_basic_use.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".mode1.BasicUseActivity">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FFF" />
</LinearLayout>

这里面只有一个TabLayout。回到BasicUseActivity中,使用它。

① 设置标题

public class BasicUseActivity extends AppCompatActivity {

    private TabLayout tabLayout;
    /**
     * 标题数组
     */
    private String[] titles = {"one","two","three","four","five"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_basic_use);

        tabLayout = findViewById(R.id.tab_layout);

        for (String title : titles) {
            //设置标签标题
            tabLayout.addTab(tabLayout.newTab().setText(title));
        }
    }
}

下面运行一下:

这样标题就设置好了,不过你会看到这个标题我设置的是英文小写,而这运行出来就变成了大写,这个原理有Button是一样的,你只需要将textAllCaps属性设置为false就可以了。

但是你发现这个TabLayout控件中并没有找到这个属性,这可怎么办呢?

设置样式,在你的res → values → styles.xml,在里面增加

	<!--Tab英文文字小写-->
    <style name="TabTextAllCapStyle" parent="TextAppearance.Design.Tab">
        <!--不启用大写-->
        <item name="textAllCaps">false</item>
    </style>

然后通过

	app:tabTextAppearance="@style/TabTextAllCapStyle"

设置到这个控件里面就可以了,如下所示。

	<com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FFF"
        app:tabTextAppearance="@style/TabTextAllCapStyle" />

再运行一下:

可以看到变成了小写。

我觉得还是有搞头的,试试看。

修改代码:

		//标签选中监听
        tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            /**
             * 选中
             * @param tab
             */
            @RequiresApi(api = Build.VERSION_CODES.M)
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                //大写
                tab.setText(titles[tab.getPosition()].toUpperCase());
            }

            /**
             * 未选中
             * @param tab
             */
            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
                //小写
                tab.setText(titles[tab.getPosition()].toLowerCase());
            }

            /**
             * 重新选中
             * @param tab
             */
            @Override
            public void onTabReselected(TabLayout.Tab tab) {
				//大写
                tab.setText(titles[tab.getPosition()].toUpperCase());
            }
        });

下面运行一下:

可以看到这样就可以使用实现了,只不过这是对于英文标题来说的,而中文标题的话通常我们是在选中时更改文字大小,而上图中,你会发现第一次进入时,是默认选中的第一个Tab,它的文字并没有大写,这是因为它没有触发监听,那么可以通过代码来设置。

		//选中第一个
        tabLayout.getTabAt(0).select();

这样设置就可以了。

② 设置图标

TabLayout也是可以设置图标的。首先放入五个图标

然后添加图标数组

	/**
     * 标题图标
     */
    private Integer[] icons = {R.mipmap.common_problem, R.mipmap.community_me, R.mipmap.good_line,
            R.mipmap.live, R.mipmap.umbrella_line};

再修改一个刚才的for循环

	for (int i = 0;i < titles.length; i++){
            //设置标签标题和图标
            tabLayout.addTab(tabLayout.newTab().setText(titles[i]).setIcon(icons[i]));
        }

这里你必须保证标题和图标数量一致,否则就会出现数组越界的情况导致程序崩溃。

下面运行一下。

这样看起来是不是很像一些App主页面的底部操作栏了,这个后面我会讲到的,怎么使用TabLayout+ViewPager+Fragment打造App主页面。

③ 设置下划线

从上面的图可以看到TabLayout默认是一个下划线的,这个下划线默认的颜色比较的丑,我们修改一下它。

通过

	app:tabIndicatorColor="#00FF00"

就可以设置下划线的颜色了00FF00就是原谅绿。爱是一道光,绿到你发慌。

然后再通过

	app:tabTextColor="#00FF00" 

把标签的文字颜色也改成这个原谅绿,

运行看看。

啧啧啧,是不是很环保啊!这个颜色。然后你如果不需要下划线,最简单的办法就是设置透明,

	app:tabIndicatorColor="#00000000"

这样设置就看不到下划线了。

当然更多实际需求是修改下划线的长度可以随文字大小改变而改变,这个设置其实也比较的简单,超出你想象之外的简单。

通过

	app:tabIndicatorFullWidth="false"

运行一下:

是不是很简单呢?基本上这个就能满足你的需求了,那么这个TabLayout的基本使用就介绍完了,有想要我添加的可以评论区留言哦,否则我就会以为你们都会了。

二、分类页面 (TabLayout + ViewPager + Fragment)

什么是分类页面呢?

可以看到类似与这种的都可以称之为分类页面,当然这是我的个人看法,我没有见过什么世面,浅显的这么认为。那么这样的页面看起来不错,但是怎么去入手呢?

分析一下可能就是TabLayout + ViewPager + Fragment构成的,这三个组合在写分类页面和App主页面时稍有不同,文中都会讲到的,莫急。

为了更好的演示,我还是会新建一个Activity,在com.llw.tablayoutdemo下新建一个mode2包,该包下新建ClassificationActivity,布局activity_classification.xml,布局代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".mode2.ClassificationActivity">
    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FFF"
        app:tabIndicatorColor="#00FF00"
        app:tabIndicatorFullWidth="false"
        app:tabMode="scrollable"
        app:tabRippleColor="#00000000"
        app:tabSelectedTextColor="#00FF00"
        app:tabTextColor="#000" />

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

还差Fragment了,假设当前的Activity是要做视频的分类,有类别如下:电视剧、电影、综艺、体育、新闻、国际这六项。那么我们就需要建6个Fragment,这个些fragment同样放在mode2包下。分别是

① 创建Fragment

TVSeriesFragment

public class TVSeriesFragment extends Fragment {

    @Override
    public View onCreateView(final LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_tv_series,
                container, false);
        return view;
    }
}

MovieFragment

public class MovieFragment extends Fragment {

    @Override
    public View onCreateView(final LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_movie,
                container, false);
        return view;
    }
}

VarietyShowFragment

public class VarietyShowFragment extends Fragment {

    @Override
    public View onCreateView(final LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_variety_show,
                container, false);
        return view;
    }
}

SportsFragment

public class SportsFragment extends Fragment {

    @Override
    public View onCreateView(final LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_sports,
                container, false);
        return view;
    }
}

NewsFragment

public class NewsFragment extends Fragment {

    @Override
    public View onCreateView(final LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_news,
                container, false);
        return view;
    }
}

InternationalFragment。

public class InternationalFragment extends Fragment {

    @Override
    public View onCreateView(final LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_international,
                container, false);
        return view;
    }
}

六个Fragment各自对应的xml如下:

fragment_tv_series.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="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="电视剧"
        android:textColor="#000"
        android:textSize="24sp" />
</LinearLayout>

fragment_movie.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="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="电影"
        android:textColor="#000"
        android:textSize="24sp" />
</LinearLayout>

fragment_variety_show.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="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="综艺"
        android:textColor="#000"
        android:textSize="24sp" />
</LinearLayout>

fragment_sports.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="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="体育"
        android:textColor="#000"
        android:textSize="24sp" />
</LinearLayout>

fragment_news.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="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="新闻"
        android:textColor="#000"
        android:textSize="24sp" />
</LinearLayout>

fragment_international.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="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="国际"
        android:textColor="#000"
        android:textSize="24sp" />
</LinearLayout>

我这么实在的博主现在可不多了,这么多余的代码我都给贴出来了。

② Fragment适配器

现在Fragment的就写好了。下面写一个适配器,在com.llw.tablayoutdemo下新建一个adapter包,该包下新建一个BasicFragmentAdapter,里面的代码如下:

package com.llw.tablayoutdemo.adapter;

import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;

import java.util.List;

/**
 * Fragment适配器
 *
 * @author llw
 * @date 2021/4/28 15:08
 */
public class BasicFragmentAdapter extends FragmentPagerAdapter {
    
    String titleArr[];
    List<Fragment> mFragmentList;

    public BasicFragmentAdapter(FragmentManager fm, List<Fragment> list, String[] titleArr) {
        super(fm);
        mFragmentList = list;
        this.titleArr = titleArr;
    }

    @Override
    public Fragment getItem(int i) {
        return mFragmentList.get(i);
    }

    @Override
    public int getCount() {
        return mFragmentList != null ? mFragmentList.size() : 0;
    }

    @Nullable
    @Override
    public CharSequence getPageTitle(int position) {
        return titleArr[position];
    }

    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
//        super.destroyItem(container, position, object);
    }
}

③ 编码运行

现在都具备了,回到ClassificationActivity中,修改代码如下:

package com.llw.tablayoutdemo.mode2;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;

import android.os.Bundle;

import com.google.android.material.tabs.TabLayout;
import com.llw.tablayoutdemo.R;
import com.llw.tablayoutdemo.adapter.BasicFragmentAdapter;

import java.util.ArrayList;
import java.util.List;

/**
 * 分类页面  ;TabLayout + ViewPager + Fragment
 *
 * @author llw
 */
public class ClassificationActivity extends AppCompatActivity {

    private TabLayout tabLayout;
    private ViewPager viewPager;
	String[] titleArray = new String[]{"电视剧", "电影", "综艺", "体育", "新闻", "国际"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_classification);

        tabLayout = findViewById(R.id.tab_layout);
        viewPager = findViewById(R.id.view_pager);

        List<Fragment> fragmentList = new ArrayList<>();
        fragmentList.add(new TVSeriesFragment());
        fragmentList.add(new MovieFragment());
        fragmentList.add(new VarietyShowFragment());
        fragmentList.add(new SportsFragment());
        fragmentList.add(new NewsFragment());
        fragmentList.add(new InternationalFragment());
        BasicFragmentAdapter adapter = new BasicFragmentAdapter(getSupportFragmentManager(), fragmentList, titleArray);
        viewPager.setAdapter(adapter);
        tabLayout.setupWithViewPager(viewPager);
    }
}

可以看到代码不多,下面运行一下吧。对了,还缺少一个页面入口,修改activity_main.xml,在里面增加一个按钮。

	<Button
        android:text="分类页面:TabLayout + ViewPager + Fragment"
        android:onClick="mode2"
        android:textAllCaps="false"
        android:layout_width="match_parent"
        android:layout_height="50dp"/>

然后在MainActivity中增加一个方法。

	/**
     * 组合使用 分类页面  ;TabLayout + ViewPager + Fragment
     * @param view
     */
    public void mode2(View view) { startActivity(new Intent(this, ClassificationActivity.class)); }

现在可以运行了。

不过这个文字并没有放大,那么再来设置一下,这里通过TextView来实现,在layout下新建一个tab_item.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="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:textColor="#333333"
        android:textSize="14sp" />

</LinearLayout>

然后在ClassificationActivity中增加如下方法。

	/**
     * 初始化TabLayout
     */
    private void initTabLayout() {
        for (int i = 0; i < tabLayout.getTabCount(); i++) {
            TabLayout.Tab tab = tabLayout.getTabAt(i);
            if (tab != null) {
                //设置标签视图 为  TextView
                tab.setCustomView(getTabView(i));
            }
        }

        updateTabText(tabLayout.getTabAt(tabLayout.getSelectedTabPosition()), true);

        tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                updateTabText(tab,true);
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
                updateTabText(tab,false);
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });
    }

    /**
     * 获取TabView
     * @param currentPosition
     * @return
     */
    private View getTabView(int currentPosition) {
        View view = LayoutInflater.from(this).inflate(R.layout.tab_item, null);
        TextView textView = view.findViewById(R.id.tv_title);
        textView.setText(titleArray[currentPosition]);
        return view;
    }

    /**
     * 更新标签文字
     * @param tab
     * @param isSelect
     */
    private void updateTabText(TabLayout.Tab tab, boolean isSelect) {
        if (isSelect) {
            //选中文字加大加粗
            TextView tabSelect = tab.getCustomView().findViewById(R.id.tv_title);
            tabSelect.setTextSize(22);
            tabSelect.setTextColor(Color.parseColor("#00FF00"));
            tabSelect.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
            tabSelect.setText(tab.getText());
        } else {
            TextView tabUnSelect = tab.getCustomView().findViewById(R.id.tv_title);
            tabUnSelect.setTextSize(14);
            tabUnSelect.setTextColor(Color.BLACK);
            tabUnSelect.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL));
            tabUnSelect.setText(tab.getText());
        }
    }

最后调用在onCreate方法中调用initTabLayout()方法。

运行一下:

嗯,这个效果还是阔以滴。

三、App主页面 (TabLayout + TabItem + ViewPager + Fragment)

现在常规的App主页面都是底部有几个菜单,4个或者5个。通讯类的基本上是4个,如果QQ、微信。购物类的基本上是5个,如果淘宝、天猫、京东等。至于有几个我们不管,主要是怎么去实现这个主页面的菜单切换。这里的实现方式其实有很多,而文本以TabLayout为主,那么自然是以TabLayout来现实了,就如我标题上说的一样,用到了,TabLayout + TabItem + ViewPager + Fragment。

① 选中图标

下面就来看看具体怎么去做吧。

这里是第三个使用方式了,那么在com.llw.tablayoutdemo包下新建一个mode3包,这个包下新建一个HomeActivity,用来作为App的主页面,然后它的布局activity_home.xml是有一点点麻烦的。里面会用到8个图标。

将图标放到layout下的drawable文件夹中。 然后在这个文件夹下新建四个xml文件,分别是: app_home.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/icon_home" android:state_selected="false"/>
    <item android:drawable="@drawable/icon_home_selected" android:state_selected="true"/>
</selector>

app_search.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/icon_search" android:state_selected="false"/>
    <item android:drawable="@drawable/icon_search_selected" android:state_selected="true"/>
</selector>

app_find.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/icon_find" android:state_selected="false"/>
    <item android:drawable="@drawable/icon_find_selected" android:state_selected="true"/>
</selector>

app_mine.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/icon_mine" android:state_selected="false"/>
    <item android:drawable="@drawable/icon_mine_selected" android:state_selected="true"/>
</selector>

下面来写activity_home.xml,里面的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".mode3.HomeActivity">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/tab_layout" />

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="#FFF"
        android:minHeight="?attr/actionBarSize"
        app:tabIndicatorColor="#00000000"
        app:tabRippleColor="#00000000"
        app:tabSelectedTextColor="#1296DB"
        app:tabTextColor="#929299">

        <com.google.android.material.tabs.TabItem
            android:id="@+id/item_home"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:icon="@drawable/app_home"
            android:text="主页" />

        <com.google.android.material.tabs.TabItem
            android:id="@+id/item_search"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:icon="@drawable/app_search"
            android:text="搜索" />

        <com.google.android.material.tabs.TabItem
            android:id="@+id/item_find"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:icon="@drawable/app_find"
            android:text="发现" />

        <com.google.android.material.tabs.TabItem
            android:id="@+id/item_mine"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:icon="@drawable/app_mine"
            android:text="我的" />

    </com.google.android.material.tabs.TabLayout>
</RelativeLayout>

这里对TabLayout控件做了一些修改,设置点击的水波纹为透明、下划线为透明,选中的文字颜色为蓝色,默认是灰色,和刚才创建的四个图标样式文件类似,选中时切换蓝色图片,未选中时灰色图片。

② 创建Fragment

这里tabItem就是用来控制菜单图片的。现在布局已经写好了,下面来写代码。

我们的主页面自然也需要显示多个Fragment,通过ViewPager来进行切换。 而这些Fragment都放在mode3包下,下面一个一个的来创建

HomeFragment

public class HomeFragment extends Fragment {

    @Override
    public View onCreateView(final LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_home,
                container, false);
        return view;
    }

}

fragment_home.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="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Home"
        android:textColor="#000"
        android:textSize="24sp" />
</LinearLayout>

SearchFragment

public class SearchFragment extends Fragment {

    @Override
    public View onCreateView(final LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_search,
                container, false);
        return view;
    }

}

fragment_search.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="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Search"
        android:textColor="#000"
        android:textSize="24sp" />
</LinearLayout>

FindFragment

public class FindFragment extends Fragment {

    @Override
    public View onCreateView(final LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_find,
                container, false);
        return view;
    }

}

fragment_find.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="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Find"
        android:textColor="#000"
        android:textSize="24sp" />
</LinearLayout>

MineFragment

public class MineFragment extends Fragment {

    @Override
    public View onCreateView(final LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_mine,
                container, false);
        return view;
    }

}

fragment_mine.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="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Mine"
        android:textColor="#000"
        android:textSize="24sp" />
</LinearLayout>

③ 编码运行

现在四个Fragment和它们的布局都写好了,下面回到HomeActivity中,修改代码如下:

package com.llw.tablayoutdemo.mode3;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;

import android.os.Bundle;

import com.google.android.material.tabs.TabLayout;
import com.llw.tablayoutdemo.R;
import com.llw.tablayoutdemo.adapter.BasicFragmentAdapter;

import java.util.ArrayList;
import java.util.List;

/**
 * 组合使用 主页面  ;TabLayout + TabItem + ViewPager + Fragment
 * @author llw
 */
public class HomeActivity extends AppCompatActivity {

    private TabLayout tabLayout;
    private ViewPager viewPager;

    final String[] titleArray = new String[]{"首页", "搜索", "发现", "我的"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);

        init();
    }

    private void init() {
        tabLayout = findViewById(R.id.tab_layout);
        viewPager = findViewById(R.id.view_pager);

        List<Fragment> fragmentList = new ArrayList<>();
        fragmentList.add(new HomeFragment());
        fragmentList.add(new SearchFragment());
        fragmentList.add(new FindFragment());
        fragmentList.add(new MineFragment());
        BasicFragmentAdapter adapter = new BasicFragmentAdapter(getSupportFragmentManager(), fragmentList, titleArray);
        viewPager.setAdapter(adapter);

        tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                viewPager.setCurrentItem(tab.getPosition());
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });

        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                tabLayout.setScrollPosition(position,positionOffset,true);
            }

            @Override
            public void onPageSelected(int position) {

            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });

    }
}

那么这里就是切换Tab的时候改变ViewPager,ViewPager改变的时候切换Tab选中。现在还不能运行的,因为缺少一个入口,这个入口依然在MainActivity中,

在activity_main.xml中添加一个按钮:

	<Button
        android:text="主页面:TabLayout + TabItem + ViewPager + Fragment"
        android:onClick="mode3"
        android:textAllCaps="false"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

在MainActivity中添加一个方法:

	/**
     * 组合使用 主页面  ;TabLayout + TabItem + ViewPager + Fragment
     * @param view
     */
    public void mode3(View view) {
        startActivity(new Intent(this, HomeActivity.class));
    }

下面运行一下:

可以看到我点击TabLayout,ViewPager就会切换,滑动ViewPager,TabLayout就会选中相应的TabItem。

这样就类似于现在的App主页面了。

四、商品分类页面

什么是商品分类页面呢?如下图

就像这种页面,你在日常的使用中应该见过。通常是在购物APP里面居多。但这个也是一个使用场景之一。那么这个页面要怎么做呢?

我们来分析一下啊,首先左边不出意外是一个列表,它的表现形式可以有多种,你可以使用RecyclerView,也可以使用TabLayout,毫无疑问我要使用TabLayout,而右边的就是一个ViewPager,可以上下滑动切换的ViewPager,里面放Fragment或者RecyclerView。我目前先这么自以为是的猜测一下。

那么下面就来实践一下吧。

① 添加第三方依赖库

首先在app下的build.gradle的dependencies{}闭包中添加如下依赖:

	//纵向TabLayout
    implementation 'q.rorbin:VerticalTabLayout:1.2.5'
    //可以纵向滑动的ViewPager
    implementation 'cn.youngkaaa:yviewpager:0.4'

然后Sync Now。

② 创建页面

那么现在是使用的第四个方式了,在com.llw.tablayoutdemo包下新建mode4包,这个包下新建一个GoodsActivity,布局activity_goods.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".mode4.GoodsActivity">
    <!--纵向TabLayout-->
    <q.rorbin.verticaltablayout.VerticalTabLayout
        android:id="@+id/tab_layout"
        android:layout_width="80dp"
        android:layout_height="match_parent"
        android:background="#EDEDED"
        app:indicator_color="#FFFFFF"
        app:indicator_gravity="fill"
        app:tab_height="50dp"
        app:tab_mode="scrollable" />
    <!--纵向ViewPager-->
    <cn.youngkaaa.yviewpager.YViewPager
        android:id="@+id/view_pager"
        android:background="#FFF"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_toEndOf="@id/tab_layout"
        app:orientation="vertical"/>
</RelativeLayout>

到这里我们思考一个问题,假设不知道商品的类别数量的情况下,怎么写,那么肯定不能写死这个Fragment,不能像之前一样创建,那样明显太笨重了不是吗?像这种商品分类页面里面的布局都是一样的,不同的只是数据而已,而这个数据也是可以变化的,因此你不能写死数据和Fragment,因此就需要动态来生成。下面在mode4包下新建一个GoodsTabFragment,它的布局是fragment_goods_tab.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="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="内容"
        android:textColor="#000"
        android:textSize="24sp" />
</LinearLayout>

这里我只是放了一个TextView,用来显示当前是哪一个商品所对应的Fragment。而GoodsTabFragment的代码也是很简单的,如下:

public class GoodsTabFragment extends Fragment {

    private TextView tvContent;
    private String content;

    public GoodsTabFragment(String content) {
        this.content = content;
    }

    @Override
    public View onCreateView(final LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_goods_tab,
                container, false);
                tvContent = view.findViewById(R.id.tv_content);
        return view;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        tvContent.setText(content);
    }
}

③ 创建适配器

下面还需要一个适配器,在adapter包下新建一个GoodsFragmentAdapter,里面的代码如下:

public class GoodsFragmentAdapter extends FragmentPagerAdapter {

    private ArrayList<Fragment> fragments;
    private ArrayList<String> tabName;

    public GoodsFragmentAdapter(FragmentManager fm, ArrayList<Fragment> fragments, ArrayList<String>
            tabName) {
        super(fm);
        this.fragments = fragments;
        this.tabName = tabName;
    }

    @Override
    public Fragment getItem(int i) {
        return fragments.get(i);
    }

    @Override
    public int getCount() {
        return fragments.size();
    }

    @Nullable
    @Override
    public CharSequence getPageTitle(int position) {
        return tabName.get(position);
    }
}

④ 编码运行

然后回到GoodsActivity中,编写代码如下:

/**
 * 商品分类页面 : VerticalTabLayout + YViewPager + Fragment
 * @author llw
 */
public class GoodsActivity extends AppCompatActivity {

    private VerticalTabLayout tabLayout;
    private YViewPager viewPager;

    private List<String> titleList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_goods);

        tabLayout = findViewById(R.id.tab_layout);
        viewPager = findViewById(R.id.view_pager);

        final ArrayList<String> tabName = new ArrayList<>();
        final ArrayList<Fragment> fragments = new ArrayList<>();

        int num = new Random().nextInt(50);
        Toast.makeText(this, num + "个商品", Toast.LENGTH_SHORT).show();

        for (int i = 0; i < num; i++) {
            titleList.add("商品" + i);
        }

        for (int i = 0; i < titleList.size(); i++) {
            fragments.add(new GoodsTabFragment(titleList.get(i)));
            tabName.add(titleList.get(i));
        }
        GoodsFragmentAdapter fragTabAdapter = new GoodsFragmentAdapter(getSupportFragmentManager(), fragments,
                tabName);
        viewPager.setAdapter(fragTabAdapter);

        tabLayout.setupWithViewPager(viewPager);

    }
}

设置一个50以内的随机数,然后设置菜单和Fragment,运行一下: