Prefetching view for child recycler view is not working - android

I have vertical RecyclerView with child(nested) horizontal RecyclerView.
For second recycler I use layout manager:
LinearLayoutManager(itemView.context, LinearLayoutManager.HORIZONTAL, false).apply {
isItemPrefetchEnabled = true
initialPrefetchItemCount = 4
}
But nested recycler builds only first two visible items.
Full code:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recycler = findViewById<RecyclerView>(R.id.recycler)
recycler.layoutManager = LinearLayoutManager(this)
recycler.adapter = MainAdapter(buildElements())
}
private fun buildElements(): List<Parent> {
return (0..2).map { pCount -> createParent(pCount) }
}
private fun createParent(index: Int): Parent {
return Parent(text = "$index",
list = (0..30).map { Child("item $it") }
)
}
}
Main adapter
class MainAdapter(private val list: List<Parent>) : RecyclerView.Adapter<MainAdapter.CustomHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomHolder {
val inflater = LayoutInflater.from(parent.context)
return CustomHolder(inflater.inflate(R.layout.item, parent, false))
}
override fun getItemCount(): Int = list.size
override fun onBindViewHolder(holder: CustomHolder, position: Int) {
holder.bind(list[position])
}
class CustomHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val recycler = itemView.findViewById<RecyclerView>(R.id.recyclerView)
private val title = itemView.findViewById<TextView>(R.id.title)
init {
recycler.layoutManager =
LinearLayoutManager(itemView.context, LinearLayoutManager.HORIZONTAL, false).apply {
isItemPrefetchEnabled = true
initialPrefetchItemCount = 4
}
}
fun bind(data: Parent) {
title.text = data.text
recycler.adapter = ChildAdapter(data.list)
}
}
}
Main recycler element
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="16dp"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_marginTop="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>
Child adapter
class ChildAdapter(private val list: List<Child>) : RecyclerView.Adapter<ChildAdapter.ChildHolder>() {
companion object {
private val TAG = ChildAdapter::class.java.simpleName
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChildHolder {
val inflater = LayoutInflater.from(parent.context)
return ChildHolder(inflater.inflate(R.layout.child, parent, false))
}
override fun getItemCount(): Int = list.size
override fun onBindViewHolder(holder: ChildHolder, position: Int) {
holder.bind(list[position])
}
class ChildHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val title = itemView.findViewById<TextView>(R.id.childText)
fun bind(data: Child) {
title.text = data.text
Log.d(TAG, "bind child element: ${data.text}")
}
}
}
And child element:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="275dp"
android:padding="8dp"
android:layout_height="100dp">
<TextView
android:id="#+id/childText"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:layout_height="wrap_content"/>
</FrameLayout>
In logs is see only this:
D/ChildAdapter: bind child element: item 0
D/ChildAdapter: bind child element: item 1
D/ChildAdapter: bind child element: item 0
D/ChildAdapter: bind child element: item 1
D/ChildAdapter: bind child element: item 0
D/ChildAdapter: bind child element: item 1
Logs mean that only the first and second elements was build, but I want prebuilt also third and forth elements.
Also I try use this, but it also not working

prefetching working only for scrolling, not for the first initial items creation.
Finally I use custom layout manager with override getExtraLayoutSpace
and return 100000. My solution potentially dangerous, but I use it, because I know that item count can't be more than 10 items.

Related

Modify the number of columns dynamically in a RecyclerView and also the size of the images

