Android之RemoteViews篇上————通知栏和桌面小控件

Android之RemoteViews篇上————通知栏和桌面小控件

一.目录

二.RemoteViews的概述

RemoteView从名字看即为远程的View,RemoteViews表示一个View结构,它可以在其他进程中显示,为了能够更新它的界面,RemoteViews提供了一组基础的操作用于跨进程更新它的界面。

RemoteViews在Android的使用场景有两种:通知栏和桌面小控件。通知栏是通过NotificationManager的notify方法实现,它除了默认效果,还可以自定义布局。桌面小部件则是通过AppWidgetProvider来实现,AppWidget本质是一个广播。这两个都无法像在Activity中那样直接更新小部件。因为两者都是运行在系统的SystemServer进程。

三.RemoteViews在通知栏上的应用

1.通知栏的基本用法

创建一个Notification对象,并进行设置

Notification notification = new NotificationCompat.Builder(content) //创建
                .setContentTitle("冰炭不投day") //设置标题
                .setContentText("啦啦啦啦啦") //设置正文
                .setWhen(System.currentTimeMillis()) //指定被创建时间
                .setSmallIcon(R.drawable.ic_launcher_foreground) //设置通知的小图标
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.icon)) //设置大图标
                .build();
NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
manager.notify(1,notification); //启动该通知

左上角为设置的小图标
这里写图片描述

大图标 标题 正文如下:
这里写图片描述

2.添加点击事件
  Intent intent = new Intent(this,NotificationActiviy.class);
        PendingIntent pi = PendingIntent.getActivity(this,0,intent,0);

        Notification notification = new NotificationCompat.Builder(this) //创建
                .setContentTitle("冰炭不投day") //设置标题
                .setContentText("啦啦啦啦啦") //设置正文
                .setWhen(System.currentTimeMillis()) //指定被创建时间
                .setSmallIcon(R.drawable.ic_launcher_foreground) //设置通知的小图标
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.icon)) //设置大图标
                .setContentIntent(pi)
                .setAutoCancel(true) //点击图标后,通知消失
                .build();

        NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        manager.notify(1,notification);
3.通知栏更多的设置
.setSound(Uri.fromFile(new File("/system/media/audio/ringtones/luna.agg"))) //通知时播放音频
.setVibrate(new long[]{0,1000,1000,1000})//通知使手机震动 数组中的参赛分别表示静止和震动的时长  需要添加权限
.setLights(Color.GREEN,1000,1000) //通知使led灯亮 三个参数分别表示led等的颜色,led等亮起来的时长 led灯灭起来的时常
.setDefaults(NotificationCompat.DEFAULT_ALL ) //使用系统默认效果
.setStyle(new NotificationCompat.BigTextStyle().bigText(",,,,,,,,,")) //设置大段文字
.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(getResources(),R.drawable.big_image))) //设置大图片

四.RemoteViews在桌面小控件的应用

1.定义小部件界面

在res/layout下新建一个XML文件,命名为widget
内容如下:

<?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">

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/icon" />
</LinearLayout>

桌面小部件的布局不同于普通的布局,它是基于RemoteViews它不支持所有类型的布局或视图窗口小部件。

RemoteViews对象(以及App Widget)可以支持以下布局类:

FrameLayout
LinearLayout
RelativeLayout
GridLayout
__
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
ViewFlipper
ListView
GridView
StackView
AdapterViewFlipper

2.定义小部件配置信息

在res/xml下新建appwidget_provider_info.xml(名称随意),添加如下内容

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/widget"
    android:minHeight="84dp"
    android:minWidth="84dp"
    android:updatePeriodMillis="86400000">
</appwidget-provider>

