implementing interface in android LinearLayout class - RecycleViewGenericAdapter [duplicate] - java

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 1 year ago.
I have a generic RecycleView for which I have different row classes, in this row class I would like to implement an interface, my objective is to pass the text from OnQueryTextListener to an activity or fragment where the interface is implemented.
public class PlantDocHeader extends LinearLayout implements RecycleViewGenericAdapter.RecyclerViewRowHeader<PlantDocViewModel> {
private Button buttonQuestion;
private Button buttonPosts;
private TextView searchTxtField;
private ImageView imageViewExpandSearch;
private SearchView searchView;
private boolean expand = true;
SearchView.OnQueryTextListener onQueryTextListener;
private onTextChange onTextChange;
Resources res;
public PlantDocHeader(Context context) {
super(context);
}
public PlantDocHeader(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public PlantDocHeader(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
searchTxtField = findViewById(R.id.textView_plant_doc_header_search);
imageViewExpandSearch = findViewById(R.id.imageView_plant_doc_header_expand_button);
searchView = findViewById(R.id.searchView_plant_doc_header);
buttonQuestion = findViewById(R.id.button_plant_doc_header_filter_my_posts);
buttonPosts = findViewById(R.id.button_plant_doc_header_filter_question);
}
#Override
public void showData(PlantDocViewModel item) {
res = getResources();
buttonQuestion.setText(res.getString(R.string.question));
buttonQuestion.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
AskQuestionFragment askQuestionFragment = new AskQuestionFragment();
ChangeFragment(askQuestionFragment, (Activity) getContext(), false);
}
});
buttonPosts.setText(res.getString(R.string.my_posts));
buttonPosts.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
PlantDocMyPostsFragment myPostsFragment = new PlantDocMyPostsFragment();
ChangeFragment(myPostsFragment, (Activity) getContext(), false);
}
});
searchTxtField.setText(res.getString(R.string.search));
imageViewExpandSearch.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(expand){
searchView.setVisibility(VISIBLE);
imageViewExpandSearch.setImageResource(R.drawable.collapse);
expand = false;
}else{
searchView.setVisibility(GONE);
imageViewExpandSearch.setImageResource(R.drawable.expand);
expand = true;
}
}
});
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
Toast.makeText(getContext(), ""+newText, Toast.LENGTH_SHORT).show();
onTextChange.textSearch(newText);
return true;
}
});
}
public interface onTextChange {
void textSearch(String searchText);
}
}
This is the error I am getting
Attempt to invoke interface method 'void
com.gardify.android.UI.PlantDoc.PlantDocHeader$onTextChange.textSearch(java.lang.String)'
on a null object reference
RecycleViewGenericAdapter
/**
* #param <T> is generic parameter type provided to List, OnRecyclerViewItemClickListener and RecyclerViewRow
* #param <V> generic type for header view Model
* #param <E> generic type for footer view Model
*/
public class RecycleViewGenericAdapter<T, V, E> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<T> mDataset;
private V mHeaderData;
private E mFooterData;
private static final int TYPE_HEADER = 000;
private static final int TYPE_ITEM = 111;
private static final int TYPE_FOOTER = 222;
private OnItemClickListener<T> onItemClickListener;
private OnItemClickListenerHeader<V> onItemClickListenerHeader;
private OnItemClickListenerFooter<E> onItemClickListenerFooter;
private int layoutId, layoutIdHeader, layoutIdFooter;
public RecycleViewGenericAdapter(List<T> mDataset, int layoutId, V mHeaderData, int layoutIdHeader,
E mFooterData, int layoutIdFooter, OnItemClickListener<T> onItemClickListener,
OnItemClickListenerHeader onItemClickListenerHeader, OnItemClickListenerFooter onItemClickListenerFooter) {
this.onItemClickListener = onItemClickListener;
this.onItemClickListenerHeader = onItemClickListenerHeader;
this.onItemClickListenerFooter = onItemClickListenerFooter;
this.mDataset = mDataset;
this.mHeaderData = mHeaderData;
this.mFooterData = mFooterData;
this.layoutId = layoutId;
this.layoutIdHeader = layoutIdHeader;
this.layoutIdFooter = layoutIdFooter;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder vh = null;
if (viewType == TYPE_ITEM) {
RecyclerViewRow<T> row = (RecyclerViewRow<T>) LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
vh = new ItemViewHolder(row);
}else if (viewType == TYPE_HEADER) {
RecyclerViewRowHeader<V> rowHeader = (RecyclerViewRowHeader<V>) LayoutInflater.from(parent.getContext()).inflate(layoutIdHeader, parent, false);
vh = new HeaderViewHolder(rowHeader);
} else if(viewType == TYPE_FOOTER) {
RecyclerViewRowFooter<E> footerRow = (RecyclerViewRowFooter<E>) LayoutInflater.from(parent.getContext()).inflate(layoutIdFooter, parent, false);
vh = new FooterViewHolder(footerRow);
}
return vh;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof RecycleViewGenericAdapter.ItemViewHolder) {
ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
itemViewHolder.onBind((mHeaderData!=null? position-1 : position), itemViewHolder);
} else if (holder instanceof RecycleViewGenericAdapter.HeaderViewHolder) {
HeaderViewHolder headerViewHolder = (HeaderViewHolder) holder;
headerViewHolder.onBind(headerViewHolder);
} else if (holder instanceof RecycleViewGenericAdapter.FooterViewHolder) {
FooterViewHolder footerViewHolder = (FooterViewHolder) holder;
footerViewHolder.onBind(footerViewHolder);
}
}
#Override
public int getItemCount() {
if (mHeaderData != null && mFooterData != null) {
return mDataset.size() + 2;
} else if (mHeaderData != null || mFooterData != null) {
return mDataset.size() + 1;
}
return mDataset.size();
}
private int getLastPosition() {
return getItemCount() - 1;
}
private boolean isLastPosition(int position) {
return position == getLastPosition();
}
#Override
public int getItemViewType(int position) {
if (position == 0 && mHeaderData!=null) {
return TYPE_HEADER;
} else if (isLastPosition(position) && mFooterData!=null) {
return TYPE_FOOTER;
} else {
return TYPE_ITEM;
}
}
/**
* Item ViewHolder
**/
public class ItemViewHolder extends RecyclerView.ViewHolder {
public RecyclerViewRow<T> mRow;
public ItemViewHolder(RecyclerViewRow<T> itemView) {
super((View) itemView);
mRow = itemView;
}
private void onBind(final int position, ItemViewHolder viewHolder) {
viewHolder.mRow.showData(mDataset.get(position));
((View) viewHolder.mRow).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (onItemClickListener != null) {
onItemClickListener.onItemClick(mDataset.get(position));
}
}
});
}
}
public interface RecyclerViewRow<T> {
void showData(T item);
}
public interface OnItemClickListener<T> {
void onItemClick(T position);
}
public void updateList(List<T> _mDataset){
this.mDataset= _mDataset;
notifyDataSetChanged();
}
/**
* Header ViewHolder
**/
public class HeaderViewHolder extends RecyclerView.ViewHolder {
public RecyclerViewRowHeader<V> mRowHeader;
public HeaderViewHolder(RecyclerViewRowHeader<V> itemView) {
super((View) itemView);
mRowHeader = itemView;
}
private void onBind(HeaderViewHolder viewHolder) {
viewHolder.mRowHeader.showData(mHeaderData);
((View) viewHolder.mRowHeader).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (onItemClickListenerHeader != null) {
onItemClickListenerHeader.onItemClickHeader(mHeaderData);
}
}
});
}
}
public interface RecyclerViewRowHeader<V> {
void showData(V item);
}
public interface OnItemClickListenerHeader<V> {
void onItemClickHeader(V position);
}
/**
* Footer ViewHolder
**/
public class FooterViewHolder extends RecyclerView.ViewHolder {
public RecyclerViewRowFooter<E> mRowFooter;
public FooterViewHolder(RecyclerViewRowFooter<E> itemView) {
super((View) itemView);
mRowFooter = itemView;
}
private void onBind(FooterViewHolder viewHolder) {
viewHolder.mRowFooter.showData(mFooterData);
((View) viewHolder.mRowFooter).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (onItemClickListenerFooter != null) {
onItemClickListenerFooter.onItemClickFooter(mFooterData);
}
}
});
}
}
public interface RecyclerViewRowFooter<E> {
void showData(E item);
}
public interface OnItemClickListenerFooter<E> {
void onItemClickFooter(E position);
}
}
Usage in Fragment
public class PlantDocFragment extends Fragment implements RecycleViewGenericAdapter.OnItemClickListener<PlantDocViewModel>, PlantDocHeader.OnTextChangeListener {
//...
RecycleViewGenericAdapter<PlantDocViewModel, PlantDocViewModel, Nullable> adapter = new RecycleViewGenericAdapter<>(plantDocList, R.layout.recycler_view_plant_doc_row_item, plantDocHeader, R.layout.recycler_view_plant_doc_header,
null, 0, this, null, null);
//...
#Override
public void textSearch(String searchText) {
Toast.makeText(getContext(), "searched text : "+searchText, Toast.LENGTH_SHORT).show();
}
the generic recycleview that i am using can be found here

Basically you need to pass fragments reference to the HeaderView .
For this first of all you have to have a setter in PlantDocHeader as below . I have removed the interface also it seems useless.
public class PlantDocHeader<T> extends LinearLayout {
private OnTextChangeListener onTextChangeListener;
public void setOnTextChangeListener(OnTextChangeListener onTextChangeListener) {
this.onTextChangeListener = onTextChangeListener;
}
public void setData(T data) {
}
}
Rephrase your Adapters constructor a bit its taking a lots of argument a Builder will probably help or you can create separate setters for optional properties.
public class RecycleViewGenericAdapter<T, V, E> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private OnItemClickListener<T> onItemClickListener;
private OnItemClickListenerHeader<V> onItemClickListenerHeader;
private OnItemClickListenerFooter<E> onItemClickListenerFooter;
private PlantDocHeader.OnTextChangeListener onTextChangeListener;
private int layoutId, layoutIdHeader, layoutIdFooter;
public RecycleViewGenericAdapter(List<T> mDataset, int layoutId, V mHeaderData, int layoutIdHeader,
E mFooterData, int layoutIdFooter) {
this.mDataset = mDataset;
this.mHeaderData = mHeaderData;
this.mFooterData = mFooterData;
this.layoutId = layoutId;
this.layoutIdHeader = layoutIdHeader;
this.layoutIdFooter = layoutIdFooter;
}
}
Create separate setters for all listeners and set them as they needed from the calling component. Now when you done setting you need to pass onTextChangeListener to header view.
public class HeaderViewHolder extends RecyclerView.ViewHolder {
public RecyclerViewRowHeader<V> mRowHeader;
public HeaderViewHolder(RecyclerViewRowHeader<V> itemView) {
super((View) itemView);
mRowHeader = itemView;
}
private void onBind(HeaderViewHolder viewHolder) {
viewHolder.mRowHeader.setOnTextChangeListener(onTextChangeListener);
viewHolder.mRowHeader.showData(mHeaderData);
((View) viewHolder.mRowHeader).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (onItemClickListenerHeader != null) {
onItemClickListenerHeader.onItemClickHeader(mHeaderData);
}
}
});
}
}
Now it should work . also you might wanna consider passing position between listener because there can be multiple headers also if not then it will be 0 always.
PS- If you are trying to create a generic adapter you should consider creating a abstract Adapter class right now your class does not seems generic because you are passing way too many arguments in constructor which should in child class only its not flexible at all. You might wanna check some open source generic adapter for this to see .

