目录
效果展示如下,完整代码在文章末尾

首先由一个主页面来展示三个字页面(“首页”,“推荐”,“我的”),这三个子页面由fragment来显示。

首页底部tab栏用 BottomNavigationView,我们可以创建一个menu文件来给tab栏按钮设置样式
BottomNavigationView 控件引用该menu,来显示底边按钮

bottom_nav_menu.xml
- <?xml version="1.0" encoding="utf-8"?>
- <menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@+id/menu_home"
- android:icon="@drawable/index"
- android:title="首页"/>
- <item android:id="@+id/menu_recommend"
- android:icon="@drawable/recommend"
- android:title="推荐"/>
- <item android:id="@+id/menu_mine"
- android:icon="@drawable/chicken"
- android:title="我的"/>
-
- </menu>
icon:设置图标

主页用 ViewPager + BottomNavigationView 来布局
ViewPager控件作用:作为容器,显示子页面
BottomNavigationView控件作用:制作底部按钮
activity_index.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"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:orientation="vertical">
- <androidx.viewpager.widget.ViewPager
- android:id="@+id/vp"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"/>
- <com.google.android.material.bottomnavigation.BottomNavigationView
- android:id="@+id/bottom_nav_menu"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:menu="@menu/bottom_nav_menu" />
-
- </LinearLayout>

我们可以把每个页面当成一个对象,我们要想创建这个对象就要使用fragment里的一些方法。所以要创建一个类并继续fragment,来构建子页面的布局
因为这里是创建最简单的fragment类,所以我们之间选择编译器为我们提供的创建方法就行
主要实现三个方法:
- newInstance 接收参数,存放在bundle内
- onCreate 设置参数,从bundle内取参数
- onCreateView 构建页面

因为我们要在子页面内放一张照片,所以我们要定义一个img参数来接收图片,并在onCreateView方法内进行设置
VPFrament
- package com.example.tabfragment.fragment;
-
- import android.os.Bundle;
-
- import androidx.fragment.app.Fragment;
-
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.ImageView;
-
- import com.example.tabfragment.R;
-
- public class VPFragment extends Fragment {
-
- private static final String ARG_PARAM1 = "title";
- private static final String ARG_PARAM2 = "img";
-
- private String title;
- private int img;
-
- public static VPFragment newInstance(String title, int img) {
- VPFragment fragment = new VPFragment();
- Bundle args = new Bundle();
- args.putString(ARG_PARAM1, title);
- args.putInt(ARG_PARAM2,img);
- fragment.setArguments(args);
- return fragment;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (getArguments() != null) {
- title = getArguments().getString(ARG_PARAM1);
- img = getArguments().getInt(ARG_PARAM2);
- }
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.fragment_v_p, container, false);
- Bundle argument = getArguments();
- ImageView iv = view.findViewById(R.id.iv);
- iv.setImageResource(argument.getInt(ARG_PARAM2,R.drawable.ji1));
- return view;
- }
- }
- <?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:orientation="vertical">
-
- <ImageView
- android:id="@+id/iv"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"/>
-
- </LinearLayout>
为什么要创建适配器?
适配器作用是把每个单独的fragment页面放在一起打包,传给ViewPager。只有ViewPager设置了适配器我们才能看见翻页的效果。
所以我们要单独写一个类并继承 FragmentPagerAdapte
Alt + Enter 主要实现 FragmentPagerAdapter 方法和构造方法
- MyFragmentStateVPAdapter 构造方法,用来接收参数
- getItem 根据position来获取页面
- getCount 获取页面的个数

