代码改变世界

Android 4.0 新增的显示数据集的桌面控件

2013-11-26 10:51  Andy Ge  阅读(568)  评论(0编辑  收藏  举报

setRemoteAdapter (int viewId, Intent intent):该方法可以使用 Intent 更新 RemoteViews 中viewId 对应的组件。

上面方法的 Intent 参数应该封装一个 RemoteViewsService 参数,RemoteViewsService 虽然继承了 Service 组件,但它的主要作用是为 RemoteViews 中 viewId 对应的组件提供列表项。

由于Intent参数负责提供列表项,因此viewId参数对应的组件可以是ListView、GridView、StackView 和 AdapterViewFlipper 等,这些组件都是 AdapterView 的子类,由此可见RemoteViewsService 负责提供的对象,应该是一个类似于 Adapter 的对象。

RemoteViewsService 通常用于被继承,继承该基类时需要重写它的 onGetViewFactory()方 法 , 该 方 法 就 需 要 返 回 一 个 类 似 于 Adapterr 对 象 —— 但 不 是 Adapter , 而 是RemoteViewsFactory 对象,RemoteViewsFactory 的功能完全类似于 Adapter。

StackViewWidget实现如下:

StackWidgetService.java

package org.crazyit.desktop;

import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;

/**
 * Description:
 * <br/>网站: <a href="http://www.crazyit.org">疯狂Java联盟</a>
 * <br/>Copyright (C), 2001-2014, Yeeku.H.Lee
 * <br/>This program is protected by copyright laws.
 * <br/>Program Name:
 * <br/>Date:
 * @author Yeeku.H.Lee kongyeeku@163.com
 * @version 1.0
 */
public class StackWidgetService extends RemoteViewsService
{
    // 重写该方法,该方法返回一个RemoteViewsFactory对象。
    // RemoteViewsFactory对象的的作用类似于Adapter,
    // 它负责为RemoteView中指定组件提供多个列表项。
    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent)
    {
        return new StackRemoteViewsFactory(this.getApplicationContext(),
            intent);  //
    }
    class StackRemoteViewsFactory implements
    RemoteViewsService.RemoteViewsFactory
    {
        // 定义一个数组来保存该组件生成的多个列表项
        private int[] items = null;
        private Context mContext;
        public StackRemoteViewsFactory(Context context, Intent intent)
        {
            mContext = context;
        }
        @Override
        public void onCreate()
        {
            // 初始化items数组
            items = new int[] { R.drawable.bomb5, R.drawable.bomb6,
                R.drawable.bomb7, R.drawable.bomb8, R.drawable.bomb9,
                R.drawable.bomb10, R.drawable.bomb11, R.drawable.bomb12,
                R.drawable.bomb13, R.drawable.bomb14, R.drawable.bomb15,
                R.drawable.bomb16
            };
        }
        @Override
        public void onDestroy()
        {
            items = null;
        }
        // 该方法的返回值控制该对象包含多少个列表项
        @Override
        public int getCount()
        {
            return items.length;
        }
        // 该方法的返回值控制各位置所显示的RemoteViews
        @Override
        public RemoteViews getViewAt(int position)
        {
            // 创建RemoteViews对象,加载/res/layout目录下widget_item.xml文件
            RemoteViews rv = new RemoteViews(mContext.getPackageName(),
                R.layout.widget_item);
            // 更新widget_item.xml布局文件中的widget_item组件
            rv.setImageViewResource(R.id.widget_item,
                items[position]);
            // 创建Intent、用于传递数据
            Intent fillInIntent = new Intent();
            fillInIntent.putExtra(StackWidgetProvider.EXTRA_ITEM, position);
            // 设置当单击该RemoteViews时传递fillInIntent包含的数据
            rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);
            // 此处使用让线程暂停0.5秒来模拟加载该组件
            try
            {
                System.out.println("加载【" + position + "】位置的组件");
                Thread.sleep(500);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            return rv;
        }
        @Override
        public RemoteViews getLoadingView()
        {
            return null;
        }
        @Override
        public int getViewTypeCount()
        {
            return 1;
        }
        @Override
        public long getItemId(int position)
        {
            return position;
        }
        @Override
        public boolean hasStableIds()
        {
            return true;
        }
        @Override
        public void onDataSetChanged()
        {
        }
    }    
}

widget_item.xml

<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/widget_item"
    android:layout_width="120dp"
    android:layout_height="120dp"
    android:gravity="center"/>

StackWidgetProvider.java

package org.crazyit.desktop;

import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;
import android.widget.Toast;