I have been able to change the number of columns in a RecyclerView with the GridLayout dynamically 2-1, but in addition I need to change the height so that when it is 1 column the image size is larger than in the 2 column row.
I have seen that with StaggeredGridLayoutManager I can do that resizing but I can't figure out how to do both.
val gridLayoutManager=GridLayoutManager(requireContext(),2)
binding.recycler.layoutManager=gridLayoutManager
gridLayoutManager.spanSizeLookup=object:GridLayoutManager.SpanSizeLookup(){
override fun getSpanSize(position: Int): Int {
return if((position+1)%3==0){
2
}else{
1
}
}
Glide.with(binding.root)
.asBitmap()
.load(bindObject.image)
.apply(RequestOptions().centerCrop())
.apply(
RequestOptions()
.override(160, 160))
.into(binding.image!!)
}
class HomeAdapter(private val items: MutableList, val viewModel: HomeViewModel): GlobalAdapter(items) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GlobalViewHolder {
val inflater = LayoutInflater.from(parent.context)
return HomeViewHolder(DataBindingUtil.inflate(inflater, R.layout.item_news2, parent, false),
viewModel)
}
override fun onBindViewHolder(holder: GlobalViewHolder<News>, position: Int) {
holder.bind(items[position])
}
fun addItems(items: List<News>?) {
if (items != null) {
this.items.addAll(items)
notifyDataSetChanged()
}
}
}
You could define multiple viewTypes in your RecyclerView.Adapter and assign them a custom height based on your needs.
Define two layout types, for example item_small and item_large.
item_small represents the smaller image:
<?xml version="1.0" encoding="utf-8"?>
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/imageViewSmall"
android:layout_width="match_parent"
android:layout_height="100dp" <!-- Smaller size -->
/>
item_large represents the bigger image:
<?xml version="1.0" encoding="utf-8"?>
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/imageViewLarge"
android:layout_width="match_parent"
android:layout_height="250dp" <!-- Bigger size -->
/>
You can then use the getItemViewType method to define the type of view that should be displayed based on your condition ((position+1)%3==0):
private const val ITEM_VIEW_TYPE_SMALL = 0
private const val ITEM_VIEW_TYPE_LARGE = 1
class HomeAdapter(
private val items: MutableList,
val viewModel: HomeViewModel
): RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun getItemViewType(position: Int): Int = if ((position+1) % 3 == 0) {
// 2 column item, return the small image
ITEM_VIEW_TYPE_SMALL
} else {
// 1 column item, return the large image
ITEM_VIEW_TYPE_LARGE
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
if (viewType == ITEM_VIEW_TYPE_SMALL) return SmallViewHolder(ItemSmallBinding.inflate(inflater, parent, false))
return LargeViewHolder(ItemLargeBinding.inflate(inflater, parent, false))
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder.bind(...) // Pass the image url here
}
fun addItems(items: List<News>?) {
if (items != null) {
this.items.addAll(items)
notifyDataSetChanged()
}
}
// Small image view holder
class SmallViewHolder(private val binding: ItemSmallBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(image: String) {
// Set the image in imageViewSmall
}
}
// Large image view holder
class LargeViewHolder(private val binding: ItemLargeBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(image: String) {
// Set the image in imageViewLarge
}
}
}

MERGE ITEMS in Recycler View when dragged & dropped on one another, in ANDROID?

