Can't Update child item in NestedRecyclerView - kotlin

How do I update the child when clicking on the categories item or when doing a search like the one in the picture
I am updating the data from Viewmodel but the child element does not update
I also tried calling the adapter child and updating it directly, but to no avail
This is the code, if you need any additional details, I will add it down
In ParentAdapter
class BaseAdapter(private val listener: OnItemClickListener
): RecyclerView.Adapter<BaseViewHolder>() {
var items : MutableList<HomeItem<Any>> = mutableListOf()
lateinit var adapter : SubjectsAdapter
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
return when (viewType){
VIEW_ITEM_SEARCH ->{
val view = LayoutInflater.from(parent.context).inflate(
R.layout.item_search_view,
parent,false
)
BaseViewHolder.SearchItemViewHolder(view)
}
VIEW_ITEM_STUDENT ->{
val view = LayoutInflater.from(parent.context).inflate(
R.layout.item_student, parent,false
)
BaseViewHolder.StudentItemViewHolder(view)
}
VIEW_ITEM_COLOR ->{
val view = LayoutInflater.from(parent.context).inflate(
R.layout.item_chip_list, parent,false
)
BaseViewHolder.YearsItemViewHolder(view)
}
VIEW_ITEM_SUBJECT ->{
val view = LayoutInflater.from(parent.context).inflate(
R.layout.item_subject_list, parent,false
)
BaseViewHolder.SubjectsItemViewHolder(view)
}
else -> throw Exception("UnKnow View Type")
}
}
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
when (holder){
is BaseViewHolder.SearchItemViewHolder -> bindSearch(holder,position)
is BaseViewHolder.StudentItemViewHolder -> bindStudent(holder,position)
is BaseViewHolder.YearsItemViewHolder -> bindYears(holder,position)
is BaseViewHolder.SubjectsItemViewHolder -> bindSubjects(holder,position)
}
}
fun bindYears(holder: BaseViewHolder.YearsItemViewHolder,position: Int){
val p = items[position].item as List<SubjectsColor>
val adapterchip = ChipAdapter { chip ->
listener.onSubjectColorClick(chip)
}
holder.binding.apply {
recyclerChipList.layoutManager = LinearLayoutManager(root.context,
LinearLayoutManager.HORIZONTAL,
false)
recyclerChipList.adapter = adapterchip
recyclerChipList.setHasFixedSize(true)
}
adapterchip.submitList(p)
}
fun bindSubjects(holder :BaseViewHolder.SubjectsItemViewHolder,position: Int){
val p = items[position].item as List<SubjectStudent>
adapter = SubjectsAdapter{ subject->
listener.onSubjectStudentClick(subject)
}
holder.binding.apply {
recyclerSubjectList.layoutManager = LinearLayoutManager(root.context,
LinearLayoutManager.VERTICAL,
false)
recyclerSubjectList.adapter = adapter
recyclerSubjectList.setHasFixedSize(true)
}
Log.d("BindSubjects","BindSubjects : $p")
adapter.submitList(p)
}
In ViewModel
#HiltViewModel
class HomeViewModel #Inject constructor(
private val subDao : SubjectsDao,
//state : SavedStateHandle
) : ViewModel(){
private val colorId = MutableLiveData(1)
val colorIdResult = colorId.asFlow().flatMapLatest(){ id ->
id.let {
subDao.getAllSubjectStudentByColor(id)
}
}.stateIn(viewModelScope, SharingStarted.Lazily, null)
fun getAllSubjectsColor() :Flow<List<SubjectsColor>> =
subDao.getAllSubjectColors()
fun onSubjectColorClick(colorI:Int){
colorId.postValue(colorI)
}}
In Fragment
adapterBase = BaseAdapter(this)
val itemsList: MutableList<HomeItem<Any>> = mutableListOf()
itemsList.add(HomeItem("Google Home", HomeItemType.TYPE_SEARCH))
itemsList.add(HomeItem("Student", HomeItemType.TYPE_PROFILE))
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
viewModel.getAllSubjectsColor().collect {
itemsList.add(HomeItem(it, HomeItemType.TYPE_COLORS))
}
}
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
viewModel.colorIdResult.collect { id ->
val result = id ?: return#collect
itemsList.add(HomeItem(result.subjectStudent, HomeItemType.TYPE_SUBJECTS))
//adapterBase.notifyDataSetChanged()
}
}
binding.apply {
recyclerBaseFragment.layoutManager = LinearLayoutManager(requireContext())
recyclerBaseFragment.setHasFixedSize(true)
recyclerBaseFragment.adapter = adapterBase
}
adapterBase.items = itemsList
}
enum class HomeItemType {
TYPE_SEARCH,
TYPE_PROFILE,
TYPE_COLORS,
TYPE_SUBJECTS
}
data class HomeItem<T>(
var item :T,
val type: HomeItemType
)

