Android——横幅通知

横幅通知,也称为提醒式通知,效果如下图:
在这里插入图片描述
这个效果在QQ,微信,钉钉等一些主流的App当中,大家一定很熟悉,今天就来说说如何实现。

可能会触发提醒式通知的条件有如下3种:

  • 用户的Activiity处于全屏模式(应用使用fullScreenIntent)
  • 通知的优先级很高,且在搭载Android 7.1(API级别25)及更低版本的设备上使用铃声或震动。
  • 在搭载Android 8.0(API级别为26)及更高版本的设备上,通知渠道的重要程度比较高。

但是现实往往是残酷的,当你按照要求照做之后发现8.0以上系统还是不能实现你想要的效果。下面来大概说道说道原因。

由于国内大多数App都比较流氓,喜欢这通知也发,那通知也发,导致用户被一堆垃圾信息困扰,系统为了保护用户不受此类消息的困扰,就关闭了权限,如果想要显示,就只能由用户手动打开该权限才行。而且系统没办法获取该权限是否打开。所以,当产品再拿QQ等国民应用来跟你说人家都能实现怎么怎么滴的时候请勇敢的怼回去。人家那些应用厂商一般都默认打开权限的,这没什么好说的,级别在那放着呢。

打开权限管理页面的方法,不同版本有些差别,需适配下:

//打开系统消息通知设置页面
Intent intent = new Intent();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    //android 8.0引导,引导到CHANNEL_ID对应的渠道设置下
    intent.setAction(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
    intent.putExtra(Settings.EXTRA_APP_PACKAGE, getContext().getPackageName());
    intent.putExtra(Settings.EXTRA_CHANNEL_ID, newChannel);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    //android 5.0-7.0,引导到所有渠道设置下(单个渠道没有具体的设置)
    intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
    intent.putExtra("app_package", getContext().getPackageName());
    intent.putExtra("app_uid", getContext().getApplicationInfo().uid);
} else {
    //其他
    intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
    intent.setData(Uri.fromParts("package", getContext().getPackageName(), null));
}

startActivity(intent);

上面是一种解决方式,还有人说了,那我想任何时候用户都能弹出横幅通知呢,那就自己写咯。大概思路就是用悬浮窗的方式展示一个Toast或者Dialog。

大概代码如下:

private static void showHeadsUpNotification(Context context, UMessage uMessage) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            //1、判断是否开启了悬浮通知栏权限   注:部分华为无效
            if (!Settings.canDrawOverlays(MyApplication.getInstance())) {
                MessageDialog.show((AppCompatActivity) ActivityUtils.getTopActivity(), "提示",
                        "请打开" + AppUtils.getAppName() + "的悬浮窗权限,否则会有很多重要消息漏掉哦!", "确定", "取消")
                        .setOnOkButtonClickListener(new OnDialogButtonClickListener() {
                            @Override
                            public boolean onClick(BaseDialog baseDialog, View v) {
                                //未显示消息入队
                                headsupMsgQueue.offer(uMessage);

                                requestOverlayPermission();
                                return false;
                            }
                        })
                        .setOnCancelButtonClickListener(new OnDialogButtonClickListener() {
                            @Override
                            public boolean onClick(BaseDialog baseDialog, View v) {
                                return false;
                            }
                        });

            } else {
                //显示横幅通知
                showHeadsUpNotification_(context, uMessage);
            }
        }
}

//请求悬浮窗权限
@TargetApi(Build.VERSION_CODES.M)
private static void requestOverlayPermission() {
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
        intent.setData(Uri.parse("package:" + AppUtils.getAppPackageName()));
        ActivityUtils.getTopActivity().startActivityForResult(intent, ALERT_WINDOW_PERMISSION_REQCODE);
}

public static void showHeadsUpNotification_(Context context) {
        if (headsupMsgQueue.isEmpty())
            return;

		//由于一开始没有悬浮窗权限,所以当收到消息通知时先将他们暂时都存入队列中,当获得权限之后再显示横幅通知
        while (!headsupMsgQueue.isEmpty()) {
            UMessage uMessage = headsupMsgQueue.poll();
            if (uMessage != null) {
                showHeadsUpNotification_(context, uMessage);
            }
        }
}

