Notification 通知栏

知识点

  • Notification

  • 设置特殊 Activity PendingIntent

  • 浮动通知

  • 锁定屏幕通知

  • 在锁定屏幕上控制媒体播放

  • PendingIntent

  • TaskStackBuilder

  • RemoteViews

下面的都是废话 不必看了 官网有中文文档 说的很清楚

直接贴代码

版本25 这个版本不需要兼容8.0

默认通知

   /**
     * 默认样式的通知栏
     *
     * @param context
     * @param builder
     * @return
     */
    public NotificationCompat.Builder defaultNotify(Context context, NotificationCompat.Builder builder) {

        builder.setContentTitle("梦里挑灯看剑")
                .setContentText("江山如此多娇,引无数英雄竞折腰")
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.test_baseline_school_black_24))// 右侧显示大图标
                .setSmallIcon(R.drawable.test_baseline_school_black_24)
                .setWhen(System.currentTimeMillis())
                .setShowWhen(true)//显示时间 默认true
                .setDefaults(NotificationCompat.DEFAULT_ALL)//默认提示 铃声 震动
                .setPriority(NotificationCompat.PRIORITY_MAX)//设置优先级 和默认提示 搭配使用 可实现 悬浮提示效果 
                .setContentIntent(getTaskStackPending())
                //.setContentInfo("ContentInfo")
                //.setNumber(5)
                .setOngoing(true)//true不可以滑动删除 false 可以滑动删除
                .setAutoCancel(true)
        ;
        return builder;
    }

进度条通知


    /**
     * 进度条通知栏
     *
     * @param builder
     * @return
     */
    public NotificationCompat.Builder progressNotify(final NotificationCompat.Builder builder) {
        builder.setContentTitle("正在下载:")
                .setAutoCancel(false)
                .setOngoing(true)
                .setShowWhen(false)//显示时间 默认true
                .setDefaults(NotificationCompat.DEFAULT_ALL)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.test_baseline_school_black_24))
                .setOnlyAlertOnce(true)
                .setSmallIcon(R.drawable.test_baseline_school_black_24);

        new Thread() {
            @Override
            public void run() {
                int progress = 0;

                for (int i = 0; i < 10; i++) {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    progress += 10;
                    builder.setProgress(100, progress, false);
                    builder.setContentTitle("正在下载:" + progress + "%");
                    builder.setContentInfo(progress + "%");
                    mNotifyManager.notify(500, builder.build());
                }
                builder.setContentTitle("下载完成:");
                mNotifyManager.notify(500, builder.build());
                //mNotifyManager.cancel(500);

            }
        }.start();
        return builder;
    }

bigTextStyle

public NotificationCompat.Builder bigTextStyleNotify(NotificationCompat.Builder builder) {
        NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle();

        bigTextStyle.bigText("Android4.1+之后出现几种折叠样式,均需要使用setStyle()方法设定 setStyle()传递一个NotificationCompat.Style对象,它是一个抽象类,Android为我们提供了三个实现类,用于显示不同的场景。Android4.1+之后出现几种折叠样式,均需要使用setStyle()方法设定 setStyle()传递一个NotificationCompat.Style对象,它是一个抽象类,Android为我们提供了三个实现类,用于显示不同的场景。Android4.1+之后出现几种折叠样式,均需要使用setStyle()方法设定 setStyle()传递一个NotificationCompat.Style对象,它是一个抽象类,Android为我们提供了三个实现类,用于显示不同的场景。");
        bigTextStyle.setBigContentTitle("折叠式:BigText"); //替换掉了 setContentTitle
        bigTextStyle.setSummaryText("摘要");
        return builder
                .setSmallIcon(R.drawable.test_baseline_school_black_24)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.test_baseline_school_black_24))
                .setContentTitle("折叠式:BigText")
                .setContentText("对折叠样式的说明")
                .setStyle(bigTextStyle)
                .setWhen(System.currentTimeMillis())
                .setDefaults(NotificationCompat.DEFAULT_ALL)
                .setPriority(NotificationCompat.PRIORITY_MAX)
                .setContentIntent(getTaskStackPending())
                // .setOngoing(true)//true不可以滑动删除 false 可以滑动删除  使用这个属性 通知栏默认展开状态 说的不是很明白 可以自己试一下
                .setAutoCancel(true);
    }

