Here I am not getting my current clicked item position. It some time shows incorrect position and some time make crash.
My Adapter Code is as below.
Some time I clicked in 1st position it will work fine. But I clicked again at any position from the item it shows it's previous item.
Thanks in advance for your answer or suggestions.
class MyAdapter(private val mList: ArrayList<MyModel>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
private lateinit var mListener: OnItemClickListener
interface OnItemClickListener : AdapterView.OnItemClickListener {
fun onItemClick(position: Int)
}
fun setOnItemClickListener(listener: OnItemClickListener) {
mListener = mListener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_all_brands, parent, false)
return ViewHolder(view, mListener)
}
#SuppressLint("SetTextI18n")
#RequiresApi(Build.VERSION_CODES.N)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.ivBrandImage.setImageResource(mList[position].image)
holder.tvBrandName.text = mList[position].name
holder.tvBrandName.isSelected = true
holder.tvBrandName.setSingleLine()
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemCount(): Int {
return mList.size
}
class ViewHolder(ItemView: View, listener: OnItemClickListener) :
RecyclerView.ViewHolder(ItemView) {
val ivBrandImage: ImageView = itemView.findViewById(R.id.ivBrandImage)
val tvBrandName: TextView = itemView.findViewById(R.id.tvBrandName)
init {
itemView.setOnClickListener {
listener.onItemClick(adapterPosition)
}
}
}
}
There is one mistake you do in the setOnItemClickListener Method.
Remove :
fun setOnItemClickListener(listener: OnItemClickListener) {
mListener = mListener
}
Replace :
fun setOnItemClickListener(listener: OnItemClickListener) {
mListener = listener
}
So your total code should look like
class MyAdapter(private val mList: ArrayList<MyModel>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
private lateinit var mListener: OnItemClickListener
interface OnItemClickListener : AdapterView.OnItemClickListener {
fun onItemClick(position: Int)
}
fun setOnItemClickListener(listener: OnItemClickListener) {
mListener = listener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_all_brands, parent, false)
return ViewHolder(view, mListener)
}
#SuppressLint("SetTextI18n")
#RequiresApi(Build.VERSION_CODES.N)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.ivBrandImage.setImageResource(mList[position].image)
holder.tvBrandName.text = mList[position].name
holder.tvBrandName.isSelected = true
holder.tvBrandName.setSingleLine()
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemCount(): Int {
return mList.size
}
class ViewHolder(ItemView: View, listener: OnItemClickListener) :
RecyclerView.ViewHolder(ItemView) {
val ivBrandImage: ImageView = itemView.findViewById(R.id.ivBrandImage)
val tvBrandName: TextView = itemView.findViewById(R.id.tvBrandName)
init {
itemView.setOnClickListener {
listener.onItemClick(adapterPosition)
}
}
}
}
Related
I am working a RecyclerView item that has two views in it: a TextView and an ImagaView. I want to be able to click on both of them to cary out different functionalities.
How can I go about this
Here is my Adapter
class AddMeasurementAdapter(private val currentList:MutableList<DressMeasurementModel>, private val listener1: RecyclerClickListener, private val listener2: RecyclerClickListener): RecyclerView.Adapter<AddMeasurementAdapter.CardViewHolder>() {
//inner class
inner class CardViewHolder (itemView : View):RecyclerView.ViewHolder(itemView), View.OnClickListener{
val display:TextView = itemView.findViewById(R.id.measurement_recyclerview_item)
//Binding the data with the view
fun bind(dressMeasurementModel: DressMeasurementModel){
display.text = "${dressMeasurementModel.measurementName} ${dressMeasurementModel.measurement}"
}
init {
itemView.setOnClickListener(this#CardViewHolder)
}
override fun onClick(v: View?) {
val position: Int = adapterPosition
if (position != RecyclerView.NO_POSITION) {
listener1.onItemClick1(position, currentList)
}
if (position != RecyclerView.NO_POSITION) {
listener2.onItemClick2(position, currentList)
}
}
}
//Creating view
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.measurement_fragment_recyclerview_items, parent, false)
return CardViewHolder(view)
}
//Binding the view
override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
holder.bind(currentList[position])
}
//Getting the item cout size
override fun getItemCount(): Int {
return currentList.size
}
}
Here is my interface
interface RecyclerClickListener {
fun onItemClick1(position: Int, currentList: MutableList<DressMeasurementModel>)
fun onItemClick2(position: Int, currentList: MutableList<DressMeasurementModel>)
}
You can add a listener in createViewHolder such as
//Creating view
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.measurement_fragment_recyclerview_items, parent, false)
view.textView.onClick{}
view.image.onClick{}
return CardViewHolder(view)
}
Okay, here is the Kotlin version of the answer.
In your bind method of viewholder,
fun bind(dressMeasurementModel: DressMeasurementModel) {
...
var position = adapterPosition
display.setOnClickListener {
listener.onTextClick(position)
}
image.setOnClickListener {
listener.onImageClick(position)
}
...
}
I later figured it So i want to share how i did it.
class AddMeasurementAdapter(
private val currentList: MutableList<DressMeasurementModel>,
private val listener1: RecyclerClickListener,
private val listener2: RecyclerClickListener
) : RecyclerView.Adapter<AddMeasurementAdapter.CardViewHolder>() {
// inner class
inner class CardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val display: TextView = itemView.findViewById(R.id.measurement_recyclerview_item)
val delete: ImageView = itemView.findViewById(R.id.measurementment_recyclerview_item_delete_button)
// Binding the data with the view
fun bind(dressMeasurementModel: DressMeasurementModel) {
display.text = "${dressMeasurementModel.measurementName} ${dressMeasurementModel.measurement}"
}
}
// Creating view
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.measurement_fragment_recyclerview_items,
parent,
false
)
return CardViewHolder(view)
}
// Binding the view and attaching the listener
override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
holder.bind(currentList[position])
holder.display.setOnClickListener {
listener1.onItemClickToEdit(holder.adapterPosition, currentList)
}
holder.delete.setOnClickListener {
listener2.onItemClickToDelete(holder.adapterPosition, currentList)
}
}
// Getting the item cout size
override fun getItemCount(): Int {
return currentList.size
}
}
When i assigned the onClickListener inside the fun bind(), the listener covered the the whole itemView layout and not the individual view inside the layout, hence only one click listener was achieved. The same thing applied inside the inner class.
Here is the interface
interface RecyclerClickListener {
fun onItemClickToEdit(position: Int, currentList: MutableList<DressMeasurementModel>)
fun onItemClickToDelete(position: Int, currentList: MutableList<DressMeasurementModel>)
}
By extending the interface in either activity of fragment, you have your onclickListener.
This article helped https://antonioleiva.com/recyclerview-listener/
I am new to Android & Kotlin. I want to click my row and intent it to other activity. That's why I tried to create an interface in my Adapter but it does not work. How can I create it?
My adapter code:
class NoteAdapter(private val titleTextArray: ArrayList<String>, private val imageArray: ArrayList<String>) : RecyclerView.Adapter<NoteAdapter.NoteHolder>() {
class NoteHolder (view: View) : RecyclerView.ViewHolder(view) {
var recyclerTitleText: TextView ?= null
var recyclerImageView: ImageView ?= null
init {
recyclerTitleText = view.findViewById(R.id.recyclerTitleText)
recyclerImageView = view.findViewById(R.id.recyclerImage)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.recycler_row, parent, false)
return NoteHolder(view)
}
override fun onBindViewHolder(holder: NoteHolder, position: Int) {
holder.recyclerTitleText?.text = titleTextArray[position]
Picasso.get().load(imageArray[position]).into(holder.recyclerImageView)
}
override fun getItemCount(): Int {
return titleTextArray.size
}
}
With Kotlin, you could to use a lambda (higher order function) to fetch the user clicks in the holder. For example, a base approach could be the next:
class YourAdapter(
private val listener: (String) -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val yourDataList = emptyList<String>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.compound_checkbox_terms_layout, parent, false)
return YourViewHolder(view)
}
override fun getItemCount(): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
private inner class YourViewHolder(view: View) : RecyclerView.ViewHolder(view) {
init {
if (adapterPosition != RecyclerView.NO_POSITION) {
val selectedData = yourDataList[adapterPosition]
listener.invoke(selectedData)
}
}
}
}
Pass the activity context to the adapter from Activity so it can launch intent
Activity Code :
mAdapter = new CustomAdapter(this, imageArray, ArrayList);
Adapter Code :
class NoteAdapter(private val titleTextArray: ArrayList, private val imageArray: ArrayList, private val context : Context) : RecyclerView.Adapter<NoteAdapter.NoteHolder>() {
class NoteHolder (view: View) : RecyclerView.ViewHolder(view) {
var recyclerTitleText: TextView?= null
var recyclerImageView : ImageView?= null
init {
recyclerTitleText = view.findViewById(R.id.recyclerTitleText)
recyclerImageView = view.findViewById(R.id.recyclerImage)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.recycler_row, parent, false)
return NoteHolder(view)
}
override fun onBindViewHolder(holder: NoteHolder, position: Int) {
holder.recyclerTitleText?.text = titleTextArray[position]
Picasso.get().load(imageArray[position]).into(holder.recyclerImageView)
holder.recyclerImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(context, NesneTani.class);
context.startActivity(intent);
}
});
}
override fun getItemCount(): Int {
return titleTextArray.size
}
}
Create an Interface to catch the click event. (position is optional)
interface OnItemClickListener {
fun onItemClick(position: Int)
}
add OnItemClickListener as a parameter in your Adapter class
class NoteAdapter(
private val titleTextArray: ArrayList<String>,
private val imageArray: ArrayList<String>,
private val listener: OnItemClickListener
) : RecyclerView.Adapter<NoteAdapter.NoteHolder>()
Then do below inside onBindViewHolder
override fun onBindViewHolder(holder: NoteHolder, position: Int) {
listener.onItemClick(position)
}
now inside your activity, implement the OnItemClickListener
class ActivityName implements OnItemClickListener{}
and while initiating adapter, do this
mAdapter = new CustomAdapter(this, imageArray, ArrayList);
Then inside the overridden method do what you want
override fun onItemClick(position: Int) {
//start the activity you want.
}
I am trying to implement the function onClick in the CustomAdapter into a button so that I can call an action. I want to make it so that when the user clicks on the recycler item, the onClick function calls openTopSheet() that is from MainActivity which brings down the top sheet. In essence, how can I make it so that onClick can perform a method from MainActivity? Any help would be appreciated
CustomAdapter.kt
class CustomAdapter(val modelList: List<Model>, val context: Context) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder as ViewHolder).bind(modelList.get(position));
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
return ViewHolder(layoutInflater.inflate(R.layout.row_item, parent, false))
}
override fun getItemCount(): Int {
return modelList.size;
}
lateinit var mClickListener: ClickListener
fun setOnItemClickListener(aClickListener: ClickListener) {
mClickListener = aClickListener
}
interface ClickListener {
fun onClick(pos: Int, aView: () -> Unit)
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
var r = MainActivity()
init {
itemView.setOnClickListener(this)
}
override fun onClick(p0: View?) {
r.openTopSheet()
}
fun bind(model: Model): Unit {
itemView.txt.text = model.name
itemView.sub_txt.text = model.version
val id = context.resources.getIdentifier(model.name.toLowerCase(Locale.ROOT), "drawable", context.packageName)
itemView.img.setBackgroundResource(id)
}
}
}
MainActivity.kt
open class MainActivity : AppCompatActivity(), BottomSheetRecyclerViewAdapter.ListTappedListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.home_page)
}
fun openTopSheet() {
topSheetBehavior.state = TopSheetBehavior.STATE_EXPANDED
topSheetBehavior.setTopSheetCallback(object : TopSheetBehavior.TopSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float, isOpening: Boolean?) {
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
}
})
}
}
In ViewHolder:
bind(model: Model, openTopSheet: ()->Unit)
itemView.button.setOnClickListener{
openTopSheet.invoke()
}
In adapter:
class CustomAdapter(
val modelList: List<Model>,
val context: Context,
val openTopSheet: ()->Unit)
)
(holder as ViewHolder).bind(modelList.get(position), openTopSheet);
In Activity:
val adapter = CustomAdapter(listOf(), this, { openTopSheet() } )
And you need to add openTopSheet method in activity.
My list is about questions and answer.
I've designed an item that has the question and answer and I'm planing to hide and show the answer, not the whole item (GONE/VISIBLE), when the user click on the question. So it will be more like expandable way.
Now this is my adapter:
class FaqExpandableAdapter internal constructor(private val context: Context, private var items: List<Faq>,private val callBack: AdapterCallBack?) : RecyclerView.Adapter<FaqExpandableAdapter.MyViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = inflater.inflate(R.layout.item_question_faq, parent, false)
return MyViewHolder(view)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.txtQuestionFaq.text = items[position].question?.toString() ?: "-"
holder.txtAnswerFaq.text = items[position].answer?.toString() ?: "-"
}
override fun getItemCount(): Int {
return items.size
}
fun update(items: List<Faq>) {
this.items = items
notifyDataSetChanged()
}
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
#BindView(R.id.txtQuestionFaq)
lateinit var txtQuestionFaq: AppCompatTextView
#BindView(R.id.txtAnswerFaq)
lateinit var txtAnswerFaq: AppCompatTextView
init {
ButterKnife.bind(this, itemView)
}
#OnClick(R.id.relativeRowFaq)
fun onItemClicked() {
callBack?.onItemClicked(adapterPosition)
}
}
interface AdapterCallBack {
fun onItemClicked(position: Int)
}
}
My last step is where to do the visibility action?
Kotlin : Write onClick method on your onBindViewHolder function.
holder.txtQuestionFaq.setOnClickListener {
holder.txtAnswerFaq.visibility = View.VISIBLE;
}
In onBindViewHolder() method, add below lines: (I use Java)
holder.txtQuestionFaq.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.txtAnswerFaq.setVisibility(View.VISIBLE);
}
});
How can I create on click event using interface?
In my application I've created view click interface to detect clicking on adapter items into parent activity. After creating interface and method into adapter how I can use this interface to call the view listener ?
Please check this code, It's working fine for me.
First Create Adapter class.
class ChapterAdapter(private val activity: Activity, val mWords: ArrayList<Chapter>, val btnlistener: BtnClickListener) : RecyclerView.Adapter<ChapterAdapter.ViewHolder>() {
companion object {
var mClickListener: BtnClickListener? = null
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
return ViewHolder(layoutInflater.inflate(R.layout.layout_capter_raw, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
mClickListener = btnlistener
val item = mWords[position]
holder.layout_chapter_name.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
if (mClickListener != null)
mClickListener?.onBtnClick(position)
}
})
}
override fun getItemCount(): Int {
return mWords.size
}
override fun getItemId(position: Int): Long {
return super.getItemId(position)
}
override fun getItemViewType(position: Int): Int {
return super.getItemViewType(position)
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val txt_capter_name = view.txt_capter_name
}
open interface BtnClickListener {
fun onBtnClick(position: Int)
}
}
After create and declare adapter in your Activity or Fragment.
listAdapter = ChapterAdapter(activity, _arrChapterList, object : ChapterAdapter.BtnClickListener {
override fun onBtnClick(position: Int, chapter_id: String, chapter_size: String, chapter_name: String) {
toast(chapter_id + " = " + chapter_size, Toast.LENGTH_LONG)
}
})
In Kotlin the proper way doing this, is using callbacks instead of Java Interfaces. Example:
class MyAdapter(private val callback: (YourModel) -> Unit) {
override fun onBindViewHolder(holder: DataBoundViewHolder<YourModel>, position: Int) {
bind(holder.binding, items!![position])
holder.binding.executePendingBindings()
holder.binding.root.setOnClickListener { callback(binding.model) }
}
}
And create the adapter somewhere using
MyAdapter myAdapter = MyAdapter( { println{"Clicked $it"} })
Edit: Since the Asker would like to see a full working code i used the code from Sumit and replaced the Interfaces with Kotlin-Callbacks.
class ChapterAdapter(private val activity: Activity,
val mWords: ArrayList<Chapter>,
val callback: (Any) -> Unit) :
RecyclerView.Adapter<ChapterAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
return ViewHolder(layoutInflater.inflate(R.layout.layout_capter_raw, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
val item = mWords[position]
holder.layout_chapter_name.setOnClickListener( callback {$it})
}
override fun getItemCount(): Int = mWords.size
override fun getItemId(position: Int): Long = super.getItemId(position)
override fun getItemViewType(position: Int): Int = super.getItemViewType(position)
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val txt_capter_name = view.txt_capter_name
}
and finally creating the Adapter
listAdapter = ChapterAdapter(activity, _arrChapterList, {
toast( "Clicked $it", Toast.LENGTH_LONG)
})
I have the same problem, and here is my solution:
package adapter
class MarketplaceListAdapter : RecyclerView.Adapter<MarketplaceListAdapter.ViewHolder> {
private val marketplaceList: ArrayList<Marketplace>
constructor(marketplaceList: ArrayList<Marketplace>) : super() {
this.marketplaceList = marketplaceList
}
private var listener: OnItemClickListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
// implementation
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
// implementation
}
override fun getItemId(position: Int): Long {
return 0
}
override fun getItemCount(): Int {
return marketplaceList.size
}
fun setListener(listener: OnItemClickListener) {
this.listener = listener
}
interface OnItemClickListener {
fun onItemClick(marketplace: Marketplace)
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
View.OnClickListener {
var tvTitle: TextView = itemView.findViewById(R.id.tvTitle)
var ivIcon: ImageView = itemView.findViewById(R.id.ivIcon)
init {
itemView.setOnClickListener(this)
}
override fun onClick(v: View) {
listener?.onItemClick(marketplaceList[adapterPosition])
}
}
}
And on my fragment:
val list = view.findViewById<RecyclerView>(R.id.list)
list?.let {
adapter?.let { adapter ->
list.adapter = adapter
adapter.setListener(object : MarketplaceListAdapter.OnItemClickListener {
override fun onItemClick(marketplace: Marketplace) {
onMarketplaceSelected(marketplace)
}
})
}
}