How to get specific view clicked from RecyclerView with onInterceptTouchEvent? - android

I have been looking for, but still I have no found anything. I tried
View child = mirecycler.findChildViewUnder(e.getX(), and.getY());
and it worked only to get the parent in my case the CardView container "row" , I need get specific TextView or ImageView.
I want to set onClick to ImageView without affect onClick of CardView.
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && mGestureDetector.onTouchEvent(e)) {
Toast.makeText(MainActivity.this, "tocaste " + child, Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}

If you have to use onInterceptTouchEvent I'm not sure I can help you. I only use it when I don't have to manage specific clicks, but maybe my solution can work together with it (Although I didn't tried it)
I will provide a solution for you to detect the point where the click was made, using a customer adapter:
public class SomeRecyclerAdapter extends RecyclerView.Adapter<SomeRecyclerAdapter.SomeListViewHolder> {
....
#Override
public void onBindViewHolder(final SomeViewHolder holder, final int position) {
SomeItem item = someList.get(position);
holder.itemView.setTag(item);
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
SomeListItem item = (SomeListItem) holder.itemView.getTag();
Toast.makeText(context, "Recycle Click " + item.getName(), Toast.LENGTH_SHORT).show();
}
});
holder.vImage.setTag(item);
holder.vImage
.setOnClickListener(new CompoundButton.OnClickListener(){
#Override
public void onClick(View v) {
SomeListItem item = (SomeItem) holder.vImage.getTag();
Toast.makeText(context, item.getName() + " Image Clicked", Toast.LENGTH_SHORT).show();
}
});
....
public static class SomeListViewHolder extends RecyclerView.ViewHolder{
protected ImageView vImage;
public SomeListViewHolder(View itemView) {
super(itemView);
vImage = (ImageView) itemView.findViewById(R.id.tem_some_list);
}
}
}
My example is how to handle a click in a Recycler Item Image.
I've tried this solution and it works, this meaning that if you click in the item (except the Image) it will print "Recycle Click ...", and if you click on the image it will print " .... Image Clicked".
Maybe (not tested, but you can do it and let us know) you can use the onInterceptTouchEvent for the CardView, and the onClickListener for the image.
I hope this is what you were looking for and if you find a better solution let me know.

public class RecyclerViewItemClickListener implements RecyclerView.OnItemTouchListener {
private static final String TAG = RecyclerItemClickListener.class.getSimpleName();
public void setListener(OnItemClickListener mListener) {
this.mListener = mListener;
}
private OnItemClickListener mListener;
public RecyclerViewItemClickListener(Context context) {
this(context, null);
}
GestureDetector mGestureDetector;
public RecyclerViewItemClickListener(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());
View exactView = findExactChild(childView, e.getX(), e.getY())
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
mListener.onItemClick(view, exactView, view. view.getChildPosition(childView));
}
return false;
}
#Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
public View findExactChild(View childView, int x, int y){
if(!(childView instanceof ViewGroup)) return child;
ViewGroup group = (ViewGroup) childView;
final int count = group.getChildCount();
for (int i = count - 1; i >= 0; i--) {
final View child = group.getChildAt(i);
final float translationX = child.getTranslationX();
final float translationY = child.getTranslationY();
if (x >= child.getLeft() + translationX &&
x <= child.getRight() + translationX &&
y >= child.getTop() + translationY &&
y <= child.getBottom() + translationY) {
return child;
}
}
return null;
}
}
As you said findViewUnder returns only the container. You need another pass to run to find out if x and y of the touch event are on any child of the container.
View exactView = findExactChild(childView, e.getX(), e.getY())
This will run another pass to find exact child.

So one solution would be to use an onTouchListener. What you can do is set an onTouchListener on you child ImageView and return true so that the touch doesn't get passed up to the view parent.

