Обнаружение onClick в представлении переработчика с использованием привязки данных в Android

  1. Я ссылаюсь на учебник по vogella для привязки данных
  2. Что я пытаюсь сделать: как лучше всего обнаружить onClick в строке представления recycler для каждого элемента, используя привязку данных

activity_second.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="temp"
            type="com.vogella.android.databinding.TemperatureData" />
        <variable
            name="presenter"
            type="com.vogella.android.databinding.MainActivityPresenter"/>
    </data>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical" />
</layout>

rowlayout.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="obj"
            type="com.vogella.android.databinding.TemperatureData"
            />
    </data>

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="?android:attr/listPreferredItemHeight"
        android:padding="6dip"
        >

        <ImageView
            android:id="@+id/icon"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:layout_alignParentBottom="true"
            android:layout_alignParentTop="true"
            android:layout_marginRight="6dip"
            android:contentDescription="TODO"
            android:src="@drawable/ic_listentry"
            />

        <TextView
            android:id="@+id/secondLine"
            android:layout_width="fill_parent"
            android:layout_height="26dip"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_toRightOf="@id/icon"
            android:ellipsize="marquee"
            android:text="@{obj.location}"
            android:textSize="12sp"
            android:maxLines="1"
        />

        <TextView
            android:id="@+id/firstLine"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_above="@id/secondLine"
            android:layout_alignParentRight="true"
            android:layout_alignParentTop="true"
            android:layout_alignWithParentIfMissing="true"
            android:layout_toRightOf="@id/icon"
            android:gravity="center_vertical"
            android:text="@{obj.celsius}"
            android:textSize="16sp"
            />

    </RelativeLayout>

</layout>

MyAdapter.java

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
        private List<TemperatureData> data;

        // Provide a reference to the views for each data item
        // Complex data items may need more than one view per item, and
        // you provide access to all the views for a data item in a view holder
        public class MyViewHolder extends RecyclerView.ViewHolder {
                // each data item is just a string in this case
                private final ViewDataBinding binding;

                public MyViewHolder(ViewDataBinding binding) {
                        super(binding.getRoot());
                        this.binding = binding;
                }
                public void bind(Object obj) {
                       binding.setVariable(BR.obj,obj);
                       binding.executePendingBindings();
                }
        }

        // Provide a suitable constructor (depends on the kind of dataset)
        public MyAdapter(List<TemperatureData> myDataset) {
                data = myDataset;
        }

        // Create new views (invoked by the layout manager)
        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                // create a new view
                LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
                ViewDataBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.rowlayout, parent, false);
                // set the view's size, margins, paddings and layout parameters
                return new MyViewHolder(binding);
        }

        // Replace the contents of a view (invoked by the layout manager)
        @Override
        public void onBindViewHolder(MyViewHolder holder, int position) {
                final TemperatureData temperatureData = data.get(position);
                holder.bind(temperatureData);

        }

        // Return the size of your dataset (invoked by the layout manager)
        @Override
        public int getItemCount() {
            return data.size();
        }

}

MyAdapter.java

public class MyAdapter extends MyBaseAdapter {

    List<TemperatureData> data;

    // Provide a suitable constructor (depends on the kind of dataset)
    public MyAdapter(List<TemperatureData> myDataset) {
        data = myDataset;
    }
    @Override
    public Object getDataAtPosition(int position) {
        return data.get(position);
    }

    @Override
    public int getLayoutIdForType(int viewType) {
        return R.layout.rowlayout;
    }

    @Override
    public int getItemCount() {
        return data.size();
    }
}


person Devrath    schedule 16.08.2017    source источник


Ответы (7)


Не уверен, что вы уже нашли решение, но мне удалось это сделать довольно легко.

1) измените метод onCreateViewHolder, чтобы он выглядел так:

@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    // create a new view
    LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
    ViewDataBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.rowlayout, parent, false);

    MainActivityPresenter presenter = new MainActivityPresenter(this, parent.getContext());
    binding.setVariable(BR.presenter,presenter);

    // set the view's size, margins, paddings and layout parameters
    return new MyViewHolder(binding);
}

