热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

AndroidNSystemUI-状态栏

手机中状态栏主要用来显示电池电量信息、时间、信号格数、系统图标(闹钟)、通知图标,我们先来看看手机statusbar的界面今天我们先来简单

手机中状态栏主要用来显示电池电量信息、时间、信号格数、系统图标(闹钟)、通知图标,我们先来看看手机statusbar的界面

今天我们先来简单介绍下这个界面是怎么显示出来,考虑到放到一起写,文章就有点太长了,后续会对信号格图标显示、通知图标、系统图标这几个复杂点的一一介绍
从上图中我们基本可以看出,从左到右基本上是通知图标显示区域、系统图标显示区域,系统图标区域里主要包括wifi、飞行模式、闹钟、耳机、信号格显示、数据业务上下行箭头、电池电量图标、时间图标,具体布局如下:

//frameworks/base/packages/SystemUI/res/layout/status_bar.xml
<com.android.systemui.statusbar.phone.PhoneStatusBarView xmlns:android&#61;"http://schemas.android.com/apk/res/android"xmlns:systemui&#61;"http://schemas.android.com/apk/res/com.android.systemui"android:id&#61;"&#64;&#43;id/status_bar"android:background&#61;"&#64;drawable/system_bar_background"android:orientation&#61;"vertical"android:focusable&#61;"false"android:descendantFocusability&#61;"afterDescendants">..."&#64;&#43;id/status_bar_contents"android:layout_width&#61;"match_parent"android:layout_height&#61;"match_parent"android:paddingStart&#61;"6dp"android:paddingEnd&#61;"8dp"android:orientation&#61;"horizontal"><com.android.systemui.statusbar.AlphaOptimizedFrameLayoutandroid:id&#61;"&#64;&#43;id/notification_icon_area"android:layout_width&#61;"0dip"android:layout_height&#61;"match_parent"android:layout_weight&#61;"1"android:orientation&#61;"horizontal" /><com.android.keyguard.AlphaOptimizedLinearLayout android:id&#61;"&#64;&#43;id/system_icon_area"android:layout_width&#61;"wrap_content"android:layout_height&#61;"match_parent"android:orientation&#61;"horizontal">"&#64;layout/system_icons" /><com.android.systemui.statusbar.policy.Clockandroid:id&#61;"&#64;&#43;id/clock"android:textAppearance&#61;"&#64;style/TextAppearance.StatusBar.Clock"android:layout_width&#61;"wrap_content"android:layout_height&#61;"match_parent"android:singleLine&#61;"true"android:paddingStart&#61;"&#64;dimen/status_bar_clock_starting_padding"android:paddingEnd&#61;"&#64;dimen/status_bar_clock_end_padding"android:gravity&#61;"center_vertical|start"/>com.android.keyguard.AlphaOptimizedLinearLayout>... com.android.systemui.statusbar.phone.PhoneStatusBarView>

如果有通知图标相关问题&#xff0c;基本上就可以根据notification_icon_area这个id去跟踪就行&#xff0c;而系统图标区域还有一个比较重要的布局文件system_icons.xml

//frameworks/base/packages/SystemUI/res/layout/system_icons.xml
<LinearLayout xmlns:android&#61;"http://schemas.android.com/apk/res/android"android:id&#61;"&#64;&#43;id/system_icons"android:layout_width&#61;"wrap_content"android:layout_height&#61;"match_parent"android:gravity&#61;"center_vertical"><com.android.keyguard.AlphaOptimizedLinearLayout android:id&#61;"&#64;&#43;id/statusIcons"android:layout_width&#61;"wrap_content"android:layout_height&#61;"match_parent"android:gravity&#61;"center_vertical"android:orientation&#61;"horizontal"/><include layout&#61;"&#64;layout/signal_cluster_view"android:layout_width&#61;"wrap_content"android:layout_height&#61;"wrap_content"android:layout_marginStart&#61;"&#64;dimen/signal_cluster_margin_start"/><com.android.systemui.BatteryMeterView android:id&#61;"&#64;&#43;id/battery"android:layout_height&#61;"&#64;dimen/status_bar_battery_icon_height"android:layout_width&#61;"&#64;dimen/status_bar_battery_icon_width"android:layout_marginBottom&#61;"&#64;dimen/battery_margin_bottom"/>
LinearLayout>