inboxStyle

public NotificationCompat.Builder inboxStyleNotify(NotificationCompat.Builder builder) {
        return builder
                .setSmallIcon(R.drawable.test_baseline_school_black_24)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.test_baseline_school_black_24))
                .setContentTitle("折叠式:Inbox")
                .setContentText("对折叠样式的说明")
                .setStyle(new NotificationCompat.InboxStyle()
                        .addLine("第一行数据")
                        .addLine("第二行数据第二行数据第二行数据第二行数据第二行数据第二行数据第二行数据第二行数据第二行数据第二行数据第二行数据第二行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                        .addLine("第一行数据")
                )
                .setWhen(System.currentTimeMillis())
                .setDefaults(NotificationCompat.DEFAULT_ALL)
                .setPriority(NotificationCompat.PRIORITY_MAX)
                .setContentIntent(getTaskStackPending())
                //.setOngoing(true)//true不可以滑动删除 false 可以滑动删除  使用这个属性 通知栏默认展开状态 说的不是很明白 可以自己试一下
                .setAutoCancel(true);
    }

bigPictureStyle

public NotificationCompat.Builder bigPictureStyleNotify(NotificationCompat.Builder builder) {
        return builder
                .setSmallIcon(R.drawable.test_baseline_school_black_24)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.test_baseline_school_black_24))
                .setContentTitle("折叠式:BigPicture")
                .setContentText("对折叠样式的说明")
                .setStyle(new NotificationCompat.BigPictureStyle()
                        .bigPicture(BitmapFactory.decodeResource(getResources(), R.drawable.test_baseline_school_black_24))
                )
                .setWhen(System.currentTimeMillis())
                .setDefaults(NotificationCompat.DEFAULT_ALL)
                .setPriority(NotificationCompat.PRIORITY_MAX)
                .setContentIntent(getTaskStackPending())
                // .setOngoing(true)//true不可以滑动删除 false 可以滑动删除  使用这个属性 通知栏默认展开状态 说的不是很明白 可以自己试一下
                .setAutoCancel(true);
    }

自定义大图 setCustomBigContentView 可理解为可折叠

 public NotificationCompat.Builder customBigNotify(NotificationCompat.Builder builder) {

        RemoteViews views = new RemoteViews(getPackageName(), R.layout.test_custom_remote_view_dj);


        views.setOnClickPendingIntent(R.id.test_iv_large_img, getPendingIntent("onClickLargeImage"));
        views.setOnClickPendingIntent(R.id.test_iv_src_right, getPendingIntent("onClickRightImage"));

        views.setOnClickPendingIntent(R.id.test_iv_src, getPendingIntent("onClickLeftImage"));
        views.setOnClickPendingIntent(R.id.test_tv_title, getPendingIntent("onClickTextTitle"));

        return builder
                .setSmallIcon(R.drawable.test_baseline_school_black_24)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.test_baseline_school_black_24))
                .setContentTitle("自定义")
                .setContentText("RemoteViews")
                .setWhen(System.currentTimeMillis())
                .setDefaults(NotificationCompat.DEFAULT_ALL)
                .setPriority(NotificationCompat.PRIORITY_MAX)
                .setContentIntent(getTaskStackPending())
                .setCustomBigContentView(views)
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
                .setOngoing(true)//true不可以滑动删除 false 可以滑动删除  使用这个属性 通知栏默认展开状态 说的不是很明白 可以自己试一下
                .setAutoCancel(true);
    }

setCustomContentView 不可折叠

