Hello 小伙伴们大家好,我是Krain
先简单介绍一下自己的情况:毕业没多久的社畜小牛犊,目前已工作一年的初级Java(可能还算不上),凭借自己对编程开发的热爱踏上这个卷不死就往死里卷的行业,接下来我将以自己CV战士(新手小白)的身份记录成长历程、督促自己学习、遇见错误记录、加深编码记忆、技术相互交流。
特此声明:本人萌新,文章如提及技术分析均为自己理解,有错还请大佬纠正
上篇我们提到使用BottomNavigationView+FrameLayout来制作app的底部导航栏,有大佬提出BottomNavigationView加载fragment会出现销毁不全面的情况,并建议我们使用FlycoTabLayout,以下是大佬上篇评论中提及的问题贴、以及FlycoTabLayout所在的github地址。
当我去了解这FlycoTabLayout时,看见那堆成山的代码,顿时虎躯一震,直接看GitHub对初出茅庐的小白极度不友好(至少我是这么认为的),但凭借自己强烈的求知欲,查遍了百度,翻遍了贴吧,看光了小破站,终于!我悟了!我研究出来了哈哈!先请各位官爷来看看我做的效果图,之后我们再做分析。
我们先以小白视角去简单理解一下FlycoTabLayout,大家就把它想象成一个书包(第三方库),可以在任何人手里(开源),书包中有3样做导航栏的工具(TabLayout),分别叫作
- SlidingTabLayout 依赖ViewPager,内部实现了fragmentPagerAdapter
- CommonTabLayout 不依赖ViewPager,可与其他控件自由搭配使用,适合做底部导航栏
- SegmentTabLayout 适合做顶部导航栏,类似qq顶部的首页消息切换
不提源码,大概就是这样吧,如果有大佬能说一下这三者的本质区别的话还请评论区补充。
想要使用FlycoTabLayout这个库,先导入以下依赖
implementation 'io.github.h07000223:flycoTabLayout:3.0.0'
我采用xml布局特别简单,一个CommonTabLayout,一个ViewPager足矣,这边有坑:ViewPager需要放在CommonTabLayout上面,不然后面会出现点击导航栏无法切换的情况,因为导航栏被ViewPager覆盖掉了
<androidx.viewpager.widget.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.flyco.tablayout.CommonTabLayout
android:id="@+id/bottomTag"
tl:tl_indicator_color="#2C97DE"
tl:tl_textSelectColor="#2C97DE"
tl:tl_textUnselectColor="#66000000"
tl:tl_underline_color="#DDDDDD"
tl:tl_underline_height="1dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#EDEBEB"
android:paddingTop="5dp"
android:paddingBottom="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
导航栏中的图标文字在Activity中加入,我的图标是在阿里的矢量图库中找的,网址在上篇中有提到。Activity中的操作想必大家经过前几篇已经并不陌生了,初始化嘛,只不过这次东西有点多
private ArrayList<Fragment> mFragments=new ArrayList<>(); //Fragment集合
private ArrayList<Fragment> mFragments2 = new ArrayList<>();
private ArrayList<CustomTabEntity> mTabEntities=new ArrayList<>(); //CustomTabEntity集合
private String[] mTitles = {"首页","视频","消息","我的"}; //标题
private int[] mIconUnselectIds = { //选中时的图标
R.drawable.home, R.drawable.video,
R.drawable.comment, R.drawable.user};
private int[] mIconSelectIds = { //未选中时的图标
R.drawable.home_selected, R.drawable.video_selected,
R.drawable.comment_selected, R.drawable.user_selected};
private ViewPager viewPager; //xml中的viewPager
private CommonTabLayout commonTabLayout; //xml中的CommonTabLayout
有小伙伴可能不知道CustomTabEntity是什么,大家可以点进源码看一下,首先Entity说明它是个实体类,它里面封装了三个方法,一个是得到标题,第二个是得到选中的图标,第三个是得到未选中的图标,这里可以知道我们定义的mTabEntities集合装的就是所有东西的汇总。
到这里,我们就知道逻辑是什么样子了,初始化控件和准备好要加载的数据 -> 处理图标的文字图片按顺序放入集合 -> 加入适配器绑定页面实现页面跟随按钮跳转 -> 分别加入页面、按钮的监听器双向绑定实现滑动切换页面及图标。那么代码不就来了嘛。
public class MainActivity extends AppCompatActivity {
private ArrayList<Fragment> mFragments=new ArrayList<>();
private ArrayList<Fragment> mFragments2 = new ArrayList<>();
private ArrayList<CustomTabEntity> mTabEntities=new ArrayList<>();
private String[] mTitles = {"首页","视频","消息","我的"};
private int[] mIconUnselectIds = { //未选中时的图标数组
R.drawable.home, R.drawable.video,
R.drawable.comment, R.drawable.user};
private int[] mIconSelectIds = { //选中时的图标数组
R.drawable.home_selected, R.drawable.video_selected,
R.drawable.comment_selected, R.drawable.user_selected};
private ViewPager viewPager;
private CommonTabLayout commonTabLayout;
--------------------------------------------以上是初始化------------------------------------------
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (String title:mTitles){ //这里我也不太清楚传这个title有什么用 HomeFragment的代码在下一段
mFragments.add(HomeFragment.getInstance("Switch ViewPager"+title));
mFragments2.add(HomeFragment.getInstance("Switch Fragment"+title));
}
for (int i=0;i<mTitles.length;i++){ //将准备好的选中图标、未选中图标、标题加入集合
mTabEntities.add(new TabEntity(mTitles[i],mIconSelectIds[i],mIconUnselectIds[i]));//TabEntity实体类代码在下下段
}
viewPager=findViewById(R.id.view_pager); //获取xml控件
commonTabLayout=findViewById(R.id.bottomTag); //获取xml控件
viewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager())); //创建对象绑定页面,不知道MyPagerAdapter是什么拉到本段代码最后
-----------------------------------------以下是滑动页面双向监听------------------------------------
commonTabLayout.setOnTabSelectListener(new OnTabSelectListener() { //监听切换按钮
@Override
public void onTabSelect(int position) {
viewPager.setCurrentItem(position);
}
@Override
public void onTabReselect(int position) {
}
});
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { //监听切换页面
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
commonTabLayout.setCurrentTab(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
---------------------------------------------------------------------------------------------
commonTabLayout.setTabData(mTabEntities); //将集合中的图标使用setTabData为CommonTabLayout赋值
}
//以下是绑定页面的内部类 是不是都不知道内部类是干啥的了 来帮大家回忆一下
//内部类的访问特点:
//内部类可以直接访问外部类的成员,包括私有
//外部类要访问内部类的成员,必须创建对象
private class MyPagerAdapter extends FragmentPagerAdapter {
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return mFragments.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mTitles[position];
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
}
}
下面是HomeFragment的代码,其实这个类我也不清楚getInstance()具体起到什么作用,我只是把他当作页面对应的Activity,被MyPagerAdapter适配器绑定,如果有大佬知道还请评论区解释一下。
public class HomeFragment extends Fragment {
private String mTitle;
public static HomeFragment getInstance(String title) {
HomeFragment sf = new HomeFragment();
sf.mTitle = title;
return sf;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false);
}
}
下面是实体类代码
public class TabEntity implements CustomTabEntity {
public String title;
public int selectedIcon;
public int unSelectedIcon;
public TabEntity(String title, int selectedIcon, int unSelectedIcon) {
this.title = title;
this.selectedIcon = selectedIcon;
this.unSelectedIcon = unSelectedIcon;
}
@Override
public String getTabTitle() {
return title;
}
@Override
public int getTabSelectedIcon() {
return selectedIcon;
}
@Override
public int getTabUnselectedIcon() {
return unSelectedIcon;
}
}
代码到这里就能实现我们上面所展示的效果了,可能一开始看有点难理解,接下来是我在做这个底部导航栏时产生的问题,对自己的问题做一个整理
- 什么是FragmentManager,他的作用是什么
- HomeFragment中重写的onCreateView是用来做什么的,
以下是我从网上学习加上自己理解的回答,欢迎补充
-
首先FragmentManager字面意思 Fragment的管理者,那Fragment是什么呢?他是安卓3.0后的一个api,应用场景是为了适应大屏幕的设备,可以理解为小型的Activity,嵌套在Activity中,有生命周期,,并直接受到Activity的影响,简单点说就是Fragment是寄生虫,Activity是被寄生的宿主,我感觉有点像JAVA中的Bean。那么FragmentManager就是统一管理这些Fragment,可以对 Fragment 进行添加,移除,替换等操作,通过getFragmentManager()或getSupportFragmentManager()获得
-
onCreateView是用来加载Fragment的视图,就和onCreate去加载xml布局一个道理,里面有三个参数,LayoutInflater inflater:和findViewById一样找控件的。ViewGroup container:一个View容器。Bundle savedInstanceState:是否保存当前状态,上面一个回答提到Fragment直接受到Activity影响,也就是当Activity进程被嘎了,Fragment也会被嘎,那么这个参数就是:当Activity被嘎了,你是否要Fragment也被嘎掉