日期、列表、指示器【1列3行】

日期、列表、指示器【1列3行】3级联动

添加了LinearSnapHelper滚动自动对齐功能

RecyclerViewDemo3Fragment类:

package com.example.androidkotlindemo2.recyclerview.demo3

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSnapHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.OnScrollListener
import com.example.androidkotlindemo2.MyApp
import com.example.androidkotlindemo2.R
import com.example.androidkotlindemo2.bean.ItemBean
import com.example.androidkotlindemo2.databinding.RecyclerViewDemo3MainBinding
import com.example.androidkotlindemo2.utils.LogUtils


/**
 * Author : wangning
 * Email : maoning20080809@163.com
 * Date : 2023/9/24 9:58
 */
class RecyclerViewDemo3Fragment : Fragment() {

    private lateinit var binding: RecyclerViewDemo3MainBinding

    /**
     * app列表适配器
     */
    private var mContentAdapter : RVDemo3Adapter? = null

    /**
     * app列表
     */
    private var mContentLayoutManager : LinearLayoutManager? = null

    /**
     * 显示app列表
     */
    private var mContentRecyclerView: RecyclerView? = null

    /**
     * 已选中的标题下标值
     */
    private var mSelectedTitlePosition = 0

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = RecyclerViewDemo3MainBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        initTitle()
        initContent()
        initIndicator()
    }

    /**
     * 标题适配器
     */
    private var mTitleAdapter : RVDemo3TitleAdapter? = null

    /**
     * 标题列表
     */
    private var mTitleList = mutableListOf<ItemBean>()

    /**
     * 内容是否正在滚动,避免重复执行,快速跳动
     */
    private var mContentScrolling = false

    private fun initTitle(){
        val recyclerViewTitle: RecyclerView = binding.recyclerTitle
        for(i in 11..17){
            var itemBean = ItemBean(i, "${i}日")
            mTitleList.add(itemBean)
        }

        recyclerViewTitle.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false)
        mTitleAdapter = RVDemo3TitleAdapter(R.layout.recycler_view3_title_item, mTitleList) //

        //必须禁用adater的点击事件,这里才有效 。holder.getViewOrNull<TextView>(R.id.tv)?.isClickable = false
        mTitleAdapter?.setOnItemClickListener { adapter, view, position ->
            LogUtils.i(TAG, "position : ${position} , title : ${mTitleList[position].name}")
            isFirstVisible = true
            mDx = 0

            mSelectedTitlePosition = position
            var list = getData(mTitleList[position].name)
            mContentAdapter?.setList(list)
            mTitleAdapter?.select(position)

            //滚动到顶部
            mContentRecyclerView?.smoothScrollToPosition(0)
            // 立即定位到顶部,不显示滚动条
            mContentLayoutManager?.scrollToPositionWithOffset(0, 0)

            //切换指示器
            switchIndicator(0)

            //这个方法带滚动条显示
            //mContentLayoutManager?.smoothScrollToPosition(mContentRecyclerView, null, 0)
        }

        recyclerViewTitle.adapter = mTitleAdapter
    }

    /**
     * 如果mDx大于0,从右向左滑动 <- , 可见下标从1到3
     * 如果mDx小于0,从左向右滑动 -> ,可见下标从3到1
     */
    private var mDx = 0

    /**
     * 当可见下标值
     */
    private var mCurrentPosition = 0

    /**
     * 最后一个可见的下标值
     */
    private var mLastPosition = 0

    /**
     * 选择指示器下标值
     */
    private var mSelectIndicatorPosition = 0

    /**
     * SnapHelper辅助RecyclerView实现滚动对齐就是通过给RecyclerView设置OnScrollerListenerh和OnFlingListener这两个监听器实现的
     */
    private var mLinearSnapHelper: LinearSnapHelper = LinearSnapHelper()

    /**
     * 初始化显示内容列表
     */
    private fun initContent(){
        mContentRecyclerView = binding.recycler
        mContentLayoutManager = object : GridLayoutManager(requireContext(), 3, HORIZONTAL, false){
            override fun canScrollHorizontally(): Boolean {
                return true
            }

            override fun scrollHorizontallyBy(
                dx: Int,
                recycler: RecyclerView.Recycler?,
                state: RecyclerView.State?
            ): Int {
                mDx = dx
                mCurrentPosition = findFirstVisibleItemPosition()
                mLastPosition = findLastCompletelyVisibleItemPosition()
                //向右滑动,然后再向左边滑动到最左边,就要保留第一个显示,还不能切换标题tab
                if(dx > 0){
                    isFirstVisible = false
                }

                /*//最后一个可见
                var lastPosition = findLastVisibleItemPosition()
                //取模  - 没列3行 spanCount
                var mode = lastPosition % spanCount
                mSelectIndicatorPosition = (lastPosition - mode) / spanCount
                LogUtils.e(TAG,"GridLayoutManager scrollHorizontallyBy ${findLastVisibleItemPosition()}  , ${findLastCompletelyVisibleItemPosition()} , spanCount : ${spanCount} , mode : ${mode}, ${mSelectIndicatorPosition}")
                switchIndicator(mSelectIndicatorPosition)*/

                return super.scrollHorizontallyBy(dx, recycler, state)
            }
        }

        var list = getData("11日")
        mContentRecyclerView?.layoutManager = mContentLayoutManager
        mContentAdapter = RVDemo3Adapter(R.layout.recycler_view3_item, list)
        mLinearSnapHelper.attachToRecyclerView(mContentRecyclerView)
        mContentRecyclerView?.adapter = mContentAdapter

        mContentRecyclerView?.addOnScrollListener(object : OnScrollListener(){
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)

                LogUtils.e(TAG,"onScrollStateChanged newState:${newState}, ${mContentLayoutManager?.findFirstCompletelyVisibleItemPosition()}")

                if(newState == RecyclerView.SCROLL_STATE_SETTLING){
                    mContentScrolling = true

                } else if (newState == RecyclerView.SCROLL_STATE_DRAGGING){
                    mContentScrolling = true
                } else if (newState == RecyclerView.SCROLL_STATE_IDLE){
                    mContentScrolling = false


                    LogUtils.i(TAG, "滚动到mSelectIndicatorPosition :${mSelectIndicatorPosition}")
                    // 滚动已停止,此时可以检查哪个项目被对齐
                     var centerView = mLinearSnapHelper.findSnapView(mContentLayoutManager)
                     centerView?.let {
                         var selectContentPosition = mContentRecyclerView?.getChildAdapterPosition(centerView)
                         selectContentPosition?.let {
                             processContentSelectPosition()
                         }
                         //如果是1行1列可以直接切换指示器
                         //switchIndicator(selectContentPosition)
                     }

                    processContentScrolling()
                }
            }

            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
            }
        })

    }

    /**
     * 处理显示内容列表的下标值
     */
    private fun processContentSelectPosition(){
        var spanCount = 3
        //最后一个可见
        var lastPosition = mContentLayoutManager?.findLastVisibleItemPosition()?:0
        //取模  - 没列3行 spanCount
        var mode = lastPosition % spanCount
        mSelectIndicatorPosition = (lastPosition - mode) / spanCount
        //切换指示器
        switchIndicator(mSelectIndicatorPosition)
    }

    /**
     * 快速滑动到左边(当前第5个,从第5各滑动到第1个),不能切换标题tab
     */
    private var isFirstVisible = false

    /**
     * 处理左右滑动
     */
    private fun processContentScrolling(){
        LogUtils.d(TAG,"processA :dx : ${mDx} , mCurrentPosition : ${mCurrentPosition},  isFirstVisible : ${isFirstVisible} , ${mContentLayoutManager?.itemCount}}")

        if(mDx < 0){
            if(!isFirstVisible){
                if(mContentLayoutManager?.findFirstCompletelyVisibleItemPosition() == 0){
                    isFirstVisible = true
                }
                return
            }
        }

        var itemCount = (mContentLayoutManager?.itemCount?:0) - 1
        LogUtils.d(TAG,"processContentScrolling :第a三: mCurrentPosition : ${mCurrentPosition} , mLastPosition : ${mLastPosition} ,  ${itemCount}, ${mContentLayoutManager?.itemCount}")

        if (mCurrentPosition == 0) {
            // 当滑动到第一个item的时候,继续滑动应该跳转到最后一个item
            if (mDx < 0 && mContentLayoutManager?.findFirstCompletelyVisibleItemPosition() == 0) {
                //从左向右滑动(->),不是第一个,要切换标题tab
                if(mSelectedTitlePosition != 0 && !mContentScrolling){
                    mContentScrolling = true
                    goPrevious(mSelectedTitlePosition -1)
                }
            }

        } else if (mLastPosition === itemCount) {
            LogUtils.i(TAG,"processContentScrolling a最后一个:${mContentLayoutManager?.findLastCompletelyVisibleItemPosition()} , ${mContentLayoutManager?.itemCount} , ${mCurrentPosition}")

            // 当滑动到最后一个item的时候,继续滑动应该跳转到第一个item
            if (mDx > 0) {
                LogUtils.i(TAG,"processContentScrolling 最后一个:${mContentLayoutManager?.findLastCompletelyVisibleItemPosition()} , ${mContentLayoutManager?.itemCount} , ${mCurrentPosition}")
                goNext(mSelectedTitlePosition + 1)
            }
        }
    }


    /**
     * 切换到上一个tab
     * @param titlePosition 下标值
     */
    private fun goPrevious(titlePosition : Int){
        mSelectedTitlePosition = titlePosition
        var list = getData(mTitleList[titlePosition].name)
        mContentAdapter?.setList(list)
        mTitleAdapter?.select(titlePosition)
    }

    /**
     * 切换到下一个tab
     * @param titlePosition 下标值
     */
    private fun goNext(titlePosition : Int){
        mSelectedTitlePosition = titlePosition
        var list = getData(mTitleList[titlePosition].name)
        mContentAdapter?.setList(list)
        mTitleAdapter?.select(titlePosition)

        //滚动到顶部
        mContentRecyclerView?.smoothScrollToPosition(0)
        // 立即定位到顶部,不显示滚动条
        mContentLayoutManager?.scrollToPositionWithOffset(0, 0)
        mSelectIndicatorPosition = 0
        switchIndicator(0)
    }


    private fun getData(title : String) : MutableList<ItemBean>{
        var itemBeans = mutableListOf<ItemBean>()
        for(i in 0..14){
            var itemBean = ItemBean(i, "【${title}】-我的测试数据$i ")
            itemBeans.add(itemBean)
        }
        return itemBeans
    }

    /**
     * 初始化指示器
     */
    private fun initIndicator(){
        binding.indicatorLayout.removeAllViews()
        for (i in 0..4) {
            var view = View(requireContext())
            var layoutParams = LinearLayout.LayoutParams(60, 60)
            layoutParams.leftMargin = 30
            layoutParams.rightMargin = 30
            view.layoutParams = layoutParams
            if(i == 0){
                view.setBackgroundColor(MyApp.myApp.resources.getColor(R.color.red))
            } else {
                view.setBackgroundColor(MyApp.myApp.resources.getColor(R.color.gray_e8e8e8))
            }
            binding.indicatorLayout.addView(view)
        }
    }

    /**
     * 切换指示器
     * @param selectPosition 选中下标值
     */
    private fun switchIndicator(selectPosition : Int){
        var childCount = binding.indicatorLayout.childCount
        LogUtils.i(TAG, "选择switchIndicator:${selectPosition}")
        for (i in 0 until childCount) {
            var childView = binding.indicatorLayout.getChildAt(i)
            if(i == selectPosition){
                LogUtils.i(TAG, "选择:${i}")
                childView.setBackgroundColor(MyApp.myApp.resources.getColor(R.color.red))
            } else {
                LogUtils.i(TAG, "不选择:${i}")
                childView.setBackgroundColor(MyApp.myApp.resources.getColor(R.color.gray_e8e8e8))
            }
        }
    }

    companion object {
        const val TAG = "RecyclerViewDemo2Fragment"
    }

}