NBajanca's answer works well but within adapter is not possible access to UI elements of mainactivity, so a solution would be the use de an callback.
our code in adapter, then, would look like this.
public class NegociosAdapter extends RecyclerView.Adapter<NegociosAdapter.ViewHolder> {
interface MyCallback{
void Presionofoto(int position);
}
private MyCallback callback;
public NegociosAdapter(ArrayList<Negocios> negociosArrayList,MyCallback callback) {
this.negociosArrayList = negociosArrayList;
this.callback=callback;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView foto;
public TextView nombre;
public ViewHolder(View itemView) {
super(itemView);
foto = (ImageView) itemView.findViewById(R.id.foto);
nombre = (TextView) itemView.findViewById(R.id.txtnombre);
}
}
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
Negocios rowNegocio = negociosArrayList.get(position);
viewHolder.nombre.setText(rowNegocio.getNombre());
viewHolder.foto.setImageBitmap(rowNegocio.getImagen());
viewHolder.foto.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
callback.Presionofoto(position);
//When click here method "Presionofoto" be running on mainactivity
}
});
}
#Override
public int getItemCount() {
return negociosArrayList.size();
}
}
in mainactivity this:
public class MainActivity extends AppCompatActivity implements NegociosAdapter.MyCallback{
NegociosAdapter adapter = new NegociosAdapter(datosResult,this);
recycler.setAdapter(adapter);
//we have that implement method abstract "Presionofoto"
#Override
public void Presionofoto(final int position) {
//here we can access to UI, this
Toast.makeText(getBaseContext(), "Hello From MainActivity", Toast.LENGTH_SHORT).show();
}
}

Related

how to handle both RecyclerView on item click listener for its rows and the views inside it

I have recyclerView which contains some text view and 2 image view.
I would like to do a task when user clicks on each row and another task when user clicks on each of image views .. but I can only handle one of them at the same time.
Now if I set recylerview.addOnItemTouchListener I can't handle images views onclicks with setting on clicklistener for them in the adapter because when user clicks on image view the row's click listener will trigger.
Below code is my class for handling recycler view items
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
private OnItemClickListener mListener;
public interface OnItemClickListener {
public void onItemClick(View view, int position);
public void onLongItemClick(View view, int position);
}
GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) {
mListener = listener;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
#Override
public void onLongPress(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null && mListener != null) {
mListener.onLongItemClick(child, recyclerView.getChildAdapterPosition(child));
}
}
});
}
#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 true;
}
return false;
}
#Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }
#Override
public void onRequestDisallowInterceptTouchEvent (boolean disallowIntercept){}
and this is my holder inside adapter
public MyViewHolder(View view) {
super(view);
tv_word = (TextView) view.findViewById(R.id.tv_word_show);
tv_english = (TextView) view.findViewById(R.id.tv_english_show);
tv_translation = (TextView) view.findViewById(R.id.tv_tranlation_show);
tv_numbershow = (TextView) view.findViewById(R.id.tv_numershow);
fav = (ImageView) view.findViewById(R.id.iv_fav);
more = (ImageView) view.findViewById(R.id.iv_more);
fav.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(mContext,"show some text for test",Toast.LENGTH_LONG).show();
}
});
I appreciate any help for my problem
Create Interface in you adapter class.
public void setClickListener(ClickListener clickListener) {
this.clickListener = clickListener;
}
public interface ClickListener {
void ItemClicked(View v, int position);
}
Declare the object on top like this:
ClickListener clickListener;
holder.view.setOnclickListener(new OnclickListener(
#Override
public void onClick(View v) {
if (clickListener != null) {
clickListener.ItemClicked(v, getAdapterPosition());
}
}
);
Implement this interface in class and intialize the clicklistener where you are setting adapter to recyclerview.
adapter.setClickListener(this);
When you will implement this in class you will get following method:
#Override
public void ItemClicked(View v, int position) {
}
Apply switch case from v.getId() you can handle as many click listeners you need to implement.

Handle Click Button in recycler view

I have recycler view with OnItemTouchListener and each row this recycler has a button with onclick event.
I want after click button event work but OnItemTouchListener work.
please help. my code.
public class RecyclerViewTouchListener implements RecyclerView.OnItemTouchListener {
private GestureDetector gestureDetector;
private ClickListener clickListener;
public RecyclerViewTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
this.clickListener=clickListener;
gestureDetector=new GestureDetector(new GestureDetector.SimpleOnGestureListener(){
#SuppressWarnings("deprecation")
#Override
public boolean onSingleTapUp(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child!=null && clickListener!=null){
clickListener.OnClick(child,recyclerView.getChildPosition(child));
}
return true;
}
});
}
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
//noinspection deprecation
clickListener.OnClick(child, rv.getChildPosition(child));
}
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
public static interface ClickListener {
void OnClick(View view,int position);
}
}
this code in viewholder
deleteButton.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
callbacks.removePosition(getAdapterPosition());
}
});
Sorry for my straightness, but first of all delete this code. Then you need to create your own RecyclerAdapter that will have such snippet of code:
View.OnClickListener clickListener;
public void setOnItemClickListener(View.OnClickListener clickListener) {
this.clickListener = clickListener;
}
#Override
public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (onClickListener != null) {
holder.itemView.setOnClickListener(clickListener);
}
}
Then somewhere where you work with RecyclerView (Activity, Fragment, custom View, custom Wrapper for View) you should do something like that:
List<SomeItem> someItems = getSomeItems();//Receive items for adapter from some place
MyRecyclerAdapter adapter = new MyRecyclerAdapter(someItems);
adapter.setOnItemClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = orderListView.getChildAdapterPosition(v);
SomeItem item = someItems.get(position);
Log.v("CLICKED", "Clicking on item(" + position + ", " + item + ")");
}
});
recyclerView.setAdapter(adapter);
Next to your question about clicking on buttons inside any item.
Define your own Holder that keeps references for View components inside you item:
public static class MyHolder extends RecyclerView.ViewHolder {
private final Button deleteButton;
public MyHolder(View itemView) {
super(itemView);
deleteButton = (Button) itemView.findViewById(R.id.delete_button);
}
public Button getDeleteButton() {
return deleteButton;
}
}
Then define your own inteface for define clicks on delete button. For example:
public interface OnDeleteButtonItemClickListener {
void onDeleteIsClick(View button, int position);
}
Then attach your interface to Adapter:
View.OnClickListener clickListener;
OnDeleteButtonItemClickListener deleteButtonListener;
public void setOnItemClickListener(View.OnClickListener clickListener) {
this.clickListener = clickListener;
}
public void setDeleteButtonListener(OnDeleteButtonItemClickListener deleteButtonListener) {
this.deleteButtonListener= deleteButtonListener;
}
#Override
public final void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
if (clickListener!= null) {
holder.itemView.setOnClickListener(clickListener);
}
if (deleteButtonListener!= null) {
MyHolder myHolder = (MyHolder) holder;
myHolder.getDeleteButton().setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
deleteButtonListener.onDeleteIsClick(v, position)
}
});;
}
}
And then update your place, where you work with RecyclerView:
List<SomeItem> someItems = getSomeItems();//Receive items for adapter from some place
MyRecyclerAdapter adapter = new MyRecyclerAdapter(someItems);
adapter.setOnItemClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = orderListView.getChildAdapterPosition(v);
SomeItem item = someItems.get(position);
Log.v("CLICKED", "Clicking on item(" + position + ", " + item + ")");
}
});
adapter.setDeleteButtonListener(new OnDeleteButtonItemClickListener {
#Override
public void onDeleteIsClick(View button, int position) {
SomeItem item = someItems.get(position);
Log.v("CLICKED", "Clicking on delete button of item(" + position + ", " + item + ")");
}
});
recyclerView.setAdapter(adapter);

