Search code examples
androidlistviewonclickcursorandroid-cursoradapter

Position of click within ListView with custom view and a CursorAdapter


I'm using a ListView to display data from a cursor. Each ListView row uses a custom layout as defined by template_item.xml below.

When I click on the TextView with id tv_item_name, I would like to get the row position of the ListView which contains that TextView. I need the row number so I can pull other columns from the cursor, so using TextView.getString() is not enough for what I need to do.

I'm struggling to find a way to do this within the onClick listener.

So far I've tried:

  1. Cursor c = myAdapter.getCursor() - The cursor seems to always be at the last row, so calling c.getPosition() returns for example 3 if the cursor has 4 rows, no matter which item name I click.
  2. LinearLayout ll = (LinearLayout) v.getParent(), LinearLayout ll2 = (LinearLayout) ll.getParent() and then ListView lv = (ListView) ll2.getParent(), however from here I can't get the position of the clicked item. lv.getSelectedItemPosition() returns -1.

Help is much appreciated.

template_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/tv_item_heading"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_weight="5">
        <TextView
            android:id="@+id/tv_item_name"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="4"/>
        <TextView
            android:id="@+id/tv_item_quantity"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>
    </LinearLayout>
</LinearLayout>

base_layout_listview.xml

<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/base_listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
</ListView>

Excerpts from my Fragment

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    return inflater.inflate(R.layout.base_layout_listview, container, false);
}


private void bindItems (Cursor data) {
    CursorQueries cq = new CursorQueries();

    myAdapter = new CursorAdapter(getContext(), data) {

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            return super.getView(position, convertView, parent);
        }

        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            return LayoutInflater.from(context).inflate(R.layout.template_item, parent, false);
        }

        @SuppressLint("ClickableViewAccessibility")
        @Override
        public void bindView(View view, Context context, Cursor cursor) {

            TextView tvHeading = view.findViewById(R.id.tv_item_heading);
            TextView tvItem = view.findViewById(R.id.tv_item_name);
            TextView tvQuantity = view.findViewById(R.id.tv_item_quantity);
            tvHeading.setText(cursor.getString(cursor.getColumnIndex("rowType")));
            tvItem.setText(cursor.getString(cursor.getColumnIndex("itmName")));
            tvQuantity.setText(cursor.getString(cursor.getColumnIndex("itmQuantity")));

            tvItem.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    TextView t = (TextView) v;
                    // Want to get the position in the list of this text view    
                }
            });

        }
    };
    ListView lv = getView().findViewById(R.id.base_listview);
    lv.setAdapter(myAdapter);
}

Solution

  • The method 1 that was tried is fine:

     Cursor cursor = myAdapter.getCursor()
     ...
    

    Either use OnItemClickListener, so that position is accessible:

    public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
       Cursor cursor = myAdapter.getCursor();
       cursor.moveToPosition(position);
       ...
    }
    

    If you want to stick with an onClickListener then do the following inside bindView:

    public void bindView(View view, Context context, Cursor cursor) {
        ...
        view.setTag(Integer.valueOf(cursor.getPosition());
    }
    

    [or set the tag on any of your views like 'tvItem' as is being down now. But setting an OnClickListener on an individual TextView may make it difficult for the user to click?]

    Then in the onClick method do:

     public void onClick(View v) {
         Cursor cursor = myAdapter.getCursor();
         int position = (Integer) v.getTag();
         cursor.moveToPosition(position);
         ...
      }
    

    It is also worth mentioning, if scroll-flinging performance becomes an issue it would be worth investigating the "View Holder Pattern" or using a RecyclerView which essentially comes with the "View Holder Pattern".