Detecting click for Recyclerview inside RecyclerView - android

I have implemented a recyclerview within recyclerview. Here is my code
activity_main
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:layout_width="0dp"
android:layout_height="0dp"
android:id="#+id/rv_activity_main"
app:layoutManager="android.support.v7.widget.GridLayoutManager"
app:spanCount="2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
MainActivity
class MainActivity : AppCompatActivity() {
private var graduationList = ArrayList<Graduation>()
private var yearList = ArrayList<YearList>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initList()
rv_activity_main.adapter = MainAdapter(graduationList)
}
private fun initList(){
graduationList.clear()
yearList.clear()
yearList.add(YearList("2011"))
yearList.add(YearList("2012"))
yearList.add(YearList("2013"))
yearList.add(YearList("2014"))
yearList.add(YearList("2015"))
yearList.add(YearList("2016"))
yearList.add(YearList("2017"))
yearList.add(YearList("2018"))
yearList.add(YearList("2019"))
graduationList.add(Graduation("UG",yearList))
graduationList.add(Graduation("PG",yearList))
}
}
Graduation Pojo Class
data class Graduation(
var name: String,
var yearList: ArrayList<YearList>
)
YearList Pojo Class
data class YearList(var yearName: String)
MainAdapter
class MainAdapter(private var graduationList: ArrayList<Graduation>): RecyclerView.Adapter<MainAdapter.MainViewHolder>(){
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): MainViewHolder {
val itemView = LayoutInflater.from(p0.context).inflate(R.layout.cell_main_adapter,p0,false)
return MainViewHolder(itemView)
}
override fun getItemCount(): Int {
return graduationList.size
}
override fun onBindViewHolder(p0: MainViewHolder, p1: Int) {
p0.graduationTextView.text = graduationList[p1].name
p0.graduationRecyclerView.adapter = SecondAdapter(graduationList[p1].yearList)
}
inner class MainViewHolder(view: View): RecyclerView.ViewHolder(view){
var graduationTextView: TextView = view.findViewById(R.id.tv_cell_main_adapter)
var graduationRecyclerView: RecyclerView = view.findViewById(R.id.rv_cell_main_adapter)
}
}
SecondAdapter
class SecondAdapter(private var yearList: ArrayList<YearList>): RecyclerView.Adapter<SecondAdapter.SecondViewHolder>(){
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): SecondViewHolder {
val itemView = LayoutInflater.from(p0.context).inflate(R.layout.cell_second_adapter,p0,false)
return SecondViewHolder(itemView)
}
override fun getItemCount(): Int {
return yearList.size
}
override fun onBindViewHolder(p0: SecondViewHolder, p1: Int) {
p0.yearTextView.text = yearList[p1].yearName
p0.itemView.setOnClickListener {
Log.i("Pritish",yearList[p1].yearName+"\n")
}
}
inner class SecondViewHolder(view: View): RecyclerView.ViewHolder(view){
var yearTextView: TextView = view.findViewById(R.id.tv_cell_year_title)
}
}
ItemClick for MainAdapter should not be clickable so I have not added any itemClick for it. Suppose user clicks a particular year I know which year the user selected but my question is, how to know when a year is clicked whether it's from UG or PG without implementing a itemclick listener for MainAdapter?

Try this way,
Pass Graduation object in SecondAdapter constuctor from MainAdapter like below.
p0.graduationRecyclerView.adapter = SecondAdapter(graduationList[p1])
Second Adapter
class SecondAdapter(private var graduation: Graduation): RecyclerView.Adapter<SecondAdapter.SecondViewHolder>(){
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): SecondViewHolder {
val itemView = LayoutInflater.from(p0.context).inflate(R.layout.cell_second_adapter,p0,false)
return SecondViewHolder(itemView)
}
override fun getItemCount(): Int {
return graduation.yearList.size
}
override fun onBindViewHolder(p0: SecondViewHolder, p1: Int) {
p0.yearTextView.text = graduation.yearList[p1].yearName
p0.itemView.setOnClickListener {
Log.i("Pritish",graduation.yearList[p1].yearName+" "+graduation.name+"\n")
}
}
inner class SecondViewHolder(view: View): RecyclerView.ViewHolder(view){
var yearTextView: TextView = view.findViewById(R.id.tv_cell_year_title)
}
}

