Android獲得屏幕尺寸和View尺寸的方法


這段時(shí)間在很多場(chǎng)景下需要測(cè)量屏幕尺寸和View的尺寸舍沙,遇到了不少坑,比如有虛擬導(dǎo)航欄的華為三星等手機(jī)屏幕尺寸獲取不準(zhǔn)確颊亮,在onCreate方法中獲取View尺寸為0等等。這里總結(jié)一下相關(guān)方法终惑,包含屏幕各種尺寸和View尺寸的測(cè)量。

Android屏幕各尺寸測(cè)量

一雹有、物理屏幕尺寸

1.底部沒(méi)有虛擬按鍵

/**
     * 獲取屏幕的寬
     *
     * @param context
     * @return
     */
    public static int getScreenWidth(Context context) {
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(dm);
        return dm.widthPixels;
    }

    /**
     * 獲取屏幕的高度
     *
     * @param context
     * @return
     */
    public static int getScreenHeight(Context context) {
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(dm);
        return dm.heightPixels;
    }

2.底部有虛擬按鍵

1.通過(guò)api獲得
華為和三星手機(jī)底部有虛擬導(dǎo)航欄(NavigationBar)偿渡,通過(guò)上面這個(gè)方式得到的屏幕高度不包含虛擬導(dǎo)航欄的高度,是屏幕高度-虛擬導(dǎo)航欄高度霸奕。所以這里的方法是:

public static int getRealHeight(Context context) {
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        int screenHeight = 0;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            DisplayMetrics dm = new DisplayMetrics();
            display.getRealMetrics(dm);
            screenHeight = dm.heightPixels;

            //或者也可以使用getRealSize方法
//            Point size = new Point();
//            display.getRealSize(size);
//            screenHeight = size.y;
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            try {
                screenHeight = (Integer) Display.class.getMethod("getRawHeight").invoke(display);
            } catch (Exception e) {
                DisplayMetrics dm = new DisplayMetrics();
                display.getMetrics(dm);
                screenHeight = dm.heightPixels;
            }
        }
        return screenHeight;
    }

2.第二種方法通過(guò)測(cè)量全屏的view獲得质帅,在onCreate方法中無(wú)法使用(不推薦)
代碼如下:

    /**
     * 通過(guò)測(cè)量準(zhǔn)確獲取屏幕高度
     * 
     * @return height
     */
    private int getScreenHeight() {
        //測(cè)量
        int width = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
        int height = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
        View view = this.getWindow().findViewById(R.id.activity);
        view.measure(width, height);
        int heightPixels = view.getHeight();
        //計(jì)算屏幕高度dp
        return heightPixels ;
    }

二留攒、獲得屏幕像素密度

測(cè)量獲得的屏幕尺寸高度是sp值炼邀,也就是想像素值,如果需要轉(zhuǎn)換成dp值拭宁,則需要屏幕密度瓣俯,用像素值除以屏幕像素密度就是dp值

    /**
     * 獲取屏幕像素密度
     * 
     * @return density
     */
private float getScreenDensity (){
        float density = 0;
        WindowManager windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
        Display display = windowManager.getDefaultDisplay();
        DisplayMetrics displayMetrics = new DisplayMetrics();
        display.getMetrics(displayMetrics);
        density = displayMetrics.density;//屏幕密度

        return density;
}

三、虛擬按鍵高度

虛擬按鍵(NavigationBar)高度可以通過(guò)讀取定義在Android系統(tǒng)尺寸資源中的 navigation_bar_height 獲得在旱。
所以不管虛擬按鍵是顯示還是隱藏推掸,得到的結(jié)果都是一樣的

public static int getNavigationBarHeight(Context context) {
        int navigationBarHeight = -1;
        Resources resources = context.getResources();
        int resourceId = resources.getIdentifier("navigation_bar_height","dimen", "android");
        if (resourceId > 0) {
            navigationBarHeight = resources.getDimensionPixelSize(resourceId);
        }
        return navigationBarHeight;
    }

四驻仅、狀態(tài)欄高度

狀態(tài)欄就是屏幕頂部顯示時(shí)間噪服,電池,wifi 等信息的欄目粘优。

1.通過(guò)Resource類

系統(tǒng)提供了一個(gè)Resource類,通過(guò)這個(gè)類可以獲取資源文件雹顺,借此可以獲取 到status_bar_height 嬉愧。

public int getStatusBarHeight() {
        int result = 0;
        int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }

2.通過(guò)反射

Android的所有資源都會(huì)有惟一標(biāo)識(shí)在R類中作為引用。我們也可以通過(guò)反射獲取R類的實(shí)例域没酣,然后找 status_bar_height裕便。