public NotificationCompat.Builder customNotify(NotificationCompat.Builder builder) {

        RemoteViews views = new RemoteViews(getPackageName(), R.layout.test_custom_remote_view_dj);

        /**
         * 加入点击事件  setAutoCancel 无效
         */
//        views.setOnClickPendingIntent(R.id.test_iv_large_img,getPendingIntent("onClickLargeImage"));
//        views.setOnClickPendingIntent(R.id.test_iv_src_right,getPendingIntent("onClickRightImage"));
//
//        views.setOnClickPendingIntent(R.id.test_iv_src,getPendingIntent("onClickLeftImage"));
//        views.setOnClickPendingIntent(R.id.test_tv_title,getPendingIntent("onClickTextTitle"));

        return builder
                .setSmallIcon(R.drawable.test_baseline_school_black_24)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.test_baseline_school_black_24))
                .setContentTitle("自定义")
                .setContentText("RemoteViews")
                .setWhen(System.currentTimeMillis())
                .setDefaults(NotificationCompat.DEFAULT_ALL)
                .setPriority(NotificationCompat.PRIORITY_MAX)
                .setContentIntent(getTaskStackPending())
                .setCustomContentView(views)
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)//锁屏通知
                .setOngoing(true)//true不可以滑动删除 false 可以滑动删除  使用这个属性 通知栏默认展开状态 说的不是很明白 可以自己试一下
                .setAutoCancel(true);
    }

调用方式

 mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        mBuilder = new NotificationCompat.Builder(context);


        findViewById(R.id.app_test_btn_send_notify).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                new Thread() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        //showProgressNotify();
                        showNotify();


                        mNotifyManager.notify(id++, defaultNotify(context, mBuilder).build());

                        progressNotify(mBuilder);

                        mNotifyManager.notify(id++, bigTextStyleNotify(mBuilder).build());

                        mNotifyManager.notify(id++, inboxStyleNotify(mBuilder).build());

                        //mNotifyManager.notify(id++, bigPictureStyleNotify(mBuilder).build());

                        //mNotifyManager.notify(id++, customBigNotify(mBuilder).build());

                        mNotifyManager.notify(id++, customNotify(mBuilder).build());


                    }
                }.start();
            }

        });
public PendingIntent getTaskStackPending() {
        //创建 Intent 以启动 Activity。
        Intent resultIntent = new Intent(this, TestReceiveActivity.class);
        //创建堆栈生成器
        TaskStackBuilder builder = TaskStackBuilder.create(this);
        //将返回栈添加到堆栈生成器。 对于在清单文件中所定义层次结构内的每个 Activity,返回栈均包含可启动 Activity 的 Intent 对象。此方法还会添加一些可在全新任务中启动堆栈的标志。
        builder.addParentStack(TestReceiveActivity.class);
        builder.addNextIntent(resultIntent);//添加可从通知中启动 Activity 的 Intent。 将在第一步中创建的 Intent 作为 addNextIntent() 的参数传递
        /**
         * 如需,请通过调用 TaskStackBuilder.editIntentAt() 向堆栈中的 Intent 对象添加参数。有时,需要确保目标 Activity 在用户使用“返回”导航回它时会显示有意义的数据。
         */
        //通过调用 getPendingIntent() 获得此返回栈的 PendingIntent。 然后,您可以使用此 PendingIntent 作为 setContentIntent() 的参数。
        return builder.getPendingIntent(200, PendingIntent.FLAG_UPDATE_CURRENT);
    }


    public PendingIntent getPendingIntent(Context context, int requestCode, Intent intent) {
        return PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    }


 private PendingIntent getPendingIntent(String action) {
        Intent intent = new Intent(this, TestReceiveActivity.class);
        intent.setAction(action);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 200, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        /**
         FLAG_ONE_SHOT 表示返回的PendingIntent仅能执行一次,执行完后自动取消
         FLAG_NO_CREATE 表示如果描述的PendingIntent不存在,并不创建相应的PendingIntent,而是返回NULL
         FLAG_CANCEL_CURRENT 表示相应的PendingIntent已经存在,则取消前者,然后创建新的PendingIntent, 这个有利于数据保持为最新的,可以用于即时通信的通信场景
         FLAG_UPDATE_CURRENT 表示更新的PendingIntent
         */
        return pendingIntent;
    }

