[英]onClick not triggering associated function - kotlin
編輯 - 我試圖在 XML 中將 android:focusable 和 android:clickable 屬性設置為 true,但它沒有改變任何東西。 還在尋找!
我不明白為什么我的 onClick 處理程序不能處理這種特殊情況。
我想使用數據綁定使 CardView 可點擊。 我已經看過很多 onClickListener 的代碼,但我決定使用這種模式,如 Android 文檔中所述。
我有一個名為 Recipe() 的 class,它包含 3 個元素:一個 ID、一個標題和一個 URL。 此單擊處理程序的最終目標是導航到新片段,將 ID 作為參數傳遞以顯示新元素。 稍后我將使用觀察者在 Fragment 中獲取 ID。
在這個例子中,我已經使用了 XML(recipe.image 和 recipe.title)中的 ViewModel 提供的數據,它工作得很好:數據被正確綁定和顯示。 但是,單擊 CardView 不會產生任何結果:由於未顯示日志,我想單擊不會觸發 onClick 事件。
您將在下面的 ViewModel 和 XML 中找到元素。 提前致謝!
class DefaultRecipeListViewModel : ViewModel() {
private val _recipeList = MutableLiveData<List<Recipe>>()
val recipeList: LiveData<List<Recipe>>
get() = _recipeList
//Defining coroutine
private var viewModelJob = Job()
private val coroutineScope = CoroutineScope(viewModelJob + Dispatchers.Main )
//Livedata observed in the Fragment
private var _navigateToRecipe = MutableLiveData<Int>()
val navigateToRecipe: LiveData<Int>
get() = _navigateToRecipe
private var _showSnackbarEvent = MutableLiveData<Boolean>()
val showSnackBarEvent: LiveData<Boolean>
get() = _showSnackbarEvent
init {
getRecipesForNewbie()
}
fun getRecipesForNewbie() {
coroutineScope.launch {
var getRecipes = service.getRecipe().await()
try {
_recipeList.value = getRecipes.results
} catch (e: Exception) {
Log.i("ViewModel","Error: $e")
}
}
}
fun onRecipeClicked(id: Int) {
_showSnackbarEvent.value = true
_navigateToRecipe.value = id
Log.i("ViewModel", "Item clicked, id: $id")
}
fun doneNavigating(){
Log.i("ViewModel", "done navigating, navigateToRecipe set to -1")
_navigateToRecipe.value = -1
}
fun doneShowingSnackbar() {
_showSnackbarEvent.value = false
}
override fun onCleared() {
super.onCleared()
viewModelJob.cancel()
}
}
這是 XML
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="recipe"
type="com.example.recipesfornewbies.recipes.Recipe" />
<variable
name="viewModel"
type="com.example.recipesfornewbies.defaultrecipelist.DefaultRecipeListViewModel" />
</data>
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent">
<androidx.cardview.widget.CardView
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp"
app:cardCornerRadius="10dp"
app:cardElevation="6dp"
android:onClick="@{()-> viewModel.onRecipeClicked(recipe.id)}">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/recipe_image"
android:layout_width="match_parent"
android:layout_height="100dp"
android:contentDescription="@{recipe.title}"
app:imageFromUrl="@{recipe.image}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="0dp" />
<TextView
android:id="@+id/recipe_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:text="@{recipe.title}"
android:textSize="24sp"
app:layout_constraintTop_toBottomOf="@id/recipe_image"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</layout>
片段代碼:
class DefaultRecipeListFragment: Fragment(){
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentDefaultRecipeListBinding.inflate(inflater)
val viewModel: DefaultRecipeListViewModel by lazy {
ViewModelProviders.of(this).get(DefaultRecipeListViewModel::class.java)
}
binding.viewModel = viewModel
// Allows Data Binding to Observe LiveData with the lifecycle of this Fragment
binding.setLifecycleOwner(this)
//Creating the RecyclerView
val manager = LinearLayoutManager(activity)
binding.recyclerRecipeList.layoutManager = manager
binding.recyclerRecipeList.adapter = RecipeListAdapter()
viewModel.navigateToRecipe.observe(this, Observer{id ->
if (id != -1){
Log.i("Fragment","Navigate to ${id}")
}
viewModel.doneNavigating()
})
viewModel.showSnackBarEvent.observe(this, Observer {
if (it == true) {
Snackbar.make(
activity!!.findViewById(android.R.id.content),
"Clicked!",
Snackbar.LENGTH_SHORT
).show()
viewModel.doneShowingSnackbar()
}
})
return binding.root
}
}
ViewHolder class,用於RecyclerView Adapter
class RecipeViewHolder(private var binding: RecipeViewBinding):
RecyclerView.ViewHolder(binding.root) {
fun bind(Recipe: Recipe) {
val imageURI = "https://spoonacular.com/recipeImages/"
Recipe.image = imageURI + Recipe.image
binding.recipe = Recipe
// Forces the data binding to execute immediately,to correctly size RecyclerVieW
binding.executePendingBindings()
}
我最終決定在我的代碼上使用這個解決方案,效果很好。
我不再嘗試直接從 XML 訪問我的 ViewModel 中的 function:我實際上在片段上監聽事件,即使我發現解決方案不如我想要的那個漂亮。
在片段中,這是我處理點擊的方式: binding.recyclerRecipeList.addOnItemTouchListener(RecyclerItemClickListener(this.context,., binding:recyclerRecipeList. object : RecyclerItemClickListener.OnItemClickListener {
override fun onItemClick(view: View, position: Int) {
viewModel.recipeList.value?.let {
val ident = it[position].id
findNavController().navigate(
DefaultRecipeListFragmentDirections.actionDefaultRecipeListFragmentToDetailedRecipeFragment(ident))
Log.i("Fragment", "id: $ident")
}
}
override fun onItemLongClick(view: View?, position: Int) {
TODO("do nothing")
}
}))
仍然不明白為什么我的第一個解決方案不起作用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.