Related

how do i update items that change in recyclerview use diffutil?

DiffUtil do not update items recyclerview with the same id and not same the content,
The model has [id : Int and title : String]. I want to the diffUtilCallBack check items with the same id and not same title and update to recyclerview
Please help me fix it
my DiffUtil CallBAck
class DiffCB(
val oldl : ArrayList<Test>,
val newl : ArrayList<Test>
) : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldl.size
}
override fun getNewListSize(): Int {
return newl.size
}
override fun areItemsTheSame(oldItemPos: Int, newItemPos: Int): Boolean {
return oldl[oldItemPos].id == newl[newItemPos].id
}
override fun areContentsTheSame(oldItemPos: Int, newItemPos: Int): Boolean {
return oldl[oldItemPos].title.equals(newl[newItemPos].title)
}
}
my Adapter.kt
class TestAdapter(val click: TestClick) :
RecyclerView.Adapter<TestAdapter.TestVH>() {
var list: ArrayList<Test> = ArrayList()
inner class TestVH(val bin: ItemTestBinding) : RecyclerView.ViewHolder(bin.root) {
fun bind(test: Test) {
bin.tvTestId.text = "[id: ${test.id}]"
bin.tvTestTitle.text = test.title
bin.cbTest.isChecked = test.select
bin.cbTest.setOnClickListener {
click.click(test)
}
}
}
fun sumbitData(newl: ArrayList<Test>) {
val diff = DiffCB(this.list, newl)
val diffResult = DiffUtil.calculateDiff(diff)
list.clear()
this.list.addAll(newl)
diffResult.dispatchUpdatesTo(this)
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: TestVH, position: Int) {
holder.bind(list[position])
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TestVH {
return TestVH(ItemTestBinding.inflate(LayoutInflater.from(parent.context),
parent, false))
}
}
my Activity.kt
class TestActivity : AppCompatActivity(), TestClick {
private var list : ArrayList<Test> = ArrayList()
private lateinit var bin : ActivityTestBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bin = ActivityTestBinding.inflate(layoutInflater)
setContentView(bin.root)
val viewModel = ViewModelProviders.of(this).get(TestViewModel::class.java)
val manager = LinearLayoutManager(this)
bin.rvTest.layoutManager = manager
val adapter = TestAdapter(this)
bin.rvTest.adapter = adapter
//init first data
viewModel.setTests(initData())
viewModel.getTests().observe(this, Observer {
adapter.sumbitData(it as ArrayList<Test>)
})
bin.btnTest.setOnClickListener{
viewModel.setTests(updateData())
}
}
fun updateData() : ArrayList<Test> {
for (i in 2..6) {
var test = Test()
test.id = i
test.title = "title test $i update"
list.add(test)
}
return list
}
fun initData() : ArrayList<Test> {
for (i in 1..3) {
var test = Test()
test.id = i
test.title = "title test $i"
list.add(test)
}
return list
}
override fun click(test: Test) {
Toast.makeText(this, "${test.id} ${test.title}", Toast.LENGTH_SHORT).show()
}
}
it is first data
after update data, items with the same id have not been updated (id 2, 3)

Spinner not charge list

