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 android.annotation.SuppressLint;
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 K:mm:ss a";
public static final int RATE_UPDATE_TIME = 1000; private static Handler mHandler;
private static WidgetUpdateTask mPendingUpdates; private static boolean mIs24Hour = true;
private static int mProgress; @SuppressLint("NewApi")
@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");
ComponentName provider = new ComponentName(context, this.getClass());
int[] ids = AppWidgetManager.getInstance(context).getAppWidgetIds(provider);
if (ids == null || ids.length == 0) {
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");
d("context:" + context);
if (mHandler == null) {
mHandler = new Handler(context.getMainLooper());
}
if (mPendingUpdates != null) {
mHandler.removeCallbacks(mPendingUpdates);
}
mPendingUpdates = new WidgetUpdateTask(context, 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);
} @SuppressLint("SimpleDateFormat")
@Override
public void onUpdateWidget(Context context) { ComponentName provider = new ComponentName(context, this.getClass());
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] ids = appWidgetManager.getAppWidgetIds(provider);
if (ids == null || ids.length == 0) {
return;
}
Date date = new Date();
SimpleDateFormat sdf;
if (mIs24Hour) {
sdf = new SimpleDateFormat(DATE_AND_TIME_24HOUR_FORMAT);
} else {
// DateFormatSymbols dfSymbols = new DateFormatSymbols();
sdf = new SimpleDateFormat(DATE_AND_TIME_12HOUR_FORMAT);
}
String dateString = sdf.format(date); 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指针指向的是不同的对象,原因是BroadcastReciever在onReceive执行完毕之后被回收.

      此后如需要更新时需要先调用接口getAppWidgetIds取得所有的id值.

      ComponentName provider = new ComponentName(context, this.getClass());

      AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
      int[] ids = appWidgetManager.getAppWidgetIds(provider);
      if (ids == null || ids.length == 0) {
        return;
      }

(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);
}
}

AppWidget学习总结的更多相关文章

  1. Android学习之AppWidget高级效果

    接着AppWidget基础学习,今天是一个"进阶版"的小例子,用来检验一下自己的学习效果.于是就做了一个掷骰子的Widget. 方便大家观看,先截图如下: 需要注意的是在drawa ...

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

    一.简介 二.代码1.xml(1)example_appwidget.xml <?xml version="1.0" encoding="utf-8"?& ...

  3. Android学习笔记之AppWidget

    什么是AppWidget?AppWidget就是我们平常在桌面上见到的那种一个个的小窗口,利用这个小窗口可以给用户提供一些方便快捷的操作. 今天的目标就是怎么创建一个简单的AppWidget. 首先我 ...

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

    版权声明:欢迎自由转载-非商用-非衍生-保持署名.作者:Benhero,博客地址:http://www.cnblogs.com/benhero/ Android Wear开发 - 学习指南 http: ...

  5. Android之桌面组件AppWidget

    转载:Android之桌面组件App Widget初探 Android开发应用除了程序应用,还有App Widget应用.好多人会开发程序应用而不会开发App Widget应用.本帖子就是帮助大家学习 ...

  6. android学习精要

    第1章 初识android1.1 android平台概述1.2 android平台体系1.2.1 linux kernel内核层1.2.2 系统运行库libraries和android runtime ...

  7. Android之AppWidget 开发浅析

    什么是AppWidget AppWidget 即桌面小部件,也叫桌面控件,就是能直接显示在Android系统桌面上的小程序,先看图: 图中我用黄色箭头指示的即为AppWidget,一些用户使用比较频繁 ...

  8. Android安装器学习笔记(一)

    Android安装器学习笔记(一) 一.Android应用的四种安装方式: 1.通过系统应用PackageInstaller.apk进行安装,安装过程中会让用户确认 2.系统程序安装:在开机的时候自动 ...

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

    文章转载仅仅能用于非商业性质,且不能带有虚拟货币.积分.注冊等附加条件.转载须注明出处http://blog.csdn.net/flowingflying/以及作者@恺风Wei. 通过widget定义 ...

随机推荐

  1. ionic 获取input的值

    1.参数传递法 例子:获取input框内容 这里有个独特的地方,直接在input处使用 #定义参数的name值,注意在ts中参数的类型 在html页面中 <ion-input type=&quo ...

  2. 【shell 每日一练6】初始化安装Mysql并修改密码

    一.简单实现mysql一键安装 参考:[第二章]MySQL数据库基于Centos7.3-部署 此脚本前提条件是防火墙,selinux都已经设置完毕: [root@web130 ~]# cat Inst ...

  3. jQuery 调用后台方法(net)

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs ...

  4. C Program进阶-数组

    (一)数组的内存布局 对于语句int a[5]; 我们明白这里定义了一个数组,数组里有5个元素,每一个元素都是int类型,我们可以用a[0],a[1]等访问数组里的元素,但是这些元素的名字就是a[0] ...

  5. 并查集(Union/Find)模板及详解

    概念: 并查集是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题.一些常见的用途有求连通子图.求最小生成树的Kruskal 算法和求最近公共祖先等. 操作: 并查集的基本操作有两个 ...

  6. LintCode-381.螺旋矩阵 II

    螺旋矩阵 II 给你一个数n生成一个包含1-n^2的螺旋形矩阵 样例 n = 3 矩阵为 [     [ 1, 2, 3 ],     [ 8, 9, 4 ],     [ 7, 6, 5 ] ] 标 ...

  7. git初始化之git config

    git初始化之git config     1. 下面的命令将修改/home/[username]/.gitconfig文件,也就是说下面的配置只对每一个ssh的用户可见,所以每个人都需要做.   提 ...

  8. 【Linux】- CentOS 7 安装.NET Core 2.1

    添加dotnet产品Feed 在安装.NET Core之前,您需要注册Microsoft产品Feed. 这只需要做一次. 首先,注册Microsoft签名密钥,然后添加Microsoft产品Feed. ...

  9. ipython matplotlib

    matplotlib实际上是一套面向对象的绘图库,它所绘制的图表中的每个绘图元素,例如线条Line2D.文字Text.刻度等在内存中都有一个对象与之对应.为了方便快速绘图matplotlib通过pyp ...

  10. mysql通过binlog恢复数据

    如果mysql不小心操作失误导致数据错误或者丢失这时候binlog起到了很大的作用 恢复有几种方式 1.按时间恢复--start-datetime   如果确定了时间点,那么按时间恢复是一个再好不过的 ...