You have not told what to do when I call textSearch() method.
You have to implement onTextChange interface (Please follow Java naming convention when declaring interface/class/variables) in some class where you will override textSearch() method and then pass the reference (implemented class) to the variable onTextChange (you have given same name of interface and instance variable so to avoid confusion you can give another name to variable).
Then when you call textSearch() in this line onTextChange.textSearch(newText); you will have implementation. And your code will work.

In your adapter create a public method:
public void setOnTextChange(onTextChange onTextChange) {
this.onTextChange = onTextChange;
}
In your fragment, change your implements to implement your interface:
implements PlantDocHeader.OnTextChange
Then set the onTextChange:
adapter.setOnTextChange(this);

Related

How to make RecyclerView Multiple ViewTypes With Retrofit and ViewModel

Recently I have created RecyclerView with Multiple ViewTypes followed this video. In the video the lecturer followed the traditional method of sending data using ArrayList In MainAvtivity. But when I used ViewModel and Retrofit it is not passing any data and nothing show in the screen.
And here an expiation of what I have done.
First:
I have 3 diffrents types of views so I created a 3 model classes for each view called (FirstImageModel,LiveModel,DailyImageModel).
FirstImageModel class -- All other 2 classes have same stretcher.
public class FirstImageModel {
#SerializedName("firstImageUrl")
private String firstImageUrl;
public FirstImageModel(String firstImageUrl) {
this.firstImageUrl = firstImageUrl;
}
public String getFirstImageUrl() {
return firstImageUrl;
}
public void setFirstImageUrl(String firstImageUrl) {
this.firstImageUrl = firstImageUrl;
}
and I have created a fourth class called ItemModel that have type int and Object object.
public class ItemModel {
private int type;
private Object object;
public ItemModel(int type) {
this.type = type;
this.object = object;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
}
Second:
I have created Adapter class called HomeAdapter that extends from RecyclerView and I made 3 classes for eatch view inside the same class HomeAdapter.
public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<ItemModel> items;
public HomeAdapter(List<ItemModel> items) {
this.items = items;
}
public void setHomeList(List<ItemModel> items) {
this.items = items;
notifyDataSetChanged();
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
// Here is types are: 0-FirstImage, 1-Live, 2-DailyImage
if (viewType == 0) {
return new FirstImageViewHolder(
LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_home_container_firstimage,
parent,
false
)
);
} else if (viewType == 1) {
return new LiveViewHolder(
LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_home_container_live,
parent,
false
)
);
} else {
return new DailyImageViewHolder(
LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_home_container_dailyimage,
parent,
false
)
);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
if (getItemViewType(position) == 0) {
FirstImageModel firstImageModel = (FirstImageModel) items.get(position).getObject();
((FirstImageViewHolder) holder).setFirstImageData(firstImageModel);
} else if (getItemViewType(position) == 1) {
LiveModel liveModel = (LiveModel) items.get(position).getObject();
((LiveViewHolder) holder).setLiveImageData(liveModel);
} else {
DailyImageModel dailyImageModel = (DailyImageModel) items.get(position).getObject();
((DailyImageViewHolder) holder).setDailyImageData(dailyImageModel);
}
}
#Override
public int getItemCount() {
if(this.items != null) {
return this.items.size();
}
return 0;
}
#Override
public int getItemViewType(int position) {
return items.get(position).getType();
}
/* firstImage Adapter */
static class FirstImageViewHolder extends RecyclerView.ViewHolder {
private ImageView firstImage;
FirstImageViewHolder(#NonNull View itemView) {
super(itemView);
firstImage = itemView.findViewById(R.id.image_home_firstImage);
}
void setFirstImageData(FirstImageModel firstImageModel) {
Glide.with(itemView.getContext())
.load(firstImageModel.getFirstImageUrl())
.into(firstImage);
}
}
/* Live Adapter */
static class LiveViewHolder extends RecyclerView.ViewHolder {
private ImageView liveImage;
LiveViewHolder(#NonNull View itemView) {
super(itemView);
liveImage = itemView.findViewById(R.id.image_home_liveImage);
}
void setLiveImageData(LiveModel liveModel) {
Glide.with(itemView.getContext())
.load(liveModel.getImageLiveInfoUrl())
.into(liveImage);
}
}
/* DailyImage Adapter */
static class DailyImageViewHolder extends RecyclerView.ViewHolder {
private ImageView dailyImage;
DailyImageViewHolder(#NonNull View itemView) {
super(itemView);
dailyImage = itemView.findViewById(R.id.image_home_dailyImage);
}
void setDailyImageData(DailyImageModel dailyImageModel) {
Glide.with(itemView.getContext())
.load(dailyImageModel.getDailyImageUrl())
.into(dailyImage);
}
}
My Problem:
When I use Retrofit to load the data, I don't Know how to pass the 3 views types data to the RecyclerView using ViewModel. My ViewModel class.
public class HomeViewModel extends ViewModel {
private MutableLiveData<List<ItemModel>> homeObjectsList;
private Call<List<ItemModel>> call;
public HomeViewModel(){
homeObjectsList = new MutableLiveData<>();
}
public MutableLiveData<List<ItemModel>> getHomeItemsListObserver() {
return homeObjectsList;
}
public void makeApiCallHome() {
APIServiceHome apiServiceHome = RetroInstanceHome.getRetroClientHome().create(APIServiceHome.class);
call = apiServiceHome.getHomeObjectsList();
call.enqueue(new Callback<List<ItemModel>>() {
#Override
public void onResponse(Call<List<ItemModel>> call, Response<List<ItemModel>> response) {
homeObjectsList.postValue(response.body());
}
#Override
public void onFailure(Call<List<ItemModel>> call, Throwable t) {
homeObjectsList.postValue(null);
}
});
}
Also here's my fragment that should dislay the data in RecyclerView.
public class HomeFragment extends Fragment {
View rootView;
RecyclerView recyclerView;
private List<ItemModel> itemModelList;
private HomeViewModel viewModel;
private HomeAdapter adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_home, container, false);
initializeContent();
initializeViewModel();
return rootView;
}
private void initializeContent() {
recyclerView = rootView.findViewById(R.id.recyclerView_home);
recyclerView.setAdapter(new HomeAdapter(itemModelList));
}
private void initializeViewModel() {
ProgressBar progressBar = rootView.findViewById(R.id.progress_bar_home);
viewModel = ViewModelProviders.of(this).get(HomeViewModel.class);
viewModel.getHomeItemsListObserver().observe(getActivity(), new Observer<List<ItemModel>>() {
#Override
public void onChanged(List<ItemModel> itemModels) {
if(itemModels != null) {
progressBar.setVisibility(View.GONE);
itemModelList = itemModels;
adapter.setHomeList(itemModels);
}
}
});
viewModel.makeApiCallHome();
}
I hope this explanation was clear to show the problem. Waiting for your answer

How to pass information from one adapter to other?

