RemoteViews,顾名思义,就是远程的View,也就是可以运行在其他进程中的View。RemoteViews常用在通知和桌面小组件中。

一、RemoteViews应用到通知

  首先来介绍一下系统自带的通知(Notification)的使用。Notification的使用有两种方法,分别是Notification直接创建的方式和使用Notification.Builder创建者模式创建的方式。

  先来看一下使用Notification直接创建的方式的代码:

Notification notification = new Notification();
notification.icon = R.mipmap.ic_launcher; // 小图标
notification.largeIcon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_round); // 大图标
notification.defaults = Notification.DEFAULT_ALL; // 设置默认的提示音、振动方式、灯光等
notification.category = "Category";
notification.when = System.currentTimeMillis(); // 设置通知发送的时间戳
notification.tickerText = "Ticker Text"; // 设置通知首次弹出时,状态栏上显示的文本
notification.flags = Notification.FLAG_AUTO_CANCEL; // 点击通知后通知在通知栏上消失
notification.contentIntent = PendingIntent.getActivity(MainActivity.this, 0x001,
new Intent(MainActivity.this, TargetActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); // 设置通知的点击事件
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(1, notification); // 发送系统通知

  通过上面的代码,就可以简单地发送一条通知到通知栏中。使用这种方式不需要有API版本的限制,但可以进行的操作比较少。

  下面来看一下使用Notification.Builder创建者模式创建通知的代码:

PendingIntent pi = PendingIntent.getActivity(MainActivity.this, 0x001,
new Intent(MainActivity.this, TargetActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
Notification.Builder nb = new Notification.Builder(MainActivity.this)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_round)) // 大图标
.setSmallIcon(R.mipmap.ic_launcher) // 小图标
.setContentText("Content Text") // 内容
.setSubText("Sub Text") // 在通知中,APP名称的副标题
.setContentTitle("Content Title") // 标题
.setTicker("Ticker") // 设置通知首次弹出时,状态栏上显示的文本
.setWhen(System.currentTimeMillis()) // 设置通知发送的时间戳
.setAutoCancel(true) // 点击通知后通知在通知栏上消失
.setDefaults(Notification.DEFAULT_ALL) // 设置默认的提示音、振动方式、灯光等
.setContentIntent(pi); // 设置通知的点击事件
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(1, nb.build()); // build()方法需要的最低API为16

  使用这种方式,需要注意对项目的API版本进行一定的控制,上面这段代码需要的API版本最低是16。

  上面两种方式都是发送系统自带的通知。如果我们需要自定义通知的样式,就需要使用到 RemoteViews 了。RemoteViews给我们提供了一种可以在其他进程中生成View并进行更新的机制,但是它可以控制和操作的View有一定的限制,具体如下:

Layout:
FrameLayout、LinearLayout、RelativeLayout、GridLayout
View:
AnalogClock、Button、Chronometer、ImageButton、ImageView、ProgressBar、TextView、ViewFlipper、ListView、GridView、StackView、AdapterViewFlipper、ViewStub

  使用RemoteViews发送一个自定义系统通知的代码如下:

PendingIntent pi = PendingIntent.getActivity(MainActivity.this, 0x001,
new Intent(MainActivity.this, TargetActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
RemoteViews remoteView = new RemoteViews(getPackageName(), R.layout.remoteview_main);
remoteView.setTextViewText(R.id.remoteview_main_title, "Title");
remoteView.setTextViewText(R.id.remoteview_main_content, "ContentContentContent");
remoteView.setImageViewResource(R.id.remoteview_main_icon, R.mipmap.ic_launcher_round);
remoteView.setOnClickPendingIntent(R.id.remoteview_main_view, pi);
Notification.Builder nb = new Notification.Builder(MainActivity.this)
.setSmallIcon(R.mipmap.ic_launcher) // 小图标
.setCustomContentView(remoteView) // 设置自定义的RemoteView,需要API最低为24
.setWhen(System.currentTimeMillis()) // 设置通知发送的时间戳
.setAutoCancel(true) // 点击通知后通知在通知栏上消失
.setDefaults(Notification.DEFAULT_ALL); // 设置默认的提示音、振动方式、灯光等
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(1, nb.build()); // build()方法需要的最低API为16

  可以看到,我们在自定义的通知布局中设置了TextView和ImageView,并在代码中动态地更新了其显示的内容。

二、RemoteViews应用到桌面小组件

  RemoteViews也可以应用到桌面小组件中。这里我们通过一个例子来了解RemoteViews应用到桌面小组件的步骤,它总共分为五步,分别是:设置桌面小组件的布局、编写桌面小组件的配置文件、编写桌面小组件更新的Service、编写桌面小组件的控制类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"> <TextView
android:id="@+id/widget_main_tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="22.0sp"
android:textStyle="bold" /> </LinearLayout>

  然后,编写桌面小组件的配置文件,具体步骤是:在项目res文件夹下新建一个xml文件夹,在xml文件夹中创建一个XML文件,具体代码如下:

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/widget_main"
android:minHeight="100.0dip"
android:minWidth="150.0dip"
android:updatePeriodMillis="8640000"> </appwidget-provider>

  这个文件中的各个参数的解释如下:

initialLayout:桌面小组件的布局XML文件
minHeight:桌面小组件的最小显示高度
minWidth:桌面小组件的最小显示宽度
updatePeriodMillis:桌面小组件的更新周期。这个周期最短是30分钟

  然后,编写一个Service,在这个Service中动态地获取到当前的时间并更新到桌面小组件中,代码如下:

import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.widget.RemoteViews; import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask; /**
* 定时器Service
*/
public class TimerService extends Service {
private Timer timer;
private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); @Override
public void onCreate() {
super.onCreate();
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
updateViews();
}
}, 0, 1000);
} @Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
} private void updateViews() {
String time = formatter.format(new Date());
RemoteViews remoteView = new RemoteViews(getPackageName(), R.layout.widget_main);
remoteView.setTextViewText(R.id.widget_main_tv_time, time);
AppWidgetManager manager = AppWidgetManager.getInstance(getApplicationContext());
ComponentName componentName = new ComponentName(getApplicationContext(), WidgetProvider.class);
manager.updateAppWidget(componentName, remoteView);
} @Override
public void onDestroy() {
super.onDestroy();
timer = null;
}
}

  然后,编写一个类继承自AppWidgetProvier,用来统一管理项目中的小组件,代码如下:

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent; /**
* AppWidgetProvider的子类,相当于一个广播
*/
public class WidgetProvider extends AppWidgetProvider {
/**
* 当小组件被添加到屏幕上时回调
*/
@Override
public void onEnabled(Context context) {
super.onEnabled(context);
context.startService(new Intent(context, TimerService.class));
} /**
* 当小组件被刷新时回调
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
/**
* 当widget小组件从屏幕移除时回调
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
} /**
* 当最后一个小组件被从屏幕中移除时回调
*/
@Override
public void onDisabled(Context context) {
super.onDisabled(context);
context.stopService(new Intent(context, TimerService.class));
}
}

  最后,在Manifest文件中配置刚刚编写的Service和BroadcastReceiver(AppWidgetProvider相当于一个广播),代码如下:

<service android:name=".TimerService" />
<receiver android:name=".WidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget" />
</receiver>

  这里需要注意,<intent-filter>标签中的action的name和<meta-data>标签中的name的值是固定的,reousrce代表的是第二步中配置文件的位置。

  编写完上述代码之后,运行结果如下图所示:

三、RemoteViews原理

  我们的通知和桌面小组件分别是由NotificationManager和AppWidgetManager管理的,而NotificationManager和AppWidgetManager又通过Binder分别和SystemServer进程中的NotificationManagerServer和AppWidgetService进行通信,这就构成了跨进程通信的场景。

  RemoteViews实现了Parcelable接口,因此它可以在进程间进行传输。

  首先,RemoteViews通过Binder传递到SystemServer进程中,系统会根据RemoteViews提供的包名等信息,去项目包中找到RemoteViews显示的布局等资源,然后通过LayoutInflator去加载RemoteViews中的布局文件,接着,系统会对RemoteViews进行一系列的更新操作,这些操作都是通过RemoteViews对象的set方法进行的,这些更新操作并不是立即执行的,而是在RemoteViews被加载完成之后才执行的,具体流程是:我们调用了set方法后,通过NotificationManager和AppWidgetManager来提交更新任务,然后在SystemServer进程中进行具体的更新操作。

