Detecting onClick in recycler view using data binding in android - android

I am Referring to vogella-tutorial for databinding
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();
}
}

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!

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
}

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()}"
....

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 .

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.

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

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
}

Related

Is there a way to implement data binding in same layout using more then one variable(different data types) for each view?

So my problem is this: I have 2 fragments(A and B) and each one has a recyclerview list. Both lists use the same card view. Fragment A list is using Pizza object to populate the list and Fragment B uses Drink object to populate the list. How should i implement data binding in the card view layout in order to have Pizzas in the first list and Drinks in the second list(swipe from fragment A to fragment B)?
<data>
<variable
name="foodItem"
type="model.Pizza" />
<variable
name="drinkItem"
type="model.Drink" />
</data>
<TextView
android:id="#+id/productName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#color/colorAccent"
android:textSize="#dimen/card_view_text_view_text_size"
android:textStyle="bold"
android:text="#{foodItem.name}" !!! here i want to use drinkItem if i swipe to drink list and foodItem if i swipe to Pizza list !!
/>
UPDATE
Adapter code:
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater=LayoutInflater.from(parent.getContext());
if (fragName.equals("Food") || fragName.equals("Drink")) {
RecyclerCardviewItemBinding view= DataBindingUtil.inflate(inflater,R.layout.recycler_cardview_item,parent,false);
return new MenuViewHolder(view, fragName, clickListener);
} else {
RecyclerCardviewItemBinding view= DataBindingUtil.inflate(inflater,R.layout.recycler_order_item,parent,false);
return new OrderViewHolder(view, iOrderClickListener);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) { //adds the data to the card views
if (fragName.equals("Food")) {
((MenuViewHolder) holder).bind(pizzaList.get(position),holder);
} else if (fragName.equals("Drink")) {
((MenuViewHolder) holder).bind(drinkList.get(position),holder);
} else if (fragName.equals("Order")) {
((OrderViewHolder) holder).bind(orderList.get(position),holder);
}
}
View Holder(i have 2 view holders)
public MenuViewHolder(#NonNull RecyclerCardviewItemBinding itemView, String fragName, IMenuClickListener iMenuClickListener) {
super(itemView.getRoot());
ButterKnife.bind(this, itemView.getRoot());
// cardView = itemView;
this.fragName = fragName;
this.clickListener = iMenuClickListener;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int position = getAdapterPosition();
clickListener.addButtonClick(position);
}
});
}
public void bind(Object item,RecyclerView.ViewHolder holder) {
if (item instanceof Pizza) {
Pizza pizza = (Pizza) item;
holder.itemView. //HOW SHOULD I BIND PIZZA ITEM TO LAYOUT HERE?
/*Glide.with(infoImage.getContext()).load(pizza.getImageLink()).error(R.mipmap.ic_launcher).into(infoImage);
cardView.setTag(pizza);
// productName.setText(pizza.getName());
productPrice.setText(pizza.getPrice().toString());*/
cardView.setOnClickListener(this);
} else {
Drink drink = (Drink) item;
/* Glide.with(infoImage.getContext()).load(drink.getImageLink()).error(R.mipmap.ic_launcher).into(infoImage);
cardView.setTag(drink);
// productName.setText(drink.getName());
productPrice.setText(drink.getPrice().toString());*/
cardView.setOnClickListener(this);
}
}
The easy way to do this is to make an interface (lets say FoodInterface)
<data>
<variable
name="item"
type="com.package.FoodInterface" />
</data>
<TextView
android:id="#+id/productName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#color/colorAccent"
android:textSize="#dimen/card_view_text_view_text_size"
android:textStyle="bold"
android:text="#{item.name}"
/>
And MyInterface might look like,
interface MyInterface{
fun foodName(): String
}
The models would be,
class Pizza: MyInterface{
fun foodName():String{
return "pizze name"
}
}
class Drink: MyInterface{
fun foodName():String{
return "drink name"
}
}
Finally, you will assign the interface (from your model) for binding.

Databinding Recyclerview and onClick

