• Android 底部导航栏(二、BottomNavigationView+自定义View+Fragment)


    上一片文章用的是BottomNavigationView+Menu+Fragment,但是可能有时候需求不一样,menu的样式不太够,所以需要自定义View来实现。

    Android 底部导航栏(一、BottomNavigationView+Menu+Fragment)_&岁月不待人&的博客-CSDN博客_android 底部导航栏

    自定义View:实现思路是自定义一个XMl布局,放五个切换的Item,根据需求的样式去绘制。最后加上Fragment的切换,点击监听等等,坏处的话就是自己写的,可能没有封装好的那么完善,很多方法,状态需要自己去写。

    直接上代码吧!首先是XML布局代码:

    1. "1.0" encoding="utf-8"?>
    2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. xmlns:app="http://schemas.android.com/apk/res-auto"
    4. xmlns:tools="http://schemas.android.com/tools"
    5. android:id="@+id/act_main_bottom_nav_ll"
    6. android:layout_width="match_parent"
    7. android:layout_height="50dp"
    8. android:background="#fff"
    9. android:clipChildren="false"
    10. android:clipToPadding="false"
    11. android:paddingTop="5dp"
    12. android:paddingBottom="5dp"
    13. app:layout_constraintBottom_toBottomOf="parent"
    14. app:layout_constraintStart_toStartOf="parent">
    15. <LinearLayout
    16. android:id="@+id/bottom_bar_home"
    17. android:layout_width="0dp"
    18. android:layout_height="wrap_content"
    19. android:gravity="center"
    20. android:orientation="vertical"
    21. app:layout_constraintBottom_toBottomOf="parent"
    22. app:layout_constraintHorizontal_weight="1"
    23. app:layout_constraintStart_toStartOf="parent"
    24. app:layout_constraintEnd_toStartOf="@id/bottom_bar_tools"
    25. tools:ignore="UseCompoundDrawables">
    26. <ImageView
    27. android:id="@+id/bottom_bar_home_icon"
    28. android:layout_width="26dp"
    29. android:layout_height="26dp"
    30. android:layout_marginBottom="4dp"
    31. android:contentDescription="@null"
    32. android:src="@drawable/gongwenbao" />
    33. <TextView
    34. android:layout_width="wrap_content"
    35. android:layout_height="wrap_content"
    36. android:includeFontPadding="false"
    37. android:text="@string/home"
    38. android:textColor="@color/black"
    39. android:textSize="8sp"
    40. tools:ignore="SmallSp" />
    41. LinearLayout>
    42. <LinearLayout
    43. android:id="@+id/bottom_bar_tools"
    44. android:layout_width="0dp"
    45. android:layout_height="wrap_content"
    46. android:gravity="center"
    47. android:orientation="vertical"
    48. app:layout_constraintBottom_toBottomOf="parent"
    49. app:layout_constraintHorizontal_weight="1"
    50. app:layout_constraintEnd_toStartOf="@id/bottom_bar_play"
    51. app:layout_constraintStart_toEndOf="@id/bottom_bar_home"
    52. tools:ignore="UseCompoundDrawables">
    53. <ImageView
    54. android:id="@+id/bottom_bar_tools_icon"
    55. android:layout_width="26dp"
    56. android:layout_height="26dp"
    57. android:layout_marginBottom="4dp"
    58. android:contentDescription="@null"
    59. android:src="@drawable/zixun" />
    60. <TextView
    61. android:layout_width="wrap_content"
    62. android:layout_height="wrap_content"
    63. android:includeFontPadding="false"
    64. android:text="@string/tools"
    65. android:textColor="@color/black"
    66. android:textSize="8sp"
    67. tools:ignore="SmallSp" />
    68. LinearLayout>
    69. <LinearLayout
    70. android:id="@+id/bottom_bar_play"
    71. android:layout_width="0dp"
    72. android:layout_height="wrap_content"
    73. android:gravity="center"
    74. android:orientation="vertical"
    75. app:layout_constraintBottom_toBottomOf="parent"
    76. app:layout_constraintHorizontal_weight="1"
    77. app:layout_constraintStart_toEndOf="@id/bottom_bar_tools"
    78. app:layout_constraintEnd_toStartOf="@id/bottom_bar_relax"
    79. tools:ignore="UseCompoundDrawables">
    80. <ImageView
    81. android:id="@+id/bottom_bar_play_icon"
    82. android:layout_width="26dp"
    83. android:layout_height="26dp"
    84. android:layout_marginBottom="4dp"
    85. android:contentDescription="@null"
    86. android:src="@drawable/youxi" />
    87. <TextView
    88. android:layout_width="wrap_content"
    89. android:layout_height="wrap_content"
    90. android:includeFontPadding="false"
    91. android:text="@string/play"
    92. android:textColor="@color/black"
    93. android:textSize="8sp"
    94. tools:ignore="SmallSp" />
    95. LinearLayout>
    96. <LinearLayout
    97. android:id="@+id/bottom_bar_relax"
    98. android:layout_width="0dp"
    99. android:layout_height="wrap_content"
    100. android:gravity="center"
    101. android:orientation="vertical"
    102. app:layout_constraintBottom_toBottomOf="parent"
    103. app:layout_constraintHorizontal_weight="1"
    104. app:layout_constraintStart_toEndOf="@id/bottom_bar_play"
    105. app:layout_constraintEnd_toStartOf="@id/bottom_bar_mine"
    106. tools:ignore="UseCompoundDrawables">
    107. <ImageView
    108. android:layout_width="26dp"
    109. android:layout_height="26dp"
    110. android:layout_marginBottom="4dp"
    111. android:contentDescription="@null"
    112. android:src="@drawable/yundong" />
    113. <TextView
    114. android:layout_width="wrap_content"
    115. android:layout_height="wrap_content"
    116. android:includeFontPadding="false"
    117. android:text="@string/video"
    118. android:textColor="@color/black"
    119. android:textSize="8sp"
    120. tools:ignore="SmallSp" />
    121. LinearLayout>
    122. <LinearLayout
    123. android:id="@+id/bottom_bar_mine"
    124. android:layout_width="0dp"
    125. android:layout_height="wrap_content"
    126. android:gravity="center"
    127. android:orientation="vertical"
    128. app:layout_constraintBottom_toBottomOf="parent"
    129. app:layout_constraintHorizontal_weight="1"
    130. app:layout_constraintStart_toEndOf="@id/bottom_bar_relax"
    131. app:layout_constraintEnd_toEndOf="parent"
    132. tools:ignore="UseCompoundDrawables">
    133. <ImageView
    134. android:id="@+id/bottom_bar_mine_icon"
    135. android:layout_width="26dp"
    136. android:layout_height="26dp"
    137. android:layout_marginBottom="4dp"
    138. android:contentDescription="@null"
    139. android:src="@drawable/yonghu" />
    140. <TextView
    141. android:layout_width="wrap_content"
    142. android:layout_height="wrap_content"
    143. android:includeFontPadding="false"
    144. android:text="@string/mine"
    145. android:textColor="@color/black"
    146. android:textSize="8sp"
    147. tools:ignore="SmallSp" />
    148. LinearLayout>
    149. androidx.constraintlayout.widget.ConstraintLayout>

    展示出来的效果是:

     就是写自定义View:

    主要就是绑定布局,为每一个Item添加点击事件,这儿我只做了简单的点击回调

    1. package com.example.lxview.base.widget
    2. import android.content.Context
    3. import android.util.AttributeSet
    4. import android.view.LayoutInflater
    5. import android.view.View
    6. import android.widget.LinearLayout
    7. import androidx.constraintlayout.widget.ConstraintLayout
    8. import com.example.lxview.R
    9. class MainBottomNavBar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ConstraintLayout(context, attrs, defStyleAttr) {
    10. private lateinit var videoContainer: ConstraintLayout
    11. private lateinit var navHome: LinearLayout
    12. private lateinit var navTools: LinearLayout
    13. private lateinit var navPlay: LinearLayout
    14. private lateinit var navMe: LinearLayout
    15. private lateinit var navRelax: LinearLayout
    16. var listener:ResultClick?=null
    17. init {
    18. initView()
    19. }
    20. private fun initView() {
    21. View.inflate(context, R.layout.main_act_bottom_nav_bar, this)
    22. videoContainer = findViewById(R.id.act_main_bottom_nav_ll)
    23. navHome = findViewById(R.id.bottom_bar_home)
    24. navTools = findViewById(R.id.bottom_bar_tools)
    25. navPlay = findViewById(R.id.bottom_bar_play)
    26. navMe = findViewById(R.id.bottom_bar_mine)
    27. navRelax = findViewById(R.id.bottom_bar_relax)
    28. navHome.setOnClickListener {
    29. listener?.click(0)
    30. }
    31. navTools.setOnClickListener {
    32. listener?.click(1)
    33. }
    34. navPlay.setOnClickListener {
    35. listener?.click(2)
    36. }
    37. navRelax.setOnClickListener {
    38. listener?.click(3)
    39. }
    40. navMe.setOnClickListener {
    41. listener?.click(4)
    42. }
    43. }
    44. }
    45. interface ResultClick{
    46. fun click(int: Int)
    47. }

    第三步,在展示的Activity 的 xml文件里引用此布局,上面的FrameLayout用来放置fragment,底部放导航栏。

    1. "1.0" encoding="utf-8"?>
    2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:layout_width="match_parent"
    4. android:layout_height="match_parent"
    5. android:id="@+id/main_cl"
    6. android:background="#f7f7f7"
    7. xmlns:app="http://schemas.android.com/apk/res-auto">
    8. <FrameLayout
    9. android:id="@+id/fragment_container"
    10. android:layout_width="match_parent"
    11. android:layout_height="0dp"
    12. app:layout_constraintEnd_toEndOf="parent"
    13. app:layout_constraintStart_toStartOf="parent"
    14. app:layout_constraintTop_toTopOf="parent"
    15. app:layout_constraintBottom_toTopOf="@id/navigation"
    16. />
    17. <com.example.lxview.base.widget.MainBottomNavBar
    18. android:id="@+id/navigation"
    19. android:layout_width="match_parent"
    20. android:layout_height="50dp"
    21. app:layout_constraintBottom_toBottomOf="parent"
    22. app:layout_constraintTop_toBottomOf="@id/fragment_container"
    23. app:layout_constraintStart_toStartOf="parent"
    24. />
    25. androidx.constraintlayout.widget.ConstraintLayout>

    第4步:在Activity代码里加上逻辑,继承ResultClick接口,复写click方法,用replaceFragment实现每次点击item时切换fragment

    1. package com.example.lxview
    2. import android.view.MotionEvent
    3. import android.widget.FrameLayout
    4. import com.example.lxview.base.activity.BaseActivity
    5. import com.example.lxview.base.fragment.BaseFragment
    6. import com.example.lxview.base.widget.MainBottomNavBar
    7. import com.example.lxview.home.fragment.*
    8. import androidx.fragment.app.Fragment
    9. import androidx.fragment.app.FragmentTransaction
    10. import com.example.lxview.base.widget.ResultClick
    11. /**
    12. * author: 李 祥
    13. * date: 2022/3/31 1:57 下午
    14. * description:
    15. */
    16. class MainActivity : BaseActivity(),ResultClick{
    17. override val contentId: Int
    18. get() = R.layout.activity_main
    19. private var indicatorView: MainBottomNavBar? = null
    20. private lateinit var fragments: Array
    21. private lateinit var curSelectId: String
    22. private lateinit var homeFragment: HomeFragment
    23. private lateinit var toolsFragment: ToolsFragment
    24. private lateinit var relaxFragment: RelaxFragment
    25. private lateinit var meFragment: MineFragment
    26. private lateinit var playFragment: PlayFragment
    27. private lateinit var container: FrameLayout
    28. override fun initView() {
    29. indicatorView = findViewById(R.id.navigation)
    30. indicatorView?.listener = this
    31. container = findViewById(R.id.fragment_container)
    32. homeFragment = HomeFragment()
    33. toolsFragment = ToolsFragment()
    34. relaxFragment = RelaxFragment()
    35. meFragment = MineFragment()
    36. playFragment = PlayFragment()
    37. curSelectId = homeFragment.id.toString()
    38. fragments = arrayOf(homeFragment, toolsFragment, playFragment, relaxFragment, meFragment)
    39. fragments.forEach {
    40. addFragment(it,it.tag.toString())
    41. hideFragment(it)
    42. }
    43. showFragment(fragments[0])
    44. }
    45. //添加Fragment到FragmentList中
    46. private fun addFragment(fragment: Fragment, tag: String) {
    47. val fragmentManager = supportFragmentManager
    48. val transaction: FragmentTransaction = fragmentManager.beginTransaction()
    49. transaction.add(R.id.fragment_container, fragment, tag)
    50. transaction.commit()
    51. }
    52. // 清空fragmentList的所有Fragment,替换成新的Fragment,注意Fragment里面的坑
    53. private fun replaceFragment(fragment: Fragment, tag: String) {
    54. val fragmentManager = supportFragmentManager
    55. val transaction: FragmentTransaction = fragmentManager.beginTransaction()
    56. transaction.replace(R.id.fragment_container, fragment, tag)
    57. transaction.commit()
    58. }
    59. //移除指定的Fragment
    60. private fun removeFragment(fragment: Fragment) {
    61. val fragmentManager = supportFragmentManager
    62. val transaction: FragmentTransaction = fragmentManager.beginTransaction()
    63. transaction.remove(fragment)
    64. transaction.commit()
    65. }
    66. //把Fragment设置成显示状态,但是并没有添加到FragmentList中
    67. private fun showFragment(fragment: Fragment) {
    68. val fragmentManager = supportFragmentManager
    69. val transaction: FragmentTransaction = fragmentManager.beginTransaction()
    70. transaction.show(fragment)
    71. transaction.commit()
    72. }
    73. //把Fragment设置成显示状态,但是并没有添加到FragmentList中
    74. private fun hideFragment(fragment: Fragment) {
    75. val fragmentManager = supportFragmentManager
    76. val transaction: FragmentTransaction = fragmentManager.beginTransaction()
    77. transaction.hide(fragment)
    78. transaction.commit()
    79. }
    80. // 效果和show相近,创建视图,添加到containerid指定的Added列表,FragmentList依然保留,但是会引起生命周期的变化
    81. private fun attachFragment(fragment: Fragment) {
    82. val fragmentManager = supportFragmentManager
    83. val transaction: FragmentTransaction = fragmentManager.beginTransaction()
    84. transaction.attach(fragment)
    85. transaction.commit()
    86. }
    87. // 效果和hide相近,清除视图,从containerid指定的Added列表移除,FragmentList依然保留,但是会引起生命周期的变化
    88. private fun detachFragment(fragment: Fragment) {
    89. val fragmentManager = supportFragmentManager
    90. val transaction: FragmentTransaction = fragmentManager.beginTransaction()
    91. transaction.detach(fragment)
    92. transaction.commit()
    93. }
    94. override fun click(int: Int) {//实现fragment切换
    95. replaceFragment(fragments[int],fragments[int].tag.toString())
    96. }
    97. override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
    98. if (ev?.action ==MotionEvent.BUTTON_BACK){
    99. application.onTerminate()
    100. }
    101. return super.dispatchTouchEvent(ev)
    102. }
    103. override fun onTouchEvent(event: MotionEvent?): Boolean {
    104. if (event?.action ==MotionEvent.BUTTON_BACK){
    105. application.onTerminate()
    106. }
    107. return super.onTouchEvent(event)
    108. }
    109. }

    总结:我这上面只实现了简单的点击切换,用自定义View来实现底部导航栏,很自由,可以实现一些稀奇古怪的需求,但是很多方法的回调,切换时的动画,就需要自己去实现,可能会相对复杂一些。所以若是普通的导航栏,还是用Android自带的组件去实现比较好!

    eg:使用replaceFragment,会清空掉当前的fragmnet列表,每次都是重新创建fragment,所以在一些界面的状态保存上就会有问题,所以也可以使用hide和show方法来进行点击切换,且不会销毁掉fragment

  • 相关阅读:
    java计算机毕业设计高校实习管理平台系统源码+mysql数据库+系统+lw文档+部署
    【C语言】VS实⽤调试技巧&(Debug和Release)监视&内存
    node.js-连接准备
    Qt生成PDF报告
    STM32之DMA
    C++ 学习(19)STL - list容器、set容器
    牛客刷题——剑指offer(第四期)
    LeetCode-组合总和 II
    Redis 中的原子操作(3)-使用Redis实现分布式锁
    Herodotus——无需bridge借助Storage proof实现的以太坊跨层数据访问
  • 原文地址:https://blog.csdn.net/LoveFHM/article/details/127651226