Fragment+ViewPager+BottomNavigationView实现页面导航3


上节我们介绍了引入BottomNavigationView实现页面导航的方法,大大简化了底部导航按钮的实现,详见: Fragment+ViewPager+BottomNavigationView实现页面导航本节我们将对Fragment+ViewPager+BottomNavigationView这种页面结构进一步丰富,引入 子Fragment,实现外层页面导航的前提下,首页内部又有 多个栏目页面的结构。

1. 目标效果

在这里插入图片描述

2. 案例教学

本节内容依然是在上节Fragment+ViewPager+BottomNavigationView实现页面导航基础上的进一步完善,细节参考上节。

2.1 主界面布局

与上节一致,直接贴代码。

activity_fragment_view_pager_bottom_navigation2.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">

    <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/bnv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?android:windowBackground"
        app:itemRippleColor="@color/green_700"
        app:itemTextAppearanceInactive="@color/green_500"
        app:labelVisibilityMode="labeled"
        app:menu="@menu/bottom_nav_menu" />

</LinearLayout>

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/selector_home"
        android:title="首页" />

    <item
        android:id="@+id/menu_find"
        android:icon="@drawable/selector_find"
        android:title="发现" />

    <item
        android:id="@+id/menu_mine"
        android:icon="@drawable/selector_mine"
        android:title="我的" />
</menu>

2.2 主界面Fragment

public class ExampleFragment extends Fragment {
    // 改为public,以便于外界能引用到
    public static final String ARG_PARAM1 = "param1";
    public static final String ARG_PARAM2 = "param2";

    private String mParam1;
    private String mParam2;
    
    private TextView mTvContent;

    public ExampleFragment() {
    }
   
    public static ExampleFragment newInstance(String param1, String param2) {
        ExampleFragment fragment = new ExampleFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

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

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mTvContent = view.findViewById(R.id.tv_content);
        // 如果传入的参数有值,则设置内容
        if (!TextUtils.isEmpty(mParam1)) {
            mTvContent.setText(mParam1);
        }
    }
}

2.3 主界面Activity

FragmentViewPagerBottomNavigationActivity2.java

public class FragmentViewPagerBottomNavigationActivity2 extends AppCompatActivity {

    private ViewPager mViewPager;
    private List<Fragment> mFragments;
    private MyViewPagerAdapterForFragment mMyViewPagerAdapter;
    private BottomNavigationView mBottomNavigationView;

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

        initView();
        initData();
        initEvent();

    }

    private void initView() {
        mViewPager = findViewById(R.id.vp);
        mBottomNavigationView = findViewById(R.id.bnv);
    }

    private void initData() {
        mFragments = new ArrayList<>();
        // 承载多个栏目的主Fragment,把它单独用一个Fragment类来写,下文会对HomeFragment详述
        mFragments.add(new HomeFragment());
		// 这里是剩余两页:发现页面、我的页面
        for (int i = 1; i < 3; i++) {
            ExampleFragment fragment = ExampleFragment.newInstance("fragment" + i, "");
            mFragments.add(fragment);
        }
    }

    private void initEvent() {

        mMyViewPagerAdapter = new MyViewPagerAdapterForFragment(getSupportFragmentManager(), mFragments);
        mViewPager.setAdapter(mMyViewPagerAdapter);
        mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                onPagerSelected(position);
                Toast.makeText(FragmentViewPagerBottomNavigationActivity2.this, "当前第" + position + "页", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });

        mBottomNavigationView.setOnItemSelectedListener(new NavigationBarView.OnItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {

                switch (item.getItemId()) {
                    case R.id.menu_home:
                        mViewPager.setCurrentItem(0);
                        return true;
                    case R.id.menu_find:
                        mViewPager.setCurrentItem(1);
                        return true;
                    case R.id.menu_mine:
                        mViewPager.setCurrentItem(2);
                        return true;
                }
                return false;
            }
        });
        mBottomNavigationView.setSelectedItemId(R.id.menu_home);

    }

    private void onPagerSelected(int position) {
        switch (position) {
            case 0:
                mBottomNavigationView.setSelectedItemId(R.id.menu_home);
                break;
            case 1:
                mBottomNavigationView.setSelectedItemId(R.id.menu_find);
                break;
            case 2:
                mBottomNavigationView.setSelectedItemId(R.id.menu_mine);
                break;
        }
    }
}

