Use AppWidgetProvider, RemoteViews, PendingIntent to develop desktop widgets

AppWidgetProvider

  • The android system provides classes for implementing desktop widgets, inheriting BroadcastReceiver, so its essence is a broadcast, and we can develop it according to the way the broadcast is used when we use it again

RemoteViews

  • Used for notification bars and desktop widgets, it provides a series of set methods to update the interface across processes, and these methods are only a subset of all View methods, and the supported View types are also limited. The internal mechanism of RemoteViews is not discussed here

PendingIntent

  • Indicates an intent in the pending state (pending, waiting, about to happen), which will happen at a specific moment, and the Intent will happen immediately. The typical usage scenario of PendingIntent is to set the click event for RemoteViews
  • PendingIntent.genActivity() is equivalent to Contex.startActivity(), PendingIntent.genSerivice() is equivalent to Contex.startSerivice(), and PendingIntent.genBroadcast() is equivalent to Contex.sendBroadcast()

Development Steps for Desktop Widgets

  1. Create a new layout file such as lcq_widget.xml and place it under res/layout
    <?xml version="1.0" encoding="utf-8"?>
    <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/lcq_img"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:contentDescription="@string/todo"
            android:src="@drawable/lcq_img" />
    </LinearLayout>
  2. Define the configuration information of the widget, such as lcq_appwidget_provider_info.xml, and put it under res/xml
    <?xml version="1.0" encoding="utf-8"?>
    <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
        android:initialLayout="@layout/lcq_widget"
        android:minWidth="100dp"
        android:minHeight="100dp"
        android:updatePeriodMillis="888000000" />

     initialLayout: The initial layout used by desktop gadgets; minHeight and minWidth are the minimum size of the gadget, updatePeriodMillis is the automatic update period, in milliseconds

  3. Define the implementation class of widgets such as LcqAppWidgetProvider
    package com.lcq.lcqappwidgetprovider;
    
    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.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Matrix;
    import android.widget.RemoteViews;
    import android.widget.Toast;
    
    public class LcqAppWidgetProvider extends AppWidgetProvider {
        public static final String ACTION_ROTATE = "com.lcq.action.rotate.CLICK";
    
        @Override
        public void onDeleted(Context context, int[] appWidgetIds) {
            //每删除一次桌面小部件就调用一次
            super.onDeleted(context, appWidgetIds);
            Toast.makeText(context, "onDeleted", Toast.LENGTH_SHORT).show();
        }
    
        @Override
        public void onEnabled(Context context) {
            //当窗口小部件第一次添加到桌面是调用该方法,可添加多次但是只在第一次添加时调用
            super.onEnabled(context);
            Toast.makeText(context, "onEnabled", Toast.LENGTH_SHORT).show();
        }
    
        @Override
        public void onDisabled(Context context) {
            //当最后一个改类型的桌面小部件被删除时调用该方法,注意是最后一个
            super.onDisabled(context);
            Toast.makeText(context, "onDisabled", Toast.LENGTH_SHORT).show();
        }
    
        @Override
        public void onReceive(Context context, Intent intent) {
            //这是广播的内置方法,用于分发据图的事件给其他方法
            super.onReceive(context, intent);
            Toast.makeText(context, "onReceive:aciton:" + intent.getAction(), Toast.LENGTH_SHORT).show();
            if (intent.getAction().equals(ACTION_ROTATE)) {
                Toast.makeText(context, "点击了图片,开始旋转", Toast.LENGTH_SHORT).show();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.lcq_img);
                        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
                        for (int i = 0; i < 37; i++) {//循环36次,将图片旋转360度
                            float degree = (i * 10) % 360;
                            RemoteViews remoteViews = obtainRemoteViews(context);
                            remoteViews.setImageViewBitmap(R.id.lcq_img, getBitmap(bitmap, degree));
                            appWidgetManager.updateAppWidget(new ComponentName(context, LcqAppWidgetProvider.class), remoteViews);
                            try {
                                Thread.sleep(30);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }).start();
            }
        }
    
        //获取旋转后的bitmap,degree为旋转角度
        private Bitmap getBitmap(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);
        }
    
        @Override
        public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
            //小部件被添加时或者每次小部件更新时都会调用一次改方法,
            //小部件的更新实际由updatePeriodMillis来指定,每个周期小部件都会自动更新异常
            super.onUpdate(context, appWidgetManager, appWidgetIds);
            final int len = appWidgetIds.length;
            Toast.makeText(context, "onUpdate:len=" + len, Toast.LENGTH_SHORT).show();
            for (int i = 0; i < len; i++) {
                updateWidget(context, appWidgetManager, appWidgetIds[i]);
            }
        }
    
        private void updateWidget(Context context, AppWidgetManager appWidgetManager, int id) {
            appWidgetManager.updateAppWidget(id, obtainRemoteViews(context));
        }
    
        //创建使用布局RemoteViews,并给lcq_img设置点击事件,注意:clickIntent必须设置ComponentName,否则8.0以上机型会接收不到广播
        private RemoteViews obtainRemoteViews(Context context) {
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.lcq_widget);
            Intent clickIntent = new Intent(ACTION_ROTATE);
            //在8.0以上广播机制有所变化,sendbroadcast前要指定下receiver的类
            clickIntent.setComponent(new ComponentName(context, LcqAppWidgetProvider.class));
            PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, clickIntent, 0);
            remoteViews.setOnClickPendingIntent(R.id.lcq_img, pendingIntent);
            return remoteViews;
        }
    }
    

    Here, a widget with ImageView is mainly implemented. Click the ImageView of the widget to rotate the picture 360 ​​degrees. Pay special attention to the clickIntent must be set to ComponentName, otherwise models above 8.0 will not receive broadcasts; several important rewriting methods of AppWidgetProvider, For example, the functions of onDeleted, onEnabled, onDisabled, onReceive, and onUpdate methods have been commented in the code

  4. Declare the widget in AndroidManifest.xml, because its essence is a broadcast, so static broadcast registration is required
            <receiver
                android:name="com.lcq.lcqappwidgetprovider.LcqAppWidgetProvider"
                android:exported="true">
                <meta-data
                    android:name="android.appwidget.provider"
                    android:resource="@xml/lcq_appwidget_provider_info" /><!--小部件的配信息-->
                <intent-filter>
                    <action android:name="com.lcq.action.rotate.CLICK" /><!--用于识别小部件的点击行为-->
                    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /><!--系统规范,必须存在,如果不加则在手机的小部件里面不会出现-->
                </intent-filter>
            </receiver>

    android:resource="@xml/lcq_appwidget_provider_info" widget configuration information; <action android:name="com.lcq.action.rotate.CLICK" />: used to identify the click behavior of widgets; <action android:
     name ="android.appwidget.action.APPWIDGET_UPDATE" />: system specification, must exist, if not added, it will not appear in the widget of the mobile phone

  5. Compile the code to generate an apk file and install it on the phone
  6. Press and hold the desktop of the mobile phone (different brands of mobile phones may operate in different ways) and the desktop setting interface will pop up, select Add Tool
  7. Then navigate to our application and select the
  8. After selecting it, go back to the desktop setting interface, and you can see that the widget we developed has been added. This repeatable operation will result in adding multiple widgets
  9. We go back to the desktop, and then click on the desktop widget we developed, we can see that it has turned up
  •  The specific development steps and precautions have been explained, and you can make more complex and better-looking effects and functions on this basis

 

Guess you like

Origin blog.csdn.net/qq_19942717/article/details/126923975