Using ItemTouchHelper class we can DRAG, DROP, & SWIPE items in the recycler view; but how to just merge two items when dragged & dropped on one another?
Is it possible to do using ItemTouchHelper (or) is there any other API for that?
You could override onMove() in your ItemTouchHelper, it is called when you drag item A over item B. It gets called with the parameters viewHolder: RecyclerView.ViewHolder and target: RecyclerView.ViewHolder where viewHolder is the viewHolder of item A, and target is item B.
Have some variable of type ViewHolder, that you set to target in onMove, to always have a reference to the item below item A.
override clearView() to detect when the item is dropped, update your model in the background, so itemA now is merged with target, then call notifyItemChanged(itemA.adapterPosition) and notifyItemRemoved(itemB.adapterPosition) to animate a "merge"
class MainActivity : AppCompatActivity() {
companion object{
val fruit = arrayListOf("apple", "pear", "orange", "banana", "grape", "pineapple")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView1 = findViewById<RecyclerView>(R.id.testRecycler)
val layoutManager = LinearLayoutManager(this)
recyclerView1.layoutManager = layoutManager
val adapter = FruitAdapter()
recyclerView1.adapter = adapter
val itemTouchHelper = ItemTouchHelper(
object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
0
) {
#SuppressLint("StaticFieldLeak")
var target: RecyclerView.ViewHolder? = null
#SuppressLint("StaticFieldLeak")
var moving: RecyclerView.ViewHolder? = null
override fun clearView(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
) {
if(target!=null && moving != null){
val targetPos = target!!.adapterPosition
val sourcePos = moving!!.adapterPosition
fruit[targetPos] += "\n\n" + fruit[sourcePos]
fruit.removeAt(sourcePos)
target = null
moving = null
adapter.notifyDataSetChanged()
}
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder
): Boolean {
this.target = target
this.moving = viewHolder
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
TODO("Not yet implemented")
}
})
itemTouchHelper.attachToRecyclerView(recyclerView1)
}
}
class FruitAdapter: RecyclerView.Adapter<FruitAdapter.FruitViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.item, parent, false)
return FruitViewHolder(itemView)
}
override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {
holder.itemView.findViewById<TextView>(R.id.fruitNameTextView).text = MainActivity.fruit[position]
}
override fun getItemCount(): Int {
return MainActivity.fruit.size
}
class FruitViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}
}
item.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:padding="20px"
android:layout_margin="20px"
android:background="#color/teal_200"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/fruitNameTextView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:id="#+id/testRecycler"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Can't show items by sections correctly in recyclerview in kotlin

I am relatively new in kotlin and I would like to know how I can display items by sections, like this: showing data in sections . I have two services, one returns the list of section names and the other returns the items for each section.
So far I did the following, the view is like this:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools">
<TextView
android:id="#+id/header_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="header" />
<androidx.appcompat.widget.SwitchCompat
android:id="#+id/header_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/list_productos_seccion"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="#+id/header_switch"
app:layout_constraintStart_toStartOf="#+id/header_title"
app:layout_constraintTop_toBottomOf="#+id/header_title" />
</androidx.constraintlayout.widget.ConstraintLayout>
and in my outer adapter I did the following, where I pass a list of sections to the adapter and for each one I consult the service to obtain items and display it:
class SeccionesAdapter(
var seccionClickAdapter: SeccionClickAdapter,
private val context: Context,
var fragment: Fragment,
var productoViewModel: ProductoViewModel,
var vm: ClientActivityViewModel
): RecyclerView.Adapter<SeccionesAdapter.MyViewHolder>() {
interface SeccionClickAdapter{
fun onSeccionClick(producto: ProductoQryDTO)
}
var dataList = emptyList<SeccionQryDTO>()
var productosLista = ArrayList<ProductoQryDTO>()
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun setData(seccionDTO: SeccionQryDTO) {
itemView.header_title.text = seccionDTO.nombre
itemView.header_switch.isChecked = seccionDTO.isSelected?: true
itemView.list_productos_seccion.layoutManager = LinearLayoutManager(context)
if (itemView.header_switch.isChecked) {
itemView.list_productos_seccion.visibility = View.VISIBLE
dataFromServer(seccionDTO.id, itemView)
}
itemView.header_switch.setOnCheckedChangeListener { _, isChecked -> visible(isChecked, seccionDTO.id) }
}
fun visible(isChecked: Boolean, idSeccionDTO: Int) {
if (isChecked) {
itemView.list_productos_seccion.visibility = View.VISIBLE
dataFromServer(idSeccionDTO, itemView)
} else {
itemView.list_productos_seccion.visibility = View.GONE
}
}
}
fun dataFromServer(idSeccionDTO: Int, itemView: View) {
productosLista.clear()
productoViewModel.getItems(
idSeccionDTO,
{
fragment.activity?.runOnUiThread {
productosLista.clear()
productosLista.addAll(it.data!!)
itemView.list_productos_seccion.adapter = ProductoxSeccionAdapter(context, productosLista, seccionClickAdapter)
}
}, {
Log.e("Error", "", it)
}
)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_header_model, parent, false))
}
override fun getItemCount(): Int {
return dataList.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.setData(dataList[position]!!)
}
fun setSecciones(seccionesList : List<SeccionQryDTO>) {
this.dataList = seccionesList
notifyDataSetChanged()
}
}
The problem is that when the application is loaded, sometimes the amount of items that should be for each section is not shown, and when I scroll it begins to make calls to the service, also the elements disappear and it remains blank as shown in this image
blank space in sections.
Could you help me please? I don't know how I could handle this situation or if I should implement it in some other way
EDIT
This is my inner adapter: ProductoxSeccionAdapter
class ProductoxSeccionAdapter (
private val context: Context,
var listProductos: List<ProductoQryDTO>?,
var seccionClickAdapter: SeccionesAdapter.SeccionClickAdapter
) : RecyclerView.Adapter<ProductoxSeccionAdapter.MyViewHolder>() {
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun setData(producto: ProductoQryDTO) {
itemView.nombreProductoLista.text = producto.nombre
itemView.descProductoLista.text = producto.detalle
itemView.precioProductoLista.text = producto.simboloMoneda + producto.precio
itemView.setOnClickListener { seccionClickAdapter.onSeccionClick(producto) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_producto_cliente_lista, parent, false))
}
override fun getItemCount(): Int {
return listProductos!!.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.setData(listProductos!![position])
}
}

