上一片文章用的是BottomNavigationView+Menu+Fragment,但是可能有时候需求不一样,menu的样式不太够,所以需要自定义View来实现。
自定义View:实现思路是自定义一个XMl布局,放五个切换的Item,根据需求的样式去绘制。最后加上Fragment的切换,点击监听等等,坏处的话就是自己写的,可能没有封装好的那么完善,很多方法,状态需要自己去写。
直接上代码吧!首先是XML布局代码:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/act_main_bottom_nav_ll" android:layout_width="match_parent" android:layout_height="50dp" android:background="#fff" android:clipChildren="false" android:clipToPadding="false" android:paddingTop="5dp" android:paddingBottom="5dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent"> <LinearLayout android:id="@+id/bottom_bar_home" android:layout_width="0dp" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_weight="1" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toStartOf="@id/bottom_bar_tools" tools:ignore="UseCompoundDrawables"> <ImageView android:id="@+id/bottom_bar_home_icon" android:layout_width="26dp" android:layout_height="26dp" android:layout_marginBottom="4dp" android:contentDescription="@null" android:src="@drawable/gongwenbao" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:includeFontPadding="false" android:text="@string/home" android:textColor="@color/black" android:textSize="8sp" tools:ignore="SmallSp" /> </LinearLayout> <LinearLayout android:id="@+id/bottom_bar_tools" android:layout_width="0dp" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_weight="1" app:layout_constraintEnd_toStartOf="@id/bottom_bar_play" app:layout_constraintStart_toEndOf="@id/bottom_bar_home" tools:ignore="UseCompoundDrawables"> <ImageView android:id="@+id/bottom_bar_tools_icon" android:layout_width="26dp" android:layout_height="26dp" android:layout_marginBottom="4dp" android:contentDescription="@null" android:src="@drawable/zixun" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:includeFontPadding="false" android:text="@string/tools" android:textColor="@color/black" android:textSize="8sp" tools:ignore="SmallSp" /> </LinearLayout> <LinearLayout android:id="@+id/bottom_bar_play" android:layout_width="0dp" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_weight="1" app:layout_constraintStart_toEndOf="@id/bottom_bar_tools" app:layout_constraintEnd_toStartOf="@id/bottom_bar_relax" tools:ignore="UseCompoundDrawables"> <ImageView android:id="@+id/bottom_bar_play_icon" android:layout_width="26dp" android:layout_height="26dp" android:layout_marginBottom="4dp" android:contentDescription="@null" android:src="@drawable/youxi" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:includeFontPadding="false" android:text="@string/play" android:textColor="@color/black" android:textSize="8sp" tools:ignore="SmallSp" /> </LinearLayout> <LinearLayout android:id="@+id/bottom_bar_relax" android:layout_width="0dp" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_weight="1" app:layout_constraintStart_toEndOf="@id/bottom_bar_play" app:layout_constraintEnd_toStartOf="@id/bottom_bar_mine" tools:ignore="UseCompoundDrawables"> <ImageView android:layout_width="26dp" android:layout_height="26dp" android:layout_marginBottom="4dp" android:contentDescription="@null" android:src="@drawable/yundong" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:includeFontPadding="false" android:text="@string/video" android:textColor="@color/black" android:textSize="8sp" tools:ignore="SmallSp" /> </LinearLayout> <LinearLayout android:id="@+id/bottom_bar_mine" android:layout_width="0dp" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_weight="1" app:layout_constraintStart_toEndOf="@id/bottom_bar_relax" app:layout_constraintEnd_toEndOf="parent" tools:ignore="UseCompoundDrawables"> <ImageView android:id="@+id/bottom_bar_mine_icon" android:layout_width="26dp" android:layout_height="26dp" android:layout_marginBottom="4dp" android:contentDescription="@null" android:src="@drawable/yonghu" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:includeFontPadding="false" android:text="@string/mine" android:textColor="@color/black" android:textSize="8sp" tools:ignore="SmallSp" /> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout> 展示出来的效果是: 就是写自定义View: 主要就是绑定布局,为每一个Item添加点击事件,这儿我只做了简单的点击回调 package com.example.lxview.base.widget import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater import android.view.View import android.widget.LinearLayout import androidx.constraintlayout.widget.ConstraintLayout import com.example.lxview.R class MainBottomNavBar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ConstraintLayout(context, attrs, defStyleAttr) { private lateinit var videoContainer: ConstraintLayout private lateinit var navHome: LinearLayout private lateinit var navTools: LinearLayout private lateinit var navPlay: LinearLayout private lateinit var navMe: LinearLayout private lateinit var navRelax: LinearLayout var listener:ResultClick?=null init { initView() } private fun initView() { View.inflate(context, R.layout.main_act_bottom_nav_bar, this) videoContainer = findViewById(R.id.act_main_bottom_nav_ll) navHome = findViewById(R.id.bottom_bar_home) navTools = findViewById(R.id.bottom_bar_tools) navPlay = findViewById(R.id.bottom_bar_play) navMe = findViewById(R.id.bottom_bar_mine) navRelax = findViewById(R.id.bottom_bar_relax) navHome.setOnClickListener { listener?.click(0) } navTools.setOnClickListener { listener?.click(1) } navPlay.setOnClickListener { listener?.click(2) } navRelax.setOnClickListener { listener?.click(3) } navMe.setOnClickListener { listener?.click(4) } } } interface ResultClick{ fun click(int: Int) } 第三步,在展示的Activity 的 xml文件里引用此布局,上面的FrameLayout用来放置fragment,底部放导航栏。 <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/main_cl" android:background="#f7f7f7" xmlns:app="http://schemas.android.com/apk/res-auto"> <FrameLayout android:id="@+id/fragment_container" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toTopOf="@id/navigation" /> <com.example.lxview.base.widget.MainBottomNavBar android:id="@+id/navigation" android:layout_width="match_parent" android:layout_height="50dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@id/fragment_container" app:layout_constraintStart_toStartOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> 第4步:在Activity代码里加上逻辑,继承ResultClick接口,复写click方法,用replaceFragment实现每次点击item时切换fragment package com.example.lxview import android.view.MotionEvent import android.widget.FrameLayout import com.example.lxview.base.activity.BaseActivity import com.example.lxview.base.fragment.BaseFragment import com.example.lxview.base.widget.MainBottomNavBar import com.example.lxview.home.fragment.* import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentTransaction import com.example.lxview.base.widget.ResultClick /** * author: 李 祥 * date: 2022/3/31 1:57 下午 * description: */ class MainActivity : BaseActivity(),ResultClick{ override val contentId: Int get() = R.layout.activity_main private var indicatorView: MainBottomNavBar? = null private lateinit var fragments: Array<BaseFragment> private lateinit var curSelectId: String private lateinit var homeFragment: HomeFragment private lateinit var toolsFragment: ToolsFragment private lateinit var relaxFragment: RelaxFragment private lateinit var meFragment: MineFragment private lateinit var playFragment: PlayFragment private lateinit var container: FrameLayout override fun initView() { indicatorView = findViewById(R.id.navigation) indicatorView?.listener = this container = findViewById(R.id.fragment_container) homeFragment = HomeFragment() toolsFragment = ToolsFragment() relaxFragment = RelaxFragment() meFragment = MineFragment() playFragment = PlayFragment() curSelectId = homeFragment.id.toString() fragments = arrayOf(homeFragment, toolsFragment, playFragment, relaxFragment, meFragment) fragments.forEach { addFragment(it,it.tag.toString()) hideFragment(it) } showFragment(fragments[0]) } //添加Fragment到FragmentList中 private fun addFragment(fragment: Fragment, tag: String) { val fragmentManager = supportFragmentManager val transaction: FragmentTransaction = fragmentManager.beginTransaction() transaction.add(R.id.fragment_container, fragment, tag) transaction.commit() } // 清空fragmentList的所有Fragment,替换成新的Fragment,注意Fragment里面的坑 private fun replaceFragment(fragment: Fragment, tag: String) { val fragmentManager = supportFragmentManager val transaction: FragmentTransaction = fragmentManager.beginTransaction() transaction.replace(R.id.fragment_container, fragment, tag) transaction.commit() } //移除指定的Fragment private fun removeFragment(fragment: Fragment) { val fragmentManager = supportFragmentManager val transaction: FragmentTransaction = fragmentManager.beginTransaction() transaction.remove(fragment) transaction.commit() } //把Fragment设置成显示状态,但是并没有添加到FragmentList中 private fun showFragment(fragment: Fragment) { val fragmentManager = supportFragmentManager val transaction: FragmentTransaction = fragmentManager.beginTransaction() transaction.show(fragment) transaction.commit() } //把Fragment设置成显示状态,但是并没有添加到FragmentList中 private fun hideFragment(fragment: Fragment) { val fragmentManager = supportFragmentManager val transaction: FragmentTransaction = fragmentManager.beginTransaction() transaction.hide(fragment) transaction.commit() } // 效果和show相近,创建视图,添加到containerid指定的Added列表,FragmentList依然保留,但是会引起生命周期的变化 private fun attachFragment(fragment: Fragment) { val fragmentManager = supportFragmentManager val transaction: FragmentTransaction = fragmentManager.beginTransaction() transaction.attach(fragment) transaction.commit() } // 效果和hide相近,清除视图,从containerid指定的Added列表移除,FragmentList依然保留,但是会引起生命周期的变化 private fun detachFragment(fragment: Fragment) { val fragmentManager = supportFragmentManager val transaction: FragmentTransaction = fragmentManager.beginTransaction() transaction.detach(fragment) transaction.commit() } override fun click(int: Int) {//实现fragment切换 replaceFragment(fragments[int],fragments[int].tag.toString()) } override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { if (ev?.action ==MotionEvent.BUTTON_BACK){ application.onTerminate() } return super.dispatchTouchEvent(ev) } override fun onTouchEvent(event: MotionEvent?): Boolean { if (event?.action ==MotionEvent.BUTTON_BACK){ application.onTerminate() } return super.onTouchEvent(event) } }
总结:我这上面只实现了简单的点击切换,用自定义View来实现底部导航栏,很自由,可以实现一些稀奇古怪的需求,但是很多方法的回调,切换时的动画,就需要自己去实现,可能会相对复杂一些。所以若是普通的导航栏,还是用Android自带的组件去实现比较好!
eg:使用replaceFragment,会清空掉当前的fragmnet列表,每次都是重新创建fragment,所以在一些界面的状态保存上就会有问题,所以也可以使用hide和show方法来进行点击切换,且不会销毁掉fragment