使用安卓小部件显示列表清单的杀手锏(二)

运用 RemoteViewsService
RemoteViewsService主要目的是返回一个RemoteViewsFactory对象,进一步完成用适当的数据填充小部件的任务。
    创建一个名为新类MyWidgetRemoteViewsService扩展类RemoteViewsService。
   
[Java] 查看源文件 复制代码
public class MyWidgetRemoteViewsService extends RemoteViewsService {
    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new MyWidgetRemoteViewsFactory(this.getApplicationContext(), intent);
    }
}


      记得在清单文件中声明这个service。
[Java] 查看源文件 复制代码
<service android:name="*.*.MyWidgetRemoteViewsService "/>

Note the special permission android.permission.BIND_REMOTEVIEWS. This lets the system bind your service to create the 
      注意添加权限android.permission.BIND_REMOTEVIEWS。这使系统可以绑定您的服务,为每一行创建小部件视图,并阻止其他应用访问您的小部件的数据。
      
[size=2.25]   启动 RemoteViewsService
     现在我们还有一个工作需要去做,那就是去创建一个WidgetProvider来启动RemoteViewService。新建一个类CollectionAppWidgetProvider :
[Java] 查看源文件 复制代码
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    for (int appWidgetId : appWidgetIds) {
        RemoteViews views = new RemoteViews(
                context.getPackageName(),
                R.layout.collection_widget
        );
        Intent intent = new Intent(context, MyWidgetRemoteViewsService.class);
        views.setRemoteAdapter(R.id.widgetListView, intent);
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
}
      创建部件布局
      现在我们在资源文件夹res/xml下新建文件 collection_widget.xml.在这个文件中,我们定义部件设置,例如那些布局文件这个部件需要使用,为了更好的用户体验,最好添加一个图片预览。
[Java] 查看源文件 复制代码
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="40dp"
    android:minHeight="40dp"
    android:updatePeriodMillis="864000"
    android:previewImage="@drawable/simple_widget_preview"
    android:initialLayout="@layout/collection_widget"
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen">
</appwidget-provider>
        在资源文件夹res/layout下创建collection_widget.xml.在这里,我们定义集合小部件中我们想要显示的布局。我们想要在顶部有个标题,然后下面是用ListView来显示待办事宜清单。
[Java] 查看源文件 复制代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/colorWhite"
            xmlns:tools="http://schemas.android.com/tools"
            android:orientation="vertical">
    <FrameLayout android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView android:layout_width="match_parent"
            android:id="@+id/widgetTitleLabel"
            android:text="@string/title_collection_widget"
            android:textColor="@color/colorWhite"
            android:background="@color/colorPrimary"
            android:textSize="18dp"
            android:gravity="center"
            android:textAllCaps="true"
            android:layout_height="@dimen/widget_title_min_height"></TextView>
    </FrameLayout>
    <LinearLayout android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <ListView android:id="@+id/widgetListView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorWhite"
            android:dividerHeight="1dp"
            android:divider="#eeeeee"
            tools:listitem="@layout/collection_widget_list_item"></ListView>
    </LinearLayout>
</LinearLayout>
我们还需要在res/layout文件夹下定义一个布局文件collection_widget_list_item.xml,这是列表单项的布局设置。
[Java] 查看源文件 复制代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:paddingLeft="@dimen/widget_listview_padding_x"
    android:paddingRight="@dimen/widget_listview_padding_x"
    android:paddingStart="@dimen/widget_listview_padding_x"
    android:paddingEnd="@dimen/widget_listview_padding_x"
    android:minHeight="@dimen/widget_listview_item_height"
    android:weightSum="2"
    android:id="@+id/widgetItemContainer"
    android:layout_height="wrap_content">

    <TextView android:id="@+id/widgetItemTaskNameLabel"
        android:layout_width="wrap_content"
        android:gravity="start"
        android:layout_weight="1"
        android:textColor="@color/text"
        android:layout_gravity="center_vertical"
        android:layout_height="wrap_content"></TextView>

</LinearLayout>


       现在运行这个app,你可以看到有待办事宜的小部件原型。
    手动更新部件
      下面的逻辑是这样子的:无论你什么时候创建一个新的待办事宜,你需要发送一个广播给WidgetProvider。在CollectionAppWidgetProvider 类中定义一个新的方法:
      