RecyclerView doesn't display last item when multiple ViewHolders are used

I'm using androidx.recyclerview.widget.RecyclerView to display a list of items, separated by an other item as a "header" with some aggregated values.
When i put only one item in my list without adding the header, everything is ok and the item is displayed correctly. As soon as i add the header item, only the header is displayed and the one single item isn't shown.
When i add two items and the header, the header and one item are displayed. I don't know why the last item of my list is missing altough it exists in the adapters datasource.
My ListAdapter inherits from RecyclerView.Adapter<RecyclerView.ViewHolder> and uses two ViewHolders detected by a viewType property of my list items.
When loading the data, the onBindViewHolder method isn't called for the last item in my list, even tough the item is in the visible section of my screen.
Does anybody has a hint, why this happens?
class ListAdapter(val onClick: (position: Long) -> Unit,
val onLongClick: (Long) -> Unit,
val onShareClick: (id: Long?) -> Unit) : RecyclerView.Adapter<RecyclerView.ViewHolder>(),
BindableAdapter<List<ListAdapterItem<*>>> {
var items: List<ListAdapterItem<*>> = emptyList()
private var actionMode: ActionMode? = null
var tracker: SelectionTracker<Long>? = null
init {
setHasStableIds(true)
}
override fun setData(data: List<ListAdapterItem<*>>) {
this.items = data // all items are set correctly here!!
notifyDataSetChanged()
}
override fun getItemViewType(position: Int): Int {
return if (items.isEmpty()) EMPTY else items[position].viewType
}
override fun getItemCount(): Int {
return if (items.isEmpty()) 1 else items.filter { it.viewType == ITEM }.size
}
override fun getItemId(position: Int): Long = position.toLong()
fun getItem(position: Long): ListViewModel.ListItem = item[position.toInt()].value as ListViewModel.ListItem
fun setActionMode(actionMode: ActionMode?) {
this.actionMode = actionMode
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
EMPTY -> EmptyViewHolder(parent)
HEADER -> HistoryGroupHeaderViewHolder(parent)
else -> HistoryViewHolder(parent)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is HistoryViewHolder) {
val item = items[position].value as ListViewModel.ListItem
tracker?.let {
holder.bind(item, it.isSelected(position.toLong()))
}
holder.itemView.setOnClickListener {
onClick(position.toLong())
}
holder.itemView.findViewById<AppCompatImageView>(R.id.history_item_share)?.setOnClickListener {
onShareClick(item.id)
}
}
else if (holder is HistoryGroupHeaderViewHolder) {
val header = items[position].value as ListViewModel.ListSectionHeader
holder.bind(header)
}
}
class HistoryViewHolder(
private val parent: ViewGroup,
private val binding: at.app.databinding.ViewHistoryListItemBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.view_history_list_item,
parent,
false
)
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: ListViewModel.ListItem, isActivated: Boolean = false) {
binding.model = item
itemView.isActivated = isActivated
val imageView = itemView.findViewById<AppCompatImageView>(R.id.history_item_image)
if(itemView.isActivated) {
val parameter = imageView?.layoutParams as ConstraintLayout.LayoutParams
parameter.setMargins(
parent.context.resources.getDimension(R.dimen.spacing_small).toInt(),
parent.context.resources.getDimension(R.dimen.spacing_small).toInt(),
parent.context.resources.getDimension(R.dimen.spacing_small).toInt(),
parent.context.resources.getDimension(R.dimen.spacing_small).toInt()
)
imageView.layoutParams = parameter
} else {
val parameter = imageView?.layoutParams as ConstraintLayout.LayoutParams
parameter.setMargins(0,0,0,0)
imageView.layoutParams = parameter
}
}
fun getItemDetails(): ItemDetailsLookup.ItemDetails<Long> =
object : ItemDetailsLookup.ItemDetails<Long>() {
override fun getPosition(): Int = adapterPosition
override fun getSelectionKey(): Long? = itemId
}
}
class HistoryGroupHeaderViewHolder(
private val parent: ViewGroup,
private val binding: at.app.databinding.ViewHistoryListGroupHeaderItemBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.view_history_list_group_header_item,
parent,
false
)
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: ListViewModel.ListSectionHeader) {
binding.model = item
}
}
class EmptyViewHolder(
private val parent: ViewGroup, view: View = LayoutInflater.from(parent.context).inflate(
R.layout.view_history_empty_item,
parent,
false
)
) : RecyclerView.ViewHolder(view)
companion object {
const val EMPTY = 0
const val ITEM = 1
const val HEADER = 2
}
}
class MyItemDetailsLookup(private val recyclerView: RecyclerView) : ItemDetailsLookup<Long>() {
private val log = LoggerFactory.getLogger(ListAdapter::class.java)
override fun getItemDetails(e: MotionEvent): ItemDetails<Long>? {
val view = recyclerView.findChildViewUnder(e.x, e.y)
if (view != null) {
return try {
if(recyclerView.getChildViewHolder(view) is ListAdapter.HistoryViewHolder) {
(recyclerView.getChildViewHolder(view) as ListAdapter.HistoryViewHolder)
.getItemDetails()
} else {
null
}
} catch (ex: Exception) {
log.error("Error on getItemDetails. ", ex)
null
}
}
return null
}
}
data class ListAdapterItem<out T>(val value: T, val viewType: Int)
And this is my layout:
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<import type="android.view.View" />
<variable
name="viewModel"
type="at.app.ui.viewmodel.ListViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="#+id/list_app_bar"
layout="#layout/layout_toolbar" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/history_recycler_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#android:color/transparent"
android:scrollbars="vertical"
app:data="#{viewModel.items}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/list_app_bar" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
When i add two items and the header, the header and one item are
displayed.
problem is in your getItemCount method.
override fun getItemCount(): Int {
return if (items.isEmpty()) 1 else items.filter { it.viewType == ITEM }.size
}
If you want to show 1 header and 2 elements that means that there are must be 3 items in recyclerview, so getItemCount must return 3. But now it looks like getItemCount will return 2, thats why recycerlview doesn't even create third element.