这个里面的signal_cluster_view.xml主要是和sim卡相关的一些图标&#xff08;vpn、信号格数、网络状态、数据业务上下行、漫游等&#xff09;、以及wifi、飞行模式图标&#xff0c;BatteryMeterView主要用来显示电池电量信息的

//frameworks/base/packages/SystemUI/res/layout/signal_cluster_view.xml<com.android.systemui.statusbar.SignalClusterViewxmlns:android&#61;"http://schemas.android.com/apk/res/android"android:id&#61;"&#64;&#43;id/signal_cluster"android:layout_height&#61;"match_parent"android:layout_width&#61;"wrap_content"android:gravity&#61;"center_vertical"android:orientation&#61;"horizontal"android:paddingEnd&#61;"&#64;dimen/signal_cluster_battery_padding" >"&#64;&#43;id/vpn"android:layout_height&#61;"wrap_content"android:layout_width&#61;"wrap_content"android:paddingEnd&#61;"6dp"android:src&#61;"&#64;drawable/stat_sys_vpn_ic"/>"&#64;&#43;id/wifi_signal_spacer" android:layout_width&#61;"&#64;dimen/status_bar_wifi_signal_spacer_width"android:layout_height&#61;"4dp"android:visibility&#61;"gone"/>"&#64;&#43;id/mobile_signal_group"android:layout_height&#61;"wrap_content"android:layout_width&#61;"wrap_content">"&#64;&#43;id/wifi_airplane_spacer" android:layout_width&#61;"&#64;dimen/status_bar_airplane_spacer_width"android:layout_height&#61;"4dp"android:visibility&#61;"gone"/>"&#64;&#43;id/airplane"android:layout_height&#61;"wrap_content"android:layout_width&#61;"wrap_content"/>com.android.systemui.statusbar.SignalClusterView>

基本布局文件差不多介绍完了&#xff0c;下面画了个简单的图总结下

下面介绍完了布局&#xff0c;就来看看这些布局是在那儿被加载的&#xff0c;以及电池电量、和时间图标的具体显示流程
status_bar.xml文件是在super_status_bar.xml里被include的&#xff0c;而super_status_bar.xml的加载是在PhoneStatusBar.java里的makeStatusBarView中初始化的&#xff0c;如下

//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java// &#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;// Constructing the view// &#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;protected PhoneStatusBarView makeStatusBarView() {...inflateStatusBarWindow(context);mStatusBarWindow.setService(this);mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {&#64;Overridepublic boolean onTouch(View v, MotionEvent event) {checkUserAutohide(v, event);if (event.getAction() &#61;&#61; MotionEvent.ACTION_DOWN) {if (mExpandedVisible) {animateCollapsePanels();}}return mStatusBarWindow.onTouchEvent(event);}});mNotificationPanel &#61; (NotificationPanelView) mStatusBarWindow.findViewById(R.id.notification_panel);mNotificationPanel.setStatusBar(this);mNotificationPanel.setGroupManager(mGroupManager);mStatusBarView &#61; (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);mStatusBarView.setBar(this);mStatusBarView.setPanel(mNotificationPanel);// set the initial view visibilitysetAreThereNotifications();mBatteryController &#61; createBatteryController();mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {&#64;Overridepublic void onPowerSaveChanged(boolean isPowerSave) {mHandler.post(mCheckBarModes);if (mDozeServiceHost !&#61; null) {mDozeServiceHost.firePowerSaveChanged(isPowerSave);}}&#64;Overridepublic void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {// noop}});mNetworkController &#61; new NetworkControllerImpl(mContext, mHandlerThread.getLooper());mNetworkController.setUserSetupComplete(mUserSetup);mHotspotController &#61; new HotspotControllerImpl(mContext);mBluetoothController &#61; new BluetoothControllerImpl(mContext, mHandlerThread.getLooper());mSecurityController &#61; new SecurityControllerImpl(mContext);if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {mRotationLockController &#61; new RotationLockControllerImpl(mContext);}initSignalCluster(mStatusBarView);initSignalCluster(mKeyguardStatusBar);initEmergencyCryptkeeperText();mCarrierLabel &#61; (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);...((BatteryMeterView) mStatusBarView.findViewById(R.id.battery)).setBatteryController(mBatteryController);mKeyguardStatusBar.setBatteryController(mBatteryController);...return mStatusBarView;}

