How to initialize Array<List<Model>> in Kotlin? - android

I have a JSON string which coming from rest API and I'm binding that into List<CategoryDO> object. I have all category/sub-category data into this list object List<CategoryDO> but I don't know how to separate sub-categories from those data into Array<List<CategoryDO>> format.
How can I add sub-category list into Array<List<CategoryDO>> object? How can I declare and initialize Array<List<CategoryDO>> in Kotlin?
All categories should be in List<CategoryDO> format and all sub-categories in Array<List<CategoryDO>> format.
For Example:
List<CategoryDO of Cat-1, CategoryDO of cat-2, ... etc>
Array<List<CategoryDO of SubCat-1 of Cat-1, CategoryDO of SubCat-2 of Cat-1>>, List<CategoryDO of SubCat-12 of Cat-2, CategoryDO of SubCat-22 of Cat-2>>, ...etc>>
CategoryDO data class:
data class CategoryDO( #SerializedName("Id")
#Expose
var id: Long? = null,
#SerializedName("Name")
#Expose
var name: String? = null,
#SerializedName("SubCategories")
#Expose
var subCategories: List<CategoryDO>? = null)
Actually, I need to pass this separate Category/Sub-Category things to CategoryAdapter class.
CategoryAdapter class sample:
class CategoryAdapter : BaseExpandableListAdapter {
private var groupItem: List<CategoryDO>
private var contentItem: Array<List<CategoryDO>>
private var context: Context
private var imageOnClickListener: View.OnClickListener
constructor(context: Context, groupItem: List<CategoryDO>, contentItem: Array<List<CategoryDO>>, imageOnClickListener: View.OnClickListener) {
this.groupItem = groupItem
this.contentItem = contentItem
this.context = context
this.imageOnClickListener = imageOnClickListener
}
.
.
.
}

If you need to convert a List<CategoryDO> to an Array<List<CategoryDO>> where the inner List is the subcategory list from each CategoryDO, you can map over the original list and convert the results to an array...
// Given
val categories: List<CategoryDO> = TODO()
val allSubCats: Array<List<CategoryDO>> =
categories.map { it. subCategories }.toTypedArray()

I try json data into recycler view adapter and it is working you can try if solved your problem..
class HeroAdapter(val item: MutableList<Hero>, val context: Context, val itemClick:OnRecyclerViewItemClickListener) : RecyclerView.Adapter<HeroAdapter.ItemViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ItemViewHolder {
val v = LayoutInflater.from(parent?.context).inflate(R.layout.adapter_layout, parent, false)
return ItemViewHolder(v)
}
var onClickListener: OnRecyclerViewItemClickListener? = null
open interface OnRecyclerViewItemClickListener {
fun click(hero: Hero)
}
override fun onBindViewHolder(holder: ItemViewHolder?, position: Int) {
var hero: Hero = item[position]
onClickListener=itemClick
holder?.mTvName?.text = hero.getName()
holder?.mTvBio?.text = hero.getBio()
holder?.mTvReal?.text = hero.getRealname()
holder?.mTvFirst?.text = hero.getFirstappearance()
holder?.mTvTeam?.text = hero.getTeam()
holder?.mTvPublisher?.text = hero.getPublisher()
holder?.mTvCreate?.text = hero.getCreatedby()
Glide.with(context)
.load(hero.getImageurl())
.into(holder?.mIvImage)
holder?.itemView?.setOnClickListener(View.OnClickListener {
this.onClickListener?.click(hero)
})
}
override fun getItemCount(): Int {
return item.size
}
class ItemViewHolder : RecyclerView.ViewHolder {
var mTvName: TextView? = null
var mTvReal: TextView? = null
var mTvCreate: TextView? = null
var mTvBio: TextView? = null
var mTvTeam: TextView? = null
var mTvPublisher: TextView? = null
var mTvFirst: TextView? = null
var mIvImage: ImageView? = null
constructor(itemView: View) : super(itemView) {
mTvName = itemView.findViewById(R.id.alTvName)
mTvReal = itemView.findViewById(R.id.alTvRealName)
mTvFirst = itemView.findViewById(R.id.alTvFirst)
mTvCreate = itemView.findViewById(R.id.alTvcreatedby)
mTvBio = itemView.findViewById(R.id.alTvBio)
mTvTeam = itemView.findViewById(R.id.alTvTeam)
mTvPublisher = itemView.findViewById(R.id.alTvpublisher)
mIvImage = itemView.findViewById(R.id.alIvUserImage)
}
}
}

