How to use RecyclerView into another RecyclerView's adapter in Android? - android

In my application i have one list and into this this i have another list!(I know this is bad idea for UI but i should develop this!)
For show this lists i used RecyclerView and initialize other RecyclerView into previous RecyclerView adapter!
Activity codes:
class DashboardCardsActivity : BaseActivity(), DashboardCardsContracts.View {
#NonNull
private lateinit var presenter: DashboardCardsPresenterImpl
private var context: Context = this
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: DashboardCardsAdapter
private lateinit var layoutManager: LinearLayoutManager
private val modelList: MutableList<UserPackageOrderResponse.Res.UserPackageOrder> = mutableListOf()
private var isLoadingFlag = false
private var isHasLoadedAll = false
private var nextPage = 1
//Token
private var token = GoodPrefs.getInstance().getString(PrefsKey.USER_JWT_TOKEN.name, "")
override var layoutId: Int = R.layout.activity_dashboard_cards
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//Initialize
context = this
presenter = DashboardCardsPresenterImpl(context, this)
layoutManager = LinearLayoutManager(context)
adapter = DashboardCardsAdapter(context, modelList)
//Init recyclerView
recyclerView = dashboardCards_pullList.recyclerView
recyclerView.initRecyclerView(layoutManager, adapter)
//Set image to loader
dashboardCards_pullList.setColorSchemeResources(R.color.colorAccent)
//Init toolbar
toolbarBase_toolbar.title = getString(R.string.basketCard)
toolbarBase_toolbar.setToolbarBackWithFinish(this)
//Call api
getLazyPullLoader()
}
I write below codes :
class DashboardListAdapter constructor(
val context: Context, val model: MutableList<UserPackageOrderResponse.Res.UserPackageOrder>
) : RecyclerView.Adapter<DashboardCardsAdapter.ViewHolder>() {
private var expansionlayout: ExpansionLayoutCollection = ExpansionLayoutCollection()
private lateinit var registerTimeUtil: TimeUtils
private var registerDateSplit: List<String> = emptyList()
private var registerDate: List<String> = emptyList()
private var orderState: String = ""
private var layoutManager: LinearLayoutManager? = null
private var adapter: DashboardCardsOrderAdapter
private val orderModelList: MutableList<UserPackageOrderResponse.Res.UserPackageOrder.Order> = mutableListOf()
init {
expansionlayout.openOnlyOne(true)
layoutManager = object : LinearLayoutManager(context) {
override fun canScrollVertically(): Boolean {
return false
}
}
adapter = DashboardCardsOrderAdapter(orderModelList)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return ViewHolder(inflater.inflate(R.layout.row_dashboard_card_list, parent, false))
}
override fun getItemCount(): Int {
return model.size
}
#SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
expansionlayout.add(holder.getExpansions())
//header
holder.orderNumber.text = model[position].hashcode
holder.rowNumber.text = "${position + 1}"
//Set date
registerDateSplit = model[position].createdAt.split(" ")
registerDate = registerDateSplit[0].split("-")
registerTimeUtil = TimeUtils(registerDate[0].toInt(), registerDate[1].toInt(), registerDate[2].toInt())
holder.orderRegisterDate.text = registerTimeUtil.getIranianDate()
//Order state
orderState = model[position].status
holder.setViewWithState(orderState, context)
//Content prices
holder.finalPrice.text = "${model[position].price.moneySeparating()} ${context.getString(R.string.toman)}"
holder.paymentPrice.text = "${model[position].price.moneySeparating()} ${context.getString(R.string.toman)}"
holder.postPrice.text = "${model[position].postFee.moneySeparating()} ${context.getString(R.string.toman)}"
holder.discountPrice.text = "${model[position].discount.moneySeparating()} ${context.getString(R.string.toman)}"
//init order list
orderModelList.clear()
orderModelList.addAll(model[position].orders)
layoutManager?.let {
holder.orderList.initRecyclerView(it, adapter)
}
}
}
But when running application show me ForceClose error and show me below message in logCat :
java.lang.IllegalArgumentException: LayoutManager com.app.android.ui.home.fragments.dashboard.activities.carts_list.DashboardCardsAdapter$1#d33c634 is already attached to a RecyclerView: androidx.recyclerview.widget.RecyclerView{5a76c5d VFED..... ......ID 0,0-682,178 #7f080089 app:id/dashboardCard_orderList}, adapter:com.app.android.ui.home.fragments.dashboard.activities.carts_list.DashboardCardsOrderAdapter#83ba9d2, layout:com.app.android.ui.home.fragments.dashboard.activities.carts_list.DashboardCardsAdapter$1#d33c634, context:com.app.android.ui.home.fragments.dashboard.activities.carts_list.DashboardCardsActivity#79c2f05
at androidx.recyclerview.widget.RecyclerView.setLayoutManager(RecyclerView.java:1340)
at com.app.android.utils.ExtensionsKt.initRecyclerView(Extensions.kt:74)
at com.app.android.ui.home.fragments.dashboard.activities.carts_list.DashboardCardsAdapter.onBindViewHolder(DashboardCardsAdapter.kt:77)
at com.app.android.ui.home.fragments.dashboard.activities.carts_list.DashboardCardsAdapter.onBindViewHolder(DashboardCardsAdapter.kt:22)
at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:6781)
at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:6823)
But after change my code with below case not show me any error, but scrolling is very slowly and show lag!
//init order list
orderModelList.clear()
orderModelList.addAll(model[position].orders)
layoutManager = object : LinearLayoutManager(context) {
override fun canScrollVertically(): Boolean {
return false
}
}
layoutManager?.let {
holder.orderList.initRecyclerView(it, adapter)
}
I initialized layoutManager in onBindView(), then not show me error but show me many lag when scrolling on items!
How can i fix this?