/**
 * Description:
 * <br/>网站: <a href="http://www.crazyit.org">疯狂Java联盟</a>
 * <br/>Copyright (C), 2001-2014, Yeeku.H.Lee
 * <br/>This program is protected by copyright laws.
 * <br/>Program Name:
 * <br/>Date:
 * @author Yeeku.H.Lee kongyeeku@163.com
 * @version 1.0
 */
public class StackWidgetProvider extends AppWidgetProvider
{
    public static final String TOAST_ACTION
        = "org.crazyit.desktop.TOAST_ACTION";
    public static final String EXTRA_ITEM 
        = "org.crazyit.desktop.EXTRA_ITEM";

    @Override
    public void onUpdate(Context context,
        AppWidgetManager appWidgetManager, int[] appWidgetIds)
    {
        // 创建RemoteViews对象,加载/res/layout目录下的widget_layout.xml文件
        RemoteViews rv = new RemoteViews(context.getPackageName(),
            R.layout.widget_layout);
        Intent intent = new Intent(context, StackWidgetService.class);
        // 使用intent更新rv中stack_view组件(StackView)
        rv.setRemoteAdapter(R.id.stack_view, intent);  //// 设置当StackWidgetService提供的列表项为空时,直接显示empty_view组件
        rv.setEmptyView(R.id.stack_view, R.id.empty_view);
        // 创建启动StackWidgetProvider组件(作为BroadcastReceiver)的Intent
        Intent toastIntent = new Intent(context,
            StackWidgetProvider.class);
        // 为该Intent设置Action属性
        toastIntent.setAction(StackWidgetProvider.TOAST_ACTION);
        // 将Intent包装成PendingIntent
        PendingIntent toastPendingIntent = PendingIntent
            .getBroadcast(context, 0, toastIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);
        // 将PendingIntent与stack_view进行关联
        rv.setPendingIntentTemplate(R.id.stack_view,
            toastPendingIntent);
        // 使用AppWidgetManager通过RemteViews更新AppWidgetProvider
        appWidgetManager.updateAppWidget(
            new ComponentName(context, StackWidgetProvider.class), rv); //
        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }
    @Override
    public void onDeleted(Context context, int[] appWidgetIds)
    {
        super.onDeleted(context, appWidgetIds);
    }

    @Override
    public void onDisabled(Context context)
    {
        super.onDisabled(context);
    }

    @Override
    public void onEnabled(Context context)
    {
        super.onEnabled(context);
    }
    // 重写该方法,将该组件当成BroadcastReceiver使用
    @Override
    public void onReceive(Context context, Intent intent)
    {
        if (intent.getAction().equals(TOAST_ACTION))
        {
            // 获取Intent中的数据
            int viewIndex = intent.getIntExtra(EXTRA_ITEM, 0);
            // 显示Toast提示
            Toast.makeText(context, "点击第【" + viewIndex + "】个列表项",
                Toast.LENGTH_SHORT).show();
        }
        super.onReceive(context, intent);
    }    
}

widget_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="8dp">
    <StackView
        android:id="@+id/stack_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:loopViews="true" />
    <TextView
        android:id="@+id/empty_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:background="#ff0f"
        android:textColor="#ffffff"
        android:textStyle="bold"
        android:text="@string/no_item"
        android:textSize="20sp" />
</FrameLayout>

Manifest.xml

<?xml version="1.0" encoding="utf-8" ?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.crazyit.desktop"
    android:versionCode="1"
    android:versionName="1.0">
    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="17" />
<application
    android:allowBackup="true"
    android:label="@string/app_name">
    <!-- 配置AppWidgetProvider,即配置桌面控件 -->
    <receiver android:name=".StackWidgetProvider">
        <!-- 通过该intent-filter指定该Receiver作为桌面控件 -->
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>
        <!-- 为桌面控件指定meta-data -->
        <meta-data
            android:name="android.appwidget.provider"
            android:resource="@xml/stackwidgetinfo" />
    </receiver>
    <!-- 配置RemoteViewsService
    必须指定权限为android.permission.BIND_REMOTEVIEWS
     -->
    <service
        android:name=".StackWidgetService"
        android:permission="android.permission.BIND_REMOTEVIEWS"
        android:exported="false" />
</application>
</manifest>

stackwidgetinfo.xml

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="110dp"
    android:minHeight="110dp"
    android:updatePeriodMillis="3600000"
    android:previewImage="@drawable/ic_launcher"
    android:initialLayout="@layout/widget_layout"
    android:resizeMode="horizontal|vertical"
    android:autoAdvanceViewId="@id/stack_view">
</appwidget-provider>

截图:

image