BottomNavigationBar 数据源配置拓展函数设置

55 阅读4分钟
package cn.eeepay.agent_home.view

import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.*
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import cn.eeepay.agent_home.adapter.FragmentAdapter
import cn.eeepay.agent_home.view.EditConfig.DESIGN_WIDTH
import cn.eeepay.lib_core.base.BaseApplication.Companion.application
import cn.eeepay.lib_core.utils.MMKVUtils.clearAll
import cn.eeepay.lib_data.constants.EditConfig
import com.ashokvarma.bottomnavigation.BottomNavigationBar
import com.ashokvarma.bottomnavigation.BottomNavigationItem
import java.util.*

/*
  * ================================================
  * 描述:bottomNavigationBar 数据源配置拓展函数
  * 作者:zhuangzeqin
  * 时间: 2025/5/22 14:32
  * 邮箱:zzq@eeepay.cn
  * 备注:
  * ----------------------------------------------------------------
  * You never know what you can do until you try !
  *      _              _           _     _   ____  _             _ _
  *     / \   _ __   __| |_ __ ___ (_) __| | / ___|| |_ _   _  __| (_) ___
  *    / _ \ | '_ \ / _` | '__/ _ | |/ _` | ___ | __| | | |/ _` | |/ _ \
  *   / ___ | | | | (_| | | | (_) | | (_| |  ___) | |_| |_| | (_| | | (_) |
  *  /_/   __| |_|__,_|_|  ___/|_|__,_| |____/ __|__,_|__,_|_|___/
  *
  * 签名:最痛苦的事不是我失败了,而是我本可以.--zzq
  * ----------------------------------------------------------------
  * ================================================
  * dataBinding.bottomNavigationBar.bottomNavigationBarHelper(
            //设置数据源
            dataSource = {
                tabTitles = titles
                fragment = mFragments
                icon = arrayListOf(
                    R.mipmap.tabbar_home_nor,
                    R.mipmap.tabbar_data_nor,
                    R.mipmap.tabbar_mine_nor
                )
            },
//            控件的属性设置
            option = {
                activeColor = cn.eeepay.lib_res.R.color.lib_res_themeColor//选中项颜色
            },
            //控件的监听事件
            tabSelectedListener = {
                setTabSelectedFun { position ->
                    dataBinding.viewPager.currentItem = position
                }
            }
        ).setUpWithViewPager(
            supportFragmentManager,
            dataBinding.viewPager,
            viewPageChangeListener = {
                setOnPageSelectedFun {
                    when (it) {
                        0 -> {
                            setHomeStatusBarColor()
                            dataBinding.bottomNavigationBar.selectTab(0)
                        }
                        1 -> {
                            setHomeStatusBarColor()
                            dataBinding.bottomNavigationBar.selectTab(1)
                        }
                        2 -> {
                            setMyStatusBarColor()
                            dataBinding.bottomNavigationBar.selectTab(2)
                        }
                    }
                }
            })
  */
/**
 * BottomNavigationBar 数据源
 */
class DataSource {
    var fragment: MutableList<Fragment> = arrayListOf()//Fragment 缓存
    var tabTitles: MutableList<String> = arrayListOf()//标题缓存
    var icon: MutableList<Int> = arrayListOf()//图标缓存
}

/**
 * viewpager 配置信息
 */
class ViewPagerConfig(var fm: FragmentManager? = null, var bindViewPager: ViewPager? = null, var viewPageChangeListener: ExtViewPageChangeListener.() -> Unit = {})


/**
 * 实现BottomNavigationBar监听事件
 */
class ExtTabSelectedListener : BottomNavigationBar.OnTabSelectedListener {
    private var onTabSelectedFun: ((Int) -> Unit)? = null
    private var onTabUnselectedFun: ((Int) -> Unit)? = null
    private var onTabReselectedFun: ((Int) -> Unit)? = null

    //添加监听
    fun setTabSelectedFun(block: (Int) -> Unit) {
        onTabSelectedFun = block
    }

    //添加监听
    fun setTabUnselectedFun(block: (Int) -> Unit) {
        onTabUnselectedFun = block
    }

    //添加监听
    fun setTabReselectedFun(block: (Int) -> Unit) {
        onTabReselectedFun = block
    }

    //监听
    override fun onTabSelected(position: Int) {
        onTabSelectedFun?.let { it(position) }
    }

    override fun onTabUnselected(position: Int) {
        onTabUnselectedFun?.let { it(position) }
    }

    override fun onTabReselected(position: Int) {
        onTabReselectedFun?.let { it(position) }
    }
}

/**
 * viewpager2 监听事件
 */