Defining a RecyclerView's onCLickListener in an Activity

A RecyclerView is different from a ListView because it doesn't offer an onItemCLickListener class to handle click events.
This can be tackled if not a lot is happening behind the scenes when an item is clicked, by defining an onClickListener in the ViewHolder.
What if the text or whatever that has to be passed to the next Activity doesnt really exist in the views that the ViewHolder has access to, but is rather a part of the Activity which has the RecyclerView?
In that case, the onClickListener must be implemented inside the activity so that the text can be forwarded. It is possible.
One way is to add an Invisible View holding that text, and then doing what has been done before; implementing onClickListener in the Adapter.
Pass that text, somehow, to the Adapter.
How can "2." be implemented?
See the example below to create RecyclerView OnClick.
Example Code:
public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {
public interface OnItemClickListener {
void onItemClick(ContentItem item);
}
private final List<ContentItem> items;
private final OnItemClickListener listener;
public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
this.items = items;
this.listener = listener;
}
#Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, parent, false);
return new ViewHolder(v);
}
#Override public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(items.get(position), listener);
}
#Override public int getItemCount() {
return items.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private TextView name;
private ImageView image;
public ViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.name);
image = (ImageView) itemView.findViewById(R.id.image);
}
public void bind(final ContentItem item, final OnItemClickListener listener) {
name.setText(item.name);
Picasso.with(itemView.getContext()).load(item.imageUrl).into(image);
itemView.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
listener.onItemClick(item);
}
});
}
}
}
And Use RecyclerView Adapter using below code:
recycler.setAdapter(new ContentAdapter(items, new ContentAdapter.OnItemClickListener() {
#Override public void onItemClick(ContentItem item) {
Toast.makeText(getContext(), "Item Clicked", Toast.LENGTH_LONG).show();
}
}));
See Set a click listener to a RecyclerView
Hope it helped you.
Write a public function in your activity like:
public void onClickCalled(String anyValue) {
// Call another acitivty here and pass some arguments to it.
}
Now in your adapter's onClick funciton
#Override
public void onClick(View view) {
((YouActivityClass) context).onClickCalled("your argument here");
}
I had this problem with the first good answer.
Had a problem(Context Error).
I put the following code in onBindViewHolder.
In adapter :
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((YouActivityClass) v.getContext()).onClickCalled("your argument here");
}
});
In Activity :
public void onClickCalled(String anyValue) {
// Call another acitivty here and pass some arguments to it.
}
The Whole
This can be solved by creating your own listener and passing it to the adapter.
public interface PositionClickListener
{
void itemClicked(int position);
}
The Activity could implement this interface, or instantiate an anonymous inner class. So your adapter could look like this:
public class MyRecyclerAdapter extends RecyclerView.Adapter
{
public final PositionClickListener listener;
public MyRecyclerAdapter(PositionClickListener listener)
{
this.listener = listener;
}
private void createView(int position)
{
View v = new View();
v.setOnClickListener(new OnClickListener(){
listener.itemClicked(position);
});
}
}
Where ListView has an onItemClickListener, RecyclerView has addOnItemTouchListener.
You can provide the listener to the recycler view from within the activity, thus the scoping will be such that the listener should be able to reference the text.
You could use something like following implementation of the OnItemTouchListener which also works to capture left and right swipes.
public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {
private static final String TAG = "RecyclerTouchListener";
private static final int SWIPE_MIN_DISTANCE = 120;
private static final int SWIPE_THRESHOLD_VELOCITY = 200;
private static final int SWIPE_MAX_OFF_PATH = 250;
private OnTouchActionListener mOnTouchActionListener;
private GestureDetectorCompat mGestureDetector;
public static interface OnTouchActionListener {
public void onLeftSwipe(View view, int position);
public void onRightSwipe(View view, int position);
public void onClick(View view, int position);
}
public RecyclerTouchListener(Context context, final RecyclerView recyclerView,
OnTouchActionListener onTouchActionListener){
mOnTouchActionListener = onTouchActionListener;
mGestureDetector = new GestureDetectorCompat(context,new GestureDetector.SimpleOnGestureListener(){
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Log.d(TAG, "On Single Tap Confirmed");
// Find the item view that was swiped based on the coordinates
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
int childPosition = recyclerView.getChildPosition(child);
mOnTouchActionListener.onClick(child, childPosition);
return false;
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY) {
Log.d(TAG, "onFling: " + e1.toString() + e2.toString());
try {
if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) {
return false;
}
// Find the item view that was swiped based on the coordinates
View child = recyclerView.findChildViewUnder(e1.getX(), e1.getY());
int childPosition = recyclerView.getChildPosition(child);
// right to left swipe
if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
Log.d(TAG, "Left Swipe");
if (mOnTouchActionListener != null && child != null) {
mOnTouchActionListener.onLeftSwipe(child, childPosition);
}
} else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
Log.d(TAG, "Right Swipe");
if (mOnTouchActionListener != null && child != null) {
mOnTouchActionListener.onRightSwipe(child, childPosition);
}
}
} catch (Exception e) {
// nothing
}
return false;
}
});
}
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
mGestureDetector.onTouchEvent(e);
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
// do nothing
}
}
Then in your activity add your listener along the lines of
mRecyclerView.addOnItemTouchListener(
new RecyclerTouchListener(getActivity(), mRecyclerView,
new RecyclerTouchListener.OnTouchActionListener() {
#Override
public void onClick(View view, int position) {
}
#Override
public void onRightSwipe(View view, int position) {
}
#Override
public void onLeftSwipe(View view, int position) {
}
}))
its easy,we don't have create interfaces look at my code just put this small code and we are
#Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
holder.img.setImageDrawable(img[position%img.length]);
holder.bodyPartName.setText(bodyPart[position]);
if(position==0) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context c = v.getContext();
Intent intent = new Intent(c, BodyPartExcercise.class);
c.startActivity(intent);
}
});
}
}