与上节不同的点就是添加Fragment数据的部分。本节我们想要对首页的Fragment里面再添加多个子Fragment,因此就不能用ExampleFragment这个仅展示一个文本的Fragment了。我们的首页Fragment里面需要有多个页面切换,以及标题,需要新的布局,新的代码。接下来我们为它新建一个HomeFragment来具体实现。而另外两个页面Find和Mine,我们还用ExampleFragment做个简单模拟显示下文本就行了。

2.4 多个子栏目的承载者HomeFragment

2.4.1 界面布局

fragment_home.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">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/home_tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabGravity="start"
        app:tabMode="auto" />

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

</LinearLayout>

为了显示每个栏目的标题tab,我们引入了TabLayout这个控件。与上节的BottomNavigationView一样,它也是material库里面的,如果你引入不到,也需要添加implementation 'com.google.android.material:material:1.5.0'的依赖。
这个控件有两个属性可以了解下:

  • app:tabMode
    设置tab的模式的,有这几个取值:

    • fixed: 固定的,也就是标题不可滑动,有多少展示多少,平均分配长度
    • scrollable:可滑动的,小于等于5个默认靠左固定,大于五个tab后就可以滑动
    • auto:自动选择是否可滑动,小于等于5个默认居中固定,大于5个自动滑动
  • app:tabGravity
    设置tab的位置

    • start: 居左
    • fill:平均分配,铺满屏幕宽度
    • center: 居中

2.4.2 代码部分

首先还是控件的初始化。
HomeFragment.java

 public class HomeFragment extends Fragment {

    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";

    private String mParam1;
    private String mParam2;

    private ViewPager mViewPager;
    private TabLayout mTabLayout;
   
    public HomeFragment() {
    }

    public static HomeFragment newInstance(String param1, String param2) {
        HomeFragment fragment = new HomeFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

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

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

    private void initView() {
        mViewPager = getView().findViewById(R.id.home_vp);
        mTabLayout = getView().findViewById(R.id.home_tab_layout);
    }
}

然后是ViewPager部分

同样的,也涉及到ViewPager、Data、Adapter三个部分,与前文一样,这里快速过一下。
ViewPager我们已经声明并初始化,然后是数据部分。

  • 数据部分
private List<Fragment> mFragmentList;
private List<String> mTitleList;

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

    initView();
    initData();
}
    
private void initData() {
    mTitleList = new ArrayList<>();
    mTitleList.add("热点");
    mTitleList.add("娱乐");
    mTitleList.add("历史");
    mTitleList.add("科技");
    mTitleList.add("体育");
    mTitleList.add("地理");
    mTitleList.add("新奇");
    mTitleList.add("美图");

    mFragmentList = new ArrayList<>();
    for (int i = 0; i < mTitleList.size(); i++) {
        SubFragment subFragment = SubFragment.newInstance("fragment" + i, "");
        mFragmentList.add(subFragment);
    }
}

这里的数据相比前文多了mTitleList,也就是那些tab标题。Fragment部分我们新建了一个SubFragment来承接。出于方便演示,SubFragment与ExampleFragment一样只有一个文本做显示,比较简单。
SubFragment.java:

public class SubFragment extends Fragment {

    public static final String ARG_PARAM1 = "param1";
    public static final String ARG_PARAM2 = "param2";

    private String mParam1;
    private String mParam2;

    private TextView mTextView;

    public SubFragment() {
    }

    public static SubFragment newInstance(String param1, String param2) {
        SubFragment fragment = new SubFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

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

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

        mTextView = view.findViewById(R.id.sub_fragment_content);


        if (!TextUtils.isEmpty(mParam1)) {
            mTextView.setText(mParam1);
        }
    }
}
  • adapter部分
    也是继承自FragmentPagerAdapter,只不过这里再多重写一个获取标题的方法getPageTitle。也就是在前文基础上做一点改造:

