I'm using RecyclerView to display a list of items.
Dependent on the specific item, i want to show additional views in the cell.
I know the viewholder should be static, but how do i achieve a cell design that depends on the properties of the item?
Minified example, which gives me weird results:
class CustomAdapter internal constructor() :
RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
private var dataSet = emptyList()
inner class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
val title: TextView
val linearLayout: LinearLayout
init {
title = v.findViewById(R.id.textView)
linearLayout = v.findViewById(R.id.linearLayout)
}
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.cardview, viewGroup, false)
return ViewHolder(v)
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
viewHolder.title.text = dataSet[position].name
for (i in 0 until 5) {
viewHolder.linearLayout.addView(ImageView())
}
}
internal fun setItems(items: List<T>) {
this.dataSet = items
notifyDataSetChanged()
}
}
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="22dp"
android:layout_marginEnd="12dp"
android:text="Title"
android:textSize="18sp"
android:textStyle="bold" />
<LinearLayout
android:id="#+id/linearLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
</LinearLayout>
</androidx.cardview.widget.CardView>
You can't add an ImageView directly as shown in your example. Try this:
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
viewHolder.title.text = dataSet[position].name
for (i in 0 until 5) {
val holder = viewHolder
val context = holder.itemView.context
val imagebyCode = ImageView(context)
imagebyCode.setImageResource(R.drawable.exampleImage)
val params : LinearLayout.LayoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT);
imagebyCode.layoutParams = params
val myLayout : LinearLayout = holder.itemView.findViewById(R.id.linearLayout)
myLayout.addView(imagebyCode)
}
}
You can add views to the xml for the recycler view row and keep the visibility hidden.
In onBindViewHolder you can check the condition and make the View visible.
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
viewHolder.title.text = dataSet[position].name
for (i in 0 until 5) {
viewHolder.linearLayout.view.visibility = View.VISIBLE //Set visibility here
}
}
Related
enter image description here
When I click on cardview I want the other cardview color to turn white
here design is that i show in recyclerview
<?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="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:backgroundTint="#android:color/transparent"
android:elevation="0dp"
android:paddingBottom="10dp">
<androidx.cardview.widget.CardView
android:id="#+id/mainCard"
android:layout_width="80dp"
android:layout_height="100dp"
android:layout_marginLeft="1dp"
android:layout_marginTop="1dp"
android:layout_marginBottom="10dp"
app:cardCornerRadius="10dp"
app:cardElevation="15dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="#+id/cardImage"
android:layout_width="90dp"
android:layout_height="90dp"
android:layout_gravity="center"
android:layout_marginBottom="20dp"
app:srcCompat="#drawable/cate1" />
<TextView
android:id="#+id/mainText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="10dp"
android:text="Telefon, Tablet\nve Aksesuarlar"
android:textColor="#302E2E"
android:textSize="10sp" />
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="#+id/colorCard"
android:layout_width="82dp"
android:layout_height="102dp"
android:visibility="invisible"
app:cardBackgroundColor="#color/orange"
app:cardCornerRadius="10dp"
app:cardElevation="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
The block with adapter codes for the recyclerview that I have listed
I couldn't understand the logic in setOnclickListener
and
I don't know how to make an algorithm
class CategoryMainAdapter(val mContext: Context) :
RecyclerView.Adapter<CategoryMainAdapter.ViewHolderClass>() {
var categoryMainList: List<CategoryMain> = listOf()
inner class ViewHolderClass(view: View) : RecyclerView.ViewHolder(view) {
val mainCard: CardView
val cardImage: ImageView
val mainText: TextView
val colorCard: CardView
init {
mainCard = view.findViewById(R.id.mainCard)
cardImage = view.findViewById(R.id.cardImage)
mainText = view.findViewById(R.id.mainText)
colorCard = view.findViewById(R.id.colorCard)
}
}
#SuppressLint("NotifyDataSetChanged")
fun setList(list: List<CategoryMain>) {
categoryMainList = list
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderClass {
val design =
LayoutInflater.from(mContext).inflate(R.layout.main_category_design, parent, false)
return ViewHolderClass(design)
}
override fun onBindViewHolder(holder: ViewHolderClass, position: Int) {
val categoryMain = categoryMainList[position]
holder.mainText.text = categoryMain.categoryName
holder.cardImage.setImageResource(categoryMain.categoryPhoto)
holder.mainCard.setOnClickListener() {
holder.colorCard.visibility = View.VISIBLE
holder.mainText.setTextColor(ContextCompat.getColor(mContext, R.color.orange))
}
}
override fun getItemCount(): Int {
return categoryMainList.size
}
}
You can use a multi-select functionality in recycler view like this: How to implement multi-select in RecyclerView? or Multiple selected items RecyclerView in Activity.java
i solved my problem
now when i click on which cardview its color changes
class CategoryMainAdapter(val mContext: Context) :
RecyclerView.Adapter<CategoryMainAdapter.ViewHolderClass>() {
var categoryMainList: List<CategoryMain> = listOf()
var clikedPosition : Int = -1
inner class ViewHolderClass(view: View) : RecyclerView.ViewHolder(view) {
val mainCard: CardView
val cardImage: ImageView
val mainText: TextView
val colorCard: CardView
init {
mainCard = view.findViewById(R.id.mainCard)
cardImage = view.findViewById(R.id.cardImage)
mainText = view.findViewById(R.id.mainText)
colorCard = view.findViewById(R.id.colorCard)
}
}
#SuppressLint("NotifyDataSetChanged")
fun setList(list: List<CategoryMain>) {
categoryMainList = list
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderClass {
val design =
LayoutInflater.from(mContext).inflate(R.layout.main_category_design, parent, false)
return ViewHolderClass(design)
}
override fun onBindViewHolder(holder: ViewHolderClass, position: Int) {
val categoryMain = categoryMainList[position]
holder.mainText.text = categoryMain.categoryName
holder.cardImage.setImageResource(categoryMain.categoryPhoto)
if (categoryMain.isSelected == true){
categoryMainList[position].isSelected = false
holder.colorCard.visibility = View.INVISIBLE
holder.mainText.setTextColor(ContextCompat.getColor(mContext, R.color.blacklow))
}
holder.mainCard.setOnClickListener() {
if (clikedPosition != -1){
notifyItemChanged(clikedPosition)
}
clikedPosition = position
categoryMainList[position].isSelected = true
holder.colorCard.visibility = View.VISIBLE
holder.mainText.setTextColor(ContextCompat.getColor(mContext, R.color.orange))
}
}
override fun getItemCount(): Int {
return categoryMainList.size
}
}
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!
I show a list of items using adapter and recyclerview. Each item shows only 4 lines of biography-textView. In order to see whole biography, user clicks 'expand' textView. My code functions but there is a problem because sometimes (somehow randomly) one 'expand' click (f.e. in item No 1) causes expanding 2-3 biography textView more in another items(f.e. items No 7 and 12). What is wrong with my code? How to check it/identify why items textView are extra expand?
Can anyone help?
class ShowMastersAdapter (
private val masterList: ArrayList<Master>,
private val itemListener: OnItemClickListener
) : RecyclerView.Adapter<ShowMastersAdapter.ShowMasterViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ShowMasterViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_master, parent, false)
return ShowMasterViewHolder(view)
}
override fun onBindViewHolder(holder: ShowMasterViewHolder, position: Int) {
holder.bindData(position)
}
override fun getItemCount(): Int {
return masterList.size
}
inner class ShowMasterViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bindData(position: Int) {
val masterBiography = itemView.findViewById<TextView>(R.id.item_master_biography)
val masterExpandBiography =itemView.findViewById<TextView>(R.id.item_master_expand_text)
masterBiography.text = masterList[position].biography
masterExpandBiography.setOnClickListener {
itemListener.onReadMoreTextClick(masterExpandBiography, masterBiography)
}
}
}
interface OnItemClickListener {
fun onReadMoreTextClick(expandText: TextView, biographyText: TextView)
}
}
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/layout_top_part">
<TextView
android:id="#+id/item_master_biography"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/item_master_margin_start"
android:layout_marginEnd="#dimen/item_master_margin_end"
android:ellipsize="end"
android:maxLines="4"
android:text="unknown"
android:textSize="#dimen/font_main_size_item_master"
app:layout_constraintBottom_toTopOf="#+id/item_master_expand_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/item_master_biography_label" />
<TextView
android:id="#+id/item_master_expand_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="#string/it_master_expand_text"
android:textColor="#color/text_as_link"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
class FragmentShowMasters() : Fragment(), ShowMastersAdapter.OnItemClickListener{
private var listOfMasters: ArrayList<Master> = ArrayList()
private lateinit var myRecycler: RecyclerView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState:Bundle?): View {
myView = inflater.inflate(R.layout.fragment_show_masters, container, false)
myRecycler = myView.findViewById(R.id.recycler_show_masters)
myRecycler.setHasFixedSize(true)
myRecycler.adapter = ShowMastersAdapter(listOfMasters, this, highlightSearchText)
myRecycler.adapter?.notifyDataSetChanged()
myRecycler.layoutManager = LinearLayoutManager(this.context)
return myView
}
override fun onReadMoreTextClick(expandText: TextView, biographyText: TextView) {
if (biographyText.maxLines == 4) {
biographyText.maxLines = Int.MAX_VALUE
expandText.text = getString(R.string.it_master_shrink_text)
} else {
biographyText.maxLines = 4
expandText.text = getString(R.string.it_master_expand_text)
}
}
}
I am new to android studio and just starter 1 month ago for learning it.
I currently try to written the app that has list of data class with name, price, quantity and unit cost.
I have done the recycle view and i am facing the problem that how to update the editText value whenever the user input the quantity in the data classs
Below are my data class code
data class food (val food: String, val price: Int, var quantity: Int, var cost: Int = price*quantity)
object ABC {
var food_List = listOf<food>(
ABC ("A", 28 ,5) ,
ABC ("B", 28, 0)
)}
Below are my recycle view adaptor
class customAdaptor (val context: Context, val food_List:List<food>):RecyclerView.Adapter<customAdaptor.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(context).inflate(R.layout.list_layout,parent, false)
return ViewHolder(v)
}
override fun getItemCount(): Int {
return food_List.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val listused= food_List[position]
holder.setData(listused,position)
}
inner class ViewHolder (itemView: View): RecyclerView.ViewHolder(itemView) {
fun setData(listused: food?, pos: Int) {
itemView.listFoodName.text = listused!!.food
itemView.listUnitPrice.text =listused.price.toString()
itemView.listUnitTotalPrice.text=listused.cost.toString()
}
}}
Below are my mainactivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_page_budget)
val layoutManager = LinearLayoutManager(this)
layoutManager.orientation = LinearLayoutManager.VERTICAL
recyclerview.layoutManager = layoutManager
val adapter = customAdaptor(this, ABC.food_List)
recyclerview.adapter = adapter}
Below are my layout for card view.
<androidx.cardview.widget.CardView
android:id="#+id/cardView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="270dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:id="#+id/listFoodName"/>
<TextView
android:layout_width="50dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:id="#+id/listUnitPrice"/>
<EditText
android:layout_width="50dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:inputType="numberDecimal"
android:id="#+id/listQuantity"/>
<TextView
android:layout_width="50dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:id="#+id/listUnitTotalPrice"/> -->
</LinearLayout>
</androidx.cardview.widget.CardView>
Then i didnt know how to write the editText for the update.
Please help. Thank you very much.
You should use this:
editText.afterTextChanged{doSomethingWithText(it)}
Your code would be something like:
fun setData(listused: food?, pos: Int) {
itemView.listFoodName.text =listused!!.food
itemView.listUnitPrice.text =listused.price.toString()
itemView.listUnitTotalPrice.text=listused.cost.toString()
itemView.listQuantity.afterTextChanged {
listused?.quantity = it.toInt()
itemView.listUnitTotalPrice.text=listused.cost.toString()
}
}
I was trying to make a custom RecyclerView with Adapter which takes a HashMap as parameter, I succeeded but now only 1 item is showing in recyclerView list and I have no idea why.
Here my code snippet:
Custom RecyclerAdapter:
class ProductsRecyclerAdapter(private val dataSet: HashMap<Int, ProductEntry>) : RecyclerView.Adapter<ProductsRecyclerAdapter.ViewHolder>() {
class ViewHolder(val linearLay: LinearLayout) : RecyclerView.ViewHolder(linearLay)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) : ProductsRecyclerAdapter.ViewHolder {
val linearLay = LayoutInflater.from(parent.context).inflate(R.layout.test_text_view, parent, false) as LinearLayout
return ViewHolder(linearLay)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.linearLay.textView.text = dataSet[position]?.pName
holder.linearLay.textView2.text = dataSet[position]?.pExpiry
}
override fun getItemCount(): Int {
return dataSet.size
}
}
Initialization:
// Setup RecyclerView
val prod1 = ProductEntry("Milk", "04/06/1996")
val prod2 = ProductEntry("Bread", "04/01/2012")
val testArray : HashMap<Int, ProductEntry> = hashMapOf(1 to prod1, 2 to prod2)
viewManager = LinearLayoutManager(this)
viewAdapter = ProductsRecyclerAdapter(testArray)
recyclerView = findViewById<RecyclerView>(R.id.productsRecyclerView).apply {
setHasFixedSize(true)
layoutManager = viewManager
adapter = viewAdapter
}
And finally, custom XML for row:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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/linearLay"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="40dp"
android:layout_weight="1"
android:text="TextView" />
Despite everything seems to be OK, I get only 1 row showing "milk".
Thanks for all help!
position in override fun onBindViewHolder(holder: ViewHolder, position: Int) starts at index 0.
So in your case, it will be [0, 1] and your map keys are [1, 2]. Only key "1" will be displayed correctly.
try:
val testArray : HashMap<Int, ProductEntry> = hashMapOf(0 to prod1, 1 to prod2)
But no need to use a map here! Just use a simple list of ProductEntry.