Related

Get multiple / dynamic EditText value in RecyclerView

I'm new with these case and I want to try to get the value of dynamic EditText(s) from RecyclerView.
And in the layout :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_marginTop="#dimen/_10sdp"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_question"
style="#style/textH4"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="bottom"
android:text="Ini adalah tempat pertanyaan pertama" />
<androidx.appcompat.widget.AppCompatEditText
android:id="#+id/et_answer"
android:layout_width="match_parent"
android:layout_height="#dimen/_120sdp"
android:hint="Tulis jawaban anda"
style="#style/EdittextPrimary"
android:gravity="top"
android:background="#drawable/bg_outline_grey"
android:inputType="textMultiLine"
android:layout_marginTop="#dimen/_10sdp"/>
</LinearLayout>
The question is.. how do I get the EditText value and put it in an ArrayList ?
Newest update codes :
I add my Recycler Adapter, and tried these :
class RequestJoinAdapter(
val onTextChanged: (text:String,position:Int)->Unit
) :
RecyclerView.Adapter<RequestJoinAdapter.ViewHolder>() {
var listData: MutableList<KeamananModel> = ArrayList()
private var textValue = ""
fun insertAll(data: List<KeamananModel>) {
data.forEach {
listData.add(it)
notifyItemInserted(listData.size - 1)
}
}
fun clear() {
if (listData.isNotEmpty()) {
listData.clear()
notifyDataSetChanged()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
ItemRequestJoinBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = listData[position]
holder.bindTo(item)
holder.binding.etAnswer.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun afterTextChanged(s: Editable?) {
onTextChanged.invoke(s.toString(), position)
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
textValue = s.toString().toLowerCase()
}
})
}
override fun getItemCount() = listData.size
inner class ViewHolder(val binding: ItemRequestJoinBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bindTo(item: KeamananModel) {
val context = binding.root.context
binding.tvQuestion.text = item.qt
}
}
private fun isLog(msg: String) {
Log.e("join grup:", msg)
}
}
and in my current Activity, I used to add some codes like
RequestJoinAdapter { text, position ->
val values = ArrayList<Jawaban>()
val hashSet = HashSet<Jawaban>()
values.add(Jawaban(pertanyaan_no = position+1, jawaban = text))
hashSet.addAll(values)
values.clear()
values.addAll(hashSet)
val valuest = ArrayList<JawabanKeamanan>()
val hashSets = HashSet<JawabanKeamanan>()
valuest.add(JawabanKeamanan(values))
hashSets.addAll(valuest)
valuest.clear()
valuest.addAll(hashSets)
isLog("currentResult: $valuest")
}
How do I set my latest valuest into my var listJawaban: MutableList<JawabanKeamanan> = ArrayList() without any duplicate datas inside it ? What I want is like JawabanKeamanan(jawaban_keamanan=[Jawaban(pertanyaan_no=1, jawaban=t)], [Jawaban(pertanyaan_no=2, jawaban=u)]). Thanks..
The simplest answer is to implement a TextWatcher to your EditText and have it return your data when your text is changed.
Now you may ask how can I get such data in my activty? Well it's pretty simple.
Create an interface in your adapter to communicate with your activity.
Pass your interface as a constructor parameter so when you initialize your adapter in the activity your methods are implemented.
Another solution for you is to add a button in each item of your list and call your interface method in the button's OnClickListener.
EDIT:
In the snippet below I have used a lambda function for when the text changes.
class RequestJoinAdapter(
private val onClickedItem: (ArrayList<String>)->Unit,
val onTextChanged: (text:String,position:Int)->Unit
):
RecyclerView.Adapter<RequestJoinAdapter.ViewHolder>() {
var listData: MutableList<KeamananModel> = ArrayList()
var listJawaban: MutableList<Jawaban> = ArrayList()
private var textValue = ""
fun insertAll(data: List<KeamananModel>) {
data.forEach {
listData.add(it)
notifyItemInserted(listData.size - 1)
}
}
fun clear() {
if (listData.isNotEmpty()) {
listData.clear()
notifyDataSetChanged()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
ItemRequestJoinBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = listData[position]
holder.bindTo(item)
}
override fun getItemCount() = listData.size
inner class ViewHolder(val binding: ItemRequestJoinBinding):
RecyclerView.ViewHolder(binding.root) {
fun bindTo(item: KeamananModel) {
val context = binding.root.context
binding.tvQuestion.text = item.qt
binding.etAnswer.addTextChangedListener(object: TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun afterTextChanged(s: Editable?) {
if (textValue.isNotEmpty()) {
isLog(textValue)
onTextChanged.invoke(s,absoluteAdapterPosition)
} else {
}
listJawaban.add(Jawaban(pertanyaan_no = adapterPosition + 1, jawaban = textValue))
isLog(listJawaban.toString())
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
textValue = s.toString().toLowerCase()
}
})
}
}
private fun isLog(msg: String) {
Log.e("join grup:", msg)
}
}
Pay attention to the onTextChanged.invoke() method. This lambda function can be used like an interface to communicate between your adapter and your view. It will be triggered every time your text is changed.
Finally instantiate your adapter like below:
val adapter = RequestJoinAdapter({
}, { text, position ->
//onTextChanged
})
The position argument is there to help you know which TextView was changed