i have two adapters one for my card view and the other one for my drawer. The cards contains links to other options in the app and the drawer contains the same options. What i want is when user clicks on the card, the options in the drawer menu should get selected. Here is my code.
Drawer Adapter:
#SuppressWarnings({"rawtypes", "ConstantConditions"})
public class DrawerAdapter extends RecyclerView.Adapter<DrawerAdapter.ViewHolder> {
private List<DrawerItem> items;
private Map<Class<? extends DrawerItem>, Integer> viewTypes;
private SparseArray<DrawerItem> holderFactories;
private OnItemSelectedListener listener;
public DrawerAdapter(List<DrawerItem> items) {
this.items = items;
this.viewTypes = new HashMap<>();
this.holderFactories = new SparseArray<>();
processViewTypes();
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
ViewHolder holder = holderFactories.get(viewType).createViewHolder(parent);
holder.adapter = this;
return holder;
}
#Override
#SuppressWarnings("unchecked")
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
items.get(position).bindViewHolder(holder);
}
#Override
public int getItemCount() {
return items.size();
}
#Override
public int getItemViewType(int position) {
return viewTypes.get(items.get(position).getClass());
}
private void processViewTypes() {
int type = 0;
for (DrawerItem item : items) {
if (!viewTypes.containsKey(item.getClass())) {
viewTypes.put(item.getClass(), type);
holderFactories.put(type, item);
type++;
}
}
}
public void setSelected(int position) {
DrawerItem newChecked = items.get(position);
if (!newChecked.isSelectable()) {
return;
}
for (int i = 0; i < items.size(); i++) {
DrawerItem item = items.get(i);
if (item.isChecked()) {
item.setChecked(false);
notifyItemChanged(i);
break;
}
}
newChecked.setChecked(true);
notifyItemChanged(position);
if (listener != null) {
listener.onItemSelected(position);
}
}
public void setListener(OnItemSelectedListener listener) {
this.listener = listener;
}
static abstract class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private DrawerAdapter adapter;
public ViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
adapter.setSelected(getAdapterPosition());
}
}
public interface OnItemSelectedListener {
void onItemSelected(int position);
}
}
Card Adapter:
public class CardAdapter extends RecyclerView.Adapter<CardAdapter.ViewHolder> {
;
private RecyclerView parentRecycler;
private List<Card> data;
public CardAdapter(List<Card> data) {
this.data = data;
}
#Override
public void onAttachedToRecyclerView(#NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
parentRecycler = recyclerView;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View v = inflater.inflate(R.layout.item_card, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Card card = data.get(position);
holder.getAdapterPosition();
Glide.with(holder.itemView.getContext())
.asGif()
.load(card.getCardIcon())
.into(holder.gifImageView);
holder.textView.setText(card.getCardName());
}
#Override
public int getItemCount() {
return data.size();
}
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private GifImageView gifImageView;
private TextView textView;
public ViewHolder(View itemView) {
super(itemView);
gifImageView = itemView.findViewById(R.id.card_gif);
textView = itemView.findViewById(R.id.card_name);
itemView.findViewById(R.id.container).setOnClickListener(this);
}
public void showText() {
int parentHeight = ((View) gifImageView.getParent()).getHeight();
float scale = (parentHeight - textView.getHeight()) / (float) gifImageView.getHeight();
gifImageView.setPivotX(gifImageView.getWidth() * 0.5f);
gifImageView.setPivotY(0);
gifImageView.animate().scaleX(scale)
.withEndAction(new Runnable() {
#Override
public void run() {
textView.setVisibility(View.VISIBLE);
// gifImageView.setColorFilter(Color.BLACK);
}
})
.scaleY(scale).setDuration(200)
.start();
}
public void hideText() {
textView.setVisibility(View.INVISIBLE);
gifImageView.animate().scaleX(1f).scaleY(1f)
.setDuration(200)
.start();
}
#Override
public void onClick(View v) { parentRecycler.smoothScrollToPosition(getAdapterPosition());
}
}
}
}
P.S: I am new to programming any kind of help is appreciated.[As you can see in the image below that I have cards in the main view and the same options are in the drawer menu for example question papers. Now when the user clicks on question paper in the main view the question paper in drawer menu should get selected.[][1]][1]
thank you!
[1]: https://i.stack.imgur.com/FooVb.jpg

How to manage multiple positions in RecyclerView OnClickListener