How to get row of particular position of recycler view in android?

In my application I need to get a complete row of recycler view (becuase I want to get some view of that row) of a particular position.I searched we can get position by method getAdapterPosition().Now after getting position via this method I want to get complete row of that position.How can I achieve it?
Edit: I have some view on every row and I have set onclick listner on buttons in click of that I want get complete row or top layout (parent layout).What I understand is when on click is called it return parent view on which button is wrapped but not complete row.
I used one class for getting views(and Click and Long clicks too)
public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {
private GestureDetector gestureDetector;
private ClickListener clickListener;
public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
this.clickListener = clickListener;
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
#Override
public void onLongPress(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null) {
clickListener.onLongClick(child, recyclerView.getChildPosition(child));
}
}
});
}
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
clickListener.onClick(child, rv.getChildPosition(child));
}
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
public interface ClickListener {
void onClick(View view, int position);
void onLongClick(View view, int position);
}
}
and to use this
/**
* Recycler view item click handler
*/
rvDraft.addOnItemTouchListener(new RecyclerTouchListener(getActivity(),
rvDraft, new RecyclerTouchListener.ClickListener() {
#Override
public void onClick(View view, final int position) {
// click operation
}
#Override
public void onLongClick(View view, final int position) {
Dialogs dialog = new Dialogs();
dialog.showAlertForAction(getActivity(), getString(R.string.are_u_sure_want_to_delete), new AlertInterface() {
#Override
public void buttonYesClick() {
// long press operation
}
#Override
public void buttonNoClick() {
}
});
}
}));
Hope this will help you .
Use a global variable View selectedView. In your custom ViewHolder class, assuming the row item to be a LinearLayout set add the following lines
rowItemLayout = (LinearLayout) itemView.findViewById(R.id.row_item);
rowItemLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
selectedView = rowItemLayout;
..
}
});
Now you can fetch that view using
public View getSelectedView() {
return selectedView;
}
I have got solution to this problem now. First get the recycler view in adapter using below method in adapter class
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
mRecyclerView = recyclerView;
}
and on click event use below code
RecyclerView.ViewHolder holder = mRecyclerView.findViewHolderForLayoutPosition(getAdapterPosition());
and then cast the holder to the actual object as mentioned below
MessageViewHolder messageViewHolder = (MessageViewHolder) holder;
Congrats!! now you have a complete row of that particular position. Happy coding
You can try
getBindingAdapterPosition(view);
getAbsoluteAdapterPosition() ;
getchildAdapterPosition(view);

