Skip to content Skip to sidebar Skip to footer

Kotlin Recyclerview Data Not Showing

Our question is how to show Parent and Child data from two SQLite DB tables? We have two tables that are added to two ArrayLists childList and parentList. Here are the Model Class'

Solution 1:

The way I would do it is flatten the list edit: like this in the constructor

val items : MutableList<Any> = mutableListOf<Any>()    

init() {
   parents //parents should be passed as a constructor argument
         .asSequence() //this is only needed if you want to also order them
         .sortedBy { it.idD } //again only if you want to sort them
         .forEach { 
              items.add(it)
              items.addAll(it.children)
          }
}

I would create a List < Any > (or if both parent and child implement an interface a List< IItem > ) that would contain all the data as you wish to see it, so for example it could look like this:

val items : List<Any> = listOf(parent1, child11,child12,child13,parent2,child12,child13,child14.....etc)

then I would implement a single adapter with multiple view types like this:

overridefungetItemCount(): Int {
    return items.size
}

fungetItem(position: Int) : Any { //or the interface return items[position]
}

override getItemViewType (position: Int) : Int {
    if (getItem(position) is ModelParent)
        return0return1
}

overridefunonCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    var view : View? = nullif (viewType == 0) {
        view = LayoutInflater.from(parent.context).inflate(R.layout.parent_layout,parent,false)
        return ParentViewHolder(view);
    } else {
       view = LayoutInflater.from(parent.context).inflate(R.layout.child_layout,parent,false)
        return ChildViewHolder(view);
    }
}

and then I would use two view holders to represent how the data is shown for the specific item

inner classParentViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView){ ...}
 inner classChildViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView){ ...}

after that I could perform different bindings by getting the type of the view ,something like this:

overridefunonBindViewHolder(holder: ViewHolder, position: Int) {
    if (getItemType(position) == 0) {
        (holder as ParentViewHolder).bindData(....)
    } else {
        (holder as ChildViewHolder).bindData(....)
    }
}

edit: Here is the complete adapter I built, it is based on two layout files:

list_item_child:

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/card_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_margin="2dp"
    card_view:cardBackgroundColor="#fff"
    card_view:cardCornerRadius="5dp"
    card_view:cardElevation="4dp"
    card_view:cardUseCompatPadding="true">

    <TextView
        android:id="@+id/child_item"
        style="@style/Base.TextAppearance.AppCompat.Display3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:padding="20dp"
        android:textSize="24sp"
        android:textStyle="bold"
        tools:text="Dept Header" />

</android.support.v7.widget.CardView>

list_item_parent:

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/card_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_margin="2dp"
    card_view:cardBackgroundColor="#fff"
    card_view:cardCornerRadius="5dp"
    card_view:cardElevation="4dp"
    card_view:cardUseCompatPadding="true">

    <TextView
        android:id="@+id/parent_department"
        style="@style/Base.TextAppearance.AppCompat.Headline"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:padding="20dp"
        tools:text="Dept Header"
        android:textSize="24sp"
        android:textStyle="bold" />

</android.support.v7.widget.CardView>

and the adapter would look something like this:

import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView

dataclassModelParent(val id: Int, val children: List<ModelChild>)
dataclassModelChild(val id: Int)


classJoinAdapter(internalvar context: Context, val parents: List<ModelParent>) : RecyclerView.Adapter<JoinAdapter.MyViewHolder>() {
    val items = mutableListOf<Any>()

    init {
        parents //parents should be passed as a constructor argument
                .forEach {
                    items.add(it)
                    items.addAll(it.children)
                }
    }

    overridefungetItemCount(): Int = items.size;


    fungetItem(position: Int): Any = items[position]

    overridefungetItemViewType(position: Int): Int = if (getItem(position) is ModelParent) 0else1overridefunonCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        var view: View? = nullif (viewType == 0) {
            view = LayoutInflater.from(parent.context).inflate(R.layout.rv_list_item_parent, parent, false)
            return ParentViewHolder(view!!)
        } else {
            view = LayoutInflater.from(parent.context).inflate(R.layout.rv_list_item_child, parent, false)
            return ChildViewHolder(view!!)
        }
    }

    overridefunonBindViewHolder(holder: MyViewHolder, position: Int) = holder.bindData(position, getItem(position))

    innerabstractclassMyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        abstractfunbindData(position: Int, item: Any)

    }

    innerclassParentViewHolder(view: View) : MyViewHolder(view) {
        var parentDept: TextView = view.findViewById(R.id.parent_department) as TextView

        overridefunbindData(position: Int, item: Any) {
            val parent = item as? ModelParent ?: return
            parentDept.text = parent.dept
        }
    }

    innerclassChildViewHolder(view: View) : MyViewHolder(view) {
        var childItem: TextView = view.findViewById(R.id.child_item) as TextView

        overridefunbindData(position: Int, item: Any) {
            val child = item as? ModelChild ?: return
            childItem.text = child.item
        }
    }

}

this when used on a single recyclerview will display all children under their parent in a list

Solution 2:

Here is a answer and some observations about the design of this app The answer does NOT place both the parent and child data on the same Recycler View List But the answer may help to solve the issue With further exploration! What we did was create the parent list and when the Parent Dept is clicked on the corresponding Child Items will be displayed. This may be a more functional design while shopping you only need to look at Produce items while in that section. This means less scrolling through a single master list of Parents and Children.

A word or two about the design we are guessing a grocery store might have 20 or more Departments (Parents) so how you plan to know which Items (Child data) is connected to the appropriate Dept (Parent) needs drastic redesign when building these two lists.

If you downloaded the code from GitHub you will have a better grasp about this design deficiency

Here is all the code with the XML files

Main Activity navigates to View Activity

