一、RemoteViews基礎認知
RemoteViews是一種可以在其他進程中顯示的View,提供了一組基礎操作用于跨進程更新它的界面。主要兩個場景:通知欄和桌面小部件。
1窃躲、Notification
1.1、分類
1)普通的notification
1.內容標題
2.大圖標
3.內容
4.內容附加信息
5.小圖標
6.時間
2)大布局Notification
大布局notification是在android4.1以后才增加的钦睡,大布局notification與小布局notification只在7部分有區(qū)別蒂窒,其它部分都一致。大布局notification只有在所有notification的最上面時才會顯示大布局荞怒,其它情況下顯示小布局洒琢。你也可以用手指將其擴展為大布局(前提是它是大布局)。
大布局notification有三種類型:圖1為NotificationCompat.InboxStyle類型挣输。圖2左部為NotificationCompat.BigTextStyle纬凤。圖2右部 為:NotificationCompat.BigPictureStyle
3)自定義布局notification
1.2、基本操作
Notification的基本操作主要有創(chuàng)建撩嚼、更新停士、取消這三種挖帘。一個Notification的必要屬性有三項,如果不設置則在運行時會拋出異常:
- 小圖標恋技,通過
setSmallIcon()
方法設置 - 標題拇舀,通過
setContentTitle
方法設置 - 內容,通過
setContentText()
方法設置
1)創(chuàng)建Notification
Notification.Builer: 使用建造者模式構建Notification對象蜻底。由于Notification.Builder 僅支持 Android 4.1及之后的版本骄崩,為了解決兼容性問題, Google 在 Android Support v4 中加入了 NotificationCompat.Builder 類薄辅。對于某些在 Android 4.1 之后才特性要拂,即使 NotificationCompat.Builder 支持該方法,在之前的版本中也不能運行站楚。
Notification : 通知對應類脱惰,保存通知相關的數(shù)據(jù)。NotificationManager 向系統(tǒng)發(fā)送通知時會用到窿春。
NotificationManager : NotificationManager是通知管理類拉一,它是一個系統(tǒng)服務。調用notify(int id, Notification notification)
方法可以向系統(tǒng)發(fā)送通知(取消通知:cancel(int id)
旧乞,id就是這么用的)蔚润。
//獲取PendingIntent
Intent mainIntent = new Intent(this, MainActivity.class);
PendingIntent mainPendingIntent = PendingIntent.getActivity(this, 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//創(chuàng)建 Notification.Builder 對象
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
//點擊通知后自動清除
.setAutoCancel(true)
.setContentTitle("我是帶Action的Notification")
.setContentText("點我會打開MainActivity")
.setContentIntent(mainPendingIntent);
//發(fā)送通知
mNotifyManager.notify(3, builder.build());
2)更新Notification
更新通知很簡單,只需要再次發(fā)送相同ID的通知即可尺栖,如果之前的通知還未被取消嫡纠,則會直接更新該通知相關的屬性;如果之前的通知已經被取消决瞳,則會重新創(chuàng)建一個新通知货徙。更新通知跟發(fā)送通知使用相同的方式。
3)取消Notification
取消通知有如下 5 種方式:
- 點擊通知欄的清除按鈕皮胡,會清除所有可清除的通知
- 設置了 setAutoCancel() 或 FLAG_AUTO_CANCEL 的通知,點擊該通知時會清除它
- 通過 NotificationManager 調用 cancel(int id) 方法清除指定 ID 的通知
- 通過 NotificationManager 調用 cancel(String tag, int id) 方法清除指定 TAG 和 ID 的通知
- 通過 NotificationManager 調用 cancelAll() 方法清除所有該應用之前發(fā)送的通知
如果你是通過 NotificationManager.notify(String tag, int id, Notification notify)
方法創(chuàng)建的通知赏迟,那么只能通過 NotificationManager.cancel(String tag, int id)
方法才能清除對應的通知屡贺,調用NotificationManager.cancel(int id)
無效。
1.3锌杀、設置Notification的通知效果
Notification 有震動甩栈、響鈴、呼吸燈三種響鈴效果糕再,可以通過 setDefaults(int defualts) 方法來設置量没。 Default 屬性有以下四種,一旦設置了 Default 效果突想,自定義的效果就會失效殴蹄。
//設置系統(tǒng)默認提醒效果究抓,一旦設置默認提醒效果,則自定義的提醒效果會全部失效袭灯。具體可看源碼
//添加默認震動效果,需要申請震動權限
//<uses-permission android:name="android.permission.VIBRATE" />
Notification.DEFAULT_VIBRATE
//添加系統(tǒng)默認聲音效果刺下,設置此值后,調用setSound()設置自定義聲音無效
Notification.DEFAULT_SOUND
//添加默認呼吸燈效果稽荧,使用時須與 Notification.FLAG_SHOW_LIGHTS 結合使用,否則無效
Notification.DEFAULT_LIGHTS
//添加上述三種默認提醒效果
Notification.DEFAULT_ALL
除了以上幾種設置 Notification 默認通知效果畅卓,還可以通過以下幾種 FLAG 設置通知效果蟋恬。
//提醒效果常用 Flag
//三色燈提醒髓介,在使用三色燈提醒時候必須加該標志符
Notification.FLAG_SHOW_LIGHTS
//發(fā)起正在運行事件(活動中)
Notification.FLAG_ONGOING_EVENT
//讓聲音、振動無限循環(huán)唐础,直到用戶響應 (取消或者打開)
Notification.FLAG_INSISTENT
//發(fā)起Notification后矾飞,鈴聲和震動均只執(zhí)行一次
Notification.FLAG_ONLY_ALERT_ONCE
//用戶單擊通知后自動消失
Notification.FLAG_AUTO_CANCEL
//只有調用NotificationManager.cancel()時才會清除
Notification.FLAG_NO_CLEAR
//表示正在運行的服務
Notification.FLAG_FOREGROUND_SERVICE
/**
* 最普通的通知效果
*/
private void showNotifyOnlyText() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(mLargeIcon)
.setContentTitle("我是只有文字效果的通知")
.setContentText("我沒有鈴聲一膨、震動、呼吸燈,但我就是一個通知");
mManager.notify(1, builder.build());
}
/**
* 展示有自定義鈴聲效果的通知
* 補充:使用系統(tǒng)自帶的鈴聲效果:Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, "6");
*/
private void showNotifyWithRing() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("我是伴有鈴聲效果的通知")
.setContentText("美妙么?安靜聽~")
//調用系統(tǒng)默認響鈴,設置此屬性后setSound()會無效
//.setDefaults(Notification.DEFAULT_SOUND)
//調用系統(tǒng)多媒體褲內的鈴聲
//.setSound(Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI,"2"));
//調用自己提供的鈴聲豹绪,位于 /res/values/raw 目錄下
.setSound(Uri.parse("android.resource://com.littlejie.notification/" + R.raw.sound));
//另一種設置鈴聲的方法
//Notification notify = builder.build();
//調用系統(tǒng)默認鈴聲
//notify.defaults = Notification.DEFAULT_SOUND;
//調用自己提供的鈴聲
//notify.sound = Uri.parse("android.resource://com.littlejie.notification/"+R.raw.sound);
//調用系統(tǒng)自帶的鈴聲
//notify.sound = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI,"2");
//mManager.notify(2,notify);
mManager.notify(2, builder.build());
}
/**
* 展示有震動效果的通知,需要在AndroidManifest.xml中申請震動權限
* <uses-permission android:name="android.permission.VIBRATE" />
* 補充:測試震動的時候,手機的模式一定要調成鈴聲+震動模式,否則你是感受不到震動的
*/
private void showNotifyWithVibrate() {
//震動也有兩種設置方法,與設置鈴聲一樣,在此不再贅述
long[] vibrate = new long[]{0, 500, 1000, 1500};
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("我是伴有震動效果的通知")
.setContentText("顫抖吧,凡人~")
//使用系統(tǒng)默認的震動參數(shù),會與自定義的沖突
//.setDefaults(Notification.DEFAULT_VIBRATE)
//自定義震動效果
.setVibrate(vibrate);
//另一種設置震動的方法
//Notification notify = builder.build();
//調用系統(tǒng)默認震動
//notify.defaults = Notification.DEFAULT_VIBRATE;
//調用自己設置的震動
//notify.vibrate = vibrate;
//mManager.notify(3,notify);
mManager.notify(3, builder.build());
}
/**
* 顯示帶有呼吸燈效果的通知,但是不知道為什么,自己這里測試沒成功
*/
private void showNotifyWithLights() {
final NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("我是帶有呼吸燈效果的通知")
.setContentText("一閃一閃亮晶晶~")
//ledARGB 表示燈光顏色瞒津、 ledOnMS 亮持續(xù)時間括尸、ledOffMS 暗的時間
.setLights(0xFF0000, 3000, 3000);
Notification notify = builder.build();
//只有在設置了標志符Flags為Notification.FLAG_SHOW_LIGHTS的時候,才支持呼吸燈提醒屁柏。
notify.flags = Notification.FLAG_SHOW_LIGHTS;
//設置lights參數(shù)的另一種方式
//notify.ledARGB = 0xFF0000;
//notify.ledOnMS = 500;
//notify.ledOffMS = 5000;
//使用handler延遲發(fā)送通知,因為連接usb時,呼吸燈一直會亮著
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
mManager.notify(4, builder.build());
}
}, 10000);
}
/**
* 顯示帶有默認鈴聲淌喻、震動雀摘、呼吸燈效果的通知
* 如需實現(xiàn)自定義效果,請參考前面三個例子
*/
private void showNotifyWithMixed() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("我是有鈴聲+震動+呼吸燈效果的通知")
.setContentText("我是最棒的~")
//等價于setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE);
.setDefaults(Notification.DEFAULT_ALL);
mManager.notify(5, builder.build());
}
/**
* 通知無限循環(huán),直到用戶取消或者打開通知欄(其實觸摸就可以了),效果與FLAG_ONLY_ALERT_ONCE相反
* 注:這里沒有給Notification設置PendingIntent,也就是說該通知無法響應,所以只能手動取消
*/
private void showInsistentNotify() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("我是一個死循環(huán),除非你取消或者響應")
.setContentText("啦啦啦~")
.setDefaults(Notification.DEFAULT_ALL);
Notification notify = builder.build();
notify.flags |= Notification.FLAG_INSISTENT;
mManager.notify(6, notify);
}
/**
* 通知只執(zhí)行一次,與默認的效果一樣
*/
private void showAlertOnceNotify() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("仔細看,我就執(zhí)行一遍")
.setContentText("好了,已經一遍了~")
.setDefaults(Notification.DEFAULT_ALL);
Notification notify = builder.build();
notify.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
mManager.notify(7, notify);
}
/**
* 清除所有通知
*/
private void clearNotify() {
mManager.cancelAll();
}
2阵赠、PendingIntent
PendingIntent是一種延遲的肌稻,即將發(fā)生的Intent灯萍,用于在某個特定時刻執(zhí)行特定的Action每聪。而Intent是立刻發(fā)生。
PendingIntent 是 Android 系統(tǒng)管理并持有绑洛,即便創(chuàng)建該PendingIntent對象的進程被殺死了童本,這個PendingItent對象在其他進程中還是可用的。PendingIntent通過send
和cancel
方法來發(fā)送和取消特定的Intent绑蔫。
PendingIntent 主要可以通過以下三種方式獲缺枚睢:
//獲取一個用于啟動Activity的PendingIntent 對象
public static PendingIntent getActivity(Context context, int requestCode, Intent intent, int flags);
//獲取一個用于啟動Service的PendingIntent對象
public static PendingIntent getService(Context context, int requestCode, Intent intent, int flags);
//獲取一個用于向BroadcastReceiver廣播的PendingIntent 對象
public static PendingIntent getBroadcast(Context context, int requestCode, Intent intent, int flags)
其中requestCode多數(shù)情況下設為0即可嫁盲,requestCode會影響flags的效果。
Intent的匹配規(guī)則:
如果兩個intent的ComponentName和intent-filter相同缸托,那么這兩個intent相同瘾蛋。Extras不參與匹配過程瘦黑。
PendingIntent的匹配規(guī)則:
如果兩個PendingIntent,它們內部的Intent相同且requestCode也相同,那這兩個PendingIntent就是相同的咬扇。
PendingIntent 具有以下幾種 flag:
-
FLAG_ONE_SHOT
當前的PendingIntent只能被使用一次,然后就會被自動cancel经窖,如果后續(xù)還有相同的PendingIntent,它們的send方法會調用失敗冰悠。對于通知欄來說配乱,同類的通知只能使用一次,后續(xù)的通知將無法打開搬泥。 -
FLAG_CANCEL_CURRENT
當前的PendingIntent如果存在(匹配的PendingIntent)忿檩,那么它們都會被cancel,然后系統(tǒng)創(chuàng)建一個新的PendingIntent沙咏。對于通知欄來說班套,那些被cancel的消息單擊后將無法打開。 -
FLAG_UPDATE_CURRENT
當前PendingIntent如果已經存在(匹配的PendingIntent)窖壕,那么它們都會被更新瞻讽。即intent中的extras會被替換成最新的熏挎。
在notify(id,notification)
中,如果id是相同的常量烦磁,那么多次調用notify只能彈出一個通知哼勇,后續(xù)的通知會把前面的通知完全替代积担。而如果每次id都不同,那么會彈出多個通知先誉。
如果id每次都不同且PendingIntent不匹配,那么flags不會對通知之間造成干擾诈闺。
如果id不同且PendingIntent匹配:
- 如果采用了FLAG_ONE_SHOT標記位雅镊,那么后續(xù)通知中的PendingIntent會和第一條通知完全一致杨帽,包括extras,單擊任何一條通知后晃危,剩下的通知均無法再打開僚饭,當所有的通知被清除后胧砰,會再次重復這一過程。
- 如果采用FLAG_CANCEL_CURRENT偿乖,那么只有最新的通知可以打開贪薪。
- 如果采用FLAG_UPDATE_CURRENT眠副,那么之前彈出的通知中的PendingIntent會被更新,與最新一條的通知完全一致霍弹,包括extras典格,并且這些通知都可以打開台丛。
3、RemoteViews在通知欄上的應用
我們用到自定義通知私恬,首先要提供一個布局文件本鸣,然后通過RemoteViews來加載硅蹦。更新view時,通過RemoteViews提供的一系列方法涮瞻。如果給一個控件加點擊事件署咽,要使用PendingIntent生音。
//獲取PendingIntent
Intent intent = new Intent(this, SecondActivity.class);
PendingIntent mainPendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
//獲取NotificationManager實例
NotificationManager notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//創(chuàng)建RemoteViews
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_notification);
remoteViews.setTextViewText(R.id.tv_content, "這是新的文本");//更新TextView
remoteViews.setImageViewResource(R.id.iv_content, R.mipmap.tencent);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
SecondActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.btn1, pendingIntent);//設置點擊事件
//實例化NotificationCompat.Builder并設置相關屬性
Notification.Builder builder = new Notification.Builder(this)
//設置小圖標缀遍,部分rom可能不生效
.setSmallIcon(R.mipmap.tencent)
//點擊通知后自動清除
.setAutoCancel(true)
//設置通知標題
.setContentTitle("我是帶Action的Notification")
//設置通知內容
.setContentText("點我會打開MainActivity")
.setContentIntent(mainPendingIntent);
Notification notification = builder.build();
notification.contentView = remoteViews;
//設置通知時間域醇,默認為系統(tǒng)發(fā)出通知的時間,通常不用設置
//.setWhen(System.currentTimeMillis());
notifyManager.notify(1, builder.build());
4锅铅、RemoteViews在桌面小部件上的應用
AppWidgetProvider是實現(xiàn)桌面小部件的類狠角,本質是一個BroadcastReceiver
- 定義小部件界面
在res/layout下新建一個xml文件
<?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="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/icon1" />
</LinearLayout>
- 定義小部件配置信息
在res/xml下新建一個xml文件
<?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="86400000" >//自動更新的周期蚪腋,單位為ms
</appwidget-provider>
- 定義小部件實現(xiàn)類屉凯,繼承AppWidgetProvider
public class MyAppWidgetProvider extends AppWidgetProvider {
public static final String TAG = "MyAppWidgetProvider";
public static final String CLICK_ACTION = "com.ryg.chapter_5.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.icon1);
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(30);
}
}
}).start();
}
}
/**
* 每次窗口小部件被點擊更新都調用一次該方法
*/
@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];
onWidgetUpdate(context, appWidgetManager, appWidgetId);
}
}
/**
* 窗口小部件更新
*
* @param context
* @param appWidgeManger
* @param appWidgetId
*/
private void onWidgetUpdate(Context context,
AppWidgetManager appWidgeManger, int appWidgetId) {
Log.i(TAG, "appWidgetId = " + appWidgetId);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget);
// "窗口小部件"點擊事件發(fā)送的Intent廣播
Intent intentClick = new Intent();
intentClick.setAction(CLICK_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
intentClick, 0);
remoteViews.setOnClickPendingIntent(R.id.imageView1, pendingIntent);
appWidgeManger.updateAppWidget(appWidgetId, remoteViews);
}
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;
}
}
- 在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="com.ryg.chapter_5.action.CLICK" />
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
</receiver>
第一個action用于識別小部件的單擊铝噩,第二個action作為小部件的標識必須存在骏庸。
AppWidgetProvider除了onUpdate方法年叮,還有一系列方法只损。這些方法會自動被onReceive方法調用。當廣播到來以后啸蜜,AppWidgetProvider會自動根據(jù)廣播的action通過onReceive方法分發(fā)廣播辈挂。
- onEnable:該小部件第一次添加到桌面時調用终蒂,添加多次只在第一次調用
- onUpdate:小部件被添加或者每次小部件更新時調用,更新時機由updatePeriodMillis指定噪叙,每個周期小部件都會自動更新一次霉翔。
- onDeleted:每刪除一次桌面小部件都會調用一次
- onDisabled:最后一個該類型的桌面小部件被刪除時調用
- onReceive:內置方法债朵,用于分發(fā)具體事件給以上方法
上面的例子實現(xiàn)了一個簡單地桌面小部件,在小部件上顯示一張圖片臭杰,點擊后會旋轉一周谚中。
二、RemoteViews的內部機制
1磁奖、相關方法
構造方法:public RemoteViews(String packageName,int layoutId)
第一個參數(shù)是當前應用的包名点寥,第二個參數(shù)是待加載的布局文件。
RemoteViews并不支持所有的view類型
RemoteViews并不支持所有的view類型,支持類型如下:
Layout:FrameLayout戚长、LinearLayout怠苔、RelativeLayout柑司、GridLayout
View:AnalogClock、Button蟆湖、Chronometer玻粪、ImageButton劲室、ImageView、ProgressBar充蓝、TextView喉磁、ViewFlipper,ListView,GridView线定、StackView、AdapterViewFlipper纱皆、ViewStub。
RemoteViews不支持以上view的子類
訪問RemoteViews的view元素搀缠,必須通過一系列set方法完成:
setTextViewText(int viewId,CharSequence text)
setTextViewTextSize(int viewId,int units,float size)
setTextColor(int viewId,int color)
setImageViewResource(int viewId,int srcId)
-
setInt(int viewId,String methodName,int value)
反射調用View對象的參數(shù)類型為Int的方法 比如上述的setImageViewResource的方法內部就是這個方法實現(xiàn) 因為srtId為int型參數(shù) -
setLong setBoolean
類似于setInt -
setOnClickPendingIntent(int viewId,PendingIntent pendingIntent)
添加點擊事件的方法
大部分set方法是通過反射來完成的艺普。
2鉴竭、RemoteViews內部機制
通知欄和小組件分別由NotificationManager(NM)和AppWidgetManager(AWM)管理搏存,而NM和AWM通過Binder分別和SystemService進程中的NotificationManagerService以及AppWidgetService中加載的,而它們運行在系統(tǒng)的SystemService中缩焦,這就和我們進程構成了跨進程通訊袁滥。
首先RemoteViews會通過Binder傳遞到SystemService進程灾螃,因為RemoteViews實現(xiàn)了Parcelable接口睦焕,因此它可以跨進程傳輸,系統(tǒng)會根據(jù)RemoteViews的包名等信息拿到該應用的資源猾普;然后通過LayoutInflater去加載RemoteViews中的布局文件本谜。接著系統(tǒng)會對View進行一系列界面更新任務乌助,這些任務就是之前我們通過set來提交的。set方法對View的更新并不會立即執(zhí)行掖肋,會記錄下來赏参,等到RemoteViews被加載以后才會執(zhí)行。
為了提高效率腰涧,系統(tǒng)沒有直接通過Binder去支持所有的View和View操作衡载。而是提供一個Action概念费彼,Action同樣實現(xiàn)Parcelable接口敌买。系統(tǒng)首先將View操作封裝到Action對象并將這些對象跨進程傳輸?shù)絊ystemService進程阶界,接著SystemService進程執(zhí)行Action對象的具體操作膘融。遠程進程通過RemoteViews的apply方法來進行View的更新操作祭玉,RemoteViews的apply方法會去遍歷所有的Action對象并調用他們的apply方法脱货。這樣避免了定義大量的Binder接口,也避免了大量IPC操作臼疫。
apply和reApply的區(qū)別在于:apply會加載布局并更新界面烫堤,而reApply則只會更新界面凤价。RemoteViews在初始化界面時會調用apply方法利诺,后續(xù)更新界面調用reApply方法。
關于單擊事件立倍,RemoteViews中只支持發(fā)起PendingIntent帐萎,不支持onClickListener那種模式。setOnClickPendingIntent用于給普通的View設置單擊事件赁项,不能給集合(ListView/StackView)中的View設置單擊事件(開銷大悠菜,系統(tǒng)禁止了這種方式)败富。如果要給ListView/StackView中的item設置單擊事件兽叮,必須將setPendingIntentTemplate和setOnClickFillInIntent組合使用才可以。
三账阻、RemoteViews的意義
當一個應用需要更新另一個應用的某個界面泽本,我們可以選擇用AIDL來實現(xiàn)规丽,但如果更新比較頻繁,效率會有問題冰抢,同時AIDL接口就可能變得很復雜晒屎。如果采用RemoteViews就沒有這個問題缓升,但RemoteViews僅支持一些常用的View港谊,如果界面的View都是RemoteViews所支持的,那么就可以考慮采用RemoteViews燥狰。