Android SystemUI组件(04)状态栏-Notification显示&管理

112 篇文章 88 订阅

该系列文章总纲链接:专题分纲目录 Android SystemUI组件


本章关键点总结 & 说明:

说明:本章节持续迭代之前章节的思维导图,主要关注下方 SystemBars分析中状态栏中的Notification部分即可。

1 Notification在APP中的常见流程

一般来说,APP中通知状态栏的流程如下所示:

NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Notification notification = new Notification.Builder(this)
        .setContentTitle("标题")
        .setContentText("通知内容~")
        .setWhen(System.currentTimeMillis()) //通知时间
        .setSmallIcon(R.mipmap.ic_launcher)  //通知小图标
        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)) //通知大图标
        .build();
manager.notify(1,notification);//发出通知

可以看出这里使用 建造者模式进行了通知栏的创建,接下来使用notify方法来通知执行通知状态栏的操作。这里主要分析两个部分:notification的创建流程(第二部分) 和 NotificationManager的notify方法的执行流程(第三部分)。

2 notification的创建

notification使用了建造者的设计模式,初始化就是参数赋值的过程,Builder的代码实现如下:

public Builder(Context context) {
    mContext = context;
    // Set defaults to match the defaults of a Notification
    mWhen = System.currentTimeMillis();
    mAudioStreamType = STREAM_DEFAULT;
    mAudioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
    mPriority = PRIORITY_DEFAULT;
    mPeople = new ArrayList<String>();

    mColorUtil = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP ?
            NotificationColorUtil.getInstance(mContext) : null;
}

可以看到主要是 成员变量的赋值,后面的setXXX操作也是类似。因此这里主要说明下notification中的关键字段(成员变量信息),如下所示:

  • icon,用于描述一个图标的资源id,通知信息需提供一个有效的图标资源,否则会被忽略。
  • iconLevel,如果icon所描述的图标资源存在level,用于告知状态栏将显示图标资源的level。
  • number,表示通知数目。假如有3条新的短信时,没有必要使用3个通知,而是将一个通知的number成员设置为3,状态栏会将这一数字显示在通知图标上。
  • contentIntent,一个PendingIntent实例,在下拉卷帘中点击本条通知时应当执行的动作。它用于启动一个Activity以便让用户能够查看关于此条通知的详细信息。例如,当用户点击一条提示微信 通知时,微信将会被启动并显示短信的详细内容。
  • deleteIntent,一个PendingIntent实例,当用户从下拉卷帘中删除本条通知时应当执行的动作。deleteIntent往往用在表示某个工作正在后台进行的通知中,以便当用户从下拉卷帘中删除通知时,发送者可以终止此后台工作。
  • tickerText,文本。当通知信息被添加时,状态栏将会在其上逐行显示这条信息。其目的在于使用户无需进行卷帘下拉操作即可从快速获取通知的内容。
  • fullScreenIntent,一个PendingIntent实例,用于告知状态栏当此条信息被添加时应当执行的动作,它是启动一个Activity用于显示与通知相关的详细信息。fullScreenIntent其实是一个替代tickerText的设置。当Notification中指定了fullScreenIntent时,StatusBar将会忽略tickerText的设置。因为这两个设置的目的都是为了让用户可以在第一时间了解通知的内容。不过相对于tickerText,fullScreenIntent强制性要明显得多,因为它将打断用户当前正在进行的工作。因此fullScreenIntent应该仅用于通知非常重要或紧急的事件,比如说来电或闹钟。
  • contentView/bigContentView,RemoteView实例,可以用来定制通知信息在下拉卷帘中的显示形式。一般来讲,相对于contentView,bigContentView可以占用更多空间以显示更详细的内容。状态栏将根据自己的判断选择将通知信息显示为contentView或bigContentView。
  • sound与audioStreamType,指定一个用于播放通知声音的Uri及其所使用的音频流类型。在默认情况下,播放通知声音所用的音频流类型为STREAM_NOTIFICATION。
  • vibrate,一个float数组,用于描述震动方式。
  • ledARGB/ledOnMS/ledOffMS,指定当此通知被添加到状态栏时设备上的LED指示灯的行为,当然 这也需要硬件设备的支持。
  • defaults,用于指示声音、震动以及LED指示灯是否使用系统的默认行为。
  • flags,用于存储一系列用于定制通知信息行为的标记。通知信息的发送者可以根据需求在其中加入这样的标记:
    • FLAG_SHOW_LIGHTS要求使用LED指示灯。
    • FLAG_ONGOING_EVENT指示通知信息用于描述一个正在进行的后台工作。
    • FLAG_INSISTENT指示通知声音将持续播放直到通知信息被移除或被用户查看。
    • FLAG_ONLY_ARLERT_ONCE指示任何时候通知信息被加入到状态栏时都会播放一次通知声音。
    • FLAG_AUTO_CANCEL指示当用户在下拉卷帘中点击通知信息时自动将其移出。
    • FLAG_FOREGROUND_SERVICE指示此通知用来表示一个正在以foreground形式运行的服务。
  • priority,描述了通知的重要性级别。低优先级的通知信息有可能不会被显示给用户,或显示在通知列表中靠下的位置。

