什么是RemoteViews?使用场景有哪些

emoteViews,顾名思义,就是远程的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,

        newIntent(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,

        newIntent(MainActivity.this, TargetActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);

Notification.Builder nb =newNotification.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,

        newIntent(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 =newNotification.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;

    privateSimpleDateFormat formatter =newSimpleDateFormat("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 =newComponentName(getApplicationContext(), WidgetProvider.class);

        manager.updateAppWidget(componentName, remoteView);

    }

    @Override

    publicvoid 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(newIntent(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(newIntent(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代表的是第二步中配置文件的位置。

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


推荐阅读更多精彩内容