Android 判斷虛擬導航欄是否存在

判斷虛擬導航欄的老方法

在全面屏手機之前,我們對虛擬導航欄的判斷就有很多種方法,

比如方法1:


    {
       // 判斷系統是否寫入了關于定義虛擬導航欄的高度相關變量。
       //如果高度大于0,則表示該手機有虛擬導航欄
            Resources res = activity.getResources();
            int resourceId = res.getIdentifier("status_bar_height", "dimen", "android");
            if (resourceId > 0) {
                return res.getDimensionPixelSize(resourceId)>0;
            }
    }


又或者是這種方法2:

{
    int id = resources.getIdentifier("config_showNavigationBar", "bool", "android");
    // 判斷系統是否寫入了關于是否顯示虛擬導航欄的相關變量,如果為true,表示有虛擬導航欄
    return id > 0 && resources.getBoolean(id);
}

又或者方法3:


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            Display display = context.getWindowManager().getDefaultDisplay();
            Point size = new Point();
            Point realSize = new Point();
            display.getSize(size);  // app繪制區域
            display.getRealSize(realSize);  
            return realSize.y != size.y;
        } else {
            boolean menu = ViewConfiguration.get(context).hasPermanentMenuKey();
            boolean back = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);// 判斷是否存在物理按鍵
            if (menu || back) {
                return false; 
            } else {
                return true;
            }
        }


以上三個方法,基本上都是看系統中是否有虛擬導航欄的相關定義,即如果我們能發現系統中由虛擬導航欄相關的定義,就認定虛擬導航欄存在。這個思考方式源于手機的物理導航鍵和虛擬導航鍵一直以來都是對立存在的,即去掉了物理導航鍵,那么就會使用虛擬導航欄,如果存在虛擬導航欄,那么就沒有物理按鍵。有了A就沒有B,如果存在了B,那就沒有A。在這種前提下,那種思考方式不會有什么問題。

然而全面屏手機打破了這種對立存在的格局,去掉了物理導航鍵,但同時也隱去了虛擬導航欄(即手機確實集成了虛擬導航欄,但是沒有使用),取而代之的是通過全面屏手勢實現三個按鍵的功能。所以說,全面屏手機+全面屏手勢。是導致以往判斷方法失效的原因。

回過頭想,導致判斷失效更本質的原因,其實是因為我們的判斷方法都是間接判斷,是去尋找必要條件,而非充分條件,就好比我們在夜晚看到了月亮的光芒,并不能證明月亮是自發光的物體,除非假設一個前提:能發光的物體都是自發光的。證明才能成立。而全面屏的到來,正好打破了這個前提,導致了我們的推導出了問題。

現在,由于全面屏手機里一般都存在虛擬導航欄和全面屏手勢這兩中操作方式,且二者必取其一,因此,網上就又出現了另一種間接判斷法,即判斷當前手機是否在用全面屏手勢,如果否,則表示在用虛擬導航欄。

以下是針對vivo,小米的全面屏虛擬導航欄的判斷方法:

    /**
     * @returnv false 表示使用的是虛擬導航鍵(NavigationBar), true 表示使用的是手勢, 默認是false
     */
    public static boolean vivoNavigationGestureEnabled(Context context) {
        int val = Settings.Secure.getInt(context.getContentResolver(), NAVIGATION_GESTURE, NAVIGATION_GESTURE_OFF);
        return val != NAVIGATION_GESTURE_OFF;
    }

   public static boolean isXiaoMiNavigationBarShow(Activity context) {
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                    if (Settings.Global.getInt(context.getContentResolver(), "force_fsg_nav_bar", 0) != 0) {
                        //開啟手勢,不顯示虛擬鍵
                        return false;
                    }
                }
   }

但是這種方法也有一些缺陷,比如,判斷方法都是廠商方面給出的,也就是說沒有通用性,還有其他廠商系統判斷方法未知;而且,這種方法很難判斷那些可隱藏/呼出的虛擬導航欄。更重要的是,通過必要條件做間接判斷始終是有隱患的。

新的解決方案

因此,為了尋找一個更加通用,準確的判斷方法,我們嘗試進入Android系統層面去嘗試尋找判斷虛擬導航欄的方案。

虛擬導航欄也是一個View,如果這個View繪制了自己,并顯示在Window布局中,那么虛擬導航欄就一定存在。也就是說,我們只要找到這個View,并證明它是否存在即可。

于是我們嘗試通過Layout Inspector分析了虛擬導航欄的布局層級,發現它是DecorView的Child View(Android5.0以上是這樣),同時我們在DecorView中找到了代表虛擬導航欄的View,那么,接下來的問題就很簡單了咯。代碼如下:

{

    private static final String NAVIGATION= "navigationBarBackground";

    // 該方法需要在View完全被繪制出來之后調用,否則判斷不了
    //在比如 onWindowFocusChanged()方法中可以得到正確的結果
    public static  boolean isNavigationBarExist(@NonNull Activity activity){
        ViewGroup vp = (ViewGroup) activity.getWindow().getDecorView();
            if (vp != null) {
                for (int i = 0; i < vp.getChildCount(); i++) {
                    vp.getChildAt(i).getContext().getPackageName();
                    if (vp.getChildAt(i).getId()!= NO_ID && NAVIGATION.equals(activity.getResources().getResourceEntryName(vp.getChildAt(i).getId()))) {
                        return true;
                    }
                }
            }
        return false;
    }
  }  


當然,還有一種判斷方案,也很好。


    public static void isNavigationBarExist(Activity activity, final OnNavigationStateListener onNavigationStateListener) {
        if (activity == null) {
            return;
        }
        final int height = getNavigationHeight(activity);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
                 activity.getWindow().getDecorView().setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
                @Override
                public WindowInsets onApplyWindowInsets(View v, WindowInsets windowInsets) {
                    boolean isShowing = false;
                    int b = 0;
                    if (windowInsets != null) {
                        b = windowInsets.getSystemWindowInsetBottom();
                        isShowing = (b == height);
                    }
                    if (onNavigationStateListener != null && b <= height) {
                        onNavigationStateListener.onNavigationState(isShowing, b);
                    }
                    return windowInsets;
                }
            });
        } 
    }

    public static int getNavigationHeight(Context activity) {
        if (activity == null) {
            return 0;
        }
        Resources resources = activity.getResources();
        int resourceId = resources.getIdentifier("navigation_bar_height",
                "dimen", "android");
        int height = 0;
        if (resourceId > 0) {
            //獲取NavigationBar的高度
            height = resources.getDimensionPixelSize(resourceId);
        }
        return height;
    }


?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容