How to display an image from API and send the user to video link after clicking the image? Kotlin, Retrofit

I am trying to display a video Thumbnail in recyclerview with it is Title
here is my data class:
data class PropertyItem(
val id: Int,
val thumbnailUrl: String,
val title: String,
val videoId: String,
val videoUrl: String
)
and here is the list_item xml:
<ImageView
android:id="#+id/imageViewid"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent=".3"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:src="#drawable/ic_launcher_background"/>
MainActivity:
class MainActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var manager: RecyclerView.LayoutManager
private lateinit var myAdapter: RecyclerView.Adapter<*>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
manager = LinearLayoutManager(this)
getAllData()
}
fun getAllData(){
Api.retrofitService.getAllData().enqueue(object: Callback<List<PropertyItem>> {
override fun onResponse(
call: Call<List<PropertyItem>>,
response: Response<List<PropertyItem>>
) {
if(response.isSuccessful){
recyclerView = findViewById<RecyclerView>(R.id.recycler_view).apply{
myAdapter = MyAdapter(response.body()!!)
layoutManager = manager
adapter = myAdapter
}
}
}
override fun onFailure(call: Call<List<PropertyItem>>, t: Throwable) {
t.printStackTrace()
}
})
}
}
Adapter
class MyAdapter(private val data: List<PropertyItem>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
class MyViewHolder(val view: View): RecyclerView.ViewHolder(view){
fun bind(propertyItem: PropertyItem){
val title = view.findViewById<TextView>(R.id.tvTitle)
val imageView = view.findViewById<ImageView>(R.id.imageViewid)
title.text = propertyItem.title
Glide.with(view.context).load(propertyItem.thumbnailUrl).centerCrop().into(imageView)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
return MyViewHolder(v)
}
override fun getItemCount(): Int {
return data.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(data[position])
}
}
so I'm getting all the data from data class but it doesn't display the image and how to add url to the imageview so the user can click the image and redirected to the viedo?
I am following this tutorial:
https://www.codevscolor.com/android-kotlin-recyclerview-with-image
am I doing anything wrong?
this code solved my problem
Picasso.with(activity)
.load(countryList[position].thumbnailUrl)
.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
.networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE)
.into(holder.thumbnailUrl)
I had to use Picasso not Glide

