RemoteViews詳細(xì)解釋
說明
想要完全的理解RetmoteView必須要說明一下Android Widet客冈。
Android widget 也稱為桌面插件霞篡,其是android系統(tǒng)應(yīng)用開發(fā)層面的一部分。Android中的AppWidget與google widget和中移動(dòng)的widget并不是一個(gè)概念挟裂,這里的AppWidget只是把一個(gè)進(jìn)程的控件嵌入到別外一個(gè)進(jìn)程的窗口里的一種方法。
AppWidgetFramework
Android系統(tǒng)增加了AppWidget 框架蛋铆,用以支持widget類型應(yīng)用的開發(fā)触幼。AppWidget 框架主要由兩個(gè)部件來組成:
(1)AppWidgetService是框架的的核心類拆祈,是系統(tǒng) service之一恨闪,它負(fù)責(zé)widgets的管理工作。加載放坏,刪除咙咽,定時(shí)事件等都需要AppWidgetService的處理。開機(jī)自啟動(dòng)的淤年。
AppWidgetService存在的目的主要是解開AppWidgetProvider和AppWidgetHost之間的耦合钧敞。如果 AppWidgetProvider和AppWidgetHost的關(guān)系固定死了,AppWidget就無法在任意進(jìn)程里顯示了麸粮。而有了 AppWidgetService溉苛,AppWidgetProvider根本不需要知道自己的AppWidget在哪里顯示了。
(2)AppWidgetManager 負(fù)責(zé)widget視圖的實(shí)際更新以及相關(guān)管理弄诲。
工作流程
- 編寫一個(gè)widget(先不考慮后臺(tái)服務(wù)以及用戶管理界面等)
實(shí)際是寫一個(gè)事件監(jiān)聽類即一個(gè)BroadcastReceiver子類愚战,當(dāng)然框架已經(jīng)提供了一個(gè)輔助類AppWidgetProvider娇唯,實(shí)現(xiàn)的類只要實(shí)現(xiàn)其方法即可,其中必須實(shí)現(xiàn)的方法是onUpdate 寂玲,其實(shí)就是一個(gè)定時(shí)事件塔插,widget監(jiān)聽此事件
另外就是規(guī)劃好視圖(layout),將此widget打包安裝拓哟。
- 當(dāng)android系統(tǒng)啟動(dòng)時(shí)想许,AppWidgetService 就將負(fù)責(zé)檢查所有的安裝包
將檢查AndroidManifest.xml(不要告訴我不知道,如果不知道可要看看基本開發(fā)知識(shí)了)文件中有<metadata android:name="android.appwidget.provider" android:resource="@xml/appwidget_info" />
信息的程序包記錄下來
- 從用戶菜單將已經(jīng)安裝的widget添加到桌面
也就是將widget在桌面上顯示出來断序,這個(gè)是由AppWidgetService和AppWidgetManager完成的流纹,其中AppWidgetManager 將負(fù)責(zé)將視圖發(fā)送到桌面顯示出來,并將此widget記錄到系統(tǒng)文件中 - AppWidgetService將根據(jù)widget配置中的updatePeriodMillis屬性來定時(shí)發(fā)送ACTION_APPWIDGET_UPDATE事件违诗,此事件將激活widget的事件監(jiān)聽方法onUpdate漱凝,此方法將通過AppWidgetManager完成widget內(nèi)容的更新和其他操作。
AppWidgetHost
AppWidgetHost 是實(shí)際控制widget的地方较雕,大家注意碉哑,widget不是一個(gè)單獨(dú)的用戶界面程序挚币,他必須寄生在某個(gè)程序(activity)中亮蒋,這樣如果程序要支持widget寄生就要實(shí)現(xiàn)AppWidgetHost,桌面程序(Launcher)就實(shí)現(xiàn)了這個(gè)接口妆毕。
AppWidgetHost和AppWidgetHostView是在框架中定義的兩個(gè)基類慎玖。
AppWidgetHostView是真正的View,但它只是一個(gè)容器笛粘,用來容納實(shí)際的AppWidget的View趁怔。這個(gè)AppWidget的View是根據(jù)RemoteViews的描述來創(chuàng)建。
AppWidgetProvider
AppWidgetProvider是AppWidget提供者需要實(shí)現(xiàn)的接口薪前,它實(shí)際上是一個(gè)BroadcastReceiver润努。只不過子類要實(shí)現(xiàn)的不再是onReceive。作為AppWidgetProvider的實(shí)現(xiàn)者示括,一定要實(shí)現(xiàn)onUpdate函數(shù)铺浇,因?yàn)檫@個(gè)函數(shù)決定widget的顯示方式,如果沒有這個(gè)函數(shù)widget根本沒辦法出現(xiàn)
RemoteViews介紹
RemoteViews表示的是一個(gè)view結(jié)構(gòu)垛膝,它可以在其他進(jìn)程中顯示鳍侣。由于它在其他進(jìn)程中顯示,為了能夠更新它的界面吼拥,RemoteViews提供了一組基礎(chǔ)的操作用于跨進(jìn)程更新它的界面倚聚。
RemoteViews主要用于通知欄通知和桌面小部件的開發(fā),通知欄通知是通過NotificationManager
的notify
方法來實(shí)現(xiàn)的凿可;桌面小部件是通過AppWidgetProvider
來實(shí)現(xiàn)的惑折,它本質(zhì)上是一個(gè)廣播(BroadcastReceiver)。這兩者的界面都是運(yùn)行在SystemServer
進(jìn)程中(跨進(jìn)程)
RemoteViews并不是一個(gè)真正的View,它沒有實(shí)現(xiàn)View的接口惨驶,而只是一個(gè)用于描述View的實(shí)體矗积。比如:創(chuàng)建View需要的資源ID和各個(gè)控件的事件響應(yīng)方法。RemoteViews會(huì)通過進(jìn)程間通信機(jī)制傳遞給AppWidgetHost敞咧。
現(xiàn)在我們可以看出棘捣,Android中的AppWidget與google widget和中移動(dòng)的widget并不是一個(gè)概念,這里的AppWidget只是把一個(gè)進(jìn)程的控件嵌入到別外一個(gè)進(jìn)程的窗口里的一種方法休建。View在另 外一個(gè)進(jìn)程里顯示乍恐,但事件的處理方法還是在原來的進(jìn)程里。
RemoteViews應(yīng)用
在通知欄的應(yīng)用
創(chuàng)建一個(gè)通知:
public void showNotification(View view) {
Notification notification = new Notification();
notification.icon = R.mipmap.ic_launcher;
notification.tickerText = "天意博文";
notification.flags = Notification.FLAG_AUTO_CANCEL;
notification.when = System.currentTimeMillis();
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("ceshi",0);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.notification);
remoteViews.setTextViewText(R.id.tv,"天意博文textview");
remoteViews.setImageViewResource(R.id.iv,R.mipmap.ic_launcher);
remoteViews.setTextColor(R.id.tv,getResources().getColor(R.color.colorPrimaryDark));
PendingIntent pendingIntent1 = PendingIntent.getActivity(this, 0, new Intent(this, Main2Activity.class), PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.iv,pendingIntent1);
notification.contentView = remoteViews;
notification.contentIntent = pendingIntent;
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0,notification);
}
對(duì)用的布局notification.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="horizontal">
<TextView
android:id="@+id/tv"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<ImageView
android:id="@+id/iv"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>
顯示如下圖所示的通知欄:
并且點(diǎn)擊圖片的時(shí)候會(huì)跳轉(zhuǎn)到Main2Activity:
給對(duì)應(yīng)的布局View設(shè)置點(diǎn)擊事件:
remoteViews.setOnClickPendingIntent(R.id.iv,pendingIntent1)
單擊通知時(shí)的響應(yīng)事件:
notification.contentIntent = pendingIntent//對(duì)應(yīng)的是第一個(gè)pendingIntent
RemoteViews在桌面小部件的應(yīng)用
新建桌面小部件测砂,在as中創(chuàng)建十分簡(jiǎn)單茵烈,在布局中新建widget,下一步即可:
創(chuàng)建完成之后會(huì)創(chuàng)建如下幾個(gè)文件:
home_widget.xml是小部件的布局文件砌些,home_widget_info.xml是小部件的配置文件呜投,Home_Widget.java是小部件的邏輯控制文件
小部件的本質(zhì)是一個(gè)BroadcastReceiver,所以還要在mainifest.xml中注冊(cè)
home_widget.xml具體實(shí)現(xiàn)存璃,都是自動(dòng)生成仑荐,和普通的布局沒有區(qū)別
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#09C"
android:padding="@dimen/widget_margin">
<TextView
android:id="@+id/appwidget_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_margin="8dp"
android:background="#09C"
android:contentDescription="@string/appwidget_text"
android:text="@string/appwidget_text"
android:textColor="#ffffff"
android:textSize="24sp"
android:textStyle="bold|italic"/>
</RelativeLayout>
home_widget_info.xml具體實(shí)現(xiàn),是小部件的配置文件纵东,指定了布局粘招,大小更新時(shí)間等
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:initialKeyguardLayout="@layout/home__widget"
android:initialLayout="@layout/home__widget"
android:minHeight="40dp"
android:minWidth="40dp"
android:previewImage="@drawable/example_appwidget_preview"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="86400000"
android:widgetCategory="home_screen">
</appwidget-provider>
Androidmainfest.xml更新標(biāo)簽,注意在intent-filter中一定要含有<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
偎球,這是系統(tǒng)的規(guī)范
<receiver android:name="layout.Home_Widget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<!--能夠響應(yīng)自定義的action-->
<action android:name="haotianyi.win"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/home__widget_info"/>
</receiver>
具體實(shí)現(xiàn)邏輯
當(dāng)每一次點(diǎn)擊小部件的時(shí)候洒扎,顯示的textview都會(huì)顯示當(dāng)前的時(shí)間
public class Home_Widget extends AppWidgetProvider {
public static final String ACTION_CLICK = "haotianyi.win";
public static final String TAG = "haotianyi.win";
public static int click_count = 0;
public void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
String widgetText = context.getString(R.string.appwidget_text);
// Construct the RemoteViews object
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.home__widget);
views.setTextViewText(R.id.appwidget_text, widgetText);
Intent intentClick = new Intent();
intentClick.setAction(ACTION_CLICK);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intentClick, 0);
views.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent);
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
Log.e(TAG, "onReceive: onReceive");
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.home__widget);
views.setTextViewText(R.id.appwidget_text, "天意博文" + System.currentTimeMillis());
Intent intentClick = new Intent();
intentClick.setAction(ACTION_CLICK);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intentClick, 0);
views.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
// Instruct the widget manager to update the widget
manager.updateAppWidget(new ComponentName(context, Home_Widget.class), views);
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// There may be multiple widgets active, so update all of them
Log.e(TAG, "onReceive: onUpdate");
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
@Override
public void onEnabled(Context context) {
// Enter relevant functionality for when the first widget is created
Log.e(TAG, "onReceive: onEnabled");
}
@Override
public void onDisabled(Context context) {
// Enter relevant functionality for when the last widget is disabled
}
}
實(shí)現(xiàn)邏輯,首先第一次添加的時(shí)候會(huì)執(zhí)行onReceive方法衰絮,在方法中設(shè)置了點(diǎn)擊監(jiān)聽袍冷,當(dāng)發(fā)生點(diǎn)擊事件的時(shí)候,由于自定義了action猫牡,所以含有特定action的broadcastReceiver會(huì)啟動(dòng)胡诗,在當(dāng)前案例中也就是Home_Widget在一次啟動(dòng),同時(shí)又執(zhí)行了onReceive镊掖,更新視圖乃戈,同時(shí)設(shè)置事件監(jiān)聽。
小部件的生命周期
onEnable
:當(dāng)小部件第一次添加到桌面時(shí)調(diào)用亩进,小部件可以添加多次但是只在第一次添加的時(shí)候調(diào)用症虑;
onUpdate
:小部件被添加時(shí)或者每次小部件更新時(shí)都會(huì)調(diào)用一次該方法,每個(gè)周期小部件都會(huì)自動(dòng)更新一次归薛,不是點(diǎn)擊的時(shí)候更新谍憔,而是到指定配置文件時(shí)間的時(shí)候才更新
onDeleted
:每刪除一次小部件就調(diào)用一次匪蝙;
onDisabled
:當(dāng)最后一個(gè)該類型的小部件被刪除時(shí)調(diào)用該方法;
onReceive
:這是廣播內(nèi)置的方法习贫,用于分發(fā)具體的事件給其他方法逛球,所以該方法一般要調(diào)用super.onReceive(context, intent);
如果自定義了其他action的廣播,就可以在調(diào)用了父類方法之后進(jìn)行判斷苫昌,如上面代碼所示颤绕。
PendingIntent
PendingIntent
表示一種處于Pending狀態(tài)的Intent,pending表示的是即將發(fā)生的意思祟身,它是在將來的某個(gè)不確定的時(shí)刻放生奥务,而Intent是立刻發(fā)生。
PendingIntent支持三種待定意圖:?jiǎn)?dòng)Activity(getActivity)袜硫,啟動(dòng)Service(getService)氯葬,發(fā)送廣播(getBroadcast)。
匹配規(guī)則
如果兩個(gè)Intent的ComponentName和intent-filter都相同婉陷,那么這兩個(gè)Intent就是相同的帚称,Extras不參與Intent的匹配過程。
參數(shù)flags常見的類型有:FLAG_ONE_SHOT
秽澳、FLAG_NO_CREATE
闯睹、FLAG_CANCEL_CURRENT
、FLAG_UPDATE_CURRENT
肝集。
FLAG_ONE_SHOT
:當(dāng)前描述的PendingIntent只能被調(diào)用一次瞻坝,然后它就會(huì)被自動(dòng)cancel蛛壳。如果后續(xù)還有相同的PendingIntent杏瞻,那么它們的send方法就會(huì)調(diào)用失敗。對(duì)于通知欄消息來說衙荐,如果采用這個(gè)flag捞挥,那么同類的通知只能使用一次,后續(xù)的通知單擊后將無法打開忧吟。
FLAG_NO_CREATE
:當(dāng)前描述的PendingIntent不會(huì)主動(dòng)創(chuàng)建砌函,如果當(dāng)前PendingIntent之前不存在,那么getActivity溜族、getService和getBroadcast方法會(huì)直接返回null讹俊,即獲取PendingIntent失敗。這個(gè)標(biāo)志位使用很少煌抒。
FLAG_CANCEL_CURRENT
:當(dāng)前描述的PendingIntent如果已經(jīng)存在仍劈,那么它們都會(huì)被cancel,然后系統(tǒng)會(huì)創(chuàng)建一個(gè)新的PendingIntent寡壮。
對(duì)于通知欄消息來說贩疙,那些被cancel的通知單擊后將無法打開讹弯。
FLAG_UPDATE_CURRENT
:當(dāng)前描述的PendingIntent如果已經(jīng)存在,那么它們都會(huì)被更新这溅,即它們的Intent中的Extras會(huì)被替換成最新的组民。
RemoteViews機(jī)制
RemoteView沒有findViewById方法,因此無法訪問里面的View元素悲靴,而必須通過RemoteViews所提供的一系列set方法來完成臭胜,這是通過反射調(diào)用的
通知欄和小組件分別由NotificationManager(NM)和AppWidgetManager(AWM)管理,而NM和AWM通過Binder分別和SystemService進(jìn)程中的NotificationManagerService以及AppWidgetService中加載的癞尚,而它們運(yùn)行在系統(tǒng)的SystemService中庇楞,這就和我們進(jìn)程構(gòu)成了跨進(jìn)程通訊。
構(gòu)造方法
public RemoteViews(String packageName, int layoutId)
否纬,第一個(gè)參數(shù)是當(dāng)前應(yīng)用的包名吕晌,第二個(gè)參數(shù)是待加載的布局文件。
支持組件
布局:FrameLayout临燃、LinearLayout睛驳、RelativeLayout、GridLayout
組件:Button膜廊、ImageButton乏沸、ImageView、ProgressBar爪瓜、TextView蹬跃、ListView、GridView铆铆、ViewStub
等(例如EditText是不允許在RemoveViews中使用的蝶缀,使用會(huì)拋異常)。
原理
系統(tǒng)將view操作封裝成Action
對(duì)象薄货,Action同樣實(shí)現(xiàn)了Parcelable接口翁都,通過Binder傳遞到SystemServer進(jìn)程。遠(yuǎn)程進(jìn)程通過RemoteViews的apply
方法來進(jìn)行view的更新操作谅猾,RemoteViews的apply方法內(nèi)部則會(huì)去遍歷所有的action對(duì)象并調(diào)用它們的apply方法來進(jìn)行view的更新操作柄慰。
這樣做的好處是不需要定義大量的Binder接口,其次批量執(zhí)行RemoteViews中的更新操作提高了程序性能税娜。
工作流程
首先RemoteViews會(huì)通過Binder傳遞到SystemService進(jìn)程坐搔,因?yàn)镽emoteViews實(shí)現(xiàn)了Parcelable接口,因此它可以跨進(jìn)程傳輸敬矩,系統(tǒng)會(huì)根據(jù)RemoteViews的包名等信息拿到該應(yīng)用的資源概行;然后通過LayoutInflater去加載RemoteViews中的布局文件。接著系統(tǒng)會(huì)對(duì)View進(jìn)行一系列界面更新任務(wù)谤绳,這些任務(wù)就是之前我們通過set來提交的占锯。set方法對(duì)View的更新并不會(huì)立即執(zhí)行袒哥,會(huì)記錄下來,等到RemoteViews被加載以后才會(huì)執(zhí)行消略。
apply和reApply的區(qū)別
apply會(huì)加載布局并更新界面堡称,而reApply則只會(huì)更新界面。通知欄和桌面小部件在界面的初始化中會(huì)調(diào)用apply方法艺演,而在后面的更新界面中會(huì)調(diào)用reapply方法
源碼分析
首先從setTextViewText方法切入:
public void setTextViewText(int viewId, CharSequence text) {
setCharSequence(viewId, "setText", text);
}
繼續(xù)跟進(jìn):
public void setCharSequence(int viewId, String methodName, CharSequence value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
}
沒有對(duì)view直接操作却紧,但是添加了一個(gè)ReflectionAction,繼續(xù)跟進(jìn):
private void addAction(Action a) {
if (hasLandscapeAndPortraitLayouts()) {
throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
" layouts cannot be modified. Instead, fully configure the landscape and" +
" portrait layouts individually before constructing the combined layout.");
}
if (mActions == null) {
mActions = new ArrayList<Action>();
}
mActions.add(a);
// update the memory usage stats
a.updateMemoryUsageEstimate(mMemoryUsageCounter);
}
這里僅僅把每一個(gè)action存進(jìn)list胎撤,似乎線索斷掉了晓殊,這時(shí)候換一個(gè)切入點(diǎn)查看updateAppWidget
方法,或者是notificationManager.notify
因?yàn)楦乱晥D都要調(diào)用者兩個(gè)方法
public void updateAppWidget(int appWidgetId, RemoteViews views) {
if (mService == null) {
return;
}
updateAppWidget(new int[] { appWidgetId }, views);
}
繼續(xù)跟進(jìn):
public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
if (mService == null) {
return;
}
try {
mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
出現(xiàn)方法無法訪問伤提,這時(shí)我們思考巫俺,RemoteViews不是真正的view啊,所以是否可以去AppWidgetHostView
看看肿男,調(diào)轉(zhuǎn)到updateAppWidget方法:
public void updateAppWidget(RemoteViews remoteViews) {
applyRemoteViews(remoteViews);
}
繼續(xù)跟進(jìn)介汹,好多代碼:
![](file:///C:\Users\SIMAXI1\AppData\LocalLow\Baidu\BAIDUP1\Account\COMMON1\CUSTOM1\RECOMM1\0C1CC91.JPG)
protected void applyRemoteViews(RemoteViews remoteViews) {
if (LOGD) Log.d(TAG, "updateAppWidget called mOld=" + mOld);
boolean recycled = false;
View content = null;
Exception exception = null;
//各種判斷,省略
if (remoteViews == null) {
if (mViewMode == VIEW_MODE_DEFAULT) {
// We've already done this -- nothing to do.
return;
}
content = getDefaultView();
mLayoutId = -1;
mViewMode = VIEW_MODE_DEFAULT;
} else {
if (mAsyncExecutor != null) {
inflateAsync(remoteViews);
return;
}
// Prepare a local reference to the remote Context so we're ready to
// inflate any requested LayoutParams.
mRemoteContext = getRemoteContext();
int layoutId = remoteViews.getLayoutId();
// If our stale view has been prepared to match active, and the new
// layout matches, try recycling it
if (content == null && layoutId == mLayoutId) {
try {
remoteViews.reapply(mContext, mView, mOnClickHandler);
content = mView;
recycled = true;
if (LOGD) Log.d(TAG, "was able to recycle existing layout");
} catch (RuntimeException e) {
exception = e;
}
}
// Try normal RemoteView inflation
if (content == null) {
try {
content = remoteViews.apply(mContext, this, mOnClickHandler);
if (LOGD) Log.d(TAG, "had to inflate new layout");
} catch (RuntimeException e) {
exception = e;
}
}
mLayoutId = layoutId;
mViewMode = VIEW_MODE_CONTENT;
}
applyContent(content, recycled, exception);
updateContentDescription(mInfo);
}
這么多行代碼舶沛,好像只有remoteViews.reapply(mContext, mView, mOnClickHandler);
有點(diǎn)意思嘹承,和RemoteViews相關(guān)聯(lián)了,那么跳轉(zhuǎn)到RemoteViews的reapply方法:
public void reapply(Context context, View v, OnClickHandler handler) {
RemoteViews rvToApply = getRemoteViewsToApply(context);
// In the case that a view has this RemoteViews applied in one orientation, is persisted
// across orientation change, and has the RemoteViews re-applied in the new orientation,
// we throw an exception, since the layouts may be completely unrelated.
if (hasLandscapeAndPortraitLayouts()) {
if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
" that does not share the same root layout id.");
}
}
rvToApply.performApply(v, (ViewGroup) v.getParent(), handler);
}
繼續(xù)跟進(jìn):
private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
if (mActions != null) {
handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
final int count = mActions.size();
for (int i = 0; i < count; i++) {
Action a = mActions.get(i);
a.apply(v, parent, handler);
}
}
}
有點(diǎn)意思了如庭,剛才我們說吧視圖轉(zhuǎn)換成action叹卷,現(xiàn)在終于看到了,由于action是抽象類坪它,我們可以看看它子類的實(shí)現(xiàn):
@Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
final View view = root.findViewById(viewId);
if (view == null) return;
Class<?> param = getParameterType();
if (param == null) {
throw new ActionException("bad type: " + this.type);
}
try {
getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value));
} catch (ActionException e) {
throw e;
} catch (Exception ex) {
throw new ActionException(ex);
}
}
終于看到反射調(diào)用改變內(nèi)容的方法了
![](file:///C:\Users\SIMAXI1\AppData\LocalLow\Baidu\BAIDUP1\Account\COMMON1\CUSTOM1\E6371E1\F21E1A1.JPG)
簡(jiǎn)單應(yīng)用
可以參考桌面小部件的原理骤竹,利用RemoteViews來實(shí)現(xiàn)兩個(gè)進(jìn)程之間View的傳遞,
首先第一個(gè)activity2啟動(dòng)activity1哟楷,在activity1中發(fā)送廣播瘤载,返回,在activity2中看到來自activity1中的天意博文
Main2Activity主要負(fù)責(zé)廣播注冊(cè)和啟動(dòng)第一個(gè)activity
public class Main2Activity extends AppCompatActivity {
private LinearLayout mLinearLayout;
private final String TAG = "ty";
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
RemoteViews view = intent.getParcelableExtra("view");
View apply = view.apply(Main2Activity.this, mLinearLayout);
mLinearLayout.addView(apply);
Log.e(TAG, "onReceive:---------- " );
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
mLinearLayout = (LinearLayout)findViewById(R.id.ll);
registerReceiver(mReceiver,new IntentFilter("haotianyi.win"));
}
@Override
protected void onDestroy() {
unregisterReceiver(mReceiver);
super.onDestroy();
}
public void startActivity(View view) {
startActivity(new Intent(this,MainActivity.class));
}
}
兩個(gè)activity跑在不同的進(jìn)程之中:
<activity
android:process=":accept"
android:name=".MainActivity">
</activity>
<activity
android:name=".Main2Activity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
activity1的代碼卖擅,就是發(fā)送廣播的操作:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void sendBroadcast(View view) {
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.emulator);
remoteViews.setTextViewText(R.id.tv,"天意博文");
Intent intent = new Intent("haotianyi.win");
intent.putExtra("view",remoteViews);
sendBroadcast(intent);
}
}
對(duì)應(yīng)的布局很簡(jiǎn)單只有一個(gè)TextView,emulator.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="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"/>
</LinearLayout>
終于搞定了D肌3徒住!?弁簟断楷!
參考
文章整理自Android開發(fā)藝術(shù)探索
http://blog.csdn.net/sadamdiyi/article/details/8245818
https://www.kancloud.cn/kancloud/art-of-android-development-reading-notes/90450
http://www.reibang.com/p/23041852bd85
https://developer.android.google.cn/reference/android/widget/RemoteViews.html