AppWidget学习总结

一.创建AppWidget.
    1.在res/xml下创建一个xml文件,以设置widget占用的空间等信息.如widget_provider.xml
        <?xml version="1.0" encoding="utf-8"?>
        <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
            android:minWidth="180dp"
            android:minHeight="20dp"
            android:updatePeriodMillis="1000"
            android:initialLayout="@layout/widget_main" >
        </appwidget-provider>
       
        属性说明
        android:minWidth 指定widget占用的宽度
        android:minHeight 指定widget占用的高度
        android:updatePeriodMillis 定义Widget的更新频率, Android框架每隔一段时间, 会回调AppWidgetProvider类的onUpdate()事件;
                                    以毫秒为单位, 更新时间为30~60分钟, 如果设定30分钟以内无作用.
        android:initialLayout 指定Widget的布局文件, 一般将此文件放到res/layout下.
   
    2.实现布局文件,如widget_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#4555" >

    <TextView
        android:id="@+id/mTvDate"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:singleLine="true"
        android:textColor="#f00"
        android:textSize="18sp" />

    <ProgressBar
        android:id="@+id/mProgressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/mTvDate" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true" >

        <TextView
            android:id="@+id/mBtnHour"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:text="@string/hour_24"
            android:textColor="#e00"
            android:textSize="18sp" />
    </LinearLayout>

</RelativeLayout>

3.实现业务逻辑类,该类继承自AppWidgetProvider.

package com.ahai.hellowidget;

import static com.ahai.util.DebugMessage.d;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

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.os.Bundle;
import android.os.Handler;
import android.widget.RemoteViews;