Ok I'll try one more time. Last time I asked about passing data between recyclerview and item and one person helped me with open item by click, but I still don't have idea how to show the data of clicked item in new activity. I want to click on an item and then display the data of that item in new activity. In this activity I want to edit data.
Does anyone knows how to do it? I need any idea.
RecyclerView Adapter with OnItemClickListener interface:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.TaskViewHolder> {
private List<MainViewModel> mTasks;
private List<Task> tasks = new ArrayList<>();
private Context context;
private EditTaskViewModel editTaskViewModel;
public RecyclerViewAdapter(List<MainViewModel> tasks, Context context, EditTaskViewModel editTaskViewModel) {
this.mTasks = tasks;
this.context = context;
this.editTaskViewModel = editTaskViewModel;
}
#NonNull
#Override
public RecyclerViewAdapter.TaskViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
final RecyclerViewItemBinding binding = DataBindingUtil.inflate(inflater, R.layout.recycler_view_item, parent, false);
binding.setItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(View view) {
Intent intent = new Intent(view.getContext(), EditTaskActivity.class);
intent.putExtra("id", binding.getPosition());
view.getContext().startActivity(intent);
Toast.makeText(view.getContext(), "ID " + binding.getPosition(), Toast.LENGTH_SHORT).show();
}
});
return new TaskViewHolder(binding);
}
#Override
public void onBindViewHolder(#NonNull final RecyclerViewAdapter.TaskViewHolder holder, final int position) {
Task currentTask = tasks.get(position);
holder.mBinding.descriptionItem.setText(currentTask.getDescription());
holder.mBinding.dateItem.setText(currentTask.getDate());
holder.mBinding.timeItem.setText(currentTask.getTime());
holder.mBinding.setPosition(position);
}
#Override
public int getItemCount() {
return tasks.size();
}
public void setTasks(List<Task> tasks) {
this.tasks = tasks;
notifyDataSetChanged();
}
public Task getTaskPosition(int position) {
return tasks.get(position);
}
public class TaskViewHolder extends RecyclerView.ViewHolder {
private final RecyclerViewItemBinding mBinding;
public TaskViewHolder(RecyclerViewItemBinding binding) {
super(binding.getRoot());
this.mBinding = binding;
}
public void bind (MainViewModel mainViewModel){
mBinding.setItemView(mainViewModel);
mBinding.executePendingBindings();
}
}
public interface OnItemClickListener {
void onItemClick(View view);
}
Item XML file:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="itemView"
type="com.example.daniellachacz.taskmvvm.viewmodel.MainViewModel">
</variable>
<variable
name="itemClickListener"
type="com.example.daniellachacz.taskmvvm.adapter.RecyclerViewAdapter.OnItemClickListener">
</variable>
<variable
name="task"
type="com.example.daniellachacz.taskmvvm.model.Task">
</variable>
<variable
name="position"
type="int">
</variable>
</data>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="120dp"
android:shadowColor="#color/colorPrimary"
android:backgroundTint="#color/cardview_shadow_end_color">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="110dp"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:onClick="#{(view)-> itemClickListener.onItemClick(view)}">
<TextView
android:id="#+id/description_item"
android:layout_width="250dp"
android:layout_height="96dp"
android:layout_marginStart="5dp"
android:layout_marginTop="9dp"
android:layout_marginBottom="5dp"
android:text="#{itemView.description}"
android:textSize="18sp"
android:textColor="#020202"
android:focusable="true" />
<TextView
android:id="#+id/date_item"
android:layout_width="90dp"
android:layout_height="40dp"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="9dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:text="#{itemView.date}"
android:textColor="#020202"
android:textSize="16sp" />
<TextView
android:id="#+id/time_item"
android:layout_width="90dp"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_alignStart="#+id/date_item"
android:layout_marginBottom="10dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:text="#{itemView.time}"
android:textColor="#020202"
android:textSize="16sp" />
</RelativeLayout>
</android.support.v7.widget.CardView>
</layout>
onCreate:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
floatingActionButton = findViewById(R.id.floating_action_button);
List<Task> tasks = new ArrayList<>();
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
final RecyclerViewAdapter recyclerViewAdapter = new RecyclerViewAdapter(context, tasks);
recyclerView.setAdapter(recyclerViewAdapter);
mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
mainViewModel.getAllTasks().observe(this, recyclerViewAdapter::setTasks);
Here are a couple of suggestions you might find useful:
Don't depend on ViewModels in your adapters. ViewModels are meant to handle events from views (Fragments or Activities) and broadcast updates back to the views via some observable mechanism (most commonly LiveData instances). Referencing your ViewModels directly inside an adapter is bad, since it couples them together. This means that it will be very hard for you to reuse your adapter with a different ViewModel if needed. I know it doesn't seem likely at this point in time, but just trust me on this one. After the changes have been applied, your adapter should look something like this:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.TaskViewHolder> {
private LayoutInflater mLayoutInflater;
private List<Task> mTasks;
private OnItemClickListener mOnItemClickListener;
public RecyclerViewAdapter(#NonNull Context context, #NonNull List<Task> tasks) {
mLayoutInflater = LayoutInflater.fromContext(context);
mTasks = tasks;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}
#NonNull
#Override
public RecyclerViewAdapter.TaskViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
final RecyclerViewItemBinding binding = DataBindingUtil.inflate(mLayoutInflater, R.layout.recycler_view_item, parent, false);
return new TaskViewHolder(binding);
}
#Override
public void onBindViewHolder(#NonNull final RecyclerViewAdapter.TaskViewHolder holder, final int position) {
Task currentTask = tasks.get(position);
holder.bind(currentTask, mOnItemClickListener);
}
#Override
public int getItemCount() {
return tasks.size();
}
public void setTasks(List<Task> tasks) {
this.tasks = tasks;
notifyDataSetChanged();
}
public Task getTaskPosition(int position) {
return tasks.get(position);
}
public class TaskViewHolder extends RecyclerView.ViewHolder {
private final RecyclerViewItemBinding mBinding;
public TaskViewHolder(RecyclerViewItemBinding binding) {
super(binding.getRoot());
this.mBinding = binding;
}
public void bind (Task item, OnItemClickListener onItemClickListener) {
mBinding.setItem(item);
mBinding.executePendingBindings();
itemView.setOnClickListener(view -> {
if (onItemClickListener != null) {
onItemClickListener.onItemClick(view, item);
}
}
}
}
public interface OnItemClickListener {
void onItemClick(View view, Task item);
}
}
The OnItemClickListener.onItemClick() method now passes the view and the item itself as parameters. This is the easiest way to expose the clicked item to whoever might be interested. The on click listener is now set at the adapter level, using setOnItemClickListener().
The setting of the OnClickListener of the item view is now done in the bind() method of the TaskViewHolder. When binding, we know the exact item that is going to populate the view, so we can return it to the OnItemClickListener.
You have to simplify the layout as well, since there are a lot of things that are not really needed. It may look like this:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="task"
type="com.example.daniellachacz.taskmvvm.model.Task">
</variable>
</data>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="120dp"
android:shadowColor="#color/colorPrimary"
android:backgroundTint="#color/cardview_shadow_end_color">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="110dp"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp">
<TextView
android:id="#+id/description_item"
android:layout_width="250dp"
android:layout_height="96dp"
android:layout_marginStart="5dp"
android:layout_marginTop="9dp"
android:layout_marginBottom="5dp"
android:text="#{item.description}"
android:textSize="18sp"
android:textColor="#020202"
android:focusable="true" />
<TextView
android:id="#+id/date_item"
android:layout_width="90dp"
android:layout_height="40dp"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="9dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:text="#{item.date}"
android:textColor="#020202"
android:textSize="16sp" />
<TextView
android:id="#+id/time_item"
android:layout_width="90dp"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_alignStart="#+id/date_item"
android:layout_marginBottom="10dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:text="#{item.time}"
android:textColor="#020202"
android:textSize="16sp" />
</RelativeLayout>
</android.support.v7.widget.CardView>
</layout>
The only variable is the item and we are binding it's properties to the TextViews.
I guess this should be enough to get you going.
Just a couple of other things that are not directly related to the question, but are important.
Null safety - you never check the input when calling setTask() in the adapter. A client may pass null and cause crashes all over the place. You should try and prevent that.
Calling notifyDataSetChanged() is not a good practice when working with RecyclerView.Adapter since this will cancel all the built-in animations of the RecyclerView. It's better to use the other notify... methods. You might want to check DiffUtil at some point.
It depends on how your data is stored and if it accessible in the second activity. If you have a static ArrayList of your data you can pull the data from it using the index you passed in your onclick of the RV item. For example:
class myData{
private ArrayList<Data> myDataArray;
static ArrayList<Data> getMyDataArray(){
return myDataArray;
}
static void setMyDataArray(array)
myDataArray = array;
}
So you fill your RV with getMyDataArray() then you set the onlclick to send the index clicked in the RV to the next activity.
In onLoad of the second activity:
int myDataIndex = getIntent().getIntExtra("id",0);
Data myData = myData.getMyDataArray().get(myDataIndex);
Note: Data is whatever your data is, could be strings, or ints, or a custom class/object with data.
Here is what I have done for item click listener in recycler view using data binding.
ADAPTER CODE
public class TC_DashboardRecViewAdapter extends RecyclerView.Adapter<TC_DashboardRecViewAdapter.ViewHolder> {
Context context;
List<String> list;
private TcDashboardItemBinding tcDashboardItemBinding;
ItemClickListener itemClickListener;
public TC_DashboardRecViewAdapter(Context context, List<String> dashboardItems, ItemClickListener itemClickListener) {
this.context = context;
this.list = dashboardItems;
this.itemClickListener = itemClickListener;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.tc_dashboard_item, parent, false);
tcDashboardItemBinding = (TcDashboardItemBinding) parent.getTag();
return new ViewHolder(binding);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.bind(list.get(position), itemClickListener, position);
}
#Override
public int getItemCount() {
return list.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
private ViewDataBinding binding;
public ViewHolder(ViewDataBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(String s, ItemClickListener itemClickListener, int position) {
this.binding.setVariable(BR.itemModel, s);
this.binding.executePendingBindings();
binding.getRoot().setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(itemClickListener !=null){
itemClickListener.onItemClicked(binding.getRoot(), s, position);
}
}
});
}
}
ITEM CLICK INTERFACE:
public interface ItemClickListener {
void onItemClicked(View vh, Object item, int pos);
}
FRAGMENT/ACTIVITY CODE:
public class TC_DashboardFragment extends BaseFragment implements ItemClickListener {
public void onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
tc_dashboardRecViewAdapter = new TC_DashboardRecViewAdapter(getContext(), getDashboardItems(), this);
linearLayoutManager = new LinearLayoutManager(getContext());
dashboardrecyclerview.setLayoutManager(new LinearLayoutManager(getContext()));
binding.setAdapter(tc_dashboardRecViewAdapter);
}
#Override
public void onItemClicked(View vh, Object item, int pos) {
Toast.makeText(mainActivity, item.toString(), Toast.LENGTH_SHORT).show();
}
When you run the code and click on recycler view item it would show a toast with the text from the item clicked.

