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
}
}
})
}