RemoteViews的基本概念
什么是RemoteViews呢?RemoteViews是一种远程View,它提供了一组基础的操作用于跨进程更新页面。RemoteViews主要有两个应用场景:自定义通知栏和桌面小部件。
1 自定义通知栏使用
首先我们看系统默认的通知栏的是怎么实现的:
Notification.Builder builder;
Intent intent;
NotificationManager manager;
Notification notification;
builder=new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setTicker("hello world");
builder.setContentText("测试");
builder.setWhen(System.currentTimeMillis());
intent=new Intent(this,DemoActivity_2.class);
PendingIntent pendingIntent=PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent);
manager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notification=builder.build();
manager.notify(sId,notification);
运行效果如下:
点击会打开一个DemoActivity_2的activity。
在正常开发中我们需要对通知栏显示样式进行自定义,这时候就要用到RemoteViews,下面看RemoteViews自定义通知栏:
Notification.Builder builder;
Intent intent;
NotificationManager manager;
Notification notification;
builder=new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setTicker("hello world");
builder.setWhen(System.currentTimeMillis());
intent=new Intent(this,DemoActivity_1.class);
intent.putExtra("sid", "" + sId);
PendingIntent pendingIntent2=PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
System.out.println(pendingIntent2);
RemoteViews remoteViews1=new RemoteViews(getPackageName(),R.layout.layout_notification);
remoteViews1.setTextViewText(R.id.msg,"zhe shi xinxi de "+sId);
remoteViews1.setImageViewResource(R.id.icon,R.mipmap.ic_launcher);
PendingIntent pendingIntent1=PendingIntent.getActivity(this,0,new Intent(this,DemoActivity_2.class),PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews1.setOnClickPendingIntent(R.id.open_activity2,pendingIntent1);
builder.setContent(remoteViews1);
builder.setContentIntent(pendingIntent2);
manager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notification=builder.build();
manager.notify(sId,notification);
layout_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:background="@color/light_green"
android:padding="10dp"
android:gravity="center"
android:orientation="horizontal" >
<ImageView
android:id="@+id/icon"
android:background="#000000"
android:layout_width="40dp"
android:scaleType="centerInside"
android:layout_height="40dp" />
<LinearLayout
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right"
android:orientation="vertical" >
<TextView
android:id="@+id/msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:textColor="@android:color/white"
android:visibility="visible"
android:text="TextView" />
<TextView
android:id="@+id/open_activity2"
android:padding="2dp"
android:background="@drawable/common_bkg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:text="open DemoActivity_2" />
</LinearLayout>
</LinearLayout>
运行效果如下:
RemoteViews使用很简单,只需要提供当前应用的包名和布局文件的资源ID就可以创建一个RemoteViews对象。在更新RemoteViews时,由于无法直接访问到RemoteViews里面的View,因为通知栏试运行在SystemServer进程中,所以如果需要更新RemoteViews必须要使用RemoteViews提供的一系列方法来更新View。具体的方法在下面会讲到。
2 桌面小部件的实现
Android提供AppWidgetProvider用于桌面小部件的实现,其本质就是一个广播。下面是一个具体的实现步骤:
第一、定义小部件页面:
widget.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">
<ImageView
android:layout_width="wrap_content"
android:id="@+id/imageView1"
android:src="@mipmap/ic_launcher"
android:layout_height="wrap_content" />
</LinearLayout>
里面放了一个小图片。
第二、定义小部件配置信息
配置信息放在res/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:minResizeHeight="84dp"
android:minResizeWidth="85dp"
android:updatePeriodMillis="86400000"
>
</appwidget-provider>
配置信息主要作用:1.initialLayout用于绑定对应的需要初始化的布局;2.minHeight minWidth用于定义小部件的最小尺寸;3.updatePeriodMillis定义小部件自动更新的周期;4.minResizeWidth 和 minResizeHeight指定了 widget 的最小绝对尺寸。也就是说,如果 widget 小于该尺寸,便会因为变得模糊、看不清或不可用。 使用这两个属性,可以允许用户重新调整 widget 的大小,使 widget 的大小可以小于 minWidth 和 minHeight。
3 定义小部件的实现类:
public class MyAppWidgetProvider extends AppWidgetProvider {
public static final String TAG="MyAppWidgetProvider";
public static final String CLICK_ACTION="com.my.learn.code.remoteviews.CLOCK";
public MyAppWidgetProvider(){
super();
}
/**
* 这是广播的内置方法,用于犯法具体的事件给其他方法
* @param context
* @param intent
*/
@Override
public void onReceive(final Context context, final Intent intent) {
super.onReceive(context, intent);
Log.v(TAG,"onReceive :action="+intent.getAction());
//这里判断是自己的ACTION,做自己的事情,比如小部件被点击后要做什么,这里是一个动画效果
if (intent.getAction().equals(CLICK_ACTION)){
Toast.makeText(context,"clicked it",Toast.LENGTH_LONG).show();
new Thread(new Runnable() {
@Override
public void run() {
Bitmap srcbBitmap= BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher);
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();
}
}
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;
}
/**
* 桌面小部件更新
* @param context
* @param appWidgetManager
* @param appWidgetId
*/
private void onWIdgetUpdate(Context context,AppWidgetManager appWidgetManager,int appWidgetId){
Log.v(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(appWidgetId,remoteViews);
}
/**
* 每次桌面小部件更新时都调用一次该方法
*zhexie lianpifu doumeiyoud e huo
*
* 小部件的更新时机由updatePeriodMillis来指定,每个周期小部件都会自动更新一次
* @param context
* @param appWidgetManager
* @param appWidgetIds
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
Log.v(TAG, "onUpdate");
final int counter=appWidgetIds.length;
Log.v(TAG, "counter = "+counter);
for (int i =0;i<counter;i++ ){
int appWidgetId=appWidgetIds[i];
onWIdgetUpdate(context,appWidgetManager,appWidgetId);
}
}
/**
* 当该窗口小部件第一次添加到桌面时调用该方法,可添加多次但只在第一次调用
* @param context
*/
@Override
public void onEnabled(Context context) {
super.onEnabled(context);
}
/**
* 没删除一次桌面小部件就调用一次
* @param context
* @param appWidgetIds
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
}
/**
* 当最后一个该类型的桌面小部件被删除时调用该方法,注意是最后一个
* @param context
*/
@Override
public void onDisabled(Context context) {
super.onDisabled(context);
}
}
4 在AndroidManifest.xml中声明小部件
<!--声明小部件-->
<receiver android:name="com.my.learn.code.remoteviews.MyAppWidgetProvider">
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget_provider_info"></meta-data>
<intent-filter
>
<!--用于识别小部件的单击行为-->
<action android:name="com.my.learn.code.remoteviews.CLOCK" />
<!--作为小部件的标识必须存在-->
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
</receiver>
RemoteViews的内部机制
RemoteViews作用是在其他进程中显示并更新View界面,RemoteViews构造方法: public RemoteViews(String packageName, int layoutId),需要两个参数:当前应用包名以及待加载的布局文件。目前RemoteViews并不能支持所有View类型,支持的View类型如下:
Layout
FrameLayout、LinearLayout、RelativeLayout、GridLayout
View
Button、ImageButton、ImageView、TextView、ListView、GridView、AnalogClock、Chronometer、ProgressBar、ViewFlipper、StackView、AdapterViewFlipper、ViewStub。
更新RemoteViews必须使用RemoteViews提供的一系列的set方法:
最后用一张图来说明RemoteViews的内部机制