RecyclerView动态插入复杂多列展示

一、说在前面的话

在做开发列表时,会经常遇到混排的情况。如下图:

不过在大部分情况下,做这种布局时都是在固定好的布局模式下做数据填充的,很少会涉及到动态根据不同的Data类型展示不同的布局。即使有,用的最多的也是新增Footer和Header类型。而今天我们要讲的事,是在已有的数据集合中,动态插入数据,并且是不同类型的数据,且保证原有的数据不能出现错乱的问题(PS:有人可能对错乱的问题,产生疑问,这点将会在后面作为重点来讲)。扔一个二向箔给你,自己去领悟

二、言归正传,回归正题

由于列的数量不同,会导致不同的算法,下面我们依次讲:单列、双列、三列,这三种模式

在使用RecyclerView时,设置layoutManager为GridLayoutManager,并结合SpanSizeLookup使用,即可达到跨行的效果。

代码如下:

layoutManager = GridLayoutManager(this, SPAN_COUNT)
rv_listView.layoutManager = layoutManager
rv_listView.adapter = myAdapter
layoutManager.spanSizeLookup = MySpanSizeLookup()

其中MySpanSizeLookup类的代码为:

inner class MySpanSizeLookup : SpanSizeLookup() {
      override fun getSpanSize(position: Int): Int {
         return if (myAdapter.isFullRow(position)) {
              SPAN_COUNT
          } else {
              1
        }
     }
    }

或者使用StaggeredGridLayoutManager也可达到跨行的效果,

如果你们是在既定的数据集,根据不同的类型,展示不同的布局,那上面的两行代码基本上已经满足了,不需要在继续往下读了。下面主要讲的数据如何动态插入不同的数据类型,以展示不同的布局。

正文

PS:下文中描述的行,可能是单列、双列、三列

举个例子来说,如果我们在一个集合A展示的列表中,第3行插入一条跨行展示的数据类型;在3行后的基础上在加4行,也就是说第7行;在插入一条跨行展示的数据类型,然后在第7行的基础上在加4行,也就是说第11行插入一条跨行数据,以此类推。

如下图:
在这里插入图片描述
如果对于单列的,可能特别容易插入数据。在第1个基础上,每条跨行数据类型加上spanSize,即可达到上面的效果。

代码如下:

    val listA = mutableListOf<String>()
    for (item in 1..10) {
        listA.add("gaozhongkui$item")
    }

    val spanSize = 4
    val bTypeCount = 4
    var preInsertBTypePosition = -1

    for (item in 1..bTypeCount) {

         //由于第一个位置和后续的位置的spanSize不一样,故需要特殊处理
         if (preInsertBTypePosition == -1) {
              preInsertBTypePosition = 2  //第3个位置,故需要写成2
         } else {
             preInsertBTypePosition += spanSize
         }

         if (preInsertBTypePosition < listA.size) {
             listA.add(preInsertBTypePosition, "hhy$preInsertBTypePosition")
         } else {
             listA.add("hhy$preInsertBTypePosition")
         }
     }

如果对于双列的话,还是按照单行的代码模式写。

代码如下:

    ...和单行代码一致,故省略
    
    val columnsSize = 2
    
    for (item in 1..bTypeCount) {

         //由于第一个位置和后续的位置的spanSize不一样,故需要特殊处理
         if (preInsertBTypePosition == -1) {
              preInsertBTypePosition = 2  //第3个位置,故需要写成2
         } else {
         	 //这段代码的含义是:上次插入的位置+间隔行数×单行的列数
             preInsertBTypePosition += spanSize * columnsSize
         }

        ...
     }		

展示的效果,如下图:

细心的同学,会发现展示的列表中出现空缺的问题。那为什么会出现这个问题那?

首先,集合A中按照上面的算法插入数据集合为:

第1条跨行数据类型位置有误,请大家忽略,但是后面的位置计算是对的

