class Split_recycler_adapter (var arrayList: ArrayList<Split_recycler_model>) :
RecyclerView.Adapter<Split_recycler_adapter.Viewholder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Viewholder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.changesplit_recycler_item,parent,false)
return Viewholder(v)
}
override fun onBindViewHolder(holder: Viewholder, position: Int) {
val split_modal = arrayList[position]
var checked = true
Log.e("position_outside_loop->","$position")
holder.split_text.text = split_modal.getText()
holder.split_text.setOnClickListener{
if(checked){
Log.e("check->","Working")
holder.split_text.setBackgroundResource(R.drawable.green_button_gradient)
holder.split_text.setTextColor(Color.parseColor("#454546"))
checked = false
}else{
holder.split_text.setBackgroundResource(R.drawable.dropdown_gradient)
holder.split_text.setTextColor(Color.parseColor("#A4A4A4"))
checked = true
}
}
}
override fun getItemCount(): Int {
return arrayList.size
}
class Viewholder(Itemview: View) : RecyclerView.ViewHolder(Itemview) {
val split_text : TextView = Itemview.findViewById(R.id.split_text)
}
}
This code helps me to change the background of the button when clicked and revert the background when one more click is done but this is not the exact thing i want , I need to toggle the background on click, when one click is done it should want to remove the background of the other button , only one button should be highlighted at a time , Please help me if any one know the solution
You can't do it this way.
You have added a click listener in a binder and performed checked logic for that item mainly situated at that holder position. It won't affect other items at other positions. So to update other positions' state, you have to manage the state for each item by adding isChecked variable in Split_recycler_model class.
class Split_recycler_model {
var isChecked : Boolean = false
}
And in onClick of view, update your list item for that particular adapterPosition.
Updated click action,
holder.split_text.setOnClickListener{
checkItem(adapterPosition)
}
fun checkItem(adapterPosition:Int) {
arrayList.forEachIndexed{index,item ->
item.apply{
isChecked = (index == adapterPosition)
}
}
/**
Here you can manually calculate the changes
to update adapter to improve performance.
For now, I'm updating it with the `notifyDataSetChanged` method.
*/
notifyDataSetChanged()
}
Related
I have a recyclerview with a list of items , when i check an item and scroll until the checked checkbox disppear , and scroll again to it , the checkbox become unchecked
this is my adapter code
class ContactsAdapter(var list:ArrayList<contact>, private val listener: (contact) -> Unit):RecyclerView.Adapter<ContactsAdapter.viewHolder>() {
inner class viewHolder(view:View):RecyclerView.ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): viewHolder {
var view = LayoutInflater.from(parent.context).inflate(R.layout.item_ui, parent, false)
return viewHolder(view)
}
override fun onBindViewHolder(holder: viewHolder, position: Int) {
var itemPosition = list[position]
holder.itemView.apply {
contact_name.text=itemPosition.contactName
contact_number.text=itemPosition.contactNumber[0]
check_number.setOnClickListener {
listener(itemPosition)
}
check_number.isChecked = itemPosition.checked
}
}
override fun getItemCount(): Int {
return list.size
}
}
A RecyclerView recycles its rows. Your setOnClickListener() needs to do something to hold onto which items are checked and unchecked, and your onBindViewHolder() needs to update the CheckBox when it binds an item. It seems like the second part is implemented, but perhaps not the first part. You may need to update itemPosition.checked in your lambda for setOnClickListener().
I have a RecyclerView which has an EditText in its items.
For each EditText I have added a textChangeListener.
Whenever the user types a character I want to do an input validation and so I can change the item, for example show red border in case of wrong input etc...
After I do the validation I call:
adapter.notifyItemChanged(itemPosition)
to update the view.
But the problem is that the focus is lost from the EditText and I have to click again inside the box in order to continue typing.
Is there a solution for this case? How can I continue typing while the view is updated after calling notifyItemChanged ?
ADAPTER
class ItemAdapter() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = LayoutItemBinding.inflate(inflater, parent, false)
binding.editText.addTextChangedListener {
// Do layout changes....
if (isInvalidInput) {
item.setError = true
....
...
}
adapter.notifyItemChanged(itemPosition)
}
return ItemViewHolder(binding)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder.bind(items[position])
}
inner class ItemViewHolder(val binding: LayoutItemBinding)
: RecyclerView.ViewHolder(binding.root) {
fun bind(model: ItemModel) {
// Setup view for item
}
}
}
Try to move your error setting code in the bind function of the ViewHolder and update the error status outside of the TextWatcher:
class ItemAdapter() : RecyclerView.Adapter<RecyclerView.ViewHolder>()
{
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = LayoutItemBinding.inflate(inflater, parent, false)
return ItemViewHolder(binding)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder.bind(items[position], position)
}
inner class ItemViewHolder(val binding: LayoutItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(model: ItemModel, position: Int) {
binding.editText.addTextChangedListener {
// Update the ItemModel on text change
adapter.notifyItemChanged(position)
}
// Update the view error status everytime it is updated
val isInvalidInput = ... // Add your logic
if (isInvalidInput) {
model.setError = true
....
....
}
}
}
}
First, Please post your code.
If you called notifyDatasetChanged(), all views of recyclerview will refreshed (it means that the views could be recreated.).
So if you want remain focus in edittext, you not call notifyDatasetChanged() or set focus to your edittext after notifyDataChanged().
But setFocus is bit more difficult.
So I suggest method that update views of recyclerview without call notifyDatasetChanged().
I think you can directly update view of recyclerview without call notifyDataChanged().
You should save the id of item that contains the EditText that is editing. And in the bind() method of ViewHolder, you will check if the item is currently being edit, you will call requestFocus() method on EditText.
And please take note that when you call notifyDataChanged(), the RecycleView maybe check the data of each item to know whether it was changed or not. If the data of an item was changed, RecycleView will redraw that item.
Actually I am using recycler view and adding a layout in the rows and I am using flip animation on cardviews(when clicked on it). The problem is when I add multiple items in the recycler the flip animation works only with the first item. I used toast to make sure that click function is working with other items or not, turns out it's working but flip animation is not working with any other items.Can any one help me out here
This is my code
override fun onCardClick(item: PacketModel, position: Int) {
val scale = this.resources.displayMetrics.density
frontCard.cameraDistance= 8000 * scale
backCard.cameraDistance = 8000 * scale
front_anim = AnimatorInflater.loadAnimator(context, R.animator.front_animator) as AnimatorSet
back_anim = AnimatorInflater.loadAnimator(context, R.animator.back_animator) as AnimatorSet
if (isFront){
front_anim.setTarget(frontCard)
back_anim.setTarget(backCard)
front_anim.start()
back_anim.start()
isFront = false
}else
{
front_anim.setTarget(backCard)
back_anim.setTarget(frontCard)
back_anim.start()
front_anim.start()
isFront = true
}
Toast.makeText(context, item.Name , Toast.LENGTH_SHORT).show()
}
}
This is the adapter Class
class PacketAdapter (val packetList: ArrayList<PacketModel> , var clickListener2: onPacketItemClickListener): RecyclerView.Adapter<PacketAdapter.ViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val a = LayoutInflater.from(parent?.context).inflate(R.layout.packet, parent, false)
return ViewHolder(a)
}
override fun getItemCount(): Int {
return packetList.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val packet : PacketModel = packetList[position]
holder.intialize(packet, clickListener2)
}
class ViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView)
{
val packetTime = itemView.findViewById<TextView>(R.id.packetTime)
val timeMessage = itemView.findViewById<TextView>(R.id.timeMessage)
fun intialize(item: PacketModel, action: onPacketItemClickListener){
packetTime.text = item.Name
timeMessage.text = item.Age
itemView.setOnClickListener {
action.onCardClick(item, adapterPosition)
}
}
}
interface onPacketItemClickListener{
fun onCardClick (item: PacketModel, position: Int)
}
}
You should place your card flipping code inside your recyclerview adapter so that recyclerview can recycle it as it should be. You can place your card flipping code inside itemview onClicklistener:
itemView.setOnClickListener {
// Place your flipping code here
action.onCardClick(item, adapterPosition)
}
Remove flipping code from onCardClick callback. Let me know if it works fine.
There is a Recyclerview in my activity. it has always 10 items. Recyclerview's item has two buttons witch each one's click listener is defined in onBindViewHolder method of adapter class. after clicking any of the items's buttons it calls a callback method witch is implemented in activity's onCreate method. The callback method passes adapterPosition to activity an activity returns a long value witch I update button's text with that.
The problem is when I click the first item's button of the recyclerview, it updates the first and the last item's text. First I used position but then changed it to adapterPosition or layoutPosition, but it didn't worked. sometime the one before last item gets updated and sometimes the last one gets updated with the updating of the first item
activity's onCreate code
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_videocrop)
var segmentAdapter = SegmentAdapter(10)
var layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
videoCropRecyclerView.adapter = segmentAdapter
videoCropRecyclerView.layoutManager = layoutManager
segmentAdapter.onSegmentSelectedListener = object : OnSegmentSelectedListener {
override fun onSelectFrom(index: Int): Int? {
return 12345
}
override fun onSelectTo(index: Int): Int? {
return 54321
}
}
}
Adapter class code
class SegmentAdapter(var segments: Int) : RecyclerView.Adapter<SegmentAdapter.SegmentViewHolder>() {
var onSegmentSelectedListener: OnSegmentSelectedListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SegmentViewHolder =
SegmentViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.videocrop_segment_item, parent, false))
override fun getItemCount(): Int = segments
override fun onBindViewHolder(holder: SegmentViewHolder, position: Int) {
holder.from!!.setOnClickListener {
holder.from!!.text = onSegmentSelectedListener!!.onSelectFrom(holder.adapterPosition)
}
holder.to!!.setOnClickListener {
holder.to!!.text = onSegmentSelectedListener!!.onSelectTo(holder.adapterPosition)
}
}
class SegmentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var from: Button? = null
var to: Button? = null
init {
from = itemView.findViewById(R.id.videoCropSegmentItemFromButton)
to = itemView.findViewById(R.id.videoCropSegmentItemToButton)
}
}
}
Callback Interface Code
interface OnSegmentSelectedListener {
fun onSelectFrom(index:Int):Int?
fun onSelectTo(index:Int):Int?
}
when I click the first item's button and its text becomes "0"
then I scroll down to the last item and I see the last item is also updated
I also checked this question but it wasn't helpful.
I would be very thankful if somebody helps me
Your onBindViewHolder method is not handling recycled views. You need to set the text every time in onBindViewHolder based on the backing data, rather than just setting it in the click listener, because you may get an itemView re-used from a different item (in this case, the first item where you've changed the text).
Hi I have a condition where I have to add different viewType with text
show more or loading...
at the bottom of the recyclerview
I have successfully added the footer view with show more and also I have added the listener on this footer item view for loading more data, here everything works fine. Since I have swipe refresh layout as a parent for recyclerview I show enable swiperefresh progress on loading and disable swiperefresh progress while loading is complete. Now what I need is whenever I click on the show more view type which is a footer attached to the recycler view I want the text to be changed with loading... and when the loading is completed the text should be changed to show more again.
Here is my adapter class
class MyTransactionAdapter(private val context: Context?,
private val transactionList: List<TransactionHistoryResponse>,
private val transactionListener: MyTransactionListener) : Filterable,
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var viewTypeList: Int = 0
private var viewTypeFooter: Int = 1
private var transactionListFiltered = transactionList
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == viewTypeList) {
val view: View = LayoutInflater.from(context).inflate(R.layout.row_my_transaction, parent, false)
ListViewHolder(view)
} else {
val view: View = LayoutInflater.from(context).inflate(R.layout.row_footer_so_more, parent, false)
FooterViewHolder(view)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val myTransaction = transactionListFiltered[position]
if (holder is ListViewHolder) {
holder.bind(myTransaction, transactionListener)
}
}
override fun getItemCount(): Int {
return transactionListFiltered.size
}
override fun getItemViewType(position: Int): Int {
return if (position == transactionList.lastIndex) {
viewTypeFooter
} else {
viewTypeList
}
}
inner class FooterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
init {
itemView.apply {
btnShowMore.setOnClickListener {
transactionListener.onLoadMoreClicked()
}
}
}
fun enableShowMore(enable: Boolean) {
if(enable){
itemView.btnShowMore.text = "show more"
}else{
itemView.btnShowMore.text = "loading..."
}
}
}
inner class ListViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private lateinit var mTransactionStatus: TransactionStatus
fun bind(myTransaction: TransactionHistoryResponse, transactionListener: MyTransactionListener){
//logic to populate data on views
}
}
}
And here is the fragment's function where I do the loading stuff
This function is being called for fetching data from remote
private fun getTransactionHistory() {
swipeContainer.enableDisableSwipeRefresh(true)
enableShowMore(false)
//logic to load web data and hide show swiperefresh
//when load is success call this function enableShowMore(true)
//when load is failed call this function enableShowMore(false)
}
#Synchronized
private fun enableShowMore(enable: Boolean) {
val viewHolder = recyclerView.findViewHolderForAdapterPosition(transactionList.size)
if (viewHolder is MyTransactionAdapter.FooterViewHolder) {
viewHolder.enableShowMore(enable)
}
}
//this is the implemented method when footer view is clicked
override fun onLoadMoreClicked() {
fetchTransactionListFromServer()
}
Is there anything I need to do with this approach because users are able to click the footer multiple at a time without letting the app to load the remote data.
Hope you have understood the situation if not let me know. Thanks in advance
Here is the screenshot what I have achieved
Can you try disabling the footer view button once the user clicks on it?
fun enableShowMore(enable: Boolean) {
if( enable ) {
itemView.btnShowMore.text = "show more"
} else {
itemView.btnShowMore.text = "loading..."
}
itemView.btnShowMore.isEnabled = enable
}
Update :
//In the adapter
mIsFooterEnabled = false
//In the View holder
fun enableShowMore() {
if( mIsFooterEnabled ) {
itemView.btnShowMore.text = "show more"
} else {
itemView.btnShowMore.text = "loading..."
}
itemView.btnShowMore.isEnabled = mIsFooterEnabled
}
//In your activity / fragment
private fun enableShowMore(enable: Boolean) {
adapter.mIsFooterEnabled = enable
adapter.notifyDataSetChanged()
}
Could you please try to put your function code inside enableShowMore() in onBindViewHolder() or set holder.bind for FooterViewHolder and put code inside binder function.
Let me know if it is useful or not.