1. 程式人生 > >Android桌面小部件(入門)

Android桌面小部件(入門)

先上效果圖

寫在前面

桌面小部件的應用還是很多的,比如網易雲音樂、微博等,雖然我們開發的APP並不一定需要開發這個,但簡單瞭解學習一下還是可以的。

AppWidgetProvider是Android提供的開發小部件的類,所以我們開發小部件即只需要繼承這個類,重寫其中的某些方法。這是我們看到系統有自帶的小部件,比如時鐘、計算器等,這些小部件的類應該是被嵌入到自家定製的系統裡了(個人猜測,不知道怎麼證明,有人知道請告訴我)。

具體開發步驟

1.定義小部件介面

<?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/imageview1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@drawable/img_2"/>

</LinearLayout>

跟activity佈局檔案的放置位置一樣,內容和命名都沒要求,自己喜歡就好。我這裡命名為widget.xml。

2.定義小部件配置資訊

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

</appwidget-provider>

這裡需要在res/xml下新建,名稱任意,這裡initialLayout就是指小部件使用的初始化佈局,minHeight和minWidth定義小部件的最小尺寸,updatePeriodMillis定義小工具的自動更新週期,毫秒為單位,每隔一個週期,小工具的自動更新就會觸發(我們前面說到的微博之類的是需要不斷更新內容的)。

3.定義小部件的實現類

package learn.yfg.com.learnapplication;

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.os.SystemClock;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.Toast;

/**
 * @version: v1.0
 * @description: 自定義小部件
 * @package: learn.yfg.com.learnapplication
 * @author: 酥小魚
 * @date :2018/10/16
 */

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

    @Override
    public void onReceive(final Context context, Intent intent) {
        super.onReceive(context, intent);
        Log.i(TAG, "onReceive: action" + intent.getAction());
        //這裡是判斷自己的action,做自己的事情,比如小部件點選了要幹什麼,這裡是做一個動畫效果
        if (intent.getAction().equals(CLICK_ACTION)){
//            Toast.makeText(context, "Clicked it", Toast.LENGTH_SHORT).show();

            new Thread(new Runnable() {
                @Override
                public void run() {
                    Bitmap srcbBitmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.img_1);
                    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.imageview1,rotateBitmap(context,srcbBitmap,degree));
                        Intent intentClick = new Intent();
                        intentClick.setAction(CLICK_ACTION);
                        PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0,intentClick,0);
                        remoteViews.setOnClickPendingIntent(R.id.imageview1,pendingIntent);
                        appWidgetManager.updateAppWidget(new ComponentName(context,MyAppWidgetProvider.class),remoteViews);
                        SystemClock.sleep(300);
                    }
                }
            }).start();
        }
    }

    private Bitmap rotateBitmap(Context context, Bitmap srcbBitmap, float degree) {
        Matrix matrix = new Matrix();
        matrix.reset();
        matrix.setRotate(degree);
        Bitmap tmpBitmap = Bitmap.createBitmap(srcbBitmap,0,0,srcbBitmap.getWidth(),srcbBitmap.getHeight(),matrix,true);
        return tmpBitmap;
    }

    /**
     * 每次桌面小部件更新都呼叫一次該方法
     */
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        Log.i(TAG, "onUpdate: ");
        final int counter = appWidgetIds.length;
        Log.i(TAG, "counter =  " + counter);
        for (int i = 0; i < counter; i++) {
            int appWidgetId = appWidgetIds[i];
            onAppWidgetUpdate(context,appWidgetManager,appWidgetId);
        }
    }

    /**
     * 桌面小部件更新
     * @param context
     * @param appWidgetManager
     * @param appWidgetId
     */
    private void onAppWidgetUpdate(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
        Log.i(TAG, "appWidgetId = " + appWidgetId);
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget);
        //“桌面小部件” 點選事件傳送的intent廣播
        Intent intentClick = new Intent();
        intentClick.setAction(CLICK_ACTION);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0,intentClick,0);
        remoteViews.setOnClickPendingIntent(R.id.imageview1,pendingIntent);
        appWidgetManager.updateAppWidget(new ComponentName(context,MyAppWidgetProvider.class),remoteViews);
    }
}

看下這個類,繼承父類,無參構造器,實現onReceive和onUpdate方法。

主要看下這兩個方法,onReceive裡判斷了action,

這裡用到了RemoteViews,這個說一下這個控制元件對應的佈局(是有一定限制的),僅支援的型別如下

Layout:FrameLayout、LinearLayout、RealativeLayout、GridLayout

View:AnalogClock、Button、Chronometer、ImageButton、ImageView、ProgressBar、TextView、ViewFlipper、ListView、GridView、StackView、AdapterViewFlipper、ViewStub

還用到了PendingIntent,這個解釋一下,這個是指即將發生的intent,和intent不同,intent是立即發生,pendingIntent不確定性,典型的應用場景就是運用在remoteViews裡面,這裡支援三種意圖,即活動、服務和廣播,對應的getActivity、getService、getBroadcast,pendingIntent發生時效果相當於startActivity等方法了。

在onReceive裡面主要分發具體的事件,關於事件分發我們有講過,我們這裡進行了動畫旋轉效果。即rotateBitmap方法。這裡涉及的Matrix等,以後我會在自定義控制元件裡面細說,這裡不多說。

還有一個onUpadate方法,這裡主要更新小部件。

另外還有幾個方法,我這裡並沒有重寫,分別為

onEnable:新增小部件第一次會被呼叫,新增多次也只會呼叫一次

onDelete:每刪除一次桌面小部件就會呼叫一次

onDisabled:當最後一個該型別的桌面小部件被刪除時呼叫

4.在AndroidManifest.xml中宣告小部件

<receiver android:name=".MyAppWidgetProvider">
            <meta-data android:name="android.appwidget.provider"
                android:resource="@xml/appwidget_provider_info">
                </meta-data>
            <intent-filter>
                <action android:name="learn.yfg.com.learnapplication.action.CLICK"/>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
            </intent-filter>
            </receiver>

這裡記得要註冊這個廣播,小部件的配置檔案、小部件行為的識別、小部件的標誌都不可少。即resource、action等。

這些完成後就可以執行我們的demo,然後直接開啟手機的小工具列表,找到我們app下的小工具,新增到桌面,點選,就和效果圖一致了(哈哈,比較魔性,這個小工具,沒事點著轉)

寫在最後

這裡基本所有的程式碼都有貼上,至於這麼靈性的圖片,哈哈,可以進群找我要。