DevAnuragGarg
DevAnuragGarg

Reputation: 1752

How to do latest jetpack "View binding" in adapter, bind the views?

How to do latest jetpack "View binding" in adapter, for automatically binding the views. I am not using the findVeiwById or Butterknife or Kotlin synthetic??? I have used the new view binding and is is working fine for Activity and for Fragment. I am able to see the ActivityHomeBinding and FragmentHomeBinding files after enabling the viewBinding in build.gradle file. Also, I am seeing the class ItemListBinding for the item xml used i.e. item_list.xml. But how to use this file in the adapter of the recyclerview

viewBinding {
        enabled = true
}

Home Activity file

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityHomeBinding.inflate(layoutInflater)
        setContentView(binding.root)
}

Fragment

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        binding = FragmentHomeBinding.inflate(inflater, container, false)
        val view = binding.root
        return view
}

Base Adapter: Want to use view binding here. I am able to see ItemListBinding, but not getting how to use it.

class BaseAdapter @Inject constructor(
    private val context: Context,
    private val picasso: Picasso
) :
    RecyclerView.Adapter<BaseAdapter.ViewHolder>() {

    private var data = ArrayList<Data>()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
            LayoutInflater.from(context).inflate(R.layout.item_list, parent, false)
        )
    }

    override fun getItemCount() = data.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        with(holder) {
              // TODO
        }
    }

    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {

        @BindView(R.id.tvMovieName)
        lateinit var nameTV: TextView

        @BindView(R.id.imageView)
        lateinit var bannerImage: ImageView

        init {
            ButterKnife.bind(this@ViewHolder, view)
        }
    }

    fun setData(serverData: ArrayList<Data>) {
        data = serverData
        notifyDataSetChanged()
    }
}

Upvotes: 36

Views: 27562

Answers (6)

Kaaveh Mohamedi
Kaaveh Mohamedi

Reputation: 1805

I use this template that also integrates with click event:


class BannerAdapter(private val listener: OnCategoryClickListener) :
    ListAdapter<Banner, BannerAdapter.BannerViewHolder>(DiffCallBack()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BannerViewHolder {
        val binding = ItemRecyclerviewCategoriesSearchBinding.inflate(
            LayoutInflater.from(parent.context),
            parent,
            false
        )
        return BannerViewHolder(binding)
    }

    override fun onBindViewHolder(holder: BannerViewHolder, position: Int) {
        val currentItem = getItem(position)
        holder.bind(currentItem)
    }

    inner class BannerViewHolder(private val binding: ItemRecyclerviewCategoriesSearchBinding) :
        RecyclerView.ViewHolder(binding.root) {

        init {
            binding.root.setOnClickListener {
                val position = absoluteAdapterPosition
                if (position != RecyclerView.NO_POSITION){
                    val banner = getItem(position)
                    listener.onCategoryClick(banner.link_type, banner.link)
                }
            }
        }

        fun bind(banner: Banner) {
            binding.apply {
                Glide.with(itemView.context)
                    .load(banner.media.url)
                    .centerCrop()
                    .transition(DrawableTransitionOptions.withCrossFade())
                    .error(R.drawable.ic_broken_image)
                    .into(imageCategory)
            }
        }
    }

    interface OnCategoryClickListener {
        fun onCategoryClick(type: String, link: String)
    }

    private class DiffCallBack : DiffUtil.ItemCallback<Banner>() {
        override fun areItemsTheSame(oldItem: Banner, newItem: Banner) =
            oldItem == newItem

        override fun areContentsTheSame(oldItem: Banner, newItem: Banner) =
            oldItem.media == newItem.media
    }
}

Upvotes: 1

Gourav
Gourav

Reputation: 2819

I found this the easiest:

class RvAdapter() :
    RecyclerView.Adapter<RvAdapter.RvViewHolder>() {
        
    inner class RvViewHolder(val binding: OtherRvItemBinding) :
    RecyclerView.ViewHolder(binding.root)
        
    // Initialise view binding here.
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RvViewHolder {
        val binding =
            OtherRvItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return RvViewHolder(binding)
    }
    
    override fun getItemCount(): Int {
        // TODO: Implement logic to return list size.
        return 0
    }
    
    // Bind view holder.
    override fun onBindViewHolder(holder: RvViewHolder, position: Int) {
        holder.binding.apply {
                // Reference your views from here.
                awesomeTv.text = "Hello World!"
        }
    }    
}

Upvotes: 2

Ghayas
Ghayas

Reputation: 1324

I found the easiest way to use View Binding in the RecyclerView Adapter class

class MediaRecyclerViewAdapter: RecyclerView.Adapter<MediaRecyclerViewAdapter.MediaViewHolder>() {


lateinit var list: List<String>

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MediaViewHolder {
    val binding = CardviewItemsRecyclerviewBinding.inflate(LayoutInflater.from(parent.context),parent,false)
    return MediaViewHolder(binding)
}

override fun onBindViewHolder(holder: MediaViewHolder, position: Int) {

    holder.binding.mediaHeadingTextView.text = "Hello"

}

override fun getItemCount(): Int {
    return list.size
}

class MediaViewHolder(val binding: CardviewItemsRecyclerviewBinding) : RecyclerView.ViewHolder(binding.root) {

   }

}

Upvotes: 4

EpicPandaForce
EpicPandaForce

Reputation: 81568

Nothing really changes compared to how you'd normally do it.

class DataAdapter(
    private val context: Context,
    private val picasso: Picasso
) : RecyclerView.Adapter<DataAdapter.ViewHolder>() {
    private var dataList: List<Data> = Collections.emptyList()

    override fun getItemCount() = dataList.size

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(
        LayoutInflater.from(context).inflate(R.layout.item_list, parent, false)
    )

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(dataList[position])
    }

    fun setData(dataList: List<Data>) {
        this.dataList = dataList
        notifyDataSetChanged()
    }

    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        private val binding = ItemListBinding.bind(view)   

        fun bind(data: Data) {
            with(binding) {
                // TODO
                nameTV.text = data.name
                bannerImage.loadWithPicasso(picasso, data.imageUrl)
            }
        }
    }
}

Upvotes: 19

DevAnuragGarg
DevAnuragGarg

Reputation: 1752

Found another to resolve it

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = ItemListBinding.inflate(LayoutInflater.from(context), parent, false)
        return MovieViewHolder(binding)
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        with(holder) {

            with(moviesData[position]) {
                binding.tvMovieName.text = title
            }
        }
    }
}

class MovieViewHolder(val binding: ItemMovieBinding) : RecyclerView.ViewHolder(binding.root)

Upvotes: 33

Pawel
Pawel

Reputation: 17268

You can use static bind method of ViewBinding to create binding from already existing layout. Add it as a property to viewholder:

class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
   val binding = ItemListBinding.bind(view)
}

then you can access all the views through the binding field, for example:

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    with(holder) {
          // TODO
          binding.tvMovieName.text = data[position].title
          binding.imageView.setDrawableImage(data[position].image)
    }
}

Upvotes: 67

Related Questions