MyViewPagerAdapterForFragment.java:

public class MyViewPagerAdapterForFragment extends FragmentPagerAdapter {

    private List<Fragment> mFragmentList;
    private List<String> mTitleList;


    public MyViewPagerAdapterForFragment(@NonNull FragmentManager fm, List<Fragment> fragmentList) {
        super(fm);
        mFragmentList = fragmentList;
    }

    public MyViewPagerAdapterForFragment(@NonNull FragmentManager fm, List<Fragment> fragmentList, List<String> titleList) {
        super(fm);
        mFragmentList = fragmentList;
        mTitleList = 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 mTitleList == null ? super.getPageTitle(position) : mTitleList.get(position);
    }
}

我们增加了一个重载的构造方法,用来传入titleList。在getPageTitle方法中返回对应的标题。

这样三个主要的部分都准备完毕,接下来这HomeFragment将其连接起来即可。

HomeFragment.java完整代码:

public class HomeFragment extends Fragment {

    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";

    private String mParam1;
    private String mParam2;

    private ViewPager mViewPager;
    private TabLayout mTabLayout;
    private List<Fragment> mFragmentList;
    private List<String> mTitleList;
    private MyViewPagerAdapterForFragment mPagerAdapterForFragment;

    public HomeFragment() {
    }

    public static HomeFragment newInstance(String param1, String param2) {
        HomeFragment fragment = new HomeFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

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

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

        initView();
        initData();
        initEvent();
    }


    private void initView() {
        mViewPager = getView().findViewById(R.id.home_vp);
        mTabLayout = getView().findViewById(R.id.home_tab_layout);
    }

    private void initData() {
        mTitleList = new ArrayList<>();
        mTitleList.add("热点");
        mTitleList.add("娱乐");
        mTitleList.add("历史");
        mTitleList.add("科技");
        mTitleList.add("体育");
        mTitleList.add("地理");
        mTitleList.add("新奇");
        mTitleList.add("美图");

        mFragmentList = new ArrayList<>();
        for (int i = 0; i < mTitleList.size(); i++) {
            SubFragment subFragment = SubFragment.newInstance("fragment" + i, "");
            mFragmentList.add(subFragment);
        }
    }

    private void initEvent() {
    	// 注意, 这里传入的是getChildFragmentManager()
        mPagerAdapterForFragment = new MyViewPagerAdapterForFragment(getChildFragmentManager(), mFragmentList, mTitleList);
        mViewPager.setAdapter(mPagerAdapterForFragment);
        // 设置默认选中第一页
        mViewPager.setCurrentItem(0);
		// 关联tabLayout与ViewPager
        mTabLayout.setupWithViewPager(mViewPager);
    }
}

增加initEvent方法,在其中进行adapter的创建以及与ViewPager的绑定。注意, 在adapter的创建的时候需要传入FragmentManager,由于此时要创建的Fragment页面是子Fragment,也就是它的承载者本身就是一个Fragment,所以,这里调用的是getChildFragmentManager()

  • 在一个Fragment里面创建子Fragment需要获取FragmentManager,调用的是getChildFragmentManager()
  • 作为对比,我们再回顾下在Activity里创建Fragment需要FragmentManager,调用的是getSupportFragmentManager(),不带Child

另外值得注意的一点是:tabLayout与ViewPager的联动。
我们滑动ViewPager,上面的标题Tab要跟着切换。反之,点击Tab,ViewPager也要跟着切换。这该怎么做到呢?很简单,有了TabLayout这个控件,只需要一行代码mTabLayout.setupWithViewPager(mViewPager);即可。

这样我们就完成整个HomeFragment部分了,结合外层Activity,整个案例也完成了。

3. 总结

本文侧重的就是子Frament多栏目这部分,结合前文的底部导航+ViewPager,构成了最实用的、常见的Frament+ViewPager+BottomNavigationView页面结构。有了这套结构,你就可以以此为模板,快速构建App了。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子林Android

感谢老板,老板大气!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值