在这里插入图片描述
所以展示的数据效果为:
在这里插入图片描述
由此看出,我们这种位置计算方式不对。那如果从后面的非跨行的数据集合中,在拿一条放在前面不就可以填充满了吗?
所以改进了插入数据的算法。

代码如下:

 ...和单行代码一致,故省略
    
    val columnsSize = 2
    
    for (item in 1..bTypeCount) {

         //由于第一个位置和后续的位置的spanSize不一样,故需要特殊处理
         if (preInsertBTypePosition == -1) {
              preInsertBTypePosition = 2  //第3个位置,故需要写成2
         } else {
         	 //这段代码的含义是:上次插入的位置+间隔行数×单行的列数
             preInsertBTypePosition += spanSize * columnsSize
			 //这段代码的含义是:上次插入的位置+(上次插入位置+(已插入的广告数量)×(列数-1))%列数
			 preInsertBTypePosition += (preInsertBTypePosition + ((item - 1) * (columnsSize - 1))) % columnsSize
         }
        ...
     }		

这样即可正常展示。 这时,有的同学对这段代码有疑问了

preInsertBTypePosition += (preInsertBTypePosition + ((item - 1) * (columnsSize - 1))) % columnsSize

它的含义是:通过spanSize * columnsSize计算所得值,赋值到变量preInsertBTypePosition,括号加上preInsertBTypePosition再加上item-1,其中item是已插入广告的数量,至于它为什么会减一,是因为item算上这次要插入的广告,故需要减一;然后在乘以columnsSize - 1,其中columnsSize是列数,至于为什么会减一,是因为广告本身也算上item,所以需要减一;然后在通过列数取余,获取需要往前提未跨行数据的数量。这块可能比较绕,扔一个二向箔给你,自己去领悟

如果对于三列的话,还是按照双列的代码模式写。
恭喜你,答对了。

下面附上完整的代码:


class MainActivity2 : AppCompatActivity() {
    private companion object {
        private const val SPAN_COUNT = 3
        private const val INTERVAL_NATIVE_AD = 5
        private const val DEBUG: Boolean = true
        private val TAG: String? = if (DEBUG) MainActivity2::class.java.name else null
        private lateinit var layoutManager: GridLayoutManager
        private val myAdapter = MyAdapter()
        private var mInsertNativeAdCount = 0
        private var mInsertNativeAdPosition = 0
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main1)
        layoutManager = GridLayoutManager(this, SPAN_COUNT)
        rv_listView.layoutManager = layoutManager
        rv_listView.adapter = myAdapter
        layoutManager.spanSizeLookup = MySpanSizeLookup()

        button4.setOnClickListener {
            mInsertNativeAdPosition = getNativeAdPositionAutoDown()
            myAdapter.addNativeAdToListView(mInsertNativeAdPosition, NativeAdInfo("gaozhongkui"))
            mInsertNativeAdCount++
        }
    }

    private fun getNativeAdPositionDown(): Int {
        var position =
            mInsertNativeAdPosition + INTERVAL_NATIVE_AD * SPAN_COUNT// mInsertNativeAdCount * INTERVAL_NATIVE_AD * SPAN_COUNT
        if (mInsertNativeAdCount >= 1) {
            position += (position + ((mInsertNativeAdCount) * (SPAN_COUNT - 1))) % SPAN_COUNT
        }
        return position
    }

    private fun getNativeAdPositionAutoDown(): Int {
        var position = getNativeAdPositionDown()
        val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
        if (firstVisibleItemPosition > position) {
            position =
                firstVisibleItemPosition + (firstVisibleItemPosition + ((mInsertNativeAdCount) * (SPAN_COUNT - 1))) % SPAN_COUNT
        }
        return position
    }

    inner class MySpanSizeLookup : SpanSizeLookup() {
        override fun getSpanSize(position: Int): Int {
            return if (myAdapter.isFullRow(position)) {
                SPAN_COUNT
            } else {
                1
            }
        }
    }
}



~~
发布了20 篇原创文章 · 获赞 30 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_37618354/article/details/89924645