2

多种方式实现底部导航栏

 1 year ago
source link: https://blackdn.github.io/2021/06/28/Bottom-NavBar-2021/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

“乌龟会飞,地铁的窗外是星空。”

因为这学期要交大作业,在匆忙中发现很多并不熟悉的功能…
搜索两小时,代码五分钟,很是难受。
趁着考试都考完了,打算回过头慢慢把这些功能的实现学习一遍。

多种方式实现底部导航栏

底部导航栏

通常说到底部导航栏,一般要和 Fragment 一起实现。
毕竟底部导航栏只是一个布局,然后在选中和未选中的状态之间变来变去。要能够和 Fragment 的切换联系起来,那才是有用的底部导航栏。
为了偷懒节省篇幅,这次就写一个带两个图标的底部导航栏,也就是在两个 Fragment 之间切换。
两个 Fragment 会了,加多也就很简单啦~
不熟悉 Fragment 的同学请移步:Fragment的使用及其过渡动画

1. 纯手工实现

我们先从最基本的实现方法入手,先大致看一下要做什么。
我们打算在一个 Activity 中实现底部导航栏以及 Fragment 的切换。那么在 Activity 中肯定是要布局 Fragment 的容器和底部导航栏的。这里就举个例子,实现两个图标的底部导航栏。
不过还是先从资源文件入手,先把要用到的图片啥的资源准备好。

资源文件包括三大块,分别是导航栏每个 item 的文字、图片、背景。每个文件都设置两个样式,分别代表被点击和未点击两种状态,用 state_selected 属性实现。
图片大家可以自己随便找找测试一下。

新建 tab_menu_bg.xml作为背景样式

<?xml version="1.0" encoding="utf-8"?>
<!--in tab_menu_bg.xml-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="false">
        <shape>
            <solid android:color="#00FFFFFF"/>
        </shape>
    </item>
    <item android:state_selected="true">
        <shape>
            <solid android:color="#FFC4C4C4"/>
        </shape>
    </item>
</selector>

新建 tab_menu_text.xml 作为文字样式

<?xml version="1.0" encoding="utf-8"?>
<!--in tab_menu_text.xml-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="#8D7A71" android:state_selected="false"/>
    <item android:color="#FFC800" android:state_selected="true"/>
</selector>

新建 tab_menu_remind.xml 作为第一个图标的图片样式

<?xml version="1.0" encoding="utf-8"?>
<!--in tab_menu_remind.xml-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@mipmap/tab_channel_normal" android:state_selected="false"/>
    <item android:drawable="@mipmap/tab_channel_pressed" android:state_selected="true" />
</selector>

新建 tab_menu_message.xml 作为第二个图标的图片样式

<?xml version="1.0" encoding="utf-8"?>
<!--in tab_menu_message.xml-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@mipmap/tab_message_normal" android:state_selected="false" />
    <item android:drawable="@mipmap/tab_message_pressed" android:state_selected="true"/>
</selector>

Fragment

我们的想法是,点击底部的一个图标,载入其对应的 Fragment。每个 Fragment 的布局及逻辑应该是不同的,所以按理说有几个图标就有几个 Fragment 。不过为了偷懒方便这里就只写一个Fragment了。
这里沿用之前 Fragment 博客中的 AnotherFragment,具体可参考Fragment的使用及其过渡动画,这里略有修改。
布局文件是 fragment_another.xml,其中就写一个 TextView

<?xml version="1.0" encoding="utf-8"?>
<!--in fragment_another.xml-->
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/another_fragment_textview"
        android:text="default text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true" />
</RelativeLayout>

然后是 AnotherFragment 里,为了方便调试,我们希望每当在底部导航栏点击图标后,能载入对应的 Fragment 。为此我们重写其构造方法,在实例化的时候传入一串文本,并让 AnotherFragment 的 TextView 显示这串文本,以区别不同的 Fragment。

public class AnotherFragment extends Fragment {
    private TextView textView;
    private String text;

    public AnotherFragment(String text) {
        this.text = text;
    }
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_another, container, false);
        textView = view.findViewById(R.id.another_fragment_textview);
        textView.setText(text);
        return view;
    }
}

Activity

Activity的代码较为复杂。因为所有的Fragment 都是由 Activity 托管,按钮的点击事件、Fragment的载入等,都是由Activity实现。
我这里新建了一个 NaviMainActivity 并设置为启动项。嫌麻烦的话可以直接用 MainActivity。
先来看布局文件 activity_navi_main.xml

