How to handle multiple layout clicks in recyclerView in Android

In our app we have list of messages. We are switching to RecyclerView. Our messages can consist from texts, images. Sample message can look like this.

To handle clicks use this class:

public class RecyclerViewItemClickListener implement  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 RecyclerViewItemClickListener(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 childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
                if(childView != null && mListener != null)
                {
                    mListener.onItemLongClick(childView, recyclerView.getChildAdapterPosition(childView));
                }
            }
        });
    }

    @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) {
    }
}

and we implement this clicks in activity like this:

chatView.setLongClickable(true);
        chatView.addOnItemTouchListener(
                new RecyclerViewItemClickListener(this, chatView, new RecyclerViewItemClickListener.OnItemClickListener() {
                    @Override public void onItemClick(View view, int position) {

                        //handleSingleClick(view, position);

                    }
                    @Override
                    public void onItemLongClick(View view, int position)
                    {
                        handleLongPress(position);
                    }
                })
        );

Clicks for message is working, now I want also handle userAvatar clicks from same GestureDetection. Is it possible to detect which list item layout was clicked from GestureDetector?

In other words, can I get GestureDetecter clicked child. In my case click is set to whole item(avatar, text message, image). Can I detect which element inside this item was clicked(for example I want catch avatar click).

Answers


Handle the click in the adapter of your RecyclerView like below example:

private class BookmarkViewHolder extends RecyclerView.ViewHolder {

        protected ImageView thumbnailIcon;
        protected TextView postTitle;
        protected TextView postViewCount;

        public BookmarkViewHolder(View v) {
            super(v);

            thumbnailIcon = (ImageView) v.findViewById(R.id.thumbnailIcon);
            postTitle = (TextView) v.findViewById(R.id.postTitleTV);
            postViewCount = (TextView) v.findViewById(R.id.viewCountTV);

            v.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {                                                
                }
            });

            thumbnailIcon.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {                                                
                }
            });

            postTitle.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {                                                
                }
            });


        }
    }

You may try this code :

GestureDetector detector;

then:

detector = new GestureDetector(MainActivity.this, MainActivity.this);

    avatar.setOnTouchListener(new OnTouchListener() {
        public boolean onTouch(View view, MotionEvent e) {
            detector.onTouchEvent(e);
            return false;
        }
    });

You can set listener into the adapter class. Here is the code :

ViewHolder class inside adapter :

    public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    TextView textViewFirstName;
    ImageView imageViewProfile;

    public MyViewHolder(View view) {
        super(view);
        textViewFirstName = (TextView) view.findViewById(R.id.txtName);
        imageViewProfile = (ImageView) view.findViewById(R.id.imageViewProfile);

//setOnClick
        view.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
    //check if the image you want to click should not invoke listener from activity/fragment
        if (v.getId() != R.id.imageViewProfile) {
            if (mItemClickListener != null) {
                mItemClickListener.onItemClick(v, getPosition());
            }
        }
    }
}

Set your imageView click in Adapter class onBindViewHolder :

@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {

    holder.textViewFirstName.setText(mArrayListTypeDetials.get(position).getFirstname());
    //set imageView click here
    holder.imageViewProfile.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
           //ImageView clicked
        }
    });
}

Creating interface inside Adapter class :

//interface for recyclerView item click
public interface OnItemClickListener {
    void onItemClick(View view, int position);
}

/*
method to set listener to the adapter ViewHolder item
 */
public void setOnItemClickListener(final OnItemClickListener mItemClickListener) {
    this.mItemClickListener = mItemClickListener;
}

Setting interface from Activity/Fragment :

    mAdapter.setOnItemClickListener(new Adapter.OnItemClickListener() {
        @Override
        public void onItemClick(View view, int position) {
            Intent intent = new Intent(getActivity(), ABC.class);
            startActivity(intent);
        }
    });

based on this post.

//init listener
rVDevicesList.addOnItemTouchListener(new  RecyclerItemClickListener(getContext(),
            rVDevicesList,
            this, R.id.tv_wonderful_item_share,  R.id.tv_wonderful_item_delete));  




public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {

private OnItemClickListener mListener;
private int[] extraIds;
private Rect childRect = new Rect();

public interface OnItemClickListener {
    void onItemClick(View view, int position);

    void onLongItemClick(View view, int position);
}

private GestureDetector mGestureDetector;

/**
 * 
 * @param context
 * @param recyclerView
 * @param listener
 * @param ids: an alternative parameter
 */
public RecyclerItemClickListener(Context context,
                                 final RecyclerView recyclerView,
                                 OnItemClickListener listener, final int... ids) {
    mListener = listener;
    extraIds = ids;
    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) {
    final float x = e.getX();
    final float y = e.getY();
    Log.d("onInterceptTouchEvent", "onInterceptTouchEvent: " + x + " " + y);
    View childView = view.findChildViewUnder(e.getX(), e.getY());
    if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
        final int position = view.getChildAdapterPosition(childView);
        View target = getViewBy(childView, x, y, extraIds);
        //if target is non-null,so you click the view. 
        mListener.onItemClick(target, position);
        return true;
    }
    return false;
}