Related

Update Inner/Nested RecyclerView from BroadCast Receiver

I have a CardViewAdapter that displays a list of folders vertically and then the inner horizontalAdapter displays the contents of the folder horizontally like this:
class CardViewAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var collectionItems : CollectionItems? = null
fun setData(collectionItems : CollectionItems) {
this.collectionItems = collectionItems
this.notifyDataSetChanged()
}
fun updateRow(resourceID : String){
this.collectionItems?.resourceList?.forEach { map ->
map.value.forEach { resourceWState ->
if (resourceWState.component?.uuid == resourceID) {
val index = (this.collectionItems?.resourceList!!.keys).indexOf(map.key)
this.notifyItemChanged(index) // This works but it updates the whole row, not item
}
}
}
}
private inner class ViewHolderCard internal constructor(itemView: View, var resource : ResourceWithState? = null, var tabKeyHolder : Int? = null) : RecyclerView.ViewHolder(itemView) {
internal var fragmentCallback: FragmentCallback? = null
internal var horizontalAdapter: ResourcesByCategoryAdapter? = ResourcesByCategoryAdapter()
internal var mLastClickTime : Long = 0
init {
itemView.inner_recyclerview.layoutManager = LinearLayoutManager(itemView.context, LinearLayoutManager.HORIZONTAL, false)
itemView.inner_recyclerview.isNestedScrollingEnabled = false
itemView.inner_recyclerview.layoutManager!!.isItemPrefetchEnabled = true
itemView.inner_recyclerview.setHasFixedSize(true)
itemView.inner_recyclerview.setItemViewCacheSize(20)
horizontalAdapter?.hasStableIds()
horizontalAdapter?.courseID = courseID
itemView.inner_recyclerview.adapter = horizontalAdapter
fragmentCallback = itemView.context as FragmentCallback
}
internal fun bind(position: Int) {
val dataList = ArrayList(collectionItems!!.resourceList.values)[position]
tabKeyHolder = tabKey
resource = ArrayList(collectionItems!!.resourceList.keys)[position]
val convertToArray : ArrayList<ResourceWithState> = ArrayList()
dataList.toCollection(convertToArray)
horizontalAdapter!!.setData(convertToArray)
horizontalAdapter!!.setCategory(itemView.book_category.text.toString())
// How to call this?
fun updateItem(resourceUUID: String){
horizontalAdapter!!.update(resourceUUID)
}
}
}
And this is the simplified CollectionItems class:
I would like to update the imageView in one of the items inside the folder when the item is downloaded. I already have a broadcast receiver in the fragment that calls the updateRow method. This works but I would like to be able to update the item, not the row. How would I access the method updateItem in the ViewHolder?
// How to call this?
fun updateItem(resourceUUID: String){
horizontalAdapter!!.update(resourceUUID)
}

Not sure where to implements parcelable ArrayList after click item in RecyclerView