基本上状态里显示的入口就在这&#xff0c;下面主要看看电池电量、和时间图标&#xff0c;从前面布局文件可知&#xff0c;显示时间对应的view为Clock.java&#xff0c;电池电量的为BatteryMeterView.java
时间的更新主要是通过监听‘android.intent.action.TIME_TICK’广播

//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.javapublic class Clock extends TextView implements DemoMode, Tunable {...&#64;Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();if (!mAttached) {mAttached &#61; true;IntentFilter filter &#61; new IntentFilter();filter.addAction(Intent.ACTION_TIME_TICK);//监听广播filter.addAction(Intent.ACTION_TIME_CHANGED);filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);filter.addAction(Intent.ACTION_USER_SWITCHED);getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter,null, getHandler());TunerService.get(getContext()).addTunable(this, CLOCK_SECONDS,StatusBarIconController.ICON_BLACKLIST);}// NOTE: It&#39;s safe to do these after registering the receiver since the receiver always runs// in the main thread, therefore the receiver can&#39;t run before this method returns.// The time zone may have changed while the receiver wasn&#39;t registered, so update the TimemCalendar &#61; Calendar.getInstance(TimeZone.getDefault());// Make sure we update to the current timeupdateClock();updateShowSeconds();}...private final BroadcastReceiver mIntentReceiver &#61; new BroadcastReceiver() {&#64;Overridepublic void onReceive(Context context, Intent intent) {String action &#61; intent.getAction();if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {String tz &#61; intent.getStringExtra("time-zone");mCalendar &#61; Calendar.getInstance(TimeZone.getTimeZone(tz));if (mClockFormat !&#61; null) {mClockFormat.setTimeZone(mCalendar.getTimeZone());}} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {final Locale newLocale &#61; getResources().getConfiguration().locale;if (! newLocale.equals(mLocale)) {mLocale &#61; newLocale;mClockFormatString &#61; ""; // force refresh}}updateClock();//更新时间}};final void updateClock() {if (mDemoMode) return;mCalendar.setTimeInMillis(System.currentTimeMillis());setText(getSmallTime());setContentDescription(mContentDescriptionFormat.format(mCalendar.getTime()));}//格式化时间private final CharSequence getSmallTime() {Context context &#61; getContext();boolean is24 &#61; DateFormat.is24HourFormat(context, ActivityManager.getCurrentUser());LocaleData d &#61; LocaleData.get(context.getResources().getConfiguration().locale);final char MAGIC1 &#61; &#39;\uEF00&#39;;final char MAGIC2 &#61; &#39;\uEF01&#39;;SimpleDateFormat sdf;String format &#61; mShowSeconds? is24 ? d.timeFormat_Hms : d.timeFormat_hms: is24 ? d.timeFormat_Hm : d.timeFormat_hm;if (!format.equals(mClockFormatString)) {mContentDescriptionFormat &#61; new SimpleDateFormat(format);/** Search for an unquoted "a" in the format string, so we can* add dummy characters around it to let us find it again after* formatting and change its size.*/if (mAmPmStyle !&#61; AM_PM_STYLE_NORMAL) {int a &#61; -1;boolean quoted &#61; false;for (int i &#61; 0; i char c &#61; format.charAt(i);if (c &#61;&#61; &#39;\&#39;&#39;) {quoted &#61; !quoted;}if (!quoted && c &#61;&#61; &#39;a&#39;) {a &#61; i;break;}}if (a >&#61; 0) {// Move a back so any whitespace before AM/PM is also in the alternate size.final int b &#61; a;while (a > 0 && Character.isWhitespace(format.charAt(a-1))) {a--;}format &#61; format.substring(0, a) &#43; MAGIC1 &#43; format.substring(a, b)&#43; "a" &#43; MAGIC2 &#43; format.substring(b &#43; 1);}}mClockFormat &#61; sdf &#61; new SimpleDateFormat(format);mClockFormatString &#61; format;} else {sdf &#61; mClockFormat;}String result &#61; sdf.format(mCalendar.getTime());if (mAmPmStyle !&#61; AM_PM_STYLE_NORMAL) {int magic1 &#61; result.indexOf(MAGIC1);int magic2 &#61; result.indexOf(MAGIC2);if (magic1 >&#61; 0 && magic2 > magic1) {SpannableStringBuilder formatted &#61; new SpannableStringBuilder(result);if (mAmPmStyle &#61;&#61; AM_PM_STYLE_GONE) {formatted.delete(magic1, magic2&#43;1);} else {if (mAmPmStyle &#61;&#61; AM_PM_STYLE_SMALL) {CharacterStyle style &#61; new RelativeSizeSpan(0.7f);formatted.setSpan(style, magic1, magic2,Spannable.SPAN_EXCLUSIVE_INCLUSIVE);}formatted.delete(magic2, magic2 &#43; 1);formatted.delete(magic1, magic1 &#43; 1);}return formatted;}}...return result;}
}

