MatiR
MatiR

Reputation: 13

Implementation of several views in one recyclerViews

How can I implement different views in one recyclerViews in Kotlin ???

I want to create an application containing legal codes. My problem is that individual legal provisions are divided into chapters. And if I can create a progran that will display all the recipes for me, I don't really know how to put it in the recyclerView between the layout with specific legal provisions layout with information about the number and title of the chapter.

The code below still shows me the same view.

package pl.nynacode.naukapraw
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.cart_view_legal_name.view.*
import kotlinx.android.synthetic.main.chapter_layout.view.*

class MyAdapter : RecyclerView.Adapter<MyAdapter.MyViewHolder>(){
    class MyViewHolder(val view: View, val view2: View):RecyclerView.ViewHolder(view) {

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val layoutInflater= LayoutInflater.from(parent.context);
        val legalName = layoutInflater.inflate(R.layout.cart_view_legal_name, parent ,false);
        val chapterName = layoutInflater.inflate(R.layout.chapter_layout, parent,false);
        return MyViewHolder(legalName, chapterName);
    }

    override fun getItemCount(): Int {
        return KodeksKarny.nrArticle.size;
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        when(position){
            0->{
                val chapter = holder.view2.tvChapterName;
                chapter.setText(KodeksKarny.nrArticle[position])
            }
            else->{
                val nrArticle = holder.view.nrArt;
                val textArticle=holder.view.txtArt;

                nrArticle.setText(KodeksKarny.nrArticle[position]);
                textArticle.setText(KodeksKarny.txtArticle[position]);

                // obsługa klikniecia na przycisk
                nrArticle.setOnClickListener{
                    if (textArticle.visibility == View.GONE){
                        textArticle.visibility = View.VISIBLE
                    }else textArticle.visibility = View.GONE
                }
            }
        }

    }
}

I will add that I'm a beginner and I can't do much yet

Upvotes: 1

Views: 308

Answers (1)

Jenea Vranceanu
Jenea Vranceanu

Reputation: 4694

RecyclerView.Adapter has a method called fun getItemViewType(position: Int): Int that returns the type of view on a given position.

Based on that function you can create different view holders or pass to the same view holder type different layouts (but avoid last one).

You simply need to override a function in your adapter and decide the type of an item at that position:

override fun getItemViewType(position: Int): Int {
    val item = getItem(position)
    // the code below is just an example. 
    val type = when (item) {
        is Header -> HEADER_TYPE
        is NotHeader -> NOT_HEADER_TYPE
    }
    return type
}

Where you could define these types? In companion object for example:

class YourAdapter: ... {
    companion object {
        private const val HEADER_TYPE = 0
        private const val NOT_HEADER_TYPE = 1
    }

    ...
}

Later in onCreateViewHolder and onBindViewHolder you can create different view holders and bind to those view holders the data you have.

class YourAdapter: ... {
    companion object {
        private const val HEADER_TYPE = 0
        private const val NOT_HEADER_TYPE = 1
    }

    ...
    override fun getItemViewType(position: Int): Int {
        val item = getItem(position)
        // the code below is just an example. 
        val type = when (item) {
            is Header -> HEADER_TYPE
            is NotHeader -> NOT_HEADER_TYPE
        }
        return type
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        if (viewType == HEADER_TYPE) {
            // Here you create HeaderViewHolder
        } else {
            val layoutInflater= LayoutInflater.from(parent.context);
            val legalName = layoutInflater.inflate(R.layout.cart_view_legal_name, parent ,false);
            val chapterName = layoutInflater.inflate(R.layout.chapter_layout, parent,false);
            return MyViewHolder(legalName, chapterName);
        }
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val itemViewType = getItemViewType(position)
        if (itemViewType == HEADER_TYPE) {
            // cast MyViewHolder to HeaderViewHolder, for example
            val header = viewHolder as HeaderViewHolder
            header.headerTitle.text = ...
        } else {
            val nrArticle = holder.view.nrArt;
            ... other type
        }

    }
}

Here are the official tutorials on how to create an adapter with different types of views.

What I personally prefer is to implement abstract class BaseViewHolder: RecyclerView.ViewHolder that will be used as a generic type argument of your adapter implementation. This BaseViewHolder should have an abstract method, like abstract fun bind(data: YourDataType). The function will be implemented by view holders that will extend the BaseViewHolder class.

Also, as Kotlin provides us with sealed classes I prefer to create a sealed class and objects that extend from it to hold view holder types so when you implement your onCreateViewHolder method it could avoid else case. But that is just what I like and is not required in any way.

An example of sealed class + objects + onCreateViewHolder:

sealed class Types(val rawType: Int) {
    object Header: Types(0)
    object NotHeader: Types(1)

    companion object {
       fun from(rawType: Int) = 
           when (rawType) {
               Header.rawType -> Header
               NotHeader.rawType -> NotHeader
               else -> throw RuntimeException("No such type")
           }
    }
}

class YourAdapter ... {

    override fun getItemViewType(position: Int): Int {
        val item = getItem(position)
        // the code below is just an example. 
        val type = when (item) {
            is Header -> Types.Header.rawType
            is NotHeader -> Types.NotHeader.rawType
        }
        return type
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder =
        when (Types.from(viewType)) {
            is Types.Header -> // return HeaderViewHolder
            is Types.NotHeader -> // return NotHeaderViewHolder
        }
}

Upvotes: 1

Related Questions