以下是appwidget-provider属性摘要:

  • minWidth和minHeight 属性的值指定App Widget 默认使用的最小空间量 。默认主屏幕根据具有已定义高度和宽度的单元格网格在其窗口中定位App Widgets。如果App Widget的最小宽度或高度的值与单元格的尺寸不匹配,则App Widget尺寸将向上舍 入到最接近的单元格大小。
  • 将minResizeWidth和minResizeHeight属性指定应用Widget的绝对最小尺寸。这些值应指定App Widget难以辨认或无法使用的大小。使用这些属性允许用户将窗口小部件的大小调整为可能小于minWidth和minHeight属性定义的默认窗口小部件大小的大小 。在Android 3.1中引入。
  • updatePeriodMillis属性定义了App Widget框架应该AppWidgetProvider通过调用 onUpdate() 回调方法来请求更新的频率。实际更新不能保证与此值准确地按时发生,我们建议尽可能不经常更新 - 可能每小时不超过一次以节省电池。您可能还允许用户调整配置中的频率 - 有些人可能希望股票代码每15分钟更新一次,或者一天只能更新四次。
  • initialLayout属性指向定义App Widget布局的布局资源。
  • configure属性定义在Activity用户添加App Widget时启动,以便他或她配置App Widget属性。这是可选的
  • previewImage属性指定应用小部件配置后的外观预览,用户在选择应用小部件时会看到该小部件。如果未提供,则用户会看到应用程序的启动器图标。该字段对应 于文件中元素的 android:previewImage属性。有关使用的更多讨论,请参阅设置预览图像。在Android 3.0中推出。AndroidManifest.xmlpreviewImage
  • autoAdvanceViewId属性指定应由窗口小部件主机自动提升的应用窗口小部件子视图的视图ID。在Android 3.0中推出
  • resizeMode属性指定可以调整窗口小部件的规则。您可以使用此属性使主屏幕小部件可以水平,垂直或在两个轴上进行调整。用户触摸按住窗口小部件以显示其调整大小手柄,然后拖动水平和/或垂直手柄以更改布局网格上的大小。resizeMode属性的值 包括“horizo​​ntal”,“vertical”和“none”。要将窗口小部件声明为水平和垂直可调整大小,请提供值“horizo​​ntal | vertical”。在Android 3.1中引入。

更多属性可查看官方文档,链接下文章最后。

3.定义小部件的实现类
public class MyWidgetProvider extends AppWidgetProvider {
    public static final String CLICK_ACTION = "com.heshucheng.remoteviews.click_action";
    private static final String TAG = "MyWidgetProvider";

    public MyWidgetProvider() {
        super();
    }

    @Override
    public void onReceive(final Context context, Intent intent) {
        super.onReceive(context, intent);
        Log.d(TAG, "onReceive: " + intent.getAction());

        //这里判断是自己的action,做自己的事情,比如小部件被点击了要干什么

        if (intent.getAction().equals(CLICK_ACTION)) {
            Toast.makeText(context, "click it", Toast.LENGTH_SHORT).show();

            new Thread(new Runnable() {
                @Override
                public void run() {
                    Bitmap srcbBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon);
                    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
                    for (int i = 0; i < 37; i++){
                        float degree = (i*10)%360;
                        RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget);
                        remoteViews.setImageViewBitmap(R.id.image, rotateBitmap(context,srcbBitmap,degree));
                        Intent intentClick = new Intent();
                        intentClick.setAction(CLICK_ACTION);
                        PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0,intentClick,0);
                        remoteViews.setOnClickPendingIntent(R.id.image,pendingIntent);
                        appWidgetManager.updateAppWidget(new ComponentName(context,MyWidgetProvider.class),remoteViews);
                        SystemClock.sleep(30);
                    }
                }
            }).start();
        }
    }

    //每次更新都会调用
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        Log.i(TAG, "onUpdate");
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
        Intent intentClick = new Intent(CLICK_ACTION);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intentClick, 0);
        remoteViews.setOnClickPendingIntent(R.id.image, pendingIntent);
        appWidgetManager.updateAppWidget(new ComponentName(context, MyWidgetProvider.class), remoteViews);
    }
    //动画 旋转一周
    private Bitmap rotateBitmap(Context context,Bitmap srcbitmap,float degree ) {
        Matrix matrix = new Matrix();
        matrix.reset();
        matrix.setRotate(degree);
        Bitmap temBitmap = Bitmap.createBitmap(srcbitmap,0,0,srcbitmap.getWidth(),srcbitmap.getHeight(),matrix,true);
        return temBitmap;
    }
}

AppWidgetProvider类广播接收器作为一个方便的类来处理应用的Widget广播延伸。AppWidgetProvider仅接收与App Widget相关的事件广播,例如更新,删除,启用和禁用App Widget时。当这些广播事件发生时,AppWidgetProvider会收到以下方法调用:

  • onUpdate() 这被调用以按updatePeriodMillis AppWidgetProviderInfo中的属性定义的间隔更新App Widget (请参阅上面的添加AppWidgetProviderInfo数据)。当用户添加App Widget时也会调用此方法,因此它应该执行基本设置,例如为视图定义事件处理程序并Service在必要时启动临时操作 。但是,如果已声明配置Activity,则在用户添加App Widget时不会调用此方法,但会为后续更新调用此方法。配置Activity负责在配置完成时执行第一次更新。(请参阅下面的“ 创建应用程序小组件配置活动”。)
  • onAppWidgetOptionsChanged():首次放置窗口小部件时以及窗口小部件调整大小时调用此方法。你可以使用此回调根据窗口小部件的大小范围显示或隐藏内容。
  • onDeleted(Context, int[]):每次从App Widget主机删除App Widget时都会调用此方法。
  • onEnabled(Context):当第一次创建App Widget的实例时调用此方法。
  • onDisabled(Context):当从App Widget主机删除App Widget的最后一个实例时调用此方法。这是您应该清理完成的任何工作的地方 onEnabled(Context),例如删除临时数据库。
  • onReceive(Context,Intent):在每个广播和每个上述回调方法之前调用此方法。您通常不需要实现此方法,因为默认的AppWidgetProvider实现过滤所有App Widget广播并根据需要调用上述方法。
