1. 程式人生 > >Android獲取View的寬高與View.measure詳解

Android獲取View的寬高與View.measure詳解

在oncreate()中無論利用view.getWidth()或是view.getHeiht()還是view.getMeasuredHeight或view.getMeasuredWidth()來獲取view的寬和高,看似沒有問題,其實他們取得值是0,並不是你想要的結果。這是因為每個佈局都要經過如下三個步驟:

測量:onMeasure 設定自己顯示在螢幕上的寬高
佈局:onLayout 設定自己顯示在螢幕上的位置(只有在自定義ViewGroup中才用到)
繪製:onDraw 控制顯示在螢幕上的樣子(viewgroup沒有這個過程)
如上的步驟是非同步進行的,在oncreate()中介面處於不可見狀態,記憶體載入元件還沒有繪製出來,所以是無法獲取他的尺寸。
那如何在繪製元件之前能獲取到該元件的尺寸大小呢?
方法一(最常用的方法):

//手動呼叫測量方法。
//制定測量規則 引數表示size + mode
int width =View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
int height =View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
view.measure(width,height);
//呼叫measure方法之後就可以獲取寬高。
int height=view.getMeasuredHeight();
int width=view.getMeasuredWidth();

方法二(設定樹樁結構監聽器):

ViewTreeObserver vto =view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener(){
@Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int height =view.getMeasuredHeight();
int width =view.getMeasuredWidth();
}
});

方法三(增加元件繪製之前的監聽):

ViewTreeObserver vto =view.getViewTreeObserver();

vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
int height =view.getMeasuredHeight();
int width =view.getMeasuredWidth();
}
});

詳解View.measure:
measure()方法是實際測量的方法,而在繪製佈局過程中呼叫的onMeasure()只是制定測量規則.
在自定義佈局中我們一般重寫onMeasure()方法,measure()方法是final的,子類無法重寫。
看到measure函式有2個引數,int widthMeasureSpec 和 int heightMeasureSpec表示具體的測量規則。
這兩個數值不是普通的數值, 它表示: size + mode
例如:
int widthMeasureSpec= View.MeasureSpec.makeMeasureSpec(1000,View.MeasureSpec.EXACTLY);
int heightMeasureSpec= View.MeasureSpec.makeMeasureSpec(1000,View.MeasureSpec.AT_MOST);
模式分為:
View.MeasureSpec.EXACTLY:表示父檢視希望子類的大小是specSize中制定的大小.
View.MeasureSpec.AT_MOST:父試圖希望子類的大小最高不超過specSize中制定的大小.
View.MeasureSpec.UNSPECIFIED:父試圖不對子類實施任何限制,子試圖可以得到自己想得到的任意大小.
需求:
在TextView中通過程式碼呼叫setText()動態的設定文字,要求在設定文字之後獲取其寬高.
TextView的佈局方式為width:match_parent height:wrap_content

    int width=textView.getMeasuredWidth(); // 開始寬度
    textView.getLayoutParams().height= ViewGroup.LayoutParams.WRAP_CONTENT;// 高度包裹內容
    int widthMeasureSpec=View.MeasureSpec.makeMeasureSpec(width,View.MeasureSpec.EXACTLY);
     //寬度是match_parent.即是View.MeasureSpec.EXACTLY
    int heightMeasureSpec= View.MeasureSpec.makeMeasureSpec(1000,View.MeasureSpec.AT_MOST);
    //讓高度最大為1000
    textView.measure(widthMeasureSpec, heightMeasureSpec);
    return des_content.getMeasuredHeight();

這樣就獲取到Textview的高的,根據文字的多少獲取不同高度。之前很多人可能直接measure(0,0)來測量看,其實就是:
int widthMeasureSpec=View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
int heightMeasureSpec= View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);

利於這個方法為ImageView設定寬高比例顯示:

   <com.example.view.MyRatioLayout
        android:id="@+id/rl_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="5dp" >
        <ImageView
            android:id="@+id/item_icon"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scaleType="fitCenter"
            android:src="@drawable/ic_default" />
  </com.example.view.MyRatioLayout>

通過一個自定義佈局把ImageView包裹起來:

重寫這個自定義佈局的onMeasure()方法修改其測量規則,讓其款高按一定比例顯示:

int ratio = 2.43
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// widthMeasureSpec 寬度的規則 包含了兩部分 模式 值
int widthMode = MeasureSpec.getMode(widthMeasureSpec); // 模式
int widthSize = MeasureSpec.getSize(widthMeasureSpec);// 寬度大小
int width = widthSize - getPaddingLeft() - getPaddingRight();// 去掉左右兩邊的padding

    int heightMode = MeasureSpec.getMode(heightMeasureSpec); // 模式
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);// 高度大小
    int height = heightSize - getPaddingTop() - getPaddingBottom();// 去掉上下兩邊的padding
               //如果width是match_parent 
    if (widthMode == MeasureSpec.EXACTLY
            && heightMode != MeasureSpec.EXACTLY) {
        // 修正一下 高度的值 讓高度=寬度/比例
        height = (int) (width / ratio + 0.5f); // 保證4舍五入
    } else if (widthMode != MeasureSpec.EXACTLY
            && heightMode == MeasureSpec.EXACTLY) {
        // 由於高度是精確的值 ,寬度隨著高度的變化而變化
        width = (int) ((height * ratio) + 0.5f);
    }
    // 重新制作了新的規則
    widthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY,
            width + getPaddingLeft() + getPaddingRight());
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY,
            height + getPaddingTop() + getPaddingBottom());

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

相關推薦

android獲取螢幕獲取控制元件

// 獲取螢幕寬高(方法1) int screenWidth = getWindowManager().getDefaultDisplay().getWidth(); // 螢幕寬(畫素,如:480px) int screenHeight = getWindowManager().getDe