基本方法都实现完后,我们要定义一个变量来存储我们每个fragment子页面
private List<Fragment> myFragmentList;
定义变量 myFragmentList 后,我们需要在构造方法内赋值,并在getItem 和getCount 方法进行操作,具体看下面代码
MyFragmentStateVPAdapter
- package com.example.tabfragment.adapter;
-
- import androidx.annotation.NonNull;
- import androidx.fragment.app.Fragment;
- import androidx.fragment.app.FragmentManager;
- import androidx.fragment.app.FragmentPagerAdapter;
-
- import java.util.List;
-
- public class MyFragmentStateVPAdapter extends FragmentPagerAdapter {
- private List
myFragmentList; - public MyFragmentStateVPAdapter(@NonNull FragmentManager fm,List
myFragmentList) { - super(fm);
- this.myFragmentList = myFragmentList;
- }
-
- /**
- * 获取页面
- * @param position 页面的位置
- * @return 返回具体页面
- */
- @NonNull
- @Override
- public Fragment getItem(int position) {
- return myFragmentList == null ? null:myFragmentList.get(position);
- }
-
- /**
- * 获取adapter内存储的页面个数
- * @return
- */
- @Override
- public int getCount() {
- return myFragmentList == null ? 0 : myFragmentList.size();
- }
- }
- private ViewPager mViewPager; // 主页面来展示子页面 Viewpager
- private BottomNavigationView mBottomNavigationView; //主页面底部tab按钮
- private List<Fragment> mFragmentList; //存储fragment页面,用来作为构造adapter的参数
- private MyFragmentStateVPAdapter mStateVPAdapter;
- private void initeView() {
- mViewPager = findViewById(R.id.vp);
- mBottomNavigationView = findViewById(R.id.bottom_nav_menu);
- }
因为我们要在ViewPage内滑动显示多个页面,所以我们要先把这几个页面创建好,存储到 mFragmentList,为下一步显示页面做准备
- private void initData() {
- mFragmentList = new ArrayList<>();
- VPFragment homeFragment = VPFragment.newInstance("首页", R.drawable.ji1);
- VPFragment recommendFragment = new VPFragment().newInstance("推荐",R.drawable.ji5);
- VPFragment mineFragment = new VPFragment().newInstance("我的",R.drawable.ji6);
-
- //添加页面
- mFragmentList.add(homeFragment);
- mFragmentList.add(recommendFragment);
- mFragmentList.add(mineFragment);
- }
我们把上一步创建好的fragment页面,作为参数来构造适配器
new MyFragmentStateVPAdapter(getSupportFragmentManager(), mFragmentList )
第一个参数:getSupportFragmentManager() 这个大家记着就可以了,固定用法
第二个参数: mFragmentList
然后让mViewPager 设置该适配器,调用setAdapter() 方法
接下来,让给mViewPager设置监听,调用addOnPageChangeListener(),目的是让mViewPager内的子页面与底部menu按钮相关联。
- private void setFListener() {
- mStateVPAdapter = new MyFragmentStateVPAdapter(getSupportFragmentManager(),mFragmentList);
- mViewPager.setAdapter(mStateVPAdapter);
- mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
- @Override
- public void onPageSelected(int position) {onPagerSelected(position);}
- @Override
- public void onPageScrollStateChanged(int state) {}
- });
- }
这里我把页面绑定底部按钮的代码提取出来了,方便大家查看。
这里我们设置监听会返回翻页面的位置,然后在该页面下绑定对于的按钮
mBottomNavigationView调用 setSelectedItemId方法,传入的参数是menu内每个item的id值
- //给每个页面设置按钮,页面关联按钮,页面动,按钮动
- private void onPagerSelected(int position) {
- switch(position){
- case 0:
- mBottomNavigationView.setSelectedItemId(R.id.menu_home);
- break;
- case 1:
- mBottomNavigationView.setSelectedItemId(R.id.menu_recommend);
- break;
- case 2:
- mBottomNavigationView.setSelectedItemId(R.id.menu_mine);
- break;
- }
- }
我们还要给底部按钮设置监听(按哪个按钮,跳到对于的页面)
- //反向处理,按钮设置点击事件,按钮关联页面
- private void setBListener() {
- mBottomNavigationView.setOnItemSelectedListener(new NavigationBarView.OnItemSelectedListener() {
- @Override
- public boolean onNavigationItemSelected(@NonNull MenuItem item) {
- switch(item.getItemId()){
- case R.id.menu_home:
- mViewPager.setCurrentItem(0);
- break;
- case R.id.menu_recommend:
- mViewPager.setCurrentItem(1);
- break;
- case R.id.menu_mine:
- mViewPager.setCurrentItem(2);
- break;
- }
- return true;
- }
- });
- }
调用以上方法并允许,结果如下
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_index);
- initeView();
- initData();
- setFListener();
- setBListener();
- }