setOnLongClickListener in android with kotlin

How can I use setOnItemClickListner in each item in my ListView?
my xml :
<ListView
android:id="#+id/tv1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
this my adapter class
inner class mo3d1Adapter : BaseAdapter {
override fun getItemId(p0: Int): Long {
return p0.toLong()
}
override fun getCount(): Int {
return listOfmo3d.size
}
var listOfMkabala = ArrayList<MeetingDetails>()
var context: Context? = null
constructor(context: Context, listOfMkabaln: ArrayList<MeetingDetails>) : super() {
this.listOfMkabala = listOfMkabaln
this.context = context
}
override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View {
val mo3d = listOfmo3d[p0]
var inflatormo3d = context!!.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
var myViewmo3d = inflatormo3d.inflate(R.layout.fragment_item, null)
lvMo3d.onItemClickListener = AdapterView.OnItemClickListener { adapterView, view, i, l ->
Toast.makeText(context, " TEST STACK ", Toast.LENGTH_LONG).show()
}
myViewmo3d.meeting_name.text = mo3d.name1!!
myViewmo3d.meeting_date.text = mo3d.date.toString()!!
myViewmo3d.attendance_number.text = mo3d.n2.toString()!!
return myViewmo3d
}
override fun getItem(p0: Int): Any {
return listOfmo3d[p0]
}
}
I want listener for each item in my ListView
And when I used this method setOnClickListener in adapter it's not working, where can I use?
Try this in your activity class
lv.setOnItemClickListener { parent, view, position, id ->
Toast.makeText(this, "Position Clicked:"+" "+position,Toast.LENGTH_SHORT).show()
}
Although a little quirky this works fine for me.
latestMessagesAdapter.setOnItemLongClickListener { item, view ->
val row = item as LatestMessageRow
return#setOnItemLongClickListener(true)
}
First of all I would like to tell that it is RecyclerView rather than ListView. You can find plenty information why to do in such. For example you can read it hear :
RecyclerView vs. ListView
Regarding your question how to do it in correct way with RecyclerView.
Insert dependencies with RecyclerView, they are now in support library in Kotlin.
implementation "com.android.support:appcompat-v7:25.4.0"
First change your ListView with RecyclerView in xml layout like this:
<android.support.v7.widget.RecyclerView
android:id="#+id/accountList"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Create Adapter for RecyclerView:
class AccountListAdapter(val accountList: AccountList, val itemListener: (Account) -> Unit) :
RecyclerView.Adapter<AccountListAdapter.ViewHolder>(){
override fun getItemCount(): Int = accountList.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) =
holder.bind(accountList[position])
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder{
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_account, parent, false)
return ViewHolder(view, itemListener)
}
class ViewHolder(itemView: View, val itemClick: (Account) -> Unit): RecyclerView.ViewHolder(itemView){
fun bind(account : Account){
with(account){
itemView.accountName.text = title
itemView.setOnClickListener{ itemClick(this)}
}
}
}
}
item_account.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/accountName"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Models (in Kotlin you can put them in one file and name for example AccountModels.kt) :
data class AccountList(val accounts: List<Account>){
val size : Int
get() = accounts.size
operator fun get(position: Int) = accounts[position]
}
data class Account(val id : Long, val title : String, val balance : Int, val defCurrency: Int)
In Fragment/Activity connect your Adapter to RecyclerView:
override fun onStart() {
super.onStart()
setupAdapter()
}
fun setupAdapter(){
Log.d(TAG, "updating ui..")
val account1 = Account(1,"Credit", 1000, 2)
val account2 = Account(2, "Debit", 500, 2)
val account3 = Account(3, "Cash", 7000, 2)
val accounts : List<Account> = listOf(account1, account2, account3)
val adapter = AccountListAdapter(AccountList(accounts)){
val title = it.title
Log.d(TAG, "$title clicked")
}
accountList.layoutManager = LinearLayoutManager(activity)
accountList.adapter = adapter
}
That is all. Everything should work now. Hope it helps.

Categories

Resources