当通知栏创建后就通过notify方法将通知栏展现在状态栏上啦。

3 NotificationManager.notify()方法解析

 NotificationManager.notify()的代码实现如下:

public void notify(int id, Notification notification)
{
    notify(null, id, notification);
}

public void notify(String tag, int id, Notification notification)
{
    int[] idOut = new int[1];
	//获取NotificationManagerService的Bp端代理
    INotificationManager service = getService();
	//获取信息发送者的包名
    String pkg = mContext.getPackageName();
    if (notification.sound != null) {
        notification.sound = notification.sound.getCanonicalUri();
        if (StrictMode.vmFileUriExposureEnabled()) {
            notification.sound.checkFileUriExposed("Notification.sound");
        }
    }
    Notification stripped = notification.clone();
    Builder.stripForDelivery(stripped);
	//...
	//将包名、tag、id以及Notification实例一并提交给NotificationManagerService
	service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
                stripped, idOut, UserHandle.myUserId());
	//...
}

关于参数的说明:notify()方法要求通知信息的发送者提供一个Notification实例、一个字符串类型的参数tag、一个int类型的参数id,这些信息共同确定了信息的意图。当一条通知信息已经被提交给NotificationManager.notify()并且仍然显示在状态栏中时,它将会被新提交的拥有相同意图(即相同的tag以及相同的id)通知信息所替换。

3.1 NotificationManagerService的通知信息

这里专注分析service的enqueueNotificationWithTag实现,代码如下所示:

@Override
public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
        Notification notification, int[] idOut, int userId) throws RemoteException {
    enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
            Binder.getCallingPid(), tag, id, notification, idOut, userId);
}

这里直接调用了enqueueNotificationInternal,代码实现如下所示:

void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
        final int callingPid, final String tag, final int id, final Notification notification,
        int[] idOut, int incomingUserId) {
	//安全检查
    checkCallerIsSystemOrSameApp(pkg);
	//是否为系统通知
    final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
    final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
    final int userId = ActivityManager.handleIncomingUser(callingPid,
            callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
    final UserHandle user = new UserHandle(userId);
	
    if (!isSystemNotification && !isNotificationFromListener) {
        synchronized (mNotificationList) {
            int count = 0;
            final int N = mNotificationList.size();
            for (int i=0; i<N; i++) {
                final NotificationRecord r = mNotificationList.get(i);
                if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
                    count++;
					//限制每个应用程序最多提交50次通知
                    if (count >= MAX_PACKAGE_NOTIFICATIONS) {
                        return;
                    }
                }
            }
        }
    }
	//...
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            synchronized (mNotificationList) {
                // ===打分操作===
                // 0. Sanitize inputs
                notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
                        Notification.PRIORITY_MAX);
                // Migrate notification flags to scores
                if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
                    if (notification.priority < Notification.PRIORITY_MAX) {
                        notification.priority = Notification.PRIORITY_MAX;
                    }
                } else if (SCORE_ONGOING_HIGHER &&
                        0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
                    if (notification.priority < Notification.PRIORITY_HIGH) {
                        notification.priority = Notification.PRIORITY_HIGH;
                    }
                }
				//为通知信息的重要性打分
                // 1. initial score: buckets of 10, around the app [-20..20]
                final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;

                // 2. extract ranking signals from the notification data
                final StatusBarNotification n = new StatusBarNotification(
                        pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
                        user);
				/*
				 NotificationRecord类型实例用于包装 所有与此通知相关的信息
				(pkg、tag、id、调用者、score、notification实例。。。)
				*/
                NotificationRecord r = new NotificationRecord(n, score);
				//old表示 因为与新通知有相同的意图 而被新通知所替换的NotificationRecord实例
                NotificationRecord old = mNotificationsByKey.get(n.getKey());
                if (old != null) {
                    // Retain ranking information from previous record
                    r.copyRankingInformation(old);
                }

                // Handle grouped notifications and bail out early if we
                // can to avoid extracting signals.
                handleGroupedNotificationLocked(r, old, callingUid, callingPid);
                boolean ignoreNotification =
                        removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);

                // This conditional is a dirty hack to limit the logging done on
                //     behalf of the download manager without affecting other apps.
                if (!pkg.equals("com.android.providers.downloads")
                        || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
                    int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
                    if (ignoreNotification) {
                        enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
                    } else if (old != null) {
                        enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
                    }
                    EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
                            pkg, id, tag, userId, notification.toString(),
                            enqueueStatus);
                }

                if (ignoreNotification) {
                    return;
                }

                mRankingHelper.extractSignals(r);

                // 3. Apply local rules

                // blocked apps
				//如果应用程序被用户禁发通知,则将通知此信息的分数设置为-1000,即JUNK_SCORE
                if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
                    if (!isSystemNotification) {
                        r.score = JUNK_SCORE;
                    }
                }
				//分数偏低,垃圾通知,直接忽略
                if (r.score < SCORE_DISPLAY_THRESHOLD) {
                    // Notification will be blocked because the score is too low.
                    return;
                }
				//获取具有相同意图的通知在mNotificationList的位置
                int index = indexOfNotificationLocked(n.getKey());
                if (index < 0) {
					//如果没有相同意图的,说明是新的,直接加入到链表mNotificationList中
                    mNotificationList.add(r);
                    mUsageStats.registerPostedByApp(r);
                } else {
					//如果通知已经存在,则将原来的通知从链表mNotificationList中删除,将新的通知加入到链表中。
                    old = mNotificationList.get(index);
                    mNotificationList.set(index, r);
                    mUsageStats.registerUpdatedByApp(r, old);
                    // Make sure we don't lose the foreground service state.
                    notification.flags |=
                            old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
                    r.isUpdate = true;
                }

                mNotificationsByKey.put(n.getKey(), r);

                // Ensure if this is a foreground service that the proper additional flags are set.
                if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
                    notification.flags |= Notification.FLAG_ONGOING_EVENT
                            | Notification.FLAG_NO_CLEAR;
                }

                applyZenModeLocked(r);
                mRankingHelper.sort(mNotificationList);
				//只显示有图标的通知
                if (notification.icon != 0) {
                    StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
                    mListeners.notifyPostedLocked(n, oldSbn);
                } else {
					//icon为0 表示通知被删除
                    if (old != null && !old.isCanceled) {
                        mListeners.notifyRemovedLocked(n);
                    }
                }
				//声音,震动,闪光灯的控制
                buzzBeepBlinkLocked(r);
            }
        }
    });
    idOut[0] = id;
}

在该方法中, 要想发出通知必须得满足以下几个条件:

  • 通过安全检查(避免恶意应用通过冒用包名、id、tag、。。。进行篡改)。
  • 非系统应用最多只能发送50个通知消息。
  • 用户设置了允许应用发送通知。
  • 通过打分过滤(将一些低重要性的、黑名单中的应用所发送的通知排斥在外。被系统判定为非垃圾通知)。
  • 通知必须得有icon。

最后使用notifyPostedLocked方法做发送操作,这里详细分析该方法,代码如下:

public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
    // Lazily initialized snapshots of the notification.
    StatusBarNotification sbnClone = null;
    StatusBarNotification sbnCloneLight = null;

    for (final ManagedServiceInfo info : mServices) {
        boolean sbnVisible = isVisibleToListener(sbn, info);
        boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
        //...
        final int trim = mListeners.getOnNotificationPostedTrim(info);

        if (trim == TRIM_LIGHT && sbnCloneLight == null) {
            sbnCloneLight = sbn.cloneLight();
        } else if (trim == TRIM_FULL && sbnClone == null) {
            sbnClone = sbn.clone();
        }
        final StatusBarNotification sbnToPost =
                (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;

        mHandler.post(new Runnable() {
            @Override
            public void run() {
                //关键方法
                notifyPosted(info, sbnToPost, update);
            }
        });
    }
}

这里最终会调用到notifyPosted方法,该方法代码实现如下:

private void notifyPosted(final ManagedServiceInfo info,
        final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
    final INotificationListener listener = (INotificationListener)info.service;
    StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
    //...
        listener.onNotificationPosted(sbnHolder, rankingUpdate);
	//...
}

这里有一个INotificationListener对象(I开头的就看出是一个IPC通信),onNotificationPosted的是在BaseStatusBar中(systemUI进程),代码实现如下:

private final NotificationListenerService mNotificationListener =
        new NotificationListenerService() {
	//...
    @Override
    public void onNotificationPosted(final StatusBarNotification sbn,
            final RankingMap rankingMap) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Notification n = sbn.getNotification();
                //根据通知的唯一key值来判断该通知是否是更新还是新增的
                boolean isUpdate = mNotificationData.get(sbn.getKey()) != null || isHeadsUp(sbn.getKey());

                if (n.isGroupChild() &&
                        mNotificationData.isGroupWithSummary(sbn.getGroupKey())) {
                    if (isUpdate) {
                        removeNotification(sbn.getKey(), rankingMap);
                    } else {
                        mNotificationData.updateRanking(rankingMap);
                    }
                    return;
                }
                if (isUpdate) {
                    updateNotification(sbn, rankingMap);
                } else {
                    //子类PhoneStatusBar中方法
                    addNotification(sbn, rankingMap);
                }
            }
        });
    }
    //...
};

从这里开始进入到下一段分析,即PhoneStatusBar(继承BaseStatusbar)中的addNotification。

3.2 PhoneStatusBar中的addNotification

PhoneStatusBar中的addNotification,代码实现如下:

@Override
public void addNotification(StatusBarNotification notification, RankingMap ranking) {
    //...
	//关键点1:创建Entry实例
    Entry shadeEntry = createNotificationViews(notification);
    if (shadeEntry == null) {
        return;
    }
	//...
	//关键点2:将Entry添加到状态栏上
    addNotificationViews(shadeEntry, ranking);
	//...
}

@1 createNotificationViews 创建Entry实例

createNotificationViews的是在父类BaseStatusbar中,代码实现如下:

protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
    // Construct the icon...
    // Construct the expanded view.
    NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
    if (!inflateViews(entry, mStackScroller)) {
        handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
        return null;
    }
    return entry;
}

这里首先实例化了NotificationData的内部类Entry。NotificationData 里面有几个比较重要的数据结构,如下所示:

ArrayMap<String, Entry> mEntries = new ArrayMap<>(); //所有Entry的集合
ArrayList<Entry> mSortedAndFiltered = new ArrayList<>(); //排序后的Entry集合

这里解读下Entry这个类,关键信息如下:

public static final class Entry {
    public String key;
    public StatusBarNotification notification;
    public StatusBarIconView icon;
    public ExpandableNotificationRow row; // the outer expanded view
    public View expanded; // the inflated RemoteViews
    public View expandedPublic; // for insecure lockscreens
    public View expandedBig;
    private boolean interruption;
    public boolean autoRedacted; // whether the redacted notification was generated by us
    public boolean legacy; // whether the notification has a legacy, dark background
    public int targetSdk;
	//...
}

一个Entry对应了一条通知栏的所有Data信息,最后添加界面上的就是这个row。在inflateViews方法里这个row会被赋值,inflateViews方法 代码如下所示:

private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
        return inflateViews(entry, parent, false);
}

继续分析,inflateViews代码实现如下:

private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent, boolean isHeadsUp) {
	//...
	//contentView和bigContentView是我们构造Notification时传过来的view
    RemoteViews contentView = sbn.getNotification().contentView;
    RemoteViews bigContentView = sbn.getNotification().bigContentView;
	//...
    ExpandableNotificationRow row;

    if (entry.row != null) {
		//...
    } else {
        // create the row view
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);
		//使用指定view填充
        row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
                parent, false);
        row.setExpansionLogger(this, entry.notification.getKey());
    }
	
	/*
	 这个expanded view(状态栏展开,而不是通知展开)就是我们在下拉状态栏中看到的每一条view,
     NotificationContentView 会根据不同状态来控制显示哪个view(默认通知/展开通知)
	 */
    NotificationContentView expanded =
            (NotificationContentView) row.findViewById(R.id.expanded);
    NotificationContentView expandedPublic =
            (NotificationContentView) row.findViewById(R.id.expandedPublic);

    row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);

	//给每一条通知设置onClick的点击事件,以来相应我们设置的动作
    PendingIntent contentIntent = sbn.getNotification().contentIntent;
    if (contentIntent != null) {
        final View.OnClickListener listener = makeClicker(contentIntent, sbn.getKey(),
                isHeadsUp);
        row.setOnClickListener(listener);
    } else {
        row.setOnClickListener(null);
    }

    // set up the adaptive layout
    View contentViewLocal = null;
    View bigContentViewLocal = null;
	
	//将构造通知栏时设置的contentView & bigContentView(RemoteView)转换为view
    contentViewLocal = contentView.apply(mContext, expanded,
            mOnClickHandler);
    if (bigContentView != null) {
        bigContentViewLocal = bigContentView.apply(mContext, expanded,
                mOnClickHandler);
    }

    //expanded(是一个FrameLayout的ViewGroup),包含2个view
    if (contentViewLocal != null) {
        contentViewLocal.setIsRootNamespace(true);
        expanded.setContractedChild(contentViewLocal);
    }
    if (bigContentViewLocal != null) {
        bigContentViewLocal.setIsRootNamespace(true);
        expanded.setExpandedChild(bigContentViewLocal);
    }
	//...
    return true;
}

