Recycler View Duplicating Data after Refreshing the page - kotlin

when i swipe to refresh the data, the first swipe is ok but after that every swipe reload and add the same data over and over again, by the end i have a list with same items over and over I tried to clear before but i don't understand what's wrong with my code, if someone could explain it to me
adapter
package com.intersoft.packhouse.adapters
import android.app.Activity
import android.text.method.TextKeyListener.clear
import android.text.method.TextKeyListener.clearMetaKeyState
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.intersoft.packhouse.R
import com.intersoft.packhouse.enums.BasicClass
import com.intersoft.packhouse.enums.BasicContext
import com.intersoft.packhouse.fragments.BaseFragment
import com.intersoft.packhouse.printing.PrintListener
import com.intersoft.packhouse.viewholders.*
class BasicAdapter(val context: Activity, val printListener: PrintListener, val frag:BaseFragment,myBasicContext:BasicContext?=null):RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val inflater = LayoutInflater.from(context)
var baseClass:BasicClass? = null
var items = mutableListOf<Any>()
var original = mutableListOf<Any>()
var myContext = context
var myBasicContext = myBasicContext
override fun getItemViewType(position: Int): Int {
val item = items[position]
return BasicClass.fromClaz(item)
.uniqueId()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when(viewType){
BasicClass.Crop.uniqueId() -> CropViewHolder(inflater.inflate(R.layout.row_crop, parent, false))
BasicClass.CropX.uniqueId() -> CropViewHolder(inflater.inflate(R.layout.row_crop, parent, false))
BasicClass.ColdRoom.uniqueId() -> CRoomViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.ColdCell.uniqueId() -> CellViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.QC.uniqueId() -> QCViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.RmStandards.uniqueId() -> RmStandardViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.Supplier.uniqueId() -> SupplierViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.Customer.uniqueId() -> CustomerViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.OrderModel.uniqueId() -> OrderViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.Receiving.uniqueId() -> ReceivingViewHolder(inflater.inflate(R.layout.row_receiving, parent, false))
BasicClass.Intake.uniqueId() -> IntakeViewHolder(inflater.inflate(R.layout.row_basic, parent, false), printListener,myBasicContext)
BasicClass.RmStorage.uniqueId() -> RmStorageViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.Requisition.uniqueId() -> RequisitionViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.Issuing.uniqueId() -> IssueingViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.Vehicle.uniqueId() -> VehicleViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.Processing.uniqueId() -> ProcessingViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.Packaging.uniqueId() -> PackagingViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.Conformance.uniqueId() -> ConformanceViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.Dispatch.uniqueId() -> DispatchViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.DispatchedItem.uniqueId() -> DispatchedItemViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.Collection.uniqueId() -> CollectionViewHolder(inflater.inflate(R.layout.row_basic, parent, false), frag)
BasicClass.FarmPurchase.uniqueId() -> FarmPurchaseViewHolder(inflater.inflate(R.layout.row_basic, parent, false), printListener)
BasicClass.CollectionDispatch.uniqueId() -> CDItemViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.Binning.uniqueId() -> BinningViewHolder(myContext,inflater.inflate(R.layout.row_basic, parent, false), printListener)
BasicClass.SortingLots.uniqueId() -> SortingLotViewHolder(myContext,inflater.inflate(R.layout.row_sorting, parent, false), printListener)
BasicClass.SortedBins.uniqueId() -> SortedBinsViewHolder(myContext,inflater.inflate(R.layout.row_sorting, parent, false), printListener)
BasicClass.NewBins.uniqueId() -> NewBinsViewHolder(myContext,inflater.inflate(R.layout.row_sorting, parent, false), printListener)
BasicClass.ProcessingLots.uniqueId() -> ProcessingLotsViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.BatchSheet.uniqueId() -> BatchSheetViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
BasicClass.Reject.uniqueId() -> RejectViewHolder(inflater.inflate(R.layout.row_basic, parent, false))
else -> CropViewHolder(inflater.inflate(R.layout.row_crop, parent, false))
}
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder as? ListHolder<Any>)?.let {
it.bind(items[position])
}
}
}
view holder
package com.intersoft.packhouse.viewholders
import android.view.View
import com.intersoft.packhouse.R
import com.intersoft.packhouse.models.BatchSheet
import com.intersoft.packhouse.utils.toMiscDate
import com.google.gson.Gson
class BatchSheetViewHolder(view: View):BaseHolder(view) , ListHolder<BatchSheet>{
override fun bind(item: BatchSheet){
clear()
icon(R.drawable.ic_human_resources)
keyValue("Date", item.date)
keyValue("Duration", "${item.start_time} - ${item.end_time} (${item.duration} mins)")
keyValue("Lot No", item?.lot?.lot_no)
keyValue("GRN", "${item?.intake?.prn} ")
keyValue("Supplier", item?.supplier?.company)
keyValue("Order No", item?._order?.order_no)
keyValue("Item",item?.item?.item_name)
keyValue("No of Boxes", item.no_of_boxes)
keyValue("Net Weight", item.net_weight)
// keyValue("ordered Item", item?.item_name)
// keyValue("4KGS", item?.lot?.four_kg_boxes)
// keyValue("10KGS", item?.lot?.ten_kg_boxes)
// keyValue("Remarks", item.remarks)
}
}
data class
package com.intersoft.packhouse.models
import android.content.ClipData
data class BatchSheet(
var _id:Int? = null,
var date: String? = null,
var duration: Int? = null,
var end_time: String? = null,
var prn: String? = null,
var intake: Intake? = null,
var net_weight: Int? = null,
var no_of_boxes: Int? = null,
var _order: Order? = null,
var remarks: String? = null,
var item: CropX? = null,
var supplier: Supplier? = null,
var start_time: String? = null,
var lot:ProcessingLots? = null,
var item_name: String? = null
):Optionable{
override fun optionTitle(): String? {
return prn
}
override fun optionValue(): Any? {
return _id
}
override fun searchString(): String? {
return "${super.searchString()} ${intake?.item?.item_name} ${intake?.prn} ${supplier?.account_name}"
//TODO:Supplier name add this when its changed to supplier object
}
}
I want the recycler view not to duplicate the data

