Search code examples
androidandroid-listviewnavigationnavigation-drawerandroid-cursoradapter

Handling custom list items click inside a navigation drawer ListView


I have custom rows for the ListView hosted in my navigation drawer, everything worked fine when I only had a simple TextView for each row.

I decided then to add two ImageButtons for each row, displaying EDIT and DISCARD ImageButtons, the problem in this case is that ImageButtons steal focus and the ListView Adapter onItemClickListener no longer receives onItemClick event, so the content of the main fragment can't change accordingly to which element i click on the drawer.

I use a custom CursorAdapter to populate the ListView of the drawer, so I decided to create an OnClickListener for each element of the row. Every single item of the row now correcly reacts to click events...The problem now is I can't trasmit the onClick event from the TextView element to the ListView in order to correctly select the item in the ListView.

custom_list_row.xml:

 <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal" 
        android:clickable="true">

        <ImageButton
            android:id="@+id/drawer_list_item_discard"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:src="@drawable/ic_discard"
            android:scaleType="centerCrop"
            android:layout_alignParentRight="true"
            android:background="@null"
            android:padding="0dp"/>

        <ImageButton
            android:id="@+id/drawer_list_item_edit"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:src="@drawable/ic_edit" 
            android:scaleType="centerCrop"
            android:padding="0dp"
            android:background="@null"
            android:layout_toLeftOf="@id/drawer_list_item_discard"/>

        <TextView 
            android:id="@+id/drawer_list_item_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:layout_centerInParent="true"
            android:textColor="#000000"
            android:textSize="20dp"
            android:layout_toLeftOf="@id/drawer_list_item_edit" 
            android:layout_alignParentLeft="true" />

    </RelativeLayout>

bindView(...) inside ProgramsCursorAdapter.java:

@Override
public void bindView(View v, Context context, Cursor c) {
    if (mContext == null) return;

    // Instances of the Views
    TextView tv_name = (TextView) v.findViewById(R.id.drawer_list_item_text);
    // Override fonts
    Typeface robotoLight = Typeface.createFromAsset(mContext.getAssets(), "fonts/Roboto-Light.ttf");
    if (tv_name != null) tv_name.setTypeface(robotoLight);
    // Retrieve values of the row
    String name = c.getString(c.getColumnIndex(DBHelper.TB_PROGRAM_NAME));
    // Assign retrieved value to the row
    tv_name.setText(name);

    tv_name.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            System.out.println("Program click!");
        }
    });

    //Set up imagebuttons
    ImageButton edit = (ImageButton) v.findViewById(R.id.drawer_list_item_edit);
    edit.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            System.out.println("Edit onclick!");
        }
    });

    ImageButton discard = (ImageButton) v.findViewById(R.id.drawer_list_item_discard);
    discard.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            System.out.println("Discard onclick!");
        }
    });
}

I saw a few post also here in SO about the problem I'm facing in this moment but no one could figure out how to solve it, I think I'm on the good way but I still can't get it done right. Thanks in advance.


Solution

  • Finally got it working, in case someone else needs this. I think that's the only possible workaround...

    I added an interface

    public interface NavigationDrawerListHandler {
        /**
         * Called when an item in the navigation drawer is selected.
         */
        void selectItem(int position);
    }
    

    either NavigationDrawerFragment and ProgramsCursorAdapter implement NavigationDrawerListHandler. ProgramsCursorAdapter also keeps a reference of the type NavigationDrawerListHandler which is passed to it by the method

    ProgramsCursorAdapter.java

    public void attach(NavigationDrawerFragment f) {
        try {
            mCallbacks = (NavigationDrawerListHandler) f;
        } catch (ClassCastException e) {
            throw new ClassCastException(
                    "Fragment must implement NavigationDrawerListHandler.");
        }
    }
    

    which is called when a new instance of ProgramsCursorAdapter is bound to the drawer's ListView like this:

    NavigationDrawerFragment.java

    // Set up adapter and bind it to the list
    ProgramsCursorAdapter adapter = new ProgramsCursorAdapter(getActionBar()
                    .getThemedContext(), database.getPrograms(), CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
    mDrawerListView.setAdapter(adapter);
    // Attach this fragment to the adapter
    adapter.attach(this);
    

    this way the adapter has direct access to the setItem(int position) method that belongs to NavigationDrawerFragment and can call setItem(int position) from the onClick(View v) like this:

    ProgramsCursorAdapter.java

    @Override
    public void bindView(View v, Context context, Cursor c) {
        /* ... */
    
        // Get the position of the clicked view
        final int p = c.getPosition();
    
        tv_name.setOnClickListener(new OnClickListener() {
    
            @Override
            public void onClick(View v) {
                System.out.println("Program click!");
                selectItem(p);
            }
        });
    
        /* ... */
    }
    
    @Override
    public void selectItem(int position) {
        mCallbacks.selectItem(position);
    }
    

    Hope that this will help!