listview items are not showing when i set the height to wrap or match or 0dp, they only show up when i use specific number - android

my listview has a relative layout parent, it is set to wrap hight and match width, the problem is in the listview height, when i set it to 100dp it works fine, the items show, but when i set it to wrap or match parent it clips and only show one or non of the items, i can see that there is a scroll bar at the side as well, how can i make it wrap?
<RelativeLayout
android:id="#+id/expandable_rl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="#dimen/xlarge">
<ListView
android:id="#+id/lisview"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
app:layout_constraintVertical_bias="1.0"
android:layout_alignParentBottom="true"
app:layout_constraintHorizontal_bias="0.0" />
</RelativeLayout>
and this is how i am setting it, its just to test it, its located in a recycler view
holder.list.adapter = ArrayAdapter(holder.itemView.context, R.layout.simple_item, arrayListOf("hello","hi","testing"))
this is how i expand the expandable layout
holder.itemView.setOnClickListener {
if (unitsList[position].isExpandable) {
holder.expandableRL.visibility = if (holder.expandableRL.visibility == View.GONE) View.VISIBLE else View.GONE
}
}
i tried using notifyItemChanged(position) but it made it expand then immediately collapse for some reason
i am new here and don't know how to fix this at the moment

A fully wrapped list means it doesn't scroll, as all of items are visible.
If that is the case, why using a ListView in the first place.
Here is a suggestion to make use of LinearLayout instead.
XML for RecyclerView item:
<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"
>
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="Click Me" />
<LinearLayout
android:id="#+id/expandable_rl"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:visibility="gone"
android:orientation="vertical">
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
And Here's the adapter:
class RecyclerAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.recycler_item,
parent,
false
)
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder as? ViewHolder)?.itemView?.let { itemView ->
val expandable = itemView.findViewById<LinearLayout>(R.id.expandable_rl)
createItems(expandable)
itemView.findViewById<Button>(R.id.button).setOnClickListener {
expandable.visibility = View.VISIBLE
}
}
}
private fun createItems(expandable: LinearLayout) {
listOf("Hi", "Hello", "Goodbye").forEach { word ->
val item = createItem(expandable.context, word)
expandable.addView(item)
}
}
private fun createItem(context: Context, word: String): View {
return LinearLayout(context).apply {
layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER
addView(
TextView(context).apply {
layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
text = word
}
)
}
}
override fun getItemCount(): Int {
return 30
}
class ViewHolder(view: View): RecyclerView.ViewHolder(view)
}
This is a basic design, but you can take it from here and design what you need.

Related

Android: RelativeLayout measures child width incorrectly

What I want to do:
My idea is simple:
I have RecyclerView in RelativeLayout
When data loaded I set first item's text to TextView for pinned message
On scroll I take first visible item and set this item's text to header (TextView)
So I have:
RelativeLayout + RecyclerView + TextView for pinned message + TextView for header
I update header on scroll, it looks like sticky header for list.
It is my layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="#+id/pin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ebebeb"
android:gravity="center"
android:padding="24dp"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/pin"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="#id/list"
android:layout_alignEnd="#id/list"
android:background="#33ff0000"
android:gravity="center"
android:padding="24dp"/>
</RelativeLayout>
And it is my Activity:
class CustomAdapter() :
RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
var data: List<UUID> = listOf()
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val textView: TextView
init {
textView = view.findViewById(R.id.text)
}
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.list_item, viewGroup, false)
return ViewHolder(view)
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
viewHolder.textView.text = data[position].toString()
}
override fun getItemCount() = data.size
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val adapter = CustomAdapter()
val header = findViewById<TextView>(R.id.header)
findViewById<RecyclerView>(R.id.list).apply {
this.adapter = adapter
layoutManager = LinearLayoutManager(this#MainActivity, LinearLayoutManager.VERTICAL, false)
addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val position = recyclerView.topChildPosition() ?: return
if (position >= 0) {
header.text = adapter.data[position].toString().substring(0, 10)
Log.w("MainActivity", header.text.toString())
} else {
header.text = null
}
}
})
}
/**
* SIMULATE DATA LOADING
*/
Handler(Looper.getMainLooper()).postDelayed({
adapter.data = List(100) {
UUID.randomUUID()
}
findViewById<TextView>(R.id.pin).text = adapter.data[0].toString()
adapter.notifyDataSetChanged()
}, 1000L)
}
private fun RecyclerView.topChildPosition(): Int? {
layoutManager.let { layoutManager ->
if (layoutManager != null && layoutManager is LinearLayoutManager) {
return if (!layoutManager.reverseLayout) layoutManager.findFirstVisibleItemPosition()
else layoutManager.findLastVisibleItemPosition()
} else {
val topChild: View = getChildAt(0) ?: return null
return getChildAdapterPosition(topChild)
}
}
}
}
And what I have:
RelativeLayout measured views, header has text = null, so header's width = padding only
Data loaded (see "SIMULATE DATA LOADING" in the activity code), I call notifyDataSetChanged and I set text to header. Header (TextView) calls requestLayout() on set text, but RelativeLayout doesn't measured new width (actually, RelativeLayout calls header's onMeasure with widthMode == MeasureSpec.EXACTLY and pass old width)
When I scroll RecyclerView header remeasured successfully.
I can reproduce it on Android 26-33
What I can do with this.
I can change layout:
from this:
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="#id/list"
android:layout_alignEnd="#id/list"
android:background="#33ff0000"
android:gravity="center"
android:padding="24dp"/>
to this:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignTop="#id/list"
android:layout_alignEnd="#id/list">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="#33ff0000"
android:gravity="center"
android:padding="24dp"/>
</FrameLayout>
But actually I can't, because really my TextView in custom view extends from TextView and it is part of library. I don't know how it will be used in layouts.
I can call requestLayout() after data loading like this:
adapter.notifyDataSetChanged()
header.doOnNextLayout { header.post { header.requestLayout() } }
But it is ugly.
So, finally my questions:
Do you know how to fix it without layout changing?
Do you know bug or some documented RelativeLayout behavior
Thanks for any help!