配置TaskStackBuilder

<application
        android:allowBackup="true"
        android:icon="@mipmap/test_ic_launcher"
        android:label="@string/test_app_name"
        android:roundIcon="@mipmap/test_ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/test_AppTheme">
        <activity android:name=".TestPluginMainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".TestNotificationActivity" />

        <activity
            android:name=".TestReceiveActivity"
            android:parentActivityName=".TestPluginMainActivity">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value=".TestPluginMainActivity" />
        </activity>

    </application>

WindowManager

简单实现

public class TestWindowService extends Service {


    private static final String TAG = "TestWindowService";
    private WindowManager.LayoutParams params;
    private WindowManager windowManager;
    private View view;
    private int startX;
    private int startY;
    private int endX;
    private int endY;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        createFloatView();
        initWindow();
        addWindowView2Window();
        initClick();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    private void initWindow() {
        windowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);

        params = new WindowManager.LayoutParams();

        // 更多type:https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#TYPE_PHONE
        params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;

        params.format = PixelFormat.TRANSPARENT;

        params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
//                |WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
//                        |WindowManager.LayoutParams.FLAG_FULLSCREEN
        ;

        // 设置通知栏的长和宽
//        params.width = windowManager.getDefaultDisplay().getWidth();
//        params.height = 200;

        params.width = WindowManager.LayoutParams.MATCH_PARENT;
        params.height = (int) (200 * getResources().getDisplayMetrics().density + 0.5);
        params.gravity = Gravity.LEFT | Gravity.TOP;

    }

    private void createFloatView() {
        view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.test_custom_remote_view_dj, null);
        int height = view.getHeight();
    }

    private void addWindowView2Window() {
        windowManager.addView(view, params);
    }

    private void initClick() {
        view.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {

                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        startX = (int) event.getRawX();
                        startY = (int) event.getRawY();

                        Log.d(TAG, "getRawX: " + startX + "  getX() =  " + event.getX());
                        Log.d(TAG, "getRawY: " + startY + "  getY() =  " + event.getY());
                        //getRawX是触摸位置相对于屏幕的坐标,getX是相对于父控件的坐标
                        break;
                    case MotionEvent.ACTION_MOVE:
                        Log.d(TAG, "onTouch: move");
                        endX = (int) event.getRawX();
                        endY = (int) event.getRawY();

                        params.x = (int) (event.getRawX() - view.getMeasuredWidth() / 2);
                        params.y = (int) (event.getRawY() - view.getMeasuredHeight() / 2);

                        windowManager.updateViewLayout(view, params);
                        return true;
                    case MotionEvent.ACTION_UP:
                        if (needIntercept()) {
                            return true;
                        }
                        break;

                    case MotionEvent.ACTION_CANCEL:
                        break;
                }
                return false;
            }
        });
    }

    private boolean needIntercept() {
        if (Math.abs(startX - endX) > 30 || Math.abs(startY - endY) > 30) {
            return true;
        }
        return false;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (windowManager != null) {
            windowManager.removeView(view);
        }
        view = null;
    }

现在我们把版本改成8.0以上

//统一编译版本
ext {
    buildToolsVersion = '28.0.3'
    compileSdkVersion = 28
    minSdkVersion = 16
    targetSdkVersion = 28
    versionCode = 1
    versionName = "1.0"
    //javaVersion = JavaVersion.VERSION_1_8
    supportLibraryVersion = '28.0.0'
}

windowmManager 问题

Unable to create service com.component.fx.plugin_test.TestWindowService: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@7a19d70 -- permission denied for window type 2010

    Unable to add window android.view.ViewRootImpl$W@7a19d70 -- permission denied for window type 2010
原因

