常用代碼整理:Android 自定義推送(Notification)

說明:大部分內容都是參考別的文章,這裏做整理是爲了以後的編程有實用的模板,可以即需即用。

默認的通知欄樣式:
    private void sendDefaultNotification() {
        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        Intent intent = new Intent(this, TestActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        Notification.Builder builder = new Notification
                .Builder(getApplicationContext())
                .setSmallIcon(R.mipmap.ic_launcher)// 一定要設置
                .setTicker("狀態欄標題")
                .setContentTitle("內容標題")
                .setContentText("內容")
                .setDefaults(Notification.DEFAULT_ALL)
                .setPriority(Notification.PRIORITY_MAX)
                .setAutoCancel(true)
                .setContentIntent(pendingIntent);

        //8.0 以後需要加上channelId 才能正常顯示
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            String channelId = getApplicationContext().getPackageName();
            String channelName = "默認通知";
            notificationManager.createNotificationChannel(new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH));
            // 一定要加上,不然不顯示
            builder.setChannelId(getApplicationContext().getPackageName());
        }

        notificationManager.notify(1, builder.build());
    }
自定義的通知欄樣式:
    private void sendCustomNotification() {
        final NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        Intent intent = new Intent(this, TestActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        Uri soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + getApplicationContext().getPackageName() + "/" + R.raw.ring);
        // 如果音頻文件已保存到本地,則採用下面代碼
        /*Uri soundUri;
        String path = "xxx/xxx.mp"
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
            soundUri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", new File(path));
        } else {
            soundUri = Uri.fromFile(new File(path));
        }*/

        mRemoteViews = new RemoteViews(getPackageName(), R.layout.notification_custom);

        Notification.Builder builder = new Notification
                .Builder(getApplicationContext())
                .setSmallIcon(R.mipmap.zzjeohbs)// 一定要設置
                .setContent(mRemoteViews)// 默認情況下通知高度爲64dp(但是又有一些不止64,比如vivo)
                //.setDefaults(Notification.DEFAULT_ALL)
                .setSound(soundUri)
                .setPriority(Notification.PRIORITY_MAX)
                .setAutoCancel(true)
                .setContentIntent(pendingIntent);

        //8.0 以後需要加上channelId 才能正常顯示
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            String channelId = getApplicationContext().getPackageName();
            String channelName = "默認通知";
            NotificationChannel notificationChannel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
            // For API 26+ you need to set the sound on the notification channel
            AudioAttributes audioAttributes = new AudioAttributes.Builder()
                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    .setUsage(AudioAttributes.USAGE_NOTIFICATION)
                    .build();
            notificationChannel.setSound(soundUri, audioAttributes);
            notificationManager.createNotificationChannel(notificationChannel);
            // 一定要加上,不然不顯示
            builder.setChannelId(getApplicationContext().getPackageName());
        } else {
            builder.setSound(soundUri);
            //.setSound(soundUri, AudioManager.STREAM_MUSIC)
        }

        final Notification notification = builder.build();
        // 適配oppo自定義通知欄無法顯示問題
        notification.flags = Notification.FLAG_AUTO_CANCEL | Notification.FLAG_ONGOING_EVENT;

        // api 24 builder.setCustomBigContentView(getBigRemoteViews());
        // notification.bigContentView = getBigRemoteViews(); // api 16 bigContentView 的最大高度是256dp

        Glide.with(this)
                .load("https://imgs.gmilesquan.com/img/home/titlebar_tab.png")
                .asBitmap()
                .into(new SimpleTarget<Bitmap>() {
                    @Override
                    public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
                        mRemoteViews.setImageViewBitmap(R.id.iv_title, resource);
                        mRemoteViews.setTextViewText(R.id.tv_content, "推送內容");
                        // TODO: 2019/8/12 通知之前要完成數據裝載
                        notificationManager.notify(1, notification);
                    }
                });
    }

    private RemoteViews getBigRemoteViews() {
        RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.notification_big_custom);
        remoteViews.setTextViewText(R.id.tv_content, "推送內容");
        return remoteViews;
    }

notification_custom.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="70dp"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/iv_title"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_centerVertical="true"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        tools:src="@mipmap/ic_launcher" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginTop="5dp"
        android:layout_toRightOf="@id/iv_title"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_title"
            style="@style/NotificationTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:maxLines="2"
            android:text="測試title" />

        <TextView
            android:id="@+id/tv_content"
            style="@style/NotificationInfo"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:ellipsize="end"
            android:maxLines="1"
            android:text="測試" />

    </LinearLayout>

</RelativeLayout>