RVDemo3Adapter类:
package com.example.androidkotlindemo2.recyclerview.demo3

import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.module.LoadMoreModule
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.example.androidkotlindemo2.R
import com.example.androidkotlindemo2.bean.ItemBean

/**
 * Author : wn
 * Email : maoning20080809@163.com
 * Date : 2023/9/24 10:07
 * Description :
 */
class RVDemo3Adapter(layoutResId: Int, data : MutableList<ItemBean>)
    : BaseQuickAdapter<ItemBean, BaseViewHolder>(layoutResId = layoutResId, data = data) , LoadMoreModule{

    override fun convert(holder: BaseViewHolder, item: ItemBean) {
        holder.setText(R.id.tv, item.name)
    }

}
RVDemo3TitleAdapter类:
package com.example.androidkotlindemo2.recyclerview.demo3

import android.widget.TextView
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.module.LoadMoreModule
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.example.androidkotlindemo2.MyApp
import com.example.androidkotlindemo2.R
import com.example.androidkotlindemo2.bean.ItemBean

/**
 * Author : wn
 * Email : maoning20080809@163.com
 * Date : 2023/9/24 10:07
 * Description :
 */
class RVDemo3TitleAdapter(layoutResId: Int, data : MutableList<ItemBean>)
    : BaseQuickAdapter<ItemBean, BaseViewHolder>(layoutResId = layoutResId, data = data) , LoadMoreModule{

    private var mSelectPosition: Int = 0

    override fun convert(holder: BaseViewHolder, item: ItemBean) {

        holder.setText(R.id.tv, item.name)
        holder.getViewOrNull<TextView>(R.id.tv)?.isClickable = false

        holder?.getViewOrNull<TextView>(R.id.tv)?.let {
            if(mSelectPosition == holder.layoutPosition){
                it.setBackgroundColor(MyApp.myApp.resources.getColor(R.color.greenyellow))
            } else {
                it.setBackgroundColor(MyApp.myApp.resources.getColor(R.color.gray_d9d9d9))
            }
        }
    }

    fun select(selectPosition : Int){
        mSelectPosition = selectPosition
        notifyDataSetChanged()
    }

    fun getPosition() : Int{
        return mSelectPosition
    }

}

recycler_view_demo3_main.xml布局

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <LinearLayout
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:id="@+id/button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginVertical="10dp"
            android:layout_gravity="center"
            android:gravity="center"
            android:layout_centerHorizontal="true"
            android:textColor="@color/black"
            android:textSize="28sp"
            android:text="日期、列表、指示器【1列3行】" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

        <LinearLayout
            android:id="@+id/indicator_layout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="20dp"
            android:orientation="horizontal"/>

    </LinearLayout>
</layout>

recycler_view3_title_item.xml布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_height="wrap_content"
        android:layout_marginVertical="6dp"
        android:layout_marginHorizontal="6dp"
        android:paddingHorizontal="6dp"
        android:background="@color/greenyellow"
        android:textAllCaps="false"
        android:textColor="@color/blue"
        android:textSize="28sp"
        android:text="item" />

</RelativeLayout>

recycler_view3_item.xml布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
    android:background="@color/gray_c0c0c0"

    android:layout_marginHorizontal="20dp"
    android:layout_height="120dp">

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginVertical="6dp"
        android:textColor="@color/red"
        android:textSize="28sp"
        android:text="item" />

</RelativeLayout>

  • 10
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

六毛六66

你的鼓励是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值