使用SYSTEM_ALERT_WINDOW权限的应用程序无法再使用以下窗口类型在其他应用程序和系统窗口上方显示警报窗口:

TYPE_PHONE
TYPE_PRIORITY_PHONE
TYPE_SYSTEM_ALERT
TYPE_SYSTEM_OVERLAY
TYPE_SYSTEM_ERROR

相反,应用必须使用名为TYPE_APPLICATION_OVERLAY的新窗口类型。

点击这个寻找答案

Android 8.0 行为变更

修改的代码

if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
            params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
        }


    /**
     * 创建一个Channel类型
     */
    @TargetApi(Build.VERSION_CODES.O)
    public static void createChannel() {

        String channelName = "聊天消息";
        int importance = NotificationManager.IMPORTANCE_MAX;
        setChannel(CHAT_ChANNEL_ID, channelName, importance);

        channelName = "新闻";
        importance = NotificationManager.IMPORTANCE_DEFAULT;
        setChannel(NEWS_ChANNEL_ID, channelName, importance);
    }


    /**
     * 创建Channel类型
     *
     * @param id
     * @param name
     * @param importance
     */
    @TargetApi(Build.VERSION_CODES.O)
    private static void setChannel(String id, String name, int importance) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationManager nm = (NotificationManager) BaseApplication.getAppContext().getSystemService(Context.NOTIFICATION_SERVICE);
            if (nm == null) {
                return;
            }
            NotificationChannel channel = new NotificationChannel(id, name, importance);
            channel.setShowBadge(true);
            nm.createNotificationChannel(channel);
        }
    }





    /**
     * 判断 是否关闭了通知
     * <p>
     * 是否允许通知
     *
     * @param context
     * @return true 可用 false 不可用
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static boolean isNotifyEnable(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            ///< 8.0手机以上
            NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            Log.d("feng", "mNm: "+notificationManager);
            if (notificationManager.getImportance() == NotificationManager.IMPORTANCE_NONE) {
                return false;
            }
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            AppOpsManager opsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
            ApplicationInfo info = context.getApplicationInfo();
            String packageName = context.getPackageName();
            int uid = info.uid;
            try {
                Class<?> appOpsClass = Class.forName(AppOpsManager.class.getName());
                Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, String.class);
                Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
                int value = (Integer) opPostNotificationValue.get(Integer.class);
                return ((Integer) checkOpNoThrowMethod.invoke(opsManager, value, uid, packageName) == AppOpsManager.MODE_ALLOWED);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        return true;

    }


    /**
     * 判断channel是否可用
     *
     * @param channelId
     * @return true 可用  false 不可用
     */
    public static boolean isChannelEnable(String channelId) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationManager nm = (NotificationManager) BaseApplication.getAppContext().getSystemService(Context.NOTIFICATION_SERVICE);
            NotificationChannel channel = nm.getNotificationChannel(channelId);
            return !(channel.getImportance() == NotificationManager.IMPORTANCE_NONE);
        }
        return true;
    }


    /**
     * 如果用户关闭了Channel通道可以根据这个方法跳转到设置页面
     *
     * @param channelId
     */
    @TargetApi(Build.VERSION_CODES.O)
    public static void openNotifyChannel(String channelId) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationManager nm = (NotificationManager) BaseApplication.getAppContext().getSystemService(Context.NOTIFICATION_SERVICE);
            Log.d("feng", "mNm: openNotifyChannel "+nm);
            NotificationChannel channel = nm.getNotificationChannel(channelId);
            if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
                Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.putExtra(Settings.EXTRA_APP_PACKAGE, BaseApplication.getAppContext().getPackageName());
                intent.putExtra(Settings.EXTRA_CHANNEL_ID, channel.getId());
                BaseApplication.getAppContext().startActivity(intent);
            }
        }
    }

defaultNotify8.0适配

image.png

元素:

  1. 标题 Title/Name

  2. 大图标 Icon/Photo

  3. 内容文字

  4. 内容信息 MESSAGE

  5. 小图标 Secondary Icon

  6. 通知的时间 Timestamp,默认为系统发出通知的时间,也可通过setWhen()来设置

