Android6.0 源碼修改之屏蔽導(dǎo)航欄虛擬按鍵(Home和RecentAPP)/動(dòng)態(tài)顯示和隱藏NavigationBar

場(chǎng)景分析,
為了完全實(shí)現(xiàn)沉浸式效果法褥,在進(jìn)入特定的app后可以將導(dǎo)航欄移除茫叭,當(dāng)退出app后再次將導(dǎo)航欄恢復(fù)。(下面將采用發(fā)送廣播的方式來(lái)移除和恢復(fù)導(dǎo)航欄)

ps:不修改源碼的情況下半等,簡(jiǎn)單的沉浸式效果實(shí)現(xiàn)代碼如下揍愁,在ACitivy中添加即可(此種做法的缺點(diǎn)是當(dāng)界面彈出對(duì)話框時(shí)或者點(diǎn)擊的屏幕的頂部或底部邊緣,會(huì)再次出現(xiàn)導(dǎo)航欄和狀態(tài)欄)

@Override
public void onWindowFocusChanged(boolean hasFocus) {//new add
    super.onWindowFocusChanged(hasFocus);

    if (hasFocus && Build.VERSION.SDK_INT >= 19) {
        View decorView = getWindow().getDecorView();
        decorView.setSystemUiVisibility(
                //hide title&navigation
                View.SYSTEM_UI_FLAG_FULLSCREEN
                        |View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                        |View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
    }
}

一杀饵、屏蔽導(dǎo)航欄虛擬按鍵(Home和RecentAPP)

我們先來(lái)分析下底部導(dǎo)航欄所處的控件,
所見(jiàn)即所得莽囤,既然是虛擬按鍵,必定有相對(duì)應(yīng)的View切距,要么是xml的布局文件朽缎,要么是自定義View,基于此思路谜悟,打開(kāi)AS中的Tools菜單下Android-->Android Device Monitor-->Hierarchy View ,不會(huì)用的童鞋可以自己參考這篇Hierarchy View话肖,這里就不再寫了。

image

我們發(fā)現(xiàn)Home按鍵對(duì)應(yīng)的id為 @+id/home, 有了id我們就已經(jīng)成功的揪出它了葡幸,接下來(lái)通過(guò)搜索命令

grep -nr 命令

image

通過(guò)搜索最筒,我們發(fā)現(xiàn)了Home按鍵的 源碼位置

frameworks\base\packages\SystemUI\res\layout-sw600dp\navigation_bar.xml
frameworks\base\packages\SystemUI\res\layout\navigation_bar.xml

     <!--2018-10-13 cczheng set home KeyButton  visibility  invisible-->
        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
            android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp"
            android:layout_height="match_parent"
            android:src="@drawable/ic_sysbar_home"
            android:scaleType="centerInside"
            systemui:keyCode="3"
            systemui:keyRepeat="true"
            android:layout_weight="0"
            android:contentDescription="@string/accessibility_home"
            android:visibility="invisible"
            />
      <!--2018-10-13 cczheng set recent KeyButton  visibility  invisible-->
        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
            android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp"
            android:layout_height="match_parent"
            android:src="@drawable/ic_sysbar_recent"
            android:scaleType="centerInside"
            android:layout_weight="0"
            android:contentDescription="@string/accessibility_recent"
            android:visibility="invisible"
            />

此處說(shuō)下為什么用invisible,而不用gone, 如果使用gone蔚叨,剩下的按鍵將自動(dòng)居中床蜘,這并不是我們想要的效果,當(dāng)你以為按照上面的代碼屏蔽后就可以安心的交差了缅叠,那你真是too young too simple了悄泥。

繼續(xù)搜索 R.id.home,我們接下來(lái)查找在java代碼中的引用虏冻,找到位置

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java

public View getBackButton() {
    ImageView view = (ImageView) mCurrentView.findViewById(R.id.back);
    view.setImageDrawable(mNavBarPlugin.getBackImage(view.getDrawable()));
    return view;
}

很明顯通過(guò)getBackButton()方法能獲取到該View肤粱,就能對(duì)View進(jìn)行操作,繼續(xù)查看調(diào)用getBackButton()的地方

找到當(dāng)前類中

public void setDisabledFlags(int disabledFlags, boolean force) {
    if (!force && mDisabledFlags == disabledFlags) return;

    mDisabledFlags = disabledFlags;
    ...

    getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
    getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
    getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);

    //2018-10-13 cczheng add recent KeyButton  visibility  invisible
    getHomeButton().setVisibility(View.INVISIBLE);  
    getRecentsButton().setVisibility(View.INVISIBLE);
    //2018-10-13 cczheng add recent KeyButton  visibility  invisible

     ...
}

另外一處位于
frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\PhoneStatusBar.java

