今天咱們主要講的是SystemUI狀態欄裏面另外一個常見的icons——notification icons,該icons主要用於顯示app或者framework發送的各類notification icon,表示當前有新的通知來了,須要下拉通知欄進行查看,以達到提示用戶的目的。java
本文主要從兩個方面講述下notification icon功能,主要分爲初始化流程和通知icon顯示流程
話很少說,咱們開始吧。android
初始化流程app
首先咱們看下狀態欄的佈局文件 status_bar.xmlasync
<!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). --> <com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/notification_icon_area" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" android:orientation="horizontal" /> <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area" android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal" >
咱們今天講的notification icons就是這個 android:id="@+id/notification_icon_area" 了。
一樣它最外層是一個AlphaOptimizedFrameLayout控件,前文已經說過相似的了,
SystemUI之狀態欄status icon加載流程
該控件實現了hasOverlappingRendering()方法,該方法用來標記當前view是否存在過分繪製。ide
接下來咱們看下SystemUI是怎麼加載這個AlphaOptimizedFrameLayout函數
CollapsedStatusBarFragment.java佈局
public void initNotificationIconArea(NotificationIconAreaController notificationIconAreaController) { ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area); mNotificationIconAreaInner = notificationIconAreaController.getNotificationInnerAreaView(); if (mNotificationIconAreaInner.getParent() != null) { ((ViewGroup) mNotificationIconAreaInner.getParent()) .removeView(mNotificationIconAreaInner); } notificationIconArea.addView(mNotificationIconAreaInner); // Default to showing until we know otherwise. showNotificationIconArea(false); }
NotificationIconAreaController.javaui
/** * Initializes the views that will represent the notification area. */ protected void initializeNotificationAreaViews(Context context) { reloadDimens(context); LayoutInflater layoutInflater = LayoutInflater.from(context); mNotificationIconArea = inflateIconArea(layoutInflater); mNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById( R.id.notificationIcons); mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout(); } protected View inflateIconArea(LayoutInflater inflater) { return inflater.inflate(R.layout.notification_icon_area, null); }
如上,咱們就找到了初始化的地方,主要是經過inflate這個R.layout.notification_icon_area文件,經過addView的方式添加到了AlphaOptimizedFrameLayout,下面就是看下R.layout.notification_icon_area這個文件了this
<com.android.keyguard.AlphaOptimizedLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/notification_icon_area_inner" android:layout_width="match_parent" android:layout_height="match_parent" > <com.android.systemui.statusbar.phone.NotificationIconContainer android:id="@+id/notificationIcons" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentStart="true" android:gravity="center_vertical" android:orientation="horizontal"/> </com.android.keyguard.AlphaOptimizedLinearLayout>
/** * A container for notification icons. It handles overflowing icons properly and positions them * correctly on the screen. */ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {}
從備註就可以看出來,這個就是全部notification icons的父控件,全部icons最後都是添加到這裏面來的,
好了,到這裏咱們的第一部分初始化流程就講完了。spa
通知icon顯示流程
首先咱們須要看下notification生成的地方
StatusBar.java
public void onNotificationPosted(final StatusBarNotification sbn, final RankingMap rankingMap) { if (isUpdate) { updateNotification(sbn, rankingMap); } else { addNotification(sbn, rankingMap); } } public void addNotification(StatusBarNotification notification, RankingMap ranking) throws InflationException { String key = notification.getKey(); if (DEBUG) Log.d(TAG, "addNotification key=" + key); mNotificationData.updateRanking(ranking); Entry shadeEntry = createNotificationViews(notification); protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) throws InflationException { if (DEBUG) { Log.d(TAG, "createNotificationViews(notification=" + sbn); } NotificationData.Entry entry = new NotificationData.Entry(sbn); Dependency.get(LeakDetector.class).trackInstance(entry); entry.createIcons(mContext, sbn); // Construct the expanded view. inflateViews(entry, mStackScroller); return entry; } }
層層調用以後,最後會經過entry.createIcons(mContext, sbn)生成notification icon,而後存放在NotificationData裏面,感興趣的能夠看下entry.createIcons(mContext, sbn), 該函數裏面主要生成了一個StatusBarIconView對象,這個就是最終顯示在狀態欄的icon。
notification生成的過程大體就是經過inflateViews(entry, mStackScroller)--->AsyncInflationTask處理加載而後生成ExpandableNotificationRow等的信息,最後經過StatusBar.java
@Override public void onAsyncInflationFinished(Entry entry) { mPendingNotifications.remove(entry.key); // If there was an async task started after the removal, we don't want to add it back to // the list, otherwise we might get leaks. boolean isNew = mNotificationData.get(entry.key) == null; if (isNew && !entry.row.isRemoved()) { addEntry(entry); } else if (!isNew && entry.row.hasLowPriorityStateUpdated()) { mVisualStabilityManager.onLowPriorityUpdated(entry); updateNotificationShade();// 此處完成添加 } entry.row.setLowPriorityStateUpdated(false); } private void updateNotificationShade() { ................................................ for (int i=0; i<toShow.size(); i++) { View v = toShow.get(i); if (v.getParent() == null) { mVisualStabilityManager.notifyViewAddition(v); mStackScroller.addView(v); } } ................................................ // Let's also update the icons mNotificationIconAreaController.updateNotificationIcons(mNotificationData);//添加notification icons }
updateNotificationShade這個函數回調完成view的添加,這個函數先是把inflate出來的通知,添加到NotificationScrollLayout裏面,而後再添加notification icon,接下來咱們就看下updateNotificationIcons裏面的邏輯了。
NotificationIconAreaController.java
private NotificationIconContainer mNotificationIcons; /** * Updates the notifications with the given list of notifications to display. */ public void updateNotificationIcons(NotificationData notificationData) { updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons, false /* showAmbient */);// 添加status bar notification icon updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons, NotificationShelf.SHOW_AMBIENT_ICONS);// 添加 notification self icon applyNotificationIconsTint(); }
主要的添加動做就在updateIconsForLayout這個函數中了
private void updateIconsForLayout(NotificationData notificationData, Function<NotificationData.Entry, StatusBarIconView> function, NotificationIconContainer hostLayout, boolean showAmbient) { ArrayList<StatusBarIconView> toShow = new ArrayList<>( mNotificationScrollLayout.getChildCount()); // Filter out ambient notifications and notification children. for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) { View view = mNotificationScrollLayout.getChildAt(i); if (view instanceof ExpandableNotificationRow) { NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry(); if (shouldShowNotificationIcon(ent, notificationData, showAmbient)) { toShow.add(function.apply(ent)); } } } ...................................... ...................................... ...................................... final FrameLayout.LayoutParams params = generateIconLayoutParams(); for (int i = 0; i < toShow.size(); i++) { View v = toShow.get(i); // The view might still be transiently added if it was just removed and added again hostLayout.removeTransientView(v); if (v.getParent() == null) { hostLayout.addView(v, i, params); } } }
首先從mNotificationScrollLayout取出NotificationData,而後把NotificationData存放的StatusBarIconView取出添加到toShow裏面,最後遍歷添加到NotificationIconContainer中,這樣就完成了往NotificationIconContainer添加icon的過程。
到這裏,notification icon加載流程已經講完,後面有時間還會講下signal icon的加載流程,敬請關注。