022.RemoteViews的介绍和使用

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 不管是小部件的界面初始化还是界面更新都必须依赖它。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章