[Java] 查看源文件 复制代码
public static void sendRefreshBroadcast(Context context) {
    Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
    intent.setComponent(new ComponentName(context, CollectionAppWidgetProvider.class));
    context.sendBroadcast(intent);
}

      然后重写onReceive方法。
[Java] 查看源文件 复制代码
@Override
public void onReceive(final Context context, Intent intent) {
    final String action = intent.getAction();
    if (action.equals(AppWidgetManager.ACTION_APPWIDGET_UPDATE)) {
        // refresh all your widgets
        AppWidgetManager mgr = AppWidgetManager.getInstance(context);
        ComponentName cn = new ComponentName(context, CollectionAppWidgetProvider.class);
        mgr.notifyAppWidgetViewDataChanged(mgr.getAppWidgetIds(cn), R.id.widgetListView);
    }
    super.onReceive(context, intent);
}

       当新的待办事宜创建时,调用在CollectionAppWidgetProvider类中定义的sendRefreshBroadcast方法。在MainActivity中,修改addTodoItem 方法。
[Java] 查看源文件 复制代码
a.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        Toast.makeText(mContext, "New task created", Toast.LENGTH_LONG).show();
        getTodoList();
        // this will send the broadcast to update the appwidget
        CollectionAppWidgetProvider.sendRefreshBroadcast(mContext);
    }
});
      部件的事件处理
       在我们的部件中,顶部有一个标题,底部有一个列表。所以当用户点击标题时,我们打开这个app。当列表中的某一项被点击时,显示待办事宜详情。也许详情显示其实没什么用处,但是我们做这个可以理解这个思想。
    单独的视图点击事件
     添加文本框和图片的点击事件很容易,接下来我们更新onUpdate方法:
[Java] 查看源文件 复制代码
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    for (int appWidgetId : appWidgetIds) {
        RemoteViews views = new RemoteViews(
                context.getPackageName(),
                R.layout.collection_widget
        );

        // click event handler for the title, launches the app when the user clicks on title
        Intent titleIntent = new Intent(context, MainActivity.class);
        PendingIntent titlePendingIntent = PendingIntent.getActivity(context, 0, titleIntent, 0);
        views.setOnClickPendingIntent(R.id.widgetTitleLabel, titlePendingIntent);

        Intent intent = new Intent(context, MyWidgetRemoteViewsService.class);
        views.setRemoteAdapter(R.id.widgetListView, intent);
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
}
      这个和我们平时添加点击事件很类似。但是由于小部件会运行在不同的环境下,我们需要去注册点击事件通过PendingIntent来获取运行环境。
    列表项点击事件
      添加列表中每一项的点击事件并不是简单的给ListView对象setOnItemClickListener。需要一些额外的步骤。
      首先,你需要为PendingIntent设置一个template。在CollectionAppWidgetProvider类的onUpdate方法中,在views.setRemoteAdapter(R.id.widgetListView,intent)的后面添加。
[Java] 查看源文件 复制代码
// template to handle the click listener for each item
Intent clickIntentTemplate = new Intent(context, DetailsActivity.class);
PendingIntent clickPendingIntentTemplate = TaskStackBuilder.create(context)
        .addNextIntentWithParentStack(clickIntentTemplate)
        .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
views.setPendingIntentTemplate(R.id.widgetListView, clickPendingIntentTemplate);
     每一个列表项我们打开一个DetailsActivity来简单的展示待办事宜的描述。然后每次都有一个RemoteViews被RemoteViewsFactory创建来填充template。
     在MyWidgetRemoteViewsFactory 类中添加getViewAt方法。
[Java] 查看源文件 复制代码
Intent fillInIntent = new Intent();
fillInIntent.putExtra(CollectionAppWidgetProvider.EXTRA_LABEL, mCursor.getString(1));
rv.setOnClickFillInIntent(R.id.widgetItemContainer, fillInIntent);
这样我们在CollectionAppWidgetProvider 类中定义了填充未确定的intents。注意我们想要的是每一行都可以点击,所以我们在collection_widget_list_item.xml的根元素中添加点击监听即可。
展开阅读全文

没有更多推荐了,返回首页