Horizontal RecyclerView with dynamic item height - android

I am trying to create a horizontal recycler view with dynamic height by following this stack overflow post. The solution seems working. But the recycler view items disappear when I try to remove an item from recycler view.
recyclerview layout:
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:clipToPadding="false"
android:orientation="horizontal"
android:paddingStart="13dp"
android:paddingEnd="13dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Recycler view item layout:
<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"
android:layout_marginEnd="4dp"
android:layout_marginStart="4dp"
app:cardBackgroundColor="#BCAAAA"
app:cardElevation="2dp">
<TextView
android:padding="36dp"
android:id="#+id/textViewName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="22sp"
tools:text="Test String"/>
</androidx.cardview.widget.CardView>
Activity:
val flexBoxLayoutManager = FlexboxLayoutManager(this, FlexDirection.ROW, FlexWrap.NOWRAP)
with(recyclerView) {
layoutManager = flexBoxLayoutManager
adapter = RecyclerViewAdapter()
setHasFixedSize(false)
}
Adapter:
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var textViewName: TextView = itemView.findViewById(R.id.textViewName)
init {
updateLayoutParamsToAllowHorizontalScrolling()
}
private fun updateLayoutParamsToAllowHorizontalScrolling() {
(itemView.layoutParams as? FlexboxLayoutManager.LayoutParams)?.let {
it.flexShrink = 0.0f
it.alignSelf = AlignItems.FLEX_START
}
}
}
override fun onBindViewHolder(holder: RecyclerViewAdapter.ViewHolder, position: Int) {
val item = listItems[position]
var msg = "Sample item no:$position "
for(i in 0..position){
msg += "dynamic content \n"
}
holder.textViewName.text = msg
holder.itemView.setOnClickListener {
val pos = listItems.indexOf(item)
listItems.removeAt(pos)
notifyItemRemoved(pos)
}
}
A screen recording of the output:
Is there any way to fix this issue? Or Are there any other approach to implement horizontal recyclerveiw with dynamic height?

I have fixed the issue without using FlexBoxLayoutManger.
Used LinearLayoutManager with horizontal orientation and the following code in onBindViewHolder in RecyclerViewAdapter.
holder.itemView.post {
val wMeasureSpec =
View.MeasureSpec.makeMeasureSpec(holder.itemView.width, View.MeasureSpec.EXACTLY)
val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
holder.itemView.measure(wMeasureSpec, hMeasureSpec)
if (holder.itemView.measuredHeight > holder.itemView.height) {
holder.itemView.layoutParams =
(holder.itemView.layoutParams as ViewGroup.LayoutParams)
.apply {
height = holder.itemView.measuredHeight
}
}
}

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!

Improve spacing between recycleView items

I have an activity with recycleView this is what I should show in design
I use this code to show it
val arr:ArrayList<string> = arrayListOf("English","Intermediate","English","English","arr","UICollectionViewFlowLayoutFlowFlowFlow","English","UICollectionViewDelegate","English","Intermediate","UIViewController","viewDidLoad","Intermediate","String","Intermediate","arr","Intermediate","UIKit","Intermediate","English","columnLayout","English","languageLabel")
recyclerView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL)
recyclerView.layoutManager = GridLayoutManager(this, 3)
adaAdapter = CustomAdapter(this, arr)
recyclerView.adapter = adaAdapter
With this adapter
class CustomAdapter(val context: DreamsActivity, val items: ArrayList<String>) : RecyclerView.Adapter<CustomAdapter.ViewHolderProductList>() {
// holds this device's screen width,
private var screenWidth = 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderProductList {
val displayMetrics = DisplayMetrics()
context.windowManager.getDefaultDisplay().getMetrics(displayMetrics)
screenWidth = displayMetrics.widthPixels
val layoutInflater = LayoutInflater.from(parent.context)
val cellForRow = layoutInflater.inflate(R.layout.dreams_collection,parent, false)
val devicewidth: Int = (displayMetrics.widthPixels / 3) - 10
val params = cellForRow.getLayoutParams() as GridLayoutManager.LayoutParams
params.width = devicewidth
cellForRow.setLayoutParams(params)
return ViewHolderProductList(cellForRow)
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: ViewHolderProductList, position: Int) {
val item = items[position]
holder.typeTv.text = item
}
inner class ViewHolderProductList(itemView: View) : RecyclerView.ViewHolder(itemView) {
var typeTv: TextView = itemView.findViewById(R.id.typeTv)
var mainL: ConstraintLayout = itemView.findViewById(R.id.mainL)
}
}
With this 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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="10dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="15dp"
android:background="#FFFFFF"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
And Item layout
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/mainL"
android:layout_width="60dp"
android:padding="10dp"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/typeTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="5dp"
android:layout_marginBottom="5dp"
android:text="TextView"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
But I got this
And don't know how to do it as I need the number of items to be dynamic according to the text length but the GridLayoutManager only sets the number of columns static.
Any help is greatly appreciated
The desired layout tends to be Chips rather than a RecyclerView
Here is a demo:
Material app gradle dependency: implementation 'com.google.android.material:material:1.4.0'
Layout:
<com.google.android.material.chip.ChipGroup xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/my_chip_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
app:chipSpacing="16dp"
app:chipSpacingHorizontal="4dp"
app:chipSpacingVertical="4dp" />
Adding chips:
val arr =
arrayListOf(
"English",
"Intermediate",
"English",
"English",
"arr",
"UICollectionViewFlowLayoutFlowFlowFlow",
"English",
"UICollectionViewDelegate",
"English",
"Intermediate",
"UIViewController",
"viewDidLoad",
"Intermediate",
"String",
"Intermediate",
"arr",
"Intermediate",
"UIKit",
"Intermediate",
"English",
"columnLayout",
"English",
"languageLabel"
)
fun addChips(context: Context) {
val chipGroup = findViewById<ChipGroup>(R.id.my_chip_group)
for (i in 0 until arr.size) {
val chip = Chip(context)
chip.layoutParams = FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)
chip.text = arr[i]
chipGroup.addView(chip)
}
}
What you're asking for is something with the visual look of a Chip from Material Components. While the documentation for Chips suggests putting them in a ChipGroup, this loses the benefit of recycling views if you have more than enough to cover the screen. On the other hand, if they need to behave like a RadioGroup, where you can select only one to be toggled at a time, then ChipGroup would be easier to use.
To do this with RecylerView, you can change your item view layouts to use a Chip instead of a TextView. Set the item's layout width to wrapContent. Then use Google's Flexbox Layout library to get the layout with variable length items.
In Gradle dependencies: implementation 'com.google.android.flexbox:flexbox:3.0.0'
In your activity:
recyclerView.layoutManager = FlexboxLayoutManager(this)
.apply { flexDirection = FlexDirection.ROW }
Result:
There's a related question here that is helpful.

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

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.

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.

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