2) сделать MyAdapter для реализации MainActivityContract.View, чтобы в итоге это выглядело следующим образом:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> implements MainActivityContract.View

3) Реализовать необходимые методы внутри MyAdapter; например:

@Override
public void showData(TemperatureData data) {
    String clickedItemCelsius = data.getCelsius();
}

4) Добавьте переменную Presenter в файл макета строки:

    <variable
        name="presenter"
        type="com.mvvm.ViewModels.MainActivityPresenter"/>

5) Наконец, подключите ваше событие onClick в RelativeLayout:

<RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:padding="6dip"
    android:onClick="@{() -> presenter.onShowData(obj)}"
    >

Надеюсь, поможет!

person Robert J.    schedule 05.11.2017
comment
Что такое MainActivityPresenter?? - person Shivam Kumar; 24.09.2019
comment
Это хороший способ сделать утечку памяти, viewHolder должен быть как можно более легким, потому что он будет воссоздан при прокрутке пользователем. Следует просто передать onClickListener в конструкторе средства просмотра - person David A; 26.09.2019
comment
Что такое MainActivityPresenter? Пожалуйста, предоставьте больше объяснений - person Dharmik Thakkar; 12.03.2020

наша модель представления, используемая в представлении переработчика

class UserViewModel (val name: String?, val onClick: () -> Unit)

макет для user_item.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<data>
    <variable
        name="model"
        type="...model.UserViewModel" />
</data>

         <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:clickable="true"
                android:focusable="true"
                android:onClick="@{()->model.onClick.invoke()}"
                android:text="@{model.name}" />
<merge>

создание моделей в Presenter или ModelView или где-то еще

fun loadData() {
   // ..
        val user = UserViewModel("name") { handleUserEvent() }

   .. //
 }

fun handleUserEvent() {
   // TODO handle on click
}
person Anet93    schedule 14.01.2019

Эй, я прочитал эту статью около недели назад и столкнулся с той же проблемой! В статье почти не упоминается, как должны обрабатываться действия, но есть документация о том, как это сделать. Короче говоря, вам понадобится handler.

Этот обработчик определен в вашем xml

<data>
    ...
    <variable name="handlers" type="com.example.MyHandlers"/>
    ...
</data>

пример использования

<TextView android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@{user.firstName}"
       android:onClick="@{handlers::onClickFriend}"/>

MyHandlers.java будет выглядеть так

public class MyHandlers {
    public void onClickFriend(View view) { ... }
}

Вы бы изменили добавление еще одной строки в свой MyAdapter.java

public class MyViewHolder extends RecyclerView.ViewHolder {
  public void bind(Object obj) {
                   binding.setVariable(BR.obj,obj);
                   binding.executePendingBindings();
                   binding.setHandlers(new MyHandlers());
            }

Я не тестировал этот код, но если это не сработает, я могу поделиться своим адаптером.

person Seabass77    schedule 16.08.2017
comment
Где я должен определить public class MyHandlers { public void onClickFriend(View view) { ... } } .... Это внутри Presenter (ViewModel) .... потому что мне нужно выполнить какое-то действие в модели представления на основе щелчка элемента - person Devrath; 17.08.2017
comment
Также я не могу установить обработчики - person Devrath; 17.08.2017
comment
Почему нельзя добавить обработчики? Проверьте, какое имя у переменной. В примере, который я написал, есть обработчики имен - person Seabass77; 17.08.2017
comment
@Devrath Просто создайте проект - person Dhruv Kaushal; 18.11.2018

Если другие предложения не работают так, как вы думаете, взгляните на обучение Google Codelab, в частности "Взаимодействие с элементами RecyclerView". Это часть серии, но если вы заинтересованы в обработке события щелчка в RecyclerView (с привязкой данных), вам просто нужно прочитать только вышеупомянутую главу.

Короче говоря, 1) создайте класс слушателя с помощью onClick(), 2) добавьте класс слушателя в качестве данных в XML-файл макета элемента списка и 3) используйте android:onClick="{...}" для сопоставления события щелчка элемента списка с слушателем.

Я уверен, что есть и другие способы достижения той же цели, но этот подход кажется довольно простым.

person solamour    schedule 27.11.2019

