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

最新文章

  1. Asp.Net Core 发布和部署( MacOS + Linux + Nginx )
  2. Java程序性能优化技巧
  3. NOIP2013
  4. JavaScript格式化日期
  5. turn.js实现翻书效果
  6. Rac &amp; DG
  7. 项目用到异步加载头像LasyList
  8. 【译】C++日志(Logging in C++)
  9. 【Android Developers Training】 12. 支持不同屏幕
  10. Linux 下各文件夹的含义
  11. 关于null的判断
  12. [转] Firewall and network filtering in libvirt
  13. DataFrame 行列数据的筛选
  14. Postgresql操作json格式数据
  15. LOJ 3093: 洛谷 P5323: 「BJOI2019」光线
  16. spring.boot mybaits集成
  17. STS启动失败:Failed to load the JNI shared library
  18. 78. Subsets(M) &amp; 90. Subsets II(M) &amp; 131. Palindrome Partitioning
  19. Oracle 与Sql Server常用函数对比
  20. http学习笔记1

热门文章

  1. 以太坊开发(二)使用Ganache CLI在私有链上搭建智能合约
  2. HDU 4300 Clairewd’s message (next函数的应用)
  3. Memory及其controller芯片整体测试方案(下篇)
  4. Java中的线程的优先级
  5. (转)NEST.net Client For Elasticsearch简单应用
  6. 使用source创建一个新项目(将本地项目文件和github远程库链接)
  7. 加密和数字签名工具GPG
  8. VC学习笔记:对话框
  9. [计算机网络] C++模拟telnet登陆SMTP服务发送邮件过程
  10. overflow:scroll 在ios 滚动卡顿