private void prepareNavigationBarView() {
    mNavigationBarView.reorient();

    mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
    mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
    mNavigationBarView.getRecentsButton().setLongClickable(true);
    mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
    mNavigationBarView.getBackButton().setLongClickable(true);
    mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
    mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
    mNavigationBarView.getHomeButton().setOnLongClickListener(mLongPressHomeListener);
    mAssistManager.onConfigurationChanged();

    //2018-10-13 cczheng set recent KeyButton  visibility  invisible
    mNavigationBarView.getHomeButton().setVisibility(View.INVISIBLE);  
    mNavigationBarView.getRecentsButton().setVisibility(View.INVISIBLE);
    //2018-10-13 cczheng set recent KeyButton  visibility  invisible

    ....
 }

ok厨相,大功告成领曼,重新mm,編譯替換SystemUI.apk重啟查看效果蛮穿。

二庶骄、動(dòng)態(tài)顯示和隱藏NavigationBar

文章開(kāi)頭提到將以廣播的方式來(lái)實(shí)現(xiàn)動(dòng)態(tài)顯示和隱藏NavigationBar這一功能,那么我們改從哪里入手呢践磅?細(xì)心的你可能已經(jīng)發(fā)現(xiàn)了上面的一個(gè)方法单刁,prepareNavigationBarView()在PhoneStatusBar.java中,從方法名字就能猜到它和NavigationBar的加載有關(guān)府适。查找prepareNavigationBarView()引用

// For small-screen devices (read: phones) that lack hardware navigation buttons
private void addNavigationBar() {
    if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
    if (mNavigationBarView == null) return;

    prepareNavigationBarView();

    mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
}

private void repositionNavigationBar() {
    if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;

    prepareNavigationBarView();

    mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams());
}

共有兩處引用羔飞,很明顯找對(duì)地方了肺樟,在addNavigationBar()中,核心方法是通過(guò)mWindowManager.addView()添加的逻淌∶床看到這你應(yīng)該明白了,導(dǎo)航欄其實(shí)就是一個(gè)在window最底部添加的View卡儒,既然有addView田柔,對(duì)應(yīng)就有removeView,查找源碼中并未發(fā)現(xiàn)提供了removeView方法骨望∮脖看來(lái)需要我們自己來(lái)實(shí)現(xiàn)這個(gè)方法。

PS:如果想實(shí)現(xiàn)屏蔽整個(gè)NavigationBar的效果擎鸠,可直接將addNavigationBar()方法注釋

@Override
public void start() {
    mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
            .getDefaultDisplay();
    updateDisplaySize();
    mScrimSrcModeEnabled = mContext.getResources().getBoolean(
            R.bool.config_status_bar_scrim_behind_use_src);

    super.start(); // calls createAndAddWindows()

    mMediaSessionManager
            = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
    // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
    // in session state

    //"anotation this can hide navigationbar" 注釋此處的addNavigationBar即可
    addNavigationBar();

    // Lastly, call to the icon policy to install/update all the icons.
    mIconPolicy = new PhoneStatusBarPolicy(mContext, mCastController, mHotspotController,
            mUserInfoController, mBluetoothController);
    mIconPolicy.setCurrentUserSetup(mUserSetup);
    mSettingsObserver.onChange(false); // set up
}

完整的通過(guò)廣播動(dòng)態(tài)顯示和隱藏NavigationBar的代碼如下所示:

////2018-10-13 cczheng hide or show navigationbat by broadcast 
private NotifyChangeNavigationBarBroadcast mNotifyChangeNavigationBarBroadcast;
private static final String SHOW_NAVIGATION = "cc.intent.systemui.shownavigation";
private static final String HIDE_NAVIGATION = "cc.intent.systemui.hidenavigation";


@Override
public void start() {
    mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
            .getDefaultDisplay();
    updateDisplaySize();
    mScrimSrcModeEnabled = mContext.getResources().getBoolean(
            R.bool.config_status_bar_scrim_behind_use_src);

    ....

    //2018-10-13 cczheng hide or show navigationbat by broadcast 
    mNotifyChangeNavigationBarBroadcast = new NotifyChangeNavigationBarBroadcast();
    IntentFilter mfilter = new IntentFilter();
    mfilter.addAction(HIDE_NAVIGATION);
    mfilter.addAction(SHOW_NAVIGATION);
    mContext.registerReceiver(mNotifyChangeNavigationBarBroadcast, mfilter);

}


