banner部分的实现,主要参考这篇:
ConcatAdapter
使用ConcatAdapter可以使recyclerview具有多个adapter。在HeaderAdapter中添加Banner。HeaderAdapter里面再嵌套一个BannerAdapter。
最终结果如下所示
新建HeaderAdapter。
class HeaderAdapter(private var adapter: BannerAdapter) :
RecyclerView.Adapter<HeaderAdapter.ViewHolder>() {
class ViewHolder(private val binding: ItemHeaderBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(adapter: BannerAdapter) {
val context = binding.root.context
binding.headerList.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
binding.headerList.adapter = adapter
}
}
private lateinit var binding: ItemHeaderBinding
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
binding = ItemHeaderBinding.inflate(layoutInflater, parent, false)
isAbleScroll = true
// 确保一次只能滑动一个数据,停止的时候图片的位置正确
val snapHelper = PagerSnapHelper()
snapHelper.attachToRecyclerView(binding.headerList)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(adapter)
}
override fun getItemCount(): Int = 1
var isAbleScroll: Boolean = false
fun smoothScrollToPosition(position: Int){
if (isAbleScroll){
binding.headerList.smoothScrollToPosition(position)
}
}
}
新建BannerAdapter和BannerViewHolder。
class BannerAdapter(var bannerList: List<Banner>): RecyclerView.Adapter< BannerViewHolder> () {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BannerViewHolder {
return BannerViewHolder(
ItemImageBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false,
)
)
}
override fun onBindViewHolder(holder: BannerViewHolder, position: Int) {
// 通过 getItem 获取数据
if (bannerList.size == 0){
return
}
val banner = bannerList.get(position % bannerList.size)
banner.let { holder.bind(it) }
}
override fun getItemCount(): Int {
// return Int.MAX_VALUE
if (bannerList.size == 0){
return 0
}
return Integer.MAX_VALUE
}
}
class BannerViewHolder(private val binding: ItemImageBinding)
: RecyclerView.ViewHolder(binding.root){
fun bind(banner: Banner) {
Glide.with(itemView)
.load(banner.imagePath)
.into(binding.itemImage)
}
}
初始化adapter,然后配置为RecyclerView的adapter,使用协程实现滑动的定时任务。
// WanAndroidViewModel
private val _bannerListStateFlow = MutableStateFlow<List<Banner>>(ArrayList())
val bannerListStateFlow: StateFlow<List<Banner>> = _bannerListStateFlow
fun updateBannerList() {
// 更新 value 数据
viewModelScope.launch {
_bannerListStateFlow.value = repository.getBannerFlow()
.catch { throwable ->
// Catch exceptions in all down stream flow
// Any error occurs after this catch operator
// will not be caught here
println(throwable)
}
.stateIn(viewModelScope)
.value
}
}
// MainActivity
private val bannerAdapter = BannerAdapter(ArrayList<Banner>())
private val headerAdapter = HeaderAdapter(bannerAdapter)
private val concatAdapter = ConcatAdapter(headerAdapter,articleAdapter)
// 为RecyclerView配置adapter
binding.recyclerview.adapter = concatAdapter
viewModel.updateBannerList()
...
lifecycleScope.launch {
// We repeat on the STARTED lifecycle because an Activity may be PAUSED
// but still visible on the screen, for example in a multi window app
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.bannerListStateFlow.collect{ value ->
// 更新 list
bannerAdapter.bannerList = value
Log.d(TAG," bannerList size = ${bannerAdapter.bannerList.size}")
//数据改变刷新视图
bannerAdapter.notifyDataSetChanged()
}
}
}
// 设置 banner 无限循环滑动
// 使用 launchWhenResumed 能保证 使得息屏后 不执行定时任务
lifecycleScope.launchWhenResumed { //onPause 的时候会暂停.
Log.d(TAG,"inner launchWhenResumed")
var counter = 0
repeat(Int.MAX_VALUE){
delay(3000L)
if (bannerAdapter.bannerList.size > 0){
val position = ++counter % bannerAdapter.bannerList.size
Log.d(TAG,"currentIndex = $counter bannerListSize = ${bannerAdapter.bannerList.size} position = $position")
headerAdapter.smoothScrollToPosition(position)
}
}
}
本文代码地址:
https://github.com/VIVILL/SimpleDemo/tree/main/RecyclerviewDemo
网友评论