I'm getting a list of company names but I can only get Spinner to show me one item from the list.
What can I do so that the spinner loads the empresa list, it only shows an item
private fun initCharacter(clientes : List<ResultsEmpresa>) {
clientes.forEach{
val empresas = it.empresa
val listCliente: MutableList<SpinnerClientes> = ArrayList()
listCliente.add(SpinnerClientes(empresas))
val spnAdapter = SpinnerClientesAdapter(this#AddObra,
R.layout.kecamatan, R.id.spinnerText, listCliente)
spnClients!!.adapter = spnAdapter
spnClients!!.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?, view: View, position: Int, id: Long,)
{
val desaDetails = spnAdapter.getItem(position)
textView_empresa!!.text = desaDetails.nom_empresa
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
}
}
the mistake is:
The list should have been created before the forEach
private fun initCharacter(clientes : List<ResultsEmpresa>) {
val listCliente: MutableList<SpinnerClientes> = ArrayList()
clientes.forEach{
val empresas = it.empresa
listCliente.add(SpinnerClientes(empresas))
val spnAdapter = SpinnerClientesAdapter(this#AddObra,
R.layout.kecamatan, R.id.spinnerText, listCliente)
spnClients!!.adapter = spnAdapter
spnClients!!.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?, view: View, position: Int, id: Long,)
{
val desaDetails = spnAdapter.getItem(position)
textView_empresa!!.text = desaDetails.nom_empresa
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
}
}

Show item info on selected item in RecyclerView using Kotlin

I am using kotlin language with android studio. I want to get the properties of the element I clicked in the RecyclerView.
Ben bu kod ile saderc id alabiliyorum
Ex: date
ListAdapter.kt
class ListAdapter(
private val context: Context
) : RecyclerView.Adapter<ListAdapter.ListViewHolder>() {
private var dataList = mutableListOf<Any>()
private lateinit var mListener: onItemClickListener
interface onItemClickListener {
fun onItemClick(position: Int)
}
fun setOnItemClickListener(listener: onItemClickListener) {
mListener = listener
}
fun setListData(data: MutableList<Any>) {
dataList = data
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.item_row, parent, false)
return ListViewHolder(view)
}
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
val question: Questionio = dataList[position] as Questionio
holder.bindView(question)
}
override fun getItemCount(): Int {
return if (dataList.size > 0) {
dataList.size
} else {
return 0
}
}
inner class ListViewHolder(itemView: View, listener: onItemClickListener) :
RecyclerView.ViewHolder(itemView) {
fun bindView(questionio: Questionio) {
itemView.findViewById<TextView>(R.id.txt_policlinic).text = questionio.policlinic
itemView.findViewById<TextView>(R.id.txt_title).text = questionio.title
itemView.findViewById<TextView>(R.id.txt_description).text = questionio.description
itemView.findViewById<TextView>(R.id.txt_date).text = questionio.date
itemView.findViewById<TextView>(R.id.txt_time).text = questionio.time
}
init {
itemView.setOnClickListener {
listener.onItemClick(adapterPosition)
}
}
}
}
My code in onCreateView inside list fragment.Edit
ListFragment
recyclerView.layoutManager = LinearLayoutManager(requireContext())
recyclerView.adapter = adapter
observeData()
adapter.setOnItemClickListener(object : ListAdapter.onItemClickListener {
override fun onItemClick(position: Int) {
showShortToast(position.toString())
}
})
this function is also my observationData(),
I made new edits
private fun observeData() {
binding.shimmerViewContainer.startShimmer()
listViewModel.fetchQuestinData("questions",
requireContext())
.observe(viewLifecycleOwner, {
binding.shimmerViewContainer.startShimmer()
binding.shimmerViewContainer.hideShimmer()
binding.shimmerViewContainer.hide()
adapter.setListData(it)
adapter.notifyDataSetChanged()
})
}
You can pass highOrderFuction into the adapter then setonclickListener for any view you want. Like this:
class ListAdapter(
private val context: Context,
private val onItemClick:(questionio: Questionio)->Unit
) : RecyclerView.Adapter<ListAdapter.ListViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.item_row, parent, false)
return ListViewHolder(view,onItemClick)
}
...
inner class ListViewHolder(itemView: View,private val onItemClick:(questionio: Questionio)->Unit) : RecyclerView.ViewHolder(itemView) {
fun bindView(questionio: Questionio) {
//set on any view you want
itemView.findViewById<TextView>(R.id.root_view_id).
setOnClickListener{onItemClick(questionio)}
itemView.findViewById<TextView>(R.id.txt_policlinic).text =
questionio.policlinic
itemView.findViewById<TextView>(R.id.txt_title).text = questionio.title
itemView.findViewById<TextView>(R.id.txt_description).text =
questionio.description
itemView.findViewById<TextView>(R.id.txt_date).text = questionio.date
itemView.findViewById<TextView>(R.id.txt_time).text = questionio.time
}
}
}