recycler view does not respect list item's margin

how it should look -
how it looks -
listitem.xml -
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="#color/card_background"
android:layout_marginVertical="5dp"
android:layout_marginHorizontal="10dp"
app:cardCornerRadius="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/is_app_or_web_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/card_spacing"
android:padding="#dimen/image_padding"
app:layout_constraintBottom_toBottomOf="#id/delete_image_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="#id/delete_image_view"
tools:src="#drawable/is_website_icon_24" />
<TextView
android:id="#+id/service_name_text_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="#dimen/card_spacing"
android:text="#string/service_name"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="#id/delete_image_view"
app:layout_constraintLeft_toRightOf="#id/is_app_or_web_image_view"
app:layout_constraintRight_toLeftOf="#id/edit_image_view"
app:layout_constraintTop_toTopOf="#id/delete_image_view" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="#+id/delete_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/card_spacing"
android:layout_marginEnd="#dimen/card_spacing"
android:background="#color/card_foreground"
android:padding="#dimen/image_padding"
android:src="#drawable/ic_baseline_delete_20"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="#+id/show_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="#dimen/card_spacing"
android:background="#color/card_foreground"
android:padding="#dimen/image_padding"
android:src="#drawable/ic_baseline_show_20"
app:layout_constraintBottom_toBottomOf="#id/delete_image_view"
app:layout_constraintRight_toLeftOf="#id/delete_image_view"
app:layout_constraintTop_toTopOf="#id/delete_image_view" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="#+id/edit_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="#dimen/card_spacing"
android:background="#color/card_foreground"
android:padding="#dimen/image_padding"
android:src="#drawable/ic_baseline_edit_20"
app:layout_constraintBottom_toBottomOf="#id/delete_image_view"
app:layout_constraintRight_toLeftOf="#id/show_image_view"
app:layout_constraintTop_toTopOf="#id/delete_image_view" />
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="#dimen/card_spacing"
app:cardBackgroundColor="#color/card_foreground"
app:cardCornerRadius="#dimen/corner_radius"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="#id/is_app_or_web_image_view"
app:layout_constraintRight_toRightOf="#id/delete_image_view"
app:layout_constraintTop_toBottomOf="#id/is_app_or_web_image_view">
<com.google.android.material.textview.MaterialTextView
android:id="#+id/service_password_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:background="#00000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="#string/service_password" />
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
what I have already tried -
setting the list item's height to be a fixed value. something like 200 dp (that had no effect. It was as if that value wasn't even entered)
tried changing layout managers(no effect)
now, I don't even know what to try. any help is useful.
adapter code -
package com.kenetic.savepass.adapters
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.kenetic.savepass.R
import com.kenetic.savepass.databinding.PassListBinding
import com.kenetic.savepass.password.PasswordData
class PassAdapter() :
ListAdapter<PasswordData, PassAdapter.PassViewHolder>(diffCallBack) {
class PassViewHolder(val binding: PassListBinding) : RecyclerView.ViewHolder(binding.root) {
var verifiedWithPassword = false
private val TAG = "PassAdapter"
fun bind(passwordData: PasswordData) {
binding.isAppOrWebImageView.setImageResource(
if (passwordData.isAnApplication) {
R.drawable.is_application_icon_24
} else {
R.drawable.is_website_icon_24
}
)
binding.serviceNameTextView.text = passwordData.serviceName
binding.servicePasswordTextView.text = passwordData.servicePassword
binding.deleteImageView.setOnClickListener {
Log.d(TAG, "delete image onClick working")
}
binding.showImageView.setOnClickListener {
Log.d(TAG, "show image onClick working")
}
binding.editImageView.setOnClickListener {
Log.d(TAG, "edit image onClick working")
}
}
}
companion object {
private val diffCallBack = object : DiffUtil.ItemCallback<PasswordData>() {
override fun areItemsTheSame(oldItem: PasswordData, newItem: PasswordData) =
(oldItem.id == newItem.id)
override fun areContentsTheSame(oldItem: PasswordData, newItem: PasswordData) =
(oldItem == newItem)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PassViewHolder {
return PassViewHolder(PassListBinding.inflate(LayoutInflater.from(parent.context)))
}
override fun onBindViewHolder(holder: PassViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
I had the same problem, but it turned that I wasn't inflating the recyclerview item the good way.
I was previously inflating these parameters :
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
return TodoViewHolder(
TodoListItemBinding.inflate(LayoutInflater.from(parent.context))
)
}
And here's the inflating way that made margin between items appear correctly :
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
return TodoViewHolder(
TodoListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
}
list item will work remove margin android:layout_marginVertical="5dp" android:layout_marginHorizontal="10dp" and add this line android:layout_margin="5dp" if you want use margin different using margin top and margin_bottom instead of marginvertical
(answering my own question) (work around)
my list item xml was a card layout. I changed it such that the card layout was inside a constraint layout and then constrained the card layout to all four sides of the parent constraint layout. So, now my list item xml is a constraint layout with a card layout inside it. Then, I added margin to the card layout which gave spacing between the border of the card layout and the constraint layout. So, now I visually have spacing between my list items.
the next issue is the constraint layout background being of white colour. To fix that, set the constraint layout colour to be transparent #00000000 . That's all.
Florian-Martin answered the question perfectly. and the solution is very simple
in your
#override
ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
}
method change following line.
return PassViewHolder(PassListBinding
.inflate(LayoutInflater
.from(parent.getContext())));
to
return PassViewHolder(PassListBinding
.inflate(LayoutInflater
// add ViewGroup parent and boolean attachToRoot parameters
.from(parent.getContext())),parent,false);

Different width of each horizontal RecyclerViews items

I get a problem with my horizontal RecyclerView. I need to create RecyclerView with different width of each item. I use wrap_content for this:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
android:id="#+id/recyclerItemLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardPreventCornerOverlap="false"
app:cardCornerRadius="#dimen/_10sdp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#android:color/darker_gray"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp">
<TextView
android:id="#+id/recyclerItemText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:textSize="#dimen/_17sdp"
android:text="test"
android:textColor="#color/colorKeyboardRecyclerViewItemText"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:layout_marginTop="7dp"
android:layout_marginBottom="7dp"/>
</androidx.cardview.widget.CardView>
But when I scroll recyclerview and get to the first element I got this:
I think it is because the adapter redraws items every time. Here is code of my adapter:
class KeyboardAdapter(private val fontsNames: FontsData, private val fontButtonCallback: (Int) -> Unit): RecyclerView.Adapter<KeyboardAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.keyboard_recyclerview_item, parent, false)
return ViewHolder(v)
}
override fun getItemCount(): Int {
return fontsNames.fontsArrayEn.count()
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemText.text = fontsNames.fontsArrayEn[position].name
val selectedColor = holder.itemLayout.context.resources.getColor(R.color.colorKeyboardRecyclerViewSelectedItem)
val backgroundColor = holder.itemLayout.context.resources.getColor(android.R.color.transparent)
holder.itemLayout.setOnClickListener{
fontButtonCallback(position)
changeIsSelectedState(position)
fontsNames.fontsArrayEn[position].isSelected = true
notifyDataSetChanged()
}
/* holder.itemLayout.post{ val itemHeight = holder.itemLayout.height.toFloat()
val itemRadius = itemHeight /2.5
holder.itemLayout.radius = Utils().intToDp(holder.itemLayout.context, itemRadius.toFloat())} */
if (fontsNames.fontsArrayEn[position].isSelected)
holder.itemLayout.setCardBackgroundColor(selectedColor)
else
holder.itemLayout.setCardBackgroundColor(backgroundColor)
}
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val itemLayout = itemView.findViewById<CardView>(R.id.recyclerItemLayout)!!
val itemText = itemView.findViewById<TextView>(R.id.recyclerItemText)!!
}
private fun changeIsSelectedState(position: Int){
for (i in 0 until fontsNames.fontsArrayEn.count()){
fontsNames.fontsArrayEn[i].isSelected = i == position
}
}
}
And i have one more question. How I can set cardCornerRadius dynamically depends of item height ?
Thanks in advance!
The TextView inside the card is the problem.
The text view is match_parent
android:layout_width="match_parent"
android:layout_height="match_parent"
But the parent is wrap_content.
Change the TextView to wrap_content and then every word is gonna be the size of the TextView and the CardView will have the size of the child.