public void getStatusBarHeightByReflect() {
        int statusBarHeight2 = -1;
        try {
            Class<?> clazz = Class.forName("com.android.internal.R$dimen");
            Object object = clazz.newInstance();
            int height = Integer.parseInt(clazz.getField("status_bar_height")
                    .get(object).toString());
            statusBarHeight2 = getResources().getDimensionPixelSize(height);
        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.e(TAG, "狀態(tài)欄高度-反射方式:" + statusBarHeight2);
    }

3.借助應(yīng)用區(qū) top 屬性。

狀態(tài)欄位于屏幕的最頂端挂疆,坐標(biāo)從 (0,0) 開始,所以應(yīng)用區(qū)的頂部的位置就是狀態(tài)欄的高度囱嫩。

/**
     * 應(yīng)用區(qū)的頂端位置即狀態(tài)欄的高度
     * *注意*該方法不能在初始化的時(shí)候用
     * */
    public void getStatusBarHeightByTop() {
        
        Rect rectangle = new Rect();
        getWindow().getDecorView().getWindowVisibleDisplayFrame(rectangle);
        Log.e(TAG, "狀態(tài)欄高度-應(yīng)用區(qū)頂部:" + rectangle.top);
    }

五墨闲、應(yīng)用區(qū)域高度

除去狀態(tài)欄剩下的都時(shí)應(yīng)用區(qū)≡П蹋可以直接測(cè)量可見應(yīng)用區(qū)高度瞻离。

/**
     * 不能在 onCreate 方法中使用。
     * 因?yàn)檫@種方法依賴于WMS(窗口管理服務(wù)的回調(diào))套利。正是因?yàn)榇翱诨卣{(diào)機(jī)制肉迫,所以在Activity初始化時(shí)執(zhí)行此方法得到的高度是0。
     * 這個(gè)方法推薦在回調(diào)方法onWindowFocusChanged()中執(zhí)行喊衫,才能得到預(yù)期結(jié)果。
     */
    public void getAppViewHeight(){
        //應(yīng)用區(qū)域
        Rect outRect1 = new Rect();
        getWindow().getDecorView().getWindowVisibleDisplayFrame(outRect1);
        int viewHeight= outRect1.height();  
        Log.e(TAG, "應(yīng)用區(qū)高度:" + viewHeight);
    }

六壳贪、setContentView 高度寝杖,view 顯示的高度

需要在界面創(chuàng)建后才能獲取到。

public static int getContentViewHeight(Activity activity) {
        Rect rectangle= new Rect();
        activity.getWindow().findViewById(Window.ID_ANDROID_CONTENT).getDrawingRect(rectangle);
        return rectangle.height();
    }

七醉拓、標(biāo)題欄高度

標(biāo)題欄高度 = 應(yīng)用區(qū)高度 - view 顯示高度

public static void getTitleBarHeight(Activity activity) {
        Rect outRect1 = new Rect();
        activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(outRect1);

        int viewTop = activity.getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();   //要用這種方法
        int titleBarH = viewTop - outRect1.top;

        Log.e(TAG, "標(biāo)題欄高度-計(jì)算:" + titleBarH);
    }


獲取View的寬度和高度

有時(shí)需要在 onCreate() 方法中獲取某個(gè) View 組件的寬度和高度亿卤,而直接調(diào)用 getWidth()鹿霸、getHeight()、getMeasuredWidth()懦鼠、getMeasuredHeight() 方法只會(huì)得到 0。

實(shí)現(xiàn)方法

一街氢、使用 View.measure 測(cè)量 View

該方法測(cè)量的寬度和高度可能與視圖繪制完成后的真實(shí)的寬度和高度不一致。

int width = View.MeasureSpec.makeMeasureSpec(0,
        View.MeasureSpec.UNSPECIFIED);
int height = View.MeasureSpec.makeMeasureSpec(0,
        View.MeasureSpec.UNSPECIFIED);
view.measure(width, height);
view.getMeasuredWidth(); // 獲取寬度
view.getMeasuredHeight(); // 獲取高度

二荣刑、使用 ViewTreeObserver. OnPreDrawListener 監(jiān)聽事件

在視圖將要繪制時(shí)調(diào)用該監(jiān)聽事件伦乔,會(huì)被調(diào)用多次烈和,因此獲取到視圖的寬度和高度后要移除該監(jiān)聽事件。

view.getViewTreeObserver().addOnPreDrawListener(
        new ViewTreeObserver.OnPreDrawListener() {

    @Override
    public boolean onPreDraw() {
        view.getViewTreeObserver().removeOnPreDrawListener(this);
        view.getWidth(); // 獲取寬度
        view.getHeight(); // 獲取高度
        return true;
    }
});

三招刹、使用 ViewTreeObserver.OnGlobalLayoutListener 監(jiān)聽事件

在布局發(fā)生改變或者某個(gè)視圖的可視狀態(tài)發(fā)生改變時(shí)調(diào)用該事件,會(huì)被多次調(diào)用蔗喂,因此需要在獲取到視圖的寬度和高度后執(zhí)行 remove 方法移除該監(jiān)聽事件高帖。

view.getViewTreeObserver().addOnGlobalLayoutListener(
        new ViewTreeObserver.OnGlobalLayoutListener() {

    @Override
    public void onGlobalLayout() {
        if (Build.VERSION.SDK_INT >= 16) {
            view.getViewTreeObserver()
                    .removeOnGlobalLayoutListener(this);
        }
        else {
            view.getViewTreeObserver()
                    .removeGlobalOnLayoutListener(this);
        }
        view.getWidth(); // 獲取寬度
        view.getHeight(); // 獲取高度
    }
});

四能岩、重寫 View 的 onSizeChanged 方法

在視圖的大小發(fā)生改變時(shí)調(diào)用該方法冒窍,會(huì)被多次調(diào)用家破,因此獲取到寬度和高度后需要考慮禁用掉代碼遇革。
該實(shí)現(xiàn)方法需要繼承 View吏祸,且多次被調(diào)用贡翘,不建議使用。

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    view.getWidth(); // 獲取寬度
    view.getHeight(); // 獲取高度
}