How to use ViewBinding in a RecyclerView.Adapter?

Can I use ViewBindings to replace findViewById in this typical RecyclerView.Adapter initialization code? I can't set a binding val in the object as the ViewHolders are different per cell.
class CardListAdapter(private val cards: LiveData<List<Card>>) : RecyclerView.Adapter<CardListAdapter.CardViewHolder>() {
class CardViewHolder(val cardView: View) : RecyclerView.ViewHolder(cardView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
val binding = CardBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return CardViewHolder(binding.root)
}
override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
val title = holder.cardView.findViewById<TextView>(R.id.title)
val description = holder.cardView.findViewById<TextView>(R.id.description)
val value = holder.cardView.findViewById<TextView>(R.id.value)
// ...
}
What you need to do is pass the generated binding class object to the holder class constructor. In below example, I have row_payment XML file for RecyclerView item and the generated class is RowPaymentBinding so like this
class PaymentAdapter(private val paymentList: List<PaymentBean>) : RecyclerView.Adapter<PaymentAdapter.PaymentHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PaymentHolder {
val itemBinding = RowPaymentBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return PaymentHolder(itemBinding)
}
override fun onBindViewHolder(holder: PaymentHolder, position: Int) {
val paymentBean: PaymentBean = paymentList[position]
holder.bind(paymentBean)
}
override fun getItemCount(): Int = paymentList.size
class PaymentHolder(private val itemBinding: RowPaymentBinding) : RecyclerView.ViewHolder(itemBinding.root) {
fun bind(paymentBean: PaymentBean) {
itemBinding.tvPaymentInvoiceNumber.text = paymentBean.invoiceNumber
itemBinding.tvPaymentAmount.text = paymentBean.totalAmount
}
}
}
Also, make sure you pass the root view to the parent class of Viewholder like this RecyclerView.ViewHolder(itemBinding.root) by accessing the passed binding class object.
Attach the binding to the ViewHolder instead of the View
class CardViewHolder(val binding: CardBinding) : RecyclerView.ViewHolder(binding.root)
You pass the binding, the binding passes binding.root to RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
val binding = CardBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return CardViewHolder(binding)
}
Then access anywhere with:
holder.binding.title
I wrote a simple and reusable one:
class ViewBindingVH constructor(val binding: ViewBinding) :
RecyclerView.ViewHolder(binding.root) {
companion object {
inline fun create(
parent: ViewGroup,
crossinline block: (inflater: LayoutInflater, container: ViewGroup, attach: Boolean) -> ViewBinding
) = ViewBindingVH(block(LayoutInflater.from(parent.context), parent, false))
}
}
class CardAdapter : RecyclerView.Adapter<ViewBindingVH>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewBindingVH {
return ViewBindingVH.create(parent, CardBinding::inflate)
}
override fun onBindViewHolder(holder: ViewBindingVH, position: Int) {
(holder.binding as CardBinding).apply {
//bind model to view
title.text = "some text"
descripiton.text = "some text"
}
}
}
You may use view-binding like this :
package com.example.kotlinprogramming.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.kotlinprogramming.data.HobbiesData
import com.example.kotlinprogramming.databinding.ItemHobbieBinding
class HobbiesAdapter(var context: Context, var hobbiesList: List<HobbiesData>) :
RecyclerView.Adapter<HobbiesAdapter.HobbiesViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HobbiesViewHolder {
val view = ItemHobbieBinding.inflate(LayoutInflater.from(context) , parent,false)
return HobbiesViewHolder(view)
}
override fun onBindViewHolder(holder: HobbiesViewHolder, position: Int) {
val hobbie = hobbiesList.get(position)
holder.viewBinding.tvHobbie.text = hobbie.title
}
inner class HobbiesViewHolder(var viewBinding: ItemHobbieBinding) : RecyclerView.ViewHolder(viewBinding.root) {
}
override fun getItemCount(): Int {
return hobbiesList.size
}
}
Here is item_hobbies.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_margin="12dp"
android:layout_height="wrap_content"
>
<TextView
android:id="#+id/tvHobbie"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:gravity="center"
android:textSize="30sp"
tools:text="Hobbie1"
/>
</androidx.cardview.widget.CardView>
If you're ok with reflection, I have a much easier way to do this.
Just call ViewGroup.toBinding() then you can get the binding object you want.
But since we're talk about reflection, remember you have to modify your proguard-rule to make it work even for proguard.
// inside adapter
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return MainViewHolder(parent.toBinding())
}
// ViewHolder
class MainViewHolder(private val binding: AdapterMainBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(data: String) {
binding.name.text = data
}
}
// The magic reflection can reused everywhere.
inline fun <reified V : ViewBinding> ViewGroup.toBinding(): V {
return V::class.java.getMethod(
"inflate",
LayoutInflater::class.java,
ViewGroup::class.java,
Boolean::class.java
).invoke(null, LayoutInflater.from(context), this, false) as V
}
I put all this into an open source project you can take a look as well.
Not only for Adapter usage but also include Activity and Fragment. And do let me know if you have any comment. Thanks.
https://github.com/Jintin/BindingExtension
just pass your model calss into xml and set these data into xml this code look fine and add a method where you add these data into binding like you don,t need to fine the id for this
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.setData(listData[position])
}
fun setData(model: ListData) {
with(binding) {
data = model
executePendingBindings()
}
}
You may use data binding like this.
class CardListAdapter(
private val mActivity: FragmentActivity?
) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var mCustomLayoutBinding: CustomLayoutBinding? = null
inner class MyViewHolder(val mBinding: CustomLayoutBinding) :
RecyclerView.ViewHolder(mBinding.getRoot())
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (layoutInflater == null)
layoutInflater = LayoutInflater.from(parent.context)
var viewHolder: RecyclerView.ViewHolder? = null
val inflater = LayoutInflater.from(parent.context)
viewHolder = getViewHolder(parent, inflater)
return viewHolder!!
}
private fun getViewHolder(
parent: ViewGroup,
inflater: LayoutInflater
): RecyclerView.ViewHolder {
mCustomLayoutBinding =
DataBindingUtil.inflate(inflater, R.layout.custom_layout, parent, false)
return MyViewHolder(this!!.mAssistanceLogCustomLayoutBinding!!)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val taskModal = mArrayList.get(position)
holder.mBinding.txtTitle.setText(taskModal.title)
}
override fun getItemCount(): Int {
return assistanceArrayList.size
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemViewType(position: Int): Int {
return position
}
}
I took what Alan W. did and added Generics to it.
class ViewBindingVH <VB: ViewBinding> constructor(val binding: VB) :
RecyclerView.ViewHolder(binding.root) {
companion object {
inline fun <VB: ViewBinding> create(
parent: ViewGroup,
crossinline block: (inflater: LayoutInflater, container: ViewGroup, attach: Boolean) -> VB
) = ViewBindingVH<VB>(block(LayoutInflater.from(parent.context), parent, false))
}}
The implementation is very easy and you avoid the casting on the adapter class:
class PlayerViewHolder : ListAdapter<Rate, ViewBindingVH<ChartItemBinding>>(RateDiff) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewBindingVH<ChartItemBinding> =
ViewBindingVH.create(parent, ChartItemBinding::inflate)
override fun onBindViewHolder(holder: ViewBindingVH<ChartItemBinding>, position: Int) {
val item = currentList[position]
holder.binding.apply {
}
}}
object RateDiff: DiffUtil.ItemCallback<Rate>(){
override fun areContentsTheSame(oldItem: Rate, newItem: Rate): Boolean {
return oldItem == newItem
}
override fun areItemsTheSame(oldItem: Rate, newItem: Rate): Boolean {
return oldItem == newItem
}}
abstract class BaseRecyclerViewAdapter<T : Any, VB : ViewBinding>(
private var dataList: ArrayList<T>)
: RecyclerView.Adapter<BaseRecyclerViewAdapter.MyViewViewHolder<VB>>()
{
protected var bindingInterface: GenericSimpleRecyclerBindingInterface<T, VB>? = null
class MyViewViewHolder<VB : ViewBinding>(val viewBinding: VB) :
RecyclerView.ViewHolder(viewBinding.root) {
fun <T : Any> bind(
item: T,
position: Int,
bindingInterface: GenericSimpleRecyclerBindingInterface<T, VB>
) = bindingInterface.bindData(item, position, viewBinding)
}
#SuppressLint("NotifyDataSetChanged")
fun updateList(list: ArrayList<T>) {
dataList.clear()
dataList.addAll(list)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):MyViewViewHolder<VB> {
val view = inflateView(LayoutInflater.from(parent.context))
return MyViewViewHolder(view)
}
override fun onBindViewHolder(holder: MyViewViewHolder<VB>, position: Int) {
val item = dataList[position]
holder.bind(item, position, bindingInterface!!)
}
override fun getItemCount(): Int = dataList.size
abstract fun inflateView(inflater: LayoutInflater): VB
}
interface GenericSimpleRecyclerBindingInterface<T : Any, VB : ViewBinding> {
fun bindData(item: T, position: Int, viewBinding: VB)
}

