RemoteView在Android中的使用场景有两种:通知栏和桌面小部件。
RemoteViews的应用
RemoteViews在实际开发中,主要用在通知栏和桌面小部件的开发过程中。主要是通过NotificationManager的notify方法实现的。桌面小部件则是通过AppWidgetProvider来实现的。AppWidgetProvider本质上是一个广播。
RemoteViews在桌面小部件上的应用
步骤:
定义小部件界面
<?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>
定义小部件配置信息
<?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" >
</appwidget-provider>
定义小部件的实现类
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);
}
}
/**
* 窗口小部件更新
*/
private void onWidgetUpdate(Context context, AppWidgetManager appWidgeManger, 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);
appWidgeManger.updateAppWidget(appWidgetId, remoteViews);
}
private Bitmap rotateBitmap(Context context, Bitmap srcbBitmap, float degree) {
Matrix matrix = new Matrix();
matrix.reset();
matrix.setRotate(degree);
return Bitmap.createBitmap(srcbBitmap, 0, 0, srcbBitmap.getWidth(), srcbBitmap.getHeight(), matrix, true);
}
}
在AndroidManifext.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>
PendingIntent概述
RemoteViews内部机制:
RemoteViews常用构造方法:public RemoteViews(String packageName,int layoutIedd),它接收两个参数,第第一个表示当前应用的包名,第一个表示待加载的布局文件。
RemoteViews只支持部分布局和View组件,下面列举的组件的子类是不支持的 :
布局:FrameLayout、LinearLayout、RelativeLayout、GridLayout
组件:Button、ImageButton、ImageView、TextView、ListView、GridView、ViewStub等
RemoteViews没有提供findViewById方法,因此无法直接访问里面的View元素。必须通过RemoteViews所提供的一些列set方法来完成。
通知栏和桌面小部件分由:NotificationManager和AppWidgetManager管理。NotificationManager和AppWidgetManager通过Binder分别和SystemServer进程中的NotificationManagerService以及,AppWidgetService进行通信。由此可见,通知栏和桌面小部件中的布局文件实际上是在NotificationManagerService以及AppWidgetService中被加载的,而它们运行在系统的SystemServer中,这就和我们的进程构成了跨进程构成了跨进程通信的场景。
RemoteViews实现了Parcelable接口,通过Binder传递到SystemServer进程,系统根据RemoteViews中使用的包名等信息去得到该应用的资源。然后加载布局文件。
系统会对View执行一些列界面更行任务。这些任务是通过之前的set方法提交的。RemoteViews会记录这些更新操作,RemoteViews被加载后,这些更新操作才会被执行。
系统将View操作封装到Action对象并将这些对象跨进程传输到远程进程,接着在远程进程中执行Action对象中的具体操作。
我们在应用中每调用一次set方法,RemoteViews中就会添加一个对应的Action对象,当我们提交更新时,遍历调用他们它们的apply方法来进行View的更新操作。
apply和reApply的区别在于:apply会加载布局并更新界面。通知栏和桌面小插件在初始化界面时会调用apply方法,而在后续更新界面时调用reapply方法。
setOnClickPendingIntent、setPendingIntentTemplate、setOnClickFillInIntent区别和联系:
RemoteViews意义
实现了跨进程UI更新。