Data binding: Why RecyclerView.Adapter onClick event is not fire?

Android Studio 3.0, Java 8.
dataBinding { enabled = true }
Here my RecyclerView adapter code (java file):
public class OfferSortAdapter extends RealmRecyclerViewAdapter<Offer, OfferSortAdapter.OfferViewHolder> {
#Override
public OfferSortAdapter.OfferViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
OfferItemBinding offerItemBinding = OfferItemBinding.inflate(inflater, parent, false);
return new OfferViewHolder(offerItemBinding.getRoot(), offerItemBinding);
}
#Override
public void onBindViewHolder(OfferSortAdapter.OfferViewHolder holder, final int position) {
Offer offer = getItem(position);
holder.offerItemBinding.setOffer(offer);
}
public static class OfferViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.imageViewPhoto)
ImageView imageViewPhoto;
OfferItemBinding offerItemBinding;
private OfferViewHolder(View view, OfferItemBinding offerItemBinding) {
super(view);
this.offerItemBinding = offerItemBinding;
ButterKnife.bind(this, view);
}
public void onClickImageViewPhoto(View view) {
// not call this method
}
}
}
Here xml layout file:
<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="handlers" type="myproject.OfferSortAdapter.OfferViewHolder" />
</data>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/imageViewPhoto"
android:layout_width="150dp"
android:layout_height="150dp"
android:onClick="#{handlers::onClickImageViewPhoto}"/>
</layout>
As you can see I set onClickHandler on xml layout: #{handlers::onClickImageViewPhoto}
But the method onClickImageViewPhoto not call when I click on ImageView.
set Handler object in your binding class.
OfferItemBinding offerItemBinding = OfferItemBinding.inflate(inflater, parent, false);
OfferViewHolder offerViewHoder = new OfferViewHolder(offerItemBinding.getRoot(), offerItemBinding);
offerItemBinding.setHandlers(offerViewHoder);
return offerViewHoder;
Suggestion.
There is no need to use ButterKnife as you can access your layout component like
binidng.image_view_photo
Just remember these fields are generated in snake case. imageViewPhoto will be converted to image_view_photo.