funonViewAll(view: View){
    val intent = Intent(this,ViewActivity::class.java)
    intent.putExtra("pickADAPTER",2)
    startActivity(intent)
}

Here is the View Activity

classViewActivity : AppCompatActivity() {

lateinitvar recyclerView: RecyclerView

privatevar RecyclerAdapter1: ViewAdapter? = nullprivatevar RecyclerAdapter2: ViewChildAdapter? = nullprivatevar linearLayoutManager: LinearLayoutManager? = nullprivateval db = DBHelper(this)

privatevar parentList:List<ModelParent> = ArrayList()
privatevar childList:List<ModelChild> = ArrayList()

var idD = 0var whichADAPTER = 0overridefunonCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_view)
    val bundle: Bundle = intent.extras
    idD = bundle.getInt("BIGi", 0)
    whichADAPTER = bundle.getInt("pickADAPTER",0)

    initRecycler()

}// end onCreateoverridefunonResume() {
    super.onResume()
    initDB()
}

privatefuninitDB() {
    parentList = db.queryDEPT()
    childList = db.queryCHILD(idD)
    // queryCHILD only selects records with a fkI equal to idD// SEE THE ModelChild and ModelParentif(parentList.isEmpty()){
        title = "No Records in DB"
    }else{
        title = "Parent List"
    }

    if(whichADAPTER == 2) {
        RecyclerAdapter1 = ViewAdapter(parents = parentList, context = applicationContext)
        (recyclerView as RecyclerView).adapter = RecyclerAdapter1
    }else{
        RecyclerAdapter2 = ViewChildAdapter(children = childList)
        (recyclerView as RecyclerView).adapter = RecyclerAdapter2
    }
}

privatefuninitRecycler() {
    val db = DBHelper(this)
    childList = db.queryITEM()
    parentList = db.queryDEPT()

    recyclerView = rv_parent

    recyclerView = this.findViewById(R.id.rv_parent)
    RecyclerAdapter1 = ViewAdapter(parents = parentList, context = applicationContext)
    linearLayoutManager = LinearLayoutManager(applicationContext)
    (recyclerView as RecyclerView).layoutManager = linearLayoutManager!!

}

Here the XML file for View Activity

<android.support.v7.widget.RecyclerView
android:id="@+id/rv_parent"android:layout_width="match_parent"android:layout_height="match_parent" />

Here is the View Adapter

classViewAdapter(privateval parents: List<ModelParent>, internalvar context: Context):RecyclerView.Adapter<ViewAdapter.ViewHolder>() {
overridefunonCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    val view = LayoutInflater.from(parent.context).inflate(R.layout.the_view,parent,false)
    return ViewHolder(view)
}

overridefungetItemCount(): Int {
    return parents.size
}

overridefunonBindViewHolder(holder: ViewHolder, position: Int) {
    val parent = parents[position]
    holder.textView.text = parent.dept

    holder.editCLICK.setOnClickListener {
        val i = Intent(context, ViewActivity::class.java)
        i.putExtra("BIGi",parent.idD)
        i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        context.startActivity(i)
    }
}

innerclassViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView){
    //val recyclerView : RecyclerView = itemView.rv_childval textView: TextView = itemView.textView
    var editCLICK: RelativeLayout = itemView.findViewById(R.id.editCLICK) as 
    RelativeLayout

And the inflated XML

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"xmlns:card_view="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/card_view"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_margin="2dp"card_view:cardBackgroundColor="#FF0000"card_view:cardCornerRadius="5dp"card_view:cardElevation="4dp"card_view:cardUseCompatPadding="true">

<RelativeLayout
    android:id="@+id/editCLICK"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textView"
        style="@style/Base.TextAppearance.AppCompat.Subhead"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignStart="@+id/rv_child"
        android:layout_alignParentTop="true"
        android:padding="10dp"
        android:background="@color/color_lightGray"
        android:text="Dept Header"
        android:textColor="@color/color_Purple"
        android:textSize="24sp"
        android:textStyle="bold" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_child"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:layout_alignParentBottom="true"
        android:layout_marginTop="50dp"
        android:layout_marginBottom="0dp"
        android:orientation="horizontal"
        android:paddingLeft="4dp"
        android:paddingTop="6dp"
        tools:layout_editor_absoluteX="74dp" />

</RelativeLayout>

This is the search routine in DBHelper

funqueryCHILD(fkI: Int): List<ModelChild> {
    val db = this.writableDatabase
    val childList = ArrayList<ModelChild>()
    val selectQuery = "SELECT  * FROM $CHILD_TABLE WHERE $colCFK = ?"val cursor = db.rawQuery(selectQuery, arrayOf(fkI.toString()))
    if (cursor != null) {
        if (cursor.moveToFirst()) {
            do {
                val contact = ModelChild()
                contact.idI = Integer.parseInt(cursor.getString(cursor.getColumnIndex(colidI)))
                contact.item = cursor.getString(cursor.getColumnIndex(colItem))
                contact.fkI = Integer.parseInt(cursor.getString(cursor.getColumnIndex(colCFK)))
                childList.add(contact)
            } while (cursor.moveToNext())
        }
    }
    cursor.close()
    return childList
}

One thing to make note of we attached the OnClickListener to a Relative Layout

 holder.editCLICK.setOnClickListener

Why we are not sure you can obtain parent.idD type information from a RecyclerView set as a listener? We need to explore this but in our testing the code failed when we tried.

Solution 3:

In the ViewActivity class, you have first set the recyclerView adapter to ViewAdapter and then to ViewChildAdapter thus, now, the adapter of recyclerView is ViewChildAdapter instead of ViewAdapter. Remove this line and the problem will be resolved.

Post a Comment for "Kotlin Recyclerview Data Not Showing"