在开头我们会发现,“首页”对应的子页面并不只是显示一个图片,而是tab栏+四个fragment,所以我们要重新准备一个fragment类和一个新的适配器。

因为新的页面顶部到导航栏有标题,所以我们要重新再写一个适配器,实现getPageTitle()方法。
getPageTitle方法用来返回每个页面的标题
- package com.example.tabfragment.adapter;
-
- import androidx.annotation.NonNull;
- import androidx.annotation.Nullable;
- import androidx.fragment.app.Fragment;
- import androidx.fragment.app.FragmentManager;
- import androidx.fragment.app.FragmentStatePagerAdapter;
-
- import java.util.List;
-
- public class MyFragmentStVpTitleAdapter extends FragmentStatePagerAdapter {
- private List
mFragmentList; - private List
titleList; -
- public MyFragmentStVpTitleAdapter(@NonNull FragmentManager fm,
- List
mFragmentList, - List
titleList) { - super(fm);
- this.mFragmentList = mFragmentList;
- this.titleList = titleList;
- }
-
- @NonNull
- @Override
- public Fragment getItem(int position) {
- return mFragmentList == null ? null:mFragmentList.get(position);
- }
-
- @Override
- public int getCount() {
- return mFragmentList==null? 0:mFragmentList.size();
- }
-
- @Nullable
- @Override
- public CharSequence getPageTitle(int position) {
- return titleList.get(position);
- }
- }
构造VPHomeFragment 类 跟 构造 VPfragment 的步骤是一样的,唯一不同就VPHomeFragment 类中多了一个 onCreateView 方法。
首先,VPHomeFragment 类要添加几个变量
- private ViewPager mViewPager;
- private TabLayout mTabLayout;
- private List<Fragment> mFragmentList;
- private List<String> titleList;
- private MyFragmentStVpTitleAdapter mStVPTitleAdapter;
onCreateView 方法
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- mViewPager = view.findViewById(R.id.home_vp);
- mTabLayout = view.findViewById(R.id.tab_layout);
- initData();
- //注意此处,getChild 嵌套
- mStVPTitleAdapter = new MyFragmentStVpTitleAdapter(getChildFragmentManager(),mFragmentList,titleList);
- mViewPager.setAdapter(mStVPTitleAdapter);
- mTabLayout.setupWithViewPager(mViewPager);//tab 适配页面
- }
- private void initData() {
- mFragmentList = new ArrayList<>();
-
- VPFragment vPfragment1 = VPFragment.newInstance("鸡",R.drawable.ji1);
- VPFragment vPfragment2 = VPFragment.newInstance("你",R.drawable.ji2);
- VPFragment vPfragment3 = VPFragment.newInstance("太",R.drawable.ji3);
- VPFragment vPfragment4 = VPFragment.newInstance("美",R.drawable.j4);
-
- mFragmentList.add(vPfragment1);
- mFragmentList.add(vPfragment2);
- mFragmentList.add(vPfragment3);
- mFragmentList.add(vPfragment4);
-
- titleList = new ArrayList<>();
- titleList.add("鸡");
- titleList.add("你");
- titleList.add("太");
- titleList.add("美");
-
- }
布局
为了让首页实现下面效果,我们要对VPHomeFragment 类调用的xm文件重新布局

fragment_v_p_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:orientation="vertical">
-
- <com.google.android.material.tabs.TabLayout
- android:id="@+id/tab_layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
-
- <androidx.viewpager.widget.ViewPager
- android:id="@+id/home_vp"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
- </LinearLayout>
最后在Activity_index.java 内把 homeFragment类型修改为 VPHomeFragment
VPHomeFragment homeFragment = VPHomeFragment.newInstance("我的",R.drawable.ji1);

gitte获取代码:点击跳转