I'm trying to create a nested recycler view with data received from the server(using Retrofit). The outer recyclerview prints fine, but the inner recyclerview doesn't print at all.
No Error, No Warning...I don't know why. please Help.
my recylcerview blueprint here
Layout Code
the layout xml look like this
(1) activity_main
<androidx.coordinatorlayout.widget.CoordinatorLayout
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=".MainActivity">
<com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.appbar.CollapsingToolbarLayout ...>
<androidx.appcompat.widget.Toolbar
android:id="#+id/homeToolbar" ... />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="#+id/homeLogo" ... />
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/homeBanner" ... />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/outerRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="50dp"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:layout_width="match_parent"
android:layout_height="50dp" ... />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
(2) item_outer_recycler
<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="wrap_content">
<ImageView
android:id="#+id/houseImage"
android:contentDescription="main image"
android:layout_width="match_parent"
android:layout_height="150dp".../>
<TextView
android:id="#+id/houseName"
android:layout_width="200dp"
android:layout_height="30dp"
tools:text="some name" ... />
<TextView
android:id="#+id/houseAddress"
android:layout_width="300dp"
android:layout_height="20dp"
android:layout_marginStart="15dp"
tools:text="some address" ... />
<TextView
android:id="#+id/houseHomepage"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="15dp".../>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/innerRecycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
(3) item_inner_recycler
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tool="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="#+id/houseTag"
android:layout_width="50dp"
android:layout_height="20dp"
android:textSize="12sp"
android:textColor="#878d95"
android:background="#e4e7ed"
android:textAlignment="center"
tool:text="roop top"/>
</LinearLayout>
Adapters Code
(1) outer recycler adapter
class MainHouseHolder(val binding: ItemOuterRecyclerBinding): RecyclerView.ViewHolder(binding.root)
class MainHouseAdapter(private val context: Context, private val data: List<House>):
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun getItemCount(): Int = data.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainHouseHolder =
MainHouseHolder(ItemOuterRecyclerBinding.inflate(LayoutInflater.from(parent.context), parent, false))
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val house = data[position]
val binding = (holder as MainHouseHolder).binding
binding.innerRecycler.adapter = MainTagAdapter(house.tags)
binding.innerRecycler.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
// process outer recycler view
}
}
(2) inner recycler adapter
class MainTagHolder(val binding: ItemInnerRecyclerBinding) : RecyclerView.ViewHolder(binding.root)
class MainTagAdapter(private val data: String) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var tagData :MutableList<String> = mutableListOf()
override fun getItemCount(): Int {
tagData = data.split(",") as MutableList<String>
return tagData.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainTagHolder =
MainTagHolder(ItemInnerRecyclerBinding.inflate(LayoutInflater.from(parent.context), parent, false))
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val binding = (holder as MainTagHolder).binding
binding.houseTag.text = tagData[position]
}
}
Activity Code
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var houseList : List<House>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.homeToolbar)
val apiClient = APIClient().connectInterface
val houseCall = apiClient.getHouseList()
houseCall.enqueue(object : Callback<HouseList> {
override fun onResponse(call: Call<HouseList>, response: Response<HouseList>) {
Log.d("CONNECTOR", "ON_RESPONSE")
houseList = response.body()?.list as List<House>
val adapter = MainHouseAdapter(applicationContext, houseList)
binding.houseList.adapter = adapter
binding.houseList.layoutManager = LinearLayoutManager(applicationContext)
adapter.notifyDataSetChanged()
}
override fun onFailure(call: Call<HouseList>, t: Throwable) {
Log.d("CONNECTOR", "FAILURE")
t.printStackTrace()
}
})
}
}
in item_outer_recycler
change
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/innerRecycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="parent" />
to
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/innerRecycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/houseAddress" />
I think you should change outer adapter from
class MainHouseHolder(val binding: ItemOuterRecyclerBinding): RecyclerView.ViewHolder(binding.root)
class MainHouseAdapter(private val context: Context, private val data: List<House>):
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun getItemCount(): Int = data.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainHouseHolder =
MainHouseHolder(ItemOuterRecyclerBinding.inflate(LayoutInflater.from(parent.context), parent, false))
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val house = data[position]
val binding = (holder as MainHouseHolder).binding
binding.innerRecycler.adapter = MainTagAdapter(house.tags)
binding.innerRecycler.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
// process outer recycler view
}
}
to
class MainHouseAdapter(private val context: Context, private val data: List<House>):
RecyclerView.Adapter<MainHouseAdapter.MainHouseHolder>() {
override fun getItemCount(): Int = data.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainHouseHolder =
MainHouseHolder(ItemOuterRecyclerBinding.inflate(LayoutInflater.from(parent.context), parent, false))
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val house = data[position]
val binding = (holder as MainHouseHolder).binding
binding.innerRecycler.adapter = MainTagAdapter(house.tags)
binding.innerRecycler.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
// process outer recycler view
}
inner class MainHouseHolder(val binding: ItemOuterRecyclerBinding): RecyclerView.ViewHolder(binding.root)
}
and make inner adapter like outer too
class MainTagAdapter(private val data: String) : RecyclerView.Adapter<MainTagAdapter.MainTagHolder>() {
var tagData :MutableList<String> = mutableListOf()
override fun getItemCount(): Int {
tagData = data.split(",") as MutableList<String>
return tagData.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainTagHolder =
MainTagHolder(ItemInnerRecyclerBinding.inflate(LayoutInflater.from(parent.context), parent, false))
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val binding = (holder as MainTagHolder).binding
binding.houseTag.text = tagData[position]
}
inner class MainTagHolder(val binding: ItemInnerRecyclerBinding) : RecyclerView.ViewHolder(binding.root)
}
Oh...the problem was that innerRecycler was set to app:layout_constraintTop_toBottomOf="parent" . I made a stupid mistake.
I'm sorry
I'm trying to implement viewpager when press of a button on secondary activity for that I created a PageAdaper class which extends Recycler view.Adapter .I got viewpager must not be null error on runtime .
PageAdaper.kt
open class PageAdapter(context: Context, arrayList: ArrayList<Uri>) : RecyclerView.Adapter<PageAdapter.MyViewHolder>() {
private var mContext: Context? =null
private var galleryUri= arrayListOf<Uri>()
private var layoutInflater: LayoutInflater? =null
init {
this.mContext=context
this.galleryUri=arrayList
this.layoutInflater=context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
}
private fun onBindViewHolderr(holder: MyViewHolder, position: Int) {
holder.iView.setImageURI(galleryUri[position])
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view=LayoutInflater.from(mContext).inflate(R.layout.image_container,parent,false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return galleryUri.size
}
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val iView: ImageView
init {
super.itemView
iView=itemView.findViewById(R.id.imageContainer)
}
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
onBindViewHolderr(holder, position)
}
}
Image_container.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/imageContainer"
android:contentDescription="#string/gallery" />
</LinearLayout>
Activity_gallery.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageButton
android:id="#+id/galleryBack"
android:layout_width="59dp"
android:layout_height="56dp"
android:background="#android:color/transparent"
android:contentDescription="#string/back_button"
android:src="#drawable/ic_baseline_arrow_back_24"
style="#style/Widget.AppCompat.Button.Colored"
app:layout_constraintHorizontal_bias="0.05"
app:layout_constraintVertical_bias="0.05"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/viewPager"
android:layout_width="409dp"
android:layout_height="729dp"
android:background="#android:color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</androidx.viewpager2.widget.ViewPager2>
</androidx.constraintlayout.widget.ConstraintLayout>
Creation on viewpager on button click from activity_main
gallery.setOnClickListener { // gallery button reference
if (lastImageUri!=null){
val intent= Intent(this,GalleryActivity::class.java)
startActivity(intent) // inflates activity_gallery.xml
val iAdapter=PageAdapter(this,galleryUri) // creating object with params
viewPager.adapter=iAdapter // here comes the error
}
}
Update
GalleryActivity.kt
It contains only setContentview to activity_gallery.xml
Do I need to add anything.
class GalleryActivity : AppCompatActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_gallery)
overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right)
galleryBack.setOnClickListener {
finish()
overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right)
}
}
}
Explain me about this as I'm new to android. Thank you!
So, startActivity will go to the GalleryActivity class. You need to put the lines after that into the GalleryActivity.
Okay, now that you've added your GalleryActivity.kt. You need to make 2 changes. First, in your activity_main:
gallery.setOnClickListener { // gallery button reference
if (lastImageUri!=null){
val intent= Intent(this,GalleryActivity::class.java)
intent.putExtra("IMAGE_URI", galleryUri)
startActivity(intent) // inflates activity_gallery.xml
}
}
Then, in your GalleryActivity.kt
class GalleryActivity : AppCompatActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_gallery)
val galleryUri = getIntent().getStringExtra("GALLERY_URI")
val iAdapter=PageAdapter(this,galleryUri) // creating object with params
viewPager.adapter=iAdapter
overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right)
galleryBack.setOnClickListener {
finish()
overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right)
}
}
}
Try that.
I am developing news app using kotlin but it is showing empty white screen and there is no error or exception on the logcat
below my SportNewsAdapter.kt
class SportNewsAdapter(val context: Context) : RecyclerView.Adapter<SportNewsAdapter.MyViewHolder>() {
var articleList : List<Article> = listOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.news_list,parent,false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return articleList.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.tvMovieName.text = articleList.get(position).title
Glide.with(context).load(articleList.get(position).urlToImage)
.apply(RequestOptions().centerCrop())
.into(holder.image)
}
fun setMovieListItems(movieList: List<Article>){
this.articleList = articleList;
notifyDataSetChanged()
}
class MyViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView!!) {
val tvMovieName: TextView = itemView!!.findViewById(R.id.title)
val image: ImageView = itemView!!.findViewById(R.id.image)
}
}
below news_item.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="150dp"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ImageView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:id="#+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#android:color/white"
android:textStyle="bold"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
below MainActivity.kt
class MainActivity : AppCompatActivity() {
lateinit var recyclerView: RecyclerView
lateinit var sportNewsAdapter: SportNewsAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.recyclerView)
sportNewsAdapter = SportNewsAdapter(this)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = sportNewsAdapter
val apiInterface = SportNewsInterface.create().getNews()
apiInterface.enqueue( object : Callback<List<Article>> {
override fun onResponse(call: Call<List<Article>>?, response: Response<List<Article>>?) {
if(response?.body() != null)
sportNewsAdapter.setMovieListItems(response.body()!!)
}
override fun onFailure(call: Call<List<Article>>?, t: Throwable?) {
}
})
}
}
below activity_main.xml
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
below SportNewsInterface.kt
interface SportNewsInterface {
#GET("v2/top-headlines?country=us&apiKey=da331087e3f3462bb534b3b0917cbee9")
fun getNews() : Call <List<Article>>
companion object {
var BASE_URL = "https://newsapi.org/"
fun create() : SportNewsInterface {
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
return retrofit.create(SportNewsInterface::class.java)
}
} }
below SportNewsResponse.kt where implemented retrofit
data class SportNewsResponse(
val articles: List<Article>,
val status: String,
val totalResults: Int
)
Change recycler view height to wrap content and run again
I'm unable to update my RecyclerView. Tried every solution on stackoverflow but nothing helps. I've tried to add layoutmanager in code, hardcode number of items, made new files, checked if I had duplicate files etc.. I also debugged and confirmed that "onAttachedToRecyclerView" is called, but getItemCount and the others i've listed in the adapter are not called except for the constructor
Adapter:
class BaseAdapter(context: Context, val items: List<Item>) :
RecyclerView.Adapter<LineBoxAdapter.BaseAdapter>() {
override fun getItemCount(): Int = items.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.textView.text = items[position].text
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.simple_item,
parent,
false
)
)
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView = itemView.findViewById<TextView>(R.id.title)
}
}
Function that updates recyclerView:
val listView =
LayoutInflater.from(context).inflate(R.layout.list, viewGroup,
false)
val list = listView.findViewById<RecyclerView>(R.id.
LinearLayoutManager(context)
val adapter = BaseAdapter(context, items)
list.adapter = adapter
adapter.notifyDataSetChanged()
RECYCLERVIEW:
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/items_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:overScrollMode="never"
android:requiresFadingEdge="horizontal"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
SIMPLE_ITEM:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:orientation="horizontal">
<LinearLayout xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="25" />
</LinearLayout>
You should avoid using existing class names from API.
I will post my working code.
simple_item:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/text_simple_item"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
RecyclerAdapter:
class RecyclerAdapter(context: Context, val items: List<Item>) :
RecyclerView.Adapter<RecyclerAdapter.MyViewHolder>() {
override fun getItemCount(): Int = items.size
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.textView.text = items[position].itemName
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
MyViewHolder {
return MyViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.simple_item,
parent,
false
)
)
}
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView = itemView.findViewById<TextView>(R.id.text_simple_item)
}
}
My Activity:
class RecyclerActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_recycler)
val recyler = findViewById<RecyclerView>(R.id.rv_activity_recycler)
val list = mutableListOf(Item("Me"), Item("You"), Item("We"))
val adapter = RecyclerAdapter(this, list)
recyler.layoutManager = LinearLayoutManager(this)
recyler.adapter = adapter
}
}
My Item:
class Item(val itemName: String)
I'm trying to create a simple recyclerView in Kotlin with data that I get via Volley (which I have confirmed returns the correct data), I keep running to the error E/RecyclerView: No adapter attached; skipping layout when in fact I have specified an adapter with the custom adapter class that I created:
class ImageAdapter(var c: Context, var list: ArrayList<Image>) : RecyclerView.Adapter<ImageAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder? {
val layoutInflater = LayoutInflater.from(parent.context)
return ViewHolder(layoutInflater.inflate(R.layout.image_cardview, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val imageUrl = list[position].url
val submitter = list[position].submitter
val color = list[position].color
holder.submitterTV.text = submitter
holder.card.setCardBackgroundColor(Color.parseColor(color))
}
override fun getItemCount() = list.size
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val card = itemView.card
val submitterTV = itemView.submitter
val imageView = itemView.image
}
}
This is my MainActivty class, where I make the actual call for the JSON and attempt to attach my adapter with my ArrayList that I have created:
class MainActivity : AppCompatActivity() {
val images = ArrayList<Image>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
imageList.layoutManager = LinearLayoutManager(applicationContext)
request("https://api.unsplash.com/photos/curated/?client_id=API_KEY")
imageList.adapter = ImageAdapter(applicationContext, images)
}
private fun request(url: String) {
val queue = Volley.newRequestQueue(this)
val stringRequest = JsonArrayRequest(url, Response.Listener<JSONArray> { response ->
try {
for (i in 0..(response.length() - 1)) {
val image: Image = Image(response.getJSONObject(i).getJSONObject("urls").getString("full"), response.getJSONObject(i).getJSONObject("user").getString("username"), response.getJSONObject(i).getString("color"))
images.add(image)
}
imageList.adapter.notifyDataSetChanged()
} catch (e: JSONException) {
e.printStackTrace()
}
}, Response.ErrorListener { })
queue.add(stringRequest)
}
}
I have created a custom data class Image which stores three fields: the imageUrl, submitter and color, all of which are derived from the JSON. I thought that calling notifyDataSetChanged() on my adapter after the request was completed would allow the recyclerView to get updated and show the items, but nothing at all shows up on the screen. Does anyone have any idea as to where I messed up?
You can download the source code from here(Recyclerview In Kotlin Android)
MainActivity.kt:
package com.deepshikha.recyclerviewkotlin
import android.app.Activity
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.view.Window
import android.view.WindowManager
import android.widget.LinearLayout
import kotlinx.android.synthetic.main.activity_main.*
import net.simplifiedcoding.recyclerviewexample.CustomAdapter
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE)
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
setContentView(R.layout.activity_main)
val users = ArrayList<Model_Details>()
users.add(Model_Details("Kaju katli", "Kaju katli, also known as kaju Katari or kaju barfi, is an Indian dessert similar to a barfi.",R.drawable.kaju))
users.add(Model_Details("Doughnut", "The doughnut is popular in many countries and prepared in various forms as a sweet snack that can be homemade or purchased in bakeries, supermarkets, food stalls, and franchised specialty outlets",R.drawable.donuts))
users.add(Model_Details("Panna cotta", "Panna cotta is an Italian dessert of sweetened cream thickened with gelatin and molded. The cream may be aromatized with rum, coffee, vanilla, or other flavorings.",R.drawable.panna_cotta))
users.add(Model_Details("Rose Cookies", "Rose cooky is a famous South Indian snack made during festivals",R.drawable.rosecookies))
users.add(Model_Details("Belgian waffle", "In North America, Belgian waffles are a variety of waffle with a lighter batter, larger squares, and deeper pockets than ordinary American waffles",R.drawable.belgianwaffle))
val obj_adapter = CustomAdapter(users)
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL, false)
recyclerView.adapter = obj_adapter
}
}
CustomAdapter.kt:
package net.simplifiedcoding.recyclerviewexample
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.deepshikha.recyclerviewkotlin.Model_Details
import com.deepshikha.recyclerviewkotlin.R
import kotlinx.android.synthetic.main.adapter_details.view.*
class CustomAdapter(val userList: ArrayList<Model_Details>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
//this method is returning the view for each item in the list
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomAdapter.ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.adapter_details, parent, false)
return ViewHolder(v)
}
//this method is binding the data on the list
override fun onBindViewHolder(holder: CustomAdapter.ViewHolder, position: Int) {
holder.bindItems(userList[position])
}
//this method is giving the size of the list
override fun getItemCount(): Int {
return userList.size
}
//the class is hodling the list view
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItems(user: Model_Details) {
itemView.tv_name.text=user.name
itemView.tv_des.text=user.des
itemView.iv_name.setImageResource(user.image)
}
}
}
Thanks!
Look at following example, I think you this can give you an idea.
You can get the example from here : https://github.com/Siddharha/RecyclerviewTest_Kotlin
Main Activity
class MainActivity : AppCompatActivity() {
private var myAdapter: MyAdapter? = null
private var arrayList: ArrayList<MyItem>? = null
private var layoutManager: RecyclerView.LayoutManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initialize()
setupList()
loaddata()
}
private fun loaddata() {
for (i in 0..9) {
val myItem = MyItem()
myItem.name = "Sid_" + i
myItem.number = "098899876" + i
arrayList!!.add(myItem)
}
myAdapter!!.notifyDataSetChanged()
}
private fun setupList() {
rlItems!!.layoutManager = layoutManager
rlItems!!.adapter = myAdapter
}
private fun initialize() {
arrayList = ArrayList<MyItem>()
layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL, false)
myAdapter = MyAdapter(arrayList!!, this, R.layout.item_cell)
}
}
Adapter Class
internal class MyAdapter(private val arrayList: ArrayList<MyItem>,
private val context: Context,
private val layout: Int) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyAdapter.ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(layout, parent, false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: MyAdapter.ViewHolder, position: Int) {
holder.bindItems(arrayList[position])
}
override fun getItemCount(): Int {
return arrayList.size
}
internal inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItems(items: MyItem) {
itemView.tvName.text = items.name
itemView.tvNumber.text = items.number
}
}
}
Item Class
internal class MyItem {
var name: String? = null
var number: String? = null
}
try to use Delegate Adapters below is reference link.
Ref Link : https://android.jlelse.eu/keddit-part-4-recyclerview-delegate-adapters-data-classes-with-kotlin-9248f44327f7
I have my RecyclerView Adapter which is as follows,
internal class OptionsAdapter : RecyclerView.Adapter<OptionsAdapter.ViewHolder>() {
private val mOptionList = ArrayList<Option>()
private var mOnItemClickListener: OnItemClickListener<Option>? = null
var data: List<Option>
get() = mOptionList
set(list) {
mOptionList.clear()
mOptionList.addAll(list)
notifyDataSetChanged()
}
fun setOnItemClickListener(listener: OnItemClickListener<Option>) {
mOnItemClickListener = listener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.layout_text, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val option = getItem(position)
holder.textViewOption.setText(option.text)
holder.textViewDescription.setText(option.subText)
}
private fun getItem(index: Int): Option {
return mOptionList[index]
}
override fun getItemCount(): Int {
return mOptionList.size
}
internal inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var textViewOption: TextView
var textViewDescription: TextView
var imageViewOptionIcon: ImageView
init {
textViewOption = itemView.findViewById<TextView>(R.id.textview_option)
imageViewOptionIcon = itemView.findViewById<ImageView>(R.id.imageview_option_icon)
textViewDescription = itemView.findViewById<TextView>(R.id.textview_description)
itemView.setOnClickListener {
if (mOnItemClickListener != null) {
mOnItemClickListener!!.onItemClick(getItem(adapterPosition))
}
}
}
}
}
On .MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//rcv is id of Recyclerview
rcv.layoutManager = LinearLayoutManager(this)
rcv.adapter = MyAdpater()
}
Create a new .kt file for creating Adapter
class MyAdpater : RecyclerView.Adapter<ViewHolder>() {
val arr = listOf("A", "B", "C", "D", "E") //static array
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.textView.setText(arr.get(position)) //set text to textview by position
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(TextView(parent.context))
}
override fun getItemCount(): Int {
return arr.count() //return array count
}}
class ViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView){ //ViewHolder with textview}
Recyclerview with different listener for different view
AddressAdapter
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
class AddressAdapter (private val listener: (AddressData, Int,Int) -> Unit):
RecyclerView.Adapter<AddressViewHolder>() {
var lstAddress = emptyList<AddressData>()
fun setData(items: List<AddressData>) {
lstAddress = items
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddressViewHolder {
return AddressViewHolder.create(parent)
}
override fun getItemCount(): Int {
return lstAddress.size
}
override fun onBindViewHolder(holder: AddressViewHolder, position: Int) {
return holder.bind(lstAddress[position], listener, position)
}
fun getData(): List<AddressData> {
return lstAddress
}
}
AddressViewHolder
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_address.view.*
class AddressViewHolder(override val containerView: View) :
RecyclerView.ViewHolder(containerView), LayoutContainer {
companion object {
fun create(parent: ViewGroup): AddressViewHolder {
val view =
LayoutInflater.from(parent.context)
.inflate(R.layout.item_address, parent, false)
return AddressViewHolder(view)
}
}
fun bind(
item: AddressData,
listener: (AddressData, Int,Int) -> Unit,
position: Int
) {
item.address.let {
itemView.tvAddress.text = it
}
itemView.tvEdit.setOnClickListener {
listener.invoke(item, position,R.id.tvEdit)
}
itemView.tvDelete.setOnClickListener {
listener.invoke(item, position,R.id.tvDelete)
}
itemView . tvAddress . setOnClickListener {
listener.invoke(item, position, R.id.tvAddress)
}
}
}
item_address
<?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"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvWorkPlace"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="#dimen/letter_medium"
android:layout_marginTop="#dimen/letter_medium"
android:gravity="top"
android:text="#string/work_place"
android:textColor="#color/black_color"
android:textSize="#dimen/letter_medium"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvDefault"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginStart="#dimen/spacing_medium"
android:text="#string/txt_default"
android:textColor="#002D56"
android:textSize="#dimen/letter_small"
app:layout_constraintBottom_toBottomOf="#+id/tvWorkPlace"
app:layout_constraintStart_toEndOf="#+id/tvWorkPlace"
app:layout_constraintTop_toTopOf="#+id/tvWorkPlace" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="#dimen/spacing_small"
android:text="76 Playfair Road, #04-06, LHK2, Singapore 367996"
android:textSize="#dimen/letter_small"
app:layout_constraintStart_toStartOf="#+id/tvWorkPlace"
app:layout_constraintTop_toBottomOf="#+id/tvWorkPlace" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="#dimen/spacing_large"
android:text="#string/txt_edit"
android:textAllCaps="true"
android:textColor="#color/colorSecondary"
android:textSize="#dimen/letter_small"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="#+id/tvAddress"
app:layout_constraintTop_toBottomOf="#+id/tvAddress" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvDelete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginStart="#dimen/spacing_large"
android:text="#string/txt_delete"
android:textAllCaps="true"
android:textColor="#color/colorPrimaryNew"
android:textSize="#dimen/letter_small"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="#+id/tvEdit"
app:layout_constraintTop_toTopOf="#+id/tvEdit" />
<View
android:layout_width="match_parent"
android:layout_height="0.2dp"
android:layout_marginTop="#dimen/spacing_medium"
android:background="#color/divider_color"
app:layout_constraintTop_toBottomOf="#+id/tvDelete" />
</androidx.constraintlayout.widget.ConstraintLayout>
AddressData
class AddressData (
var code: Int,
var address:String
)
In Activity
private fun setUpAdapter() {
adapter = AddressAdapter(fun(item: AddressData, position: Int, type: Int) {
when (type) {
R.id.tvEdit -> {
context.showToast(
"Editbtn" + position,
showInReleaseBuild = true
)
}
R.id.tvDelete -> {
context.showToast(
"Deletebtn" + position,
showInReleaseBuild = true
)
}
R.id.tvAddress -> {
context.showToast(
"addressbtn" + position,
showInReleaseBuild = true
)
}
}
})
rvAddress.adapter = adapter
rvAddress.setEmptyView(llEmptyViewMain)
val addressData = AddressData(1, "abcd")
val addressData1 = AddressData(2, "abceeed")
lstData = listOf(addressData, addressData1)
adapter.setData(lstData)
}
CustomRecycler Xml
<com.example.views.CustomRecyclerView
android:id="#+id/rvAddress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="#dimen/mediumSize"
android:layout_marginTop="#dimen/mediumSize"
android:layout_marginEnd="#dimen/mediumSize"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:list_type="list"
tools:listitem="#layout/item_address" />
<include layout="#layout/layout_recycler_emptyview" />
I have created a nice adapter may be it helped in reducing code..
class ResultActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.question_list_activity)
if (intent != null) {
var results = intent.getParcelableArrayListExtra<Parcelable>("keyResults") as ArrayList<ResultBO>
if (results.size > 0) {
recycler_view.adapter = ResultAdapter(results, object : OnRecyclerItemClickListener {
override fun onItemClicked(view: View?, position: Int) {
/*var intent = Intent(this#SubjectListActivity,McqActivity::class.java)
intent.putExtra("keyTagBO",subjects.get(position))
startActivity(intent)*/
}
});
}
}
}
}
item_result.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/titleTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Dummy Value"
android:textAlignment="center"
android:textAppearance="#style/TextAppearance.AppCompat.Large" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<TextView
android:id="#+id/option1Tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Dummy"
android:textAlignment="center"
android:textAppearance="#style/TextAppearance.AppCompat.Medium" />
<TextView
android:id="#+id/option2Tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Dummy Value"
android:textAlignment="center"
android:textAppearance="#style/TextAppearance.AppCompat.Medium" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<TextView
android:id="#+id/option3Tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Dummy"
android:textAlignment="center"
android:textAppearance="#style/TextAppearance.AppCompat.Medium" />
<TextView
android:id="#+id/option4Tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Dummy Value"
android:textAlignment="center"
android:textAppearance="#style/TextAppearance.AppCompat.Medium" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
</LinearLayout>
</LinearLayout>
ResultADapter.class
class ResultAdapter(items: List<ResultBO>, onRecyclerItemClickListener: OnRecyclerItemClickListener) : BaseAdapter<ResultBO, ResultViewHolder>(items, onRecyclerItemClickListener) {
override fun onCreateViewHolder(parent: ViewGroup, pos: Int): ResultViewHolder {
return ResultViewHolder(parent, R.layout.item_result,onRecyclerItemClickListener)
}
}
BaseViewHolder.class
abstract class BaseViewHolder<T : BaseModel>(parent: ViewGroup, #LayoutRes itemLayoutId: Int,
var onRecyclerItemClickListener: OnRecyclerItemClickListener) :
RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(itemLayoutId, parent,
false)), View.OnClickListener {
override fun onClick(v: View?) {
onRecyclerItemClickListener.onItemClicked(v, adapterPosition)
}
abstract fun bindData(data: T)
init {
itemView.setOnClickListener(this)
}
}
BaseAdapter
abstract class BaseAdapter<T : BaseModel, U : BaseViewHolder<T>>
(var items: List<T>, var onRecyclerItemClickListener: OnRecyclerItemClickListener)
: RecyclerView.Adapter<U>() {
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: U, pos: Int) {
holder.bindData(items.get(pos))
}
}
ResultViewHolder.class
class ResultViewHolder(parent: ViewGroup, itemLayoutId: Int, onRecyclerItemClickListener: OnRecyclerItemClickListener) : BaseViewHolder<ResultBO>(parent, itemLayoutId, onRecyclerItemClickListener) {
override fun bindData(data: ResultBO) {
itemView.titleTv.setText(data.questionBO.title)
itemView.option1Tv.setText(data.questionBO.options.get(0))
itemView.option2Tv.setText(data.questionBO.options.get(1))
itemView.option3Tv.setText(data.questionBO.options.get(2))
itemView.option4Tv.setText(data.questionBO.options.get(3))
when(data.orignalAnswer()) {
OptionType.A -> itemView.option1Tv.setBackgroundColor(Color.GREEN)
OptionType.B -> itemView.option2Tv.setBackgroundColor(Color.GREEN)
OptionType.C -> itemView.option3Tv.setBackgroundColor(Color.GREEN)
OptionType.D -> itemView.option4Tv.setBackgroundColor(Color.GREEN)
}
if(!data.isCorrectlyAnswered()){
when(data.selectedOption) {
OptionType.A -> itemView.option1Tv.setBackgroundColor(Color.RED)
OptionType.B -> itemView.option2Tv.setBackgroundColor(Color.RED)
OptionType.C -> itemView.option3Tv.setBackgroundColor(Color.RED)
OptionType.D -> itemView.option4Tv.setBackgroundColor(Color.RED)
}
}
}
}