• AppWidget学习总结



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

       

  • 相关阅读:
    实验4:开源控制器实践——OpenDaylight
    实验3:OpenFlow协议分析实践
    实验2:Open vSwitch虚拟交换机实践
    第一次个人编程作业
    SDN实验1:SDN拓扑实践
    第一次博客作业
    LeetCode-1290.Convert Binary Number in a Linked List to Integer
    面试题 02.02. Kth Node From End of List LCCI
    剑指 Offer 24. 反转链表
    剑指 Offer 06. 从尾到头打印链表
  • 原文地址:https://www.cnblogs.com/diysoul/p/5164154.html
Copyright © 2020-2023  润新知