Search code examples
androidandroid-adapterandroid-listfragment

accessing a view in a SimpleCursorAdapter from the ListFragment


I have a ListFragment with a SimpleCursorAdapter and want to set a longClickListener to each of the items in the list i.e. to one of the TextViews in each item. So I need to Access the TextView to call setOnLongClickListener on that view.

The ListFragment (where I always get null from findViewById but would like to use the setOnLongClickListener):

 public class OverviewFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {

    ...

    @Override
    public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle b ) {
        View fragView = inflater.inflate( R.layout.overview_fragment, container, false );
        return fragView;
    }

    @Override
    public void onActivityCreated( Bundle state ) {
        super.onActivityCreated (state );
        getLoaderManager().initLoader( LOADER_ID_READ_OVERVIEW, null, this );
        adapter = new SimpleCursorAdapter( getActivity(), R.layout.overview_row, null, overviewDbColumns, overviewTargetViewIds, 0 );
        setListAdapter( adapter );
        TextView tv = (TextView) getListView().findViewById( R.id.overviewFooName );
        if ( tv == null ) { //is always null!!!
            Log.e( TAG, "OverviewFragment->onActivityCreated: tv == null" );
        } else {
            tv.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View view) {
                    if ( mActionMode != null ) { 
                        return false;
                    }
                    mActionMode = getActivity().startActionMode(mActionModeCallback);
                    view.setSelected(true);
                    return true;
                }
            });
        }
    }

The overview_fragment.xml:

<LinearLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<include layout="@android:layout/list_content" />
<ListView
    android:id="@android:id/list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:choiceMode="singleChoice" >
</ListView>
<TextView
    android:id="@android:id/empty"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/no_foo_in_list" 
/>

and the overview_row.xml (used by tha SimpleCursorAdapter):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
    android:id="@+id/overviewFooName"
    android:clickable="true"
    android:layout_width="wrap_content"
    android:layout_height="fill_parent" />
<TextView
    android:id="@+id/overviewFooType"
    android:layout_width="wrap_content"
    android:layout_height="26dp" />

<TextView
    android:id="@+id/overviewFooDbIndex"
    android:layout_width="wrap_content"
    android:layout_height="26dp" />

My idea of how android uses the view hierarchy seems to be wrong. My idea is that of one single tree, where I can access every node - as long as I know its ID - from any other node that is nearer to the root. Is this right?

If this idea is wrong, as I expect - how do I get access to the TextView (or the whole item)? And where do I place the code for the mActionModeCallback?


Solution

  • Without having tried this myself I suppose that the problem here is that the ID you are giving to findViewById (R.id.overViewFooName) is ambiguous: Since every list item contains a TextView with this ID, how should the framework decide, which of these to return in findViewById?

    So, I think you have two options:

    1. just use the built-in capabilities of the ListView, namely setOnItemLongClickListener:

    http://developer.android.com/reference/android/widget/AdapterView.html#setOnItemLongClickListener%28android.widget.AdapterView.OnItemLongClickListener%29

    That is, you set a OnItemLongClickListener on the whole ListView - it has a method:

    public abstract boolean onItemLongClick (AdapterView<?> parent, View view, int position, long id)
    

    which will be called whenever an item in the ListView is long-clicked. As you can see, the method also receives the position of the long-clicked item and you can then handle the click accordingly.

    1. If you really need to have just the TextView within each item long-clickable (and not the whole item as in solution 1.), you would need to set the listener in the getView-method of the adapter that creates the list item views.

      That is, you subclass SimpleCursorAdapter and overwrite the getView method. There you inflate your XML layout, get a reference to the TextView and set the OnLongClickListener. In this case, since you are doing this for each item seperately, the findViewById will work because you are in a subtree of the view tree where there is indeed only one View with the ID R.id.overViewFooName.