<?xml version="1.0" encoding="utf-8"?>
<!--in activity_navi_main.xml-->
<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=".NaviMainActivity">
    
    <LinearLayout
        android:id="@+id/nav_bar"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:layout_alignParentBottom="true"
        android:background="@color/bg_white"
        android:orientation="horizontal">
        
        <TextView
            android:id="@+id/nav_icon_remind"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@drawable/tab_menu_bg"
            android:drawablePadding="3dp"
            android:drawableTop="@drawable/tab_menu_channel"
            android:gravity="center"
            android:padding="5dp"
            android:text="提醒"
            android:textColor="@drawable/tab_menu_text"
            android:textSize="16sp"/>
        <TextView
            android:id="@+id/nav_icon_message"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@drawable/tab_menu_bg"
            android:drawablePadding="3dp"
            android:drawableTop="@drawable/tab_menu_message"
            android:gravity="center"
            android:padding="5dp"
            android:text="信息"
            android:textColor="@drawable/tab_menu_text"
            android:textSize="16sp"/>
    </LinearLayout>
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/nav_bar"/>
</RelativeLayout>

根布局无所谓,用 LinearLayout 也能实现。内部其实只有两部分,一部分是作为容器的 FragmentContainerView ;另一部分是两个TextView,作为底部导航栏的两个点击图标。
想使用 FragmentContainerView 记得在模块级gradle中声明依赖,当然嫌麻烦的可以用 FrameLayout 代替。

// in build.gradle: app
dependencies {
   	···
    def fragment_version = "1.3.4"
    implementation "androidx.fragment:fragment:$fragment_version"
}

然后看看 TextView ,其实就是一个文字+图片,图片就用 android:drawableTop 放到文字的上面。其中的 backgroundtextColor 都要用我们之前写的资源文件,不然就不会有点击效果啦。
最后是 Activity 的逻辑,来到 NaviMainActivity。因为代码比较多,我们一步步来看。
绑定布局和设置监听器不会有人不会吧,不会吧不会吧?

    //in NaviMainActivity.java
	private void init() {
        navIconRemind = findViewById(R.id.nav_icon_remind);
        navIconMessage = findViewById(R.id.nav_icon_message);
        navIconRemind.setOnClickListener(this);
        navIconMessage.setOnClickListener(this);
    }

现在来明确我们的逻辑,我点击导航栏的一个图标,这个图标变为选中状态,然后载入当前图标所对应的 Fragment。

在布局中,我们的导航栏的图片、文字颜色、背景颜色都设置了两个,分别是 android:state_selected 为 true 和 false 两种,对应选中和未选中两种状态。要注意一点,为了避免当我们点完一个图标再去点另一个的时候,两个图标都变成点下的状态,需要先把所有图标变为未被点下的状态,再把当前图标变为点下的状态。对应的代码如下:

    //in NaviMainActivity.java
	private void allUnselected() {
        navIconRemind.setSelected(false);
        navIconMessage.setSelected(false);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.nav_icon_remind:
                allUnselected();
                navIconRemind.setSelected(true);
                break;
            case R.id.nav_icon_message:
                allUnselected();
                navIconMessage.setSelected(true);
                break;
        }
    }

接着是在点击事件中放入 Fragment 的逻辑。当然就是点哪个图标加入对应的 Fragment。不过因为一次只能显示一个 Fragment,所以我们在加入 Fragment 之前需要先隐藏所有 Fragment。

    //in NaviMainActivity.java
	private void hideAllFragment(FragmentTransaction transaction) {
        if (f1 != null) {
            transaction.hide(f1);
        }
        if (f2 != null) {
            transaction.hide(f2);
        }
    }
    @Override
    public void onClick(View view) {
        FragmentTransaction transaction = manager.beginTransaction();
        hideAllFragment(transaction);
        allUnselected();
        switch (view.getId()) {
            case R.id.nav_icon_remind:
                navIconRemind.setSelected(true);
                if (f1 == null) {
                    f1 = new AnotherFragment("Fragment 1");
                    transaction.add(R.id.fragment_container, f1);
                } else {
                    transaction.show(f1);
                }
                break;
            case R.id.nav_icon_message:
                navIconMessage.setSelected(true);
                if (f2 == null) {
                    f2 = new AnotherFragment("Fragment 2");
                    transaction.add(R.id.fragment_container, f2);
                } else {
                    transaction.show(f2);
                }
                break;
        }
        transaction.commit();
    }

每次我们点击图标的时候,除了进行 selected 的设置,还判断当前 Fragment 是否存在。如果不存在则说明是第一次点,就add新的 Fragment 进来;如果存在,则说明是被隐藏了,让其显示出来就好了。