Вероятно, наиболее распространенным решением было бы поместить прослушиватель кликов в корневое представление макета строки и вызвать метод в вашей модели представления. Например, в rowlayout.xml:

...
<RelativeLayout
    android:onClick="@{() -> obj.performClickAction()}"
....
person Ackerbar    schedule 16.08.2017
comment
Да, я обычно так к этому отношусь. Тогда ваша проблема больше связана с отладкой, это должно сработать. Попробуйте установить clickable=true в RelativeLayout, кроме того, это просто вопрос отладки, чтобы найти то, что не вызывается. - person Ackerbar; 17.08.2017
comment
Я думаю, что это не работает в listView... работает в простых представлениях - person Devrath; 17.08.2017

Таким образом, мы можем использовать элемент, нажав на привязку данных

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.CustomView> {


    List<NewsModel> newsList;
    private LayoutInflater layoutInflater;


    public CustomAdapter(List<NewsModel> newsList)
    {
        this.newsList = newsList;
    }

    @Override
    public CustomView onCreateViewHolder(final ViewGroup parent, final int viewType) {


        if(layoutInflater == null)
        {
            layoutInflater = LayoutInflater.from(parent.getContext());
        }

        final NewsBinding newsBinding  = NewsBinding.inflate(layoutInflater,parent,false);

        newsBinding.setPresenter(new ClickListener() {
            @Override
            public void onclickListener() {

                Log.d("click me ","click me "+newsBinding.getNewsview().Title);
                Toast.makeText(parent.getContext(),""+newsBinding.getNewsview().Title,Toast.LENGTH_LONG).show();

            }
        });

      //  View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.innerlayout,parent,false);
       return new CustomView(newsBinding);
    }

    @Override
    public void onBindViewHolder(CustomView holder, int position) {

      //  News news = newsList.get(position);
       // holder.title.setText(news.getTitle());
       // holder.desc.setText(news.getDesc());

        NewsModel newsModel = newsList.get(position);
        holder.bind(newsModel);




    }

    @Override
    public int getItemCount() {
        return newsList.size();
    }

    public class CustomView extends RecyclerView.ViewHolder {

        private NewsBinding newsBinding;
       // TextView title, desc;
        public CustomView(NewsBinding newsBinding) {
            super(newsBinding.getRoot());

            this.newsBinding = newsBinding;
            //title = (TextView)itemView.findViewById(R.id.titleval);
            //desc =(TextView)itemView.findViewById(R.id.descval);

        }

        public void bind(NewsModel newsModel)
        {
            this.newsBinding.setNewsview(newsModel);
        }

        public NewsBinding getNewsBinding()
        {
            return newsBinding;
        }

    }
}

полный проект: https://github.com/Vishulucky/DataBinding-MVVM.git

person Vishal    schedule 08.03.2018

class MyAdapter() :
   RecyclerView.Adapter<MyAdapter.MyViewHolder> {



      inner class MyViewHolder(private val binding: ItemMyListBinding) :
           RecyclerView.ViewHolder(binding.root) {


             fun bind(yourItem: YourItem) {
                 binding.setVariable(BR.item, yourItem)
                 binding.executePendingBindings()
                 binding.animeListCard.setOnClickListener {
                                onItemClickListener?.let { click ->
                    click(yourItem)
                }

            }
        }


        }

  
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)

        val binding: ItemMyListBinding = DataBindingUtil.inflate(
            layoutInflater,
            R.layout.item_My_list,
            parent,
            false
        )

            return  MyViewHolder(binding)

    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {


       holder.bind(getItem(position))


    }

    
    //add these  
    private var onItemClickListener: (( YourItem) -> Unit)? = null

    fun setOnItemClickListener(listener: ( YourItem) -> Unit) {
        onItemClickListener = listener
    }


}

Макет R.layout.item_My_list:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="item"
            type="com.temp.example.data.MyItem" />

    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="8dp"
        android:orientation="vertical">

            <TextView
            android:id="@+id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{item.title}"
              />    


  

    </LinearLayout>

</layout>

затем в вашей деятельности:

myAdapter.setOnItemClickListener { item->
         
           //your code here 
           
       }
person Pranav Choudhary    schedule 18.01.2021