Detect Adapter onclick Listener method in activity using Entrypoints Dagger hilt

Viewed 356

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()
    }
}
2 Answers

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.

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")
}
Related