You have defined this:
private var layoutManager: LinearLayoutManager? = null
Define another one say,
private var layoutManager1: LinearLayoutManager? = null
Assign that to your second RecyclerView and run it. It should work.
P.S: Not familiar with Kotlin, in Java we use
recyclerview.setLayoutManager(new LinearLayoutManager(MyActivity.this));

Related

How to set OnclickListener in recyclerView for going another activity

**Hello everyone. I am new in android development. I am using room database and I set data in recyclerView. Now I am trying to Do when I click any recyclerview item then I need to go another activity.Anyone Please help me. **
MainActivity RecyclerView Adding code
private fun setUpListOfDataIntoRecyclerView(registerList:ArrayList<registerEntity>,
registerDao: registerDao){
if(registerList.isNotEmpty()){
var itemAdapter = MainAdapter(registerList)
binding?.rvHappyPlaceList?.layoutManager = LinearLayoutManager(this)
binding?.rvHappyPlaceList?.adapter = itemAdapter
binding?.rvHappyPlaceList?.visibility = View.VISIBLE
binding?.noRecordText?.visibility = View.GONE
}else{
binding?.rvHappyPlaceList?.visibility = View.GONE
binding?.noRecordText?.visibility = View.VISIBLE
}
}
MainAdapter
class MainAdapter(val items: ArrayList<registerEntity>):
RecyclerView.Adapter<MainAdapter.MainHolder>() {
//step 2
private var onClickListener: AdapterView.OnItemClickListener? = null
inner class MainHolder(var binding: ItemHappyPlaceBinding): RecyclerView.
ViewHolder(binding.root) {
val llTitle = binding.tvTitle
val llDescription = binding.tvDescription
var llImage = binding.ivPlaceImage
}
fun setOnClickListener(onClickListener: View.OnContextClickListener){
this.onClickListener = onClickListener
}
override fun onBindViewHolder(holder: MainHolder, position: Int) {
val item = items[position]
holder.llTitle.text = item.title
holder.llDescription.text = item.description
holder.llImage.setImageURI(Uri.parse(item.image))
}
I recommend you replace:
private var onClickListener: AdapterView.OnItemClickListener? = null
with this:
var onItemClick: (() -> Unit)? = null
You can also delete the fun setOnClickListener(..){}
And add in your MainHolder the binding.root.setOnClickListener{} like:
inner class MainHolder(var binding: ItemHappyPlaceBinding): RecyclerView.ViewHolder(binding.root) {
init {
binding.root.setOnClickListener {
onItemClick?.invoke()
}
}
val llTitle = binding.tvTitle
val llDescription = binding.tvDescription
var llImage = binding.ivPlaceImage
}
Or if you prefer u can also add the onItemClick?.invoke() in the onBindViewHolder like:
holder.itemView.setOnClickListener {
onItemClick?.invoke()
}
And then in your activity, in fun setUpListOfDataIntoRecyclerView add:
var itemAdapter = MainAdapter(registerList)
itemAdapter.onItemClick = this::goToActivity()
And also add the goToActivity fun like:
fun goToActivity(){
// launch the activity
}

android - RecyclerView and Intent : No value passed for parameter 'ListUserAdapter'

hi im currently using kotlin for my android project,as per instruction,i was told to make an apps that has recycleview to show list item and intent when you click on one of the list shown.
but i have this error when i want to run the app,the error was "No value passed for parameter 'ListUserAdapter'"
here is my code
ListUserAdapter.kt
class ListUserAdapter(private val ListUserAdapter: ArrayList<User>) : RecyclerView.Adapter<ListUserAdapter.ListViewHolder>() {
private lateinit var onItemClickCallBack: OnItemClickCallBack
fun setOnItemClickCallback(onItemClickCallback: OnItemClickCallBack) {
this.onItemClickCallBack = onItemClickCallback
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
val view: View = LayoutInflater.from(parent.context).inflate(R.layout.row_user, parent, false)
return ListViewHolder(view)
}
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
val (name, username) = ListUserAdapter[position]
holder.tvName.text = name
holder.tvUserName.text= username
holder.itemView.setOnClickListener {
onItemClickCallBack.onItemClicked(ListUserAdapter[holder.adapterPosition])
}
}
override fun getItemCount(): Int = ListUserAdapter.size
class ListViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var tvName: TextView = itemView.findViewById(R.id.tv_username)
var tvUserName: TextView = itemView.findViewById(R.id.tv_name)
}
interface OnItemClickCallBack {
fun onItemClicked(data : User)
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var adapter: ListUserAdapter
private lateinit var dataName: Array<String>
private lateinit var dataUsername: Array<String>
private lateinit var dataLocation: Array<String>
private lateinit var dataRepo: Array<String>
private lateinit var dataCompany: Array<String>
private lateinit var dataFollowers: Array<String>
private lateinit var dataFollowing: Array<String>
private lateinit var dataPhoto: TypedArray
private var users = arrayListOf<User>()
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setAdapter()
prepare()
addItem()
}
private fun setAdapter() {
adapter = ListUserAdapter() //error in here
with(binding) {
rvList.adapter = adapter
rvList.layoutManager =
GridLayoutManager(this#MainActivity, 2, GridLayoutManager.HORIZONTAL, false)
rvList.setHasFixedSize(true)
}
adapter.setOnItemClickCallback(object : ListUserAdapter.OnItemClickCallBack{
override fun onItemClicked(user: User) {
val intent = Intent(this#MainActivity, DetailActivity::class.java)
intent.putExtra(DetailActivity.KEY_USER, user)
startActivity(intent)
}
})
}
private fun prepare() {
dataName = resources.getStringArray(R.array.name)
dataUsername = resources.getStringArray(R.array.username)
dataPhoto = resources.obtainTypedArray(R.array.avatar)
dataLocation = resources.getStringArray(R.array.location)
dataRepo = resources.getStringArray(R.array.repository)
dataCompany = resources.getStringArray(R.array.company)
dataFollowers = resources.getStringArray(R.array.followers)
dataFollowing = resources.getStringArray(R.array.following)
}
private fun addItem() {
for (position in dataName.indices) {
val user = User(
dataUsername[position],
dataName[position],
dataLocation[position],
dataCompany[position],
dataRepo[position],
dataFollowers[position],
dataFollowing[position],
dataPhoto.getResourceId(position, -1)
)
users.add(user)
}
}
}
DetailActivity.kt
class DetailActivity : AppCompatActivity() {
private lateinit var binding: ActivityDetailBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDetailBinding.inflate(layoutInflater)
setContentView(binding.root)
setData()
}
private fun setData() {
val dataUser = intent.getParcelableExtra<User>(KEY_USER) as User
with(binding) {
Glide.with(root)
.load(dataUser.photo)
.circleCrop()
.into(ivDetailAvatar)
}
}
companion object {
const val KEY_USER = "key_user"
}
}
just you need to replace adapter = ListUserAdapter() //error in here with adapter = ListUserAdapter(users) then your problem solve
You're just forgetting the constructor parameter :)
Your adapter class needs to receive a ArrayList of User to be instantiated, you already have it in your Activity, you just need to pass it as the constructor parameter :)
I would also rename the parameter name to something like users or usersList instead of `ListUserAdapter but because currently it is misleading, since this parameter is not an adapter, it is a list of users.
Just change the line with error to
adapter = ListUserAdapter(users)
But I think it is best for you to first call prepare() and addItem() and then instantiate your adapter. Or you can instantiate your adapter, but also create a addItems function, since it is a best practice for adapters.
fun addItems(users: List<User>) {
this.ListUserAdapter.addAll(users)
notifyDataSetChanged()
}
and use it after setting everything up inside onCreate doing something like
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setAdapter()
prepare()
addItem()
adapter.addItems(users)
}
but you could probably reorganize these methods to improve readability as well, but it will work :)

How to use ViewModel in RecyclerView correctly?

This Line is wrong adapter = ItemAdapter(applicationContext,userViewModel.getListUsers())
**Because the adapter parameters context: Context, arrayList: ArrayList (but not MutableLiveData) **
I don’t know what to do about it, and I’m not entirely sure if I am using the LiveData correctly.
My Adapter
class ItemAdapter(var context: Context, private var arrayList: ArrayList<NumberModel>):RecyclerView.Adapter<ItemAdapter.ItemHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemHolder{
val itemHolder = LayoutInflater.from(parent.context).inflate(
R.layout.grid_layout_list_item,
parent,
false
)
return ItemHolder(itemHolder)
}
override fun onBindViewHolder(holder: ItemHolder, position: Int) {
var positionOfNumber:NumberModel = arrayList.get(position)
holder.textOfNumber.text = positionOfNumber.numberOfElement
holder.button.setOnClickListener {
var positionForDelete = holder.adapterPosition
arrayList.removeAt(positionForDelete)
notifyItemRemoved(positionForDelete)
notifyItemRangeChanged(positionForDelete,arrayList.size)
}
}
override fun getItemCount(): Int {
return arrayList.size
}
class ItemHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var textOfNumber = itemView.findViewById<TextView>(R.id.numberTextView)
var button:Button = itemView.findViewById(R.id.buttonClick)
}
}
MainActivity
recyclerView = findViewById(R.id.recyclerViewList)
gridLayoutManager = GridLayoutManager(applicationContext,2,LinearLayoutManager.VERTICAL,false)
recyclerView?.layoutManager = gridLayoutManager
recyclerView?.setHasFixedSize(true)
recyclerView?.adapter = adapter
userViewModel.getListUsers().observe(this, Observer {
it?.let {
adapter = ItemAdapter(applicationContext,userViewModel.getListUsers())
}
})
}
ViewModel
class MainActivityViewModel : ViewModel() {
var elementsList: MutableLiveData<ArrayList<NumberModel>> = MutableLiveData()
init {
elementsList.value = setElements()
}
fun getListUsers() = elementsList
private fun setElements() : ArrayList<NumberModel> {
var itemArrayList:ArrayList<NumberModel> = ArrayList()
itemArrayList.add(NumberModel("1"))
itemArrayList.add(NumberModel("2"))
itemArrayList.add(NumberModel("3"))
itemArrayList.add(NumberModel("4"))
itemArrayList.add(NumberModel("5"))
itemArrayList.add(NumberModel("6"))
itemArrayList.add(NumberModel("7"))
itemArrayList.add(NumberModel("8"))
itemArrayList.add(NumberModel("9"))
itemArrayList.add(NumberModel("10"))
return itemArrayList
}
Reason for Because the adapter parameters context: Context, arrayList: ArrayList (but not MutableLiveData) this error.
we are passing the MutableLiveData instead of ArrayList. so we need to pass the ArrayList as the second parameter.
userViewModel.getListUsers().observe(this, Observer {
it?.let { list->
adapter = ItemAdapter(applicationContext,list)
}
})
userViewModel.getListUsers() will give the ArrayList so we can pass this to get the instance of ItemAdapter
instead of passing the applicationContext we can give thisor requireActivity() (will give the MainActivity context),
if we pass the applicationContext, we may lead to a memory leak we have to pass the right context to the object.

How to make delete button to delete recyclerview items in kotlin?

I made selectDelete button on the main activity instead of RecyclerView.
When I click that button, I want to delete the items which are checked.
There is an error that checks the RecyclerView item checkbox and unchecks the item when scrolling.
Activity
class CartViewActivity : AppCompatActivity(), SwipeRefreshLayout.OnRefreshListener {
private val tag = this::class.java.simpleName
lateinit var adapter: CartItemRecyclerAdapter
var itemList: MutableList<CartItemDataVo> = arrayListOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_cart_view)
selectDelete.setOnClickListener {
if (checkBox.isChecked) {
*adapter.removeItems()*
}
}
swipeRefreshLo.setOnRefreshListener(this)
itemList.add(CartItemDataVo("item1", 1, 16800, "cart_doll", false))
itemList.add(CartItemDataVo("item2", 1, 16800, "cart_cup", false))
itemList.add(CartItemDataVo("item3", 1, 30000, "cart_perfume", false))
itemList.add(CartItemDataVo("item4", 1, 16800, "cart_fan", false))
adapter = CartItemRecyclerAdapter(this, this, itemList)
recycler_view.adapter = adapter
recycler_view.layoutManager =
androidx.recyclerview.widget.LinearLayoutManager(applicationContext)
}
override fun onRefresh() {
swipeRefreshLo.isRefreshing = false
}
}
Adapter:
class CartItemDataVo(
title: String,
itemNumber: Int,
pointValue: Int,
imageView: String,
CheckBox: Boolean
) {
var title: String = title
var itemNumber: Int = itemNumber
var pointValue: Int = pointValue
var image: String = imageView
var isChecked: Boolean = CheckBox
}
class CartItemRecyclerAdapter(
val context: Context,
private var activity: Activity,
private var dataList: MutableList<CartItemDataVo>
) : RecyclerView.Adapter<CartItemRecyclerAdapter.Holder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val view = LayoutInflater.from(context).inflate(R.layout.cart_item_list, parent, false)
return Holder(view)
}
override fun onBindViewHolder(holder: Holder, position: Int) {
holder?.bind(dataList[position], context)
}
override fun getItemCount(): Int = dataList.size
*#SuppressLint("NewApi")
fun removeItems() {
dataList.removeIf { it.isChecked }
notifyDataSetChanged()
}*
fun toggleItems() {
for (item: CartItemDataVo in dataList) {
var state = item.isChecked
item.isChecked = state.not()
}
notifyDataSetChanged()
}
inner class Holder(itemView: View?) : RecyclerView.ViewHolder(itemView!!) {
var titleText = itemView?.findViewById(R.id.titleText) as TextView
var temNumerTextt = itemView?.findViewById(R.id.textViewItemNumer) as TextView
var pointValueText = itemView?.findViewById(R.id.pointValueText) as TextView
var imageView = itemView?.findViewById(R.id.imageView) as ImageView
var checkBox = itemView?.findViewById(R.id.checkBox) as CheckBox
fun bind(data: CartItemDataVo, context: Context) {
if (data.image != "") {
val resourceId =
context.resources.getIdentifier(data.image, "drawable", context.packageName)
imageView?.setImageResource(resourceId)
} else {
imageView.setImageResource(R.mipmap.ic_launcher)
}
titleText?.text = data.title
temNumerTextt?.text = data.itemNumber.toString()
pointValueText?.text = data.pointValue.toString() + "P"
if (data.isChecked) {
checkBox.buttonDrawable =
checkBox.context.getDrawable(R.drawable.check_box_active_cs)
val layout = activity?.findViewById(R.id.layoutOrder) as LinearLayout
layout.visibility = View.VISIBLE
} else {
checkBox.buttonDrawable = checkBox.context.getDrawable(R.drawable.check_box_no)
val layout = activity?.findViewById(R.id.layoutOrder) as LinearLayout
layout.visibility = View.GONE
}
checkBox?.setOnClickListener {
if (checkBox.isChecked == data.isChecked) {
checkBox.buttonDrawable = it.context.getDrawable(R.drawable.check_box_active_cs)
val layout = activity?.findViewById(R.id.layoutOrder) as LinearLayout
layout.visibility = View.VISIBLE
} else {
checkBox.buttonDrawable = it.context.getDrawable(R.drawable.check_box_no)
val layout = activity?.findViewById(R.id.layoutOrder) as LinearLayout
layout.visibility = View.GONE
}
}
}
}
}
First of all, replace
var itemList: MutableList<CartItemDataVo> = arrayListOf()
with
val itemList: MutableList<CartItemDataVo> = arrayListOf()
You don't want mutable property, which is also mutable collection at the same time. This is very bad practice.
Same situation in adapter
private var dataList : MutableList<CartItemDataVo>
replace with
private val dataList : MutableList<CartItemDataVo>
Then remove private var activity : Activity from your adapter's constructor. Don't put any references to Activity or Fragment in adapter!
Adapter should be only responsible for displaying list.
If in any case, you need communication between Activity or Fragment and adapter, use interface instead. This is not ViewHolder responsibility to hold reference to parent layout and manipulate it!
val layout = activity?.findViewById(R.id.layoutOrder) as LinearLayout
All lines like below should be removed from ViewHolder. If you need set something which belongs to Activity, based on action on list, use interface.
Finally, in adapter add method which will remove checked items from it:
fun removeItems() {
itemList.removeIf { it.isChecked }
notifyDataSetChanged()
}
Add the following line right after checkBox?.setOnClickListener { (as first line in listener)
data.isChecked = !data.isChecked
And replace
selectDelete.setOnClickListener {
if(checkBox.isChecked){
}
}
with
selectDelete.setOnClickListener {
if(checkBox.isChecked){
adapter?.removeItems()
}
}
Bonus:
read about data class, and use it for CartItemDataVo (don't use CheckBox in constructor),
updateData method can be optimized using DiffUtil for RecyclerView,
it can be improved by modifying data in Activity, not in adapter, so responsible would be moved to better place,
read about SOLID principles,
read about MVVM and MVP design patterns.

Kotlin: No adapter attached; skipping layout :RecyclerView

I'm trying to implement Recyclerview in my kotlin code....
And I'm using Retrofit getting data from webservice and plot it into recycler view
MainActivity.class
class MainActivity : AppCompatActivity() {
internal lateinit var jsonApi:MyAPI
private val compositeDisposable = CompositeDisposable()
lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.recycler_drivers)
// init API
val retrofitt = RetrofitClient.instance
if (retrofitt != null) {
jsonApi = retrofitt.create(MyAPI::class.java)
}
//View
recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = LinearLayoutManager(this)
fetchData()
}
private fun fetchData() {
compositeDisposable.add(jsonApi.drivers
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe{drivers->displayData(drivers)}
)
}
private fun displayData(drivers: List<Driver>?) {
val adapter = DriverAdapter(this,drivers!!)
recycler_drivers.adapter = adapter
}
}
Adapter.class
class DriverAdapter(internal var contex:Context, internal var driverList:List<Driver>): RecyclerView.Adapter<DriverViewHolder>()
{
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.driver_layout, parent, false)
return DriverViewHolder(itemView)
}
override fun getItemCount(): Int {
return driverList.size
}
override fun onBindViewHolder(holder: DriverViewHolder, position: Int) {
holder.txt_driver_number.text = driverList[position].driver_number
holder.txt_first_name.text = driverList[position].first_name
holder.txt_ph_number.text = driverList[position].ph_number.toString()
}
}
ViewHolder.class
class DriverViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val txt_driver_number = itemView.txt_driver_number
val txt_first_name = itemView.txt_first_name
val txt_ph_number = itemView.txt_ph_number
}
This is the API interface
interface MyAPI {
#get:GET("data")
val drivers:Observable<List<Driver>>
}
RetrofitClient Object
object RetrofitClient {
private var ourInstance : Retrofit? = null
var instance: Retrofit? = null
get(){
if(ourInstance == null){
ourInstance = Retrofit.Builder()
.baseUrl("http://localhost/BSProject/public/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
return ourInstance!!
}
}
and this is the Model class which is basically the data coming form my localhost server
class Driver {
var driver_number: String = ""
var first_name: String = ""
var ph_number: Int = 0
}
As you can see I have attached an adapter for Recycleview. so why do I keep getting this error?
I have read other questions related to same problem, but none helps.
Either build the recyclerView inside your displayData()
private fun displayData(drivers: MutableList<Driver>?) {
recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = LinearLayoutManager(this)
val adapter = DriverAdapter(this,drivers!!)
recycler_drivers.adapter = adapter
}
Or do what Gabriele Suggested where you attach your adapter to the recyclerviewin onCreate() and add your response data to your adapter after having made the call. This is the ideal approach
class MainActivity: {
lateinit var driverAdapter: DriverAdapter
protected void onCreate() {
...
recyclerView = findViewById(R.id.recycler_drivers)
recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = LinearLayoutManager(this)
val adapter = DriverAdapter(this)
recycler_drivers.adapter = adapter
}
private fun displayData(drivers: List<Driver>?) {
driverAdapter.setDrivers(drivers)
}
And you'd expose a method in your adapter to set the data setDrivers()
class DriverAdapter(internal var contex:Context):
RecyclerView.Adapter<DriverViewHolder>()
{
val drivers = mutableListOf()
...
fun setDrivers(drivers: MutableList<Driver>) {
this.drivers = drivers
notifyDataSetChanged()
}
}
This will get rid of your No adapter attached; skipping layout :RecyclerView error
I think you are seeing this issue because of the asynchronous nature of querying the web service through retrofit. You don't actually assign the RecyclerView.Adapter until after onCreate exits.
Try changing the visiblility of the RecyclerView to Gone until the adapter is applied in displayData, then set it to Visible

Categories

Resources