今天来看一下android 手机的状态栏的源码流程分析。

相关类

frameworks\base\services\java\com\android\server\SystemServer.java
frameworks\base\packages\SystemUI\src\com\android\systemui\SystemUIService.java
frameworks\base\packages\SystemUI\src\com\android\systemui\SystemUIApplication.java
frameworks\base\packages\SystemUI\res\values\config.xml
systemui\app\src\main\java\com\android\systemui\statusbar\phone\StatusBar.java
app\src\main\java\com\android\systemui\statusbar\phone\CollapsedStatusBarFragment
app\src\main\java\com\android\systemui\statusbar\phone\StatusBarWindowManager
app\src\main\java\com\android\systemui\statusbar\phone\PhoneStatusBarPolicy.java
settingslib\src\main\java\com\android\settingslib\graph\BatteryMeterDrawableBase

1.systemUI的启动

frameworks\base\services\java\com\android\server\SystemServer.javastatic final void startSystemUi(Context context, WindowManagerService windowManager) {Intent intent = new Intent();intent.setComponent(new ComponentName("com.android.systemui","com.android.systemui.SystemUIService"));intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);//Slog.d(TAG, "Starting service: " + intent);context.startServiceAsUser(intent, UserHandle.SYSTEM);windowManager.onSystemUiStarted();}
frameworks\base\packages\SystemUI\src\com\android\systemui\SystemUIService.java@Overridepublic void onCreate() {super.onCreate();((SystemUIApplication) getApplication()).startServicesIfNeeded();// 在这里启动// For debugging RescuePartyif (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) {throw new RuntimeException();}if (Build.IS_DEBUGGABLE) {// b/71353150 - looking for leaked binder proxiesBinderInternal.nSetBinderProxyCountEnabled(true);BinderInternal.nSetBinderProxyCountWatermarks(1000,900);BinderInternal.setBinderProxyCountCallback(new BinderInternal.BinderProxyLimitListener() {@Overridepublic void onLimitReached(int uid) {Slog.w(SystemUIApplication.TAG,"uid " + uid + " sent too many Binder proxies to uid "+ Process.myUid());}}, Dependency.get(Dependency.MAIN_HANDLER));}}
frameworks\base\packages\SystemUI\src\com\android\systemui\SystemUIApplication.javapublic void startServicesIfNeeded() {String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);startServicesIfNeeded(names);}//这里可以看到有一个array的数组需要启动    private void startServicesIfNeeded(String[] services) {if (mServicesStarted) {return;}mServices = new SystemUI[services.length];if (!mBootCompleted) {// check to see if maybe it was already completed long before we began// see ActivityManagerService.finishBooting()if ("1".equals(SystemProperties.get("sys.boot_completed"))) {mBootCompleted = true;if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");}}Log.v(TAG, "Starting SystemUI services for user " +Process.myUserHandle().getIdentifier() + ".");TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",Trace.TRACE_TAG_APP);log.traceBegin("StartServices");final int N = services.length;for (int i = 0; i < N; i++) {String clsName = services[i];if (DEBUG) Log.d(TAG, "loading: " + clsName);log.traceBegin("StartServices" + clsName);long ti = System.currentTimeMillis();Class cls;try {cls = Class.forName(clsName);mServices[i] = (SystemUI) cls.newInstance();} catch(ClassNotFoundException ex){throw new RuntimeException(ex);} catch (IllegalAccessException ex) {throw new RuntimeException(ex);} catch (InstantiationException ex) {throw new RuntimeException(ex);}mServices[i].mContext = this;mServices[i].mComponents = mComponents;if (DEBUG) Log.d(TAG, "running: " + mServices[i]);mServices[i].start();   // 每个都从这里启动log.traceEnd();// Warn if initialization of component takes too longti = System.currentTimeMillis() - ti;if (ti > 1000) {Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms");}if (mBootCompleted) {mServices[i].onBootCompleted();}}

这里其实大家不要被名称搞混了,这不是服务,而是继承systemUI的类。通过start方法去加载启动

