'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 |