电池电量图标这个和时钟差不多&#xff0c;主要是BatteryMeterView&#xff08;ImageView的子类&#xff09;和BatteryMeterDrawable&#xff08;Drawable的子类&#xff09;这两个类&#xff0c;其中BatteryMeterView负责view显示&#xff0c;BatteryMeterDrawable负责图标内容更新&#xff0c;并且这两个类都实现了BatteryController.BatteryStateChangeCallback这个接口

public interface BatteryController extends DemoMode {.../*** A listener that will be notified whenever a change in battery level or power save mode* has occurred.*/interface BatteryStateChangeCallback {void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging);void onPowerSaveChanged(boolean isPowerSave);}
}

frameworks/base/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.javapublic class BatteryMeterDrawable extends Drawable implementsBatteryController.BatteryStateChangeCallback {...&#64;Overridepublic void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {mLevel &#61; level;mPluggedIn &#61; pluggedIn;postInvalidate();//更新图标}&#64;Overridepublic void draw(Canvas c) {...}
}

onBatteryLevelChanged是在BatteryControllerImpl.java里被调用的

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.javapublic class BatteryControllerImpl extends BroadcastReceiver implements BatteryController {...public BatteryControllerImpl(Context context) {mContext &#61; context;mHandler &#61; new Handler();mPowerManager &#61; (PowerManager) context.getSystemService(Context.POWER_SERVICE);registerReceiver();updatePowerSave();}private void registerReceiver() {IntentFilter filter &#61; new IntentFilter();filter.addAction(Intent.ACTION_BATTERY_CHANGED); //注册电量改变的广播filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);filter.addAction(ACTION_LEVEL_TEST);mContext.registerReceiver(this, filter);}&#64;Overridepublic void onReceive(final Context context, Intent intent) {final String action &#61; intent.getAction();if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {if (mTestmode && !intent.getBooleanExtra("testmode", false)) return;...fireBatteryLevelChanged();} }...protected void fireBatteryLevelChanged() {synchronized (mChangeCallbacks) {final int N &#61; mChangeCallbacks.size();for (int i &#61; 0; i }

后续会对信号格图标显示、通知图标、系统图标都单独分析介绍&#xff0c;如有错误&#xff0c;麻烦留言指出。


推荐阅读
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有