private static void showHeadsUpNotification_(Context context, UMessage uMessage) {
	new Handler(Looper.getMainLooper()).post(() -> {//这里要看是否在子线程中再加
            // 传入 Application 对象表示设置成全局的,但需要有悬浮窗权限
            new XToast<>(MyApplication.getInstance())
                    .setView(R.layout.layout_headsup_notification)
                    // 设置成可拖拽的
                    //.setDraggable()
                    // 设置显示时长
                    .setDuration(6000)
                    // 设置动画样式
                    //.setAnimStyle(android.R.style.Animation_Translucent)
                    // 设置外层是否能被触摸
                    //.setOutsideTouchable(false)
                    // 设置窗口背景阴影强度
//                .setBackgroundDimAmount(0.5f)
                    .setGravity(Gravity.TOP)
                    .setText(R.id.notification_app_name, AppUtils.getAppName())
                    .setText(R.id.notification_title, uMessage.title)
                    .setText(R.id.notification_text, uMessage.text)
                    .setOnClickListener(new XToast.OnClickListener<View>() {
                        @Override
                        public void onClick(XToast<?> toast, View view) {
                            // 点击这个 View 后消失
                            toast.cancel();

                            if (uMessage.extra != null && uMessage.extra.containsKey("type") && uMessage.extra.containsKey("content")) {
                                // 跳转到某个Activity
                                Intent handleIntent = new Intent(context, NotifyActivity.class);
                                toast.startActivity(handleIntent);
                            }
                        }
                    })
                    .show();
        });
  }

代码仅提供实现思路,不能直接运行,有需要还是结合自己的需求自己写吧。

弹框可参考这两个库:
XPopup
XToast

最近一直搞这个通知,唉,系统升级版本越高权限控制是越来越严格了,有时候发现我们利用系统提供的能力无能为力的时候还是自己动手实现吧。

要实现 FCM 自定义通知横幅,你需要在客户端和服务器端分别进行以下配置: 客户端配置: 1. 在你的项目级 build.gradle 文件中添加以下依赖: ``` implementation 'com.google.firebase:firebase-messaging:22.0.0' ``` 2. 在你的应用级 build.gradle 文件中添加以下配置: ``` android { // ... defaultConfig { // ... // 设置通道 ID,用于兼容 Android 8.0 及以上版本的通知 notificationChannelId "my_channel_id" } } // 在 AndroidManifest.xml 文件中添加以下权限和服务声明 <uses-permission android:name="android.permission.INTERNET" /> <service android:name=".MyFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service> <service android:name=".MyFirebaseInstanceIDService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.INSTANCE_ID_EVENT" /> </intent-filter> </service> ``` 3. 创建一个继承自 FirebaseMessagingService 的服务类,并重写 onMessageReceived 方法,用于处理接收到的消息,如下所示: ``` public class MyFirebaseMessagingService extends FirebaseMessagingService { private static final String TAG = "MyFirebaseMessagingService"; @Override public void onMessageReceived(RemoteMessage remoteMessage) { Log.d(TAG, "From: " + remoteMessage.getFrom()); // Check if message contains a notification payload. if (remoteMessage.getNotification() != null) { Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody()); // 发送自定义横幅通知 sendCustomNotification(remoteMessage); } } private void sendCustomNotification(RemoteMessage remoteMessage) { // 创建一个 NotificationCompat.Builder 对象 NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "my_channel_id") .setSmallIcon(R.drawable.notification_icon) .setContentTitle(remoteMessage.getNotification().getTitle()) .setContentText(remoteMessage.getNotification().getBody()) .setPriority(NotificationCompat.PRIORITY_HIGH) .setAutoCancel(true); // 添加自定义横幅样式 NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle() .setBigContentTitle(remoteMessage.getNotification().getTitle()) .bigText(remoteMessage.getNotification().getBody()); builder.setStyle(bigTextStyle); // 显示通知 NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); notificationManager.notify(1, builder.build()); } } ``` 服务器端配置: 1. 使用 Firebase 控制台创建一个新的 Firebase 项目,并在项目设置中获取到项目的 Server key。 2. 使用项目的 Server key 发送消息到 FCM 接口,消息格式如下: ``` { "to": "设备的 FCM token", "notification": { "title": "通知标题", "body": "通知内容" }, "data": { // 自定义数据 } } ``` 这样,当你的应用接收到消息时,就会发送一个自定义横幅通知。需要注意的是,自定义横幅样式只会在 Android 5.0 及以上版本的设备上生效。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值