Internal Recyclerview does not receive click event

I have a parent recyclerview with filled cards and an internal recyclerview inside each card.

Scrolling for the internal recyclerview inside the maps was disabled, but it also affected the ability of the internal recyclerview to receive click events.

To disable scrolling, I followed a very similar Lucas Crawford answer here, which suggested creating your own recyclerview class and overriding dispatchTouchEvent: Disable scrolling in a child Recyclerview android

My class is as follows:

public class ScrollThroughRecyclerView extends RecyclerView { private boolean isOnClick; public ScrollThroughRecyclerView(Context context) { super(context); } public ScrollThroughRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); } public ScrollThroughRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { final int action = ev.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; Log.e("actionmasked", "" + actionMasked); switch (actionMasked) { case MotionEvent.ACTION_DOWN: Log.e("motionDown", "onclick: "+isOnClick); isOnClick = true; break; case MotionEvent.ACTION_MOVE: isOnClick = false; Log.e("motionMove", "onclick: "+isOnClick); return true; case MotionEvent.ACTION_UP: Log.e("motionUp", "onclick: "+isOnClick); if (isOnClick) { return super.dispatchTouchEvent(ev); } break; default: return true; } return true; } 

However, it never logs click events, but scrolling is disabled correctly.

How can I get an internal recyclerview to log click events?

+2
android android-recyclerview motionevent
Aug 30 '15 at 11:02
source share
1 answer

So, about a day later, trying to figure it out, I finally managed to solve this problem.

After completing all these steps, you should:

  • An external recyclerview that does not interfere with scroll events of the internal recyclerview — scrolling in the internal recyclerview is disabled. This is useful in a scenario where you want to use the collapsible toolbar approach with both external and internal recyclerview.
  • Elements in the internal recyclerview can be clicked (which uses an internal recyclerview that displays a list of elements if they cannot be clicked).

Firstly, I watched several videos on the topic of touch in android: https://www.youtube.com/watch?v=SYoN-OvdZ3M&list=PLonJJ3BVjZW6CtAMbJz1XD8ELUs1KXaTD&index=19

I got a video link from here: Android: difference between onInterceptTouchEvent and dispatchTouchEvent?

Now I had to configure my onDispatchTouchEvent:

 @Override public boolean dispatchTouchEvent(MotionEvent ev) { final int action = ev.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; Log.e("actionmasked", "" + actionMasked); switch (actionMasked) { case MotionEvent.ACTION_DOWN: return super.dispatchTouchEvent(ev); case MotionEvent.ACTION_MOVE: return true; case MotionEvent.ACTION_CANCEL: return true; case MotionEvent.ACTION_UP: return super.dispatchTouchEvent(ev); default: return super.dispatchTouchEvent(ev); } 

I called super.dispatchTouchEvent (ev) in both DOWN and UP and the default cases, since we want the child of the internal recyclerview to handle these events. The event should go from the view group, which is this custom recyclerview (ScrollThroughRecyclerView) for the views in the recyclerview.

For MOVE and CANCEL, we return true to say that the internal recyclerview has handled these events, and the event can return back to the parent element, which will allow the external recyclerview to scroll properly. This will not interfere with the behavior of the resettable toolbar application.

Now we need a custom onInterceptTouchEvent method:

 @Override public boolean onInterceptTouchEvent(MotionEvent e) { final int action = e.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; if (actionMasked == MotionEvent.ACTION_DOWN) { return false; //intercepted by the viewgroup and passed down to child } else if (actionMasked == MotionEvent.ACTION_UP) { return false; //intercepted by the viewgroup and passed down to child } return super.onInterceptTouchEvent(e); } 

For UP and DOWN, we return false, since we want the child inside the internal recyclerview to handle these events (from UP and DOWN, we can determine which element in recyclerview actually clicked).

For everything else, we use the default behavior, so I called: super.onInterceptTouchEvent (e)

Now in the recycliewiew adapter:

 @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { if (holder instanceof PostViewHolder) { ((PostViewHolder) holder).rl.setOnTouchListener(onTouchListener); } 

Set the touch artist to the view you are listening for a touch event in your recyclerview. For me, since I listen to clicks across the entire recyclerview line, I set the touch listener to rl, which means the relative Layout of the line displayed in the recyclerview.

The touch artist will not receive the DOWN and UP tags that the view group views in the onInterceptTouchEvent method.

Since we only have DOWN and UP motionevents, click detection can be a little more tedious than the general way to say setOnClickListener. Also, since you are using Touch, Touch actually overrides onClickListener and nothing is called onClickListener. To detect clicks on an element in recyclerview via onTouchListener you will need this method:

 View.OnTouchListener onTouchListener = new View.OnTouchListener() { private float startX; private float startY; private static final int CLICK_ACTION_THRESHHOLD = 5; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startX = event.getX(); startY = event.getY(); break; case MotionEvent.ACTION_UP: { float endX = event.getX(); float endY = event.getY(); if (isAClick(startX, endX, startY, endY)) { Log.e("UserClick", "user has clicked");// WE HAVE A CLICK!! } break; } default: return true; } return true; } private boolean isAClick(float startX, float endX, float startY, float endY) { float differenceX = Math.abs(startX - endX); float differenceY = Math.abs(startY - endY); if (differenceX > CLICK_ACTION_THRESHHOLD || differenceY > CLICK_ACTION_THRESHHOLD) { return false; } return true; } }; 
+4
Aug 30 '15 at 18:29
source share



All Articles