private void prepareNavigationBarView() {
    mNavigationBarView.reorient();

    mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
    mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
    mNavigationBarView.getRecentsButton().setLongClickable(true);
    mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
    mNavigationBarView.getBackButton().setLongClickable(true);
    .....
    //2018-10-13 cczheng hide or show navigationbat by broadcast start
    if (!isNavigationShow) {
        int newVal = mSystemUiVisibility;
        mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
        setSystemUiVisibility(newVal, /*SYSTEM_UI_VISIBILITY_MASK*/Color.TRANSPARENT);
        int hints = mNavigationIconHints;
        mNavigationIconHints = 0;
        setNavigationIconHints(hints);
        topAppWindowChanged(mShowMenu);
    }
    //2018-10-13 cczheng hide or show navigationbat by broadcast end

}

//2018-10-13 cczheng hide or show navigationbat by broadcast start
private static boolean isNavigationShow = true ;
class NotifyChangeNavigationBarBroadcast extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Log.i(TAG, "NotifyChangeNavigationBarBroadcast: action =" + action); 
        if (HIDE_NAVIGATION.equals(action)) {
            isNavigationShow = false;
            hideNavigationBar();
        }else if (SHOW_NAVIGATION.equals(action)) {
            if (isNavigationShow) return;

            showNavigationBar();
        }
    }
};

private void showNavigationBar() {
    mNavigationBarView =(NavigationBarView) View.inflate(mContext, R.layout.navigation_bar, null);
    mNavigationBarView.setBar(this);
    
    addNavigationBar();
    isNavigationShow = true;

    // mNavigationBarView.setBackgroundColor(Color.TRANSPARENT);
}

private void hideNavigationBar() {
    Log.d(TAG, "hideNavigationBar: about to remove"  + mNavigationBarView);
    if (mNavigationBarView == null) return;

    mWindowManager.removeView(mNavigationBarView);
    mNavigationBarView = null;
}
//2018-10-13 cczheng hide or show navigationbat by broadcast end

ok, 動(dòng)態(tài)顯示和隱藏NavigationBar的功能也搞定了摆屯。

三、總結(jié)

Hierarchy View是一個(gè)非常好用的利器糠亩,不僅是對(duì)源碼分析查找虐骑,當(dāng)你要查看借鑒別人的app時(shí)也會(huì)是一個(gè)非常好用的扳手。So赎线,多研究研究吧廷没。

下一篇將介紹在不修改源碼的情況下,通過(guò)兩種方式實(shí)現(xiàn)加載loading對(duì)話框的功能(不退出沉浸式效果)


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末垂寥,一起剝皮案震驚了整個(gè)濱河市颠黎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌滞项,老刑警劉巖狭归,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異文判,居然都是意外死亡过椎,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門戏仓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)疚宇,“玉大人,你說(shuō)我怎么就攤上這事赏殃》蟠” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵仁热,是天一觀的道長(zhǎng)榜揖。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么举哟? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任钳幅,我火速辦了婚禮,結(jié)果婚禮上炎滞,老公的妹妹穿的比我還像新娘敢艰。我一直安慰自己,他們只是感情好册赛,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布钠导。 她就那樣靜靜地躺著,像睡著了一般森瘪。 火紅的嫁衣襯著肌膚如雪牡属。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天扼睬,我揣著相機(jī)與錄音逮栅,去河邊找鬼。 笑死窗宇,一個(gè)胖子當(dāng)著我的面吹牛措伐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播军俊,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼侥加,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了粪躬?” 一聲冷哼從身側(cè)響起担败,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎镰官,沒(méi)想到半個(gè)月后提前,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泳唠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年狈网,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片警检。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡孙援,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出扇雕,到底是詐尸還是另有隱情,我是刑警寧澤窥摄,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布镶奉,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏哨苛。R本人自食惡果不足惜鸽凶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望建峭。 院中可真熱鬧玻侥,春花似錦、人聲如沸亿蒸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)边锁。三九已至姑食,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茅坛,已是汗流浹背音半。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贡蓖,地道東北人曹鸠。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像斥铺,于是被迫代替她去往敵國(guó)和親物延。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 《永遠(yuǎn)的蝴蝶》 文/傅繹璇 在這個(gè)深秋 總有一抹紅 為你綻放 即便破繭成蝶 太多苦痛 也會(huì)為你堅(jiān)忍 不知爺爺為何給...
    傅繹璇閱讀 414評(píng)論 3 12
  • 《生態(tài)宜居》 人有左右心室左右心房 家庭須三室一廳 這就是最生態(tài) 健康仅父。
    詩(shī)者如斯夫閱讀 362評(píng)論 8 10
  • 筆記 一叛薯、小片段 1、技術(shù)其實(shí)沒(méi)有那么重要笙纤。當(dāng)你專注于某項(xiàng)技術(shù)時(shí)耗溜,容易成為固守技術(shù)的人。創(chuàng)新為什么沒(méi)有我們想的那么...
    懿拾閱讀 762評(píng)論 0 0