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下的小工具,新增到桌面,點選,就和效果圖一致了(哈哈,比較魔性,這個小工具,沒事點著轉)
寫在最後
這裡基本所有的程式碼都有貼上,至於這麼靈性的圖片,哈哈,可以進群找我要。