Android 底部导航栏(三、ViewPager+TabLayout+Fragment)简单易懂

简介: 底部导航栏在Android应用中随处可见,今天使用ViewPager+TabLayout+Fragment这三个控件来实现此功能。前面使用了另外两个方法来实现导航栏,不过我还是更喜欢Viewpager,代码也少,毕竟前两个不能左右滑动。

底部导航栏在Android应用中随处可见,今天使用ViewPager+TabLayout+Fragment这三个控件来实现此功能。


前面使用了另外两个方法来实现导航栏,不过我还是更喜欢Viewpager,代码也少,毕竟前两个不能左右滑动。



现在常用的App主页都会有一个底部导航栏,根据需求也使用过好几种方法进行实现,于是想着还是总结一下,今天还写一个简单的BottomNavigationView方法来实现这个功能它是android原生的一个底部导航框架,一般和Fragment一起使用。xml布局:最外层的layout不用管,那是databinding框架的根布局,主要的布局FrameLayout来装载fragment列表,BottomNavigationView实现底部导航栏,最后监听绑定实现点击切换其中BottomNavigation


总结:我这上面只实现了简单的点击切换,用自定义View来实现底部导航栏,很自由,可以实现一些稀奇古怪的需求,但是很多方法的回调,切换时的动画,就需要自己去实现,可能会相对复杂一些。最后加上Fragment的切换,点击监听等等,坏处的话就是自己写的,可能没有封装好的那么完善,很多方法,状态需要自己去写。上一片文章用的是BottomNavigationView+Menu+Fragment,但是可能有时候需求不一样,menu的样式不太够,所以需要自定义View来实现。

https://blog.csdn.net/LoveFHM/article/details/127651226?spm=1001.2014.3001.5501


一、基本介绍


ViewPager:Android 3.0后引入的一个UI控件——ViewPager(视图滑动切换工具),Viewpager使用起来就是我们通过创建adapter给它填充多个view,左右滑动时,切换不同的view。Google官方是建议我们使用Fragment来填充ViewPager的,这样 可以更加方便的生成每个Page,以及管理每个Page的生命周期。可以用来主页/模块切换,图片轮播,新手引导等等。


ViewPager 全面总结_淡然一笑、的博客-CSDN博客_viewpager


TabLayout:TabLayout提供了一个水平布局用于展示tabs,继承自HorizontalScrollView。TabLayout一般结合ViewPager+Fragment的使用实现滑动的标签选择器。


Android控件-TabLayout使用介绍_Android开发猿的博客-CSDN博客_tablayout


Fragment:Fragment可以说是轻量级的Activity,是Android3.0新增的概念。这个就很基础了,不给链接了。


二、实现原理


Fragment用于承载和展示内容,Viewpager用于界面的切换,TabLayout用于展示导航栏和点击事件通知ViewPager切换页面。


三、实现过程

第一步:先拉界面哦


<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        android:orientation="vertical">
        <androidx.appcompat.widget.AppCompatTextView//只是个文案
            android:id="@+id/nav_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:gravity="center"
            android:padding="4dp"
            android:text="ViewPager+TabLayout+Fragment"
            android:textColor="@color/black"
            android:textSize="18sp"
            android:textStyle="bold" />
        <androidx.viewpager.widget.ViewPager //承载内容
            android:id="@+id/fragment_container_viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_above="@id/nav_tablayout"
            android:layout_below="@id/nav_text" />
        <View
            android:layout_width="match_parent"//只是个分割线
            android:layout_height="0.3dp"
            android:layout_above="@id/nav_tablayout"
            android:background="@color/tab_color_false"/>
        <com.google.android.material.tabs.TabLayout
            android:id="@+id/nav_tablayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:background="@color/white"
            app:tabBackground="@color/white"
            app:tabIndicatorAnimationMode="elastic"
            app:tabIndicatorColor="@color/blue"
            app:tabIndicatorFullWidth="false"
            app:tabIndicatorGravity="bottom"
            app:tabMode="fixed"
            app:tabRippleColor="@android:color/transparent"
            app:tabSelectedTextColor="@color/black"
            app:tabTextColor="@color/bar_grey"
            tools:layout_height="50dp"
        />
    </RelativeLayout>