最后,在 onCreate() 中,除了绑定视图和设置监听器,再调用 navIconRemind.performClick() 进行第一个图标的点击事件,这样进来的页面时第一个图标所对应的 Fragment。总不能刚进来是个啥也没有的 Activity 吧。

全部代码如下:

//in NaviMainActivity.java
public class NaviMainActivity extends AppCompatActivity implements View.OnClickListener{
    private TextView navIconRemind;
    private TextView navIconMessage;
    private AnotherFragment f1, f2;
    private FragmentManager manager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_navi_main);
        
        manager = getSupportFragmentManager();
        init();
        navIconRemind.performClick();
    }
    private void init() {
        navIconRemind = findViewById(R.id.nav_icon_remind);
        navIconMessage = findViewById(R.id.nav_icon_message);
        navIconRemind.setOnClickListener(this);
        navIconMessage.setOnClickListener(this);
    }
    private void setSelected() {
        navIconRemind.setSelected(false);
        navIconMessage.setSelected(false);
    }
    private void hideAllFragment(FragmentTransaction transaction) {
        if (f1 != null) {
            transaction.hide(f1);
        }
        if (f2 != null) {
            transaction.hide(f2);
        }
    }
    @Override
    public void onClick(View view) {
        FragmentTransaction transaction = manager.beginTransaction();
        hideAllFragment(transaction);

        switch (view.getId()) {
            case R.id.nav_icon_remind:
                setSelected();
                navIconRemind.setSelected(true);
                if (f1 == null) {
                    f1 = new AnotherFragment("Fragment 1");
                    transaction.add(R.id.fragment_container, f1);
                } else {
                    transaction.show(f1);
                }
                break;
            case R.id.nav_icon_message:
                setSelected();
                navIconMessage.setSelected(true);
                if (f2 == null) {
                    f2 = new AnotherFragment("Fragment 2");
                    transaction.add(R.id.fragment_container, f2);
                } else {
                    transaction.show(f2);
                }
                break;
        }
        transaction.commit();
    }
}
  1. 编写资源文件:被点击和未点击的文字、背景、图片等样式
  2. 编写Fragment:每个图标所对应的 Fragment 页面
  3. 编写Activity:处理点击图标后的样式、Fragment等逻辑

2. RadioGroup 实现

RadioGroup 其实是一个单选框组件,继承自 LinearLayout。在其中用 RadioButton 实现每个选项。具体结构其实和上面的 LinearLayout + TextView 差不多。

资源文件其实也不用变,不过要把之前的 android:state_selected 变为 android:state_checked 。之前我们用 TextView ,以 selected 判断是否被选中;而 RadioButton 则以 checked 判断是否被选中。

<!--in tab_menu_bg.xml-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="false">
        ···
    </item>
    <item android:state_checked="true">
        ···
    </item>
</selector>

<!--in tab_menu_text.xml-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="#8D7A71" android:state_checked="false"/>
    <item android:color="#FFC800" android:state_checked="true"/>
</selector>

<!--in tab_menu_remind-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@mipmap/tab_channel_normal" android:state_checked="false"/>
    <item android:drawable="@mipmap/tab_channel_pressed" android:state_checked="true" />
</selector>

<!--in tab_menu_message.xml-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@mipmap/tab_message_normal" android:state_checked="false" />
    <item android:drawable="@mipmap/tab_message_pressed" android:state_checked="true"/>
</selector>

在RadioButton 里,属性android:button="@null"可以去掉Button前面的圆点

Fragment

一点没变,参考上面,skr。

Activity

先看布局。在之前的布局中我们发现,作为导航栏的图标,很多属性都是一样的,所以这时候我们就把他们提取出来作为共用的样式,减少博客篇幅代码冗余。
所以来到 res->themes->themes.xml,自定义新的样式。

<!--in themes.xml-->
<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.FragmentTest" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
		···
    </style>

    <style name="nav_item">
        <item name="android:layout_height">match_parent</item>
        <item name="android:layout_width">0dp</item>
        <item name="android:layout_weight">1</item>
        <item name="background">@drawable/tab_menu_bg</item>
        <item name="android:gravity">center</item>
        <item name="android:paddingTop">3dp</item>
        <item name="android:textColor">@drawable/tab_menu_text</item>
        <item name="android:textSize">18sp</item>
        <item name="android:button">@null</item>
    </style>
</resources>

然后回到主界面布局,来到 activity_navi_main.xml