【Android - 进阶】之RemoteViews简介的更多相关文章

  1. 我的Android进阶之旅------>经典的大牛博客推荐(排名不分先后)!!

    本文来自:http://blog.csdn.net/ouyang_peng/article/details/11358405 今天看到一篇文章,收藏了很多大牛的博客,在这里分享一下 谦虚的天下 柳志超 ...

  2. Android进阶之路(1)-详解MVC

    最近因为换工作的原因没有写博客,现在慢慢稳定了,我准备写一些关于Android 进阶的文章,也是为了督促自己学习,大家一起进步! 今天详细的分析一下Android APP架构之一:MVC ### MV ...

  3. Android进阶之路(2)-详解MVP

    ### MVP简介 >MVP 全称:Model-View-Presenter :MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的[地方](https://baike.baidu.co ...

  4. Android 进阶 Android 中的 IOC 框架 【ViewInject】 (下)

    上一篇博客我们已经带大家简单的吹了一下IoC,实现了Activity中View的布局以及控件的注入,如果你不了解,请参考:Android 进阶 教你打造 Android 中的 IOC 框架 [View ...

  5. 【Android进阶系列教程】前言

    起因 因为初学Android的时候还没有写博客的意识,现在Android的门是入了,正在进阶的道路上行走,但是就这一路也走了不少的弯路.我想,总得来说Android入门还是比较容易的,网络资源比较丰富 ...

  6. Android开发面试经——4.常见Android进阶笔试题(更新中...)

      Android开发(29)  版权声明:本文为寻梦-finddreams原创文章,请关注:http://blog.csdn.net/finddreams 关注finddreams博客:http:/ ...

  7. 【转】Android 防破解技术简介

    http://www.cnblogs.com/likeandroid/p/4888808.html Android 防破解技术简介 这几年随着互联网的不断发展,Android App 也越来越多!但是 ...

  8. [置顶] 我的Android进阶之旅------>介绍一款集录制与剪辑为一体的屏幕GIF 动画制作工具 GifCam

    由于上一篇文章:我的Android进阶之旅------>Android之动画之Frame Animation实例 中展示的是Frame动画效果,但是之前我是将图片截取下来,不好说明确切的动画过程 ...

  9. 《Android进阶》之第七篇 NDK的使用

    <Android进阶>之第一篇 在Java中调用C库函数 这一篇列举的方法是在NDK没有出来时候用的方式 在Android发布NDK之后,可以这样使用 一.首先下载android-ndk ...

  10. 我的Android进阶之旅------&gt; Android在TextView中显示图片方法

    面试题:请说出Android SDK支持哪些方式显示富文本信息(不同颜色.大小.并包括图像的文本信息).并简要说明实现方法. 答案:Android SDK支持例如以下显示富文本信息的方式. 1.使用T ...

随机推荐

  1. 数据存储之关系型数据库存储---MySQL存储

    MySQL的存储 利用PyMySQL连接MySQL 连接数据库 import pymysql # 连接MySQL MySQL在本地运行 用户名为root 密码为123456 默认端口3306 db = ...

  2. MySQL的统计信息学习总结

    统计信息概念 MySQL统计信息是指数据库通过采样.统计出来的表.索引的相关信息,例如,表的记录数.聚集索引page个数.字段的Cardinality.....MySQL在生成执行计划时,需要根据索引 ...

  3. 学习笔记64_k邻近算法

    1 .假定已知数据的各个属性值,以及其类型,例如: 电影名称 打斗镜头 接吻镜头 电影类别 m1 3 104 爱情片 m2 2 100 爱情片 m3 1 81 爱情片 m4 2 90 爱情片 w1 1 ...

  4. 差异---虐爆了yxs的 后缀数组裸题 板子题 单调栈的简单应用 字符串的基础理解考察题

    先玩柿子,发现可以拆开,前半部分可以瞬间求出,于是只求后半部分 然后抄板子就好了,完结撒花! 下边是个人口胡,因为已经被虐爆头脑不清醒了 定义:LCP(a,b)为排名为a,b两个后缀的最长公共前缀 证 ...

  5. Maven多模块项目介绍和搭建

    http://www.open-open.com/lib/view/open1418263515855.html

  6. 使用Typescript重构axios(二十七)——添加请求状态码合法性校验

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  7. Python Socket学习之旅(二)

    Socket函数 注解: Socket的close和shutdown--结束数据传输: close-----关闭本进程的socket id,但链接还是开着的,用这个socket id的其它进程还能用这 ...

  8. Uber Go 语言编码规范

    Uber Go 语言编码规范 Uber 是一家美国硅谷的科技公司,也是 Go 语言的早期 adopter.其开源了很多 golang 项目,诸如被 Gopher 圈熟知的 zap.jaeger 等.2 ...

  9. dubbo中出现can not be invoked any more

    具体错误示例如下 从错误看,是客户方发起调用时,dubbo会去检查本地的invoker instance,如果发现invoker已经是destroy status,则直接抛出上面的异常,下面先来说下平 ...

  10. @Transactional 的回滚

    默认情况下,Exception是不会引起回滚操作的,RuntimeException才会引起回滚操作. 当然如果所有的Exception都要回滚的话,直接@Transactional(rollback ...