РЕДАКТИРОВАТЬ - Я пытался установить атрибуты android: focusable и android: clickable в true в XML, но это ничего не изменило.По-прежнему выглядишь!
Я не могу понять, почему мой обработчик onClick не работает в этом конкретном случае.
Я хочу сделать CardView кликабельным, используя привязку данных.Я видел много кода для onClickListener, но я решил использовать этот шаблон, как описано в документе Android.
У меня есть класс с именем Recipe (), который содержит 3 элемента: идентификатор,Название и URL.Конечная цель этого обработчика кликов - перейти к новому фрагменту, передав ID в качестве параметра для отображения новых элементов.Позже я получу идентификатор во фрагменте с помощью обозревателя.
В этом примере я уже использую данные, предоставленные ViewModel в XML (recipe.image и recipe.title), и все работает отлично:данные правильно связаны и отображаются.Однако нажатие на 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>
Код фрагмента:
Класс 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, используемый в адаптере RecyclerView
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()
}