Android獲取ViewView.measure

在oncreate()中無論利用view.getWidth()或是view.getHeiht()還是view.getMeasuredHeight或view.getMeasuredWidth()來獲取view的寬和高,看似沒有問題,其實他們取得值是0,並不是你

Android 獲取layout之前呼叫View.measure(0,0)的原因

private ConstraintLayout constraintLayout; constraintLayout=findViewById(R.id.screen_test); constraintLayout.measure(0,0); Log.i(TAG,"onCreate:"+const

Android獲取螢幕,狀態列,actionbar,layout,導航欄高度的方法彙總

看這個部落格你可以知道 獲取螢幕寬高,狀態列寬高,actionbar寬高,layout寬高,導航欄(虛擬按鍵欄)高度的方法   目錄順序為 程式碼測試的機型 狀態列高度 actionbar高度 螢幕高度 導航欄(虛擬按鍵欄)高度 layout寬高 總

Android獲取裝置,以及狀態列高度

//獲取裝置寬度和高度 DisplayMetrics dm=new DisplayMetrics();WindowManager manager= (WindowManager) this.getSystemService(this.WINDOW_SERVICE);mana

Android 獲取本地儲存路徑的各種方法

Android 中獲取本地儲存路徑,有四個方法, getCacheDir()、getFilesDir()、getExternalFilesDir()、getExternalCacheDir()。 接下來介紹下每個方法的特點以及路徑地址 getCacheDir(

Android——View的設定和多種獲取的方法、layout_grivatygrivaty的區別

一、設定控制元件寬高 設定Layout_width/height引發的寬高思考 方式一: 結果不符合預期。 執行結果: 方式二: 結果符合預期。 如下圖: Android控制元件寬高的規則:   Android下的控制元件預設沒有寬高,是由

android onCreate中獲取view為0的多種解決方法

  這個問題大家肯定遇到過不止一次,其實很簡單,解決它也很容易,但是咱們追求的畢竟不是解決它,而是找到幾種方法去解決,並且這麼解決的原理是什麼。   這裡列出4種解決方案: Activity/View#onWindowFocusChanged   這個函式

Android獲取View方法

Android開發中經常需要獲取控制元件的寬高,比如前不久我在寫一個圖片載入庫時,因為需要對Bitmap進行裁剪就遇到了需要獲取ImageView寬高的問題。 如果稍微瞭解過一下View的繪製過程,就會知道直接在onCreate()等生命週期回撥方法中獲取寬高,獲取到的值是0

android在程式碼中獲取view/為0解決方法

在專案中,我們要在程式碼中獲取view的寬高資訊,有可能就會在onCreat或者onResume方法中去獲取,原因就是view的measure過程與Activity的生命週期不是同步執行的,因此無法保證在onCreat,onResume,onStart時這個vi

Activity正確獲取View

在View的measure完成後,一般可以通過getMeasureWidth/getMeasureWidth方法可以正確的獲取View的寬高,而在特殊情況下,可能需要多次measure才能確定最終的測量寬高,onMeasure無法獲取正確的寬高,但可以在onLayout方法中獲取測量寬高。

Android-View測量研究

前言 平時寫自定義控制元件包括之前寫下拉重新整理庫的時候,有時候都需要預先知道View的寬度或者高度,這樣能夠幫助我們很好地實現效果,但是我們都或多或少知道View的度量和繪製是立刻完成的操作,所以當我初始化一個View之後,是無法立刻拿到它的寬度和高度值的,

獲取View的幾種方式及View和ViewGroup測量的簡單實現

自己指定測量規則 //這裡自己指定寬wrap_content 高100, view = (Button) findViewById(R.id.button1); int widthMeasureSpec = MeasureSpec.makeMeasureS

iOS xib View不能改變

table 單獨 ios 寬高 p s none post url htm IOS - xib(Interface Builder,view) - can‘t change view size(view不能改變大小問題) 今天在試著swift語言寫個demo,,當

測量 View ,測到的數值是 px值,然後轉為 dp

實現效果:(button點選後,吐司彈出寬高) 主程式碼 final ViewTreeObserver vto =view.getViewTreeObserver(); vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawLi

菜鳥學android——獲取listview某個item的view物件

網上查了查,發現這個問題很冷門,可現在又有這個需求,而且不是從OnItemClickListener中呼叫,只好自己摸索一下了。 首先說,listview有個getChildAt(int position)的方法,但是這個方法只計算可視的item,也就是說position

Android螢幕適配3-動態獲取螢幕及動態設定控制元件

1、問題 在螢幕適配中,要求應用在不同的螢幕上顯示一樣的效果時,我們的佈局可以採用百分比來定位,也就是 layout_weight,但對於一些層層巢狀或更加複雜的情況下,使用百分比的效果並不是很好,頁無法解決一些問題,比如文字大小。 2、解決思路 我們

Android中自定義樣式View的建構函式中的第三個引數defStyle的意義

零、序 零、序   系統自帶的View可以在xml中配置屬性,對於寫的好的Custom View同樣可以在xml中配置屬性,為了使自定義的View的屬性可以在xml中配置,需要以下4個步驟: 通過<declare-styleable>為自定

Android獲取螢幕

WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); wm.getDefault

android 開發 View _14 MotionEvent和事件處理實踐自定義滑動條View

MotionEvent MotionEvent物件是與使用者觸控相關的時間序列,該序列從使用者首次觸控式螢幕幕開始,經歷手指在螢幕表面的任何移動,直到手指離開螢幕時結束。手指的初次觸控(ACTION_DOWN操作),滑動(ACTION_MOVE操作)和擡起(ACTION