notification_big_custom.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="256dp"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/iv_title"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_centerVertical="true"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:src="@mipmap/ic_launcher" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toRightOf="@id/iv_title"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_title"
            style="@style/NotificationTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="測試title" />

        <TextView
            android:id="@+id/tv_content"
            style="@style/NotificationInfo"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:text="測試" />

    </LinearLayout>

</RelativeLayout>

自定義通知欄時,因爲各手機系統通知欄的背景主題明暗不同,所以建議內容文本顏色採用系統提供樣式,api_21前後不同,所以要在values-v21文件中做適配:

1、默認 values
    <!-- Android 自定義Notification字體顏色適配 -->
    <style name="NotificationInfo" parent="android:TextAppearance.StatusBar.EventContent" />
    <style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" />

2、values-v21
    <!-- Android 自定義Notification字體顏色適配 -->
    <style name="NotificationInfo" parent="android:TextAppearance.Material.Notification.Info" />
    <style name="NotificationTitle" parent="android:TextAppearance.Material.Notification.Title" />

上面的內容文本顏色適配方案,在某些手機系統上還是生效不了,所以在網上找到了下面這個方案,通過分析系統通知欄主題色系是明還是暗,來設置內容文本顏色明暗:

    private boolean isDarkNotificationTheme(Context context) {
        return !isSimilarColor(Color.BLACK, getNotificationColor(context));
    }

    private int getNotificationColor(Context context) {
        Notification.Builder builder = new Notification.Builder(context);
        Notification notification = builder.build();
        int layoutId = notification.contentView.getLayoutId();
        ViewGroup viewGroup = (ViewGroup) LayoutInflater.from(context).inflate(layoutId, null, false);
        if (viewGroup.findViewById(android.R.id.title) != null) {
            return ((TextView) viewGroup.findViewById(android.R.id.title)).getCurrentTextColor();
        }
        return findColor(viewGroup);
    }

    private boolean isSimilarColor(int baseColor, int color) {
        int simpleBaseColor = baseColor | 0xff000000;
        int simpleColor = color | 0xff000000;
        int baseRed = Color.red(simpleBaseColor) - Color.red(simpleColor);
        int baseGreen = Color.green(simpleBaseColor) - Color.green(simpleColor);
        int baseBlue = Color.blue(simpleBaseColor) - Color.blue(simpleColor);
        double value = Math.sqrt(baseRed * baseRed + baseGreen * baseGreen + baseBlue * baseBlue);
        if (value < 180.0) {
            return true;
        }
        return false;
    }

    private int findColor(ViewGroup viewGroupSource) {
        int color = Color.TRANSPARENT;
        LinkedList<ViewGroup> viewGroups = new LinkedList<>();
        viewGroups.add(viewGroupSource);
        while (viewGroups.size() > 0) {
            ViewGroup viewGroup1 = viewGroups.getFirst();
            for (int i = 0; i < viewGroup1.getChildCount(); i++) {
                if (viewGroup1.getChildAt(i) instanceof ViewGroup) {
                    viewGroups.add((ViewGroup) viewGroup1.getChildAt(i));
                } else if (viewGroup1.getChildAt(i) instanceof TextView) {
                    if (((TextView) viewGroup1.getChildAt(i)).getCurrentTextColor() != -1) {
                        color = ((TextView) viewGroup1.getChildAt(i)).getCurrentTextColor();
                    }
                }
            }
            viewGroups.remove(viewGroup1);
        }
        return color;
    }

這個適配方案在某些國產手機系統上運行分析時,會奔潰,呵呵:

        try {
            if (isDarkNotificationTheme(context)) {
                remoteViews.setTextColor(R.id.tv_title, context.getResources().getColor(R.color.custom_notification_title_color));
                remoteViews.setTextColor(R.id.tv_content, context.getResources().getColor(R.color.custom_notification_content_color));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    private int getNotificationColor(Context context) {
        Notification.Builder builder = new Notification.Builder(context);
        Notification notification = builder.build();
        RemoteViews contentView = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            contentView = builder.createContentView();
        } else {
            // As of N, this field may be null.
            contentView = notification.contentView;
        }
        int layoutId = contentView.getLayoutId();
        ViewGroup viewGroup = (ViewGroup) LayoutInflater.from(context).inflate(layoutId, null, false);
        if (viewGroup.findViewById(android.R.id.title) != null) {
            return ((TextView) viewGroup.findViewById(android.R.id.title)).getCurrentTextColor();
        }
        return findColor(viewGroup);
    }

還有一點,我們有時只想設置不帶icon的文本通知,但是在某些國產手機系統上,還是會在左上角展示一個小icon,甚至有些會在左邊展示一個大icon。。。


參考文章:
1、https://blog.csdn.net/u010005281/article/details/78708172
2、https://blog.csdn.net/cloudzyy/article/details/83151090

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章