自定义通知布局的可用高度取决于通知视图。普通视图布局限制为 64 dp,扩展视图布局限制为 256 dp。

Notification

通知是您可以在应用的常规 UI 外部向用户显示的消息。当您告知系统发出通知时,它将先以图标的形式显示在通知区域中。用户可以打开抽屉式通知栏查看通知的详细信息。 通知区域和抽屉式通知栏均是由系统控制的区域,用户可以随时查看。

一个最简单的通知栏。

NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
        builder.setContentText("今天中午吃什么?")
                .setSmallIcon(R.drawable.test_ic_launcher_background)
                .setContentTitle("我是标题");


        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        notificationManager.notify(100, builder.build());

这几行代码就可以显示出一个通知栏。

必需设置的通知内容

  • setContentText 内容
  • setSmallIcon 图标
  • setContentTitle 标题

少一个都报错。

setContentIntent

现在我们加入意图:

.setContentIntent(getPendingIntent())//设置意图

获取一个PendiingIntent包含着点击之后的行为

private PendingIntent getPendingIntent() {
        Intent intent = new Intent(this, TestPluginMainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 200, intent, 0);
        return pendingIntent;
    }

其实Pendding就是Intent的包装,下面会说,我们的行为定义了打开一个Activity。

现在点击发送按钮,会发送一条通知,通知栏显示出我们发送的通知

findViewById(R.id.app_test_btn_send_notify).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                showNotify();
            }

        });

点击通知,触发定义的行为,跳转到TestPluginMainActivity类中。

请注意,现在没有对通知的消失做任何处理,所点点击通知,通知栏不会消失。

image.png

官方网站是这样获取pendingIntent

public PendingIntent getTaskStackPending(String action) {
        Intent resultIntent = new Intent(this, TestPluginMainActivity.class);
        TaskStackBuilder builder = TaskStackBuilder.create(this);
        builder.addParentStack(TestPluginMainActivity.class);
        builder.addNextIntent(resultIntent);
        return builder.getPendingIntent(200, PendingIntent.FLAG_UPDATE_CURRENT);
    }

行为是一样的,有个知识点TaskStackBuilder一会在说,先留意下。

只有一条通知?

在内容上在加入时间,点击发送 在发送一条, 会发现只是内容做了替换,但是还是只有一条消息。

这样可不行,做成这样,需求会打死我的。

其实很好解决,在发送通知的时候有个参数id

NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        notificationManager.notify(100, builder.build());

我这里写死了 是100,所以才会只有一条消息。

修改之后

notificationManager.notify(id++, builder.build());

效果

image.png
image.png

发送的内容过多时,会自动折叠,展开后,显示每条数据。

setDeleteIntent

当通知被删除时触发。

.setDeleteIntent(getPendingIntent("onDelete"))//设置删除意图

可以在通知被删除时做一些行为。
稍微修改下getPendingIntent()

.setContentIntent(getPendingIntent("onClick"))//设置意图
.setDeleteIntent(getPendingIntent("onDelete"))//设置删除意图

发送时加入一个action,在接受端 打印这个action。

private PendingIntent getPendingIntent(String action) {
        Intent intent = new Intent(this, TestPluginMainActivity.class);
        intent.setAction(action);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 200, intent, 0);
        return pendingIntent;
    }

可以发现 点击通知栏时,在TestPluginMainActivity类,toast弹出onClick ,删除通知时,弹出onDelete。

.setPriority() //设置优先级

  • Notification.PRIORITY_MAX
    重要而紧急的通知,通知用户这个事件是时间上紧迫的或者需要立即处理的。
  • Notification.PRIORITY_HIGH
    高优先级用于重要的通信内容,例如短消息或者聊天,这些都是对用户来说比较有兴趣的
  • Notification.PRIORITY_DEFAULT
    默认优先级用于没有特殊优先级分类的通知
  • Notification.PRIORITY_LOW
    低优先级可以通知用户但又不是很紧急的事件。只显示状态栏图标
  • Notification.PRIORITY_MIN
    用于后台消息 (例如天气或者位置信息)。只有用户下拉通知抽屉才能看到内容

