Devrath
Devrath

Reputation: 42824

Detecting onClick in recycler view using data binding in android

  1. I am Referring to vogella-tutorial for databinding
  2. What i am trying to do: What is the best way to detect onClick in recycler view row for each Item using the dataBinding

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();
    }
}

Upvotes: 16

Views: 28018

Answers (7)

Pranav Choudhary
Pranav Choudhary

Reputation: 2796

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:

<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>

then in your activity :

myAdapter.setOnItemClickListener { item->
         
           //your code here 
           
       }

Upvotes: 0

solamour
solamour

Reputation: 3214

If other suggestions don't seem to work the way you think it should, take a look at Google Codelab training, specifically "Interacting with RecyclerView items". It's a part of series, but if you are interested in handling the click event in RecyclerView (with Data Binding), you just need to read the aforementioned chapter only.

In short, 1) create a listener class with onClick(), 2) add listener class as data in list item's xml layout file, and 3) use android:onClick="{...}" to map the list item's click event to the listener.

I'm sure there are other ways to achieve the same goal, but this approach seems fairly straightforward.

Upvotes: 1

Anet93
Anet93

Reputation: 51

our viewModel used in recycler view

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

layout for 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>

creating of models in presenter or modelView or somewhere else

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

   .. //
 }

fun handleUserEvent() {
   // TODO handle on click
}

Upvotes: 2

Vishal
Vishal

Reputation: 478

This way we can use the item click on databinding

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;
        }

    }
}

complete project is https://github.com/Vishulucky/DataBinding-MVVM.git

Upvotes: 0

Robert J.
Robert J.

Reputation: 2701

Not sure if you have already found a solution, but I managed to do it quite easily.

1) modify onCreateViewHolder method to look like this:

@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) make MyAdapter to implement MainActivityContract.View so in the end it looks like following:

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

3) Implement necessary methods within MyAdapter; e.g:

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

4) Add Presenter variable to your row layout file:

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

5) Finally hook your onClick event under RelativeLayout:

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

Hope it helps!

Upvotes: 9

Seabass77
Seabass77

Reputation: 187

Hey I read that article about a week ago and had the same problem! The article barely mentions how actions should be handled but there is documentation on how to do it. In short, you are going to want a handler.

This handler is defined in your xml

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

example usage

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

The MyHandlers.java would look like this

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

You would change the add one more line to your MyAdapter.java

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

I haven't tested this code but if this doesn't work I can share my adapter .

Upvotes: 1

Ackerbar
Ackerbar

Reputation: 109

Probably the most common solution would be to put a click listener on the row layout's root view and call a method on your view model. For example in rowlayout.xml:

...
<RelativeLayout
    android:onClick="@{() -> obj.performClickAction()}"
....

Upvotes: 1

Related Questions