I wanna pass some of informations of the selected item to be viewed in new activity AboutApp.kt, but here I test by one info only (name). I do not have any trouble with RecyclerView, it works. I've seen many ways to do parcelable ArrayList object but feeling confuse where activity to be implemented, so it's getting error in MainActivity and AboutApp (destination intent).
A piece code MainActivity.kt getting error in showSelectedHerbal, when I use position to putExtra
class MainActivity : AppCompatActivity() {
private lateinit var rvHerbal: RecyclerView
private var list: ArrayList<Herbal> = arrayListOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
rvHerbal = findViewById(R.id.rv_herbal)
rvHerbal.setHasFixedSize(true)
list.addAll(HerbalData.listData)
showRecyclerList()
}
private fun showRecyclerList() {
rvHerbal.layoutManager = LinearLayoutManager(this)
val listHerbalAdapter = ListHerbalAdapter(list)
rvHerbal.adapter = listHerbalAdapter
listHerbalAdapter.setOnItemClickCallback(object : ListHerbalAdapter.OnItemClickCallback {
override fun onItemClicked(data: Herbal) {
showSelectedHerbal(data)
}
})
}
........
private fun showSelectedHerbal(data: Herbal) {
val moveIntent = Intent(this, AboutApp::class.java)
moveIntent.putExtra("Example_Item", list!![position])
this.startActivity(moveIntent)
}
.......
}
A piece code from AboutApp.kt that getting error in herbalName(). I know that I haven't implemented the parcelable so it's wrong
val intent = intent
val herbalData: HerbalData = intent.getParcelableExtra("Example_Item")
val title: String = herbalData.herbalName()
val itemName = findViewById<TextView>(R.id.item_name)
itemName.text = title
I'm so sorry, I attach you some of activities that I confuse may be one of them is the right place to be implement parcelable. Here is my data class Herbal.kt
data class Herbal(
var name: String = "",
var detail: String = "",
var photo: Int = 0
)
A piece code of the object HerbalData.kt
object HerbalData {
private val herbalName = arrayOf("Cengkeh",
"Ginseng",
"Jahe")
..........
val listData: ArrayList<Herbal>
get() {
val list = arrayListOf<Herbal>()
for (position in herbalName.indices) {
val herbal = Herbal()
herbal.name = herbalName[position]
herbal.detail = herbalDetail[position]
herbal.photo = herbalImage[position]
list.add(herbal)
}
return list
}
}
Help me please where activity to be write the parcelable ArrayList and how to fix it. Thanks in advance for any help.
First of all the error in your AboutApp.kt is because you have herbalName private in your HerbalData object. Remove the private modifier to access it there.
Just add #Parcelize annotation over your data class to automatically generate writeToParcel and createFromParcel methods for you!
#Parcelize
data class Herbal(...) : Parcelable
Add this in your build.gradle file:
androidExtensions {
features = ["parcelize"]
}
PS: Reference: https://medium.com/#BladeCoder/a-study-of-the-parcelize-feature-from-kotlin-android-extensions-59a5adcd5909
I recommend you read this.
You just have to make the Herbal class Parcelable.
data class Herbal(
var name: String = "",
var detail: String = "",
var photo: Int = 0
) : Parcelable {
companion object {
#JvmField
val CREATOR = object : Parcelable.Creator<Herbal> {
override fun createFromParcel(parcel: Parcel) = Herbal(parcel)
override fun newArray(size: Int) = arrayOfNulls<Herbal>(size)
}
}
private constructor(parcel: Parcel) : this(
name = parcel.readString(),
detail = parcel.readString(),
photo = parcel.readInt(),
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(name)
parcel.writeString(detail)
parcel.writeInt(photo)
}
override fun describeContents() = 0
}

How do I change the viewmodel observable data in android?