access RecyclerView.Adapter's method from ViewHolder or ViewModel of ViewHolder

I need to access a method of RecyclerView Adapter from View Holder. I don't find any solution for this.
Or is it possible to access adpater'method(get an instance of adapter) from ViewModel class (I've defined a viewModel for items of RecyclerView in MVVM pattern) of ViewHolder.
Regards,
Habib
Thanks to all, here is code snippets. in ItemViewModel class I want to get adapter's methods.
Adapter && ViewHolder :
public class MoviesAdapter extends RecyclerView.Adapter<MoviesAdapter.MyViewHolder> {
public class MyViewHolder extends RecyclerView.ViewHolder {
........
public MyViewHolder(View view) {
super(view);
......
}
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
return itemView;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
........
}
#Override
public int getItemCount() {
.........
}
}
list_item.xml :
<?xml version="1.0" encoding="utf-8"?>
<data>
<variable
name="viewModel"
type="program.viewmodel.ItemViewModel" />
</data>
<android.support.v7.widget.CardView
android:id="#+id/cvMain"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="#{viewModel.onClickCard()}"
android:onLongClick="#{viewModel.onLongClickCard()}" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingLeft="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/engM"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:paddingLeft="5dp"
android:text="#{viewModel.engligh}"
android:textSize="20sp" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
ItemViewModel class :
public class ItemViewModel{
private Proverb proverb; //Proverb is model class
public ItemViewModel(Proverb item) {
this.proverb = item;
notifyChange();
}
#Bindable
public String getEngligh() {
return proverb.getEngligh();
}
/**
* click each item
* #return
*/
public View.OnClickListener onClickProverb() {
return new View.OnClickListener() {
#Override
public void onClick(View view) {
// here I need to access adapter's method
}
};
}
/**
* long click listener
* #return
*/
public View.OnLongClickListener onLongClickProverb() {
return new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
// here I need to access adapter's method
return true;
}
};
}
}

