android - 处理recyclerview适配器(Kotlin)中的按钮点击?
问题描述
我有一个适配器,其中每个项目都有 3 个按钮,它们会生成一个对话框,然后执行一个操作。我有一种感觉应该从适配器中删除它(我有可用的视图模型),但它有效,我想知道:我应该将逻辑移动到片段,视图模型,我是否需要移动它(下面的代码是不好的做法,如果是,为什么)?任何帮助/输入将不胜感激。
这是适配器代码:
class ViewRecipesAdapter(val context: Context, private val recipes: List<Recipe>, private val parentFragment: Fragment) :
RecyclerView.Adapter<ViewRecipesAdapter.RecipeViewHolder>()
{
private var listToUse: List<Recipe> = recipes
private lateinit var recipesViewModel: RecipesViewModel
private var isView = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipeViewHolder
{
val layoutInflater = LayoutInflater.from(parent.context)
val binding: ViewRecipesItemBinding =
DataBindingUtil.inflate(layoutInflater, R.layout.view_recipes_item, parent, false)
return RecipeViewHolder(binding, context)
}
override fun getItemCount() = listToUse.size
override fun onBindViewHolder(holder: RecipeViewHolder, position: Int)
{
val recipe = listToUse[position]
// to delete and edit items
val dao = RecipesDatabase.getInstance(context).recipeDao()
val repository = RecipeRepository(dao)
recipesViewModel = RecipesViewModel(repository)
//display data on list item
holder.bind(recipe)
Glide.with(context).load(recipe.imageOne)
.into(holder.binding.imageViewItemImage)
//tried to handle clicks here through the viewModel but I could not get it working from fragment
//the function call after viewModel calls is what works and it seems to work well
holder.binding.imageButtonItemdelete.setOnClickListener {
recipesViewModel.setIsDelete(true)
recipesViewModel.setPositionFromAdapter(position)
startDeleteDialog(position)
}
holder.binding.imageButtonItemedit.setOnClickListener {
recipesViewModel.setIsView(false)
recipesViewModel.setPositionFromAdapter(position)
isView = false
startEditOrViewDialog(position)
}
holder.binding.imageButtonItemview.setOnClickListener {
recipesViewModel.setIsView(true)
recipesViewModel.setPositionFromAdapter(position)
isView = true
startEditOrViewDialog(position)
}
}
fun setList(newList: List<Recipe>)
{
listToUse = newList
}
//dialog functions for the edit, delete, and view buttons on each item
private fun startDeleteDialog(position: Int)
{
AlertDialog.Builder(context)
.setTitle("Delete recipe?")
.setPositiveButton("Yes") { _, _ ->
recipesViewModel.deleteRecipe(recipes[position])
notifyItemRemoved(position)
}
.setNegativeButton("No") { dialog, _ ->
dialog.dismiss()
}.show()
}
private fun startEditOrViewDialog(position: Int)
{
when (isView)
{
true ->
{
AlertDialog.Builder(context).setTitle("View recipe?")
.setPositiveButton("Yes") { _, _ ->
//get relevant data from current recipe
val recipe = recipes[position]
//create a dialog that shows this data in an inflated layout
val viewDialog = AlertDialog.Builder(context)
val inflater = LayoutInflater.from(context)
val view = inflater.inflate(R.layout.fragment_edit_or_view, null)
view.editText_editrecipe_directions.setText(recipe.directions)
view.editText_editrecipe_ingredients.setText(recipe.ingredients)
view.editText_editrecipe_notes.setText(recipe.notes)
view.editText_editrecipe_title.setText(recipe.title)
view.textView_date_edit.text = recipe.date
view.editText_editrecipe_title.keyListener = null
view.editText_editrecipe_directions.keyListener = null
view.editText_editrecipe_ingredients.keyListener = null
view.editText_editrecipe_notes.keyListener = null
if (recipe.rating != null)
{
view.ratingBar_edit.rating = recipe.rating
}
Glide.with(context)
.load(recipe.imageOne)
.into(view.imageView_addphoto_edit)
viewDialog.setView(view).show()
}
.setNegativeButton("No") { dialog, _ ->
dialog.dismiss()
}.show()
}
false ->
{
AlertDialog.Builder(context).setTitle("Edit recipe?")
.setPositiveButton("Yes") { _, _ ->
//get relevant data from current recipe
val recipe = recipes[position]
val idString = recipe.id.toString()
recipesViewModel.setId(idString)
recipesViewModel.getRecipeById2(idString)
notifyDataSetChanged()
val controller = parentFragment.findNavController()
controller.navigate(
ViewRecipesFragmentDirections.actionNavViewrecipesToNavAddrecipe(
recipe.id.toString()
)
)
}
.setNegativeButton("No") { dialog, _ ->
dialog.dismiss()
}.show()
}
}
}
override fun getItemId(position: Int): Long
{
return position.toLong()
}
override fun getItemViewType(position: Int): Int
{
return position
}
class RecipeViewHolder(val binding: ViewRecipesItemBinding, val context: Context) :
RecyclerView.ViewHolder(binding.root)
{
fun bind(recipe: Recipe)
{
if (recipe.isLeftover == true)
{
binding.tvIsLeftovers.visibility = View.VISIBLE
}
binding.textViewItemTitle.text = recipe.title
if (recipe.date != null)
{
binding.textViewItemDate.text = recipe.date
}
if (recipe.rating != null)
{
binding.ratingBar2.rating = recipe.rating
}
binding.root.animation = AlphaAnimation(0.0f, 1.0f).apply {
duration = 1000
}
}
}
}
这是视图模型,设置了实时数据变量,我无法在此 RecyclerView 所在的片段中工作:
class RecipesViewModel(private val repository: RecipeRepository) : ViewModel()
{
val recipesList = repository.getAllRecipes()
private val _isView = MutableLiveData<Boolean>()
val isView: MutableLiveData<Boolean> = _isView
private val _isEdit = MutableLiveData<Boolean>()
val isEdit: MutableLiveData<Boolean> = _isEdit
private val _positionFromAdapter = MutableLiveData<Int>()
val positionFromAdapter: MutableLiveData<Int> = _positionFromAdapter
private val _isDelete = MutableLiveData<Boolean>()
val isDelete: MutableLiveData<Boolean> = _isDelete
private val _recipesListFromSearch = MutableLiveData<List<Recipe>>()
val recipesListFromSearch: LiveData<List<Recipe>> = _recipesListFromSearch
private val _recipe = MutableLiveData<Recipe>()
val recipe: LiveData<Recipe> = _recipe
lateinit var searchString: String
val savedId = MutableLiveData<String>()
fun setPositionFromAdapter(position: Int)
{
_positionFromAdapter.value = position
}
fun setIsView(isView: Boolean)
{
_isView.value = isView
}
fun setIsDelete(isDelete: Boolean)
{
_isView.value = isDelete
}
fun setIsEdit(isEdit: Boolean)
{
_isEdit.value = isEdit
}
fun setId(id: String)
{
savedId.value = id
}
fun insertRecipe(recipe: Recipe)
{
CoroutineScope(Dispatchers.IO).launch {
repository.insertRecipe(recipe)
}
}
fun getRecipesFromQuery(query: String)
{
CoroutineScope(Dispatchers.IO).launch {
val list = repository.getRecipesSearch(query)
MainScope().launch { _recipesListFromSearch.value = list }
}
}
fun saveUserRecipeToDb(
title: String?,
ingredients: String?,
directions: String?,
notes: String?,
uriToSave: String?,
rating: Float?,
date: String?,
isLeftover: Boolean,
loadedId: String
): Boolean
{
val recipeToSave = Recipe(
title,
ingredients,
directions,
notes,
uriToSave,
null,
null,
rating,
date,
isLeftover
)
if (loadedId != "666")
{
recipeToSave.id = loadedId.toInt()
}
insertRecipe(recipeToSave)
return false
}
fun getRecipeById2(id: String) = repository.getRecipeByIdLive(id)
fun deleteRecipe(recipe: Recipe)
{
CoroutineScope(Dispatchers.IO).launch {
repository.deleteRecipe(recipe)
}
}
}
解决方案
如何onClick
在 RecyclerView 中实现。假设在 Your Recycler 中,每个视图都是某些视图的可视化,item
当您单击它时,您想对该项目执行某些操作:
- 创建类
ClickListener
::
class ClickListener(
val clickListener: (itemId: Int) -> Unit,
)
{
fun onClick(item: ItemClass) = clickListener(item.id)
}
- 现在在你的 RecylerViewAdapter 作为参数传递这个监听器:
class RecylerViewAdapter(
private val clickListener: ClickListener
)
onBindViewHolder
传入这个 Listenner 作为参数
override fun onBindViewHolder(holder: ViewHolder, position: Int)
{
holder.bind(getItem(position)!!, clickListener)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder
{
return ViewHolder.from(
parent
)
}
- 在您的 ViewHolder 类中:
class ViewHolder private constructor(private val binding: ItemRecyclerBinding) :
RecyclerView.ViewHolder(binding.root)
{
companion object
{
fun from(parent: ViewGroup): ViewHolder
{
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ItemRecyclerBinding.inflate(layoutInflater, parent, false)
return ViewHolder(
binding
)
}
}
fun bind(
item : Item,
clickListener: ClickListener
)
{
binding.item = item
binding.clickListener = clickListener
binding.executePendingBindings()
}
}
- 在您的项目布局(必须转换为数据绑定布局)中添加:
<data>
<variable
name="item"
type="com.example.sth.database.Item" /> // path to `Item`
<variable
name="clickListener"
type="com.example.sth.ui.adapter.ClickListener" /> // Path to `ClickListener`
</data>
- 现在您可以
onClick
向 Button 添加方法:
android:onClick="@{() -> clickListener.onClick(item)}"
- 当您在片段或活动中创建适配器时,您必须
clickListenner
作为参数传递。通过这种方式,您可以处理来自片段的所有内容,而 RecyclerView 并不关心您在此函数中所做的事情。
val clickListenner = ClickListenner(
{ id -> viewModel.clickItemWithid(id) }, // click. This function from ViewModel will be executed when You click on item in recycler View
)
val adapter = RecylerViewAdapter (
clickListenner
)
此方法基于 Udacity 上的 Google 开发人员代码实验室。
在这里您可以检查整个代码实验室。它是免费的。
这里只是一个实现点击监听器的视频
推荐阅读
- composer-php - 限制版本后如何升级作曲家包?
- mysql - 如何处理 org.hibernate.exception.ConstraintViolationException,我使用 try catch 但它不起作用
- javascript - HTML 登录页面在页面源代码中显示凭据
- javascript - 用于 URL 的 JavaScript 字符串 .replace
- ios - 为 iOS 构建后的 Ionic 应用程序链接器错误
- angular - 在一个值中使用多个 validators.pattern
- android - 使用android导航组件从第一个活动导航到第二个活动时如何关闭/完成第一个活动
- accessibility - 单击按钮时出现蓝色阴影
- html - 如何在 wordpress 中自定义侧边栏并在其中添加可折叠菜单?
- java - Knime中多行的正则表达式