带有多个待处理意图的ListView小部件

8
我使用带有3种项目类型的listview小部件。对于每种项目类型,我应该使用不同的待处理意图。当前,我正在使用以下代码:
public class MyWidgetProvider extends AppWidgetProvider {

    @Override
    public void onUpdate(Context context, AppWidgetManager widgetManager, int[] widgetIds) {
        for (int widgetId : widgetIds) {
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_view);
            bindEmptyView(remoteViews);
            bindRemoteAdapter(context, widgetId, remoteViews);
            bindIntentTemplate(context, widgetId, remoteViews);
            widgetManager.updateAppWidget(widgetId, remoteViews);
        }
    }

    private void bindEmptyView(RemoteViews remoteViews) {
        remoteViews.setEmptyView(android.R.id.list, android.R.id.empty);
    }

    private void bindRemoteAdapter(Context context, int widgetId, RemoteViews remoteViews) {
        Intent intent = new Intent(context, MyViewService.class);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
        intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
        remoteViews.setRemoteAdapter(android.R.id.list, intent);
    }

    private void bindIntentTemplate(Context context, int widgetId, RemoteViews remoteViews) {

        Intent intent = new Intent(context, MyActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
        intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
        PendingIntent template = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        remoteViews.setPendingIntentTemplate(android.R.id.list, template);
    }
}

从这个点开始,我不明白如何为不同的列表项调用特定的意图。
1个回答

12
希望我理解了您的问题,那么我将尝试详细解释小部件上列表项的单击事件发生的原因以及如何处理它。我假设您已经知道您必须实现一个类,其中:
extends BroadcastReceiver implements RemoteViewsService.RemoteViewsFactory

这将作为您的小部件ListView的“适配器”(我们将其称为MyListRemoteViewFactory)。

如果您想在小部件的ListView上处理项目点击,您需要执行以下操作:

1)在您的AppWidgetProvider类中设置setPendingIntentTemplate

2)在MyListRemoteViewFactory覆盖的getViewAt(int position)方法中设置setOnClickFillInIntent

现在进行第一步,您可能需要执行以下操作:

final Intent serviceIntent = new Intent(context, MyListRemoteViewFactory.class);
serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
    views.setRemoteAdapter(R.id.widget_list_view, serviceIntent);
} else {
    views.setRemoteAdapter(widgetId, R.id.widget_list_view, serviceIntent);
}