Method param require nothing instead of typed param

There's superclass with typed parameter:
abstract inner class CalendarViewHolder<T : CalendarItem>(view: View) :
RecyclerView.ViewHolder(view) {
open fun bindData(data: T) {}
}
I expect that the method bindData will accept params which are objects of CalendarItem subclasses.
But in onBindViewHolder I got the following error:
Type mismatch.
Required:
Nothing
Found:
CalendarItem
Here's the implementation of the method:
override fun onBindViewHolder(holder: CalendarViewHolder<out CalendarItem>, position: Int) {
val item = items[position]
holder.bindData(item)
if (item is DayItem) {
if (holder is OnClickStrategy) {
holder.onClickListener = {
selectedDayOfYear = item.date?.get(Calendar.DAY_OF_YEAR) ?: 0
notifyItemChanged(position)
if (previousClickedPosition != -1) {
notifyItemChanged(previousClickedPosition)
}
previousClickedPosition = holder.adapterPosition
}
}
}
}
Here's the full code of adapter:
class CalendarAdapter :
RecyclerView.Adapter<CalendarAdapter.CalendarViewHolder<out CalendarItem>>() {
private val items = mutableListOf<CalendarItem>()
private var selectedDayOfYear = -1
private var previousClickedPosition = -1
abstract inner class CalendarViewHolder<T : CalendarItem>(view: View) :
RecyclerView.ViewHolder(view) {
open fun bindData(data: T) {}
}
abstract inner class CalendarDateViewHolder(view: View) : CalendarViewHolder<DayItem>(view) {
protected lateinit var tvDate: AppCompatTextView
protected lateinit var tvMonth: AppCompatTextView
protected lateinit var tvDayDescription: AppCompatTextView
protected lateinit var clRoot: ConstraintLayout
#CallSuper
override fun bindData(data: DayItem) {
tvDate = itemView.findViewById(R.id.tv_day_number)
tvMonth = itemView.findViewById(R.id.tv_month)
tvDayDescription = itemView.findViewById(R.id.tv_day_description)
clRoot = itemView.findViewById(R.id.cl_root)
}
}
interface OnClickStrategy {
var onClickListener: () -> Unit
}
inner class CalendarEmptyViewHolder(view: View) : CalendarViewHolder<EmptyDayItem>(view)
inner class CalendarWithDateViewHolder(view: View) :
CalendarDateViewHolder(view), OnClickStrategy {
override lateinit var onClickListener: () -> Unit
override fun bindData(data: DayItem) {
super.bindData(data)
tvDate.text = data.date?.get(Calendar.DAY_OF_MONTH)?.toString()
clRoot.setOnClickListener {
onClickListener.invoke()
}
clRoot.background = ContextCompat.getDrawable(
itemView.context,
when (selectedDayOfYear) {
data.date?.get(Calendar.DAY_OF_YEAR) -> R.drawable.item_selected_background
else -> R.drawable.item_default_background
}
)
tvDate.setTextColor(
ContextCompat.getColor(
itemView.context,
if (selectedDayOfYear == data.date?.get(Calendar.DAY_OF_YEAR)) R.color.white
else R.color.black
)
)
}
}
inner class CalendarTodayDateViewHolder(view: View) : CalendarDateViewHolder(view) {
override fun bindData(data: DayItem) {
super.bindData(data)
tvDayDescription.text = "hoy"
tvDate.text = data.date?.get(Calendar.DAY_OF_MONTH)?.toString()
clRoot.background = ContextCompat.getDrawable(
itemView.context,
R.drawable.item_today_background
)
val monthName = when (data.date?.get(Calendar.MONTH)) {
0 -> "January"
1 -> "February"
else -> "March"
}
tvMonth.text = monthName
tvDate.setTextColor(ContextCompat.getColor(itemView.context, R.color.white))
tvDayDescription.setTextColor(ContextCompat.getColor(itemView.context, R.color.white))
tvMonth.setTextColor(ContextCompat.getColor(itemView.context, R.color.white))
}
}
inner class CalendarNewMonthDateViewHolder(view: View) : CalendarDateViewHolder(view),
OnClickStrategy {
override lateinit var onClickListener: () -> Unit
override fun bindData(data: DayItem) {
super.bindData(data)
tvDate.text = data.date?.get(Calendar.DAY_OF_MONTH)?.toString()
val monthName = when (data.date?.get(Calendar.MONTH)) {
0 -> "January"
1 -> "February"
else -> "March"
}
tvMonth.text = monthName
clRoot.setOnClickListener {
onClickListener.invoke()
}
tvDate.setTextColor(
ContextCompat.getColor(
itemView.context,
if (selectedDayOfYear == data.date?.get(Calendar.DAY_OF_YEAR)) R.color.white
else R.color.black
)
)
clRoot.background = ContextCompat.getDrawable(
itemView.context,
when (selectedDayOfYear) {
data.date?.get(Calendar.DAY_OF_YEAR) -> R.drawable.item_selected_background
else -> R.drawable.item_default_background
}
)
}
}
inner class CalendarDisabledDateViewHolder(view: View) : CalendarDateViewHolder(view) {
override fun bindData(data: DayItem) {
super.bindData(data)
tvDate.text = data.date?.get(Calendar.DAY_OF_MONTH)?.toString()
clRoot.background = ContextCompat.getDrawable(
itemView.context,
R.drawable.item_disabled_background
)
tvDate.setTextColor(ContextCompat.getColor(itemView.context, R.color.silver))
}
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
) = when (viewType) {
CALENDAR_EMPTY_ITEM_TYPE -> onCreateCalendarEmptyViewHolder(parent)
CALENDAR_TODAY_DATE_ITEM_TYPE -> onCreateCalendarTodayDateViewHolder(parent)
CALENDAR_NEW_MONTH_DATE_ITEM_TYPE -> onCreateCalendarNewMonthDateViewHolder(parent)
CALENDAR_WITH_DATE_ITEM_TYPE -> onCreateCalendarWithDateViewHolder(parent)
else -> onCreateCalendarDisabledDateViewHolder(parent)
}
override fun getItemViewType(position: Int) = when (val item = items[position]) {
EmptyDayItem -> CALENDAR_EMPTY_ITEM_TYPE
is DayItem -> when {
item.isToday() -> CALENDAR_TODAY_DATE_ITEM_TYPE
item.isNewMonth() -> CALENDAR_NEW_MONTH_DATE_ITEM_TYPE
item.isDateEnabled -> CALENDAR_WITH_DATE_ITEM_TYPE
else -> CALENDAR_DISABLED_DATE_ITEM_TYPE
}
else -> UNKNOWN_ITEM_TYPE
}
private fun onCreateCalendarWithDateViewHolder(parent: ViewGroup) = CalendarWithDateViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_calendar_day, parent, false)
)
private fun onCreateCalendarEmptyViewHolder(parent: ViewGroup): CalendarViewHolder<EmptyDayItem> =
CalendarEmptyViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_calendar_empty, parent, false)
)
private fun onCreateCalendarTodayDateViewHolder(parent: ViewGroup) =
CalendarTodayDateViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_calendar_day, parent, false)
)
private fun onCreateCalendarNewMonthDateViewHolder(parent: ViewGroup) =
CalendarNewMonthDateViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_calendar_day, parent, false)
)
private fun onCreateCalendarDisabledDateViewHolder(parent: ViewGroup) =
CalendarDisabledDateViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_calendar_day, parent, false)
)
override fun onBindViewHolder(holder: CalendarViewHolder<out CalendarItem>, position: Int) {
val item = items[position]
holder.bindData(item)
if (item is DayItem) {
if (holder is OnClickStrategy) {
holder.onClickListener = {
selectedDayOfYear = item.date?.get(Calendar.DAY_OF_YEAR) ?: 0
notifyItemChanged(position)
if (previousClickedPosition != -1) {
notifyItemChanged(previousClickedPosition)
}
previousClickedPosition = holder.adapterPosition
}
}
}
}
override fun getItemCount() = items.size
fun setItems(items: MutableList<DayItem>) {
val localItems: MutableList<CalendarItem> = items.toMutableList()
if (!items.isNullOrEmpty()) {
val firstDayNumber = items[0].date?.get(Calendar.DAY_OF_WEEK) ?: 0
for (i in 1 until firstDayNumber) {
localItems.add(0, EmptyDayItem)
}
this.items.clear()
this.items.addAll(localItems)
}
}
companion object {
private const val CALENDAR_EMPTY_ITEM_TYPE = 0
private const val CALENDAR_TODAY_DATE_ITEM_TYPE = 1
private const val CALENDAR_NEW_MONTH_DATE_ITEM_TYPE = 2
private const val CALENDAR_WITH_DATE_ITEM_TYPE = 3
private const val CALENDAR_DISABLED_DATE_ITEM_TYPE = 4
private const val CALENDAR_HEADER_ITEM_TYPE = 5
private const val UNKNOWN_ITEM_TYPE = -1
}
}
sealed class CalendarItem
class DayItem(
val date: Calendar? = null,
val isDateEnabled: Boolean = true
): CalendarItem() {
fun isToday() = if (date != null) isSameDay(date, Calendar.getInstance()) else false
fun isNewMonth() = date?.get(Calendar.DAY_OF_MONTH) == 1
private fun isSameDay(cal1: Calendar, cal2: Calendar) =
cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR)
}
object EmptyDayItem : CalendarItem()
What's the reason of the problem?
You specified the type as out CalendarItem, but the bind function consumes the item of type T, so it is in an in position, not out. So by marking it as out at the class declaration, you have restricted it to Nothing wherever T appears as a function parameter type.
There's no way to make generics work for you here, as I'm assuming you're trying to get it to allow you to bind items without casting. onCreateViewHolder has your ViewHolder in an out position, which means your particular ViewHolder cannot have invariant or contravariant (in) type if you want to be able to return different types of your ViewHolder subclass from onCreateViewHolder.
It's just not logically possible for generics to safely allow you to pass these items to the appropriate types, and you will have to use casting.
I suggest you eliminate the generic type from your abstract ViewHolder, and make each subclass throw an error if it gets the wrong type. Then you will quickly get exceptions when you test your app if there are any type mismatches and you will know the problem lies in either getItemViewType or onCreateViewHolder, where you've hooked up your classes and types incorrectly.
I also suggest using View Binding, because it will make these classes much more streamlined. No lateinit view properties or findViewById calls.
For example:
abstract class CalendarViewHolder(view: View): RecyclerView.ViewHolder(view) {
abstract fun bindData(data: CalendarItem): Any
// Any return type here allows subclasses to use "= with(binding)"
// to reduce nesting.
}
class CalendarDateViewHolder(private val binding: ItemCalendarDayBinding): MyViewHolder(binding.root) {
override fun bind(item: CalendarItem) = with(binding) {
item as? DayItem ?: error("Wrong item type.")
// item is now smart cast as DayItem
// bind the DayItem data to the views.
// can access the views directly by name in with block.
}
}
private fun onCreateCalendarWithDateViewHolder(parent: ViewGroup) = CalendarWithDateViewHolder(
ItemCalendarDayBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
And I suggest also getting rid of all those onCreate functions and adding constructors that use the parent ViewGroup, just to streamline things, for example:
class CalendarDateViewHolder private constructor(private val binding: ItemCalendarDayBinding): MyViewHolder(binding.root) {
constructor(parent: ViewGroup): this(ItemCalendarDayBinding.inflate(LayoutInflater.from(parent.context), parent, false))
//...
}

Unresolved reference: myViewHolder after switching to View Binding

After switching from removing kotlin_extensions and switching to view binding, I received a "Unresolved reference: myViewHolder" in my onBindViewHolder method and when I replace "myViewHolder" with "holder", it then gives me a "Unresolved reference: bind". How do I resolve this.
MyAdapter
class MyAdapter(private val context: Context, private val mHelper : TaskDbHelper) : RecyclerView.Adapter<MyAdapter.MyViewHolder>(),
SwipeAndDragHelper.ActionCompletionContract {
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun binding() {
}
}
private var touchHelper: ItemTouchHelper? = null
private var list = mutableListOf<MyObject>()
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
initList()
super.onAttachedToRecyclerView(recyclerView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(CellCardsBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
#RequiresApi(Build.VERSION_CODES.P)
#SuppressLint("ClickableViewAccessibility")
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val myObject = list[position]
myViewHolder.bind(myObject)
val activity: Activity = context as Activity
holder.binding.text.setOnClickListener{
activity.launchActivity<AddNoteActivity>(42) {
putExtra("PositionInList", position.toString())
putExtra("TaskTitle", myObject.title)
putExtra("TaskText", myObject.text)
}
}
activity.findViewById<RecyclerView>(R.id.recyclerView).setOnTouchListener { _, event ->
when (event.actionMasked) {
MotionEvent.ACTION_UP -> {
updateNotesPositionInDb()
false
}
else -> {
false
}
}
}
holder.binding.title.setOnTouchListener { _, event ->
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
touchHelper!!.startDrag(holder)
false
}
else -> {
false
}
}
}
}
private fun initList() {
list.clear()
val db = mHelper.readableDatabase
val cursor = db.query(
TaskContract.TaskEntry.TABLE,
arrayOf(
TaskContract.TaskEntry.ID,
TaskContract.TaskEntry.COL_TASK_TITLE,
TaskContract.TaskEntry.COL_TASK_TEXT,
TaskContract.TaskEntry.COL_TASK_DATE),null, null, null, null, TaskContract.TaskEntry.ID)
while (cursor.moveToNext()) {
val id = cursor.getColumnIndex(TaskContract.TaskEntry.ID)
val idTitle = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_TITLE)
val idText = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_TEXT)
val idDate = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_DATE)
list.add(MyObject(cursor.getString(id), cursor.getString(idTitle), cursor.getString(idText), cursor.getString(idDate)))
}
notifyDataSetChanged()
cursor.close()
db.close()
}
override fun getItemCount(): Int {
return list.size
}
override fun onViewMoved(oldPosition: Int, newPosition: Int) {
val target = list[oldPosition]
list.removeAt(oldPosition)
list.add(newPosition, target)
notifyItemMoved(oldPosition, newPosition)
}
override fun onViewSwiped(position: Int) {
deleteTask(list[position].ID)
list.removeAt(position)
notifyItemRemoved(position)
updateNotesPositionInDb()
}
fun setTouchHelper(touchHelper: ItemTouchHelper) {
this.touchHelper = touchHelper
}
fun addTask(taskTitle : String, taskText: String) {
val values = ContentValues()
val sdf = SimpleDateFormat("dd/MM/yyyy/", Locale.US)
val date = sdf.format(Date())
values.put(TaskContract.TaskEntry.ID, list.size)
values.put(TaskContract.TaskEntry.COL_TASK_TITLE, taskTitle)
values.put(TaskContract.TaskEntry.COL_TASK_TEXT, taskText)
values.put(TaskContract.TaskEntry.COL_TASK_DATE, date)
val db = mHelper.readableDatabase
db.insertWithOnConflict(TaskContract.TaskEntry.TABLE,
null,
values,
SQLiteDatabase.CONFLICT_REPLACE)
db.close()
list.add(MyObject(list.size.toString(), taskTitle, taskText, date))
notifyItemInserted(list.size)
}
fun addTask() {
val test: Activity = context as Activity
test.launchActivity<AddNoteActivity>(42) {
/* putExtra("user", "854")
p utExtra("user2", "46850") */
}
}
private fun deleteTask(taskId: String) {
val db = mHelper.readableDatabase
db.delete(TaskContract.TaskEntry.TABLE,
"id=$taskId", null)
db.close()
}
fun modifyTask(taskPosition: String, taskTitle: String, taskText: String) {
val target = list[taskPosition.toInt()]
target.title = taskTitle
target.text = taskText
val values = ContentValues()
val sdf = SimpleDateFormat("dd/MM/yyyy/", Locale.US)
val date = sdf.format(Date())
values.put(TaskContract.TaskEntry.ID, taskPosition)
values.put(TaskContract.TaskEntry.COL_TASK_TITLE, taskTitle)
values.put(TaskContract.TaskEntry.COL_TASK_TEXT, taskText)
values.put(TaskContract.TaskEntry.COL_TASK_DATE, date)
val db = mHelper.readableDatabase
db.update(TaskContract.TaskEntry.TABLE,
values, TaskContract.TaskEntry.ID + "=" + target.ID, null)
db.close()
notifyItemChanged(taskPosition.toInt())
}
private fun updateNotesPositionInDb() {
val db = mHelper.readableDatabase
var i = 0
while (i < list.size) {
val values = ContentValues()
values.put(TaskContract.TaskEntry.ID, i)
db.update(TaskContract.TaskEntry.TABLE,
values, TaskContract.TaskEntry.ID + "=? AND " + TaskContract.TaskEntry.COL_TASK_TITLE + "=?", arrayOf(list[i].ID, list[i].title))
i++
}
db.close()
}
I've tried reading Android Studio's official documentation, but it cannot solve my specific problem.
in your class MyViewHolder you have method called binding and you need also to implement it and add paramter
shoud be
class MyViewHolder(private val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun bind(data:MyObject) {
binding.yourView=data.title ...
}
}
in onBindViewHolder
..
holder.bind(myObject)
After switching from removing kotlin_extensions and switching to view binding, I received a "Unresolved reference: myViewHolder" in my onBindViewHolder method
Well, your onBindViewHolder method is passing a variable called holder and you're trying to use a variable called myViewHolder, so that seems like a problem.
// --------------------this-----v
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val myObject = list[position]
// v--- doesn't match this
myViewHolder.bind(myObject)
and when I replace "myViewHolder" with "holder", it then gives me a "Unresolved reference: bind". How do I resolve this.
Your MyViewHolder class has a method called binding that takes no arguments. There is no bind method that takes a "myObject".
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun binding() {
}
}
Edit
You should pass an instance of the data class
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun bind(object: MyObject) {
// Set variables on binding
}
}
Then pass an instance from your list via onBindViewHolder:
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val myObject = list[position]
holder.bind(myObject)
Please check this blog post for more.
I got my answer.
class MyAdapter(private val context: Context, private val mHelper : TaskDbHelper) : RecyclerView.Adapter<MyAdapter.MyViewHolder>(),
SwipeAndDragHelper.ActionCompletionContract {
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
private val titleView: TextView = itemView.findViewById<View>(R.id.title) as TextView
val textView: TextView = itemView.findViewById<View>(R.id.text) as TextView
private val dateTextView: TextView = itemView.findViewById<View>(R.id.date) as TextView
fun binding (myObject: MyObject) {
titleView.text = myObject.title
textView.text = myObject.text
dateTextView.text = myObject.date
}
}
I simply initialised the view I wanted to reference in my layout and called them in the binding() function.