1

Ok, here is the deal. I have wasted 13 hours yesterday dealing with the click handling on ListView and then switched to RecyclerView (thanks to a member here) solving my original issue with handling item clicks. I am new to android programming so bear with me please.

I managed to solve my problem using the RecyclerView\RecyclerItemClickListener from here:

RecyclerView onClick

Now, that I have added some buttons that I want to use, this complicates things. I want to be able to click on the buttons and do a variety of actions while clicking on the main item and doing another action. Is there any way to extend the RecyclerItemClickListener to allow me to check if the click is on the buttons or the Recycler item itself. I found that there are coordinates on the RecyclerItemClickListener. So, I thought to myself maybe I could use those to determine the controls being clicked but I remembered that devices have different screen sizes.

I have seen some way to handle the clicking at the ViewHolder level but I want to check for both the buttons and the item itself. I have been searching over and over and I have decided to use my layout to specify onclick elements on the layout tag and on every element on the layout forwarding the non-button elements to the item (layout tag) method while giving the buttons separate methods.

Is what I am doing a hack? Can you point me the right direction to accomplish what I want?

John Anderson
  • 124
  • 1
  • 1
  • 9

2 Answers2

1

You can do something like below in your ViewHolder:

class MyViewHolder extends RecyclerView.ViewHolder {

    private Button myButton;

    public MyViewHolder(View itemView) {
        super(itemView);
        myButton = itemView.findViewById(R.id.my_button);

        itemView.setOnClickListener(mainViewClickListener);
        myButton.setOnClickListener(buttonClickListener);
    }

    private View.OnClickListener mainViewClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // Do main thing here.
        }
    };

    private View.OnClickListener buttonClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // Do button click handling here
        }
    };
}
Abdullah
  • 7,143
  • 6
  • 25
  • 41
  • Thanks for the feedback, Abdullah. Much appreciated. This is actually the programmatic version of what I did on my XML. It is interesting nontheless. – John Anderson Dec 07 '16 at 23:57
  • Could you add your code so that we can have better idea what you tried to do. – Abdullah Dec 08 '16 at 06:33
  • It seems there is not better way to accomplish what I want exactly. So, I decided to go with my original solution. I am setting this as the answer because it provides a programmatic version of what I am doing. – John Anderson Dec 08 '16 at 13:36
1

You may use a Facade view to deal with this issue. By creating LinearLayout within and LinearLayout you can choose what deals with what clicks. So for how to handle long clicks setOnCreateContextMenuListener, short clicks and clicks on items within the view.

Here is an example layout, take note of focusable/clickable params.

Layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clickable="false"
    android:focusable="false"
    >

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/showClickable"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clickable="true"
        android:focusable="true"
        android:background="?android:attr/selectableItemBackground"
        >

        <TextView
            android:text="TextView"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:id="@+id/itemShowTitle"
            android:layout_weight="1"
            />

        <Button
            android:id="@+id/buttonWorthClicking"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="button worth clicking"/>
    </LinearLayout>
</LinearLayout>

Then we need the ViewHolder that passes along or deals with clicks as needed

public class ShowsRecyclerViewAdapter extends RecyclerView.Adapter<ShowsRecyclerViewAdapter.ViewHolder> {

    public ShowsRecyclerViewAdapter(List<Show> shows) {
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Context context = parent.getContext();
        LayoutInflater inflater = LayoutInflater.from(context);
        View view = inflater.inflate(R.layout.show_list_item, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.itemShowTitle.setText("" + position);
    }

    @Override
    public int getItemCount() {
        return 10;
    }

    static class ViewHolder extends RecyclerView.ViewHolder
            implements View.OnClickListener, View.OnCreateContextMenuListener {

        final TextView itemShowTitle;
        final Button buttonWorthClicking;

        ViewHolder(final View itemView) {
            super(itemView);
            itemView.setOnClickListener(this);
            itemShowTitle = (TextView) itemView.findViewById(R.id.itemShowTitle);
            buttonWorthClicking = (Button) itemView.findViewById(R.id.buttonWorthClicking);

            setViewFacade(itemView);

            itemView.setOnCreateContextMenuListener(this);
        }

        /**
         * Because the layout is nested to for the ripple we build a facade to pass along clicks
         */
        private void setViewFacade(final View itemView) {
            View facedForClicks = itemView.findViewById(R.id.showClickable);
            facedForClicks.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    // pass along the short click
                    ViewHolder.this.onClick(itemView);
                }
            });
            facedForClicks.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
                @Override
                public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
                    // no-op but passes up to the ViewHolder
                }
            });

            buttonWorthClicking.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.d("one-off", "the button was clicked");
                }
            });
        }

        @Override
        public void onClick(View v) {
            Log.d("one-off", "short click on item");
        }

        @Override
        public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
            Log.d("one-off", "long click on item");
        }
    }
}

I made a branch on my Podcast Player project on github you can see a working example.

Class

XML

branch

Victory
  • 5,811
  • 2
  • 26
  • 45
  • Thanks Victory. I appreciate it. The facade concept is new to me. I will try to understand and digest the solution then come back to comment. – John Anderson Dec 07 '16 at 23:59
  • This is very interesting. You are redirecting anything on the layout tag to the item click method. I guess one could add the extra click handlers for the extra buttons. Though, two of the TextView elements consume the clicks and prevent the redirection at my case. On my XML Layout, I have a TextView, LinearLayout (which has two TextViews) and a button (I removed the rest). The inner TextViews consume the tabs while the main TextView does not. I have even tried adding an id to both the Layout elements then adding an extra segement for the one inside at setViewFacade. That did not work, either. – John Anderson Dec 08 '16 at 01:46