Search code examples
androidlistviewandroid-appwidget

how to attach a onClick Listener to a listview item on an app widget


I like to add a onClick Listener to every item of my listview, but nothing what I have tried work.

Here is my RemoteViewsFactory:

public class MyRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {

    [...]

    public RemoteViews getViewAt(int position) {

        final RemoteViews remoteView = new RemoteViews(
            context.getPackageName(), R.layout.widget_item);

        [...]

        final Intent activityIntent = new Intent(context, ListActivity.class);
        activityIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        activityIntent.putExtra(AppWidgetManager.ACTION_APPWIDGET_PICK, position);
        PendingIntent pI = PendingIntent.getActivity(
            context, appWidgetId, activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        remoteView.setOnClickPendingIntent(R.id.item_frame, pI);

        return remoteView;
    }
}

list-widget.xml

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

    <include layout="@layout/picture_frame" />

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_toRightOf="@+id/picframe"
        android:layout_margin="5dp"
        android:gravity="center"
        android:loopViews="true" />

</RelativeLayout>

list-item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_frame"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:gravity="center_horizontal"
    android:layout_centerVertical="true" >

    <TextView
        android:id="@+id/date"
        style="@style/WidgetItemText" />

    <TextView
        android:id="@+id/name"
        style="@style/WidgetItemText" />

</LinearLayout>

If I click an item, nothing is happen.

The intent works properly, if I hang it in the provider directly to the listview, but then I can no longer scroll and not respond to individual items.


Solution

  • The section of Adding behavior to individual items in the official doc say:

    As described in Using the AppWidgetProvider Class, you normally use setOnClickPendingIntent() to set an object's click behavior—such as to cause a button to launch an Activity. But this approach is not allowed for child views in an individual collection item (to clarify, you could use setOnClickPendingIntent() to set up a global button in the Gmail app widget that launches the app, for example, but not on the individual list items). Instead, to add click behavior to individual items in a collection, you use setOnClickFillInIntent(). This entails setting up up a pending intent template for your collection view, and then setting a fill-in intent on each item in the collection via your RemoteViewsFactory.

    Your RemoteViewsFactory must set a fill-in intent on each item in the collection. This makes it possible to distinguish the individual on-click action of a given item. The fill-in intent is then combined with the PendingIntent template in order to determine the final intent that will be executed when the item is clicked.

    Your need to call setOnClickFillInIntent to differentiate the item on-click behavior. Something like this:

    public RemoteViews getViewAt(int position) {
    
            final RemoteViews remoteView = new RemoteViews(
                context.getPackageName(), R.layout.widget_item);
    
            [...]
    
            // Next, set a fill-intent, which will be used to fill in the pending intent template
            // that is set on the collection view in StackWidgetProvider.
            Bundle extras = new Bundle();
            extras.putInt(YouWidgetProvider.EXTRA_ITEM, position);
            Intent fillInIntent = new Intent();
            fillInIntent.putExtras(extras);
            // Make it possible to distinguish the individual on-click
            // action of a given item
            remoteView.setOnClickFillInIntent(R.id.item_frame, fillInIntent);
    
    
            return remoteView;
        }
    

    Also setPendingIntentTemplate should be use instead of setOnClickPendingIntent to set a single PendingIntent template on the collection. Set the pending intent in onUpdate method corresponding to you subclass of AppWidgetProvider, something like this:

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
            // update each of the app widgets with the remote adapter
            for (int i = 0; i < appWidgetIds.length; ++i) {
                ...
                RemoteViews remoteView = new RemoteViews(context.getPackageName(), 
                    R.layout.widget_item);
                ...
    
                // This section makes it possible for items to have individualized behavior.
                // It does this by setting up a pending intent template. Individuals items of a collection
                // cannot set up their own pending intents. Instead, the collection as a whole sets
                // up a pending intent template, and the individual items set a fillInIntent
                // to create unique behavior on an item-by-item basis.
                Intent activityIntent = new Intent(context, ListActivity.class);
                // Set the action for the intent.
                // When the user touches a particular view.
                activityIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
                activityIntent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
                PendingIntent pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], 
                    activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
                remoteView.setPendingIntentTemplate(R.id.stack_view, pendingIntent);
    
                appWidgetManager.updateAppWidget(appWidgetIds[i], remoteView);
            }
            super.onUpdate(context, appWidgetManager, appWidgetIds);
        }