I created a gridView that has an ArrayAdapter, the gridView contains only photos, I am fetching the image url in an Array and I am observing the array through my activity. Here is my viewmodel
class ProfileViewModel constructor(): ViewModel() {
var first_name: String? = null
var last_name: String? = null
var dob: String? = null
var address: String? = null
var organization: String? = null
var hobby: String? = null
var bio: String? = null
var imagePath: String = ""
private val imageList : MutableLiveData<ArrayList<ProfileViewModel>> = MutableLiveData()
constructor(photo : Photo) : this() {
this.imagePath = photo.imageUrl
}
fun getImageUrl() : String {
return imagePath
}
companion object {
#BindingAdapter("imageUrl")
#JvmStatic
fun loadImage(imageView: ImageView, imageUrl: String) {
Glide.with(imageView.context)
.load(imageUrl)
.apply(RequestOptions.centerCropTransform())
.placeholder(R.drawable.ic_add_icon)
.into(imageView)
}
}
val profileViewModels : MutableLiveData<ArrayList<ProfileViewModel>>
get() {
val profileViewModels = ArrayList<ProfileViewModel>()
val photo1 = Photo("")
val profileVM = ProfileViewModel(photo1)
repeat(6) {
profileViewModels.add(profileVM)
}
imageList.value = profileViewModels
return imageList
}
}
}
Here is my activity where I am observing the data
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityProfileBinding =
DataBindingUtil.setContentView(this, R.layout.activity_profile)
val viewModel = ViewModelProvider(this).get(ProfileViewModel::class.java)
viewModel.profileViewModels.observe(this,
Observer<ArrayList<ProfileViewModel>> { image_paths ->
Log.d("added", "$image_paths")
val imageAdapter = ImageAdapter(this#Profile, R.layout.image_card, image_paths!!)
gridView.adapter = imageAdapter
})
}
I am getting images in the gridView but I want to update the observable value on gridView Item click in the clicked position. How do I do that?
First, you can create a function in viewmodel, that you want to do when clicked. for example:
private fun doSomethingWhenClicked(listPosition: Int){
val clickedImage = profileViewModels[position]
//do something here for clicked image
//..
}
Then, initialize the viewmodel in adapter like this. So you can update your profileViewModels in onClickListener inside the ImageAdapter
viewmodel.doSomethingWhenClicked(position)
Hope this answer you!

How to use RecyclerView into another RecyclerView's adapter in 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));

Dynamic RecyclerView Adapter accepting any list

I have an app in which depending on the currency selected, I pass the list to the adapter and based on the type of list passed as an argument, I decide which model class should be used.
RecyclerView Adapter
class CoinAdapter : RecyclerView.Adapter<CoinAdapter.MyViewHolder> {
private var coinList: List<Coin>? = null
private var coinINRList: List<CoinINR>? = null
private var coinEURList: List<CoinEUR>? = null
private var coinGBPList: List<CoinGBP>? = null
private var context: Context? = null
inner class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var coinName: TextView
var coinPrice: TextView
init {
coinName = view.findViewById(R.id.coin_title_text)
coinPrice = view.findViewById(R.id.coin_price_text)
}
}
constructor(coinList: List<Coin>?, context: Context?) {
this.coinList = coinList
this.context = context
}
constructor(coinList: List<CoinINR>?, context: Context?, second: String) {
this.coinINRList = coinList
this.context = context
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.coin_list_row, parent, false)
return MyViewHolder(itemView)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
when (currencyUnit) {
"USD" -> {
val coin = coinList?.get(position)
holder.coinName.text = coin?.name
holder.coinPrice.text = coin?.price
}
"INR" -> {
val coinINR = coinINRList?.get(position)
holder.coinName.text = coinINR?.name
holder.coinPrice.text = coinINR?.price
}
}
}
override fun getItemCount(): Int {
when (currencyUnit) {
"USD" -> return coinList?.size ?: 0
"INR" -> return coinINRList?.size ?: 0
else -> return coinList?.size ?: 0
}
}
}
Now, I need to support multiple currencies and so the code is becoming boilerplate. Is there any way that I can make the RecyclerView accept any type of list and then perform task depending on the list?
Thanks in advance.
My suggestion is to create a class Coin that will be a parent of all other currency objects.
open class Coin(val name: String, val price: Float)
data class CoinINR(name: String, price: Float) : Coin(name, price)
Than your adapter would have only one List and your onBindViewHolder method will look like this:
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
with (coinList?.get(position)) {
holder.coinName.text = it.name
holder.coinPrice.text = it.price
}
}

Categories

Resources