Recycleview in Kotlin

I have a problem with:
RecyclerView: No adapter attached; skipping layout
I can't define adapter in OnCreate because the list is not ready.
How I can define adapter in OnCreate? or what is a possible solution for resolve my problem?
In onCreate I did:
adapter = MyAdapter(this#MyActivity)
adapter.data = ArrayList()
Then later I just set adapter.date = xxx
In my adapter I have:
class MyAdapter(val activity: MyActivity) :
RecyclerView.Adapter<MyAdapter.BodyViewHolder>() {
var data: MutableList<MyModel>? = null
set(value) {
field = value
notifyDataSetChanged()
}
override fun getItemCount() = data?.size ?: 0
fun ViewGroup.inflate(#LayoutRes layoutRes: Int, attachToRoot: Boolean = false): View {
return LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BodyViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.my_layout, parent, false)
return BodyViewHolder(view)
}
override fun onBindViewHolder(holder: BodyViewHolder, position: Int) {
holder.bindValue(data?.get(position), activity)
}
class BodyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindValue(record: MyModel?, activity: MyActivity) {
record?.let {
itemView.mTextView.text = ....
}
}
}
}
Worth mentioning that the height of my recycler view is wrap_content
<android.support.v7.widget.RecyclerView
android:id="#+id/mRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
It's totally ok if you don't have data in onCreate. What you need to do is to define the adapter and bind it with your RecyclerView. Once you have data ready, add data to the list in adapter and notify it. Example as below
class MyAdapter: RecyclerView.Adapter<MyViewHolder>() {
private val data = mutableListOf<MyModel>()
override fun getItemCount() = data.count()
fun addData(data : MyModel) {
// add single data the list or call addAll() to add a group of data.
// just remember not to replace the variable
}
fun clearData() {
}
fun deleteData(id: Int) {
}
}
class adpCoba(val context: Context, val datalist:ArrayList<dataCoba>):RecyclerView.Adapter<adpCoba.MyViewHolder>(){
class MyViewHolder (itemView:View):RecyclerView.ViewHolder(itemView){
val text :TextView=itemView.findViewById(R.id.textcoba)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(context).inflate(R.layout.fetch_coba,parent,false)
return MyViewHolder(itemView)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currntItem = datalist [position]
holder.text.text= currntItem.nama
}
override fun getItemCount(): Int {
return datalist.size
}
}