frameworks\base\packages\SystemUI\res\values\config.xml
这里可以看到有这么多类需要启动加载。<!-- SystemUI Services: The classes of the stuff to start. --><string-array name="config_systemUIServiceComponents" translatable="false"><item>com.android.systemui.Dependency</item><item>com.android.systemui.util.NotificationChannels</item><item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item><item>com.android.systemui.keyguard.KeyguardViewMediator</item><item>com.android.systemui.recents.Recents</item><item>com.android.systemui.volume.VolumeUI</item><item>com.android.systemui.stackdivider.Divider</item><item>com.android.systemui.SystemBars</item><item>com.android.systemui.usb.StorageNotification</item><item>com.android.systemui.power.PowerUI</item><item>com.android.systemui.media.RingtonePlayer</item><item>com.android.systemui.keyboard.KeyboardUI</item><item>com.android.systemui.pip.PipUI</item><item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item><item>@string/config_systemUIVendorServiceComponent</item><item>com.android.systemui.util.leak.GarbageMonitor$Service</item><item>com.android.systemui.LatencyTester</item><item>com.android.systemui.globalactions.GlobalActionsComponent</item><item>com.android.systemui.ScreenDecorations</item><item>com.android.systemui.fingerprint.FingerprintDialogImpl</item><item>com.android.systemui.SliceBroadcastRelayHandler</item></string-array>

这里只分析com.android.systemui.SystemBars的流程

