Android学习笔记之桌面小工具AppWidget

本文详细介绍了如何使用AppWidgetProvider创建Android桌面小工具,包括定义界面、配置信息、实现类及注册过程。并演示了一个简单小工具的完整实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现桌面小工具的关键类AppWidgProvider

AppWidgetProvider的本质是一个广播接收者,如下:

public class AppWidgetProvider extends BroadcastReceiver 

说道广播接收者,那么它的工作原理应该是接收到对应的广播后会去调用其onReceive方法。接下来就来看它的onReceive方法:

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);
            }
        }
    } else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
        Bundle extras = intent.getExtras();
        if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
            final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
            this.onDeleted(context, new int[] { appWidgetId });
        }
    } else if (AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED.equals(action)) {
        Bundle extras = intent.getExtras();
        if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)
                && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS)) {
            int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
            Bundle widgetExtras = extras.getBundle(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS);
            this.onAppWidgetOptionsChanged(context, AppWidgetManager.getInstance(context),
                    appWidgetId, widgetExtras);
        }
    } else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
        this.onEnabled(context);
    } else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
        this.onDisabled(context);
    } else if (AppWidgetManager.ACTION_APPWIDGET_RESTORED.equals(action)) {
        Bundle extras = intent.getExtras();
        if (extras != null) {
            int[] oldIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS);
            int[] newIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
            if (oldIds != null && oldIds.length > 0) {
                this.onRestored(context, oldIds, newIds);
                this.onUpdate(context, AppWidgetManager.getInstance(context), newIds);
            }
        }
    }
}

从上述源码中可以看出其逻辑近似于switch,APPWidgetProvider 会自动比对广播的 Action,从而分发广播,也就是分情况调用 onUpdate,onEnabled,onDisabled,onDeleted等方法。而这几种方法的调用时机如下:

  • onEnable:当该桌面小工具第一次添加到桌面时调用该方法,可添加多次但只在第一次调用
  • onUpdate:小工具被添加时或者每次小工具更新时都会调用一次该方法,小工具的更新时机由updatePeriodMillis来指定,每个周期小工具都会自动更新一次
  • onDeleted:每删除一次桌面小工具就调用一次
  • onDisabled:当最后一个该类型的桌面小工具被删除时调用该方法
  • onReceive:这是广播的内置方法,用于分发具体的事件给其他方法

实现桌面小工具的步骤

1.定义小工具的界面

和创建布局文件一样,在res/layout/目录下新建一个xml文件即可,名称和内容均可自定义。这里只是简单的添加了一个ImageView.

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/timg"/>
</LinearLayout>

2.定义小工具的配置信息

在res/xml/目录下新建一个xml文件,名称可以自定义,这里新建了一个appwidget_info.xml:

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    android:initialLayout="@layout/widget"
    android:minHeight="84dp"
    android:minWidth="84dp"
    android:updatePeriodMillis="86400000"
    xmlns:android="http://schemas.android.com/apk/res/android">
</appwidget-provider>

 这里只用到了最基本的最小宽高属性以及刷新频率,其中86400000单位是ms即24小时,也就是一天一次。除此之外,它还支持一些其他的属性,如下:

  • previewImage:当用户选择添加 widget 时的预览图片。如果该属性没有定义,则展示 applicationg 的 launcher icon
  • configure:定义了用户在添加 widget 时弹出的配置页面的 Activity,用户可以在此进行 Widget 的一些配置,该 Activity 是可选的,如果不需要可以不进行声明
  • resizeMode:widget 在水平和垂直方向是否可以调整大小,值可以为:horizontal(水平方向可以调整大小),vertical (垂直方向可以调整大小),none(不可以调整大小),也可以 horizontal|vertical 组合表示水平和垂直方向均可以调整大小
  • widgetCategory:表示 widget 可以显示的位置,包括 home_screen(桌面),keyboard(锁屏)

3. 定义小工具的实现类

这个类需要继承AppWidge Provider,代码如下:

public class MyAppWidgetProvider extends AppWidgetProvider {
    private static final String TAG = "MyAppWidgetProvider";
    public static final String CLICK_ACTION = "com.appwidget.action.CLICK";
    public MyAppWidgetProvider() {
        super();
    }

    @Override
    public void onReceive(final Context context, Intent intent) {
        super.onReceive(context, intent);
        Log.d(TAG, "onReceive: ");
        if(intent.getAction().equals(CLICK_ACTION)){//如果是自己的Action
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Bitmap srcBitmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.timg);
                    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
                    //旋转
                    for(int i=0;i<37;i++){
                        float degree = (i*10)%360;
                        RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget);
                        //设置旋转后的图片
                        remoteViews.setImageViewBitmap(R.id.iv_icon,rotateBitmap(srcBitmap,degree));

                        Intent intentClick = new Intent();
                        intentClick.setAction(CLICK_ACTION);
                        PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0,intentClick,0);
                        remoteViews.setOnClickPendingIntent(R.id.iv_icon,pendingIntent);//设置点击事件监听

                        appWidgetManager.updateAppWidget(new ComponentName(context,MyAppWidgetProvider.class),remoteViews);
                        SystemClock.sleep(30);
                    }
                }
            }).start();
        }
    }
    /**
     * 每次桌面小工具更新时都会调用一次该方法
     * */
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        final int counter = appWidgetIds.length;
        for(int i = 0;i<counter;i++){
            int appWidgetId = appWidgetIds[i];
            onWidgetUpdate(context,appWidgetManager,appWidgetId);
        }
    }

    /**
     * 更新桌面小工具
     * */
    private void onWidgetUpdate(Context context,AppWidgetManager appWidgetManager,int appWidgetId){
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget);
        Intent intentClick = new Intent();
        intentClick.setAction(CLICK_ACTION);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0,intentClick,0);
        remoteViews.setOnClickPendingIntent(R.id.iv_icon,pendingIntent);
        appWidgetManager.updateAppWidget(appWidgetId,remoteViews);
    }

    /**
     * 旋转图片
     * */
    private Bitmap rotateBitmap(Bitmap bitmap,float degree){
        Matrix matrix = new Matrix();
        matrix.reset();
        matrix.setRotate(degree);
        return Bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true);
    }
}

上面的代码实现了一个简单的桌面小工具,该小工具展示一张图片,并且点击它之后,这个图片会旋转一周。当这个桌面小工具被添加到桌面后,会通过 RemoteViews 来加载布局文件,而当桌面小工具被单击之后的旋转效果则是通过不断的更新RemoteViews 来实现的。

4.在AndroidManifest.xml文件中声明该桌面小工具、然后动态注册小工具的实现类(广播接收者)

<receiver android:name=".MyAppWidgetProvider">
    <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/appwidget_info"></meta-data>
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
    </intent-filter>
</receiver>
MyAppWidgetProvider appWidgetProvider = new MyAppWidgetProvider();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(MyAppWidgetProvider.CLICK_ACTION);
registerReceiver(appWidgetProvider,intentFilter);

其中 AndroidManifest 中的 action 是桌面小工具的标识,是系统规范,如果不加那么这个 receiver 则不是一个桌面小工具,无法在手机的小工具列表中找到它。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值