setOnLongClickListener in android with kotlin

How can I use setOnItemClickListner in each item in my ListView?
my xml :
<ListView
android:id="#+id/tv1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
this my adapter class
inner class mo3d1Adapter : BaseAdapter {
override fun getItemId(p0: Int): Long {
return p0.toLong()
}
override fun getCount(): Int {
return listOfmo3d.size
}
var listOfMkabala = ArrayList<MeetingDetails>()
var context: Context? = null
constructor(context: Context, listOfMkabaln: ArrayList<MeetingDetails>) : super() {
this.listOfMkabala = listOfMkabaln
this.context = context
}
override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View {
val mo3d = listOfmo3d[p0]
var inflatormo3d = context!!.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
var myViewmo3d = inflatormo3d.inflate(R.layout.fragment_item, null)
lvMo3d.onItemClickListener = AdapterView.OnItemClickListener { adapterView, view, i, l ->
Toast.makeText(context, " TEST STACK ", Toast.LENGTH_LONG).show()
}
myViewmo3d.meeting_name.text = mo3d.name1!!
myViewmo3d.meeting_date.text = mo3d.date.toString()!!
myViewmo3d.attendance_number.text = mo3d.n2.toString()!!
return myViewmo3d
}
override fun getItem(p0: Int): Any {
return listOfmo3d[p0]
}
}
I want listener for each item in my ListView
And when I used this method setOnClickListener in adapter it's not working, where can I use?
Try this in your activity class
lv.setOnItemClickListener { parent, view, position, id ->
Toast.makeText(this, "Position Clicked:"+" "+position,Toast.LENGTH_SHORT).show()
}
Although a little quirky this works fine for me.
latestMessagesAdapter.setOnItemLongClickListener { item, view ->
val row = item as LatestMessageRow
return#setOnItemLongClickListener(true)
}
First of all I would like to tell that it is RecyclerView rather than ListView. You can find plenty information why to do in such. For example you can read it hear :
RecyclerView vs. ListView
Regarding your question how to do it in correct way with RecyclerView.
Insert dependencies with RecyclerView, they are now in support library in Kotlin.
implementation "com.android.support:appcompat-v7:25.4.0"
First change your ListView with RecyclerView in xml layout like this:
<android.support.v7.widget.RecyclerView
android:id="#+id/accountList"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Create Adapter for RecyclerView:
class AccountListAdapter(val accountList: AccountList, val itemListener: (Account) -> Unit) :
RecyclerView.Adapter<AccountListAdapter.ViewHolder>(){
override fun getItemCount(): Int = accountList.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) =
holder.bind(accountList[position])
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder{
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_account, parent, false)
return ViewHolder(view, itemListener)
}
class ViewHolder(itemView: View, val itemClick: (Account) -> Unit): RecyclerView.ViewHolder(itemView){
fun bind(account : Account){
with(account){
itemView.accountName.text = title
itemView.setOnClickListener{ itemClick(this)}
}
}
}
}
item_account.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/accountName"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Models (in Kotlin you can put them in one file and name for example AccountModels.kt) :
data class AccountList(val accounts: List<Account>){
val size : Int
get() = accounts.size
operator fun get(position: Int) = accounts[position]
}
data class Account(val id : Long, val title : String, val balance : Int, val defCurrency: Int)
In Fragment/Activity connect your Adapter to RecyclerView:
override fun onStart() {
super.onStart()
setupAdapter()
}
fun setupAdapter(){
Log.d(TAG, "updating ui..")
val account1 = Account(1,"Credit", 1000, 2)
val account2 = Account(2, "Debit", 500, 2)
val account3 = Account(3, "Cash", 7000, 2)
val accounts : List<Account> = listOf(account1, account2, account3)
val adapter = AccountListAdapter(AccountList(accounts)){
val title = it.title
Log.d(TAG, "$title clicked")
}
accountList.layoutManager = LinearLayoutManager(activity)
accountList.adapter = adapter
}
That is all. Everything should work now. Hope it helps.

Categories

Resources