I want to know how I can get access to other elements of the array in this onclicklistener for a Recycler view. Specifically, I want to be able to change the enabled state of a position other than the current Listener position, but within the click event.
I'm trying to make it such that if three colors are checked (those are check boxes) every box not checked will be disabled until the number of boxes checked is < 3.
The for-loops I have I wrote make sense, but I can't programmatically change the enabled state within the onClickListener for some reason.
private void buildRecyclerView() {
mRecyclerView = findViewById(R.id.recyclerView);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
mAdapter = new ExampleAdapter(mExampleList);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
mAdapter.setOnItemClickListener(new ExampleAdapter.OnItemClickListener() {
#Override
public void onItemClick(int position) {
boolean checkedState;
if (mExampleList.get(position).getChecked1() == false) {
checkedState = true;
} else {
checkedState = false;
}
changeItem(position, checkedState);
int sum = 0;
for (int i = 0; i < stringArray.length; i++) {
Boolean checked = mExampleList.get(i).getChecked1();
if (checked == true) {
sum = sum + 1;
}
}
for (int i = 0; i < stringArray.length; i++) {
Boolean checked = mExampleList.get(i).getChecked1();
if (!checked && sum == 3) {
mExampleList.get(i).setEnabled1(false);
} else {
mExampleList.get(i).setEnabled1(true);
}
}
}
});
}
adapter
public class ExampleAdapter extends RecyclerView.Adapter<ExampleAdapter.ExampleViewHolder> {
private ArrayList<ExampleItem> mExampleList;
private OnItemClickListener mListener;
public interface OnItemClickListener {
void onItemClick(int position);
}
public void setOnItemClickListener(OnItemClickListener listener) {
mListener = listener;
}
public static class ExampleViewHolder extends RecyclerView.ViewHolder {
public CheckBox mCheckBox;
public ExampleViewHolder(#NonNull View itemView, final OnItemClickListener listener) {
super(itemView);
mCheckBox = itemView.findViewById(R.id.checkBox);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null){
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION);
listener.onItemClick(position);
}
}
});
}
}
public ExampleAdapter(ArrayList<ExampleItem> exampleList) {
mExampleList = exampleList;
}
#NonNull
#Override
public ExampleViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.example_item, parent, false);
ExampleViewHolder evh = new ExampleViewHolder(v, mListener);
return evh;
}
#Override
public void onBindViewHolder(#NonNull ExampleViewHolder holder, int position) {
ExampleItem currentItem = mExampleList.get(position);
holder.mCheckBox.setText(currentItem.getCheckText());
holder.mCheckBox.setEnabled(currentItem.getEnabled1());
holder.mCheckBox.setChecked(currentItem.getChecked1());
}
#Override
public int getItemCount() {
return mExampleList.size();
}
}
Item Class
public class ExampleItem {
public String mCheckText;
public Boolean mEnabled;
public Boolean mChecked;
public ExampleItem(String mCheckText, Boolean mEnabled, Boolean mChecked) {
this.mCheckText = mCheckText;
this.mEnabled = mEnabled;
this.mChecked = mChecked;
}
public ExampleItem(String mCheckText) {
this.mCheckText = mCheckText;
}
public String getCheckText() {
return mCheckText;
}
public void setCheckText(String mCheckText) {
this.mCheckText = mCheckText;
}
public Boolean getEnabled1() {
return mEnabled;
}
public void setEnabled1(Boolean mEnabled) {
this.mEnabled = mEnabled;
}
public Boolean getChecked1() {
return mChecked;
}
public void setChecked1(Boolean mChecked) {
this.mChecked = mChecked;
}
}
In other words, I am trying to make everything below blue disabled until I uncheck Red, Green, or Blue!
Try the following:
In adapter, add below codes:
private int selectedCount = 0;
public int getSelectedCount() {
return selectedCount;
}
public void setSelectedCount(int selectedCount) {
this.selectedCount = selectedCount;
notifyDataSetChanged();
}
and change onBindViewHolder() to:
#Override
public void onBindViewHolder(#NonNull ExampleViewHolder holder, int position) {
ExampleItem currentItem = mExampleList.get(position);
holder.mCheckBox.setText(currentItem.getCheckText());
holder.mCheckBox.setChecked(currentItem.getChecked1());
if ((selectedCount == 3)) holder.mCheckBox.setEnabled(currentItem.getChecked1());
else holder.mCheckBox.setEnabled(true);
}
Then in Activity/Fragment, change mAdapter.setOnItemClickListener(new ExampleAdapter.OnItemClickListener()... to:
mAdapter.setOnItemClickListener(new ExampleAdapter.OnItemClickListener() {
#Override
public void onItemClick(int position) {
boolean checkedState;
int selectedCount = mAdapter.getSelectedCount();
if ((selectedCount != 3) || (mExampleList.get(position).getChecked1())) {
if (mExampleList.get(position).getChecked1() == false) {
checkedState = true;
selectedCount++;
} else {
checkedState = false;
selectedCount--;
}
changeItem(position, checkedState);
mAdapter.setSelectedCount(selectedCount);
}
}
});
Pls note that no need to have mEnabled field in the ExampleItem class. Hope that helps!
Explanantion:
About the onClick:- This is because CheckBox totally covered the layout, so itemView cannot recieve the click event. About RecyclerView:- The idea is simple, after modified the data set [not list, here I refer to the mExampleList and also the selectedCount], then call notifyDataSetChanged() which will redraw all recycler item views.
You would need to create a setter function for your 'mExampleList' in your Adpater class, to update the dataset that the adapter is using. It obviously does not change anything, if you make changes to 'mExampleList' in your activity, without updating the list used by the Adapter.
So at the end of your click listener you would have something like that:
mAdapter.setExampleList(mExampleList)
mAdapter.notifyDataSetChanged()

How To make RecyclerView handle onclicklistener [duplicate]

This question already has answers here:
RecyclerView onClick
(49 answers)
Closed 7 years ago.
Has anyone using RecyclerView found a way to set an onClickListener to items in the RecyclerView?
this my code:
Pasal_Bab.java
public class Pasal_Bab extends Fragment implements SearchView.OnQueryTextListener {
private RecyclerViewEmptySupport rv;
private List<PasalBabModel> mPBH;
private PasalBabAdapter adapter;
private static ArrayList<PasalBabModel> people;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_pasal, container, false);
rv = (RecyclerViewEmptySupport) view.findViewById(R.id.rv_pasal);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
rv.setLayoutManager(layoutManager);
rv.setEmptyView(view.findViewById(R.id.empty));
rv.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setHasOptionsMenu(true);
String[] locales = Locale.getISOCountries();
mPBH = new ArrayList<>();
for (String countryCode : locales) {
Locale obj = new Locale("Pasalxyz", countryCode);
mPBH.add(new PasalBabModel(obj.getDisplayCountry(), obj.getISO3Country()));
}
adapter = new PasalBabAdapter(mPBH);
rv.setAdapter(adapter);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_main, menu);
final MenuItem item = menu.findItem(R.id.action_search);
final SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
searchView.setOnQueryTextListener(this);
MenuItemCompat.setOnActionExpandListener(item,
new MenuItemCompat.OnActionExpandListener() {
#Override
public boolean onMenuItemActionCollapse(MenuItem item) {
// Do something when collapsed
adapter.setFilter(mPBH);
return true; // Return true to collapse action view
}
#Override
public boolean onMenuItemActionExpand(MenuItem item) {
// Do something when expanded
return true; // Return true to expand action view
}
});
}
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
final List<PasalBabModel> filteredModelList = filter(mPBH, newText);
adapter.animateTo(filteredModelList);
adapter.setFilter(filteredModelList);
rv.scrollToPosition(0);
return false;
}
private List<PasalBabModel> filter(List<PasalBabModel> models, String query) {
query = query.toLowerCase();
final List<PasalBabModel> filteredModelList = new ArrayList<>();
for (PasalBabModel model : models) {
final String text = model.getpasalbab_p().toLowerCase();
if (text.contains(query)) {
filteredModelList.add(model);
}
}
return filteredModelList;
}
}
PasalBabAdapter.java
public class PasalBabAdapter extends RecyclerView.Adapter<PasalBabVH> {
private List<PasalBabModel> mPasalBabModel;
public PasalBabAdapter(List<PasalBabModel> mPasalBabModel) {
this.mPasalBabModel = mPasalBabModel;
}
#Override
public void onBindViewHolder(PasalBabVH PasalBabVH, int i) {
final PasalBabModel model = mPasalBabModel.get(i);
PasalBabVH.bind(model);
}
#Override
public PasalBabVH onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.pasalbab_row, viewGroup, false);
return new PasalBabVH(view);
}
public void setFilter(List<PasalBabModel> PasalBabModels) {
mPasalBabModel = new ArrayList<>();
mPasalBabModel.addAll(PasalBabModels);
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return mPasalBabModel.size();
}
public void animateTo(List<PasalBabModel> models) {
applyAndAnimateRemovals(models);
applyAndAnimateAdditions(models);
applyAndAnimateMovedItems(models);
}
private void applyAndAnimateRemovals(List<PasalBabModel> newModels) {
for (int i = mPasalBabModel.size() - 1; i >= 0; i--) {
final PasalBabModel model = mPasalBabModel.get(i);
if (!newModels.contains(model)) {
removeItem(i);
}
}
}
private void applyAndAnimateAdditions(List<PasalBabModel> newModels) {
for (int i = 0, count = newModels.size(); i < count; i++) {
final PasalBabModel model = newModels.get(i);
if (!mPasalBabModel.contains(model)) {
addItem(i, model);
}
}
}
private void applyAndAnimateMovedItems(List<PasalBabModel> newModels) {
for (int toPosition = newModels.size() - 1; toPosition >= 0; toPosition--) {
final PasalBabModel model = newModels.get(toPosition);
final int fromPosition = mPasalBabModel.indexOf(model);
if (fromPosition >= 0 && fromPosition != toPosition) {
moveItem(fromPosition, toPosition);
}
}
}
public PasalBabModel removeItem(int position) {
final PasalBabModel model = mPasalBabModel.remove(position);
notifyItemRemoved(position);
return model;
}
public void addItem(int position, PasalBabModel model) {
mPasalBabModel.add(position, model);
notifyItemInserted(position);
}
public void moveItem(int fromPosition, int toPosition) {
final PasalBabModel model = mPasalBabModel.remove(fromPosition);
mPasalBabModel.add(toPosition, model);
notifyItemMoved(fromPosition, toPosition);
}
}
PasalBabVH.java
public class PasalBabVH extends RecyclerView.ViewHolder {
public TextView p_TextView;
public TextView b_TextView;
public PasalBabVH(View itemView) {
super(itemView);
p_TextView = (TextView) itemView.findViewById(R.id.uud_pasal);
b_TextView = (TextView) itemView.findViewById(R.id.uud_bab);
}
public void bind(PasalBabModel mx) {
p_TextView.setText(mx.getpasalbab_p());
b_TextView.setText(mx.getpasalbab_b());
}
}
PasalBabModel.java
public class PasalBabModel {
String pasalbab_p;
String pasalbab_b;
public PasalBabModel(String pasalbab_p, String pasalbab_b) {
this.pasalbab_p = pasalbab_p;
this.pasalbab_b = pasalbab_b;
}
public String getpasalbab_p() {
return pasalbab_p;
}
public String getpasalbab_b() {
return pasalbab_b;
}
}
please helpme...i'm a beginner :)
Create the adapter like so:
public class CustomAdapter extends RecyclerView.Adapter<Custom.ViewHolder> {
private List<SomeObject> mDataSet;
public OnItemClickListener mItemClickListener;
private Context mContext;
public CustomAdapter(Context context, List<SomeObject> myDataset, OnItemClickListener mItemClickListener) {
this.mDataSet = myDataset;
this.mItemClickListener = mItemClickListener;
this.mContext = context;
}
public void updateData(List<SomeObject> mDataSet) {
this.mDataSet = mDataSet;
notifyDataSetChanged();
}
public void removeItem(int position) {
mDataSet.remove(position);
notifyItemRemoved(position);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
final SomeObject obj = mDataSet.get(position);
holder.name.setText(obj.getName());
}
#Override
public int getItemCount() {
return mDataSet.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
TextView name;
public ViewHolder(View v) {
super(v);
name = (TextView) v.findViewById(R.id.naam);
v.setOnClickListener(this);
v.setOnLongClickListener(this);
}
#Override
public void onClick(View v) {
// If not long clicked, pass last variable as false.
mItemClickListener.onItemClick(v, getLayoutPosition(), false, mDataSet);
}
#Override
public boolean onLongClick(View v) {
// If long clicked, passed last variable as true.
mItemClickListener.onItemClick(v, getLayoutPosition(), true, mDataSet);
return true;
}
}
public interface OnItemClickListener {
void onItemClick(View view, int position, boolean isLongClick, List<SomeObject> mFilteredList);
}
}
And set the adapter like this:
mAdapter = new CustomAdapter(getActivity(), dataSet, new CustomAdapter.OnItemClickListener() {
#Override
public void onItemClick(View v, int position, boolean isLongClick, List<SomeObject> mDataSet) {
if (isLongClick) {
//Long click
SomeObject obj = mDataSet.get(position);
} else {
//Normal click
SomeObject obj = mDataSet.get(position);
}
}
});
Use interfaces to handle onClicks.
Put this in the adapter:
public interface OnItemClickListener {
void onClickItem(View view, int position);
}
public void SetOnItemClickListener(OnItemClickListener mItemClickListener) {
this.mItemClickListener = mItemClickListener;
}
and then in view holder do this:
public class PasalBabVH extends RecyclerView.ViewHolder {
public TextView p_TextView;
public TextView b_TextView;
public PasalBabVH(View itemView) {
super(itemView);
p_TextView = (TextView) itemView.findViewById(R.id.uud_pasal);
b_TextView = (TextView) itemView.findViewById(R.id.uud_bab);
p_TextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mItemClickListener != null) {
}
}
});
b_TextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mItemClickListener != null) {
}
}
});
}
public void bind(PasalBabModel mx) {
p_TextView.setText(mx.getpasalbab_p());
b_TextView.setText(mx.getpasalbab_b());
}
}
In fragment do this:
adapter.SetOnItemClickListener(new PasalBabAdapter.OnItemClickListener() {
#Override
public void onClickItem(View view, int position) {
}});

Why doesn't RecyclerView have onItemClickListener()?