<?xml version="1.0" encoding="utf-8"?>
<!--in activity_navi_main.xml-->
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".NaviMainActivity">
    <RadioGroup
        android:id="@+id/nav_bar"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:layout_alignParentBottom="true"
        android:background="#FFFFFF"
        android:orientation="horizontal">
        <RadioButton
            android:id="@+id/nav_icon_remind"
            style="@style/nav_item"
            android:drawableTop="@drawable/tab_menu_remind"
            android:text="提醒"/>
        <RadioButton
            android:id="@+id/nav_icon_message"
            style="@style/nav_item"
            android:drawableTop="@drawable/tab_menu_message"
            android:text="消息"/>
    </RadioGroup>

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/nav_bar"/>
</RelativeLayout>

既然 RadioGroup 继承自 LinearLayout,所以他也有 android:orientation 属性,记得加上噢。同时其子控件还可以使用 android:layout_weight
最后进行逻辑部分的完善,来到 NaviMainActivity
其实内容没有多大变化。因为之前用的是 TextView,我们给每个 TextView 设置监听器;这次用的是 RadioGroup,也有给每个RadioButton 设置监听器。不过实现的接口是不同的。

//in NaviMainActivity.java
public class NaviMainActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener{
    private RadioGroup radioGroup;
    private RadioButton remindButton;
    private AnotherFragment f1, f2;
    private FragmentManager manager;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_navi_main);

        manager = getSupportFragmentManager();
        init();
        remindButton.setChecked(true);
    }
    private void init() {
        radioGroup = findViewById(R.id.nav_bar);
        radioGroup.setOnCheckedChangeListener(this);
        remindButton = findViewById(R.id.nav_icon_remind);
    }
    private void hideAllFragment(FragmentTransaction transaction) {
        if (f1 != null) {
            transaction.hide(f1);
        }
        if (f2 != null) {
            transaction.hide(f2);
        }
    }
    @Override
    public void onCheckedChanged(RadioGroup radioGroup, int checkedId) {
        FragmentTransaction transaction = manager.beginTransaction();
        hideAllFragment(transaction);

        switch (checkedId) {
            case R.id.nav_icon_remind:
                if (f1 == null) {
                    f1 = new AnotherFragment("Fragment remind");
                    transaction.add(R.id.fragment_container, f1);
                } else {
                    transaction.show(f1);
                }
                break;
            case R.id.nav_icon_message:
                if (f2 == null) {
                    f2 = new AnotherFragment("Fragment message");
                    transaction.add(R.id.fragment_container, f2);
                } else {
                    transaction.show(f2);
                }
                break;
        }
        transaction.commit();
    }
}

可以看到,使用 RadioGroup 的方式和第一种方式大同小异。一开始模拟点击第一个图标的方式也变为 setChecked(),和 CheckBox 的使用十分类似。

3. ViewPager 实现滑动切换

