理解 RemoteViews
RemoteViews 表示的是一個 View 結構,它可以在其他程序中顯示,並提供了一組基礎的操作用於跨程序更新介面。
RemoteViews 的應用
- RemoteViews 在實際中,主要用於開發通知欄和桌面小部件。
-
通知欄通過 NotificationManager 的
notify
方法來實現。 - 桌面小部件通過 AppWidgetProvider 來實現,本質上是一個廣播接收器。
-
RemoteViews 在通知欄上的應用示例:
Notification notification = new Notification(); notification.icon = R.drawable.ic_launcher; notification.tickerText = "hello world"; notification.when = System.currentTimeMillis(); notification.flags = Notification.FLAG_AUTO_CANCEL; Intent intent = new Intent(this, DemoActivity_1.class); intent.putExtra("sid", "" + sId); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_notification); remoteViews.setTextViewText(R.id.msg, "chapter_5: " + sId); remoteViews.setImageViewResource(R.id.icon, R.drawable.icon1); PendingIntent openActivity2PendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, DemoActivity_2.class), PendingIntent.FLAG_UPDATE_CURRENT); remoteViews.setOnClickPendingIntent(R.id.open_activity2, openActivity2PendingIntent); notification.contentView = remoteViews; notification.contentIntent = pendingIntent; NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); manager.notify(sId, notification);
-
RemoteViews 在桌面小部件上的應用示例:
-
定義小部件佈局介面
widget.xml
-
在 res/xml 下新建
widget_info.xml
小部件配置資訊<?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:initialLayout="@layout/bus_widget" // 指向佈局檔案 android:minHeight="250dp" // 尺寸可以通過公式計算出:70dp × n − 30dp,n 代表所佔格數 android:minWidth="250dp"// 同上 android:previewImage="@drawable/bus_widget_preview" // 新增小部件時的預覽圖 android:updatePeriodMillis="86400000"> // 小部件自動更新週期,單位為毫秒 </appwidget-provider>
-
定義小部件的實現類 MyAppWidgetProvider 繼承 AppWidgetProvider
-
在 AndroidManifest.xml 中宣告小部件
<receiver android:name=".MyAppWidgetProvider" android:label="xxxx 小部件"> <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_info"> </meta-data> <intent-filter> <action android:name="com.xxx.action.REFRESH" /> // 自定義 action,用於點選或觸發某些操作 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> // 必須要加,否則無法正常顯示和使用小部件 </intent-filter> </receiver>
-
AppWidgetProvider 常見方法:
- onEnable :當小部件第一次新增到桌面時呼叫該方法,可新增多次,但只在第一次時呼叫
- onUpdate :小部件被新增時或者每次小部件更新時都會呼叫一次該方法,小部件更新時機由 updatePeriodMillis 來決定
- onDelete :沒刪除一次小部件就會呼叫一次
- onDisabled :當最後一個該型別的小部件被刪除時呼叫該方法
- onReceive :這是廣播接收器的內建方法,用於分發具體的事件給其他方法
-
定義小部件佈局介面
- PendingIntent 表示一種處於 pending 狀態的意圖,典型應用場景就是給 RemoteViews 新增點選事件。
- PendingIntent 支援三種待定意圖:啟動 Activity(getActivity)、啟動 Service(getService) 和傳送廣播(getBroadcast)。
-
以
getBroadcast(Context context, int requestCode, Intent intent, int flags)
為例說明下 PendingIntent 的匹配規則:- 如果兩個 PendingIntent 內部 Intent 相同並且 requestCode 相同,那麼它們就是相同的。
- 如果兩個 Intent 的 ComponentName 和 intent-filter 都相同,那麼它們就是相同的。
-
PendingIntent flags 引數含義:
- FLAG_ONE_SHOT :表示當前描述的 PendingIntent 只能被使用一次,對於通知欄訊息來說,同類的訊息只能開啟一次,後續的都無法開啟
- FLAG_NO_CREATE :表示當前描述的 PendingIntent 不會主動建立,直接返回 null
- FLAG_CANCEL_CURRENT :表示當前描述的 PendingIntent 如果已存在,那麼它們都會被 cancel,系統會建立一個新的 PendingIntent,對於通知欄訊息來說,被 cancel 的訊息都無法開啟
- FLAG_UPDATE_CURRENT :表示當前描述的 PendingIntent 如果已存在,那麼它們都會被更新,即它們的 Intent 中的 Extras 都會被替換成最新的。
- FLAG_IMMUTABLE :表示當前描述的 PendingIntent 是不可變的。
RemoteViews 的內部機制
-
常見構造方法:
public RemoteViews(String packageName, int layoutId)
-
RemoteViews 並不支援所有 View 型別,支援的型別如下:
-
Layout
FrameLayout、LinearLayout、RelativeLayout、GridLayout
-
View
Button、ImageView、ImageButton、TextView、ProgressBar、ListView、GridView、StackView、ViewStub、AdapterViewFlipper、ViewFlipper、AnalogClock、Chronometer
-
-
RemoteViews 部分 set 方法:
- setTextViewText(viewId, text)設定文字 - setTextColor(viewId, color)設定文字顏色 - setTextViewTextSize(viewId, units, size)設定文字大小 - setImageViewBitmap(viewId, bitmap)設定圖片 - setImageViewResource(viewId, srcId)根據圖片資源設定圖片 - setViewPadding(viewId, left, top, right, bottom)設定 Padding 間距 - setOnClickPendingIntent(viewId, pendingIntent)設定點選事件 - setInt(viewId, methodName, value)反射呼叫引數為 int 的 methodName 方法 - setLong(viewId, methodName, value)反射呼叫引數為 long 的 methodName 方法 ...
-
具體過程:
首先 RemoteViews 會通過 Binder 傳遞到 SystemServer 程序,這是因為 RemoteViews 實現了 Parcelable 介面,因此它可以跨程序傳輸,系統會根據 RemoteViews 中的包名等資訊區得到該應用的資源。然後會通過 LayoutInflater 載入 RemoteViews 中的佈局檔案,在 SystemServer 程序中載入後的佈局檔案是一個普通的 View,只不過相對本地程序它是一個 RemoteViews 而已。接著系統會對 View 執行一系列的介面更新任務(action),這些任務就是我們通過 set 方法提交的。set 方法對 View 所做的更新並不是立刻執行的,在 RemoteViews 內部會記錄所有的更新操作,具體的執行操作要等到 RemoteViews 被載入以後才能執行,這樣 RemoteViews 就可以在 SystemServer 程序中顯示了。當需要更新 RemoteViews 時,我們需要呼叫一系列 set 方法並通過 NotificationManager 和 AppWidgetManager 來提交更新任務,具體的更新操作也是在 SystemServer 程序中完成的。
- RemoteViews 只支援發起 PendingIntent。
-
setOnClickPendingIntent
只能用於給 RemoteViews 中的普通 View 設定點選事件,不能用於集合(ListView 和 StackView)中的 Item 點選事件。 -
ListView 和 StackView 中的 item 新增點選事件,則必須將
setPendingIntentTemplate
和setOnClickFillInIntent
組合使用。
本章節配套原始碼ofollow,noindex">戳我