OnClick не вызывает связанную функцию - kotlin - PullRequest
0 голосов
/ 20 сентября 2019

РЕДАКТИРОВАТЬ - Я пытался установить атрибуты 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()

    }

1 Ответ

0 голосов
/ 21 сентября 2019

Я, наконец, решил использовать это решение в моем коде, который работает довольно хорошо.

Я прекратил попытки доступа к функции в моей ViewModel напрямую из XML: я на самом деле слушаюсобытие во фрагменте, даже если я нахожу решение менее привлекательным, чем то, которое я хотел.

Во фрагменте это то, как я обрабатываю щелчок: 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")
        }
    }))

Все еще не поняли, почему мое первое решение не сработало.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...