Image gets replaced in recylerview while scrolling, android?

I am displaying a list of countries along with their flags in recylerview
The 1st element does not have a image and uses a default image which is visible on launch of page
But when I scroll and come back to it the image gets changed to some random from the list which should not happen
This is my adapter
class CountryAdapter(private val list: MutableList<Data?>?) :
RecyclerView.Adapter<CountryAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ElementCountryBinding.inflate(inflater)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val country: Data? = list?.get(position)
if (country != null) {
holder.bind(country)
}
holder.itemView.setOnClickListener {
}
}
override fun getItemCount(): Int = list!!.size
inner class ViewHolder(val binding: ElementCountryBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(country: Data) {
binding.data = country
if (country.filePath != null)
Glide.with(binding.root.context)
.load(country.filePath!!.trim()).into(binding.ivFlag)
}
}
}
This is the xml 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">
<data>
<variable
name="data"
type="com.mountmeru.model.Data" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:id="#+id/main_cardview"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_marginBottom="5dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/iv_flag"
android:layout_width="100dp"
android:layout_height="70dp"
android:layout_marginStart="10dp"
android:adjustViewBounds="true"
android:src="#drawable/ic_share"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvCountryName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="#{data.countryName}"
app:layout_constraintBottom_toBottomOf="#+id/iv_flag"
app:layout_constraintLeft_toRightOf="#+id/iv_flag"
app:layout_constraintTop_toTopOf="#+id/iv_flag" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
< /RelativeLayout>
</layout>
screenshot
So the default image you specified in your XML layout is the ic_share, this means that when onBindViewHolder is called, the image gets substituted by:
.load(country.filePath!!.trim()).into(binding.ivFlag)
However, you never specified that at position 0, the icon must be ic_share, so because of RecyclerView's nature, when you scroll downwards and upwards and the first itemHolder is created (again) it uses a recycled view from further down, and as you're not setting ic_share to iv_flag at position 0 it just uses the recycled view image.
If you just add a line of code like #ADM suggested in your bind method like this:
if(adapterPosition==0){
binding.ivFlag.setImageResource(R.drawable.ic_share)
}
With the ic_share, I think that should make it work
That's normal due to the recycling mechanism of views in RV/LV. To avoid that set it to null
if (country.filePath != null)
Glide.with(binding.root.context)
.load(country.filePath!!.trim()).into(binding.ivFlag)
else
binding.ivFlag.setImageDrawable(null)
assuming ivFlag is an ImageView, or a default/placeholder if you have it
This is happening because you never set ic_share during bind View.
inner class ViewHolder(val binding: ElementCountryBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(country: Data) {
binding.data = country
if(adapterPosition==0){
binding.ivFlag.setImageResource(R.drawable.binding.ivFlag)
}else {
if (country.filePath != null)
Glide.with(binding.root.context)
.load(country.filePath!!.trim()).into(binding.ivFlag)
}
}
}
Having the function getItemViewType also solves the problem
override fun getItemViewType(position: Int): Int {
return position
}