/**
 * find the view ,which view_rect contains the {x,y} in the window.
 * @param view: this view is  item layout view,may be a viewGroup
 * @param x
 * @param y
 * @param ids
 * @return true: hit a view which id in ids.
 */
private View getViewBy(View view, float x, float y, int... ids) {
    if (ids == null || ids.length == 0 || !(view instanceof ViewGroup))
        return view;
    for (int i : ids) {
        View vChild = view.findViewById(i);
        if (vChild == null) {
            Log.d("onInterceptTouchEvent", "null");
        } else {
            int xy[] = new int[2];
            vChild.getLocationOnScreen(xy);
            childRect.left = xy[0];
            childRect.top = xy[1];
            childRect.right = xy[0] + vChild.getWidth();
            childRect.bottom = xy[1] + vChild.getHeight();
            boolean hit = childRect.contains((int) x, (int) y);
            Log.d("onInterceptTouchEvent", "find view: " + hit);
            if (hit)
                return vChild;
        }
    }
    return view;
}

@Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {

}

@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}

}


I tried this same way like @user320676, But I tried to complete with a Listener:

interface MultipleClickListener {
        void onItemClickOne(int position, View v);
        void onItemClickTwo(int position, View v);
        void onItemClickThree(int position, View v);
}

then you can use the listener in a activity or fragment. Here you can see my full Adapter:

public class MultipleClicksAdapter extends RecyclerView
        .Adapter<MultipleClicksAdapter
        .DataObjectHolder> {

    private ArrayList<MultipleClickData> mDataset;
    private static MultipleClickListener sClickListener;

    static class DataObjectHolder extends RecyclerView.ViewHolder {

        @BindView(R.id.multiple_clicks_one_textView)
        TextView mOne;
        @BindView(R.id.multiple_clicks_two_textView)
        TextView mTwo;
        @BindView(R.id.multiple_clicks_three_textView)
        TextView mThree;

        DataObjectHolder(View itemView) {
            super(itemView);

            ButterKnife.bind(this, itemView);

            mOne.setOnClickListener((View v) ->
                    sClickListener.onItemClickOne(getAdapterPosition(), mOne));

            mTwo.setOnClickListener((View v) ->
                    sClickListener.onItemClickTwo(getAdapterPosition(), mTwo));

            mThree.setOnClickListener((View v) ->
                    sClickListener.onItemClickThree(getAdapterPosition(), mThree));
        }

    }

    void setOnItemClickListener(MultipleClickListener myClickListener) {
        this.sClickListener = myClickListener;
    }

    MultipleClicksAdapter(ArrayList<MultipleClickData> myDataset) {
        mDataset = myDataset;
    }

    @Override
    public MultipleClicksAdapter.DataObjectHolder onCreateViewHolder(ViewGroup parent,
                                                                           int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.multiple_clicks_list_item, parent, false);

        MultipleClicksAdapter.DataObjectHolder dataObjectHolder = new MultipleClicksAdapter.DataObjectHolder(view);
        return dataObjectHolder;
    }

    @Override
    public void onBindViewHolder(MultipleClicksAdapter.DataObjectHolder holder, int position) {
        holder.mOne.setText(mDataset.get(position).getmTitle());
        holder.mTwo.setText(mDataset.get(position).getmSubTitle());
        holder.mThree.setText(mDataset.get(position).getmThirdTitle());
    }

    @Override
    public int getItemCount() {
        return mDataset.size();
    }

    interface MultipleClickListener {
        void onItemClickOne(int position, View v);
        void onItemClickTwo(int position, View v);
        void onItemClickThree(int position, View v);
    }

}

Also you can check the complete code in GitHub.


Click need to be handle in Adapter of RecyclerView, Visit RecyclerView example similar like onItemClickListener of ListView


Need Your Help

Trying to add delay to jQuery AJAX request

javascript jquery ajax delay onkeyup

I am trying to delay an AJAX request so that it is sent out 2-3 seconds after the LAST keyup of an input cell.

AngularJS NG-Repeat Select and set selected on page load?

angularjs asp.net-mvc

I am generating a grid thru Angular and each row will have a dropdown. I want the dropdown to populate with data from the server which is working but during page load, it needs to set the selected ...