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. Java 基本数据类型的取值范围

    基本数据类型,字节数,位数,最大值和最小值. 1. 基本类型:short 二进制位数:16 包装类:java.lang.Short 最小值:Short.MIN_VALUE=-32768 (-2的15此 ...

  2. 一:XMind

  3. Java面试官最爱问的volatile关键字

    在Java的面试当中,面试官最爱问的就是volatile关键字相关的问题.经过多次面试之后,你是否思考过,为什么他们那么爱问volatile关键字相关的问题?而对于你,如果作为面试官,是否也会考虑采用 ...

  4. 使用float设置经典的网站前端结构(深入探讨)

    .要是DIV的子元素宽度大于它自己的宽度,不管子元素有没有脱离文档流,子元素会在横向向右溢出. 关于高度:1.要是DIV的高度没有设定,其高度受“没有脱离文档流”的子元素影响.以下是DIV宽度为0的情 ...

  5. 学习笔记45_log4net日志

    1.配置添加一个App.config *对于网站,就使用web.config ***对于App.config和web.config的配置,在表现形式上是不一致的,使用的时候应该在网上查对于的配置设置. ...

  6. 区块链之Hyperledger(超级账本)Fabric v1.0 的环境搭建(超详细教程)

    https://blog.csdn.net/so5418418/article/details/78355868

  7. ASP.NET Core 3.0 gRPC 拦截器

    目录 ASP.NET Core 3.0 使用gRPC ASP.NET Core 3.0 gRPC 双向流 ASP.NET Core 3.0 gRPC 拦截器 一. 前言 前面两篇文章给大家介绍了使用g ...

  8. Apache的虚拟主机功能

    Apache的虚拟主机功能 (Virtual Host) 是可以让一台服务器基于IP.主机名或端口号实现提供多个网站服务的技术. 第一种情况:基于IP地址 这种情况很常见:一台服务器拥有多个IP地址, ...

  9. 一个帮助理清思路的神奇工具--debug

    今天在回顾复习之前的知识的时候,对嵌套循环犯了迷糊,于是我使用了debug这个方法,不仅让我弄清楚嵌套循环输出结果是如何得来,也让我明白了具体流程. 在这里,IDE我使用的是PyCharm,自然用它自 ...

  10. @resource和@autowired的区别是什么-CSDN论坛-CSDN.NET-中国最大的IT技术社区 - Google Chrome

    @Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了.@Resource有两个属性是比较重要的,分 ...