Related

how to update Textview in main activity with button in adapter?

i want to update the value of my textview that is in the PosActivity when the button in MyAdapter is click ( to increase/decrease the quantity and to delete the card from recycler view). and i can't find anything on how to do it.
here is what i tried.
in MyAdapter :
package com.mycodlabs.pos.ui.sale.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.mycodlabs.pos.R
import com.mycodlabs.pos.domain.inventory.ProductModel
import kotlinx.android.synthetic.main.pos_item_card.view.*
import java.math.BigDecimal
class MyAdapter(mUx: Context, var selectedItems: ArrayList<ProductModel>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
val mUx = mUx
// var quantity = 1
var totalPrice = BigDecimal.ZERO
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var pclose = itemView.removeItempos
var pname = itemView.pos_name
var pprice = itemView.pos_price
var pqty = itemView.pos_qty
var pminus = itemView.cart_minus_img
var pplus = itemView.cart_plus_img
// val pimage = itemView.pos_image
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.pos_item_card, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val pos: ProductModel = selectedItems[position]
holder.pname.text = pos.name
holder.pprice.text = pos.unitPrice.toString()
holder.pqty.text = pos.quantity.toString()
// holder.pimage.= pos.image
holder.pclose.setOnClickListener {
val username = pos.name
var price = pos.unitPrice
totalPrice -= price
selectedItems.removeAt(position)
notifyDataSetChanged()
notifyItemRangeChanged(position, selectedItems.size)
// Toast.makeText(mUx, "User $username Deleted", Toast.LENGTH_SHORT).show()
}
holder.pminus.setOnClickListener {
if(pos.quantity == 1 ){
// Toast.makeText(mUx,"Can't go any lower", Toast.LENGTH_SHORT).show()
}else {
pos.quantity -= 1
notifyItemChanged(position)
}
}
holder.pplus.setOnClickListener {
pos.quantity += 1
notifyItemChanged(position)
}
}
fun grandTotal(items: ArrayList<ProductModel>): BigDecimal {
totalPrice = BigDecimal.ZERO
for (i in items.indices) {
totalPrice += items[i].unitPrice.multiply(items[i].quantity.toBigDecimal())
}
return totalPrice
}
fun clearData() {
selectedItems.clear()
notifyDataSetChanged()
}
override fun getItemCount() = selectedItems.size
}
and in the Activity:
package com.mycodlabs.pos.ui
import android.os.Bundle
import android.view.View
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.mycodlabs.pos.R
import com.mycodlabs.pos.db.AndroidDatabase
import com.mycodlabs.pos.db.DatabaseTables
import com.mycodlabs.pos.db.inventory.InventoryDbo
import com.mycodlabs.pos.db.sale.SalesLinesDao
import com.mycodlabs.pos.domain.inventory.ProductModel
import com.mycodlabs.pos.ui.sale.adapter.MyAdapter
import kotlinx.android.synthetic.main.activity_pos.*
import kotlinx.android.synthetic.main.adapter_available_promotions.*
import kotlinx.android.synthetic.main.dialog_paymentsuccession.view.*
import kotlinx.android.synthetic.main.dialog_saleedit.*
import kotlinx.android.synthetic.main.layout_addcategory.*
import kotlinx.android.synthetic.main.layout_sale.*
import kotlinx.android.synthetic.main.listview_stock.*
import kotlinx.android.synthetic.main.pos_bottom_sheet.*
import kotlinx.android.synthetic.main.pos_item_card.*
class PosActivity : AppCompatActivity() {
private lateinit var bottomSheetBehavior: BottomSheetBehavior<LinearLayout>
private lateinit var productNames: ArrayList<String>
private lateinit var recyclerView: RecyclerView
private lateinit var db: SalesLinesDao
//// Create an empty list to store the selected items
var selectedItems = ArrayList<ProductModel>()
//// Create an adapter for the RecyclerView
val adapter = MyAdapter(this,selectedItems)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pos)
// for Back button
back.setOnClickListener {
finish()
}
check_out_Pos.setOnClickListener{
// val pos = ProductModel()
//
// db = SalesLinesDbo(this)
//
// pos.name = pos_name.text.toString()
//
// db.addLineItem(pos.id, LineItemModel())
adapter.clearData()
}
// val autoCompleteTextView = findViewById<AutoCompleteTextView>(R.id.searchBoxPos)
// autoCompleteTextView.threshold = 0
// val suggestions =InventoryDbo.getInstance(applicationContext).allProduct
// var NameArray=suggestions.toList().filter { t -> t.name.contains(autoCompleteTextView)}.toList().map { m->m.name }
// val arrayAdapter = ArrayAdapter(this, android.R.layout.simple_expandable_list_item_2 ,NameArray)
// autoCompleteTextView.setAdapter(arrayAdapter)
// Auto Complete Textview filtering from Product table colm "productName"
val autoCompleteTextView = findViewById<AutoCompleteTextView>(R.id.searchBoxPos)
val productNames = ArrayList<String>()
val dbHelper = AndroidDatabase(this)
val db = dbHelper.readableDatabase
val cursor = db.rawQuery(
"SELECT DISTINCT ${InventoryDbo.colm_productName} FROM ${DatabaseTables.TABLE_PRODUCT} WHERE ${InventoryDbo.colm_productName} like '%%'",
null
)
if (cursor.moveToFirst()) {
do {
productNames.add(cursor.getString(cursor.getColumnIndex(InventoryDbo.colm_productName)))
} while (cursor.moveToNext())
}
cursor.close()
db.close()
val adapterr = ArrayAdapter(this, android.R.layout.simple_dropdown_item_1line, productNames)
autoCompleteTextView.setAdapter(adapterr)
// //// Auto Complete suggestion item display in recyclerview on select
// // Create the RecyclerView
val recyclerView = findViewById<RecyclerView>(R.id.sale_List_Pos)
//// Create a layout manager for the RecyclerView
val layoutManager = LinearLayoutManager(this)
recyclerView.layoutManager = layoutManager
recyclerView.adapter = adapter
// Set an item click listener for the AutoCompleteTextView
searchBoxPos.setOnItemClickListener { _, _, position, _ ->
// Get the selected product name and product from the list
val selectedItem = autoCompleteTextView.adapter.getItem(position).toString()
// val selectedProductName = productNames[position]
val selectedProduct = InventoryDbo.getInstance(applicationContext).getPosProductByName(selectedItem).first()
//InventoryDbo.getProductByName(selectedProductName)
// Add the selected product to the selected items list
selectedItems.add(selectedProduct)
// Notify the adapter that the data has changed
adapter.notifyDataSetChanged()
// Clear the focus and text from the AutoCompleteTextView
searchBoxPos.clearFocus()
searchBoxPos.setText("")
total_items_Pos.text = adapter.grandTotal(selectedItems).toString()
}
//for the bottomsheet
bottomSheetBehavior = BottomSheetBehavior.from<LinearLayout>(std_btm_sht)
bottomSheetBehavior.setBottomSheetCallback(object :
BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, state: Int) {
print(state)
when (state) {
BottomSheetBehavior.STATE_HIDDEN -> {
}
BottomSheetBehavior.STATE_EXPANDED -> {
total_items_Pos.text = adapter.grandTotal(selectedItems).toString()
}
BottomSheetBehavior.STATE_COLLAPSED -> {
total_items_Pos.text = adapter.grandTotal(selectedItems).toString()
}
BottomSheetBehavior.STATE_DRAGGING -> {
total_items_Pos.text = adapter.grandTotal(selectedItems).toString()
}
BottomSheetBehavior.STATE_SETTLING -> {
total_items_Pos.text = adapter.grandTotal(selectedItems).toString()
}
BottomSheetBehavior.STATE_HALF_EXPANDED -> {
total_items_Pos.text = adapter.grandTotal(selectedItems).toString()
}
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
})
}
}
i hope you can help me solve this because it was bugging me all day and i couldn't find anything about it
I'll post the changes for the plus button, you can then repeat it for the others. Mind that this is just an example, as I don't know in what way you'd like to update the text or what's the actual name of your TextView.
class MyAdapter(
mUx: Context,
var selectedItems: ArrayList<ProductModel>,
val plusLambda: (String) -> Unit // <------
) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
...
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val pos: ProductModel = selectedItems[position]
...
holder.pplus.setOnClickListener {
pos.quantity += 1
plusLambda("The new quantity is: ${ pos.quantity }") // <------
notifyItemChanged(position)
}
...
class PosActivity : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pos)
...
val adapterr = ArrayAdapter(this,
android.R.layout.simple_dropdown_item_1line,
productNames) {
someTextView.text = it // <------
}
autoCompleteTextView.setAdapter(adapterr)
...
Of course, you can use just one lambda for all buttons if all you want to do is to change the text of the TextView.

