'Detect Adapter onclick Listener method in activity using Entrypoints Dagger hilt

I need a help to detect adapter onclick event in activity class with the help of dagger hilt.

Step1 : I have created one interface class which name is ItemClickListener and use EntryPoint using Dagger hilt.

 @InstallIn(SingletonComponent::class)
 @EntryPoint
 interface ItemClickListener {
     fun onItemClick(bundle: Bundle?)
 }

Step2: Then after I create adapter class which name is CountryNewAdapter. I also inject my application context inside it.

    @Singleton
   class CountryNewsAdapter @Inject constructor(
       @ApplicationContext var context: Context
    ) :
    RecyclerView.Adapter<CountryNewsAdapter.OnCountryNewsViewHolder>(), OnItemUpdateListener {

    private val TAG: String = javaClass.simpleName
    private var hasWritePermission = false
    private var itemList: List<CountryNews> = ArrayList()

    var itemClickEntryPoint = EntryPoints.get(context, ItemClickListener::class.java)
  


    private var diffCallback = object : DiffUtil.ItemCallback<CountryNews>() {
        override fun areItemsTheSame(oldItem: CountryNews, newItem: CountryNews): Boolean {
            return oldItem.title == newItem.title
        }

        override fun areContentsTheSame(oldItem: CountryNews, newItem: CountryNews): Boolean {
            return oldItem.hashCode() == newItem.hashCode()
        }

    }

    private val differ = AsyncListDiffer(this, diffCallback)

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

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

        bindView(holder, position)
    }

    private fun bindView(holder: OnCountryNewsViewHolder, position: Int) {
        with(holder) {
            with(differ.currentList[position]) {
                if (imageHref == null || localImageHref.isNullOrEmpty())
                    binding.ivFeature.visibility = View.GONE
                else {
                    val urlToLoad = if (hasWritePermission) if (localImageHref.isNullOrEmpty())
                        imageHref else localImageHref else ""

                    Glide.with(context)
                        .load(urlToLoad)
                        .placeholder(R.drawable.ic_loader)
                        .error(R.drawable.ic_loader)
                        .into(binding.ivFeature)
                    binding.ivFeature.visibility = View.VISIBLE
                }

                binding.tvTitle.text = title
                binding.tvDesc.text = description
                binding.ivFeature.setOnClickListener {
                    Log.e(TAG, "bindView: " )
                    itemClickEntryPoint.onItemClick(null)
                }
            }
        }
    }

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

    fun setItemData(itemList: List<CountryNews>?) {
        if (itemList != null) {
            differ.submitList(itemList)
        }
    }

    fun hasPermission(status: Boolean) {
        this.hasWritePermission = status
        notifyDataSetChanged()
    }

    inner class OnCountryNewsViewHolder(val binding: CountryNewsRowItemBinding) :
        RecyclerView.ViewHolder(binding.root)

    override fun onItemUpdate(bundle: Bundle?) {
        Handler(Looper.getMainLooper()).post {
            notifyDataSetChanged()
        }
    }
 }

Step3: Now on MainActivity I have injected my adapter class and set this adapter class into my activity. And on result I get list of item on screen but when I click on it. it will not detect on click event. I want to detect with Entrypoints. I already try with implement my interface inside MainActivity but both are not working for me.

@AndroidEntryPoint
class MainActivity : AppCompatActivity(), ItemClickListener {
    private val TAG: String = javaClass.simpleName
    private lateinit var binding: ActivityMainBinding
    private val mainViewModel: MainViewModel by viewModels()

    @Inject
    lateinit var countryNewsAdapter: CountryNewsAdapter


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        initViewModel()
        initAdapter()
        requestPermissions()
    }

    private fun initViewModel() {

        lifecycleScope.launch {
            mainViewModel.newsList.collect {
                when (it.status) {
                    Status.LOADING -> {
                        binding.progressBar.isVisible = true
                        binding.rvCountryNews.isVisible = false
                        binding.tvNoDataFound.isVisible = false
                    }
                    Status.SUCCESS -> {
                        binding.progressBar.isVisible = false
                        if (!it.data.isNullOrEmpty()) {
                            countryNewsAdapter.setItemData(it.data)
                            binding.rvCountryNews.isVisible = true
                            binding.tvNoDataFound.isVisible = false
                        } else {
                            binding.rvCountryNews.isVisible = false
                            binding.tvNoDataFound.isVisible = true
                        }
                    }
                    Status.ERROR -> {
                        binding.progressBar.isVisible = false
                        binding.rvCountryNews.isVisible = false
                        binding.tvNoDataFound.isVisible = true
                        Toast.makeText(
                            this@MainActivity,
                            it.message,
                            Toast.LENGTH_SHORT
                        ).show()
                    }
                }
            }
        }
    }

    private fun initAdapter() {

        /* Bind Json String with Kotlin class.
           * Intialize Adapter and attached with recyclerView.*/
        binding.rvCountryNews.layoutManager = LinearLayoutManager(this)
        countryNewsAdapter.itemClickEntryPoint.apply {
            Log.e(TAG, "initAdapter: " )
        }
        binding.rvCountryNews.adapter = countryNewsAdapter
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray,
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when (requestCode) {
            MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE -> {
                // If request is cancelled, the result arrays are empty.
                if ((grantResults.isNotEmpty() && grantResults[0] != PackageManager.PERMISSION_GRANTED)) {
                    Toast.makeText(
                        this,
                        getString(R.string.str_please_allow_storage_permission),
                        Toast.LENGTH_SHORT
                    ).show()
                } else {
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                    mainViewModel.getNewsList()
                    countryNewsAdapter.hasPermission(true)
                }
                return
            }
            // Add other 'when' lines to check for other
            // permissions this app might request.
            else -> {
                // Ignore all other requests.
            }
        }
    }

    @TargetApi(Build.VERSION_CODES.M)
    fun requestPermissions() {
        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE
            )
        } else {
            mainViewModel.getNewsList()
            countryNewsAdapter.hasPermission(true)
        }
    }

    override fun onItemClick(bundle: Bundle?) {
        Log.e(TAG, "onItemClick: " )
        requestPermissions()
    }
}


Solution 1:[1]

I think You ask to use hilt in onclickListners,but I don't know why, Kotlin Provide Multiple Functions to Archive it.


in your Adapter


     var itemClickListener: ((position: Int, name: String) -> Unit)? = null
//bindviewholder
     itemClickListner.invoke(1,"anyvalue")

in activity

   adapter.itemClickListener = { 
            position, name ->
        Toast.makeText(requireContext(),"position is $position name is $name ",Toast.LENGTH_SHORT).show()
    }
 

in this method you inject adapter only.

Solution 2:[2]

You need to define callback lambda in your adapter class and use that in your activity or fragment when you want.

In your adapter

class InventoryAdapter @Inject constructor() : RecyclerView.Adapter<DataViewHolder?>() {

    var onItemClickCallback: ((InventoryModel?) -> Unit)? = null
   
}

In your activity/fragment

@Inject
lateinit var adapter: InventoryAdapter

private fun setUpListView() {
    binding.recyclerView.adapter = adapter
    adapter.onItemClickCallback = { inventoryModel ->
        Log.e("onItemClickCallback")
    }
}

or using method

@Inject
lateinit var adapter: InventoryAdapter

private fun setUpListView() {
    binding.recyclerView.adapter = adapter
    adapter.onItemClickCallback = this::onItemClick
}

private fun onItemClick(inventoryModel: InventoryModel?) {
    Log.e("onItemClickCallback")
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Vijay S
Solution 2 Dhaval Baldha