其它flag

提醒标志符成员:

Notification.FLAG_SHOW_LIGHTS
//三色灯提醒,在使用三色灯提醒时候必须加该标志符

  • Notification.FLAG_ONGOING_EVENT
    //发起正在运行事件(活动中)

  • Notification.FLAG_INSISTENT
    //让声音、振动无限循环,直到用户响应 (取消或者打开)

  • Notification.FLAG_ONLY_ALERT_ONCE
    //发起Notification后,铃声和震动均只执行一次

  • Notification.FLAG_AUTO_CANCEL
    //用户单击通知后自动消失

  • Notification.FLAG_NO_CLEAR
    //只有全部清除时,Notification才会清除 ,不清楚该通知(QQ的通知无法清除,就是用的这个)

  • Notification.FLAG_FOREGROUND_SERVICE
    //表示正在运行的服务

setDefaults 需要的flags

  • Notification.DEFAULT_VIBRATE
    //添加默认震动提醒 需要 VIBRATE permission

  • Notification.DEFAULT_SOUND
    // 添加默认声音提醒

  • Notification.DEFAULT_LIGHTS
    // 添加默认三色灯提醒

  • Notification.DEFAULT_ALL
    // 添加默认以上3种全部提醒

震动

  • setDefaults(Notification.DEFAULT_ALL)
    //向通知添加声音、闪灯和振动效果的最简单、 使用默认(defaults)属性,可以组合多个属性,

               Notification.DEFAULT_VIBRATE(添加默认震动提醒);
               Notification.DEFAULT_SOUND(添加默认声音提醒);
               Notification.DEFAULT_LIGHTS(添加默认三色灯提醒)
               Notification.DEFAULT_ALL(添加默认以上3种全部提醒)
    
  • setVibrate(new long[]{0, 300, 500, 700,500,1000,500,1000})
    设置自定义震动 延迟0ms,然后振动300ms,在延迟500ms, 接着再振动700ms,

setDefaults 和 setVibrate 如果都设置 setDefaults生效,setVibrate不生效。

铃声

  • setSound(Uri.parse("file:///sdcard/a.mp3"))
    加载本地音乐

  • setSound((Uri.withAppendedPath(internalContentUri, "5")))
    //设置接收到通知时的铃声,可以用系统的,也可以自己设置 获取Android多媒体库内的铃声

三色灯

  • setLights(0xffff0000,1000,0)
    //设置三色灯,参数依次是:灯光颜色, 亮持续时间,暗的时间,不是所有颜色都可以,这跟设备有关,有些手机还不带三色灯; 另外,还需要为Notification设置flags为Notification.FLAG_SHOW_LIGHTS才支持三色灯提醒!

setOngoing(boolean ongoing)

设置为ture,表示它为一个正在进行的通知。他们通常是用来表示 一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载, 同步操作,主动网络连接)

setProgress()

max:进度条最大数值 、progress:当前进度、indeterminate:表示进度是否不确定,true为不确定,如下所示 ,false为确定 如下所示

image.png
image.png

功能:设置带进度条的通知,可以在下载中使用

