Android LinearSnapHelper+RecyclerView實現滾動列表固定卡在第一個Item

Android LinearSnapHelper+RecyclerView實現滾動列表固定卡在第一個Item

前言

先上效果圖
在這裏插入圖片描述
看圖能猜到什麼效果吧,就是一直選中最左側的日期,懶得截GIF了。

LinearSnapHelper+RecyclerView

理論上需要使用LinearSnapHelper+RecyclerView的,但是想偷個懶,直接使用做好的輪子。
https://github.com/TakuSemba/MultiSnapRecyclerView

使用方法:

dependencies {
    implementation 'com.github.takusemba:multisnaprecyclerview:2.0.0'
}

然後在佈局中添加:

                <com.takusemba.multisnaprecyclerview.MultiSnapRecyclerView
                    android:id="@+id/rcv_day"
                    android:layout_width="match_parent"
                    android:layout_height="60dp"
                    android:layout_marginStart="10dp"
                    android:layout_marginTop="10dp"
                    android:visibility="@{isShowCalendar? View.GONE: View.VISIBLE}"
                    app:msrv_gravity="start"
                    app:msrv_interval="1"
                    app:msrv_speed_ms_per_inch="300"
                    tools:listitem="@layout/item_main_snap_day" />

再寫一個Adapter,這個部分大家都會,只當參考哈

class MainSnapDayAdapter constructor(
    var mHandler: Handler? = null,
    var mList: MutableList<SnapDayInfo> = mutableListOf()
): RecyclerView.Adapter<MainSnapDayAdapter.ViewHolder>() {

    private var mSelectDay: Int = 0
    private var mHasDataDayList: MutableList<Int> = mutableListOf()

    fun setSelectDay(selectDay: Int) {
        this.mSelectDay = selectDay
        notifyDataSetChanged()
    }

    fun setHasDataDayList(dayList: List<Int>) {
        mHasDataDayList.clear()
        mHasDataDayList.addAll(dayList)
    }

    fun update(list: MutableList<SnapDayInfo>, selectDay: Int) {
        this.mList = list
        this.mSelectDay = selectDay
        notifyDataSetChanged()
    }

    data class SnapDayInfo(
        var day: Int,
        var lunar: String
    ): Serializable

    inner class ViewHolder constructor(
        val binding: ItemMainSnapDayBinding
    ) : RecyclerView.ViewHolder(binding.root) {

        fun bind(info: SnapDayInfo) {
            binding.dayInfo = info
            binding.isSelected = mSelectDay == info.day
            binding.isHasData = mHasDataDayList.contains(info.day)
            binding.executePendingBindings()
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding: ItemMainSnapDayBinding = DataBindingUtil.inflate(
            LayoutInflater.from(parent.context), R.layout.item_main_snap_day,
            parent, false)
        return ViewHolder(binding)
    }

    override fun getItemCount(): Int = this.mList.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val info = this.mList[position]
        holder.bind(info)
    }
}

最後在Fragment中說使用

        // 日期列表
        binding.rcvDay.layoutManager = LinearLayoutManager(requireContext(),
            LinearLayoutManager.HORIZONTAL, false)
        binding.rcvDay.adapter = dayAdapter
        // 滾動選中事件
        binding.rcvDay.setOnSnapListener(object : OnSnapListener{
            override fun snapped(position: Int) {
                binding.selectDay = position + 1
                dayAdapter.setSelectDay(binding.selectDay)
                binding.calendarView.scrollToCalendar(binding.selectYear, binding.selectMonth, binding.selectDay, false, false)
                updateSelectMember(binding.memberInfo?: return)
            }
        })
        refreshDayList()

重點來啦
最主要的就是最後一個item永遠滾不到第一個來,在這裏我卡了好久,後來同事提出的想法就是將最後一個item的寬度佔recyclerview的寬度

    /** 刷新日期列表 */
    private fun refreshDayList() {
        val list = mutableListOf<MainSnapDayAdapter.SnapDayInfo>()
        for (i in 1 .. DateUtils.getDaysOfMonth(binding.selectYear, binding.selectMonth)) {
            list.add(MainSnapDayAdapter.SnapDayInfo(i, DateUtils.getLunarString(binding.selectYear, binding.selectMonth, i)))
        }
        val day = binding.selectDay
        dayAdapter.update(list, day)
        val scroll = TopSmoothScroller(requireContext())
        scroll.targetPosition = day -1
        binding.rcvDay.layoutManager?.startSmoothScroll(scroll)
        
        binding.rcvDay.addItemDecoration(object : RecyclerView.ItemDecoration(){
            override fun getItemOffsets(
                outRect: Rect,
                view: View,
                parent: RecyclerView,
                state: RecyclerView.State
            ) {
                super.getItemOffsets(outRect, view, parent, state)
                val position = (view.layoutParams as RecyclerView.LayoutParams).viewLayoutPosition
                if (position == list.size -1) {
                    // 給最後一個item增加寬度
                    outRect.right = parent.width - view.width
                }
            }
        })
    }

完事

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章