Show RecyclerView in popup window

I have a RecyclerView, when RecyclerView item clicked, want to open a popup window which contains another RecyclerView. It is almost done, but in popup window, cardviews don't appear. I can't figure out why, can any one help?
1- My Main RecyclerView Adapter
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private ArrayList<Mission> mDataset;
private Context mContext;
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(ArrayList<Mission> myDataset, Context context) {
mDataset = myDataset;
this.mContext = context;
}
// Create new views (invoked by the layout manager)
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
View v = LayoutInflater.from(mContext)
.inflate(R.layout.mission_card_item, parent, false);
// set the view's size, margins, paddings and layout parameters
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.mTextView.setText(mDataset.get(position).getName());
holder.mPuanView.setText(mDataset.get(position).getPoint());
holder.mRankView.setText(mDataset.get(position).getRank());
holder.btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mContext,"Buton Clicked", Toast.LENGTH_SHORT).show();
}
});
}
#Override
public int getItemCount() {
return mDataset.size();
}
// 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 {
public CardView mCardView;
public TextView mTextView;
public TextView mPuanView;
public TextView mRankView;
public Button btnAdd;
public MyViewHolder(final View itemView) {
super(itemView);
mCardView = (CardView) itemView.findViewById(R.id.card_view);
mTextView = (TextView) itemView.findViewById(R.id.tv_text);
mRankView = (TextView) itemView.findViewById(R.id.tv_rank);
mPuanView = (TextView) itemView.findViewById(R.id.tv_puan);
btnAdd = (Button) itemView.findViewById(R.id.button_add);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showPopup();
Toast.makeText(itemView.getContext(),"Element " + getAdapterPosition() + " clicked", Toast.LENGTH_SHORT).show();
Log.d("hello", "Element " + getAdapterPosition() + " clicked.");
}
});
}
}
public void showPopup(){
final View popupView = LayoutInflater.from(mContext).inflate(R.layout.recycler_popup_window, null);
final PopupWindow popupWindow = new PopupWindow(popupView, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);
Button btn = (Button) popupView.findViewById(R.id.button);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
popupWindow.dismiss();
}
});
RecyclerView recyclerView = (RecyclerView) popupView.findViewById(R.id.rv_recycler_view);
ArrayList<String> data = new ArrayList<>();
data.add("my data");
data.add("my test data");
PopupRecyclerViewAdapter adapter = new PopupRecyclerViewAdapter(mContext,data);
recyclerView.setAdapter(adapter);
popupWindow.showAtLocation(popupView,Gravity.CENTER, 0, 0);
}
}
2- My second RecyclerView adapter, its for popup window
public class PopupRecyclerViewAdapter extends RecyclerView.Adapter<PopupRecyclerViewAdapter.MyViewHolder>{
private Context mContext;
private ArrayList<String> data;
public PopupRecyclerViewAdapter(Context mContext, ArrayList<String> data) {
this.mContext = mContext;
this.data = data;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(mContext).inflate(R.layout.recycler_popup_card_item, parent,false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.mTextView.setText(data.get(position));
}
#Override
public int getItemCount() {
return data.size();
}
//View Holder
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public MyViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.tv_text2);
}
}
}
3- Layout for Recycler popup window
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_recycler_view2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/button"
android:background="#ff4545">
</android.support.v7.widget.RecyclerView>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Close"
android:id="#+id/button"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
4- CardView Layout for popup RecyclerView
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<android.support.v7.widget.CardView
android:id="#+id/card_view"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_margin="10dp"
android:layout_height="wrap_content"
card_view:cardCornerRadius="4dp"
card_view:elevation="14dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:layout_width="match_parent"
android:layout_height="175dp"
android:id="#+id/imageView2"
android:src="#mipmap/testimage"
android:layout_marginBottom="10dp"
android:scaleType="centerCrop"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/tv_text2"
android:text="Blah blah blah..."
android:gravity="center"
android:layout_marginBottom="10dp"/>
</LinearLayout>
</android.support.v7.widget.CardView>
</RelativeLayout>
Add the below lines in your recyclerview popup:
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyler_view.setLayoutManager(mLayoutManager);
You may, like me, find this becomes rather a lot of boilerplate code just for a simple Popup Window with a list of clickable items, and so I made a custom Drop Down class which is easily reusable. You just need the PopupWindow custom class, and the RecyclerView adapter, as well as an item data class (in Kotlin), and this is then easily reusable.
The DropDown class:
class DropDown(context: Context, items: List<DropDownItem>, val listener: DropDownClickListener) : PopupWindow(context) {
private val binding = DropdownLayoutBinding.inflate(LayoutInflater.from(context))
init {
contentView = binding.root
setBackgroundDrawable(null)
elevation = 8f
isOutsideTouchable = true
isFocusable = true
binding.recyclerView.adapter = DropDownItemAdapter(items) { item -> onItemClicked(item) }
setOnDismissListener { listener.onMenuDismissed() }
}
private fun onItemClicked(item: DropDownItem) {
listener.onMenuItemClicked(item)
dismiss()
}
fun show(anchor: View) {
showAsDropDown(anchor, 0, 20)
}
interface DropDownClickListener {
fun onMenuItemClicked(item: DropDownItem)
fun onMenuDismissed()
}
}
data class DropDownItem(
val text: String,
val icon: Int
)
With accompanying layout code:
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/rounded_corners"
android:id="#+id/recycler_view"
xmlns:tools="http://schemas.android.com/tools"
tools:listitem="#layout/item_drop_down"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Explanation: This reusable new DropDown class uses ViewBinding of a simple layout that is simply a RecyclerView (with whatever Item layout you want, again mine is a reusable one called item_drop_down, a TextView with a Drawable.)
The constructor just needs three things, the context, the list of drop down items (I made a DropDownItem data class, which for simplicity I put in the same file), and then the listener - an interface also defined in this class).
In the init for this class, we set the content view, setBackgroundDrawable to null to get rid of an ugly border, give it some elevation, then make it dismissable by touching outside or pressing the back button. We then set the recyclerview adapter here from the list of items in the constructor. Finally, we set the on dismiss listener and defer to our interface.
Now the adapter code:
class DropDownItemAdapter(val items: List<DropDownItem>, val clickListener: (DropDownItem) -> Unit) : RecyclerView.Adapter<DropDownItemAdapter.ViewHolder>() {
override fun getItemCount() = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(ItemDropDownBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
inner class ViewHolder(val binding: ItemDropDownBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: DropDownItem) {
binding.textView.text = item.text
binding.textView.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(binding.root.context, item.icon), null, null, null)
itemView.setOnClickListener { clickListener(item) }
}
}
}
This is just a straight forward RecyclerView adapter, and rather than another interface, the only thing this needs to notify is when an item is clicked, so simply accept a function parameter in the constructor for handling the clicks.
Altogether, it looks like this in the calling code:
DropDown(view.context, dropDownMenu, this).show(view)
Where dropDownMenu is a list of DropDownItem.
You can then handle the interface functions as desired:
override fun onMenuItemClicked(item: DropDownItem) {
when (item.text) {
"Delete" -> // respond as desired
"Edit" -> // simply inspect the text, or alter your DropDownItem to have an ID
}
}
override fun onMenuDismissed() {
// it may be useful to know when the popup is dismissed
// in my case, I select the item which has been long pressed, and want to know when to un-select it
}
With a nice rounded corners background on my DropDown layout, and being able to know when popup showing and when dismissed to highlight my chosen item, I get this nice effect:

Categories

Resources