【问题标题】:Circular horizontal Recycler View Adapter圆形水平回收器视图适配器
【发布时间】:2021-05-02 17:09:15
【问题描述】:

我有一个水平的回收视图适配器,我正试图让它在两个方向上都是圆形的。我为此找到了多种解决方案,但在 Kotlin 中没有找到。我的 Adapter 的 getItemCount 和 onBindViewHolder 看起来像这样。

override fun onbindViewHolder(holder: ViewHolder<T>, position: Int){ 
    val Data = items[position]
    holder.bind(Data, position, viewHoldBindFunc)

override fun getItemCount(): Int = items.size 

我尝试将这两个函数更改为以下内容,但 Integer.MAX_VALUE 会导致整个屏幕崩溃。

    override fun getItemCount(): Int {return Integer.MAX_VALUE}
and 
    override fun onbindViewHolder(holder: ViewHolder<T>, position: Int){ 
        val NewPosition = position % items.size
        val Data = items[NewPosition]
        holder.bind(Data, NewPosition, viewHoldBindFunc)

谢谢

【问题讨论】:

    标签: java android kotlin android-recyclerview


    【解决方案1】:

    这是一个在 Kotlin 中似乎可行的解决方案。我基于https://gist.github.com/nesquena/d09dc68ff07e845cc622 中的一些代码。

    事实证明,如果您调用回收器适配器的 notifyItemRemoved() 和 notifyItemInserted() 方法(对于您看不到的视图),它不会移动您当前正在查看的视图的位置,但它会改变 RecyclerView 感知到的在你的右边和左边。

    我得到了这个工作,所以我不妨分享一下结果:这个回收器适配器假装你展示的同一组项目有 5 个副本,并且每当显示左侧或右侧最远副本之一的项目时,它使用 adapter.notifyItemRemoved() 和 notifyItemInserted() 将 RecyclerView 的感知转移到正在滚动的一侧。

    您可能需要对此进行一些测试。滚动几秒钟似乎对我来说很好。为了安全起见,我总共放了 5 个副本(1 个在中间,两个在周围),但我敢打赌,如果你稍微弄乱了它,这将适用于 3 个副本。 :)

    package com.github.ajsnarr98.testingplayground
    
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import androidx.recyclerview.widget.LinearLayoutManager
    import androidx.recyclerview.widget.RecyclerView
    import com.github.ajsnarr98.testingplayground.databinding.ActivityCircularBinding
    
    class CircularActivity : AppCompatActivity() {
    
        private val list = listOf<Int>(1,2,3,4,5,6,7,8,9,10,11,12)
    
        lateinit var binding: ActivityCircularBinding
        lateinit var recyclerAdapter: CircularAdapter
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = ActivityCircularBinding.inflate(layoutInflater)
            setContentView(binding.root)
    
            recyclerAdapter = CircularAdapter(list, binding.list)
            binding.list.apply {
                adapter = recyclerAdapter
                layoutManager = LinearLayoutManager(this@CircularActivity, RecyclerView.HORIZONTAL, false)
            }
    
            recyclerAdapter.init()
        }
    }
    
    package com.github.ajsnarr98.testingplayground
    
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.TextView
    import androidx.recyclerview.widget.LinearLayoutManager
    import androidx.recyclerview.widget.RecyclerView
    
    class CircularAdapter(
            private val items: List<Int>,
            private val recyclerView: RecyclerView,
    ) : RecyclerView.Adapter<CircularAdapter.ViewHolder>() {
    
        var offset: Int = 0
        var numSets = 5
    
        val initialPosition = items.size * 2 // two sets of items before, two sets after
    
        fun init() {
            recyclerView.scrollToPosition(initialPosition)
            recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
                // This happens many times a second during a scroll, so be wary of the code you place here.
                // We are given a few useful parameters to help us work out if we need to load some more data,
                // but first we check if we are waiting for the previous load to finish.'
                override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                    // this example only supports LinearLayoutManager
                    val layoutManager: LinearLayoutManager = recyclerView.layoutManager as? LinearLayoutManager ?: return
                    val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
                    val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
    
                    if (isInBoundarySet(firstVisibleItemPosition)) {
                        insertItemsLeft()
                    }
                    if (isInBoundarySet(lastVisibleItemPosition)) {
                        insertItemsRight()
                    }
                }
            })
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            return ViewHolder(
                    LayoutInflater.from(parent.context).inflate(R.layout.item_number, parent, false)
            )
        }
    
        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            holder.bind(itemForPosition(position))
        }
    
        override fun getItemCount(): Int = items.size * numSets
    
        /**
         * Returns true if the given position is within items.size from 0 or
         * getItemCount().
         */
        private fun isInBoundarySet(position: Int): Boolean {
            return position + items.size >= itemCount || position - items.size < 0
        }
    
        private fun itemForPosition(position: Int) = items[position % items.size]
    
        /**
         * Pretends a new set of items was inserted to the right, and a set was was removed to the left.
         */
        fun insertItemsRight() {
            numSets--
            notifyItemRangeRemoved(0, items.size)
            numSets++
            notifyItemRangeInserted(itemCount, items.size)
        }
    
        /**
         * Pretends a new set of items was inserted to the left, and a set was was removed to the right.
         */
        fun insertItemsLeft() {
            numSets--
            notifyItemRangeRemoved(itemCount-items.size, items.size)
            numSets++
            notifyItemRangeInserted(0, items.size)
        }
    
        class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
            fun bind(item: Int) {
                if (this.itemView is TextView) {
                    this.itemView.text = item.toString()
                }
            }
        }
    
        companion object {
            private const val NUMBER_VIEW = 0
        }
    }
    

    item_number.xml

    <?xml version="1.0" encoding="utf-8"?>
    <TextView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:background="@drawable/frame"
        android:textAlignment="center"
        android:textStyle="bold"
        android:textSize="40sp"
        tools:text="1"/>
    

    activity_circular.xml

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout 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:layout_height="match_parent"
        tools:context=".CircularActivity">
    
        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Circular recycler view"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toTopOf="@id/list"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" />
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toBottomOf="@id/title"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" />
    
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    frame.xml

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle" >
    
        <!-- View background color -->
        <solid
            android:color="@color/white" >
        </solid>
    
        <!-- View border color and width -->
        <stroke
            android:width="3dp"
            android:color="@color/black" >
        </stroke>
    
    </shape>
    

    【讨论】:

    • 感谢您的评论。看起来 addOnScroll 已被贬值,我正在尝试寻找替代解决方案
    • 没问题。 不推荐使用 addOnScrollListener。见developer.android.com/reference/kotlin/androidx/recyclerview/…
    • 据我从 docs/warnings 中看到的,我所附的代码都没有被弃用。
    • 是的,你是对的,它没有贬值,对不起!
    • 我尽可能地遵循了你的实现。它给了我我需要的想法,所以谢谢你!我可以很好地循环前进,但是当我尝试使其双向加载屏幕时,我可以看到滚动到中间位置。你知道我可以使用什么技巧吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-05
    • 2021-06-08
    • 2017-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多