RemoteViews表示是一个可以在其他的进程中显示View结构,由于它在其他进程中显示,因此,我们刷新这个界面需要通过跨进程通信来实现,而RemoteViews 提供了一组基础的操作用于更新界面。
RemoteViews在Android中的使用场景有两种:通知栏和桌面小部件。
1.RemoteViews的应用
RemoteViews在实际开发中,主要用在通知栏和桌面小部件的开发过程中。通知栏就是使用NotificationManager的notify方法来实现在通知栏显示通知信息。使用NotificationManager我们可以使用默认的效果,也可以使用自定义的布局(提供一个自定义布局文件)。桌面小部件可以通过AppWidgetProvider来实现的,AppWidgetProvier本质上是使用广播来实现的。通知栏和桌面小部件的开发过程中都会用到RemoteViews,它们在更新界面的时候,不能像在Activity那样,直接拿View的对象来更新View的信息。这是因为这两个的界面都是运行在系统的SystemServer进程当中,属于跨进程。为了跨进程更新界面,RemoteViews提供了一系列set方法,并且这些方法都在View当中定义过,并且RemoteViews支持的View的类型也是有限制的。
1.1 RemoteViews在通知栏上的应用
下面复习一下NotificationManager的使用:
NotificationManager:通知栏的管理类,负责发通知、清除通知等。NotificationManager是一个系统的Service,获取NotificationManager的对象需要调用Context的getSystemService()方法,如下:
NotificationManager nm = ( Notificationmanager )getSystemService( Context.NOTIFICATION_SERVICE ) ;
通过调用NotificationManager的notify方法,可以让通知显示出来,notify方法有2个参数,第一个参数是Notification的Id 这个ID是唯一的,当你的ID的通知已经存在就会覆盖掉之前的,第二个是Notification的对象。下面来介绍一下Notification.
Notification:是具体的状态栏通知对象,可以设置icon、文字、提示声音、震动等参数。
基本参数为:
- 通知图标 icon
- 标题和内容 title and expanded message
- 点击执行的跳转 PendingIntent对象
可选的设置:
- 状态栏顶部提示消息 A ticker-text message
- 提示音 An alert sound
- 震动 A vibrate setting
- 灯光 A flashing LED setting
1.创建一个Notification:
Notification notification = new Notification();
notification.icon = R.drawable.ic_launcher;
//通知时在状态栏显示的内容
notification.tickerText = "hello_world";
//设置通知的显示时间
notification.when = System.currentTimeMills();
//意指点击这个Notification后,立刻取消自身
notification.flags = Notification.FLAG_AUTO_CANCEL;
//通知的默认参数 DEFAULT_SOUND, DEFAULT_VIBRATE, DEFAULT_LIGHTS.
//如果要全部采用默认值, 用 DEFAULT_ALL.
//此处采用默认声音
notification.defaults = Notification.DEFAULT_SOUND ;
Intengt inetent =new Intent( MainActivity.this , TestActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity( MainActivity.this , 0 ,intent , PendingIntent.FLAG_UPDATE_CURRENT);
notification.setLatestEventInfo( MainActivity.this , "TestTitle","TestMessage", pendingIntent);
PendingIntent.FLAG_UPDATE_CURRENT会更新之前PendingIntent的消息,比如,你推送了消息1,并在其中的Intent中putExtra了一个值“ABC”,在未点击该消息前,继续推送第二条消息,并在其中的Intent中putExtra了一个值“CBA”,好了,这时候,如果你单击消息1或者消息2,你会发现,他俩个的Intent中读取过来的信息都是“CBA”,就是说,第二个替换了第一个的内容
上面的代码,我们就创建了一个通知,之后,我们是用如下代码:
nm.notify( 1 , notification);
就可以弹出一个通知了,这个通知的ID 是1,我们可以通过ID 来找到这个通知,实现更新这个通知的信息,也可以取消这条通知。
2.更新Notification
上面我们创建了一条ID为1 的通知,现在我们需要更新这通知,那么我们就采用如下:
notification.setLatestEventInfo( MainActivity.this , "TestTitle2","TestMessage",pengdingIntent);
nm.notify( 1 , notification );
这样就能更新通知的内容,如果我们把上面的1,换成2,那么就会显示2条通知,因为ID不一样,而我们如果不用之前的notification而是新建了一个Notification的对象,只要ID一样 也是实现更新的效果。
3.删除Notification
删除Notification很简单,只需要调用NotificationManager的cancel 方法,传入ID就可以了
4.Notification其他的设置
(1)声音:声音可以通过使用如下代码采用系统默认的声音:
notification.defaults = Notification.DEFAULT_SOUND;
如果要使用默认的,那么就要使用到notification的sound属性了,
也可以使用自定义铃声,就需要使用自定义的Uri了
比如使用SD卡的铃声如下:
notification.defaults = Uri.parse("file:///sdcard/notification/ringer.mp3");
使用raw目录的铃声如下:
notification.defaults = Uri.parse("android.resource://"+getPackageName()+"/"+R.raw.cat);
使用assets目录下:
notification.defaults = Uri.parse("file:///android_asset/RELATIVEPATH");
比如使用系统的铃声如下:
notification.sound = Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, "6");
如果default和sound同时出现,那么sound设置的会无效。
默认情况下,通知的声音播放一遍就结束,如果需要声音循环播放,就要在flag上添加参数FLAG_INSISTENT 。这样生意你回到用户响应以后才停止,比如下拉状态栏
notification.flags != notification.FLAG_INSISTENT;
(2)震动:震动
如果是使用默认的震动方式,那么也可以使用default
notification.defaults |= Notification.DEFAULT_VIBRATE;
当然也可以自己定义震动形式,这个就需要使用Long数组,
long[] vibrate = {0,100,200,300};
notification.vibrate = vibrate;
在上面的Long型数组中,第一个参数是开始振动前的等待时间,第二个是第一次震动的时间,第三个是第二次震动的时间,以此类推,不过没办法做到重复震动。
同样,如果有default 那么就会采用default。
使用振动器之前我们需要在清单文件里面声明权限:
<uses-permission android:name="android.permission.VIBRATE"></uses-permission>
(4)闪光
同样的闪光也是有默认的,
notification.defaults |= Notification.DEFAULT_LIGHTS;
自定义:
notification.ledARGB = 0xff00ff00;
notification.ledOnMS = 300;
notification.ledOffMS = 1000;
notification.flags |= Notification.FLAG_SHOW_LIGHTS;
其中ledARGB 表示灯光颜色、 ledOnMS 亮持续时间、ledOffMS 暗的时间。
注意:这边的颜色跟设备有关,不是所有的颜色都可以,要看具体设备。
4.使用自定义样式的通知栏
使用自定义的通知,我们首先要提供一个布局文件,然后使用RemoteViews来加载这个布局文件就可以改变通知的样式,代码如下:
Notification notification = new Notification();
notification.icon = R.drawable.ic_launcher;
notification.tickerText = "Test Ticker";
notification.when = System.currentTimeMillis();
notification.flags = Notification.FLAG_AUTO_CANCEL;
Intent intent = new Intent( this , TestActivity.class);
PendingIntent pengdingIntent = PendingIntent.getActivity( this , 0, intent , PendingIntent.FLAG_UPDATE_CURRENT );
RemoteViews remoteViews = new RemoteViews( getPackageName(), R.layout.layout_notification);
remoteViews.setTextViewText( R.id.tvTitle,"testTitle");
remoteView.setImageViewResource( R.id.ivNotifyIcon,R.drawable.icon1);
PendingIntent pd = PendingIntent.getActivity( this , 0 , new Intent( this , TestActivity2.class),PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent( R.id.btnShowNotify, pd);
notification.contentView = remoteVIews;
notification.contentIntent = pengdingIntent;
NotificationManager nm = ( NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE );
manager.notify( 2 , notification);
上面就是使用RemoteViews的代码。RemoteViews提供了多种构造函数,在这边,我们采用的是使用包名和布局ID来创建一个RemoteViews。
从上面的代码,可以看到,我们在访问RemoteViews的方法时候,和正常的访问不一样,我们并不是通过使用对象的引用来更新RemoteViews中的内容,而是RemoteViews提供了一些封装过的方法,我们来更新。而如果我们需要在控件上添加点击事件,可以在setOnClickPendingIntent使用PengdingIntent 实现,而且,RemoteViews也提供了反射机制,使得我们可以通过methodName和一些参数来调用指定ID上的方法(对参数还是有一些限制的)。
5. 其他有用的设置:
flags:
Notification.FLAG_INSISTENT; //让声音、振动无限循环,直到用户响应
Notification.FLAG_AUTO_CANCEL; //通知被点击后,自动消失
Notification.FLAG_NO_CLEAR; //点击'Clear'时,不清楚该通知(QQ的通知无法清除,就是用的这个)
上面就是RemoteView在Notification上的应用了,我们也回顾了Notification,下面开始学习RemoteViews在桌面小部件上的应用
1.2 RemoteViews在桌面小部件上的应用
AppWidgetProvider是Android中提供的用于实现桌面小部件的类,它的本质是一个广播,也就是BrocastReceiver。所以说,AppWidgetProvider可以直接当成一个BrocastReceiver来使用。下面开始学习桌面小部件的开发步骤。
1.2.1 定义小部件界面
首先,我们在res/layout目录下新建一个布局文件layout_custom_appwidget.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/ivWidgetImg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_field"
/>
</LinearLayout>
1.2.2 定义小部件配置信息
在res/xml下新建custom_appwidget_provider_info.xml
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/layout_custom_appwidget"
android:minWidth="@dimen/appwidget_min_width"
android:minHeight="@dimen/appwiget_min_height"
android:updatePeriodMillis="@integer/widget_updatePeriodMillis"
>
</appwidget-provider>
上面几个参数的意义很明确,initialLayout指的是小工具使用的初始化布局文件,minHeight和minWidth 定义小公举的最小尺寸,updatePeriodMills定义小工具的自动更新周期,毫秒为单位,每隔一个周期,小工具的自动更新就会触发。
3.定义小部件的实现类
public class MyAppWidgetProvider extends AppWidgetProvider {
public static final String TAG = "MyAppWidgetProvider";
public static final String CLICK_ACTION = "com.zhenfei.MyAppWidgetProvider.action";
public static final int NOTIFY_ID = 80001;
public MyAppWidgetProvider()
{
super();
}
@SuppressLint("NewApi")
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
Log.e(TAG, "onReceive: action=" + intent.getAction() );
if( CLICK_ACTION.endsWith( intent.getAction()))
{
NotificationManager notificationManager =(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder builder = new Notification.Builder(context);
builder.setTicker( context.getResources().getString(R.string.notify_titker_text));
builder.setWhen( System.currentTimeMillis() + 1000 );
builder.setVibrate( new long [] { 0 , 200 , 400 , 600 , 800 } );
builder.setDefaults( Notification.DEFAULT_SOUND );
builder.setSmallIcon( R.drawable.ic_field );
builder.setAutoCancel(true);
Intent mIntent = new Intent(context ,MainActivity2.class );
mIntent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK);
mIntent.setFlags( Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
Log.e(TAG, "getPackageName:" + context.getPackageName() );
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, mIntent, 0);
builder.setContentIntent(pendingIntent);
RemoteViews remoteViews = new RemoteViews( context.getPackageName(), R.layout.layout_custom_notification);
remoteViews.setTextViewText(R.id.tvTitle , "有新的信息到了");
remoteViews.setTextViewText( R.id.tvContent, "你到家了吗");
builder.setContent(remoteViews);
Notification notification = builder.build();
notificationManager.notify(NOTIFY_ID, notification);
}
}
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
// TODO Auto-generated method stub
super.onDeleted(context, appWidgetIds);
}
@Override
public void onDisabled(Context context) {
// TODO Auto-generated method stub
super.onDisabled(context);
}
@SuppressLint("NewApi")
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
Intent intent = new Intent();
intent.setAction(CLICK_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0 , intent, 0 );
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_custom_appwidget);
remoteViews.setOnClickPendingIntent(R.id.ivWidgetImg, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
}
在AndroidManifest.xml中声明如下:
<receiver
android:name="com.example.notificationdemo.MyAppWidgetProvider"
>
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="com.zhenfei.MyAppWidgetProvider.action"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/custom_appwidget_provider_info"
/>
</receiver>
声明这个部件类接受两个广播,一个是桌面部件更新广播UPDATE,一个是自定义的按钮事件广播。AppWiget主要有:
onEnable,onDisabled,onDeleted 和 onReceive这些方法:
- onEnable : 当该窗口小部件第一次添加到桌面的时候调用这个方法,可以添加多次,但是只在第一次调用。
- onUpdate: 小部件被添加的时候或者每次小部件更新都会调用一次该方法,小部件的更新时机由updatePeriodMillis来指定,每个周期小部件都会自动更新一次。
- onDeleted:每删除一次桌面小部件就会调用一次。
- onDisable:当最后一个桌面小部件被删除的时候调用。
- onReceive:这个是广播的内置方法,用于分发事件。
并且为它指定了meta-data ,这个meta-data 提供了这个部件的布局。
上面的代码实现了一个简单的桌面小部件,在小部件上面会显示一个按钮,点击以后,这个按钮就会发送一个广播,广播被自己接收,然后我们就会弹出一个通知,点击通知会进入指定的Activity。桌面小部件在界面更新上都需要使用RemoteViews 不管是小部件的界面初始化还是界面更新都必须依赖它。