</layout>
第二步:业务代码,就是初始化这两个控件,tablayout初始化item,设置属性,使用tabLayout.setupWithViewPager(viewPager)绑定viewpager,然后往Viewpager里面加fragment列表,然后给viewpager设置fragment。(kotlin实现,如果你是java代码,拷进项目它会自动转码为java,布局绑定用的databinding,没用过看核心代码就行了)
class NavViewPagerTabActivity : BaseBindActivity<NavViewpagerTablayoutActivityLayoutBinding>() {
    override val layout: Int
        get() = R.layout.nav_viewpager_tablayout_activity_layout
    private lateinit var homeFragment: HomeFragment
    private lateinit var toolsFragment: ToolsFragment
    private lateinit var relaxFragment: RelaxFragment
    private lateinit var viewPager: ViewPager
    private lateinit var tabLayout: TabLayout
    private lateinit var meFragment: MineFragment
    private lateinit var playFragment: PlayFragment
    private lateinit var fragments: Array<BaseFragment>
    private val titles = arrayOf("首页", "视频", "用户")
    override fun initView() {
        initFragment()//初始化fragment列表
        viewPager = mBinding.fragmentContainerViewpager
        tabLayout = mBinding.navTablayout
        tabLayout.setupWithViewPager(viewPager)//绑定viewpager
//        for (element in titles) tabLayout.addTab(tabLayout.newTab().setText(element))
        viewPager.adapter = object :FragmentPagerAdapter(supportFragmentManager,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT){
            override fun getCount(): Int {
                return fragments.size
            }
            override fun getItem(position: Int): Fragment {
                return fragments[position]
            }
            override fun getPageTitle(position: Int): CharSequence {
                return titles[position]
            }
        }
    }
    private fun initFragment() {
        homeFragment = HomeFragment()
        toolsFragment = ToolsFragment()
        relaxFragment = RelaxFragment()
        meFragment = MineFragment()
        playFragment = PlayFragment()
//        fragments = arrayOf(homeFragment, toolsFragment, playFragment, relaxFragment, meFragment)
        fragments = arrayOf(homeFragment, toolsFragment, meFragment)
    }
}
eg:可以看到我把上面的这行代码注释,但是运行出来,tab是添加上了,为什么呢?
// for (element in titles) tabLayout.addTab(tabLayout.newTab())
原因很简单,是在tabLayout.setupWithViewPager的时候,TabLayout中先将所有tabs remove了,然后取的PagerAdapter中的getPageTitle返回值添加的tab。就是在给ViewPager设置Adapter的时候,一定要重写getPageTitle(int position)方法,不然TabLayout中的标签是看不到的,即使在addTab时newTab().setText(tabs[i])也没用。
这样就简单的实现了导航栏!可以单独为viewpager和tablayout添加监听事件,并且绑定也只需要一行代码,可以实现左右滑动切换界面,当然,如果想禁止左右滑的触摸事件也可以对viewpager事件作拦截。关于viewpager和tablayout,他们还有很多属性可以去配置,如果想详细了解的可以去看上面的两个链接去学习一下!
实现效果:
进阶知识点一:
为tablayout添加监听事件:
tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabSelected(tab: TabLayout.Tab?) {
                TODO("Not yet implemented")
            }
            override fun onTabUnselected(tab: TabLayout.Tab?) {
                TODO("Not yet implemented")
            }
            override fun onTabReselected(tab: TabLayout.Tab?) {
                TODO("Not yet implemented")
            }
        })
 为ViewPager添加监听事件:
viewPager?.setOnPageChangeListener(object : ViewPager.OnPageChangeListener {
            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
            }
            override fun onPageSelected(position: Int) {
            }
            override fun onPageScrollStateChanged(state: Int) {
            }
        })

进阶知识点二:


为TabLayout设置自定义View,有时候底部导航栏可能不只是文字,他可能是图片和文字混合,可能点击后有图片,文字的大小变化,那么就需要为其单独配置view,并在监听事件里重新绘制界面。


这儿我展示一份项目里的代码:


override fun onTabSelected(tab: TabLayout.Tab) {
                viewPager?.currentItem = tab.position
                val customView = tab.customView
                if (tab.position == 0) {//如果是第一个tab
                    if (customView == null) tab.setCustomView(R.layout.tab_text_layout)//设置布局
                    val textView = customView?.findViewById<TextView>(R.id.text1)
                    textView?.text = titles[tab.position].i18n()
                    textView?.setTextAppearance(requireContext(), R.style.TabLayoutTextSelected)//设置点击后的文本格式
                    val point = customView?.findViewById<TextView>(R.id.point_1)//这是tab右上角的红点
                    val layoutParams = RelativeLayout.LayoutParams(SizeUtils.dp2px(8f), SizeUtils.dp2px(8f))
                    layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP)
                    layoutParams.addRule(RelativeLayout.END_OF, R.id.text1)
                    layoutParams.setMargins(5, 0, 0, 0)
                    point?.layoutParams = layoutParams
                } else {
                    if (customView == null) tab.setCustomView(R.layout.tab_text_layout2)
                    val textView = customView?.findViewById<TextView>(R.id.tab_text2)
                    textView?.text = titles[tab.position].i18n()
                    textView?.setTextAppearance(requireContext(), R.style.TabLayoutTextSelected)
                }
            }


相关文章
|
2月前
|
缓存 前端开发 Android开发
Android实战之如何截取Activity或者Fragment的内容?
本文首发于公众号“AntDream”,介绍了如何在Android中截取Activity或Fragment的屏幕内容并保存为图片。包括截取整个Activity、特定控件或区域的方法,以及处理包含RecyclerView的复杂情况。
27 3
|
6月前
|
Android开发 容器
35. 【Android教程】视频页面:ViewPager
35. 【Android教程】视频页面:ViewPager
64 3
|
4月前
|
Android开发
Android使用ViewPager做无限轮播,人为滑动时停止
Android使用ViewPager做无限轮播,人为滑动时停止
92 2
|
7月前
|
XML 存储 Android开发
Android技能树 — Fragment总体小结,2024年最新腾讯面试gm
Android技能树 — Fragment总体小结,2024年最新腾讯面试gm
|
7月前
|
Android开发
Android使用ViewPager实现图片轮播系列之三:手动滑动 + 左右箭头(1)
Android使用ViewPager实现图片轮播系列之三:手动滑动 + 左右箭头(1)
|
7月前
|
XML Android开发 数据格式
Fragment的使用,零基础入门android逆向视频课程
Fragment的使用,零基础入门android逆向视频课程
|
Android开发 容器
Android小技巧之无限循环的ViewPager
前言 之所以会写着篇文章的原因是我现在项目用运用到了广告轮播(BannerView),当时在赶项目的时候在github上面找到了符合的开 源库 就直接引用了,但是该开源库稍微有点庞大,功能比较繁多。
1203 0
|
19天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
40 19