在SystemUI中,状态栏和通知栏都是在PhoneStatusBar的makeStatusBarView方法添加进来的,这里主要说说状态栏中的QuickSettingPanel
// ================================================================================
// Constructing the view
// ================================================================================
protected PhoneStatusBarView makeStatusBarView() {
final Context context = mContext;
Resources res = context.getResources();
updateDisplaySize(); // populates mDisplayMetrics
updateResources();
mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
R.layout.super_status_bar, null);
....
mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
mStatusBarView.setBar(this);
PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
mStatusBarView.setPanelHolder(holder);
mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
R.id.notification_panel);
mNotificationPanel.setStatusBar(this);
// Set up the quick settings tile panel
mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
if (mQSPanel != null) {
final QSTileHost qsh = new QSTileHost(mContext, this,
mBluetoothController, mLocationController, mRotationLockController,
mNetworkController, mZenModeController, mHotspotController,
mCastController, mFlashlightController,
mUserSwitcherController, mKeyguardMonitor,
mSecurityController);
mQSPanel.setHost(qsh);
mQSPanel.setTiles(qsh.getTiles());
mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
mHeader.setQSPanel(mQSPanel);
qsh.setCallback(new QSTileHost.Callback() {
@Override
public void onTilesChanged() {
mQSPanel.setTiles(qsh.getTiles());
}
});
}
....
return mStatusBarView;
}
下面主要说下图中QuickSetting是什么显示出来以及图中的每个控件功能是何如实现的
具体图标对应的id在这就不一一说明了,可以自己对着上面的图去看,下面主要分析下这个界面是怎么显示出来的,在PhoneStatusBar的makeStatusBarView方法中加载了super_status_bar.xml文件
mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_status_bar, null);
在上面这个布局文件中include了status_bar_expanded.xml,在这个文件中差不多就包含了图中所有控件,其中qs_panel.xml主要包括亮度调节框及以下的那一块,status_bar_expanded_header主要是亮度调节框以上部分<com.android.systemui.statusbar.phone.StatusBarWindowView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:sysui="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> .... <include layout="@layout/status_bar" android:layout_width="match_parent" android:layout_height="@dimen/status_bar_height" /> <FrameLayout android:id="@+id/brightness_mirror" android:layout_width="@dimen/notification_panel_width" android:layout_height="wrap_content" android:layout_gravity="@integer/notification_panel_layout_gravity" android:paddingLeft="@dimen/notification_side_padding" android:paddingRight="@dimen/notification_side_padding" android:visibility="invisible"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:elevation="2dp" android:background="@drawable/brightness_mirror_background"> <include layout="@layout/quick_settings_brightness_dialog" android:layout_width="match_parent" android:layout_height="wrap_content" /> </FrameLayout> </FrameLayout> <com.android.systemui.statusbar.phone.PanelHolder android:id="@+id/panel_holder" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/transparent" > <include layout="@layout/status_bar_expanded" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" /> </com.android.systemui.statusbar.phone.PanelHolder> .... </com.android.systemui.statusbar.phone.StatusBarWindowView>
<com.android.systemui.statusbar.phone.NotificationPanelView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" android:id="@+id/notification_panel" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent" > .... <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="@integer/notification_panel_layout_gravity" android:id="@+id/notification_container_parent" android:clipToPadding="false" android:clipChildren="false"> <com.android.systemui.statusbar.phone.ObservableScrollView android:id="@+id/scroll_view" android:layout_width="@dimen/notification_panel_width" android:layout_height="match_parent" android:layout_gravity="@integer/notification_panel_layout_gravity" android:scrollbars="none" android:overScrollMode="never" android:fillViewport="true"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <include layout="@layout/qs_panel" android:layout_marginTop="@dimen/status_bar_header_height_expanded" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/notification_side_padding" android:layout_marginRight="@dimen/notification_side_padding"/> <!-- A view to reserve space for the collapsed stack --> <!-- Layout height: notification_min_height + bottom_stack_peek_amount --> <View android:id="@+id/reserve_notification_space" android:layout_height="@dimen/min_stack_height" android:layout_width="match_parent" android:layout_marginTop="@dimen/notifications_top_padding" /> <View android:layout_height="@dimen/notification_side_padding" android:layout_width="match_parent" /> </LinearLayout> </com.android.systemui.statusbar.phone.ObservableScrollView> <com.android.systemui.statusbar.stack.NotificationStackScrollLayout android:id="@+id/notification_stack_scroller" android:layout_width="@dimen/notification_panel_width" android:layout_height="match_parent" android:layout_gravity="@integer/notification_panel_layout_gravity" android:layout_marginBottom="@dimen/close_handle_underlap" android:importantForAccessibility="no" /> <ViewStub android:id="@+id/keyguard_user_switcher" android:layout="@layout/keyguard_user_switcher" android:layout_height="match_parent" android:layout_width="match_parent" /> <include layout="@layout/keyguard_status_bar" android:visibility="invisible" /> </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer> <include layout="@layout/keyguard_bottom_area" android:visibility="gone" /> <include layout="@layout/status_bar_expanded_header" /> .... </com.android.systemui.statusbar.phone.NotificationPanelView>
在这个布局中包含了status_bar_expanded_header.xml,下面具体看看status_bar_expanded_header这个布局文件,这个布局文件中主要包含了亮度调节框上面那一部分其余的控件差不多都在qs_panel.xml里了<com.android.systemui.statusbar.phone.StatusBarHeaderView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/header" android:layout_width="@dimen/notification_panel_width" android:layout_height="@dimen/status_bar_header_height" android:layout_gravity="@integer/notification_panel_layout_gravity" android:paddingStart="@dimen/notification_side_padding" android:paddingEnd="@dimen/notification_side_padding" android:baselineAligned="false" android:elevation="4dp" android:background="@drawable/notification_header_bg" android:clickable="true" android:focusable="true" > <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch" android:layout_width="@dimen/multi_user_switch_width_collapsed" android:layout_height="@dimen/status_bar_header_height" android:layout_alignParentEnd="true" android:background="@drawable/ripple_drawable" > <ImageView android:id="@+id/multi_user_avatar" android:layout_width="@dimen/multi_user_avatar_expanded_size" android:layout_height="@dimen/multi_user_avatar_expanded_size" android:layout_gravity="center" android:scaleType="centerInside"/> </com.android.systemui.statusbar.phone.MultiUserSwitch> <com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/settings_button_container" android:layout_width="48dp" android:layout_height="@dimen/status_bar_header_height" android:clipChildren="false" android:clipToPadding="false" android:layout_toStartOf="@id/multi_user_switch"> <com.android.systemui.statusbar.phone.SettingsButton android:id="@+id/settings_button" style="@android:style/Widget.Material.Button.Borderless" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/ripple_drawable" android:src="@drawable/ic_settings" android:contentDescription="@string/accessibility_desc_settings" /> <com.android.systemui.statusbar.AlphaOptimizedImageView android:id="@+id/tuner_icon" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingStart="36dp" android:tint="#4DFFFFFF" android:tintMode="src_in" android:visibility="invisible" android:src="@drawable/tuner" /> </com.android.systemui.statusbar.AlphaOptimizedFrameLayout> <LinearLayout android:id="@+id/system_icons_super_container" android:layout_width="wrap_content" android:layout_height="@dimen/status_bar_header_height" android:layout_toStartOf="@id/multi_user_switch" android:layout_alignWithParentIfMissing="true" android:layout_marginStart="16dp" android:background="@drawable/ripple_drawable" android:paddingEnd="4dp" > <FrameLayout android:id="@+id/system_icons_container" android:layout_width="wrap_content" android:layout_height="@dimen/status_bar_height" android:layout_gravity="center_vertical" > <include layout="@layout/system_icons" /> </FrameLayout> <TextView android:id="@+id/battery_level" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="@dimen/header_battery_margin_expanded" android:paddingEnd="@dimen/battery_level_padding_end" android:textColor="#ffffff" android:textSize="@dimen/battery_level_text_size" android:importantForAccessibility="noHideDescendants"/> </LinearLayout> <TextView android:id="@+id/header_emergency_calls_only" android:layout_height="@dimen/status_bar_header_height" android:layout_width="wrap_content" android:layout_alignParentStart="true" android:layout_toStartOf="@id/system_icons_super_container" android:paddingStart="16dp" android:paddingEnd="16dp" android:visibility="gone" android:textAppearance="@style/TextAppearance.StatusBar.Expanded.EmergencyCallsOnly" android:text="@*android:string/emergency_calls_only" android:singleLine="true" android:gravity="center_vertical" /> <FrameLayout android:id="@+id/date_group" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/clock_collapsed_bottom_margin" android:layout_alignParentBottom="true"> <com.android.systemui.statusbar.policy.DateView android:id="@+id/date_collapsed" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:singleLine="true" android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date" android:layout_below="@id/clock" systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" /> <com.android.systemui.statusbar.policy.DateView android:id="@+id/date_expanded" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:singleLine="true" android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date" android:layout_below="@id/clock" systemui:datePattern="eeeeMMMMd" /> </FrameLayout> <include layout="@layout/split_clock_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_above="@id/date_group" android:id="@+id/clock" /> <com.android.systemui.statusbar.AlphaOptimizedButton android:id="@+id/alarm_status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_toEndOf="@id/date_group" android:layout_marginBottom="4dp" android:drawablePadding="6dp" android:drawableStart="@drawable/ic_access_alarms_small" android:textColor="#64ffffff" android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date" android:paddingEnd="6dp" android:paddingStart="6dp" android:paddingTop="16dp" android:paddingBottom="16dp" android:background="?android:attr/selectableItemBackground" android:visibility="gone" /> <include android:id="@+id/qs_detail_header" layout="@layout/qs_detail_header" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" /> <com.android.systemui.statusbar.AlphaOptimizedImageView android:id="@+id/qs_detail_header_progress" android:src="@drawable/indeterminate_anim" android:alpha="0" android:background="@color/qs_detail_progress_track" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" systemui:hasOverlappingRendering="false" /> <TextView android:id="@+id/header_debug_info" android:visibility="invisible" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:fontFamily="sans-serif-condensed" android:textSize="11dp" android:textStyle="bold" android:textColor="#00A040" android:padding="2dp" /> </com.android.systemui.statusbar.phone.StatusBarHeaderView>
其中QSPanel是继承自ViewGroup的一个控件,里面包含了亮度调节框,以及wifi,蓝牙这些图标<com.android.systemui.qs.QSContainer xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/quick_settings_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/qs_background_primary" android:paddingTop="8dp" android:paddingBottom="8dp" android:elevation="2dp"> <com.android.systemui.qs.QSPanel android:id="@+id/quick_settings_panel" android:background="#0000" android:layout_width="match_parent" android:layout_height="wrap_content" /> </com.android.systemui.qs.QSContainer>
public QSPanel(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; mDetail = LayoutInflater.from(context).inflate(R.layout.qs_detail, this, false); mDetailContent = (ViewGroup) mDetail.findViewById(android.R.id.content); mDetailSettingsButton = (TextView) mDetail.findViewById(android.R.id.button2); mDetailDoneButton = (TextView) mDetail.findViewById(android.R.id.button1); updateDetailText(); mDetail.setVisibility(GONE); mDetail.setClickable(true); mBrightnessView = LayoutInflater.from(context).inflate( R.layout.quick_settings_brightness_dialog, this, false); mFooter = new QSFooter(this, context); addView(mDetail); addView(mBrightnessView); addView(mFooter.getView()); mClipper = new QSDetailClipper(mDetail); updateResources(); mBrightnessController = new BrightnessController(getContext(), (ImageView) findViewById(R.id.brightness_icon), (ToggleSlider) findViewById(R.id.brightness_slider));
在QSTileHost.java中会去加载默认配置需要显示的图标// Set up the quick settings tile panel mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel); if (mQSPanel != null) { final QSTileHost qsh = new QSTileHost(mContext, this, mBluetoothController, mLocationController, mRotationLockController, mNetworkController, mZenModeController, mHotspotController, mCastController, mFlashlightController, mUserSwitcherController, mKeyguardMonitor, mSecurityController); mQSPanel.setHost(qsh); mQSPanel.setTiles(qsh.getTiles()); mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow); mQSPanel.setBrightnessMirror(mBrightnessMirrorController); mHeader.setQSPanel(mQSPanel); qsh.setCallback(new QSTileHost.Callback() { @Override public void onTilesChanged() { mQSPanel.setTiles(qsh.getTiles()); } }); }
protected List<String> loadTileSpecs(String tileList) { final Resources res = mContext.getResources(); final String defaultTileList = res.getString(R.string.quick_settings_tiles_default); if (tileList == null) { tileList = res.getString(R.string.quick_settings_tiles); if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList); } else { if (DEBUG) Log.d(TAG, "Loaded tile specs from setting: " + tileList); } final ArrayList<String> tiles = new ArrayList<String>(); boolean addedDefault = false; for (String tile : tileList.split(",")) { tile = tile.trim(); if (tile.isEmpty()) continue; if (tile.equals("default")) { if (!addedDefault) { tiles.addAll(Arrays.asList(defaultTileList.split(","))); addedDefault = true; } } else { tiles.add(tile); } } return tiles; }
只有配置了才可能会显示wifi,bt等这些图标,这些图标都对应着一个实体类,这些类都继承自QSTile<QSTile.BooleanState>,实际上是否显示都有这些实体类来决定的。比如flashlight对应着FlashlightTile.java,在handleUpdateState中会实时更新控件的状态,会根据mFlashlightController.isAvailable()的值,来决定是否显示闪光灯<!-- The default tiles to display in QuickSettings --> <string name="quick_settings_tiles_default" translatable="false"> wifi,bt,inversion,dnd,cell,airplane,rotation,flashlight,location,cast,hotspot </string>
其中反色和热点两个图标会根据是不是最近使用过mUsageTracker.isRecentlyUsed(),来动态显示或隐藏@Override protected void handleUpdateState(BooleanState state, Object arg) { state.visible = mFlashlightController.isAvailable(); state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label); if (arg instanceof UserBoolean) { boolean value = ((UserBoolean) arg).value; if (value == state.value) { return; } state.value = value; } else { state.value = mFlashlightController.isEnabled(); } final AnimationIcon icon = state.value ? mEnable : mDisable; icon.setAllowAnimation(arg instanceof UserBoolean && ((UserBoolean) arg).userInitiated); state.icon = icon; int onOrOffId = state.value ? R.string.accessibility_quick_settings_flashlight_on : R.string.accessibility_quick_settings_flashlight_off; state.contentDescription = mContext.getString(onOrOffId); }
HotspotTile.java @Override protected void handleUpdateState(BooleanState state, Object arg) { state.visible = mController.isHotspotSupported() && mUsageTracker.isRecentlyUsed(); state.label = mContext.getString(R.string.quick_settings_hotspot_label); if (arg instanceof Boolean) { state.value = (boolean) arg; } else { state.value = mController.isHotspotEnabled(); } state.icon = state.visible && state.value ? mEnable : mDisable; }
其它几个控件就不在详细说明了,可以对着第一张图看。另外提一点就是自己最近遇到的问题,亮度条不能拖动调节,只能点击调节。如果遇到通知栏里的控件不能click或move事件没有效果,可以在NotificationPanelView.java和PanelView.java的onInterceptTouchEvent和onTouchEvent中添加log,ColorInversionTile.java @Override protected void handleUpdateState(BooleanState state, Object arg) { final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue(); final boolean enabled = value != 0; state.visible = enabled || mUsageTracker.isRecentlyUsed(); state.value = enabled; state.label = mContext.getString(R.string.quick_settings_inversion_label); state.icon = enabled ? mEnable : mDisable; }
看是不是有对应event事件被拦截了,导致控件本身的event事件没有响应到。