class ExtViewPage2ChangeCallback : ViewPager2.OnPageChangeCallback() {

    private var onPageSelectedFun: ((Int) -> Unit)? = null
    private var onPageScrolledFun: ((Int, Float, Int) -> Unit)? = null
    private var onPageScrollStateChangedFun: ((Int) -> Unit)? = null

    //供外围实现的接口1
    fun setonPageSelectedFun(block: (Int) -> Unit) {
        this.onPageSelectedFun = block
    }

    //供外围实现的接口2
    fun setonPageScrolledFun(block: (Int, Float, Int) -> Unit) {
        this.onPageScrolledFun = block
    }

    //供外围实现的接口3
    fun setonPageScrollStateChangedFun(block: (Int) -> Unit) {
        this.onPageScrollStateChangedFun = block
    }

    override fun onPageSelected(position: Int) {
        onPageSelectedFun?.let { it(position) }
    }

    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
        onPageScrolledFun?.let { it(position, positionOffset, positionOffsetPixels) }
    }

    override fun onPageScrollStateChanged(state: Int) {
        onPageScrollStateChangedFun?.let { it(state) }
    }
}

/**
 * viewpager 监听事件
 */
class ExtViewPageChangeListener : ViewPager.OnPageChangeListener {
    private var onPageScrolledFun: ((Int, Float, Int) -> Unit)? = null
    private var onPageSelectedFun: ((Int) -> Unit)? = null
    private var onPageScrollStateChangedFun: ((Int) -> Unit)? = null

    //添加监听
    fun setOnPageScrolledFun(block: (Int, Float, Int) -> Unit) {
        onPageScrolledFun = block
    }

    //添加监听
    fun setOnPageSelectedFun(block: (Int) -> Unit) {
        onPageSelectedFun = block
    }

    //添加监听
    fun setOnPageScrollStateChangedFun(block: (Int) -> Unit) {
        onPageScrollStateChangedFun = block
    }

    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
        onPageScrolledFun?.let { it(position, positionOffset, positionOffsetPixels) }
    }

    override fun onPageSelected(position: Int) {
        onPageSelectedFun?.let { it(position) }
    }

    override fun onPageScrollStateChanged(state: Int) {
        onPageScrollStateChangedFun?.let { it(state) }
    }

}
//ViewPager 使用 PagerAdapter、FragmentPagerAdapter、FragmentStatePagerAdapter 作为其适配器。
//PagerAdapter:
//PagerAdapter 是最基本的适配器,用于管理非 Fragment 的页面。
//它提供了 instantiateItem(ViewGroup container, int position) 和 destroyItem(ViewGroup container, int position, Object object) 方法来控制页面的创建和销毁。
//PagerAdapter 需要手动管理页面的缓存和复用,这可能会导致内存泄漏,如果页面中的资源没有正确释放。
//FragmentPagerAdapter:
//FragmentPagerAdapter 是 PagerAdapter 的子类,专为 Fragment 设计。
//它管理一个 Fragment 的集合,每个 Fragment 代表一个页面。
//FragmentPagerAdapter 会缓存所有的 Fragment 实例,即使它们没有显示在屏幕上。这意味着即使用户导航离开了某个页面,该页面的 Fragment 仍然存在,这有助于保持状态,但也可能增加内存使用。
//FragmentStatePagerAdapter:
//FragmentStatePagerAdapter 也是 PagerAdapter 的子类,同样用于 Fragment。
//与 FragmentPagerAdapter 不同的是,FragmentStatePagerAdapter 只会保留当前页面和直接相邻页面的 Fragment 实例。当页面不可见时,它的 Fragment 会被销毁,只保留必要的信息以恢复该页面。
//这种适配器适用于页面数量较多的情况,或者当页面状态不重要时,可以帮助减少内存使用。

//ViewPager2 使用 RecyclerView.Adapter,并且对于 Fragment,使用 FragmentStateAdapter 作为其适配器 。
/**
 * viewpager 绑定的adapter
 */
class ViewPageAdapter(fm: FragmentManager?, private var mFragments: List<Fragment>, private val mTitles: List<String>) : FragmentStatePagerAdapter(fm!!, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
    override fun getItem(position: Int): Fragment {
        return mFragments[position]
    }

    override fun getCount(): Int {
        return mFragments.size
    }

    override fun getPageTitle(position: Int): CharSequence {
        return mTitles[position]
    }

}

/**
 * viewpager2->绑定的adapter
 */
class ViewPage2Adapter(fm: FragmentManager, private var mFragments: List<Fragment>, lifecycle: Lifecycle) : FragmentStateAdapter(fm, lifecycle) {
    override fun getItemCount(): Int = mFragments.size