五鸣驱、重寫 View 的 onLayout 方法

該方法會(huì)被多次調(diào)用踊东,獲取到寬度和高度后需要考慮禁用掉代碼刚操。
該實(shí)現(xiàn)方法需要繼承 View再芋,且多次被調(diào)用,不建議使用占卧。

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);

    view.getWidth(); // 獲取寬度
    view.getHeight(); // 獲取高度
}

六联喘、使用 View.OnLayoutChangeListener 監(jiān)聽事件(API >= 11)

在視圖的 layout 改變時(shí)調(diào)用該事件,會(huì)被多次調(diào)用豁遭,因此需要在獲取到視圖的寬度和高度后執(zhí)行 remove 方法移除該監(jiān)聽事件蓖谢。

view.addOnLayoutChangeListener(
        new View.OnLayoutChangeListener() {

    @Override
    public void onLayoutChange(View v, int l, int t, int r, int b,
            int oldL, int oldT, int oldR, int oldB) {
        view.removeOnLayoutChangeListener(this);
        view.getWidth(); // 獲取寬度
        view.getHeight(); // 獲取高度
     }
});

七、使用 View.post() 方法

Runnable 對(duì)象中的方法會(huì)在 View 的 measure啥辨、layout 等事件完成后觸發(fā)盯腌。
UI 事件隊(duì)列會(huì)按順序處理事件,在 setContentView() 被調(diào)用后腕够,事件隊(duì)列中會(huì)包含一個(gè)要求重新 layout 的 message帚湘,所以任何 post 到隊(duì)列中的 Runnable 對(duì)象都會(huì)在 Layout 發(fā)生變化后執(zhí)行。
該方法只會(huì)執(zhí)行一次大诸,且邏輯簡(jiǎn)單,建議使用焙贷。

view.post(new Runnable() {

    @Override
    public void run() {
        view.getWidth(); // 獲取寬度
        view.getHeight(); // 獲取高度
    }
});


參考

Android 屏幕各尺寸的獲取
獲取View的寬度和高度

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盈厘,一起剝皮案震驚了整個(gè)濱河市官边,隨后出現(xiàn)的幾起案子外遇,更是在濱河造成了極大的恐慌契吉,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件菲语,死亡現(xiàn)場(chǎng)離奇詭異惑灵,居然都是意外死亡英支,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門妄帘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)池凄,“玉大人,你說(shuō)我怎么就攤上這事致盟“馗保” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)萎河。 經(jīng)常有香客問(wèn)我虐杯,道長(zhǎng),這世上最難降的妖魔是什么擎椰? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任达舒,我火速辦了婚禮叹侄,結(jié)果婚禮上昨登,老公的妹妹穿的比我還像新娘。我一直安慰自己撒强,他們只是感情好笙什,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著杖玲,像睡著了一般淘正。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上囤采,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天惩淳,我揣著相機(jī)與錄音,去河邊找鬼代虾。 笑死激蹲,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的乘瓤。 我是一名探鬼主播策泣,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼统抬!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起聪建,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤妆偏,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后叔锐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體见秽,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年步责,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了禀苦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片振乏。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖调限,靈堂內(nèi)的尸體忽然破棺而出误澳,到底是詐尸還是另有隱情,我是刑警寧澤忆谓,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布陪毡,位于F島的核電站,受9級(jí)特大地震影響毡琉,放射性物質(zhì)發(fā)生泄漏妙色。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一芍碧、第九天 我趴在偏房一處隱蔽的房頂上張望号俐。 院中可真熱鬧,春花似錦吏饿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至袱结,卻和暖如春途凫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背颖榜。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工掩完, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人欣硼。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓恶阴,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親焦匈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子昵仅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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