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

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

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

自定义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

猜你喜欢

转载自blog.csdn.net/LoveFHM/article/details/127651226
今日推荐