public void showProgressNotify() {
        mNotifyManager =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        mBuilder = new NotificationCompat.Builder(this);
        mBuilder.setContentTitle("Picture Download")
                .setContentText("Download in progress")
                .setSmallIcon(R.drawable.test_ic_launcher_background);
// Start a lengthy operation in a background thread
        new Thread(
                new Runnable() {
                    @SuppressLint("LongLogTag")
                    @Override
                    public void run() {
                        int incr;
                        // Do the "lengthy" operation 20 times
                        for (incr = 0; incr <= 100; incr += 5) {
                            // Sets the progress indicator to a max value, the
                            // current completion percentage, and "determinate"
                            // state
                            mBuilder.setProgress(100, incr, true);
                            // Displays the progress bar for the first time.
                            mNotifyManager.notify(0, mBuilder.build());
                            // Sleeps the thread, simulating an operation
                            // that takes time
                            try {
                                // Sleep for 5 seconds
                                Thread.sleep(5 * 1000);
                            } catch (InterruptedException e) {
                                Log.d(TAG, "sleep failure");
                            }
                        }
                        // When the loop is finished, updates the notification
                        mBuilder.setContentText("Download complete")
                                // Removes the progress bar
                                .setProgress(0, 0, false);
                        mNotifyManager.notify(id++, mBuilder.build());
                    }
                }
// Starts the thread by calling the run() method in its Runnable
        ).start();
    }

setTicker

在 api 21 后不再显示,仅用于辅助服务。

builder.setTicker("this is ticker");

PendingIntent

PendingIntent 是一种特殊的 Intent ,字面意思可以解释为延迟的 Intent ,用于在某个事件结束后执行特定的 Action

PendingIntent 是 Android 系统管理并持有的用于描述和获取原始数据的对象的标志(引用)。也就是说,即便创建该PendingIntent对象的进程被杀死了,这个PendingItent对象在其他进程中还是可用的。 日常使用中的短信、闹钟等都用到了 PendingIntent。

//获取一个用于启动 Activity 的 PendingIntent 对象

public static PendingIntent getActivity(Context context, int requestCode, Intent intent, int flags);

//获取一个用于启动 Service 的 PendingIntent 对象

public static PendingIntent getService(Context context, int requestCode, Intent intent, int flags);


//获取一个用于向 BroadcastReceiver 广播的 PendingIntent 对象

public static PendingIntent getBroadcast(Context context, int requestCode, Intent intent, int flags)
Intent intent = new Intent(this, TestReceiveActivity.class);
        intent.setAction(action);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 200, intent, FLAG_CANCEL_CURRENT);
  • FLAG_ONE_SHOT
    表示返回的PendingIntent仅能执行一次,执行完后自动取消

  • FLAG_NO_CREATE
    表示如果描述的PendingIntent不存在,并不创建相应的PendingIntent,而是返回NULL

  • FLAG_CANCEL_CURRENT
    表示相应的PendingIntent已经存在,则取消前者,然后创建新的PendingIntent, 这个有利于数据保持为最新的,可以用于即时通信的通信场景

  • FLAG_UPDATE_CURRENT
    表示更新的PendingIntent

Intent和PendingIntent的区别
  • Intent是立即使用的,而PendingIntent可以等到事件发生后触发,PendingIntent可以cancel

  • Intent在程序结束后即终止,而PendingIntent在程序结束后依然有效

  • PendingIntent自带Context,而Intent需要在某个Context内运行

  • Intent在原task中运行,PendingIntent在新的task中运行

TaskStackBuilder

有三个界面 A主界面,B发送通知栏界面,C 通知栏跳转界面。
现在 我需要 进入A界面,在跳转B界面,发送一个通知栏,点击通知栏进入C界面,注意 现在我们在C界面,如果点击返回按钮,如何返回到A界面?

设置特殊 Activity PendingIntent

浮动通知

实现的方法有两种:

  1. 通知拥有高优先级而且使用了铃声和震动。
  2. setFullScreenIntent(),如以下例子:

锁定屏幕通知

RemoteViews

Notification的自定义布局是RemoteViews,和其他RemoteViews一样,在自定义视图布局文件中,仅支持FrameLayout、LinearLayout、RelativeLayout三种布局控件和AnalogClock、Chronometer、Button、ImageButton、ImageView、ProgressBar、TextView、ViewFlipper、ListView、GridView、StackView和AdapterViewFlipper这些显示控件,不支持这些类的子类或Android提供的其他控件。否则会引起ClassNotFoundException异常

image.png
image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容