使用RemoteViews美化通知栏和生成桌面小部件笔记

1,475 阅读3分钟

自定义通知栏格式

  1. 完成notification的初始化。
  2. 如果需要点击进入下一个界面则对pendingintent进行初始化。
  3. 设置RemoteViews
RomoteViews remoteviews = new RemoteViews(getPackagename(),R.layout.layout_notification);
//参数分别为包名和通知栏布局样式xml的文件名
remoteviews.setTextViewText(R.id.text,"文字内容");
//参数分别为textview的id和内容
remoteviews.setImageViewResouce(R.id.pic,R.drawable.icon1);
remoteviews.setOnClickPendingIntent(R.id.open_activity,pendingIntent);
//参数为布局(viewgroup)的id,第二为设置将要打开的活动的pendingIntent
//最后设置view即可
notification。contentView=remoteviews;

需要设置不同的样式在布局文件中自行设置即可。 如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/open_activity"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/pic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
    <TextView
        android:id="@+id/text1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="默认值"/>

</LinearLayout>

需要注意的是在SDK版本>=26后,通知栏弹出需要加上信道的处理。


生成桌面小部件

小部件的实现通过使用android提供的AppWidgetProvider类来实现的,其本质是一个广播。要定义一个桌面小部件,首先定义它的界面。

  1. 在res/layout/下新建一个XML文件。命名为widget.xml(名字和内容可以自定义)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/touch"
    android:layout_height="match_parent"
    android:layout_width="match_parent">

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/label_1"/>
</LinearLayout>
  1. 定义小部件的配置信息。在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>

initialLayout是指小工具使用的初始化布局。还有小部件的最小尺寸以及自动更新时间。

  1. 小部件的实现类,新建一个类MyAppWidgetProvider继承AppWidgetProvider。并重写onUpdate(小部件启动和自动更新时调用)和onRecieve(小部件启动和自动更新时调用)
package com.example.administrator.wifitest;

import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.os.SystemClock;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.Toast;

public class MyAppWidgetProvider extends AppWidgetProvider {
    public static final String TAG = "MyAppWidgetProvider";
    public static final String CLICK_ACTION = "com.example.administrator.MyAppWidgetProvider.action.CLICK";
    public MyAppWidgetProvider(){
        super();
    }

    @Override
    public void onReceive(final Context context, Intent intent) {
        super.onReceive(context, intent);
        Log.d(TAG, "onReceive: action = "+intent.getAction());
        //判断是不是自己的action,好进行下一步操作
        if(intent.getAction().equals(CLICK_ACTION)){
            Toast.makeText(context,"Clicked it",Toast.LENGTH_SHORT).show();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Bitmap srcbBitmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.label_1);
                    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.imageView1,
                                rotateBitmap(context,srcbBitmap,degree));
                        Intent intentClick = new Intent();
                        intentClick.setAction(CLICK_ACTION);
                        intentClick.setComponent(new ComponentName(context.getPackageName(),
                                "com.example.administrator.wifitest.MyAppWidgetProvider"));
                                //添加这个的原因是因为sdk>=26后要求给出准确的广播位置。
                        PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0,
                                intentClick,0);
                        remoteViews.setOnClickPendingIntent(R.id.touch,pendingIntent);
                        appWidgetManager.updateAppWidget(new ComponentName(context,MyAppWidgetProvider.class),
                                remoteViews);
                        SystemClock.sleep(30);

                    }
                    Intent intent2= new Intent(context,Main2Activity.class);
                    context.startActivity(intent2);
                }
            }).start();
        }
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    //添加时会调用
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        Log.d(TAG, "onUpdate: ");
        final int counter = appWidgetIds.length;
        Log.d(TAG, "counter = "+counter);
        for(int i=0;i<counter;i++){
            int appWidgetId = appWidgetIds[i];
            onWidgetUpdate(context,appWidgetManager,appWidgetId);
        }

    }

    private void onWidgetUpdate(Context context,AppWidgetManager appWidgetManager,int appWidgetId){
        Log.d(TAG, "onWidgetUpdate: ="+appWidgetId);
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget);
        //将布局设置在RemoteViews中
        Intent intentClick = new Intent(CLICK_ACTION);
        intentClick.setComponent(new ComponentName(context.getPackageName(),
                "com.example.administrator.wifitest.MyAppWidgetProvider"));
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0,
                intentClick,0);
        remoteViews.setOnClickPendingIntent(R.id.touch,pendingIntent);
        //设置点击后发送广播,之后实现类的onReceieve就可以收到
        appWidgetManager.updateAppWidget(appWidgetId,remoteViews);
    }

    private Bitmap rotateBitmap(Context context, Bitmap srcbBitmap, float degree){
        Matrix matrix =new Matrix();
        matrix.reset();
        matrix.setRotate(degree);
        Bitmap tmpBitmap = Bitmap.createBitmap(srcbBitmap,0,0,srcbBitmap.getWidth(),
                srcbBitmap.getHeight(),matrix,true);
        return tmpBitmap;
    }
}
  1. 最后要在AndroidManifest中声明小部件(因为它本质是一个广播组件,所以要注册)
<receiver android:name=".MyAppWidgetProvider">
            <meta-data android:name="android.appwidget.provider"
                        android:resource="@xml/appwidget_provider_info">
            </meta-data>
            <intent-filter>
                <action android:name="com.example.administrator.MyAppWidgetProvider.action.CLICK"/>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
            </intent-filter>
        </receiver>

其中android.appwidget.action.APPWIDGET_UPDATE是不可缺少的。不加这个action就无法在小部件列表中找到小部件。com.example.administrator.MyAppWidgetProvider.action.CLICK是自己定义的action(内容可以更改,其实在这里的作用是声明这个action)。要和实现类对应上。