用到 ViewPager,自然去看看官方文档。结果发现 ViewPager 已经加入 AndroidX豪华大礼包了,文档的代码还是用 support.v4 包;不仅如此,文档还用了 AndroidX 包中已经被弃用的 FragmentStatePagerAdapter 。官方怎么回事啊,行不行啊?(
开个玩笑,其实 ViewPager 用还是可以用,不过由于在2019年官方推出了 ViewPager2,其作为 ViewPager 的升级版,官方当然大力推荐。一不做二不休,这才有了 FragmentStatePagerAdapter 等以前和 ViewPager 配套使用的工具被弃用的情况。

关于 ViewPager、ViewPager2 等不在这里过多描述,有空再写一篇ViewPager的使用,我们就用 ViewPager2 来实现可以滑动切换的底部导航栏
不过,如果单单只使用 ViewPager,能够实现的仅仅是滑动切换 Fragment,因此我们还是要自己写底部导航栏,上面两种方法都可以,这里就用 RadioGroup 了。

资源文件和Fragment

都不需要改动

Activity

Activity的布局变化不大, RadioGroupRadioButton 不用变,我们把 FragmentContainerView 改为 ViewPager2

<?xml version="1.0" encoding="utf-8"?>
<!--in activity_navi_main.xml-->
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".NaviMainActivity">
    <RadioGroup
        android:id="@+id/nav_bar"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:layout_alignParentBottom="true"
        android:background="#FFFFFF"
        android:orientation="horizontal">
        <RadioButton
            android:id="@+id/nav_icon_remind"
            style="@style/nav_item"
            android:drawableTop="@drawable/tab_menu_remind"
            android:text="提醒"/>
        <RadioButton
            android:id="@+id/nav_icon_message"
            style="@style/nav_item"
            android:drawableTop="@drawable/tab_menu_message"
            android:text="消息"/>
    </RadioGroup>

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/nav_bar"/>
</RelativeLayout>

因为我们用的是 ViewPager2,所以得有 Adapter 来加载 ViewPager 每个页面的Fragment。 新建 MyFragmentStateAdapter 并继承 FragmentStateAdapter。当然还有实现他的一些抽象方法。

//in MyFragmentStateAdapter.java
public class MyFragmentStateAdapter extends FragmentStateAdapter {
    public static final int PAGE_NUM = 2;

    public MyFragmentStateAdapter(@NonNull @NotNull FragmentActivity fragmentActivity) {
        super(fragmentActivity);
    }

    @Override
    public Fragment createFragment(int position) {
        Fragment currentFragment = null;
        switch (position) {
            case 0:
                currentFragment = new AnotherFragment("First Fragment");
                break;
            case 1:
                currentFragment = new AnotherFragment("Second Fragment");
                break;
        }
        return currentFragment;
    }

    @Override
    public int getItemCount() {
        return PAGE_NUM;
    }
}

其中,createFragmentgetItemCount 是我们实现的抽象方法。
getItemCount 则返回 Fragment 的总数,因此我们给一个静态常量表示总数。
createFragment 就是 Fragment 的方法。我们根据 position 生成不同的 Fragment,可以依次从左到右滑动。position 的范围就是 getItemCount 返回的值,因此 position 的取值就是0~ getItemCount -1。

此外,继承 FragmentStateAdapter 后还要求我们写一个构造方法,传入一个 FragmentActivity,在这就是我们的 NaviMainActivity。
最后来把 ViewPager 和 Adapter 运用到 Activity 里,来到 NaviMainActivity

public class NaviMainActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener {
    private RadioGroup radioGroup;
    private RadioButton remindButton, messageButton;
    
    private ViewPager2 viewPager;
    private MyFragmentStateAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_navi_main);
        init();
        remindButton.setChecked(true);
    }

    private void init() {
        //bind views
        radioGroup = findViewById(R.id.nav_bar);
        remindButton = findViewById(R.id.nav_icon_remind);
        messageButton = findViewById(R.id.nav_icon_message);
        viewPager = findViewById(R.id.fragment_container_viewpager);

        radioGroup.setOnCheckedChangeListener(this);

        adapter = new MyFragmentStateAdapter(this);
        viewPager.setAdapter(adapter);
        setViewPager();
    }

    @Override
    public void onCheckedChanged(RadioGroup radioGroup, int checkedId) {
        switch (checkedId) {
            case R.id.nav_icon_remind:
                viewPager.setCurrentItem(0);
                break;
            case R.id.nav_icon_message:
                viewPager.setCurrentItem(1);
                break;
        }
    }

    public void setViewPager() {
        viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
            @Override
            public void onPageSelected(int position) {
                switch (position) {
                    case 0:
                        remindButton.setChecked(true);
                        break;
                    case 1:
                        messageButton.setChecked(true);
                        break;
                }
            }
        });
    }
}

首先,因为我们的 Fragment 交给 Adapter来生成了,所以这里就不需要 FragmentManager 了。当然如果涉及到其他操作,比如切换 Fragment 的时候涉及隐藏(hide)Fragment 或者 替换(replace)操作,那还是需要 FragmentManager 的。

然后让我们来看看有哪些变化。绑定布局和设置 RadioGroup 的监听器不变。但是因为点击 RadioButton 的时候 Fragment 要跟着切换,所以调用 viewPager.setCurrentItem(); 来选定当前的 Fragment。传入的0或1就是我们 MyFragmentStateAdapter 里 createFragment() 的0和1,对应不同的 Fragment。 然后简单实现一个 MyFragmentStateAdapter,调用构造方法,传入的就是 this,作为 viewpager 的 Adapter。

除此之外,因为ViewPager自动帮我们实现滑动切换,但切换的仅仅是 Fragment,底部导航栏的图标不会自动切换的,所以这点需要我们自己实现。
也就是调用 viewpager 的 registerOnPageChangeCallback ,实现其中接口。根据当前的 Fragment 的 position,设置对应的 RadioButton 为选中状态

虽然底部导航栏的实现方法挺多,甚至菜鸟教程里还有能在右上角显示小红点的那种,但并不存在一种用法能适应所有情况
比如 ViewPager 实现的滑动切换,虽然方便,但是没有manager管理也是比较麻烦的(而且这只是ViewPager最基础的用法)
甚至会在Activity创建的时候一下把它托管的所有Fragment加载出来,并且一直存在(因为没有hide, stop等)
这也不利于系统的优化,所以还是得看具体看需求的
顺便希望以后有空能写一篇ViewPager2的用法~



About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK