1. 程式人生 > >[android]狀態列的電量圖示變化程式碼流程

[android]狀態列的電量圖示變化程式碼流程

Android version : 5.0

Author :iceBear

date     :2015/08/27

Android 5.0對狀態列做了一定改動,電池圖示預設不再有數字形式。但在下拉選單中,圖示旁邊會有一個數字格式的電量顯示。電量變化時,數字會變化;通常每當電量降低10%後,圖示會變化。本文對這個流程做簡單的分析,先說數字形式的。

數字形式的圖示包含在佈局檔案Status_bar_expanded_header.xml (\frameworks\base\packages\systemui\res\layout)中,

        <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"/>

並在StatusBarHeaderView.java (\frameworks\base\packages\systemui\src\com\android\systemui\statusbar\phone)中被獲取。

mBatteryLevel = (TextView) findViewById(R.id.battery_level);
每當電量變化時,數字會變化。
    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {//傳進來的level即是當時的電量
        mBatteryLevel.setText(getResources().getString(R.string.battery_level_template, level));
    }
但是onBatteryLevelChanged()又是如何得知電量的變化的呢?

是因為StatusBarHeaderView實現了BatteryController.BatteryStateChangeCallback介面。

    public interface BatteryStateChangeCallback {
        void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging);
        void onPowerSaveChanged();
    }

而它又通過setBatteryController() 關聯了一個batteryController物件;

    public void setBatteryController(BatteryController batteryController) {
        mBatteryController = batteryController;//自己的catteryController
        ((BatteryMeterView) findViewById(R.id.battery)).setBatteryController(batteryController);
            //圖示電量的controller,
           //注意這個是下拉選單中的圖示,而不是狀態列中的,這裡先不理它BatteryController.BatteryStateChangeCallback
    }
這個batteryController物件是在PhoneStatusBar.java (\frameworks\base\packages\systemui\src\com\android\systemui\statusbar\phone)中被new出來的。
    BatteryController mBatteryController;
    ......
    StatusBarHeaderView mHeader;
而後,又將這個物件set給了數字電量圖示所在的StatusBarHeaderView。
protected PhoneStatusBarView makeStatusBarView() {
    ......
    mBatteryController = new BatteryController(mContext); 
    mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {/*此處省略*/});
    ......
    mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
    ......
    mHeader.setBatteryController(mBatteryController);
}
其中,makeStatusBarView()經過一系列的引用,最終是在systemUI.java中的start()中被呼叫,該方法的具體情況仍待學習,這裡不再展開。

PhoneStatusBar把batteryController和我們的數字顯示TextView關聯在了一起,TextView可以通過batteryController感知到電量的變化並對剩餘電量的值進行修改。那麼接下來看看batteryController是如何感知電量變化的。

首先,它繼承了BroadcastReceiver,定義了IntentFilter並註冊了監聽

    public BatteryController(Context context) {
        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);

        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_BATTERY_CHANGED);//我們需要的intent
        filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
        filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
        context.registerReceiver(this, filter);

        ......
    }
然後,在onReceive()中進行處理
public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
            mLevel = (int)(100f                              //當前電量
                    * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
                    / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
            mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;

            final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
                    BatteryManager.BATTERY_STATUS_UNKNOWN);
            mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
            mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;

            fireBatteryLevelChanged();//往下分發資料
        } 
            ......
    }
fireBatteryLevelChanged()程式碼清單:
    private void fireBatteryLevelChanged() {
        final int N = mChangeCallbacks.size();
        for (int i = 0; i < N; i++) {            
            //將事件分發給實現了介面BatteryController.BatteryStateChangeCallback的類
            mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
        }
    }
其中:
    private final ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
在StatusBarHeaderView.java中
    private void updateListeners() {
        if (mListening) {
            mBatteryController.addStateChangedCallback(this);//將自己新增到mChangeCallbacks中去
            mNextAlarmController.addStateChangedCallback(this);
        } else {
            mBatteryController.removeStateChangedCallback(this);
            mNextAlarmController.removeStateChangedCallback(this);
        }
    }

    public void setListening(boolean listening) {
        if (listening == mListening) {
            return;
        }
        mListening = listening;
        updateListeners();
    }

在NotificationPanelView.java (\frameworks\base\packages\systemui\src\com\android\systemui\statusbar\phone) 中

    private StatusBarHeaderView mHeader;
    ......
    private void setListening(boolean listening) {
        mHeader.setListening(listening);
        mKeyguardStatusBar.setListening(listening);
        mQsPanel.setListening(listening);
    }

 public boolean onTouchEvent(MotionEvent event) {

        ......

        if (mTwoFingerQsExpandPossible && event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN
                && event.getPointerCount() == 2
                && event.getY(event.getActionIndex()) < mStatusBarMinHeight) {
            mTwoFingerQsExpand = true;
            requestPanelHeightUpdate();

            // Normally, we start listening when the panel is expanded, but here we need to start
            // earlier so the state is already up to date when dragging down.
            setListening(true);
        }
        super.onTouchEvent(event);
        return true;
    }
由於只在下拉選單欄中才有數字格式的電量顯示,所以當往下拉選單欄的時候,開始監聽並改變電量的值。程式碼到這裡就基本結束了,再往下就是TouchEvent的處理了,是另外的模組。下一期講一講電量圖示相關的內容。