Handle Button click inside a row in RecyclerView

I am using following code for handling row clicks. (source)
static class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {
private GestureDetector gestureDetector;
private ClickListener clickListener;
public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
this.clickListener = clickListener;
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
#Override
public void onLongPress(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null) {
clickListener.onLongClick(child, recyclerView.getChildPosition(child));
}
}
});
}
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
clickListener.onClick(child, rv.getChildPosition(child));
}
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
}
This works however, if I want to have say a delete button on each row. I am not sure to how to implement that with this.
I attached OnClick listener to delete button which works (deletes the row) but it also fires the onclick on full row.
Can anybody help me in how to avoid full row click if a single button is clicked.
Thanks.
this is how I handle multiple onClick events inside a recyclerView:
Edit : Updated to include callbacks (as mentioned in other comments). I have used a WeakReference in the ViewHolder to eliminate a potential memory leak.
Define interface :
public interface ClickListener {
void onPositionClicked(int position);
void onLongClicked(int position);
}
Then the Adapter :
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private final ClickListener listener;
private final List<MyItems> itemsList;
public MyAdapter(List<MyItems> itemsList, ClickListener listener) {
this.listener = listener;
this.itemsList = itemsList;
}
#Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.my_row_layout), parent, false), listener);
}
#Override public void onBindViewHolder(MyViewHolder holder, int position) {
// bind layout and data etc..
}
#Override public int getItemCount() {
return itemsList.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
private ImageView iconImageView;
private TextView iconTextView;
private WeakReference<ClickListener> listenerRef;
public MyViewHolder(final View itemView, ClickListener listener) {
super(itemView);
listenerRef = new WeakReference<>(listener);
iconImageView = (ImageView) itemView.findViewById(R.id.myRecyclerImageView);
iconTextView = (TextView) itemView.findViewById(R.id.myRecyclerTextView);
itemView.setOnClickListener(this);
iconTextView.setOnClickListener(this);
iconImageView.setOnLongClickListener(this);
}
// onClick Listener for view
#Override
public void onClick(View v) {
if (v.getId() == iconTextView.getId()) {
Toast.makeText(v.getContext(), "ITEM PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(v.getContext(), "ROW PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();
}
listenerRef.get().onPositionClicked(getAdapterPosition());
}
//onLongClickListener for view
#Override
public boolean onLongClick(View v) {
final AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
builder.setTitle("Hello Dialog")
.setMessage("LONG CLICK DIALOG WINDOW FOR ICON " + String.valueOf(getAdapterPosition()))
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.create().show();
listenerRef.get().onLongClicked(getAdapterPosition());
return true;
}
}
}
Then in your activity/fragment - whatever you can implement : Clicklistener - or anonymous class if you wish like so :
MyAdapter adapter = new MyAdapter(myItems, new ClickListener() {
#Override public void onPositionClicked(int position) {
// callback performed on click
}
#Override public void onLongClicked(int position) {
// callback performed on click
}
});
To get which item was clicked you match the view id i.e. v.getId() == whateverItem.getId()
Hope this approach helps!
I find that typically:
I need to use multiple listeners because I have several buttons.
I want my logic to be in the activity and not the adapter or viewholder.
So #mark-keen's answer works well but having an interface provides more flexibility:
public static class MyViewHolder extends RecyclerView.ViewHolder {
public ImageView iconImageView;
public TextView iconTextView;
public MyViewHolder(final View itemView) {
super(itemView);
iconImageView = (ImageView) itemView.findViewById(R.id.myRecyclerImageView);
iconTextView = (TextView) itemView.findViewById(R.id.myRecyclerTextView);
iconTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onClickListener.iconTextViewOnClick(v, getAdapterPosition());
}
});
iconImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onClickListener.iconImageViewOnClick(v, getAdapterPosition());
}
});
}
}
Where onClickListener is defined in your adapter:
public MyAdapterListener onClickListener;
public interface MyAdapterListener {
void iconTextViewOnClick(View v, int position);
void iconImageViewOnClick(View v, int position);
}
And probably set through your constructor:
public MyAdapter(ArrayList<MyListItems> newRows, MyAdapterListener listener) {
rows = newRows;
onClickListener = listener;
}
Then you can handle the events in your Activity or wherever your RecyclerView is being used:
mAdapter = new MyAdapter(mRows, new MyAdapter.MyAdapterListener() {
#Override
public void iconTextViewOnClick(View v, int position) {
Log.d(TAG, "iconTextViewOnClick at position "+position);
}
#Override
public void iconImageViewOnClick(View v, int position) {
Log.d(TAG, "iconImageViewOnClick at position "+position);
}
});
mRecycler.setAdapter(mAdapter);
I wanted a solution that did not create any extra objects (ie listeners) that would have to be garbage collected later, and did not require nesting a view holder inside an adapter class.
In the ViewHolder class
private static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private final TextView ....// declare the fields in your view
private ClickHandler ClickHandler;
public MyHolder(final View itemView) {
super(itemView);
nameField = (TextView) itemView.findViewById(R.id.name);
//find other fields here...
Button myButton = (Button) itemView.findViewById(R.id.my_button);
myButton.setOnClickListener(this);
}
...
#Override
public void onClick(final View view) {
if (clickHandler != null) {
clickHandler.onMyButtonClicked(getAdapterPosition());
}
}
Points to note: the ClickHandler interface is defined, but not initialized here, so there is no assumption in the onClick method that it was ever initialized.
The ClickHandler interface looks like this:
private interface ClickHandler {
void onMyButtonClicked(final int position);
}
In the adapter, set an instance of 'ClickHandler' in the constructor, and override onBindViewHolder, to initialize `clickHandler' on the view holder:
private class MyAdapter extends ...{
private final ClickHandler clickHandler;
public MyAdapter(final ClickHandler clickHandler) {
super(...);
this.clickHandler = clickHandler;
}
#Override
public void onBindViewHolder(final MyViewHolder viewHolder, final int position) {
super.onBindViewHolder(viewHolder, position);
viewHolder.clickHandler = this.clickHandler;
}
Note: I know that viewHolder.clickHandler is potentially getting set multiple times with the exact same value, but this is cheaper than checking for null and branching, and there is no memory cost, just an extra instruction.
Finally, when you create the adapter, you are forced to pass a ClickHandlerinstance to the constructor, as so:
adapter = new MyAdapter(new ClickHandler() {
#Override
public void onMyButtonClicked(final int position) {
final MyModel model = adapter.getItem(position);
//do something with the model where the button was clicked
}
});
Note that adapter is a member variable here, not a local variable
Just wanted to add another solution if you already have a recycler touch listener and want to handle all of the touch events in it rather than dealing with the button touch event separately in the view holder. The key thing this adapted version of the class does is return the button view in the onItemClick() callback when it's tapped, as opposed to the item container. You can then test for the view being a button, and carry out a different action. Note, long tapping on the button is interpreted as a long tap on the whole row still.
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener
{
public static interface OnItemClickListener
{
public void onItemClick(View view, int position);
public void onItemLongClick(View view, int position);
}
private OnItemClickListener mListener;
private GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener)
{
mListener = listener;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener()
{
#Override
public boolean onSingleTapUp(MotionEvent e)
{
// Important: x and y are translated coordinates here
final ViewGroup childViewGroup = (ViewGroup) recyclerView.findChildViewUnder(e.getX(), e.getY());
if (childViewGroup != null && mListener != null) {
final List<View> viewHierarchy = new ArrayList<View>();
// Important: x and y are raw screen coordinates here
getViewHierarchyUnderChild(childViewGroup, e.getRawX(), e.getRawY(), viewHierarchy);
View touchedView = childViewGroup;
if (viewHierarchy.size() > 0) {
touchedView = viewHierarchy.get(0);
}
mListener.onItemClick(touchedView, recyclerView.getChildPosition(childViewGroup));
return true;
}
return false;
}
#Override
public void onLongPress(MotionEvent e)
{
View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
if(childView != null && mListener != null)
{
mListener.onItemLongClick(childView, recyclerView.getChildPosition(childView));
}
}
});
}
public void getViewHierarchyUnderChild(ViewGroup root, float x, float y, List<View> viewHierarchy) {
int[] location = new int[2];
final int childCount = root.getChildCount();
for (int i = 0; i < childCount; ++i) {
final View child = root.getChildAt(i);
child.getLocationOnScreen(location);
final int childLeft = location[0], childRight = childLeft + child.getWidth();
final int childTop = location[1], childBottom = childTop + child.getHeight();
if (child.isShown() && x >= childLeft && x <= childRight && y >= childTop && y <= childBottom) {
viewHierarchy.add(0, child);
}
if (child instanceof ViewGroup) {
getViewHierarchyUnderChild((ViewGroup) child, x, y, viewHierarchy);
}
}
}
#Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e)
{
mGestureDetector.onTouchEvent(e);
return false;
}
#Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent){}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
Then using it from activity / fragment:
recyclerView.addOnItemTouchListener(createItemClickListener(recyclerView));
public RecyclerItemClickListener createItemClickListener(final RecyclerView recyclerView) {
return new RecyclerItemClickListener (context, recyclerView, new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
if (view instanceof AppCompatButton) {
// ... tapped on the button, so go do something
} else {
// ... tapped on the item container (row), so do something different
}
}
#Override
public void onItemLongClick(View view, int position) {
}
});
}
You need to return true inside onInterceptTouchEvent() when you handle click event.
Just put an override method named getItemId
Get it by right click>generate>override methods>getItemId
Put this method in the Adapter class
You can check if you have any similar entries first, if you get a collection with size 0, start a new query to save.
OR
more professional and faster way.
create a cloud trigger (before save)
check out this answer
https://stackoverflow.com/a/35194514/1388852

Categories

Resources