Can't Update child item in NestedRecyclerView

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
)

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))
//...
}

Please help kotlin error 'No value passed for parameter'

I have a chat application. But I am getting such error in adapters.
Error: No value passed for parameter 'itemView2' What I'm trying to do is to display the item named itemView2 in the xml page. I am not very proficient in Kotlin language. That's why I need help.
This is MessagingAdapter.kt
class MessagingAdapter(private val messages: OrderedRealmCollection<Message>, autoUpdate: Boolean,
private val context: Context, private val lifecycleOwner: LifecycleOwner, var user: User, private val myThumbImg: String,
private val selectedItems: LiveData<List<Message>>,
private val progressMap: LiveData<Map<String, Int>>, private val audibleState: LiveData<Map<String, AudibleState>>)
: RealmRecyclerViewAdapter<Message, RecyclerView.ViewHolder>(messages, autoUpdate)
, StickyHeaderAdapter<RecyclerView.ViewHolder> {
private val interaction = context as? Interaction?
private val contactHolderInteraction = context as? ContactHolderInteraction?
private val audibleHolderInteraction = context as? AudibleInteraction?
//timestamps to implement the date header
var timestamps = HashMap<Int, Long>()
var lastTimestampPos = 0
//date header
override fun getHeaderId(position: Int): Long {
return if (timestamps.containsKey(position)) {
timestamps[position] ?: 0
} else 0
}
//date header
override fun onCreateHeaderViewHolder(parent: ViewGroup): RecyclerView.ViewHolder? {
val view = LayoutInflater.from(parent.context).inflate(R.layout.row_day, parent, false)
return HeaderHolder(view)
}
//date header
override fun onBindHeaderViewHolder(viewholder: RecyclerView.ViewHolder?, position: Int) {
val mHolder = viewholder as HeaderHolder?
//if there are no timestamps in this day then hide the header
//otherwise show it
val headerId = getHeaderId(position)
if (headerId == 0L) mHolder?.header?.visibility = View.GONE else {
val formatted = TimeHelper.getChatTime(headerId)
mHolder?.header?.text = formatted
}
}
override fun getItemCount() = messages.size
override fun getItemViewType(position: Int): Int {
val message = messages[position]
return message.type
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
// check the type of view and return holder
return getHolderByType(parent, viewType)
}
override fun onBindViewHolder(mHolder: RecyclerView.ViewHolder, position: Int) {
//get itemView type
val type = getItemViewType(position)
val message = messages[position]
when (type) {
MessageType.SENT_TEXT -> {
val sentTextHolder = mHolder as SentTextHolder
initHolder(sentTextHolder)
sentTextHolder.bind(message, user)
}
MessageType.SENT_IMAGE -> {
val sentImageHolder = mHolder as SentImageHolder
initHolder(sentImageHolder)
sentImageHolder.bind(message, user)
}
MessageType.SENT_VOICE_MESSAGE -> {
val sentVoiceMessageHolder = mHolder as SentVoiceMessageHolder
initHolder(sentVoiceMessageHolder)
initAudibleHolder(sentVoiceMessageHolder)
sentVoiceMessageHolder.bind(message, user)
}
MessageType.SENT_VIDEO -> {
val sentVideoMessageHolder = mHolder as SentVideoMessageHolder
initHolder(sentVideoMessageHolder)
sentVideoMessageHolder.bind(message, user)
}
MessageType.SENT_FILE -> {
val sentFileHolder = mHolder as SentFileHolder
initHolder(sentFileHolder)
sentFileHolder.bind(message, user)
}
MessageType.SENT_AUDIO -> {
val sentAudioHolder = mHolder as SentAudioHolder
initHolder(sentAudioHolder)
initAudibleHolder(sentAudioHolder)
sentAudioHolder.bind(message, user)
}
MessageType.SENT_CONTACT -> {
val sentContactHolder = mHolder as SentContactHolder
initHolder(sentContactHolder)
initContactHolder(sentContactHolder)
sentContactHolder.bind(message, user)
}
MessageType.SENT_LOCATION -> {
val sentLocationHolder = mHolder as SentLocationHolder
initHolder(sentLocationHolder)
sentLocationHolder.bind(message, user)
}
MessageType.SENT_STICKER -> {
val sentStickerHolder = mHolder as SentStickerHolder
initHolder(sentStickerHolder)
sentStickerHolder.bind(message, user)
}
MessageType.RECEIVED_TEXT -> {
val holder = mHolder as ReceivedTextHolder
initHolder(holder)
holder.bind(message, user)
}
MessageType.RECEIVED_IMAGE -> {
val receivedImageHolder = mHolder as ReceivedImageHolder
initHolder(receivedImageHolder)
receivedImageHolder.bind(message, user)
}
MessageType.RECEIVED_VOICE_MESSAGE -> {
val receivedVoiceMessageHolder = mHolder as ReceivedVoiceMessageHolder
initHolder(receivedVoiceMessageHolder)
initAudibleHolder(receivedVoiceMessageHolder)
receivedVoiceMessageHolder.bind(message, user)
}
MessageType.RECEIVED_VIDEO -> {
val receivedVideoMessageHolder = mHolder as ReceivedVideoMessageHolder
initHolder(receivedVideoMessageHolder)
receivedVideoMessageHolder.bind(message, user)
}
MessageType.RECEIVED_FILE -> {
val receivedFileHolder = mHolder as ReceivedFileHolder
initHolder(receivedFileHolder)
receivedFileHolder.bind(message, user)
}
MessageType.RECEIVED_AUDIO -> {
val receivedAudioHolder = mHolder as ReceivedAudioHolder
initHolder(receivedAudioHolder)
initAudibleHolder(receivedAudioHolder)
receivedAudioHolder.bind(message, user)
}
MessageType.RECEIVED_CONTACT -> {
val receivedContactHolder = mHolder as ReceivedContactHolder
initHolder(receivedContactHolder)
initContactHolder(receivedContactHolder)
receivedContactHolder.bind(message, user)
}
MessageType.RECEIVED_LOCATION -> {
val receivedLocationHolder = mHolder as ReceivedLocationHolder
initHolder(receivedLocationHolder)
receivedLocationHolder.bind(message, user)
}
MessageType.SENT_DELETED_MESSAGE -> {
val sentDeletedMessageHolder = mHolder as SentDeletedMessageHolder
sentDeletedMessageHolder.bind(message, user)
}
MessageType.RECEIVED_DELETED_MESSAGE -> {
val receivedDeletedMessageHolder = mHolder as ReceivedDeletedMessageHolder
receivedDeletedMessageHolder.bind(message, user)
}
MessageType.GROUP_EVENT -> {
val groupEventHolder = mHolder as GroupEventHolder
groupEventHolder.bind(message, user)
}
MessageType.RECEIVED_STICKER -> {
val receivedStickerHolder = mHolder as ReceivedStickerHolder
initHolder(receivedStickerHolder)
receivedStickerHolder.bind(message, user)
}
else -> {
val notSupportedTypeHolder = mHolder as? NotSupportedTypeHolder
notSupportedTypeHolder?.bind(message, user)
}
}
}
private fun initHolder(baseHolder: BaseHolder) {
baseHolder.selectedItems = selectedItems
baseHolder.progressMap = progressMap
baseHolder.lifecycleOwner = lifecycleOwner
baseHolder.interaction = interaction
}
private fun initAudibleHolder(audibleBase: AudibleBase) {
audibleBase.audibleInteraction = audibleHolderInteraction
audibleBase.audibleState = audibleState
}
private fun initContactHolder(contactHolderBase: ContactHolderBase) {
contactHolderBase.contactHolderInteraction = contactHolderInteraction
}
private fun distinctMessagesTimestamps() {
for (i in messages.indices) {
val timestamp = messages[i].timestamp.toLong()
if (i == 0) {
timestamps[i] = timestamp
lastTimestampPos = i
} else {
val oldTimestamp = messages[i - 1].timestamp.toLong()
if (!TimeHelper.isSameDay(timestamp, oldTimestamp)) {
timestamps[i] = timestamp
lastTimestampPos = i
}
}
}
}
//update timestamps if needed when a new message inserted
fun messageInserted() {
val index = messages.size - 1
val newTimestamp = messages[index].timestamp.toLong()
if (timestamps.isEmpty()) {
timestamps[index] = newTimestamp
lastTimestampPos = index
return
}
val lastTimestamp = timestamps[lastTimestampPos]!!
if (!TimeHelper.isSameDay(lastTimestamp, newTimestamp)) {
timestamps[index] = newTimestamp
lastTimestampPos = index
}
}
private fun getHolderByType(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when (viewType) {
MessageType.DAY_ROW -> return TimestampHolder(LayoutInflater.from(parent.context).inflate(R.layout.row_day, parent, false))
MessageType.SENT_DELETED_MESSAGE -> return SentDeletedMessageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_deleted_message, parent, false))
MessageType.RECEIVED_DELETED_MESSAGE -> return ReceivedDeletedMessageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_deleted_message, parent, false))
MessageType.SENT_TEXT -> return SentTextHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_message_text, parent, false))
MessageType.SENT_IMAGE -> return SentImageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_message_img, parent, false))
MessageType.RECEIVED_TEXT -> return ReceivedTextHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_message_text, parent, false)) //ERROR HERE
MessageType.RECEIVED_IMAGE -> return ReceivedImageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_message_img, parent, false))
MessageType.SENT_VOICE_MESSAGE -> return SentVoiceMessageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_voice_message, parent, false), myThumbImg)
MessageType.RECEIVED_VOICE_MESSAGE -> return ReceivedVoiceMessageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_message_voice, parent, false))
MessageType.RECEIVED_VIDEO -> return ReceivedVideoMessageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_message_video, parent, false))
MessageType.SENT_VIDEO -> return SentVideoMessageHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_message_video, parent, false))
MessageType.SENT_FILE -> return SentFileHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_file, parent, false))
MessageType.RECEIVED_FILE -> return ReceivedFileHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_file, parent, false))
MessageType.SENT_AUDIO -> return SentAudioHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_audio, parent, false))
MessageType.RECEIVED_AUDIO -> return ReceivedAudioHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_audio, parent, false))
MessageType.SENT_CONTACT -> return SentContactHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_contact, parent, false))
MessageType.RECEIVED_CONTACT -> return ReceivedContactHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_contact, parent, false))
MessageType.SENT_LOCATION -> return SentLocationHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_location, parent, false))
MessageType.RECEIVED_LOCATION -> return ReceivedLocationHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_location, parent, false))
MessageType.GROUP_EVENT -> return GroupEventHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_group_event, parent, false))
MessageType.SENT_STICKER -> return SentStickerHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_sent_sticker, parent, false))
MessageType.RECEIVED_STICKER -> return ReceivedStickerHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_sticker, parent, false))
}
return NotSupportedTypeHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_not_supported, parent, false))
}
init {
distinctMessagesTimestamps()
}
}
This is ReceivedTextHolder.kt
class ReceivedTextHolder(context: Context, itemView: View,itemView2: View) : BaseReceivedHolder(context,itemView) {
private var tvMessageContent: AXEmojiTextView = itemView.findViewById(R.id.tv_message_content)
private val circleImg: CircleImageView = itemView2.findViewById<View>(R.id.voice_circle_img_text) as CircleImageView
override fun bind(message: Message,user: User) {
super.bind(message,user)
tvMessageContent.text = message.content
loadUserPhoto(user,message.fromId, circleImg)
}
private fun loadUserPhoto(user:User,fromId: String, imageView: ImageView) {
//if it's a group load the user image
if (user.isGroupBool && user.group.users != null) {
val mUser = ListUtil.getUserById(fromId, user.group.users)
if (mUser != null && mUser.thumbImg != null) {
Glide.with(context).load(mUser.thumbImg).into(imageView)
}
} else {
if (user.thumbImg != null) Glide.with(context).load(user.thumbImg).into(imageView)
}
}
}
Please help guys :(
What I'm trying to do is to display the item named itemView2 in the xml page.
What the error means is that you're trying to call a function that asks for a parameter named itemView2, but you didn't pass that parameter. Usually this happens when you provide too few arguments when you call the method.
In this specific case, the method in question is actually the constructor of ReceivedTextHolder. As you can see, the constructor is declared to take 3 parameters (context, itemView, and itemView2):
class ReceivedTextHolder(context: Context, itemView: View,itemView2: View)
But when you call this constructor, you only give 2 arguments:
return ReceivedTextHolder(context, LayoutInflater.from(parent.context).inflate(R.layout.row_received_message_text, parent, false)) //ERROR HERE
Here, the first argument (context) receives the value context, and the second argument (itemView) receives the value of the expression:
LayoutInflater.from(parent.context).inflate(R.layout.row_received_message_text, parent, false)
But you're missing the 3rd argument. The 3rd would probably look like the 2nd one. You just need to find and inflate the correct view for that.

How to get the selected item from ListView in Kotlin?

Code Sample:
package tech.kapoor.listviewdemo
import android.content.Context
import android.graphics.Color
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.ListView
import android.widget.TextView
import android.widget.AdapterView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val listView = findViewById<ListView>(R.id.main_listview)
var redColor = Color.parseColor("#FF0000")
listView.setBackgroundColor(redColor)
listView.adapter = CustomAdapter(this)
}
private class CustomAdapter(context: Context): BaseAdapter() {
private val mContext: Context
init {
mContext = context
}
override fun getCount(): Int {
return 80
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItem(position: Int): Any {
return position
}
override fun getView(position: Int, view: View?, viewGroup: ViewGroup?): View {
val textView = TextView(mContext)
textView.text = "Here comes the !!"
return textView
}
}
}
I'm trying to understand list view instead of recycler view to understand basics first.
Anybody knows how we get the selected row id/index value on selection or onclick and also how to perform some action on selection of a specific row in kotlin?
To populate listview you must have dataset. Dataset may be any list of either datatypes like Strings or you can use list of model class. Something like this:
This is my simple list of dataset which I will use in ListView:
val data = ArrayList<TopicDTO>()
data.add(TopicDTO("1", "Info 1", true))
data.add(TopicDTO("2", "Info 2", false))
data.add(TopicDTO("3", "Info 3", true))
data.add(TopicDTO("4", "Info 4", false))
I have created one model class named TopicDTO which contains id,title and its status.
Now let's populate this into ListView:
list.adapter = ButtonListAdapter(baseContext, data)
Here is a simple adapter:
class ButtonListAdapter(//Class for rendering each ListItem
private val context: Context, private val rowItems: List<TopicDTO>) : BaseAdapter() {
override fun getCount(): Int {
return rowItems.size
}
override fun getItem(position: Int): Any {
return rowItems[position]
}
override fun getItemId(position: Int): Long {
return rowItems.indexOf(getItem(position)).toLong()
}
private inner class ViewHolder {
internal var main_text: TextView? = null //Display Name
internal var subtitle: TextView? = null //Display Description
internal var can_view_you_online: Button? = null //Button to set and display status of CanViewYouOnline flag of the class
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var convertView = convertView
var holder: ViewHolder? = null
val mInflater = context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE) as LayoutInflater
holder = ViewHolder()
if (convertView == null) {
convertView = mInflater.inflate(R.layout.main_lp_view_item, null)
holder.main_text = convertView!!.findViewById(R.id.main_lp_text) as TextView
holder.subtitle = convertView.findViewById(R.id.main_lp_subtitle) as TextView
holder.can_view_you_online = convertView.findViewById(R.id.can_view_you_online) as Button
convertView.tag = holder
} else {
holder = convertView.tag as ViewHolder
}
val rowItem = rowItems[position]
val main_text: String
val subtitle: String
holder.main_text!!.text = rowItem.info
holder.subtitle!!.text = rowItem.info
if (rowItem.canViewYouOnline) {
holder.can_view_you_online!!.setBackgroundColor(context.resources.getColor(R.color.colorPrimary))
} else {
holder.can_view_you_online!!.setBackgroundColor(context.resources.getColor(R.color.colorAccent))
}
holder.can_view_you_online!!.setOnClickListener(object : View.OnClickListener {
internal var buttonClickFlag: Boolean = false
override fun onClick(v: View) { //The Onclick function allows one to click the button on the list item and set/reset the canViewYouOnline flag. It is working fine.
}
})
return convertView
}
}
Now you can get your selected item like this:
list.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
// This is your listview's selected item
val item = parent.getItemAtPosition(position) as TopicDTO
}
Hope you understands this.
You can use inside the getView() method something like:
view.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
//use getItem(position) to get the item
}
})
or using the lambda:
view.setOnClickListener({ v -> //use theItem(position) })
Just a tip:
I'm trying to understand list view instead of recycler view to understand basics first.
In my opinion in your projects you will use RecyclerView in 99% of the cases.
add OnItemClickListener in you oncreate()
listView.setOnItemClickListener{ parent, view, position, id ->
Toast.makeText(this, "You Clicked:"+" "+position,Toast.LENGTH_SHORT).show()
}
Add the array of Items in your CustomAdapter class.
class CustomAdptor(private val context: Activity): BaseAdapter() {
//Array of fruits names
var names = arrayOf("Apple", "Strawberry", "Pomegranates", "Oranges", "Watermelon", "Bananas", "Kiwi", "Tomato", "Grapes")
//Array of fruits desc
var desc = arrayOf("Malus Domestica", "Fragaria Ananassa ", "Punica Granatum", "Citrus Sinensis", "Citrullus Vulgaris", "Musa Acuminata", "Actinidia Deliciosa", "Solanum Lycopersicum", "Vitis vinifera")
//Array of fruits images
var image = intArrayOf(R.drawable.apple, R.drawable.strawberry, R.drawable.pomegranates, R.drawable.oranges, R.drawable.watermelon, R.drawable.banana, R.drawable.kiwi, R.drawable.tomato, R.drawable.grapes)
override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View {
val inflater = context.layoutInflater
val view1 = inflater.inflate(R.layout.row_data,null)
val fimage = view1.findViewById(R.id.fimageView)
var fName = view1.findViewById(R.id.fName)
var fDesc = view1.findViewById(R.id.fDesc)
fimage.setImageResource(image[p0])
fName.setText(names[p0])
fDesc.setText(desc[p0])
return view1
}
override fun getItem(p0: Int): Any {
return image[p0]
}
override fun getItemId(p0: Int): Long {
return p0.toLong()
}
override fun getCount(): Int {
return image.size
}
}
You can find the whole tutorial at: listview with onItemClickListener using kotlin