    override fun createFragment(position: Int): Fragment = mFragments[position]

}


// 使用 WeakHashMap 避免内存泄漏
private val dataSourceMap = WeakHashMap<BottomNavigationBar, DataSource>()

//定义一个扩展属性dataSourceEtx
var BottomNavigationBar.dataSourceEtx: DataSource
    set(value) {
        dataSourceMap[this] = value
    }
    get(): DataSource {
        return dataSourceMap[this] ?: DataSource().apply {
            // 初始化默认值(如果需要)
            dataSourceMap[this@dataSourceEtx] = this
        }
    }


/**
 * BottomNavigationBar 拓展函数,方便实现
 */
fun BottomNavigationBar.bottomNavigationBarHelper(dataSource: DataSource.() -> Unit, option: BottomNavigationBar.() -> Unit = {}, tabSelectedListener: ExtTabSelectedListener.() -> Unit): BottomNavigationBar {
    dataSourceEtx = DataSource().apply(dataSource)
    val listener = ExtTabSelectedListener().apply {
        tabSelectedListener()
    }
    apply {
        clearAll()
        setMode(BottomNavigationBar.MODE_FIXED)//吸底部模式
        setBackgroundStyle(BottomNavigationBar.BACKGROUND_STYLE_STATIC)
        dataSourceEtx.fragment.forEachIndexed { index, _ ->
            addItem(BottomNavigationItem(dataSourceEtx.icon.elementAtOrElse(index) { 0 }, dataSourceEtx.tabTitles.elementAtOrElse(index) { "" }))
        }
        setFirstSelectedPosition(0)//默认选中第一项
//        activeColor = cn.eeepay.lib_res.R.color.lib_res_themeColor//选中项颜色
        elevation = 0.0F
        option.invoke(this)
        initialise()
        //设置监听事件
        setTabSelectedListener(listener)
    }

    return this
}

/**
 *  关联 ViewPager 设置
 */
fun BottomNavigationBar.setUpWithViewPager(act: FragmentActivity? = null, bindViewPager: ViewPager? = null, viewPageChangeListener: ExtViewPageChangeListener.() -> Unit = {}) {
    act ?: return
//    val dataSourceTemp = DataSource().apply(dataSource)
    val listener = ExtViewPageChangeListener().apply {
        viewPageChangeListener()
    }
    bindViewPager?.apply {
        offscreenPageLimit = dataSourceEtx.fragment.size - 1
        adapter = ViewPageAdapter(act.supportFragmentManager, dataSourceEtx.fragment, dataSourceEtx.tabTitles)
        setOnPageChangeListener(listener)
    }
}
/**
 *  关联 ViewPager2 设置
 */
fun BottomNavigationBar.setUpWithViewPager2(act: FragmentActivity? = null, bindViewPager: ViewPager2? = null,  viewPage2ChangeListener: ExtViewPage2ChangeCallback.() -> Unit = {}) {
    act ?: return
    val listener = ExtViewPage2ChangeCallback().apply {
        viewPage2ChangeListener()
    }
    bindViewPager?.apply {
        offscreenPageLimit = dataSourceEtx.fragment.size - 1
        adapter = ViewPage2Adapter(act.supportFragmentManager, dataSourceEtx.fragment, act.lifecycle)
        registerOnPageChangeCallback(listener)
    }
}
//class ExtRvOnItemTouchListener : RecyclerView.OnItemTouchListener {
//    override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
//        TODO("Not yet implemented")
//    }
//
//    override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
//        TODO("Not yet implemented")
//    }
//
//    override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
//        TODO("Not yet implemented")
//    }
//}
/**
 * ViewPager2 + 横向 RecyclerView 滑动的冲突的解决方案
 */
//dataBinding.rvAccountList.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
//    override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
//        when (e.action) {
//            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
//                // 告诉父容器不要拦截触摸事件
//                rv.parent.requestDisallowInterceptTouchEvent(true)
//            }
//            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
//                rv.parent.requestDisallowInterceptTouchEvent(false)
//            }
//        }
//        return false
//    }
//
//    override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {}
//    override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
//})


/**
 *  val width = 360.w     // 按照设计稿比例换算成 px
val padding = 16.dp   // dp 转 px
 */

val Int.w: Int
    get() = (this * application.resources.displayMetrics.widthPixels / DESIGN_WIDTH.toFloat()).toInt()

val Float.dp: Int
    get() = (this * application.resources.displayMetrics.density + 0.5f).toInt()

val Int.dp: Int
    get() = (this * application.resources.displayMetrics.density + 0.5f).toInt()

object EditConfig {
    const val DESIGN_WIDTH = 375  // 设计稿宽度
}