public class DateWidgetProvider extends AppWidgetProvider implements
        WidgetUpdateTask.Callbacks {

    public static final String ACTION_REMOTE_CLICK_UPDATE = "action.remote.click.update";
    public static final String ACTION_REMOTE_CLICK_HOUR = "action.remote.click.hour";

    public static final String DATE_AND_TIME_24HOUR_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_AND_TIME_12HOUR_FORMAT = "yyyy-MM-dd hh:mm:ss";
    public static final int RATE_UPDATE_TIME = 1000;

    private static Handler mHandler;
    private static WidgetUpdateTask mPendingUpdates;
    private static Set<Integer> mIds = new HashSet<Integer>();

    private static boolean mIs24Hour = true;
    private static int mProgress;

    @Override
    public void onAppWidgetOptionsChanged(Context context,
            AppWidgetManager appWidgetManager, int appWidgetId,
            Bundle newOptions) {
        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId,
                newOptions);
        d("[DateWidgetProvider]onAppWidgetOptionsChanged");
    }

    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        super.onDeleted(context, appWidgetIds);
        d("[DateWidgetProvider]onDeleted");
        for (int i : appWidgetIds) {
            mIds.remove(i);
        }
        if (mIds.isEmpty()) {
            if (mPendingUpdates != null) {
                mPendingUpdates.stop();
                mPendingUpdates = null;
            }
        }
    }

    @Override
    public void onDisabled(Context context) {
        super.onDisabled(context);
        d("[DateWidgetProvider]onDisabled");
    }

    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
        d("[DateWidgetProvider]onEnabled");
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        d("[DateWidgetProvider]onReceive");
        d("intent=" + intent);
        final String action = intent.getAction();
        d("action=" + action);

        if (ACTION_REMOTE_CLICK_UPDATE.equals(action)) {

            if (mPendingUpdates != null) {
                mHandler.removeCallbacks(mPendingUpdates);
                mHandler.post(mPendingUpdates);
            }
        } else if (ACTION_REMOTE_CLICK_HOUR.equals(action)) {

            RemoteViews views = new RemoteViews(context.getPackageName(),
                    R.layout.widget_main);
            mIs24Hour = !mIs24Hour;
            if (mIs24Hour) {
                views.setTextViewText(R.id.mBtnHour,
                        context.getString(R.string.hour_24));
            } else {
                views.setTextViewText(R.id.mBtnHour,
                        context.getString(R.string.hour_12));
            }
            ComponentName widgetComponent = new ComponentName(context, this.getClass());
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
            appWidgetManager.updateAppWidget(widgetComponent, views);

            if (mPendingUpdates != null) {
                mHandler.removeCallbacks(mPendingUpdates);
                mHandler.post(mPendingUpdates);
            }
        }
    }

    @Override
    public void onRestored(Context context, int[] oldWidgetIds,
            int[] newWidgetIds) {
        super.onRestored(context, oldWidgetIds, newWidgetIds);
        d("[DateWidgetProvider]onRestored");
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
            int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        d("[DateWidgetProvider]onUpdate");
        for (int i : appWidgetIds) {
            mIds.add(i);
        }
        if (mHandler == null) {
            mHandler = new Handler(context.getMainLooper());
        }
        if (mPendingUpdates != null) {
            mHandler.removeCallbacks(mPendingUpdates);
        }
        mPendingUpdates = new WidgetUpdateTask(context, appWidgetManager, mIds,
                this, mHandler, RATE_UPDATE_TIME);
        mHandler.post(mPendingUpdates);

        RemoteViews views = new RemoteViews(context.getPackageName(),
                R.layout.widget_main);

        Intent intent = new Intent(ACTION_REMOTE_CLICK_UPDATE);
        PendingIntent pendingIntentUpdate = PendingIntent.getBroadcast(context,
                0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        views.setOnClickPendingIntent(R.id.mTvDate, pendingIntentUpdate);

        intent = new Intent(ACTION_REMOTE_CLICK_HOUR);
        PendingIntent pendingIntent12Hour = PendingIntent.getBroadcast(context,
                0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        views.setOnClickPendingIntent(R.id.mBtnHour, pendingIntent12Hour);

        appWidgetManager.updateAppWidget(appWidgetIds, views);
    }

    @SuppressWarnings("deprecation")
    @Override
    public void onUpdateWidget(Context context,
            AppWidgetManager appWidgetManager, Set<Integer> ids) {
        Date date = new Date();
        SimpleDateFormat sdf;
        String dateString;
        if (mIs24Hour) {
            sdf = new SimpleDateFormat(DATE_AND_TIME_24HOUR_FORMAT);
            dateString = sdf.format(date);
        } else {
            sdf = new SimpleDateFormat(DATE_AND_TIME_12HOUR_FORMAT);
            dateString = sdf.format(date);
            dateString += " ";
            if (date.getHours() > 12)
                dateString += "PM";
            else
                dateString += "AM";
        }

        mProgress++;
        if (mProgress > 10)
            mProgress = 0;

        for (int appWidgetId : ids) {
            // d("update id:" + appWidgetId);
            RemoteViews views = new RemoteViews(context.getPackageName(),
                    R.layout.widget_main);
            views.setTextViewText(R.id.mTvDate, dateString);
            views.setProgressBar(R.id.mProgressBar, 10, mProgress, false);
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}

AppWidgetProvider是BroadcastReciever的子类,因此实现的类也是一个reciver.
        (1)查看AppWidgetProvider的onReceiver方法源码,
            public void onReceive(Context context, Intent intent) {
                // Protect against rogue update broadcasts (not really a security issue,
                // just filter bad broacasts out so subclasses are less likely to crash).
                String action = intent.getAction();
                if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
                    Bundle extras = intent.getExtras();
                    if (extras != null) {
                        int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
                        if (appWidgetIds != null && appWidgetIds.length > 0) {
                            this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
                        }
                    }
                }
            }
        可以发现,当收到AppWidgetManager.ACTION_APPWIDGET_UPDATE消息时,将会自动调用onUpdate方法.因此在写onReceiver方法时,可以不处理此action.
        其它的action也会有相应的回调.
        AppWidgetManager.ACTION_APPWIDGET_DELETED -- onDeleted
        AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED -- onAppWidgetOptionsChanged
        AppWidgetManager.ACTION_APPWIDGET_ENABLED -- onEnabled
        AppWidgetManager.ACTION_APPWIDGET_DISABLED -- onDisabled
       
        (2)public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
        当每创建一个widget时会调用一次onUpdate, appWidgetIds中会传入对应的id,注意,以前创建的widget的id不会传入.
       
        (3)onUpdate,onDeleted等方法传入时,this指针指向的是不同的对象,因此类中使用的变量或方法应定义为static.
       
        (4)实现类本身是一个BroadcastReceiver,因此可以在manifest.xml文件中注册其它的消息,以便更新界面信息.
        对按钮等控件的点击等消息,也通过消息发送.因此需要别的类处理的消息,需要使用BroadcastReceiver注册并响应.
        注意,如果在manifest.xml在本身的receiver中已经注册了某个消息,则别的BroadcastReceiver收不到该消息.

   (5)设置回调时,对不同的控件需要定义不同的action,不能在同一个action中定义不同的buddle等信息来区分响应消息,这样会导致收到的响应消息中,所有的buddle等信息都是最后设置的那个.    
        
    4.修改manifest.xml中<application>节点下,注册实现的reciever类.其中android.appwidget.action.APPWIDGET_UPDATE是必须要添加的action,其它action如果设置了按钮响应等也可以在此添加,此后onReceiver中会收到对应的消息,但其它注册该action的receiver不能收到此消息.
        <receiver android:name="com.ahai.hellowidget.DateWidgetProvider" >
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_provider" />

<intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                <action android:name="action.remote.click.update" />
                <action android:name="action.remote.click.hour" />
            </intent-filter>
        </receiver>
       
二.相关的类说明
    1.RemoteViews, 一个可以在其他应用进程中运行的类, xml布局文件中定义了界面组件, 通过创建RemoteViews对象, 对widget的信息进行更新.
        (1) 创建RemoteViews对象
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_main);
       
        (2) 更新RemoteViews信息, 对界面信息的修改都需要调用RemoteViews类的方法updateAppWidget.
        注意,updateAppWidget有多个版本.通常用下面的两个方法:
            appWidgetManager.updateAppWidget(appWidgetIds, views);
           
            ComponentName widgetComponent = new ComponentName(context, this.getClass());
            appWidgetManager.updateAppWidget(widgetComponent, views);
        onUpdate中会传入对应的AppWidgetMannager对象.也可以自己取得此对象
            appWidgetManager = AppWidgetManager.getInstance(context).
       
        (3)定义回调
        Intent intent = new Intent(ACTION_REMOTE_CLICK_UPDATE);
  PendingIntent pendingIntentUpdate = PendingIntent.getBroadcast(context,
    0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
  views.setOnClickPendingIntent(R.id.mTvDate, pendingIntentUpdate);
       
        (4)更新Text和进度条
        views.setTextViewText(R.id.mTvDate, dateString);
        views.setProgressBar(R.id.mProgressBar, 10, mProgress, false);
       
    2.AppWidgetManger类, 负责管理 AppWidget, 向 AppwidgetProvider 发送通知
        bindAppWidgetId(int appWidgetId, ComponentName provider)
            通过给定的ComponentName 绑定appWidgetId
        getAppWidgetIds(ComponentName provider)
            通过给定的ComponentName 获取AppWidgetId
        getAppWidgetInfo(int appWidgetId)
            通过AppWidgetId 获取 AppWidget 信息
        getInstalledProviders()
            返回一个List<AppWidgetProviderInfo>的信息
        getInstance(Context context)
            获取 AppWidgetManger 实例使用的上下文对象
        updateAppWidget(int[] appWidgetIds, RemoteViews views)
            通过appWidgetId 对传进来的 RemoteView 进行修改,并重新刷新AppWidget 组件
        updateAppWidget(ComponentName provider, RemoteViews views)
            通过 ComponentName 对传进来的 RemoeteView 进行修改,并重新刷新AppWidget 组件
        updateAppWidget(int appWidgetId, RemoteViews views)
            通过appWidgetId 对传进来的 RemoteView 进行修改,并重新刷新AppWidget 组件

三.工具类代码

package com.ahai.hellowidget;

import java.util.Set;

import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.os.Handler;

public class WidgetUpdateTask implements Runnable {

    public interface Callbacks {
        void onUpdateWidget(Context context, AppWidgetManager appWidgetManager,
                Set<Integer> ids);
    }

    private Context mContext;
    private AppWidgetManager mAppWidgetManager;
    private Set<Integer> mIDs;
    private boolean mIsStopped;
    private Callbacks mCallbacks;
    private Handler mHandler;
    private int mUpdateRate;

    public WidgetUpdateTask(Context context, AppWidgetManager appWidgetManager,
            Set<Integer> ids, Callbacks callbacks, Handler handler,
            int rateMills) {
        mContext = context;
        mAppWidgetManager = appWidgetManager;
        mIDs = ids;
        mCallbacks = callbacks;
        mHandler = handler;
        mUpdateRate = rateMills;
    }

    public void stop() {
        mIsStopped = true;
    }

    @Override
    public void run() {
        if (mIsStopped)
            return;
        mCallbacks.onUpdateWidget(mContext, mAppWidgetManager, mIDs);
        mHandler.postDelayed(this, mUpdateRate);
    }
}
时间: 2025-01-02 14:39:17

AppWidget学习总结的相关文章

appWidget的学习理解

AppWidget1.声明AppWIdget的布局文件.2.声明AppWidget的元数据文件3,声明Appwidget的控制器,该类继承子AppWidgetProvider4.在manefast文件中注册<receiver android:name=“com.example.android_day09_appwidget.MyAppWidget”><intent-filter><action android:name="android.appwidget.acti

android学习记录(十八)---AppWidget显示内容随数据改变而更新

我们知道,appWidget可通过设置widgetinfo中的updateTimeMillies设置更新的间隔,那么,当我们需要实时进行更新的时候呢?该如何进行appWidget显示内容的更新?如行程应用中添加了新的行程,想立刻在appWidget显示内容更新. 如何? 我们知道,widgetProvider本质上就是一个广播接收器,所以我们可以在数据修改处发送一个与之符合的广播. 当我们的intent并没附带任何额外数据,只定义了action的时候,会调用WidgetProvider中的onR

ANDROID_MARS学习笔记_S02_006_APPWIDGET3_AppWidget发送广播及更新AppWidget

一.简介 二.代码1.xml(1)example_appwidget.xml 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="vertical" 4 android:layout_w

Android学习笔记:Home Screen Widgets(2):关于Widget

通过widget定义,我们在widget列表中看到了我们的TestWidget.当我们拖拽widget到主页时,假设在appwidet-provider中定义了android:configure的java类,在widget实例创建后会立即唤起配置activity. 这个activity主要完毕两个任务:1.配置初始化数据:2.将配置数据适配到widget实例中. 利用preference中存贮配置数据 widget数据能够保持在文件.Share preference,或者SQLite3中.wid

AppWidght全面学习之电量监控小部件的实现详解

前几天翻看之前下载的各种资料,无意中发现了一款AppWidght应用的源代码,想起之前一直想研究这块,却一直没机会,于是花费了两天时间,把这款桌面电量监控小插件的实现研究了一下,收获颇丰,特此把学到的东西与大家分享.明天就是苦逼的信息论的期末考试了,我是一点看不懂,唉,就这样吧,重修再说吧,我们换个好心情,看一下这款小软件是如何实现的. 虽然这个小软件实现的不错,但是代码质量我却不敢恭维,费了好大劲,才把很多没用的代码和文件剔除,并且对一些实现进行了优化,话不多说,咱们先来看看效果图饱饱眼福.

Android Wear(手表)开发 - 学习指南

版权声明:欢迎自由转载-非商用-非衍生-保持署名.作者:Benhero,博客地址:http://www.cnblogs.com/benhero/ Android Wear开发 - 学习指南 http://www.cnblogs.com/benhero/p/4273800.html 前言 本篇文章是本人对这这一阶段Android Wear的开发总结,主要是对之前写的关于Android Wear方面技术的梳理,便于新人更好地交流学习. Android Wear究竟是什么? Android Wear和

android 中 AppWidget 的 ListView 的实现

3.0 以后系统直接支持了ListView. 关于ListView 的国内资料匮乏,大多数例子都是转来转去.由于初学android, 鄙人在搜索资料的时候遇到了不少麻烦~很是郁闷和苦恼~深感国内学习氛围确实怪异,学习方式需要改变.应该多去查看官方文档.... 话不多说,现在开始listView 实现: 这是文档列出的支持的布局和widget控件: A RemoteViews object (and, consequently, an App Widget) can support the fol

Pro Android学习笔记(一三七):Home Screen Widgets(3):配置Activity

文章转载仅仅能用于非商业性质,且不能带有虚拟货币.积分.注冊等附加条件.转载须注明出处http://blog.csdn.net/flowingflying/以及作者@恺风Wei. 通过widget定义.我们在widget列表中看到了我们的TestWidget.当我们拖拽widget到主页时.假设在appwidet-provider中定义了android:configure的java类,在widget实例创建后会立即唤起配置activity.这个activity主要完毕两个任务:1.配置初始化数据

Android的Widget桌面应用学习

一.Android应用的Widget的介绍 介绍:Android应用的Widget是应用程序窗口小部件(Widget)是微型的应用程序视图,它可以被嵌入到其它应用程序中(比如桌面)并接收周期性的更新.你可以通过一个App Widget Provider来发布一个Widget. 图片:首先上一张图来给大家看一看效果. 二.一些必要的概念介绍 2.1.AppWidgetProvider类 AppWidgetProvider 继承自 BroadcastReceiver,它能接收 widget 相关的广