Kotlin recyclerview有2种视图类型 [英] kotlin recyclerview with 2 view types

查看:64
本文介绍了Kotlin recyclerview有2种视图类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

每第5个项目之后,我要加载一个按钮视图.

after every 5th item, i want a button view to load.

    override fun getItemViewType(position: Int): Int {

    if (position % 5 == 0 && position != 0) {
        return R.layout.button10th
    } else {
        return R.layout.checkitout
    }
}

当我运行它时,这将带走我的第五个物品. 我如何不带任何阵列物品就能做到这一点?我需要修复我的getItemCount吗?

when i run it, this is taking away my 5th item. how can i achieve this without taking any of my array items? do i need to fix my getItemCount?

   override fun getItemCount(): Int {

    return lists.size
}

我希望它看起来像下面. 1.约翰 2.迈克 3.克里斯 4.简 5.起诉 6.可点击的按钮

i want it look like below. 1. John 2. Mike 3. Chris 4. Jane 5. Sue 6. CLickable BUTTON

推荐答案

一段时间后,使用多个视图RecyclerViews,这是我最好的方法(它可以防止错误并具有良好的实现):

After some time working with multiple view RecyclerViews, here is my best way to do it (that prevents bugs and has decent implementation):

为您的两个项目类型创建两个viewHolder类,每个类内部都有一个bind()函数:

Create two viewHolder classes for your two item types, each having a bind() function inside:

class NameViewHolder(itemView: View) : 
     RecyclerView.ViewHolder(itemView) {

     fun bind(cell: Cell) {
          //Do your bindViewHolder logic in here
     }

}

由于我们有多种视图类型,因此有多种viewHolders,我们需要创建一个包含公共信息的普通Java对象.我叫我的Cell().称其为适合您需求的任何东西.我们将解决这个问题.

Since we have multiple view types, thus multiple viewHolders, we need to create a plain java object that holds the common information. I called mine Cell(). Call it whatever suits your needs. We'll get to that.

因此,现在您有两个viewHolder类:NameViewHolder()ButtonViewHolder().

So now you have your two viewHolder classes: NameViewHolder() and ButtonViewHolder().

现在让我们创建Cell类和对象:

Now lets create our Cell class and objects:

open class Cell {
    fun identifier() = this::class.java.name.hashCode()
}

class CellName(
    val name: String
) : Cell()

class CellButton(
    val buttonText: String
) : Cell()

让我解释一下:因此,我创建了一个全局Cell()对象,该对象内部具有一个标识符函数,该函数给了我一个类哈希.此函数稍后将为我们提供获取视图类型的服务.在我的RecyclerView中,我不使用Int或其他东西来标识我的视图类型,而是使用对象类本身的哈希值.哈希是每个类的唯一字符串.因此,如果我的适配器在其项列表中偶然发现了一个名为CellName()的对象,则recyclerView使用我的identifier()函数获取其哈希,并意识到该视图的类型为Name,而不是Button(来自您的示例)以上). 其他类扩展了全局Cell()类,并具有其自定义的单独逻辑.给他们您喜欢或需要的任何参数.

Let me explain: So I created a global Cell() object that has an identifier function inside that gives me a class hash. This function will serve us later to get our view type. In my RecyclerView I don't use Int or other things to identify my view types, but the hash of my object class itself. The hash is a unique string for every class. So if my adapter, in its list of items, stumbles upon a object that is CellName(), the recyclerView gets its hash using my identifier() function and realises that the view type for this is Name, not a Button (from your example above). The other classes extend the global Cell() class and have their custom individual logic. Give them whatever parameters you like or need.

现在,在适配器内部,我们将像这样的参数添加单元列表:

Now inside our adapter we will add our list of Cells as a parameter like this:

class MyAdapter(
   var items: ArrayList<Cell> = ArrayList()
): RecyclerView.Adapter<RecyclerView.ViewHolder>() {

请确保完全像上面那样实现RecyclerView.Adapter,否则多视图将无法工作.

Make sure you implement the RecyclerView.Adapter exactly like above, otherwise the multiple view will not work.

现在,用于选择viewType的getItemViewType重写方法将如下所示:

Now the getItemViewType override method that chooses your viewTypes will look like this:

override fun getItemViewType(position: Int) = items[position].identifier()

如您所见,在这里,我们使用了我之前谈到的identifier()函数,以使适配器知道根据Cell()类哈希选择哪种视图类型.

As you can see, we use the identifier() function I previously talked about, here, to let the adapter know what view type to choose based on the Cell() class hash.

现在将onCreateViewHolder的视图放大:

Now the onCreateViewHolder where your views get inflated:

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): 
     RecyclerView.ViewHolder {
        return when (viewType) {
           CellName::class.java.name.hashCode() -> 
                NameViewHolder(parent.inflate(R.layout.your_name_view)
           CellButton::class.java.name.hashCode() -> 
                ButtonViewHolder(parent.inflate(R.layout.your_button_view)
        }
     }

现在,当适配器找到名称"视图类型时,它将使用所需的布局对NameViewHolder进行膨胀,对具有ButtonViewHolder的Button进行相同的设置.接下来,onBindViewHoder:

Now, when the adapter finds a Name view type, it inflates the NameViewHolder with the desired layout, and same for the Button with ButtonViewHolder. Next, onBindViewHoder:

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (val item = items[holder.adapterPosition]) {
            is CellName -> (holder as NameViewHolder).bind(item)
            is CellButton -> (holder as ButtonViewHolder).bind(item)
         }}

基本上,对于每种类型的单元格类,您都可以从viewHolder类访问绑定函数.

Basically for each type of cell class, you access the bind functions from your viewHolder classes.

使用recyclerView适配器就可以了.到目前为止,这是整个文件(接下来我们将继续创建您的单元格列表):

That's about it with the recyclerView adapter. This is the whole file so far(next we will move on to creating your list of cells):

class MyAdapter(
    private var items: ArrayList<Cell> = ArrayList()
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    override fun getItemCount(): Int  = items.size
    override fun getItemViewType(position: Int) = items[position].identifier()

    @Suppress("HasPlatformType")
    fun ViewGroup.inflate(@LayoutRes resId: Int) = LayoutInflater.from(this.context)
        .inflate(resId, this, false)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
            RecyclerView.ViewHolder {
        return when (viewType) {
            CellName::class.java.name.hashCode() ->
                NameViewHolder(parent.inflate(R.layout.your_name_view))

            CellButton::class.java.name.hashCode() ->
                ButtonViewHolder(parent.inflate(R.layout.your_button_view))

        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (val item = items[holder.adapterPosition]) {
            is CellName -> (holder as NameViewHolder)
                .bind(item)
            is CellButton -> (holder as ButtonViewHolder)
                .bind(item)
        }
    }
}

class NameViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    fun bind(cell: Cell) {
        //Do your bindViewHolder logic in here
    }
}

class ButtonViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    fun bind(cell: Cell) {
        //Do your bindViewHolder logic in here
    }
}

open class Cell {
    fun identifier() = this::class.java.name.hashCode()
}

class CellName(
    val name: String
) : Cell()

class CellButton(
    val buttonText: String
) : Cell()

请确保滚动上面的文本,因为它很大以适合显示.另外,

Make sure you scroll the above text snipped as it's very large to fit. Also, the

@Suppress("HasPlatformType")
fun ViewGroup.inflate(@LayoutRes resId: Int) = LayoutInflater.from(this.context)
    .inflate(resId, this, false)

是Kotlin扩展功能,可以使布局膨胀更快.按原样使用.

is a Kotlin Extension Function that makes layout inflation faster. Use as is.

接下来,创建您的单元格列表:

Next, your cell list creation:

fun createCells(
    // Whatever params.
): ArrayList(Cell){
    val cellList = ArrayList<Cell>()
    var firstCell = CellName("Christina")
    val secondCell = CellName("Mary")
    val thirdCell = CellButton("View More!")
    cellList.add(firstCell)
    cellList.add(secondCell)
    cellList.add(thirdCell)
    // Note that here you can do whatever you want, use forEach, for, anything to 
    create a full list of Cells with your desired information.
    return cellList
}

在具有适配器的任何活动/片段中使用此功能.

Use this function in any activity/fragment where you have that adapter.

val myAdapter = MyAdapter(createCells())
recyclerView.adapter = myAdapter

就是这样.随意自定义您的单元格并根据需要查看视图类型.记住,对于recyclerView中所需的每种新视图类型,都必须为其创建一个单元格类和一个视图持有者类. 这是我关于多种视图类型的完整教程.并回答您有关如何执行此操作的明确问题:

That's it. Feel free to customise your cells and view types as much as you want. Remember that for each new view type you need in your recyclerView, you have to create a cell class and a view holder class for it. This is my full tutorial on multiple view types. And to answer your explicit question of how to do this:

if (position % 5 == 0 && position != 0) {
    return R.layout.button10th
} else {
    return R.layout.checkitout
} //Your code from your question here

...你不知道.您在recyclerView中不再需要这种逻辑.适配器只能接收一个单元列表,仅此而已.这只会阻止大量的错误,并使您的代码更整洁,更易于阅读.您只需在createCells()函数中创建单元格,例如:

... you don't. You don't to this logic anymore inside the recyclerView. The adapter must only receive a list of Cells and nothing more. This just prevents a full load of bugs and makes your code cleaner and easier to read. You just create your cells in the createCells() function like:

something.forEachIndexed {index, item ->
if(index % 5 == 0 && position != 0)
    cellList.add(CellButton("button_stuff"))
else
    cellList.add(CellName("blabla"))
}

,您无需担心产品编号和位置.只需使用createCells()函数完成整个逻辑,但返回单个完整列表,适配器便知道该怎么做.

and you won't have to worry about item numbers and positions anymore. Just use the createCells() function to do your entire logic but return a single full list and the adapter knows what to do.

如果您想知道如何使用bindViewHolder中的bind()函数,则可以在适配器中正常执行该代码块中的所有操作,例如在textViews和按钮中设置文本,滑动或通过资源链接创建按钮功能.我将实际解释如何实现按钮功能和bind():

And if you're wondering what to do with the bind() function inside your bindViewHolder, you can do whatever you would do in that code block normally, inside your adapter, like setting texts in textViews and buttons, setting images with Glide or by resource linking, create your button functionality. I'll actually explain how to do your button functionality and bind():

请记住我们如何在单元格对象中设置所需的信息.

Remember how we already set our desired information in our cell objects.

class ButtonViewHolder(itemView: View) : 
     RecyclerView.ViewHolder(itemView) {

     fun bind(cell: Cell) {
      //Do your bindViewHolder logic in here
     }
}

在这里您可以访问该对象,因此让我们创建一个按钮并为其添加回调.为此,您必须使用回调变量来更新绑定函数.

Here you have access to that object, so let's create a button and add a callback for it. In order to do so, you have to update your bind function with a callback variable.

fun bind(cell: Cell, buttonCallback: (() -> Unit)) {
    //Do your bindViewHolder logic in here

    itemView.yourXMLTitleWhatever.text = cell.titleText //(if you have a title for example)

    itemView.yourXMLButton.setOnClickListener {
         buttonCallback.invoke()
    }
}

invoke函数告诉您的回调函数该按钮已被按下.现在,为了使回调起作用,我们需要在适配器中将回调变量声明为public.因此,在适配器内部添加以下内容:

The invoke function tells your callback that the button has been pressed. Now in order to make the callback work, we need to declare the callback variable as public in your adapter. So inside your adapter add this:

class MyAdapter(
    private var items: ArrayList<Cell> = ArrayList()
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    var buttonCallback: (() -> Unit) = {}

(...)

同样不要忘记将var作为参数添加到适配器中的绑定调用中:

And also don't forget to add the var as parameter to your bind call here in the adapter:

所以代替:

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (val item = items[holder.adapterPosition]) {
            is CellName -> (holder as NameViewHolder).bind(item)
            is CellButton -> (holder as ButtonViewHolder).bind(item)
         }}

我们将拥有:

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (val item = items[holder.adapterPosition]) {
            is CellName -> (holder as NameViewHolder).bind(item)
            is CellButton -> (holder as ButtonViewHolder).bind(item, buttonCallback)
         }}

基本上,这种回调变量是接口的Kotlin快捷方式,在Java中,当您创建一个接口来处理适配器单击时,它会做同样的事情.

Basically this type of callback variable is a Kotlin shortcut for an interface that would do the same thing, you know, in Java, when you create an interface to handle your adapter clicks.

但是我们还没有完成,请确保您的适配器回调变量不是私有的,并在您的ACTIVITY中执行以下操作以访问它:

But we are not done yet, make sure your adapter callback variable is not private, and in your ACTIVITY, do this to access it:

myAdapter.onButtonClick = { 
     //add your button click functionality here (like activity change or anything).
 }

其中myAdapter = MyAdapter(cellList)

希望我能帮上忙.

这篇关于Kotlin recyclerview有2种视图类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