systemui\app\src\main\java\com\android\systemui\SystemBars@Overridepublic void start() {if (DEBUG) Log.d(TAG, "start");createStatusBrFromConfig();}private void createStatusBarFromConfig() {if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");final String clsName = mContext.getString(R.string.config_statusBarComponent);//    <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>// 这里这个采用反射if (clsName == null || clsName.length() == 0) {throw andLog("No status bar component configured", null);}Class<?> cls = null;try {cls = mContext.getClassLoader().loadClass(clsName);} catch (Throwable t) {throw andLog("Error loading status bar component: " + clsName, t);}try {mStatusBar = (SystemUI) cls.newInstance();} catch (Throwable t) {throw andLog("Error creating status bar component: " + clsName, t);}mStatusBar.mContext = mContext;mStatusBar.mComponents = mComponents;mStatusBar.start();if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());}
systemui\app\src\main\java\com\android\systemui\statusbar\phone\StatusBar.java@Overridepublic void start() {...  省略部分代码,只保留文章相关的createAndAddWindows() ;...}private void addStatusBarWindow() {makeStatusBarView();// 调用此处方法mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);mRemoteInputManager.setUpWithPresenter(this, mEntryManager, this,new RemoteInputController.Delegate() {public void setRemoteInputActive(NotificationData.Entry entry,boolean remoteInputActive) {mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);entry.row.notifyHeightChanged(true /* needsAnimation */);updateFooter();}public void lockScrollTo(NotificationData.Entry entry) {mStackScroller.lockScrollTo(entry.row);}public void requestDisallowLongPressAndDismiss() {mStackScroller.requestDisallowLongPress();mStackScroller.requestDisallowDismiss();}});mRemoteInputManager.getController().addCallback(mStatusBarWindowManager);mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); // 这个方法里面其实就可以看出是个window}protected void makeStatusBarView() {inflateStatusBarWindow(context);  // 加载布局,下面分析FragmentHostManager.get(mStatusBarWindow).addTagListener(CollapsedStatusBarFragment.TAG, new FragmentHostManager.FragmentListener() {@Overridepublic void onFragmentViewCreated(String tag, Fragment fragment) {CollapsedStatusBarFragment statusBarFragment =(CollapsedStatusBarFragment) fragment;//这里这个fragment 才是我们的状态栏的fragment statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);mStatusBarView = (PhoneStatusBarView) fragment.getView();mStatusBarView.setBar(StatusBar.this);mStatusBarView.setPanel(mNotificationPanel);mStatusBarView.setScrimController(mScrimController);mStatusBarView.setBouncerShowing(mBouncerShowing);if (mHeadsUpAppearanceController != null) {// This view is being recreated, let's destroy the old onemHeadsUpAppearanceController.destroy();}mHeadsUpAppearanceController = new HeadsUpAppearanceController(mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);StatusBar.this.setAreThereNotifications();StatusBar.this.checkBarModes();/// M: add for plmn display feature @{StatusBar.this.attachPlmnPlugin();///@}}}).getFragmentManager().beginTransaction().replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),CollapsedStatusBarFragment.TAG)  // 这可以看到这里也是在加载这个fragment 状态栏.commit();mIconController = Dependency.get(StatusBarIconController.class);
}// 加载布局protected void inflateStatusBarWindow(Context context) {mStatusBarWindow = (StatusBarWindowView) View.inflate(context,R.layout.super_status_bar, null);}
app\src\main\java\com\android\systemui\statusbar\phone\CollapsedStatusBarFragment@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,Bundle savedInstanceState) {return inflater.inflate(R.layout.status_bar, container, false);// 这个布局文件就是我们手机的状态栏}
app\src\main\java\com\android\systemui\statusbar\phone\StatusBarWindowManagerpublic void add(View statusBarView, int barHeight) {// Now that the status bar window encompasses the sliding panel and its// translucent backdrop, the entire thing is made TRANSLUCENT and is// hardware-accelerated.mLp = new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,barHeight,WindowManager.LayoutParams.TYPE_STATUS_BAR,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,PixelFormat.TRANSLUCENT);mLp.token = new Binder();mLp.gravity = Gravity.TOP;mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;mLp.setTitle("StatusBar");mLp.packageName = mContext.getPackageName();mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;mStatusBarView = statusBarView;mBarHeight = barHeight;mWindowManager.addView(mStatusBarView, mLp);  // 可以看到这个其实也是一个windowmLpChanged = new WindowManager.LayoutParams();mLpChanged.copyFrom(mLp);}

到此,状态栏就启动了。接下来说说状态栏的加载以及布局

2.状态栏布局

super_status_bar.xml<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"><com.android.systemui.statusbar.BackDropViewandroid:id="@+id/backdrop"android:layout_width="match_parent"android:layout_height="match_parent"android:visibility="gone"sysui:ignoreRightInset="true"><ImageViewandroid:id="@+id/backdrop_back"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop" /><ImageViewandroid:id="@+id/backdrop_front"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop"android:visibility="invisible" /></com.android.systemui.statusbar.BackDropView><com.android.systemui.statusbar.ScrimViewandroid:id="@+id/scrim_behind"android:layout_width="match_parent"android:layout_height="match_parent"android:importantForAccessibility="no"sysui:ignoreRightInset="true" /><FrameLayoutandroid:id="@+id/status_bar_container"  // 这个fragment就是我们之前说的那个状态栏的fragmentandroid:layout_width="match_parent"android:layout_height="wrap_content" /><ViewStubandroid:id="@+id/fullscreen_user_switcher_stub"android:layout_width="match_parent"android:layout_height="match_parent"android:layout="@layout/car_fullscreen_user_switcher" /><includelayout="@layout/status_bar_expanded"android:layout_width="match_parent"android:layout_height="match_parent"android:visibility="invisible" /><include layout="@layout/brightness_mirror" /><com.android.systemui.statusbar.ScrimViewandroid:id="@+id/scrim_in_front"android:layout_width="match_parent"android:layout_height="match_parent"android:importantForAccessibility="no"sysui:ignoreRightInset="true" /></com.android.systemui.statusbar.phone.StatusBarWindowView>

// 真正状态栏的显示

status_bar.xml<com.android.systemui.statusbar.phone.PhoneStatusBarViewxmlns:android="http://schemas.android.com/apk/res/android"xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"android:layout_width="match_parent"android:layout_height="@dimen/status_bar_height"android:id="@+id/status_bar"android:background="@drawable/system_bar_background"android:orientation="vertical"android:focusable="false"android:descendantFocusability="afterDescendants"android:accessibilityPaneTitle="@string/status_bar"><ImageViewandroid:id="@+id/notification_lights_out"android:layout_width="@dimen/status_bar_icon_size"android:layout_height="match_parent"android:paddingStart="@dimen/status_bar_padding_start"android:paddingBottom="2dip"android:src="@drawable/ic_sysbar_lights_out_dot_small"android:scaleType="center"android:visibility="gone"/><LinearLayout android:id="@+id/status_bar_contents"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingStart="@dimen/status_bar_padding_start"android:paddingEnd="@dimen/status_bar_padding_end"android:orientation="horizontal"><ViewStubandroid:id="@+id/operator_name"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout="@layout/operator_name" /><FrameLayoutandroid:layout_height="match_parent"android:layout_width="0dp"android:layout_weight="1"><include layout="@layout/heads_up_status_bar_layout" /><!-- The alpha of the left side is controlled by PhoneStatusBarTransitions, and theindividual views are controlled by StatusBarManager disable flags DISABLE_CLOCK andDISABLE_NOTIFICATION_ICONS, respectively --><LinearLayoutandroid:id="@+id/status_bar_left_side"android:layout_height="match_parent"android:layout_width="match_parent"android:clipChildren="false">// 时间显示<com.android.systemui.statusbar.policy.Clockandroid:id="@+id/clock"android:layout_width="wrap_content"android:layout_height="match_parent"android:textAppearance="@style/TextAppearance.StatusBar.Clock"android:singleLine="true"android:paddingStart="@dimen/status_bar_left_clock_starting_padding"android:paddingEnd="@dimen/status_bar_left_clock_end_padding"android:gravity="center_vertical|start"/>// 通知显示区域<com.android.systemui.statusbar.AlphaOptimizedFrameLayoutandroid:id="@+id/notification_icon_area"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:orientation="horizontal"android:clipChildren="false"/></LinearLayout></FrameLayout><!-- Space should cover the notch (if it exists) and let other views lay out around it --><android.widget.Spaceandroid:id="@+id/cutout_space_view"android:layout_width="0dp"android:layout_height="match_parent"android:gravity="center_horizontal|center_vertical"/>// 系统icon<com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:orientation="horizontal"android:gravity="center_vertical|end"><include layout="@layout/system_icons" /></com.android.keyguard.AlphaOptimizedLinearLayout></LinearLayout><ViewStubandroid:id="@+id/emergency_cryptkeeper_text"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout="@layout/emergency_cryptkeeper_text"/></com.android.systemui.statusbar.phone.PhoneStatusBarView>
system_icons.xml
这里主要就是显示一些系统的图标,包括闹钟,wifi。蓝牙等,系统中这些图片都是矢量图
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/system_icons"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center_vertical"><com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"android:layout_width="0dp"android:layout_weight="1"android:layout_height="match_parent"android:paddingEnd="@dimen/signal_cluster_battery_padding"android:gravity="center_vertical"android:orientation="horizontal"/>// 电池<com.android.systemui.BatteryMeterView android:id="@+id/battery"android:layout_height="match_parent"android:layout_width="wrap_content"android:clipToPadding="false"android:clipChildren="false" /></LinearLayout>

总结下来状态栏如下所示

接下来看一下系统icon 栏是如何显示的

app\src\main\java\com\android\systemui\statusbar\phone\PhoneStatusBarPolicy.java
系统icon 栏的初始化和更新都是在这个类里面去实现的@VisibleForTestingpublic PhoneStatusBarPolicy(Context context, StatusBarIconController iconController) {mContext = context;mIconController = iconController;mCast = Dependency.get(CastController.class);mHotspot = Dependency.get(HotspotController.class);mBluetooth = Dependency.get(BluetoothController.class);mNextAlarmController = Dependency.get(NextAlarmController.class);mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);mUserInfoController = Dependency.get(UserInfoController.class);mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);mRotationLockController = Dependency.get(RotationLockController.class);mDataSaver = Dependency.get(DataSaverController.class);mZenController = Dependency.get(ZenModeController.class);mProvisionedController = Dependency.get(DeviceProvisionedController.class);mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);mLocationController = Dependency.get(LocationController.class);mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);mSlotBluetooth = context.getString(com.android.internal.R.string.status_bar_bluetooth);mSlotTty = context.getString(com.android.internal.R.string.status_bar_tty);mSlotZen = context.getString(com.android.internal.R.string.status_bar_zen);mSlotVolume = context.getString(com.android.internal.R.string.status_bar_volume);mSlotAlarmClock = context.getString(com.android.internal.R.string.status_bar_alarm_clock);mSlotManagedProfile = context.getString(com.android.internal.R.string.status_bar_managed_profile);mSlotRotate = context.getString(com.android.internal.R.string.status_bar_rotate);mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);// systemicon 的初始化// listen for broadcastsIntentFilter filter = new IntentFilter();/// M: [Multi-User] Will register this action using special receiver.//filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);filter.addAction(AudioManager.ACTION_HEADSET_PLUG);filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);/// M: [Multi-User] Add user switched action for updating possible alarm icon.filter.addAction(Intent.ACTION_USER_SWITCHED);filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);// 广播的注册/// M: Add icon for embms session.filter.addAction(TelephonyIntents.ACTION_EMBMS_SESSION_STATUS_CHANGED);filter.addAction(SMART_DEVICE_BROADCAST);mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);/// M: [Multi-User] Register Alarm intent by userregisterAlarmClockChanged(UserHandle.USER_OWNER, false);// Alarm clockmIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);mIconController.setIconVisibility(mSlotAlarmClock, false);接受到广播后去更新iconprivate void updateAlarm() {final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;int zen = mZenController.getZen();final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;mIconController.setIcon(mSlotAlarmClock, zenNone ? R.drawable.stat_sys_alarm_dim: R.drawable.stat_sys_alarm, buildAlarmContentDescription());mIconController.setIconVisibility(mSlotAlarmClock, mCurrentUserSetup && hasAlarm);}// 广播接收器
@VisibleForTesting/*private*/ BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();switch (action) {case AudioManager.RINGER_MODE_CHANGED_ACTION:case AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION:updateVolumeZen();break;case TelephonyIntents.ACTION_SIM_STATE_CHANGED:// Avoid rebroadcast because SysUI is direct boot aware.if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK,false)) {break;}updateSimState(intent);break;case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED:updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,TelecomManager.TTY_MODE_OFF));break;case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:case Intent.ACTION_MANAGED_PROFILE_REMOVED:updateManagedProfile();break;case AudioManager.ACTION_HEADSET_PLUG:updateHeadsetPlug(intent);break;case Intent.ACTION_USER_SWITCHED:updateAlarm();int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);registerAlarmClockChanged(newUserId, true);break;case TelephonyIntents.ACTION_EMBMS_SESSION_STATUS_CHANGED:updateEmbmsState(intent);break;case SMART_DEVICE_BROADCAST:updateSmartDevice(intent);break;}}};

所以如果,需要在systemicon 栏客制化加入一个其他的icon,则可以在这个类里面仿照做修改

4. 电池电量的更新

从上面的xml 中可以看到电池电量的UI是在BatteryMeterView.java 类中
而真正去绘制电视的UI显示则是在settingslib的BatteryMeterDrawableBase.java 类中

settingslib\src\main\java\com\android\settingslib\graph\BatteryMeterDrawableBase// 初始化画笔public BatteryMeterDrawableBase(Context context, int frameColor) {mContext = context;final Resources res = context.getResources();TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);final int N = levels.length();mColors = new int[2 * N];for (int i = 0; i < N; i++) {mColors[2 * i] = levels.getInt(i, 0);if (colors.getType(i) == TypedValue.TYPE_ATTRIBUTE) {mColors[2 * i + 1] = Utils.getColorAttr(context, colors.getThemeAttributeId(i, 0));} else {mColors[2 * i + 1] = colors.getColor(i, 0);}}levels.recycle();colors.recycle();mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);mCriticalLevel = mContext.getResources().getInteger(com.android.internal.R.integer.config_criticalBatteryWarningLevel);mButtonHeightFraction = context.getResources().getFraction(R.fraction.battery_button_height_fraction, 1, 1);mSubpixelSmoothingLeft = context.getResources().getFraction(R.fraction.battery_subpixel_smoothing_left, 1, 1);mSubpixelSmoothingRight = context.getResources().getFraction(R.fraction.battery_subpixel_smoothing_right, 1, 1);mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);mFramePaint.setColor(frameColor);mFramePaint.setDither(true);mFramePaint.setStrokeWidth(0);mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mBatteryPaint.setDither(true);mBatteryPaint.setStrokeWidth(0);mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD);mTextPaint.setTypeface(font);mTextPaint.setTextAlign(Paint.Align.CENTER);mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);font = Typeface.create("sans-serif", Typeface.BOLD);mWarningTextPaint.setTypeface(font);mWarningTextPaint.setTextAlign(Paint.Align.CENTER);if (mColors.length > 1) {mWarningTextPaint.setColor(mColors[1]);}mChargeColor = Utils.getDefaultColor(mContext, android.R.color.holo_red_dark);mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mBoltPaint.setColor(Utils.getDefaultColor(mContext,  android.R.color.holo_green_dark));mBoltPoints = loadPoints(res, R.array.batterymeter_bolt_points);mPlusPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPlusPaint.setColor(Utils.getDefaultColor(mContext, R.color.batterymeter_plus_color));mPlusPoints = loadPoints(res, R.array.batterymeter_plus_points);mPowersavePaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPowersavePaint.setColor(mPlusPaint.getColor());mPowersavePaint.setStyle(Style.STROKE);mPowersavePaint.setStrokeWidth(context.getResources().getDimensionPixelSize(R.dimen.battery_powersave_outline_thickness));mIntrinsicWidth = context.getResources().getDimensionPixelSize(R.dimen.battery_width);mIntrinsicHeight = context.getResources().getDimensionPixelSize(R.dimen.battery_height);}// 画电池@Overridepublic void draw(Canvas c) {final int level = mLevel;final Rect bounds = getBounds();if (level == -1) return;float drawFrac = (float) level / 100f;final int height = mHeight;final int width = (int) (getAspectRatio() * mHeight);final int px = (mWidth - width) / 2;final int buttonHeight = Math.round(height * mButtonHeightFraction);final int left = mPadding.left + bounds.left;final int top = bounds.bottom - mPadding.bottom - height;mFrame.set(left, top, width + left, height + top);mFrame.offset(px, 0);// button-frame: area above the battery bodymButtonFrame.set(mFrame.left + Math.round(width * 0.28f),mFrame.top,mFrame.right - Math.round(width * 0.28f),mFrame.top + buttonHeight);// frame: battery body areamFrame.top += buttonHeight;// set the battery charging colormBatteryPaint.setColor(batteryColorForLevel(level));if (level >= FULL) {drawFrac = 1f;} else if (level <= mCriticalLevel) {drawFrac = 0f;}final float levelTop = drawFrac == 1f ? mButtonFrame.top: (mFrame.top + (mFrame.height() * (1f - drawFrac)));// define the battery shapemShapePath.reset();mOutlinePath.reset();final float radius = getRadiusRatio() * (mFrame.height() + buttonHeight);mShapePath.setFillType(FillType.WINDING);mShapePath.addRoundRect(mFrame, radius, radius, Direction.CW);mShapePath.addRect(mButtonFrame, Direction.CW);mOutlinePath.addRoundRect(mFrame, radius, radius, Direction.CW);Path p = new Path();p.addRect(mButtonFrame, Direction.CW);mOutlinePath.op(p, Op.XOR);if (mCharging) {// define the bolt shape// Shift right by 1px for maximal bolt-goodnessfinal float bl = mFrame.left + mFrame.width() / 4f + 1;final float bt = mFrame.top + mFrame.height() / 6f;final float br = mFrame.right - mFrame.width() / 4f + 1;final float bb = mFrame.bottom - mFrame.height() / 10f;if (mBoltFrame.left != bl || mBoltFrame.top != bt|| mBoltFrame.right != br || mBoltFrame.bottom != bb) {mBoltFrame.set(bl, bt, br, bb);mBoltPath.reset();mBoltPath.moveTo(mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());for (int i = 2; i < mBoltPoints.length; i += 2) {mBoltPath.lineTo(mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());}mBoltPath.lineTo(mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());}float boltPct = (mBoltFrame.bottom - levelTop) / (mBoltFrame.bottom - mBoltFrame.top);boltPct = Math.min(Math.max(boltPct, 0), 1);if (boltPct <= BOLT_LEVEL_THRESHOLD) {// draw the bolt if opaquec.drawPath(mBoltPath, mBoltPaint);} else {// otherwise cut the bolt out of the overall shapemShapePath.op(mBoltPath, Path.Op.DIFFERENCE);}} else if (mPowerSaveEnabled) {// define the plus shapefinal float pw = mFrame.width() * 2 / 3;final float pl = mFrame.left + (mFrame.width() - pw) / 2;final float pt = mFrame.top + (mFrame.height() - pw) / 2;final float pr = mFrame.right - (mFrame.width() - pw) / 2;final float pb = mFrame.bottom - (mFrame.height() - pw) / 2;if (mPlusFrame.left != pl || mPlusFrame.top != pt|| mPlusFrame.right != pr || mPlusFrame.bottom != pb) {mPlusFrame.set(pl, pt, pr, pb);mPlusPath.reset();mPlusPath.moveTo(mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(),mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height());for (int i = 2; i < mPlusPoints.length; i += 2) {mPlusPath.lineTo(mPlusFrame.left + mPlusPoints[i] * mPlusFrame.width(),mPlusFrame.top + mPlusPoints[i + 1] * mPlusFrame.height());}mPlusPath.lineTo(mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(),mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height());}// Always cut out of the whole shape, and sometimes filled colorErrormShapePath.op(mPlusPath, Path.Op.DIFFERENCE);if (mPowerSaveAsColorError) {c.drawPath(mPlusPath, mPlusPaint);}}// compute percentage textboolean pctOpaque = false;float pctX = 0, pctY = 0;String pctText = null;if (!mCharging && !mPowerSaveEnabled && level > mCriticalLevel && mShowPercent) {mTextPaint.setColor(getColorForLevel(level));mTextPaint.setTextSize(height *(SINGLE_DIGIT_PERCENT ? 0.75f: (mLevel == 100 ? 0.38f : 0.5f)));mTextHeight = -mTextPaint.getFontMetrics().ascent;pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level / 10) : level);pctX = mWidth * 0.5f + left;pctY = (mHeight + mTextHeight) * 0.47f + top;pctOpaque = levelTop > pctY;if (!pctOpaque) {mTextPath.reset();mTextPaint.getTextPath(pctText, 0, pctText.length(), pctX, pctY, mTextPath);// cut the percentage text out of the overall shapemShapePath.op(mTextPath, Path.Op.DIFFERENCE);}}// draw the battery shape backgroundc.drawPath(mShapePath, mFramePaint);// draw the battery shape, clipped to charging levelmFrame.top = levelTop;c.save();c.clipRect(mFrame);c.drawPath(mShapePath, mBatteryPaint);c.restore();if (!mCharging && !mPowerSaveEnabled) {if (level <= mCriticalLevel) {// draw the warning textfinal float x = mWidth * 0.5f + left;final float y = (mHeight + mWarningTextHeight) * 0.48f + top;c.drawText(mWarningString, x, y, mWarningTextPaint);} else if (pctOpaque) {// draw the percentage textc.drawText(pctText, pctX, pctY, mTextPaint);}}// Draw the powersave outline lastif (!mCharging && mPowerSaveEnabled && mPowerSaveAsColorError) {c.drawPath(mOutlinePath, mPowersavePaint);}}

你真的了解你手机的状态栏吗?相关推荐

  1. 华为手机获取状态栏高度是错误的_你的华为手机状态栏有HD图标吗?这又代表着什么?看完你就懂了...

    平时比较细心的朋友应该都发现了,华为手机的状态栏(信号栏)有个"HD"图标.有些朋友看到这个图标,可能会担心自己手机是不是出问题了?会不会对手机有什么影响?能不能把它关闭?下面就给 ...

  2. 纯CSS3制作逼真的iphone 6手机模型

    注意这里我只是转载过来的(来自 jQuery之家 的分享),然后加以整理.嘻嘻~ 纯CSS3制作逼真的iphone 6手机模型 iphone.html <div class="ipho ...

  3. ios keychain 不被清理_清理垃圾,恢复出厂设置真的能让手机变快吗?

    支付宝每天领红包 打开支付宝首页, 搜索 "9479339",领取支付宝红包,最高99元 搜索"体验金71127639"领百万体验金,收益归你 搜索" ...

  4. android的相机自动曝光,你真的会用手机拍照?一篇文章了解手动曝光!

    只要手机摄像头好,手机也能拍出单反的效果?亲,您想多了!拍照绝对是智能手机最频繁的应用之一,但你真的会用手机拍照吗?从最基本的光线应用到画面构图,小编将为大家奉上系列专题,本期,就让我们从手动曝光谈起 ...

  5. 苹果的全面屏真的可以引领手机领域吗?

    苹果手机的节奏终于在2017年被打破,跳过了iPhone 7s直接迈进苹果8时代,并推出了搭载全面屏的iPhone X.正如很多果粉所说,苹果终于彻底洗涮掉了乔布斯的印记,正式迈入库克时代. 在苹果手 ...

  6. 扩展坞可以把手机投到显示器吗_你真的会用手机吗?这款ORICO多功能扩展坞可以让手机变的更强大...

    原标题:你真的会用手机吗?这款ORICO多功能扩展坞可以让手机变的更强大 现如今科技越来越发达,手机也早已变的不仅仅是通讯工具那么简单,可以说我们一天生活.工作.学习中的许多事物都已经离不开手机.正因 ...

  7. uniapp 解决app头部导航和手机顶部状态栏叠加问题及样式拼接写法

    app开发,手机顶部状态栏会和app头部导航叠加在一起 解决方法:拿到顶部状态栏的高度,再给头部导航加个padding-top 在app.vue里拿到状态栏的高度并存放在globalData里 onL ...

  8. 华为手机获取状态栏高度是错误的_聊聊获取屏幕高度这件事

    问题的起因是我发现 PopupWindow弹出位置不正确时发现的.其实早在两年多前,我就发现我手上的小米MIX2s 获取屏幕高度不正确,后面参考V2EX 的这篇帖子处理了.最近又一次做到类似功能,发现 ...

  9. 华为手机获取状态栏高度是错误的_华为后置指纹这么多功能,你不会还以为只能解锁和支付吧...

    对于指纹识别这个功能想必多数人都不会陌生,同时也相信大部分人对指纹识别的认知还停留在解锁和支付上,然而这些功能只是华为手机的一部分,指纹的用法也远不止此. 一.拍摄 拍摄一直就是华为的强项之一,想要拍 ...

  10. oppo禁用android系统通知栏,OPPO全机型手机去除状态栏ROOT警告-安卓刷机教程

    教程适用于OPPO机型,分别支持6.0 7.1 8.1这三个常见版本,支持常见机型 A51 A57 A59 A59S A7X A8X R7 R9系列,R9S系列,R11系列 R11S系列等 由于OPP ...

最新文章

  1. 201521123117 《Java程序设计》第1周学习总结
  2. 835 由于安全层无法对远程计算机进行身份验证_干货 | 看黑客都是这样远程桌面安全设置...
  3. PyQt5 技术篇-透明窗口设置方法,窗口透明度的设置
  4. spring boot 整合mongoDb
  5. 看新排美国话剧《哗变》
  6. linux下生成源程序控制流图,Linux下控制(统计)文件的生成的C代码实现
  7. 【区间DP】摆渡线路(2017 特长生 T4)
  8. 微型计算机中被处理信息称为,2011海南省计算机等级考试试题 二级C试题考资料...
  9. python筛选含变量的特定行_Python SQL从特定变量字段中选择行
  10. 信息学奥赛一本通(1308:【例1.5】高精除)
  11. vs2008 生成项目xml文件和路径配置
  12. bzoj 4237: 稻草人(CDQ分治+单调栈+二分)
  13. 水晶报表合并模块部署指南(.Net2.0,VS2005)
  14. Acronis Disk Director 11 Home下载,分区之王
  15. DXP导出PCB为PDF格式的设置
  16. linux设置usb选择性暂停,usb大容量存储设备是什么?无法启动怎么解决?
  17. android微信支付+指纹支付密码错误,为什么微信付款无法指纹支付密码?如何开启指纹支付?...
  18. 程序员看来都羡慕:一个寒门博士的致谢及其女友回复
  19. Thinkphp3.2如何where查询条件如何同时添加字符串条件和数组条件
  20. 视频教程-python全栈习题课-Python

热门文章

  1. 2023跨境出海指南:韩国网红营销白皮书
  2. git切换远程分支为develop
  3. 全国计算机等级考试 三级网络技术 知识点总结
  4. VMware虚拟机中安装Win10系统
  5. git推送拉取需要验证信息
  6. 【spring】 官网文档手册(附中文网址)
  7. 周围剃光头顶留长发型_四周都剃光,头顶留在发的那叫什么发型
  8. 海洋cms(海洋视频内容管理系统) v12.5
  9. 基于C++的psf2otf实现
  10. PHP做大转盘抽奖的思路,PHP实现大转盘抽奖算法(代码实例)