Entry.row添加到屏幕上前,做了如下的属性赋值:

  • inflate布局文件status_bar_notification_row(通知栏的根view)
  • 给status_bar_notification_row设置监听器
  • 将在构造通知过程中的bigContentView 和 contentView加入到通知栏的根view里面

@2 addNotificationViews 将Entry添加到状态栏上

addNotificationViews代码实现如下:

protected void addNotificationViews(Entry entry, RankingMap ranking) {
    if (entry == null) {
        return;
    }
    // Add the expanded view and icon.
    mNotificationData.add(entry, ranking);
    updateNotifications();
}

将得到的Entry添加到mNotificationData里面,然后调用updateNotifications,代码实现如下:

@Override
protected void updateNotifications() {
    // TODO: Move this into updateNotificationIcons()?
    if (mNotificationIcons == null) return;
    mNotificationData.filterAndSort();
    updateNotificationShade();
    updateNotificationIcons();
}

继续分析 updateNotificationShade,代码实现如下:

private void updateNotificationShade() {
	//...
	//从mNotificationData对象中获取一个list<Entry>对象
    ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
	//ExpandableNotificationRow对应着每一个通知,继承自FrameLayout
    ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
    final int N = activeNotifications.size();
    for (int i=0; i<N; i++) {
        Entry ent = activeNotifications.get(i);
		//...
		//将mNotificationData中的每一个Entry对象的row属性添加到List<ExpandableNotificationRow>中
        toShow.add(ent.row);
    }
	//...
    for (int i=0; i<toShow.size(); i++) {
		//将ExpandableNotificationRow添加到mStackScroller里面
        View v = toShow.get(i);
        if (v.getParent() == null) {
            mStackScroller.addView(v);
        }
    }
	//...
}

这里的成员变量 mStackScroller是NotificationStackScrollLayout(继承ViewGroup,是我们下拉状态栏看到的整片view的根)中的对象,而ExpandableNotificationRow(继承FrameLayout)对应着每一个通知。

4 整体总结

通知信息被显示在状态栏共经历 通知发送者、NotificationManagerService(通过安全检查、打分等机制 今儿确定显示顺序和忽略与否,通知信息被封装在NotificationRecord对象中,被保存在mNotificationList中)、PhoneStatusBar、状态栏(通知信息抽象为NotificationDataEntry)这些过程,最终呈现在用户面前。

以上就是一个通知栏从初始化到显示的流程。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Android中,如果想要自定义下拉通知栏的颜色,可以通过修改SystemUI的相关设置来实现。 首先,为了修改SystemUI的颜色,需要获取相应的权限。我们可以在AndroidManifest.xml文件中添加如下代码: ```xml <uses-permission android:name="android.permission.STATUS_BAR"/> ``` 接下来,在我们的项目中创建一个名为values的文件夹,并在其中创建一个名为colors.xml的文件。在这个文件中,我们可以定义我们想要使用的颜色。例如,我们可以定义一个名为notification_background的颜色,用于设置下拉通知栏的背景颜色。代码如下: ```xml <resources> <color name="notification_background">#FF0000</color> </resources> ``` 然后,我们需要修改SystemUI的源代码,以更新背景颜色。具体来说,我们需要找到StatusBar类中的updateResources方法,并在该方法中添加以下代码: ```java Context context = mContext.createPackageContext("com.example.notificationtest", Context.CONTEXT_IGNORE_SECURITY); // 替换为自己的包名 int color = context.getResources().getColor(R.color.notification_background); mBackgroundView.setBackgroundColor(color); ``` 最后,我们需要重新编译并安装我们的应用程序。一旦安装完成,我们就可以看到下拉通知栏的背景颜色已经根据我们在colors.xml中定义的颜色进行了自定义。 以上是通过修改SystemUI的方式来自定义下拉通知栏的颜色。请注意,这种方式需要具备系统级权限,因此只适用于特定的Android设备。在实际开发中,请确保在使用这种方式之前了解并遵守相关的法规和政策,以避免违规行为。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值