I'm writing an app to show a tree view of drug groups, drugs, and their information. Essentially, it's an ExpandableListView of drug groups, which shows individual drug names as the children views and opens a new fragment with more information on click.
I'm stuck with populating the child views with correct data. The adapter seems to get the group data correctly and from logging and debugging it seems that the child data is also passed on correctly. However, the text in the childViews in the ExpandableListView is only correct for the first group I open, every next group shows seemingly random contents (order of opening doesn't matter). The number of childViews is correct. The detail views (onClick) show correct info and on pressing the back button, the menu is then being showed with the correct info (however, any newly opened group then still shows wrong contents).
I've done at least 20 rounds checking and clarifying any dubious code but to no avail.
Screenshots for clarification:
list view with two groups expanded
detail view, showing correct info (but not matching that shown in list view)
list view upon returning (notice contents now shown correctly)
Here's the ExpandableListAdapter:
class MedicationsListAdapter(
private val context: Context,
private val groupList: List<String>,
private val itemList: List<List<String>>
) : BaseExpandableListAdapter() {
override fun getGroupCount(): Int {
return groupList.size
}
override fun getChildrenCount(groupPosition: Int): Int {
return itemList[groupPosition].size
}
override fun getGroup(groupPosition: Int): List<String> {
return itemList[groupPosition]
}
override fun getChild(groupPosition: Int, childPosition: Int): String {
return itemList[groupPosition][childPosition]
}
override fun getGroupId(groupPosition: Int): Long {
return groupPosition.toLong()
}
override fun getChildId(groupPosition: Int, childPosition: Int): Long {
return childPosition.toLong()
}
override fun hasStableIds(): Boolean {
return true
}
override fun getGroupView(
groupPosition: Int,
isExpanded: Boolean,
convertView: View?,
parent: ViewGroup?,
): View {
var groupView = convertView
if (groupView == null) {
val layoutInflater =
this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
groupView = layoutInflater.inflate(R.layout.medication_list_group, null)
val groupTextView: TextView = groupView.findViewById(R.id.text_group_name)
groupTextView.text = groupList[groupPosition]
} else return groupView
return groupView
}
override fun getChildView(
groupPosition: Int,
childPosition: Int,
isLastChild: Boolean,
convertView: View?,
parent: ViewGroup?
): View {
var childView = convertView
if (childView == null) {
val layoutInflater =
this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
childView = layoutInflater.inflate(R.layout.medication_list_item, null)
childView.findViewById<TextView>(R.id.text_medication_name).text = getChild(groupPosition, childPosition)
} else return childView
return childView
}
override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean {
return true
}
}
Here's the detail view fragment:
class MedicationItemFragment : Fragment() {
private lateinit var medicationName: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//get medication name from SafeArgs
arguments?.let {
medicationName = it.getString("medicationName").toString()
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_medication_item, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// get the correct medication data
val medication: Medication = MedicationsListData().getMedication(medicationName)
// populate the view with current medication's data
view.findViewById<TextView>(R.id.text_pharmacodynamics_body).text =
medication.pharmacodynamics
view.findViewById<TextView>(R.id.text_contraindications_body).text =
medication.contraindications
}
companion object {
fun newInstance(): ParametersFragment = ParametersFragment()
}
}
Here's the class providing the adapter's data:
class GroupListData {
fun getItemData(): List<List<String>> {
return listOf(
listOf("amoxicillin + clavulanate","penicillin","clindamycin","vancomycin"),
listOf("epinephrine","norepinephrine","dopamine"),
listOf("metoprolol","adenosine","amiodarone"),
listOf("metoclopramide")
)
}
fun getGroupData(): List<String> {
return listOf(
"antibiotics",
"vasopressors",
"antiarrhythmics",
"antiemetics"
)
}
}
I can elaborate or explain anything if neccessary. Any help is very much appreciated!
After reading around I now understand that the problem is the recycling views not populating correctly. A fix to this was to just stop recycling the views and creating a new one for every child instead:
override fun getChildView(
groupPosition: Int,
childPosition: Int,
isLastChild: Boolean,
convertView: View?,
parent: ViewGroup?
): View {
var childView = convertView
val layoutInflater = this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
childView = layoutInflater.inflate(R.layout.medication_list_item, null)
val medicationName = GroupListData().getItemData()[groupPosition][childPosition]
val medication: Medication = MedicationsListData().getMedication(medicationName)
childView.findViewById<TextView>(R.id.text_medication_name).text = medication.medicationName
if(medication.brandName != "null") {
childView.findViewById<TextView>(R.id.text_brand_name).text = medication.brandName
}
return childView
}
I wouldn't exactly consider this a solution as much as a workaround, since the amount of (non-recycled) views will probably cause some performance issues. But if anyone else is struggling with the same issue (and doesn't anticipate performance problems), this works.
Related
I have a view that I'm creating multiple child views to create a dynamic form. Some will act like a dropdown and some are EditText controls for data entry. However, when the user enters some data, then goes to the next fragment and hits the back button...all of the EditText controls get the same values. It's whatever was entered on the last one.
I've been racking my brain for over an hour trying to figure out where my bug is. Is it because all of the programmatically created textboxes have the same id?
Here's my onCreateView:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentNewReceiptInboundCustomAttributesBinding.inflate(layoutInflater)
val layoutManager: LinearLayout = LinearLayout(context)
for (i in receipt.InboundCustomAttributes) {
// val x = newbinding.root
if (i.CustomControlType in 1..3) {
val newbindingTB = CustomControlTextboxBinding.inflate(inflater)
newbindingTB.textView.text = i.LabelCaption
newbindingTB.textboxvalue.setText(i.DefaultValue)
binding.layoutItems.addView(newbindingTB.root)
} else if (i.CustomControlType==4) {
val newbindingDD = CustomControlDropdownBinding.inflate(inflater)
newbindingDD.label.text = i.LabelCaption
newbindingDD.value2.text = i.Value
newbindingDD.rlCustom.setOnClickListener {
val fragment = NewReceiptSelectOptions()
val args = Bundle()
val receiptblank = Receipt()
args.putSerializable("receipt", receiptblank)
args.putString("entity", "customattr")
args.putSerializable("customattribute", i)
fragment.arguments = args
activity?.getSupportFragmentManager()?.beginTransaction()
?.replace(R.id.nav_newreceipt_fragment, fragment)
?.addToBackStack("receiptOptions")
?.commit();
}
binding.layoutItems.addView(newbindingDD.root)
}
}
binding.nextButton.setOnClickListener {
var errorcnt: Int =0
for (i in receipt.InboundCustomAttributes) {
if (i.Required && i.Value.length==0) {
Toast.makeText(
this#NewReceiptInboundCustomAttributes.context,
i.ErrorMessage,
Toast.LENGTH_SHORT
).show()
errorcnt=1
}
}
if (errorcnt==0) {
// addReceiptHeader()
}
}
val view = binding.root
return view
}
Here's a screenshot after hitting the back button.
UPDATE:
Now I have converted to use RecyclerView, but the same thing is happening. Here's my adapter...I'm binding to a specific class depending on ViewType and it works well. However, all of my EditText controls still have the same id, so when the user goes to the next fragment and hits the back button, Android is attempting to remember the inputted values. How can i prevent this?
class NewReceiptInboundCustomAttributesAdapter (
var optionList: MutableList<InboundCustomAttribute>,
private val listener: NewReceiptInboundCustomAttributes
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
// inner class ViewHolder(val binding: QuestionItemBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
when (viewType) {
1 -> {
bindingTB = CustomControlTextboxBinding.inflate(LayoutInflater.from(parent.context), parent, false)
CustomControlTB(bindingTB)
}
4 -> {
bindingDD = CustomControlDropdownBinding.inflate(LayoutInflater.from(parent.context), parent, false)
CustomControlDD(bindingDD)
}
6 -> {
bindingCB = CustomControlCheckboxBinding.inflate(LayoutInflater.from(parent.context), parent, false)
CustomControlCB(bindingCB)
}
else -> {
bindingTB = CustomControlTextboxBinding.inflate(LayoutInflater.from(parent.context), parent, false)
CustomControlTB(bindingTB)
}
}
override fun getItemViewType(position: Int) = optionList[position].CustomControlType
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder.getItemViewType()) {
1 -> {
(holder as CustomControlTB).bind(optionList[position])
bindingTB.textboxvalue.addTextChangedListener (object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int,
count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int,
before: Int, count: Int) {
listener.textboxChanged(position, bindingTB.textboxvalue.text.toString())
}
})
}
4 -> {
(holder as CustomControlDD).bind(optionList[position])
}
6 -> {
(holder as CustomControlCB).bind(optionList[position])
}
}
holder.itemView.setOnClickListener { v ->
if (position != RecyclerView.NO_POSITION) {
listener.optionSelected(position)
}
}
}
// return the size of languageList
override fun getItemCount(): Int {
return optionList.size
}
}
You really probably shouldn't be using a dynamic view for that, but using a recyclerview. Which would likely fix your problem. Your other option is you're going to have to write custom onSaveInstanceState/onRestoreInstanceState handlers. The problem is that all of your text views likely have the same id, so the built in one uses the same key for all of them and gets confused.
Although really, use a RecyclerView here. Not only does it fix a lot of things and give you a lot of functionality, but you're basically making your own lower quality version of it with the pattern you're using to create this.
I have a simple Kotlin class WarningFragment in which I set an adapter for ExpandableListView and pass list headers and details of every list header inside. For the record, here's the code of the class:
class WarningFragment : Fragment() {
private var _binding: FragmentWarningBinding? = null
private val binding get() = _binding!!
private val header: List<String> = WarningData.warningData.keys.toList()
private val body: List<List<String>> = WarningData.warningData.values.toList()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentWarningBinding.inflate(inflater, container, false)
val root: View = binding.root
return root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val warningHeader : TextView = binding.warningTextView
warningHeader.text = WarningData.warningHeader
val expandableListViewWarning : ExpandableListView = binding.expandableListViewWarning
expandableListViewWarning.setAdapter(WarningExpandableListAdapter(requireActivity().applicationContext, expandableListViewWarning, header, body))
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
The code of list adapter class:
class WarningExpandableListAdapter(var context: Context,
var expandableListView: ExpandableListView,
var header: List<String>,
var body: List<List<String>>) : BaseExpandableListAdapter() {
override fun getGroupCount(): Int {
return header.size
}
override fun getChildrenCount(groupPosition: Int): Int {
return body[groupPosition].size
}
override fun getGroup(groupPosition: Int): String {
return header[groupPosition]
}
override fun getChild(groupPosition: Int, childPosition: Int): String {
return body[groupPosition][childPosition]
}
override fun getGroupId(groupPosition: Int): Long {
return groupPosition.toLong()
}
override fun getChildId(groupPosition: Int, childPosition: Int): Long {
return childPosition.toLong()
}
override fun hasStableIds(): Boolean {
return false
}
override fun getGroupView(
groupPosition: Int,
isExpanded: Boolean,
convertView: View?,
parent: ViewGroup?
): View? {
var convertView = convertView
if (convertView == null) {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
convertView = inflater.inflate(R.layout.warning_list_row_group, null)
}
val title = convertView?.findViewById<TextView>(R.id.text_view_warning_list_row_group)
title?.text = getGroup(groupPosition)
//title?.textColors = R.attr.colorOnSurface
//title?.text = getGroup(groupPosition)
title?.setOnClickListener {
if (expandableListView.isGroupExpanded(groupPosition))
expandableListView.collapseGroup(groupPosition)
else
expandableListView.expandGroup(groupPosition)
//Toast.makeText(context, getGroup(groupPosition), Toast.LENGTH_SHORT).show()
}
return convertView
}
override fun getChildView(
groupPosition: Int,
childPosition: Int,
isLastChild: Boolean,
convertView: View?,
parent: ViewGroup?
): View? {
var convertView = convertView
if (convertView == null) {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
convertView = inflater.inflate(R.layout.warning_list_row_child, null)
}
val title = convertView?.findViewById<TextView>(R.id.text_view_warning_list_row_child)
title?.text = getChild(groupPosition, childPosition)
//title?.setTextColor(R.attr.colorOnSurface)
title?.setOnClickListener {
//Toast.makeText(context, getChild(groupPosition, childPosition), Toast.LENGTH_SHORT).show()
}
return convertView
}
override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean {
return true
}
}
Everything worked fine until I've tried to change the color of the TextView in the XML file representing the list header item (group) or details item (child) with this line:
android:textColor="?attr/colorOnSurface"
Here's the XML code example of the list header item:
<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="horizontal"
android:padding="8dp">
<TextView
android:id="#+id/text_view_warning_list_row_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:paddingTop="8dp"
android:paddingEnd="8dp"
android:paddingBottom="8dp"
android:fontFamily="sans-serif-condensed-medium"
android:textColor="?attr/colorOnSurface"
tools:text="ZMIANY W SAMOPOCZUCIU FIZYCZNYM" />
</LinearLayout>
Now, when I launch my app and go to my Warning Fragment the app crashes and throws the android.view.InflateException with "Error inflating class android.widget.TextView" info. The exception stack shows this line (in WarningExpandableListAdapter class) causes the exception:
convertView = inflater.inflate(R.layout.warning_list_row_group, null)
Respectively, if I set the same textColor attribute in XML file representing child, then the bugged line:
convertView = inflater.inflate(R.layout.warning_list_row_child, null)
I've tried to hardcode the textColor attribute to #color/black. In this case the app works well but this text color remains black after swithing to dark mode (I want the color to change in case of changing app theme, that's why i use ?attr/...)
I've also tried to set the text color programmatically but I don't know why the TextView color sets to purple, not black or white.
Is there any way to get rid of that problem or changing the text color depending on dark or light mode?
Thank you in advance for any help.
i am making an app in which i have to insert data into reyclerview , the recyclerview is working fine but the problem is that when i scroll it up adapter reupdate the data , so to solve this issue the code looks fine but still getting this issue ....................................................
BaseClass
abstract class MultiViewModelBaseAdapter<M : Model, VDB : ViewDataBinding>(private var diffCallback: DiffUtil.ItemCallback<M>) : ListAdapter<M ,BaseViewHolder<VDB>>(diffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<VDB> {
val inflator = LayoutInflater.from(parent.context)
val binding = createBinding(viewType, inflator, parent)
return BaseViewHolder(binding)
}
abstract fun createBinding(viewType: Int, inflater: LayoutInflater, parent: ViewGroup) : VDB
override fun onBindViewHolder(holder: BaseViewHolder<VDB>, position: Int) {
bind(holder.mBinding, getItem(position), position)
holder.mBinding.executePendingBindings()
}
abstract fun bind(binding: VDB, item: M, position: Int)
abstract fun onDataChanged(values: Boolean)}
}
Adapter
class LanguageAdapter(
private val context: Context,
private val mViewModel: LanguageListViewModel,
private val onClickListener: OnItemClickListener<String>
) : MultiViewModelBaseAdapter<LanguageSupportModel, ViewDataBinding>(diffCallback) {
companion object {
private val ADS = 1
private val LANGUAGES = 2
val diffCallback = object : DiffUtil.ItemCallback<LanguageSupportModel>() {
override fun areItemsTheSame(
oldItem: LanguageSupportModel,
newItem: LanguageSupportModel
): Boolean = oldItem.dataId == newItem.dataId
/**
* Note that in kotlin, == checking on data classes compares all contents, but in Java,
* typically you'll implement Object#equals, and use it to compare object contents.
*/
override fun areContentsTheSame(
oldItem: LanguageSupportModel,
newItem: LanguageSupportModel
): Boolean = oldItem == newItem
}
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemViewType(position: Int): Int {
return position
}
override fun createBinding(viewType: Int, inflater: LayoutInflater, parent: ViewGroup): ViewDataBinding {
return DataBindingUtil.inflate(inflater, R.layout.language_view, parent, false)
}
override fun bind(binding: ViewDataBinding, item: LanguageSupportModel, position: Int) {
binding as LanguageViewDataBinding
binding.apply {
language = item
//click
}
}
override fun onDataChanged(values: Boolean) {}
}
Fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val languageAdapter = LanguageAdapter(requireContext(), mViewModel, this ,lifecycleScope)
languageAdapter.submitList(LanguageArray.arrayValues())
reyclerview.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
adapter = languageAdapter
}
If You will Use Paging concept in Scrolling then it will Solve.
In the JAVA or KOTLIN We can implement.. like..
import androidx.viewpager.widget.PagerAdapter;
Then extends it into Adapter Class.
public void addList(List<ClsList> list) {
this.mResources = list;
notifyDataSetChanged(); // also this main line
}
Problem Solve.
☻♥ Have Fun..
I'm new to Android Development and Kotlin, so any help would be greatly appreciated.
So I've created an app in which there's a ListView which, once clicked, takes you to a new activity. However, for every element in the ListView, it takes me to the same activity. How do I change it so that every element takes me to a unique activity?
Here's my code so far (not all of it, the important bits, but please let me know if you'd like to see more)
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val listView = findViewById<ListView>(R.id.main_ListView)
listView.adapter = MyCustomAdapter(this) // custom adapter --> tells list what to render
listView.isClickable = true
listView.onItemClickListener = AdapterView.OnItemClickListener { _, _, _, _ ->
setContentView(R.layout.activity_uni_detail)
}
}
}
private class MyCustomAdapter(context: Context): BaseAdapter() {
private val mContext: Context = context
private val nameOfUni = arrayListOf<String>(
"University of Toronto", "University of Waterloo", "McMaster University"
)
// responsible for rows
override fun getCount(): Int {
return nameOfUni.size
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItem(position: Int): Any {
return "TEST STRING"
}
// responsible for rendering out each row
override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup?): View {
val layoutInflater = LayoutInflater.from(mContext)
val rowMain = layoutInflater.inflate(R.layout.row_main, viewGroup, false)
val uniNameTextView = rowMain.findViewById<TextView>(R.id.uniName)
uniNameTextView.text = nameOfUni.get(position)
return rowMain
}
}
This is not a good implementation .First of all you should implement onClickListener in your adapter. for example on uniNameTextView not on the hole list. For the details activity you should not create a unique activity for every item , you must create one activity and for every item that is clicked ,you send the details to the activity.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val listView = findViewById<ListView>(R.id.main_ListView)
listView.adapter = MyCustomAdapter(this) // custom adapter --> tells list what to render
}
private class MyCustomAdapter(context: Context,onClick:OnClick): BaseAdapter() {
private val mContext: Context = context
private val nameOfUni = arrayListOf<String>(
"University of Toronto", "University of Waterloo", "McMaster University"
)
// responsible for rows
override fun getCount(): Int {
return nameOfUni.size
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItem(position: Int): Any {
return "TEST STRING"
}
// responsible for rendering out each row
override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup?): View {
val layoutInflater = LayoutInflater.from(mContext)
val rowMain = layoutInflater.inflate(R.layout.row_main, viewGroup, false)
val uniNameTextView = rowMain.findViewById<TextView>(R.id.uniName)
uniNameTextView.text = nameOfUni.get(position)
uniNameTextView.setOnClickListener{
////////////////////////// here
val intent = Intent(this, DetailsActivity::class.java)
intent.putExtra("samplename", nameOfUni.get(position))
startActivity(intent)
}
return rowMain
}
}
DetailActivity:
class DetailActivity :AppCompact() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_details)
val ss:String = intent.getStringExtra("samplename")
text_view_detail.text=ss
}
}
I've been trying to implement recycling in my PagerAdapter, which I could do successfully thanks to this question however I'm running into a problem to cache the views with data binding.
I have tried like this, keeping a Stack<View>:
class CustomAdapter : PagerAdapter() {
private var recycledViews: Stack<View> = Stack()
var items: List<Item> = ArrayList()
set(value) {
field = value
notifyDataSetChanged()
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val binding = inflateOrRecycle(container)
binding.item = items[position]
binding.handler = this
container.addView(binding.root)
return binding.root
}
private fun inflateOrRecycle(container: ViewGroup): CustomBinding {
val inflater = LayoutInflater.from(container.context)
return if (recycledViews.isEmpty()) {
CustomBinding.inflate(inflater, container, false)
} else {
val view = recycledViews.pop()
CustomBinding.bind(view)
}
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
val view = `object` as View
container.removeView(view)
recycledViews.add(view)
}
}
However, whenever it tries to use a recycled view for the first time and calls CustomBinding.bind(view) it crashes because the view must have a tag. I've searched this, but none of the answers I've found have quite fixed my problem.
I've also tried keeping a Stack<CustomBinding>, but the problem is I'm not sure how to handle the destroyItem method. Because if I do:
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
val view = `object` as View
container.removeView(view)
recycledViews.add(CustomBinding.bind(view))
}
I'll still get the same error. How can I "recycle" data binding objects like this? Or, if I recycle the views themselves, how do I convert them back to binding objects?
You have done a simple mistake, I guess.
You can correct me if I am wrong, I tried with this adapter and it worked.
val demoAdapter = object : PagerAdapter() {
private var recycledViews: Stack<View> = Stack()
var items: List<String> = ArrayList()
set(value) {
field = value
notifyDataSetChanged()
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val binding = inflateOrRecycle(container)
container.addView(binding.root)
return binding.root
}
private fun inflateOrRecycle(container: ViewGroup): DemoItemBinding {
val inflater = LayoutInflater.from(container.context)
return if (recycledViews.isEmpty()) {
DemoItemBinding.inflate(inflater, container, false)
} else {
val view = recycledViews.pop()
val custBinding = DataBindingUtil.getBinding<DemoItemBinding>(view)
if(custBinding == null)
DemoItemBinding.bind(view)
else
custBinding
}
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
val view = `object` as View
container.removeView(view)
recycledViews.add(view)
}
override fun isViewFromObject(view: View, `object`: Any): Boolean {
return `object` is View && view.equals(`object`)
}
override fun getCount(): Int {
return 4
}
}
The portion I changed from your code was this
return if (recycledViews.isEmpty()) {
CustomBinding.inflate(inflater, container, false)
} else {
val view = recycledViews.pop()
CustomBinding.bind(view)
}
to
return if (recycledViews.isEmpty()) {
DemoItemBinding.inflate(inflater, container, false)
} else {
val view = recycledViews.pop()
val custBinding = DataBindingUtil.getBinding<DemoItemBinding>(view)
if(custBinding == null)
DemoItemBinding.bind(view)
else
custBinding
}
I think, you were trying to bind to a view which already has a Binding attached to it. Thus it was giving you an error. What I have done is check for any previous binding, if it's there return the associated binding.