Solution 1 :
Is this what you want? Can you check if this works and satisfies your need.
NOTE: I expect that you did setup annotations well so hilt was setup and works correctly in your project (ex: annotating ItemListFragment
fragment with @AndroidEntryPoint
, etc…)
I had such case, and I thought rather than feeding the callback in the constructor, I should provide it through a setter method of the adapter.
For ItemListAdapter
, we can make a property of type ItemListOnClickListener
, and provide setOnClickListener
setter method.
That’s it, now you can make use of onClickListener
in ViewHolder
(Now ItemListAdapter
constructor is a no-arg one, and Hilt is able to provide a binding for it for your case):
@Singleton
class ItemListAdapter @Inject constructor():
ListAdapter<Item, ItemListAdapter.ViewHolder>(ItemListDiffCallback()) {
private var onClickListener: ItemListOnClickListener? = null
...
fun setOnClickListener(onClickListener: ItemListOnClickListener) {
this.onClickListener = onClickListener;
}
inner class ViewHolder constructor(private val binding: ListItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: Item) {
binding.item = item
binding.executePendingBindings()
// Use `onClickListener` when binding `OnClickListener` callback
binding.view.setOnClickListener(v -> onClickListener?.onClick(...))
}
}
}
For ItemListFragment
, you don’t need to construct an adapter, because Hilt will be able to provide you with one. All you have to do is provide a listener through setOnClickListener
method of this adapter:
@AndroidEntryPoint
class ItemListFragment: Fragment() {
@Inject
var adapter: ItemListAdapter? = null
private val viewModel: ItemListViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentItemListBinding.inflate(inflater)
...
adapter?.setOnClickListener(
ItemListOnClickListener {
itemId -> viewModel.onItemClicked(itemId)
}
)
binding.itemList.adapter = adapter
return binding.root
}
}
Solution 2 :
Answer Feb 2021
This is the easiest answer.
In Module class
@Module
@InstallIn(SingletonComponent::class)
class AppModule {
@Provides
@Singleton
fun provideAdapter(): MyAdapter = MyAdapter()
}
In Adapter class
private var onItemClickListener: ((RestaurantX) -> Unit)? = null
fun setOnItemClickListener(listener: (RestaurantX) -> Unit) {
onItemClickListener = listener
}
Then put this line inside view.setOnClckListener
onItemClickListener?.let {
it(restaurantX)
}
In Activity/Fragment
@Inject
lateinit var myAdapter: MyAdapter
And then inside a method
myAdapter.setOnItemClickListener {it->
//do whatever you like
}
If you still have confussion here is full source code
Problem :
I have a complete recyclerView example, now I want to use hilt inject the ItemListAdapter of this example to my ItemListFragment. But it seems something that can not be done if I still want the way of hilt.
class ItemListFragment : Fragment() {
private val viewModel: ItemListViewModel by viewModels()
private var adapter: ItemListAdapter? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentItemListBinding.inflate(inflater)
binding.lifecycleOwner = this
binding.viewModel = viewModel
adapter =
ItemListAdapter(
ItemListOnClickListener { itemId ->
viewModel.onItemClicked(itemId)
})
binding.itemList.adapter = adapter
return binding.root
}
}
class ItemListAdapter(private val onClickListener: ItemListOnClickListener):
ListAdapter<Item, ItemListAdapter.ViewHolder>(
ItemListDiffCallback()
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
ListItemBinding.inflate(LayoutInflater.from(parent.context))
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position)
holder.bind(item)
}
class ViewHolder constructor(private val binding: ListItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: Item) {
binding.item = item
binding.executePendingBindings()
}
}
}
class ItemListDiffCallback : DiffUtil.ItemCallback<Item>() {
override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
return oldItem.id == newItem.id
}
}
class ItemListOnClickListener(val clickListener: (itemID: String) -> Unit) {
fun onClick(itemID: String) = clickListener(itemID)
}
Comments
Comment posted by Bartek Lipinski
May I ask why you want to do this? You don’t need to inject everything, everywhere.
Comment posted by ccd
I tried to do this, but I don’t know is it need to do this.
Comment posted by EL TEGANI MOHAMED HAMAD GABIR
Sir @BartekLipinski is it bad practice to inject adapter this way or not inject it at all?
Comment posted by Bartek Lipinski
@ELTEGANIMOHAMEDHAMMADGABIR that’s entirely opinion-based. If you ask me if I do injections like that in my code, the answer is no. But who the hell am I to tell you how to write your own code
Comment posted by EL TEGANI MOHAMED HAMAD GABIR
@BartekLipinski okay, I will follow your approach. thanks for your humble
Comment posted by ccd
Thanks for advice, after trying the hilt for adapter I think it might need more complex operation than without it.
Comment posted by Mohammad Sultan Al Nahiyan
If ItemListAdapter have an interface like clickListener, ItemListFragment implement that interface and ItemListFragment pass the instance of interface in constructor, How to inject adapter? with hilt
Comment posted by Ahmed Shendy
@MohammadSultanAlNahiyan, my apology I don’t develop Android these days to help you more with the question.
Comment posted by Vlad
@Singleton
Comment posted by Abhijith Brumal
According to the official docs. Normally you use providers mainly for the classes from third-party libraries or the classes we don’t own. I think simple construct-inject for the adapter.
Comment posted by waheed shah
After implementing this I come to know that setOnItemClickListener is not working. Can you please see it?