4.AndroidManifest.xml中声明小部件
  <intent-filter>
                <action android:name="com.heshucheng.remoteviews.click_action" />
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>

上面代码中的两个Action,第一个是用来识别小部件的单击行为,第二个是作为小部件的标识而必须存在的。

这里写图片描述

五. PendingIntent

PendingIntent表示一种处于Pending状态的Intent,pending表示的是即将发生的意思,它是在将来的某个不确定的时刻放生,而Intent是立刻发生。

PendingIntent支持三种待定意图:启动Activity(getActivity),启动Service(getService),发送广播(getBroadcast)。
这里写图片描述
如图中所示,这三个方法的参数都是一样的,主要理解的是第二个参数requstCode和第四个参数flags,code代表的是发送码,多数情况下为0,而且code会影响到flag,flag常见的有几种我们下面会说,其实最主要是理解匹配规则。

PendingIntent的匹配规则为:如果两个PendingIntent他们内部的Intent相同并且requstCode也相同的话,那么PendingIntent就是相同的,code比较好理解,那什么情况下Intent相同呢,Intent的匹配规则是:如果两个Intent的ComponentName的匹配过程,只要Intent之间的ComponentName和intent-filter相同,那么这两个intent就相同,需要注意的是Extras不参与匹配过程,只要intent之间的name和intent-filter相同就行,我们再来说下flags的参数含义

  • FLAG_ONE_SHOT
    当前描述的PendingIntent只能被使用一次,然后他就会被cancel,如果后续还有相同的PendingIntent,那么他的send方法就会失败,对于通知栏的消息来说,如果采用此标记位,那么同类的通知只能使用一次,后续将无法打开

  • FLAG_NO_CREATE
    当前描述的PendingIntent不会主动去创建,如果当前PendingIntent之前不存在,那么getActivity等方法都会直接返回null,即获取PendingIntent失败,这个标记位很少见,他无法单独使用,因此在日常开发当中,并没有太多的意义,这里就不过多的介绍了

  • FLAG_CANCEL_CURRENT
    当前描述的PendingIntent如果已经存在,那么就会被cancel,然后系统创建一个新的PendingIntent,对于通知栏来说,那些被cancel的消息将无法被打开

  • FLAG_UPDATE_CURRENT
    当前描述的PendingIntent如果已经存在的话,那么他们就会被更新,他们的intent中的extras会被替换成新的

从上面的分析看还是不太好理解这四个标记位,下面结合实际的项目来,这里分析两种情况,如下代码,如果notify的第一个参数id是常量,那么多次通知就只能弹出一个通知,后续会把前面的替换掉,如果每次不一样,就会多弹出几个通知

如果notify方法的id是常量,那么不管PendingIntent是否匹配,后面的通知会替换前面的通知,这个很好理解

如果notify方法的id每次不同,那么当PendingIntent不匹配时,这里的匹配是指PendingIntent中Intent相同切requstCode相同,在这种情况下不管采用了何种标记位,这些通知之间不互相干扰。如果PendingIntent处于匹配状态,这个时候要分情况讨论,如果采用FLAG_ONE_SHOT标记位,那么后续通知中,PendingIntent会和第一条通知保持一致,包括其中的Extras,单击任何一条通知,剩余的都无法打开当所有的通知被清除后,会重复这个过程,如果采用FLAG_CANCEL_CURRENT标记位,那么只有最新的通知可以打开,之前弹出的所有通知均无法打开,如果采用FLAG_UPDATE_CURRENT标记位。那么之前弹出的通知PendingIntent会被更新,最终他们和最新的一条通知保持一致,包括其中的Extras,那么这些通知都可以被打开

六.参考资料

《第一行代码》
《android艺术开发探索》
官方文档

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值