• remoteViews简介


    RemoteViews从字面上看是一种远程视图。RemoteViews具有View的结构,既然是远程View,那么它就可以在其他进程中显示。由于它可以跨进程显示,所以为了能够更新他的界面,RemoteViews提供一组基础的操作用于跨进程更新它的UI。

    RemoteViews在Android日常开发中最常见的使用场景有两种:通知栏的通知和桌面小部件。通知栏通知大家应该都不陌生,因为这还经常用到的,notification主要是通过NotificationManager的notify方法来实现,它除了默认的效果外,开发人员还可以根据自己的需求自定义UI布局。桌面小部件是通过AppWidgetProvider来实现的,其实AppWidgetProvider是一个广播。通知栏通知和小部件的开发过程中经常会用到RemoteViews。它们在更新UI的时候无法像Activity和Fragment那样直接更新,前面讲过,因为它是跨进程的view,更确切一点来说的话,它是运行在SystemServer进程中。为了能够跨进程更新界面,RemoteViews提供了一些列可以跨进程更新UI的方法,内部有一些列的set方法,这些方法都是View的子集。

    一、RemoteViews自定义Notification通知栏

     1 public class MainActivity extends Activity implements View.OnClickListener {
     2 
     3     private Button btn_one,btn_two,btn_three,btn_four;
     4     private NotificationManager manager;
     5 
     6     @Override
     7     protected void onCreate(Bundle savedInstanceState) {
     8         super.onCreate(savedInstanceState);
     9         setContentView(R.layout.activity_main);
    10         btn_one = (Button) findViewById(R.id.btn_one);
    11         btn_one.setOnClickListener(this);
    12         btn_two = (Button) findViewById(R.id.btn_two);
    13         btn_two.setOnClickListener(this);
    14         btn_three = (Button) findViewById(R.id.btn_three);
    15         btn_three.setOnClickListener(this);
    16         btn_four = (Button) findViewById(R.id.btn_four);
    17         btn_four.setOnClickListener(this);
    18         manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    19     }
    20 
    21     @Override
    22     public void onClick(View view) {
    23         switch (view.getId()) {
    24             case R.id.btn_one://默认通知
    25                 PendingIntent pendingIntent1=PendingIntent.getActivity(this,0,new Intent(this,OtherActivity.class),PendingIntent.FLAG_UPDATE_CURRENT);
    26                 Notification notification1=new Notification();
    27                 notification1.icon=R.drawable.head;
    28                 notification1.when=System.currentTimeMillis();
    29                 notification1.tickerText="您有新短消息,请注意查收!";
    30                 notification1.contentIntent=pendingIntent1;
    31                 manager.notify(1,notification1);
    32 
    33                 break;
    34             case R.id.btn_two://API11之后使用
    35                 PendingIntent pendingIntent2=PendingIntent.getActivity(this,0,new Intent(this,OtherActivity.class),PendingIntent.FLAG_UPDATE_CURRENT);
    36                 Notification notification2=new Notification.Builder(this)
    37                         .setSmallIcon(R.drawable.head)//即便自定义了图标,但有时候还是显示系统默认的,应该与手机类型有关
    38                         .setContentTitle("好消息")
    39                         .setContentText("API11之后的通知")
    40                         .setTicker("hello world")
    41                         .setWhen(System.currentTimeMillis())
    42                         .setContentIntent(pendingIntent2)
    43                         .setNumber(1)       //在最右侧现实。这个number起到一个序列号的作用,如果多个触发多个通知(同一ID),可以指定显示哪一个。
    44                         .getNotification(); //build()是在API16及之后增加的,在API11中可以使用getNotificatin()来代替
    45                 manager.notify(1,notification2);
    46                 break;
    47             case R.id.btn_three://API16之后使用
    48                 PendingIntent pendingIntent3=PendingIntent.getActivity(this,0,new Intent(this,OtherActivity.class),PendingIntent.FLAG_UPDATE_CURRENT);
    49                 Notification notification3=new Notification.Builder(this)
    50                         .setSmallIcon(R.drawable.head)
    51                         .setContentTitle("好消息")
    52                         .setContentText("API16之后的通知")
    53                         .setTicker("hello world")
    54                         .setWhen(System.currentTimeMillis())
    55                         .setContentIntent(pendingIntent3)
    56                         .setNumber(1)
    57                         .build();
    58                 manager.notify(1,notification3);
    59                 break;
    60             case R.id.btn_four://自定义通知,
    61                 Notification notification = new Notification();
    62                 notification.when = System.currentTimeMillis();
    63                 notification.flags = Notification.FLAG_AUTO_CANCEL;
    64                 notification.tickerText = "hello world";
    65                 notification.icon = R.drawable.head;//这是个坑,如果不设置icon,通知不显示
    66 
    67                 Intent intent = new Intent(MainActivity.this, OtherActivity.class);
    68                 PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    69                 RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.remote_layout);
    70                 remoteViews.setTextViewText(R.id.tv_title, "请假条");
    71                 remoteViews.setTextViewText(R.id.tv_content, "世界这么大,我想去看看");
    72                 remoteViews.setImageViewResource(R.id.iv_head, R.drawable.head);
    73 
    74                 notification.contentIntent = pendingIntent;
    75                 notification.contentView = remoteViews;
    76                 manager.notify(1, notification);
    77                 break;
    78         }
    79     }
    80 }

    通知栏的布局remote_layout

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="horizontal">
    
        <ImageView
            android:id="@+id/iv_head"
            android:layout_width="40dp"
            android:layout_height="40dp" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingLeft="10dp"
            android:orientation="vertical">
    
            <TextView
                android:id="@+id/tv_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="标题" />
    
            <TextView
                android:id="@+id/tv_content"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="3dp"
                android:text="内容。。。。" />
        </LinearLayout>
    </LinearLayout>

    二、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中动态地获取到当前的时间并更新到桌面小组件中,代码如下:

     1 import android.app.Service;
     2 import android.appwidget.AppWidgetManager;
     3 import android.content.ComponentName;
     4 import android.content.Intent;
     5 import android.os.IBinder;
     6 import android.support.annotation.Nullable;
     7 import android.widget.RemoteViews;
     8 
     9 import java.text.SimpleDateFormat;
    10 import java.util.Date;
    11 import java.util.Timer;
    12 import java.util.TimerTask;
    13 
    14 /**
    15  * 定时器Service
    16  */
    17 public class TimerService extends Service {
    18     private Timer timer;
    19     private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    20 
    21     @Override
    22     public void onCreate() {
    23         super.onCreate();
    24         timer = new Timer();
    25         timer.schedule(new TimerTask() {
    26             @Override
    27             public void run() {
    28                 updateViews();
    29             }
    30         }, 0, 1000);
    31     }
    32 
    33     @Nullable
    34     @Override
    35     public IBinder onBind(Intent intent) {
    36         return null;
    37     }
    38 
    39     private void updateViews() {
    40         String time = formatter.format(new Date());
    41         RemoteViews remoteView = new RemoteViews(getPackageName(), R.layout.widget_main);
    42         remoteView.setTextViewText(R.id.widget_main_tv_time, time);
    43         AppWidgetManager manager = AppWidgetManager.getInstance(getApplicationContext());
    44         ComponentName componentName = new ComponentName(getApplicationContext(), WidgetProvider.class);
    45         manager.updateAppWidget(componentName, remoteView);
    46     }
    47 
    48     @Override
    49     public void onDestroy() {
    50         super.onDestroy();
    51         timer = null;
    52     }
    53 }

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

     1 import android.appwidget.AppWidgetManager;
     2 import android.appwidget.AppWidgetProvider;
     3 import android.content.Context;
     4 import android.content.Intent;
     5 
     6 /**
     7  * AppWidgetProvider的子类,相当于一个广播
     8  */
     9 public class WidgetProvider extends AppWidgetProvider {
    10     /**
    11      * 当小组件被添加到屏幕上时回调
    12      */
    13     @Override
    14     public void onEnabled(Context context) {
    15         super.onEnabled(context);
    16         context.startService(new Intent(context, TimerService.class));
    17     }
    18 
    19     /**
    20      * 当小组件被刷新时回调
    21      */
    22     @Override
    23     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    24         super.onUpdate(context, appWidgetManager, appWidgetIds);
    25     }
    26     /**
    27      * 当widget小组件从屏幕移除时回调
    28      */
    29     @Override
    30     public void onDeleted(Context context, int[] appWidgetIds) {
    31         super.onDeleted(context, appWidgetIds);
    32     }
    33 
    34     /**
    35      * 当最后一个小组件被从屏幕中移除时回调
    36      */
    37     @Override
    38     public void onDisabled(Context context) {
    39         super.onDisabled(context);
    40         context.stopService(new Intent(context, TimerService.class));
    41     }
    42 }

    最后,在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进程中进行具体的更新操作。

     

  • 相关阅读:
    java soket 和nio
    面试题中问到 aop di ioc 怎么回答
    细谈hashmap
    java中length和length()还有size()的区别
    MySQL 中实现可重复读(RR)的原理--MVCC
    关于字节流/字符流操作文件的问题
    MySQL 中索引优化(即避免索引失效)
    MySQL 数据库中索引的实现 和 建立索引的原则
    CAS和ABA问题
    Volatile的简单理解
  • 原文地址:https://www.cnblogs.com/ganchuanpu/p/8385336.html
Copyright © 2020-2023  润新知