I was exploring RecyclerView and I was surprised to see that RecyclerView does not have onItemClickListener().
I've two question.
Main Question
I want to know why Google removed onItemClickListener()?
Is there a performance issue or something else?
Secondary Question
I solved my problem by writing onClick in my RecyclerView.Adapter:
public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
public TextView txtViewTitle;
public ImageView imgViewIcon;
public ViewHolder(View itemLayoutView) {
super(itemLayoutView);
txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
}
#Override
public void onClick(View v) {
}
}
Is this ok / is there any better way?
tl;dr 2016 Use RxJava and a PublishSubject to expose an Observable for the clicks.
public class ReactiveAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
String[] mDataset = { "Data", "In", "Adapter" };
private final PublishSubject<String> onClickSubject = PublishSubject.create();
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
final String element = mDataset[position];
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onClickSubject.onNext(element);
}
});
}
public Observable<String> getPositionClicks(){
return onClickSubject.asObservable();
}
}
Original Post:
Since the introduction of ListView, onItemClickListener has been problematic. The moment you have a click listener for any of the internal elements the callback would not be triggered but it wasn't notified or well documented (if at all) so there was a lot of confusion and SO questions about it.
Given that RecyclerView takes it a step further and doesn't have a concept of a row/column, but rather an arbitrarily laid out amount of children, they have delegated the onClick to each one of them, or to programmer implementation.
Think of Recyclerview not as a ListView 1:1 replacement but rather as a more flexible component for complex use cases. And as you say, your solution is what google expected of you. Now you have an adapter who can delegate onClick to an interface passed on the constructor, which is the correct pattern for both ListView and Recyclerview.
public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
public TextView txtViewTitle;
public ImageView imgViewIcon;
public IMyViewHolderClicks mListener;
public ViewHolder(View itemLayoutView, IMyViewHolderClicks listener) {
super(itemLayoutView);
mListener = listener;
txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
imgViewIcon.setOnClickListener(this);
itemLayoutView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if (v instanceof ImageView){
mListener.onTomato((ImageView)v);
} else {
mListener.onPotato(v);
}
}
public static interface IMyViewHolderClicks {
public void onPotato(View caller);
public void onTomato(ImageView callerImage);
}
}
and then on your adapter
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
String[] mDataset = { "Data" };
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_layout, parent, false);
MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() {
public void onPotato(View caller) { Log.d("VEGETABLES", "Poh-tah-tos"); };
public void onTomato(ImageView callerImage) { Log.d("VEGETABLES", "To-m8-tohs"); }
});
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
// Get element from your dataset at this position
// Replace the contents of the view with that element
// Clear the ones that won't be used
holder.txtViewTitle.setText(mDataset[position]);
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return mDataset.length;
}
...
Now look into that last piece of code: onCreateViewHolder(ViewGroup parent, int viewType) the signature already suggest different view types. For each one of them you'll require a different viewholder too, and subsequently each one of them can have a different set of clicks. Or you can just create a generic viewholder that takes any view and one onClickListener and applies accordingly. Or delegate up one level to the orchestrator so several fragments/activities have the same list with different click behaviour. Again, all flexibility is on your side.
It is a really needed component and fairly close to what our internal implementations and improvements to ListView were until now. It's good that Google finally acknowledges it.
Why the RecyclerView has no onItemClickListener
The RecyclerView is a toolbox, in contrast of the old ListView it has less build in features and more flexibility. The onItemClickListener is not the only feature being removed from ListView. But it has lot of listeners and method to extend it to your liking, it's far more powerful in the right hands ;).
In my opinion the most complex feature removed in RecyclerView is the Fast Scroll. Most of the other features can be easily re-implemented.
If you want to know what other cool features RecyclerView added read this answer to another question.
Memory efficient - drop-in solution for onItemClickListener
This solution has been proposed by Hugo Visser, an Android GDE, right after RecyclerView was released. He made a licence-free class available for you to just drop in your code and use it.
It showcase some of the versatility introduced with RecyclerView by making use of RecyclerView.OnChildAttachStateChangeListener.
Edit 2019: kotlin version by me, java one, from Hugo Visser, kept below
Kotlin / Java
Create a file values/ids.xml and put this in it:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="item_click_support" type="id" />
</resources>
then add the code below to your source
Kotlin
Usage:
recyclerView.onItemClick { recyclerView, position, v ->
// do it
}
(it also support long item click and see below for another feature I've added).
implementation (my adaptation to Hugo Visser Java code):
typealias OnRecyclerViewItemClickListener = (recyclerView: RecyclerView, position: Int, v: View) -> Unit
typealias OnRecyclerViewItemLongClickListener = (recyclerView: RecyclerView, position: Int, v: View) -> Boolean
class ItemClickSupport private constructor(private val recyclerView: RecyclerView) {
private var onItemClickListener: OnRecyclerViewItemClickListener? = null
private var onItemLongClickListener: OnRecyclerViewItemLongClickListener? = null
private val attachListener: RecyclerView.OnChildAttachStateChangeListener = object : RecyclerView.OnChildAttachStateChangeListener {
override fun onChildViewAttachedToWindow(view: View) {
// every time a new child view is attached add click listeners to it
val holder = this#ItemClickSupport.recyclerView.getChildViewHolder(view)
.takeIf { it is ItemClickSupportViewHolder } as? ItemClickSupportViewHolder
if (onItemClickListener != null && holder?.isClickable != false) {
view.setOnClickListener(onClickListener)
}
if (onItemLongClickListener != null && holder?.isLongClickable != false) {
view.setOnLongClickListener(onLongClickListener)
}
}
override fun onChildViewDetachedFromWindow(view: View) {
}
}
init {
// the ID must be declared in XML, used to avoid
// replacing the ItemClickSupport without removing
// the old one from the RecyclerView
this.recyclerView.setTag(R.id.item_click_support, this)
this.recyclerView.addOnChildAttachStateChangeListener(attachListener)
}
companion object {
fun addTo(view: RecyclerView): ItemClickSupport {
// if there's already an ItemClickSupport attached
// to this RecyclerView do not replace it, use it
var support: ItemClickSupport? = view.getTag(R.id.item_click_support) as? ItemClickSupport
if (support == null) {
support = ItemClickSupport(view)
}
return support
}
fun removeFrom(view: RecyclerView): ItemClickSupport? {
val support = view.getTag(R.id.item_click_support) as? ItemClickSupport
support?.detach(view)
return support
}
}
private val onClickListener = View.OnClickListener { v ->
val listener = onItemClickListener ?: return#OnClickListener
// ask the RecyclerView for the viewHolder of this view.
// then use it to get the position for the adapter
val holder = this.recyclerView.getChildViewHolder(v)
listener.invoke(this.recyclerView, holder.adapterPosition, v)
}
private val onLongClickListener = View.OnLongClickListener { v ->
val listener = onItemLongClickListener ?: return#OnLongClickListener false
val holder = this.recyclerView.getChildViewHolder(v)
return#OnLongClickListener listener.invoke(this.recyclerView, holder.adapterPosition, v)
}
private fun detach(view: RecyclerView) {
view.removeOnChildAttachStateChangeListener(attachListener)
view.setTag(R.id.item_click_support, null)
}
fun onItemClick(listener: OnRecyclerViewItemClickListener?): ItemClickSupport {
onItemClickListener = listener
return this
}
fun onItemLongClick(listener: OnRecyclerViewItemLongClickListener?): ItemClickSupport {
onItemLongClickListener = listener
return this
}
}
/** Give click-ability and long-click-ability control to the ViewHolder */
interface ItemClickSupportViewHolder {
val isClickable: Boolean get() = true
val isLongClickable: Boolean get() = true
}
// Extension function
fun RecyclerView.addItemClickSupport(configuration: ItemClickSupport.() -> Unit = {}) = ItemClickSupport.addTo(this).apply(configuration)
fun RecyclerView.removeItemClickSupport() = ItemClickSupport.removeFrom(this)
fun RecyclerView.onItemClick(onClick: OnRecyclerViewItemClickListener) {
addItemClickSupport { onItemClick(onClick) }
}
fun RecyclerView.onItemLongClick(onLongClick: OnRecyclerViewItemLongClickListener) {
addItemClickSupport { onItemLongClick(onLongClick) }
}
(Remember you also need to add an XML file, see above this section)
Bonus feature of Kotlin version
Sometimes you do not want all the items of the RecyclerView to be clickable.
To handle this I've introduced the ItemClickSupportViewHolder interface that you can use on your ViewHolder to control which item is clickable.
Example:
class MyViewHolder(view): RecyclerView.ViewHolder(view), ItemClickSupportViewHolder {
override val isClickable: Boolean get() = false
override val isLongClickable: Boolean get() = false
}
Java
Usage:
ItemClickSupport.addTo(mRecyclerView)
.setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
#Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// do it
}
});
(it also support long item click)
Implementation (comments added by me):
public class ItemClickSupport {
private final RecyclerView mRecyclerView;
private OnItemClickListener mOnItemClickListener;
private OnItemLongClickListener mOnItemLongClickListener;
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
// ask the RecyclerView for the viewHolder of this view.
// then use it to get the position for the adapter
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
}
}
};
private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
if (mOnItemLongClickListener != null) {
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
}
return false;
}
};
private RecyclerView.OnChildAttachStateChangeListener mAttachListener
= new RecyclerView.OnChildAttachStateChangeListener() {
#Override
public void onChildViewAttachedToWindow(View view) {
// every time a new child view is attached add click listeners to it
if (mOnItemClickListener != null) {
view.setOnClickListener(mOnClickListener);
}
if (mOnItemLongClickListener != null) {
view.setOnLongClickListener(mOnLongClickListener);
}
}
#Override
public void onChildViewDetachedFromWindow(View view) {
}
};
private ItemClickSupport(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
// the ID must be declared in XML, used to avoid
// replacing the ItemClickSupport without removing
// the old one from the RecyclerView
mRecyclerView.setTag(R.id.item_click_support, this);
mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
}
public static ItemClickSupport addTo(RecyclerView view) {
// if there's already an ItemClickSupport attached
// to this RecyclerView do not replace it, use it
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
if (support == null) {
support = new ItemClickSupport(view);
}
return support;
}
public static ItemClickSupport removeFrom(RecyclerView view) {
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
if (support != null) {
support.detach(view);
}
return support;
}
public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
return this;
}
public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
mOnItemLongClickListener = listener;
return this;
}
private void detach(RecyclerView view) {
view.removeOnChildAttachStateChangeListener(mAttachListener);
view.setTag(R.id.item_click_support, null);
}
public interface OnItemClickListener {
void onItemClicked(RecyclerView recyclerView, int position, View v);
}
public interface OnItemLongClickListener {
boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
}
}
How it works (why it's efficient)
This class works by attaching a RecyclerView.OnChildAttachStateChangeListener to the RecyclerView. This listener is notified every time a child is attached or detached from the RecyclerView. The code use this to append a tap/long click listener to the view. That listener ask the RecyclerView for the RecyclerView.ViewHolder which contains the position.
This is more efficient then other solutions because it avoid creating multiple listeners for each view and keep destroying and creating them while the RecyclerView is being scrolled.
You could also adapt the code to give you back the holder itself if you need more.
Final remark
Keep in mind that it's COMPLETELY fine to handle it in your adapter by setting on each view of your list a click listener, like other answer proposed.
It's just not the most efficient thing to do (you create a new listener every time you reuse a view) but it works and in most cases it's not an issue.
It is also a bit against separation of concerns cause it's not really the Job of the Adapter to delegate click events.
I like this way and I'm using it
Inside
public Adapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
Put
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_image_and_text, parent, false);
v.setOnClickListener(new MyOnClickListener());
And create this class anywhere you want it
class MyOnClickListener implements View.OnClickListener {
#Override
public void onClick(View v) {
int itemPosition = recyclerView.indexOfChild(v);
Log.e("Clicked and Position is ",String.valueOf(itemPosition));
}
}
I've read before that there is a better way but I like this way is easy and not complicated.
Android Recyclerview With onItemClickListener,
Why we cant try this is working like ListView only.
Source : Link
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
private OnItemClickListener mListener;
public interface OnItemClickListener {
public void onItemClick(View view, int position);
}
GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
mListener = listener;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
}
#Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
View childView = view.findChildViewUnder(e.getX(), e.getY());
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
}
return false;
}
#Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
And Set this to RecyclerView:
recyclerView = (RecyclerView)rootView. findViewById(R.id.recyclerView);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.addOnItemTouchListener(
new RecyclerItemClickListener(getActivity(), new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
// TODO Handle item click
Log.e("#####",""+position);
}
})
);
Thanks to #marmor, I updated my answer.
I think it's a good solution to handle the onClick() in the ViewHolder class constructor and pass it to the parent class via OnItemClickListener interface.
MyAdapter.java
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
private LayoutInflater layoutInflater;
private List<MyObject> items;
private AdapterView.OnItemClickListener onItemClickListener;
public MyAdapter(Context context, AdapterView.OnItemClickListener onItemClickListener, List<MyObject> items) {
layoutInflater = LayoutInflater.from(context);
this.items = items;
this.onItemClickListener = onItemClickListener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = layoutInflater.inflate(R.layout.my_row_layout, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
MyObject item = items.get(position);
}
public MyObject getItem(int position) {
return items.get(position);
}
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView title;
private ImageView avatar;
public ViewHolder(View itemView) {
super(itemView);
title = itemView.findViewById(R.id.title);
avatar = itemView.findViewById(R.id.avatar);
title.setOnClickListener(this);
avatar.setOnClickListener(this);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
//passing the clicked position to the parent class
onItemClickListener.onItemClick(null, view, getAdapterPosition(), view.getId());
}
}
}
Usage of adapter in other classes:
MyFragment.java
public class MyFragment extends Fragment implements AdapterView.OnItemClickListener {
private RecyclerView recycleview;
private MyAdapter adapter;
.
.
.
private void init(Context context) {
//passing this fragment as OnItemClickListener to the adapter
adapter = new MyAdapter(context, this, items);
recycleview.setAdapter(adapter);
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//you can get the clicked item from the adapter using its position
MyObject item = adapter.getItem(position);
//you can also find out which view was clicked
switch (view.getId()) {
case R.id.title:
//title view was clicked
break;
case R.id.avatar:
//avatar view was clicked
break;
default:
//the whole row was clicked
}
}
}
> How RecyclerView is different from Listview?
One difference is that there is LayoutManager class with RecyclerView by which you can manage your RecyclerView like-
Horizontal or Vertical scrolling by LinearLayoutManager
GridLayout by GridLayoutManager
Staggered GridLayout by StaggeredGridLayoutManager
Like for horizontal scrolling for RecyclerView-
LinearLayoutManager llm = new LinearLayoutManager(context);
llm.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(llm);
Guys use this code in Your main activity. Very Efficient Method
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.users_list);
UsersAdapter adapter = new UsersAdapter(users, this);
recyclerView.setAdapter(adapter);
adapter.setOnCardClickListner(this);
Here is your Adapter class.
public class UsersAdapter extends RecyclerView.Adapter<UsersAdapter.UserViewHolder> {
private ArrayList<User> mDataSet;
OnCardClickListner onCardClickListner;
public UsersAdapter(ArrayList<User> mDataSet) {
this.mDataSet = mDataSet;
}
#Override
public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_row_layout, parent, false);
UserViewHolder userViewHolder = new UserViewHolder(v);
return userViewHolder;
}
#Override
public void onBindViewHolder(UserViewHolder holder, final int position) {
holder.name_entry.setText(mDataSet.get(position).getUser_name());
holder.cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onCardClickListner.OnCardClicked(v, position);
}
});
}
#Override
public int getItemCount() {
return mDataSet.size();
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
public static class UserViewHolder extends RecyclerView.ViewHolder {
CardView cardView;
TextView name_entry;
public UserViewHolder(View itemView) {
super(itemView);
cardView = (CardView) itemView.findViewById(R.id.user_layout);
name_entry = (TextView) itemView.findViewById(R.id.name_entry);
}
}
public interface OnCardClickListner {
void OnCardClicked(View view, int position);
}
public void setOnCardClickListner(OnCardClickListner onCardClickListner) {
this.onCardClickListner = onCardClickListner;
}
}
After this you will get this override method in your activity.
#Override
public void OnCardClicked(View view, int position) {
Log.d("OnClick", "Card Position" + position);
}
How to put it all together example...
onClick() handling
Cursor - RecyclerView
ViewHolder types
public class OrderListCursorAdapter extends CursorRecyclerViewAdapter<OrderListCursorAdapter.ViewHolder> {
private static final String TAG = OrderListCursorAdapter.class.getSimpleName();
private static final int ID_VIEW_HOLDER_ACTUAL = 0;
private static final int ID_VIEW_HOLDER = 1;
public OrderListCursorAdapter(Context context, Cursor cursor) {
super(context, cursor);
}
public static class ViewHolderActual extends ViewHolder {
private static final String TAG = ViewHolderActual.class.getSimpleName();
protected IViewHolderClick listener;
protected Button button;
public ViewHolderActual(View v, IViewHolderClick listener) {
super(v, listener);
this.listener = listener;
button = (Button) v.findViewById(R.id.orderList_item_button);
button.setOnClickListener(this);
}
public void initFromData(OrderData data) {
Log.d(TAG, "><initFromData(data=" + data + ")");
orderId = data.getId();
vAddressStart.setText(data.getAddressStart());
vAddressEnd.setText(data.getAddressEnd());
}
#Override
public void onClick(View view) {
if (view instanceof Button) {
listener.onButtonClick((Button) view, getPosition(), this);
} else {
super.onClick(view);
}
}
public interface IViewHolderClick extends ViewHolder.IViewHolderClick {
public void onButtonClick(Button button, int position, ViewHolder viewHolder);
}
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private static final String TAG = ViewHolder.class.getSimpleName();
protected long orderId;
protected IViewHolderClick listener;
protected TextView vAddressStart;
protected TextView vAddressEnd;
protected TextView vStatus;
public ViewHolder(View v, IViewHolderClick listener) {
super(v);
this.listener = listener;
v.setOnClickListener(this);
vAddressStart = (TextView) v.findViewById(R.id.addressStart);
vAddressEnd = (TextView) v.findViewById(R.id.addressEnd);
vStatus = (TextView) v.findViewById(R.id.status);
}
public void initFromData(OrderData data) {
Log.d(TAG, "><initFromData(data=" + data + ")");
orderId = data.getId();
vAddressStart.setText(data.getAddressStart());
vAddressEnd.setText(data.getAddressEnd());
}
public long getOrderId() {
return orderId;
}
#Override
public void onClick(View view) {
listener.onCardClick(view, getPosition(), this);
}
public interface IViewHolderClick {
public void onCardClick(View view, int position, ViewHolder viewHolder);
}
}
#Override
public int getItemViewType(int position) {
return position == 0 ? ID_VIEW_HOLDER_ACTUAL : ID_VIEW_HOLDER;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.d(TAG, ">>onCreateViewHolder(parent=" + parent + ", viewType=" + viewType + ")");
ViewHolder result;
switch (viewType) {
case ID_VIEW_HOLDER_ACTUAL: {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_layout_actual, parent, false);
result = new ViewHolderActual(itemView, new ViewHolderActual.IViewHolderClick() {
#Override
public void onCardClick(View view, int position, ViewHolder viewHolder) {
Log.d(TAG, "><onCardClick(view=" + view + ", position=" + position + ", viewHolder=" + viewHolder + ")");
Intent intent = new Intent(view.getContext(), OrderDetailActivity.class);
intent.putExtra(OrderDetailActivity.ARG_ORDER_ID, viewHolder.getOrderId());
view.getContext().startActivity(intent);
}
#Override
public void onButtonClick(Button button, int position, ViewHolder viewHolder) {
Log.d(TAG, "><onButtonClick(button=" + button + ", position=" + position + ", viewHolder=" + viewHolder + ")");
Intent intent = new Intent(button.getContext(), OrderMapActivity.class);
intent.putExtra(OrderMapActivity.ARG_ORDER_ID, viewHolder.getOrderId());
button.getContext().startActivity(intent);
}
});
break;
}
case ID_VIEW_HOLDER:
default: {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_layout, parent, false);
result = new ViewHolder(itemView, new ViewHolder.IViewHolderClick() {
#Override
public void onCardClick(View view, int position, ViewHolder viewHolder) {
Log.d(TAG, "><onCardClick(view=" + view + ", position=" + position + ", viewHolder=" + viewHolder + ")");
Intent intent = new Intent(view.getContext(), OrderDetailActivity.class);
intent.putExtra(OrderDetailActivity.ARG_ORDER_ID, viewHolder.getOrderId());
view.getContext().startActivity(intent);
}
});
break;
}
}
Log.d(TAG, "<<onCreateViewHolder(parent=" + parent + ", viewType=" + viewType + ")= " + result);
return result;
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, Cursor cursor) {
Log.d(TAG, "><onBindViewHolder(viewHolder=" + viewHolder + ", cursor=" + cursor + ")");
final OrderData orderData = new OrderData(cursor);
viewHolder.initFromData(orderData);
}
}
Following up MLProgrammer-CiM's excellent RxJava solution
Consume / Observe clicks
Consumer<String> mClickConsumer = new Consumer<String>() {
#Override
public void accept(#NonNull String element) throws Exception {
Toast.makeText(getApplicationContext(), element +" was clicked", Toast.LENGTH_LONG).show();
}
};
ReactiveAdapter rxAdapter = new ReactiveAdapter();
rxAdapter.getPositionClicks().subscribe(mClickConsumer);
RxJava 2.+
Modify the original tl;dr as:
public Observable<String> getPositionClicks(){
return onClickSubject;
}
PublishSubject#asObservable() was removed. Just return the PublishSubject which is an Observable.
RecyclerView doesn't have an onItemClickListener because RecyclerView is responsible for recycling views (surprise!), so it's the responsibility of the view that is recycled to handle the click events it receives.
This actually makes it much easier to use, especially if you had items that can be clicked in multiple places.
Anyways, detecting click on a RecyclerView item is very easy. All you need to do is define an interface (if you're not using Kotlin, in which case you just pass in a lambda):
public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
private final Clicks clicks;
public MyAdapter(Clicks clicks) {
this.clicks = clicks;
}
private List<MyObject> items = Collections.emptyList();
public void updateData(List<MyObject> items) {
this.items = items;
notifyDataSetChanged(); // TODO: use ListAdapter for diffing instead if you need animations
}
public interface Clicks {
void onItemSelected(MyObject myObject, int position);
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private MyObject myObject;
public MyViewHolder(View view) {
super(view);
// bind views
view.setOnClickListener((v) -> {
int adapterPosition = getBindingAdapterPosition();
if(adapterPosition >= 0) {
clicks.onItemSelected(myObject, adapterPosition);
}
});
}
public void bind(MyObject myObject) {
this.myObject = myObject;
// bind data to views
}
}
}
Same code in Kotlin:
class MyAdapter(val itemClicks: (MyObject, Int) -> Unit): RecyclerView.Adapter<MyViewHolder>() {
private var items: List<MyObject> = Collections.emptyList()
fun updateData(items: List<MyObject>) {
this.items = items
notifyDataSetChanged() // TODO: use ListAdapter for diffing instead if you need animations
}
inner class MyViewHolder(val myView: View): RecyclerView.ViewHolder(myView) {
private lateinit var myObject: MyObject
init {
// binds views
myView.onClick {
val adapterPosition = getBindingAdapterPosition()
if(adapterPosition >= 0) {
itemClicks.invoke(myObject, adapterPosition)
}
}
}
fun bind(myObject: MyObject) {
this.myObject = myObject
// bind data to views
}
}
}
Thing you DON'T need to do:
1.) you don't need to intercept touch events manually
2.) you don't need to mess around with child attach state change listeners
3.) you don't need PublishSubject/PublishRelay from RxJava
Just use a click listener.
As far as I understand MLProgrammer-CiM answer, simply it's possible to just do this:
class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private ImageView image;
private TextView title;
private TextView price;
public MyViewHolder(View itemView) {
super(itemView);
image = (ImageView)itemView.findViewById(R.id.horizontal_list_image);
title = (TextView)itemView.findViewById(R.id.horizontal_list_title);
price = (TextView)itemView.findViewById(R.id.horizontal_list_price);
image.setOnClickListener(this);
title.setOnClickListener(this);
price.setOnClickListener(this);
}
#Override
public void onClick(View v) {
Toast.makeText(context, "Item click nr: "+getLayoutPosition(), Toast.LENGTH_SHORT).show();
}
}
After reading #MLProgrammer-CiM's answer, here is my code:
class NormalViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
#Bind(R.id.card_item_normal)
CardView cardView;
public NormalViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
cardView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if(v instanceof CardView) {
// use getAdapterPosition() instead of getLayoutPosition()
int itemPosition = getAdapterPosition();
removeItem(itemPosition);
}
}
}
I have done this way, its very simple:
Just add 1 Line for Clicked RecyclerView position:
int position = getLayoutPosition()
Full code for ViewHolder class:
private class ChildViewHolder extends RecyclerView.ViewHolder {
public ImageView imageView;
public TextView txtView;
public ChildViewHolder(View itemView) {
super(itemView);
imageView= (ImageView)itemView.findViewById(R.id.imageView);
txtView= (TextView) itemView.findViewById(R.id.txtView);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.i("RecyclerView Item Click Position", String.valueOf(getLayoutPosition()));
}
});
}
}
Hope this will help you.
I use this method to start an Intent from RecyclerView:
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
final MyClass myClass = mList.get(i);
viewHolder.txtViewTitle.setText(myclass.name);
...
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
Intent detailIntent = new Intent(mContext, type.class);
detailIntent.putExtra("MyClass", myclass);
mContext.startActivity(detailIntent);
}
}
);
See my approach on this:
First declare an interface like this:
/**
* Interface used for delegating item click events in a {#link android.support.v7.widget.RecyclerView}
* Created by Alex on 11/28/2015.
*/
public interface OnRecyclerItemClickListener<T> {
/**
* Called when a click occurred inside a recyclerView item view
* #param view that was clicked
* #param position of the clicked view
* #param item the concrete data that is displayed through the clicked view
*/
void onItemClick(View view, int position, T item);
}
Then create the adapter:
public class CustomRecyclerAdapter extends RecyclerView.Adapter {
private class InternalClickListener implements View.OnClickListener{
#Override
public void onClick(View v) {
if(mRecyclerView != null && mItemClickListener != null){
// find the position of the item that was clicked
int position = mRecyclerView.getChildAdapterPosition(v);
Data data = getItem(position);
// notify the main listener
mItemClickListener.onItemClick(v, position, data);
}
}
}
private final OnRecyclerItemClickListener mItemClickListener;
private RecyclerView mRecyclerView;
private InternalClickListener mInternalClickListener;
/**
*
* #param itemClickListener used to trigger an item click event
*/
public PlayerListRecyclerAdapter(OnRecyclerItemClickListener itemClickListener){
mItemClickListener = itemClickListener;
mInternalClickListener = new InternalClickListener();
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item, parent, false);
v.setOnClickListener(mInternalClickListener);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// do your binding here
}
#Override
public int getItemCount() {
return mDataSet.size();
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
mRecyclerView = recyclerView;
}
#Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
mRecyclerView = null;
}
public Data getItem(int position){
return mDataset.get(position);
}
}
And now let's see how to integrate this from a fragment:
public class TestFragment extends Fragment implements OnRecyclerItemClickListener<Data>{
private RecyclerView mRecyclerView;
#Override
public void onItemClick(View view, int position, Data item) {
// do something
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.test_fragment, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
mRecyclerView = view.findViewById(idOfTheRecycler);
mRecyclerView .setAdapter(new CustomRecyclerAdapter(this));
}
If you want to add onClick() to the child view of items, for example, a button in item, I found that you can do it easily in onCreateViewHolder() of your own RecyclerView.Adapter just like this:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater
.from(parent.getContext())
.inflate(R.layout.cell, null);
Button btn = (Button) v.findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//do it
}
});
return new MyViewHolder(v);
}
i don't know whether it's a good way, but it works well. If anyone has a better idea, very glad to tell me and correct my answer! :)
Here is a way to implement it quite easily if you have a list of POJOs and want to retrieve one on click from outside the adapter.
In your adapter, create a listener for the click events and a method to set it:
public class MyAdapter extends RecyclerView.Adapter<SitesListAdapter.ViewHolder> {
...
private List<MyPojo> mMyPojos;
private static OnItemClickListener mOnItemClickListener;
...
public interface OnItemClickListener {
public void onItemClick(MyPojo pojo);
}
...
public void setOnItemClickListener(OnItemClickListener onItemClickListener){
mOnItemClickListener = onItemClickListener;
}
...
}
In your ViewHolder, implement onClickListener and create a class member to temporarily store the POJO the view is presenting, that way (this is an example, creating a setter would be better):
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public MyPojo mCurrentPojo;
...
public ViewHolder(View view) {
super(v);
...
view.setOnClickListener(this); //You could set this on part of the layout too
}
...
#Override
public void onClick(View view) {
if(mOnItemClickListener != null && mCurrentPojo != null){
mOnItemClickListener.onItemClick(mCurrentPojo);
}
}
Back in your adapter, set the current POJO when the ViewHolder is bound (or to null if the current view doesn't have one):
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final MyPojo currentPojo = mMyPojos.get(position);
holder.mCurrentPojo = currentPojo;
...
That's it, now you can use it like this from your fragment/activity:
mMyAdapter.setOnItemClickListener(new mMyAdapter.OnItemClickListener() {
#Override
public void onItemClick(MyPojo pojo) {
//Do whatever you want with your pojo here
}
});
Yes you can
public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {
//inflate the view
View view = LayoutInflator.from(parent.getContext()).inflate(R.layout.layoutID,null);
ViewHolder holder = new ViewHolder(view);
//here we can set onClicklistener
view.setOnClickListener(new View.OnClickListeener(){
public void onClick(View v)
{
//action
}
});
return holder;
This worked for me:
#Override
public void onBindViewHolder(PlacesListViewAdapter.ViewHolder holder, int position) {
----
----
----
// Set setOnClickListener(holder);
}
#Override
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
----
----
----
#Override
public void onClick(View view) {
// Use to get the item clicked getAdapterPosition()
}
}
Here you can handle multiple onclick see below code and it is very efficient
public class RVNewsAdapter extends RecyclerView.Adapter<RVNewsAdapter.FeedHolder> {
private Context context;
List<News> newsList;
// Allows to remember the last item shown on screen
private int lastPosition = -1;
public RVNewsAdapter(List<News> newsList, Context context) {
this.newsList = newsList;
this.context = context;
}
public static class FeedHolder extends RecyclerView.ViewHolder implements OnClickListener {
ImageView img_main;
TextView tv_title;
Button bt_facebook, bt_twitter, bt_share, bt_comment;
public FeedHolder(View itemView) {
super(itemView);
img_main = (ImageView) itemView.findViewById(R.id.img_main);
tv_title = (TextView) itemView.findViewById(R.id.tv_title);
bt_facebook = (Button) itemView.findViewById(R.id.bt_facebook);
bt_twitter = (Button) itemView.findViewById(R.id.bt_twitter);
bt_share = (Button) itemView.findViewById(R.id.bt_share);
bt_comment = (Button) itemView.findViewById(R.id.bt_comment);
img_main.setOnClickListener(this);
bt_facebook.setOnClickListener(this);
bt_twitter.setOnClickListener(this);
bt_comment.setOnClickListener(this);
bt_share.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if (v.getId() == bt_comment.getId()) {
Toast.makeText(v.getContext(), "Comment " , Toast.LENGTH_SHORT).show();
} else if (v.getId() == bt_facebook.getId()) {
Toast.makeText(v.getContext(), "Facebook " , Toast.LENGTH_SHORT).show();
} else if (v.getId() == bt_twitter.getId()) {
Toast.makeText(v.getContext(), "Twitter " , Toast.LENGTH_SHORT).show();
} else if (v.getId() == bt_share.getId()) {
Toast.makeText(v.getContext(), "share " , Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(v.getContext(), "ROW PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();
}
}
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
#Override
public FeedHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.feed_row, parent, false);
FeedHolder feedHolder = new FeedHolder(view);
return feedHolder;
}
#Override
public void onBindViewHolder(FeedHolder holder, int position) {
holder.tv_title.setText(newsList.get(position).getTitle());
// Here you apply the animation when the view is bound
setAnimation(holder.img_main, position);
}
#Override
public int getItemCount() {
return newsList.size();
}
/**
* Here is the key method to apply the animation
*/
private void setAnimation(View viewToAnimate, int position) {
// If the bound view wasn't previously displayed on screen, it's animated
if (position > lastPosition) {
Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
viewToAnimate.startAnimation(animation);
lastPosition = position;
}
}
}
Modified my comment...
public class MyViewHolder extends RecyclerView.ViewHolder {
private Context mContext;
public MyViewHolder(View itemView) {
super(itemView);
mContext = itemView.getContext();
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int itemPosition = getLayoutPosition();
Toast.makeText(mContext, "" + itemPosition, Toast.LENGTH_SHORT).show();
}
});
}
Check this one in which I have implemented all the things with a proper way
RecyclerViewHolder Class
public class RecyclerViewHolder extends RecyclerView.ViewHolder {
//view holder is for girdview as we used in the listView
public ImageView imageView,imageView2;
public RecyclerViewHolder(View itemView) {
super(itemView);
this.imageView=(ImageView)itemView.findViewById(R.id.image);
}
}
Adapter
public class RecyclerView_Adapter extends RecyclerView.Adapter<RecyclerViewHolder> {
//RecyclerView will extend to recayclerview Adapter
private ArrayList<ModelClass> arrayList;
private Context context;
private static RecyclerViewClickListener itemListener;
//constructor of the RecyclerView Adapter
RecyclerView_Adapter(Context context,ArrayList<ModelClass> arrayList,RecyclerViewClickListener itemListener){
this.context=context;
this.arrayList=arrayList;
this.itemListener=itemListener;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//this method will inflate the custom layout and return as viewHolder
LayoutInflater layoutInflater=LayoutInflater.from(parent.getContext());
ViewGroup mainGroup=(ViewGroup) layoutInflater.inflate(R.layout.single_item,parent,false);
RecyclerViewHolder listHolder=new RecyclerViewHolder(mainGroup);
return listHolder;
}
#Override
public void onBindViewHolder(RecyclerViewHolder holder, final int position) {
final ModelClass modelClass=arrayList.get(position);
//holder
RecyclerViewHolder mainHolder=(RecyclerViewHolder)holder;
//convert the drawable image into bitmap
Bitmap image= BitmapFactory.decodeResource(context.getResources(),modelClass.getImage());
//set the image into imageView
mainHolder.imageView.setImageBitmap(image);
//to handle on click event when clicked on the recyclerview item and
// get it through the RecyclerViewHolder class we have defined the views there
mainHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//get the position of the image which is clicked
itemListener.recyclerViewListClicked(v,position);
}
});
}
#Override
public int getItemCount() {
return (null!=arrayList?arrayList.size():0);
}
}
The interface
public interface RecyclerViewClickListener {
//this is method to handle the event when clicked on the image in Recyclerview
public void recyclerViewListClicked(View v,int position);
}
//and to call this method in activity
RecyclerView_Adapter adapter=new RecyclerView_Adapter(Wallpaper.this,arrayList,this);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
#Override
public void recyclerViewListClicked(View v,int position){
imageView.setImageResource(wallpaperImages[position]);
}
Access the mainView of rowLayout(cell) for you RecyclerView and in your OnBindViewHolder write this code:
#Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
Movie movie = moviesList.get(position);
holder.mainView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
System.out.println("pos " + position);
}
});
}
it worked for me. Hope it will help. Most simplest way.
Inside View Holder
class GeneralViewHolder extends RecyclerView.ViewHolder {
View cachedView = null;
public GeneralViewHolder(View itemView) {
super(itemView);
cachedView = itemView;
}
Inside OnBindViewHolder()
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
final GeneralViewHolder generalViewHolder = (GeneralViewHolder) holder;
generalViewHolder.cachedView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, "item Clicked at "+position, Toast.LENGTH_SHORT).show();
}
});
And let me know, do you have any question about this solution ?
main_recyclerview.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e)
{
int position=rv.getChildAdapterPosition(rv.findChildViewUnder(e.getX(),e.getY()));
switch (position)
{
case 0:
{
wifi(position);
adapter2.notifyDataSetChanged();
}
break;
case 1:
{
sound(position);
adapter2.notifyDataSetChanged();
}
break;
case 2:
{
bluetooth(position);
adapter2.notifyDataSetChanged();
}
break;
}
return true;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e)
{
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
});
Instead of implementing interface View.OnClickListener inside view holder or creating and interface and implementing interface in your activity..
I used this code for simple on OnClickListener implementation.
public static class SimpleStringRecyclerViewAdapter
extends RecyclerView.Adapter<SimpleStringRecyclerViewAdapter.ViewHolder> {
// Your initializations goes here...
private List<String> mValues;
public static class ViewHolder extends RecyclerView.ViewHolder {
//create a variable mView
public final View mView;
/*All your row widgets goes here
public final ImageView mImageView;
public final TextView mTextView;*/
public ViewHolder(View view) {
super(view);
//Initialize it here
mView = view;
/* your row widgets initializations goes here
mImageView = (ImageView) view.findViewById(R.id.avatar);
mTextView = (TextView) view.findViewById(android.R.id.text1);*/
}
}
public String getValueAt(int position) {
return mValues.get(position);
}
public SimpleStringRecyclerViewAdapter(Context context, List<String> items) {
mBackground = mTypedValue.resourceId;
mValues = items;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item, parent, false);
view.setBackgroundResource(mBackground);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mBoundString = mValues.get(position);
holder.mTextView.setText(mValues.get(position));
//Here it is simply write onItemClick listener here
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, ExampleActivity.class);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return mValues.size();
}
}
use PlaceHolderView
#Layout(R.layout.item_view_1)
public class View1{
#View(R.id.txt)
public TextView txt;
#Resolve
public void onResolved() {
txt.setText(String.valueOf(System.currentTimeMillis() / 1000));
}
#Click(R.id.btn)
public void onClick(){
txt.setText(String.valueOf(System.currentTimeMillis() / 1000));
}
}
I wrote a library to handle android recycler view item click event. You can find whole tutorial in https://github.com/ChathuraHettiarachchi/RecycleClick
RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemClickListener(new RecycleClick.OnItemClickListener() {
#Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// YOUR CODE
}
});
or to handle item long press you can use
RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemLongClickListener(new RecycleClick.OnItemLongClickListener() {
#Override
public boolean onItemLongClicked(RecyclerView recyclerView, int position, View v) {
// YOUR CODE
return true;
}
});
recyclerview animation has not been tested, the other is normal. I think it has been optimized to the maximum. Interface has other uses, you can temporarily ignore.
public abstract class BaseAdapterRV<VH extends BaseViewHolder> extends RecyclerView.Adapter<VH> implements AdapterInterface {
public final String TAG = getClass().getSimpleName();
protected final Activity mActivity;
protected final LayoutInflater mInflater;
protected ItemClickInterface<?, Integer> mListener;
public BaseAdapterRV(Activity activity) {
mActivity = activity;
mInflater = LayoutInflater.from(mActivity);
}
#Override
public final VH onCreateViewHolder(ViewGroup parent, int viewType) {
return onCreateViewHolder(parent, viewType, mInflater);
}
#Override
public final void onBindViewHolder(VH holder, int position) {
holder.itemView.setTag(R.id.tag_view_click, position);
//创建点击事件
holder.itemView.setOnClickListener(mListener);
holder.itemView.setOnLongClickListener(mListener);
onBindVH(holder, position);
}
///////////////////////////////////////////////////////////////////////////
// 以下是增加的方法
///////////////////////////////////////////////////////////////////////////
/**
* 注意!涉及到notifyItemInserted刷新时立即获取position可能会不正确
* 里面也有onItemLongClick
*/
public void setOnItemClickListener(ItemClickInterface<?, Integer> listener) {
mListener = listener;
notifyDataSetChanged();
}
#NonNull
protected abstract VH onCreateViewHolder(ViewGroup parent, int viewType, LayoutInflater inflater);
protected abstract void onBindVH(VH holder, int position);
}
This is Interface
/**
* OnItemClickListener的接口
* 见子类实现{#link OnItemClickListener}{#link OnItemItemClickListener}
*/
public interface ItemClickInterface<DATA1, DATA2> extends View.OnClickListener, View.OnLongClickListener {
void onItemClick(DATA1 data1, DATA2 data2);
boolean onItemLongClick(DATA1 data1, DATA2 data2);
}
This is an abstract class
public abstract class OnItemClickListener<DATA> implements ItemClickInterface<View, DATA> {
#Override
public void onClick(View v) {
onItemClick(v, (DATA) v.getTag(R.id.tag_view_click));
}
#Override
public boolean onLongClick(View v) {
return onItemLongClick(v, (DATA) v.getTag(R.id.tag_view_click));
}
#Override
public boolean onItemLongClick(View view, DATA data) {
return false;
}
}
You only need it
mAdapter.setOnItemClickListener(new OnItemClickListener<Integer>() {
#Override
public void onItemClick(View view, Integer integer) {
}
#Override
public boolean onItemLongClick(View view, Integer integer) {
return true;
}
});
I found one of the shortest ways using androidx lifecycle mutable live data
Adapter:
private val onItemClickListener = MutableLiveData<YourAdapterItem>()
override fun onBindViewHolder(holder: GifsViewHolder, position: Int) {
holder.itemView.setOnClickListener { onItemClickListener.value = gifs[position] }
}
fun getOnItemClickListener(): MutableLiveData<Gif> {
return onItemClickListener
}
anywhere in MainActivity
yourFancyAdapter.getOnItemClickListener().observe(this, Observer {
println(it)
})

Categories

Resources