How to change attribute of view programatically from the view in my recyclerView in my fragment?

I set the margin of the view (card view) in my xml of my item file, this xml item file will be used in for my recyclerView adapter.
As you can see in my xml below, that I have given margin to top, bottom, start and end. and I want to change the margin from my fragment
Here is my xml file, item_category_list.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="8dp"
app:cardElevation="4dp"
android:id="#+id/cardView_item_category_list" android:layout_marginStart="8dp" android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp" android:layout_marginTop="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/background_light">
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
app:srcCompat="#drawable/logo_apps"
android:id="#+id/categoryImageView_Item"
android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="24dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="24dp"
app:layout_constraintDimensionRatio="w,1:1" android:scaleType="centerCrop"/>
<TextView
android:text="#string/Category"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/categoryName_textView_item"
app:layout_constraintTop_toBottomOf="#+id/categoryImageView_Item"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="4dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="4dp"
android:textAlignment="center"
android:minLines="1"
android:maxLines="2"
app:autoSizeTextType="uniform"
app:autoSizeMinTextSize="10sp"
app:autoSizeMaxTextSize="15sp"
app:autoSizeStepGranularity="1sp"
android:layout_marginBottom="24dp"
android:layout_marginTop="24dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
Here is the adapter:
class CategoryAdapter(val context: Context, val categories: List<Category>) : RecyclerView.Adapter<CategoryAdapter.ViewHolderCategory>() {
private lateinit var mListener : CategoryAdapterListener
interface CategoryAdapterListener {
fun onItemClick(position: Int)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderCategory {
val layoutInflater = LayoutInflater.from(parent.context)
val itemView = layoutInflater.inflate(R.layout.item_category_list,parent, false)
return ViewHolderCategory(itemView,mListener)
}
override fun getItemCount(): Int {
return categories.size
}
override fun onBindViewHolder(holder: ViewHolderCategory, position: Int) {
val category = categories[position]
holder.categoryNameTextView.text = category.name
Glide
.with(context)
.load(category.getFormattedImageURL())
.into(holder.categoryImageView)
}
inner class ViewHolderCategory(itemView: View, listener: CategoryAdapterListener) : RecyclerView.ViewHolder(itemView) {
val categoryImageView = itemView.findViewById<ImageView>(R.id.categoryImageView_Item)
val categoryNameTextView = itemView.findViewById<TextView>(R.id.categoryName_textView_item)
val cardView = itemView.findViewById<CardView>(R.id.cardView_item_category_list)
init {
itemView.setOnClickListener {
val position = adapterPosition
if (position != RecyclerView.NO_POSITION) {
listener.onItemClick(position)
}
}
}
}
fun setCategoryAdapterListener(listener: CategoryAdapterListener) {
mListener = listener
}
}
and in the fragment, I set the adapter to the recycler view:
val categoryAdapter = CategoryAdapter(mContext,parentCategory)
val layoutManager = GridLayoutManager(mContext,4,RecyclerView.VERTICAL,false)
recyclerViewParentCategory.adapter = categoryAdapter
recyclerViewParentCategory.layoutManager = layoutManager
recyclerViewParentCategory.setHasFixedSize(true)
I want to change that margin in card view in my item_category_list.xml programatically in my java/kotlin file (in my fragment file), so I can change the margin from my fragment.
So how can I achieve it ? Java/Kotlin any language is preferred.
First Of all its a long way . So i'm just suggesting a way .
First of all . In your Fragment when some action happen you need to change cardview
size in adapter list item xml.
So . You need a interface for that (Let's say interface ChangeMargin). create
interface in Fragment and implement that interface in your adapter like this
class CategoryAdapter(val context: Context, val categories: List<Category>):RecyclerView.Adapter<CategoryAdapter.ViewHolderCategory>(),ChangeMargin()
For how to create interface so can go through this
Now in that interface you need to get cardview and assign new margin .
#Override
public void ChangeMargin() {
val linear_params=LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT)
linear_params.setMargins(leftMargin,topmargin,rightMargin,bottomMargin)
cardView?.layoutParams=linear_params
}
and don't forget to notify adapter
You can do something like this.
Make id of cardview , create instance of it in your adapter and do this code
ViewGroup.MarginLayoutParams layoutParams =
(ViewGroup.MarginLayoutParams) myCardView.getLayoutParams();
layoutParams.setMargins(10, 10, 10, 10);
myCardView.requestLayout();
To set margins to the cardView, you will have to create layoutParams, set margins to it and then set it as cardView LayoutParams like:
inner class ViewHolderCategory(itemView: View, listener: CategoryAdapterListener) : RecyclerView.ViewHolder(itemView) {
val categoryImageView = itemView.findViewById<ImageView>(R.id.categoryImageView_Item)
val categoryNameTextView = itemView.findViewById<TextView>(R.id.categoryName_textView_item)
val cardView = itemView.findViewById<CardView>(R.id.cardView_item_category_list)
//Main code here
val lparams=LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT)
lparams.setMargins(leftMargin,topmargin,rightMargin,bottomMargin)
cardView?.layoutParams=lparams
}

Categories

Resources