// 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 listItemClickIntent = new Intent(context, MyWidgetProvider.class); // This is the name of your AppWidgetProvider class
// Set the action for the intent. When the user touches a particular view, it will have the effect of broadcasting an action
listItemClickIntent.setAction(context.getString("com.example.list.item.click"));
 listItemClickIntent.setData(Uri.parse(listItemClickIntent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent clickPendingIntent = PendingIntent.getBroadcast(context, 0, listItemClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setPendingIntentTemplate(R.id.widget_list_view, clickPendingIntent);

您可以将上述代码片段放在初始化 RemoteViews 对象的任何位置之后:
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.layout_widget);)

现在你已经准备好了你的pendingIntentTemplate。还有一件事要做,那就是实现类的onReceive方法,这样当上面的情况发生时,你就可以决定要做什么。所以你需要这样做:

@Override
public void onReceive(Context context, Intent intent) {
    // Called on every broadcast and before each of the above callback methods.
    super.onReceive(context, intent);

    ComponentName name = new ComponentName(context, WidgetProvider.class);
    int[] appWidgetIds = AppWidgetManager.getInstance(context).getAppWidgetIds(name);
    if (appWidgetIds == null || appWidgetIds.length == 0) {
        return;
    }

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

    if (intent.getAction().equals("com.example.list.item.click") { 
        // This is where you have to decide what you'll do depending on which list item was tapped. BUT this depends on the fill Intent set in the MyListRemoteViewFactory class's getViewAt() method
        // I'll jump straight to the logic here, but once you're done reading this post, get back here to understand the logic because this is the key here. But simple as hell.
        int simpleDecidingFactor =  intent.getIntExtra("SELECTED_ITEM", 0)
        if (simpleDecidingFactor != 0) {
            if (simpleDecidingFactor == 1) {
                // Do something for the first case. Probably open activity2
            } else if (simpleDecidingFactor == 2) {
                // Do something for the second case. Probably open activity2
            } else if (simpleDecidingFactor == 3) {
                // Do something for the second case. Probably open activity3
            }
         }
    }
}

第二步)执行(从现在开始我们将讨论MyListRemoteViewFactory类中的实现)

如果您需要在列表中显示3个不同的项,首先您需要向MyListRemoteViewFactory添加此方法(无论如何您都必须覆盖它,关键是要返回您拥有的视图数):

@Override
public int getViewTypeCount() {
    return 3;
}

getViewAt()方法中,您可以根据自己的位置添加逻辑来决定要显示什么。类似这样的内容:
@Override
public RemoteViews getViewAt(int position) {
    if (position >= mItems.size()) {
        return null;
    }

    RemoteViews views;
    if (mItems.get(position).getViewType() == 0) {
        views = new RemoteViews(mContext.getPackageName(), R.layout.list_item_first);
        setUpItem(views, mItems.get(position), 1); // !!!! the 3rd parameter is very important here, as you'll expect this number in the MyWidgetProvider class' onReceive method. See the simpleDecidingFactor variable there.
    } else if (mItems.get(position).getViewType() == 1) {
        views = new RemoteViews(mContext.getPackageName(), R.layout.list_item_second);
        setUpItem(views, mItems.get(position), 2);
    } else {
        views = new RemoteViews(mContext.getPackageName(), R.layout.list_item_third);
        setUpItem(views, mItems.get(position), 3);
    } // Or add whatever logic you have. Here, I supposed I have a field inside my object telling me what type my item is

    return views;
}

setUpItem 方法可能如下:

private void setUpItem(RemoteViews views, MyObject object, int viewTypeKey) {
     // This is where you set your clickFillInIntent. Without setting it, nothing'll be functional

    Bundle extras = new Bundle();
    extras.putInt("SELECTED_ITEM", viewTypeKey);
    //extras.putParcelable("LIST_ITEM_OBJECT", object); // You may send your object as well if you need it
    Intent fillInIntent = new Intent();
    fillInIntent.putExtras(extras);

    // You have your fillInIntent prepared, you only have to decide on what view to place it.
    // I assume you have a Button on all 3 of your list item layouts with the id button_click. Let's place the Intent:

    views.setOnClickFillInIntent(R.id.button_click, fillInIntent);
}

您可能需要确保在清单文件中声明了所有内容。您必须声明您的小部件提供程序、列表的接收器和处理Factoryclass的服务。您应该有类似于:

<receiver
    android:name=".MyWidgetProvider"
    android:enabled="true"
    android:label="My awesome widget">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        <action android:name="android.appwidget.action.APPWIDGET_DELETED" />
        <action android:name="android.appwidget.action.APPWIDGET_DISABLED" />
        <!-- You have to declare your used actions here, so the AppWidgetProvider knows what to listen for-->
            <action android:name="com.example.list.item.click"/>
    </intent-filter>

    <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/widget_provider_info"/>
</receiver>

<!-- The service serving the RemoteViews to the collection widget -->
<service
    android:name=".WidgetRemoteViewsService"
    android:exported="false"
    android:permission="android.permission.BIND_REMOTEVIEWS"/>

<receiver
    android:name=".ui.widget.MyListRemoteViewFactory"
    android:enabled="true"
    android:exported="false">
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT"/>
        <!-- You might want to use an action to notify the appwidget provider from the MyListRemoteViewFactory's onReceive method. This class extends a BroadcastReceiver, so you must implement it's onReceive(Context context, Intent intent) method. If you need help on this, let me know and I'll edit my answer with some example for that too -->
        <action android:name="com.example.refresh.remote.views"/>
    </intent-filter>
</receiver>

哦,顺便提一下,WidgetRemoteViewsService 类应该是这样的:

public class WidgetNewsRemoteViewsService extends RemoteViewsService {

    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new MyListRemoteViewFactory();
    }
}

我想这大概就是全部了,希望我没有漏掉什么。


谢谢Mike,这非常有帮助! - Figen Güngör

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接