Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android如何在初始化的時候獲取加載的布局的寬高

Android如何在初始化的時候獲取加載的布局的寬高

編輯:關於Android編程

在自定義ListView中,需要將下拉刷新的View在初始化的時候設置padding隱藏起來,這時就要在初始化的時候獲得要加載的布局View的高度。

private View headView;
headView = inflater.inflate(R.layout.header, null);

如果接下來調用:

headView.getHeight();
headView.getMeasuredHeight();

我們知道都會返回0,原因是getMeasuredHeight要在measure後才有值,getHeight要在layout後才有值。要在這裡取得加載的布局的寬高,需要程序員調用measure測量出該布局的寬高。以前在寫下拉ListView的時候有這樣一段代碼,一直不太明白,今天研究了一下:

private void measureView(View child) {
		ViewGroup.LayoutParams lp = child.getLayoutParams();
		if(lp == null){
			lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
		}
		//headerView的寬度信息
		int childMeasureWidth = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
		int childMeasureHeight;
		if(lp.height > 0){
			childMeasureHeight = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
			//最後一個參數表示:適合、匹配
		} else {
			childMeasureHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);//未指定
		}
//System.out.println("childViewWidth"+childMeasureWidth);
//System.out.println("childViewHeight"+childMeasureHeight);
		//將寬和高設置給child
		child.measure(childMeasureWidth, childMeasureHeight);
	}

在inflate完布局後,調用measureView(headView),然後再調用getMeasureHeight函數就可以獲得實際高度了。

接下來從原理上分析一下這段代碼:

通過inflater.inflate(R.layout.header, null);將XML轉化為View的時候,是沒有設置View的LayoutParams的,就會進入到lp ==null條件中new一個LayoutParams。接下來設置View的childMeasureWidth和childMeasureHeight這點比較費解。

childMeasureWidth:

調用ViewGroup的getChildMeasureSpec傳遞的第一個參數(父類的spec)竟然是0,傳進0表示父類的spec是未指定的MeasureSpec.UNSPECIFIED模式,size也為0。那麼該函數返回的值就是一個UNSPECIFIED類型size為0的值。

childMeasureHeight:

和上面類似,如果lp.height > 0就表示設置的是一個精確值,接下來會封裝一個EXACTLY和lp.height的值給childMeasureHeight,然後這裡lp.height是wrap_content,他是個負值(-2),所以會封裝一個未指定UNSPECIFIED和size為0的值給childMeasureHeight。

即childMeasureWidth和childMeasureHeight兩個參數均為0。

關於傳遞給child的widthMeasureSpec和heightMeasureSpec可以通過打印出的結果看出來確實是這樣的。

然後將這兩個值作為參數傳遞給子View的measure函數。

上次在張鴻洋大神的博客看到在劃動刪除代碼裡這樣寫,獲取PopupWindow的寬高之前調用了mPopupWindow.getContentView().measure(0, 0);獲取他的寬高。

View view = mInflater.inflate(R.layout.delete_btn, null);  
        mDelBtn = (Button) view.findViewById(R.id.id_item_btn);  
        mPopupWindow = new PopupWindow(view, LinearLayout.LayoutParams.WRAP_CONTENT,  
                LinearLayout.LayoutParams.WRAP_CONTENT);  
        /** 
         * 先調用下measure,否則拿不到寬和高 
         */  
        mPopupWindow.getContentView().measure(0, 0);  
        mPopupWindowHeight = mPopupWindow.getContentView().getMeasuredHeight();  
        mPopupWindowWidth = mPopupWindow.getContentView().getMeasuredWidth();

這樣看來,其實和一開始給的代碼是一樣的。因為分析完開始的代碼,我們發現最後執行child.measure(childMeasureWidth, childMeasureHeight);的時候兩個參數就是0。

我最初很難理解父布局傳遞給子布局一個UNSPECIFIED和0的值。因為默認情況下對於View來說會直接設置:

setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));

裡面的函數前面都學習過,很明顯會將傳過來的0設置給該View。但是調用這段代碼後卻是得到了真事的尺寸不是0。

帶著問題,我將測量的布局僅僅寫一個View,命名為view_layout.xml:

還有一個View被LinearLayout包裹著,viewgroup_layout.xml:


	

還有一個textview_layout.xml:

activity_main.xml


然後在onCerate的時候執行下面代碼:

inflater = LayoutInflater.from(this);
        mainLayout = (LinearLayout) findViewById(R.id.mainLayout);
        view = inflater.inflate(R.layout.view_layout, null);
        textview = inflater.inflate(R.layout.textview_layout, null);
        viewgroup = inflater.inflate(R.layout.viewgroup_layout, null);
        
        measureView(view);
        measureView(textview);
        measureView(viewgroup);
        
        Log.i(TAG, "view的width和height分別是:" + view.getMeasuredWidth()+ "  " + view.getMeasuredHeight());
        Log.i(TAG, "textview的width和height分別是:" + textview.getMeasuredWidth()+ "  " + textview.getMeasuredHeight());
        Log.i(TAG, "viewgroup的width和height分別是:" + viewgroup.getMeasuredWidth()+ "  " + viewgroup.getMeasuredHeight());

得到的結果是:

vcHLVmlld7XEs9+056OsxMfDtNTavavL+8PHzO2807W9uLiyvL7Wo6zU2bvxyKHS1M/CsuLBv7/tuN+jrNXiyrG1xL/tuN+y4sG/0qrU2m9uV2luZG93Rm9jdXNDaGFuZ2VkwO/D5qGjCjxwPjwvcD4KPHByZSBjbGFzcz0="brush:java;">mainLayout.addView(view); mainLayout.addView(textview); mainLayout.addView(viewgroup); @Override public void onWindowFocusChanged(boolean hasFocus) { Log.i(TAG, "獲得焦點後"); // Log.i(TAG, "view的width和height分別是:" + view.getMeasuredWidth()+ " " + view.getHeight()); Log.i(TAG, "textview的width和height分別是:" + textview.getMeasuredWidth()+ " " + textview.getMeasuredHeight()); Log.i(TAG, "viewgroup的width和height分別是:" + viewgroup.getMeasuredWidth()+ " " + viewgroup.getMeasuredHeight()); super.onWindowFocusChanged(hasFocus); }

打印出來的Log如下:

\

View的寬高竟然有值了!原因是什麼?在調用addView的時候會使試圖重新繪制!至於寬和高為什麼是這樣的,那是因為調用generateDefaultLayoutParams返回的寬為match_parent高為wrap_content。最終傳測量該View是的onMeasure的參數是AT_MOST和ScreenSize。因為是個View,直接調用setMeasuredDimension設置。

後面兩個出現0的原因是View將屏幕占滿了。如果不調用mainLayout.addView(view);可以看到:

\

改成"horizontal"後,打印信息如下:

\

至於高度,textview為15,由於是最外層,最終為包裹內容。Viewgroup的最終寬高為50dp,由於我的手機像素密度為0.75,因此換算為像素為38,結果一致。

這就告訴我,自己調用onMeasure只是暫時拿到了View的寬高測量值,這個值與傳入的父布局的widthMeasureSpec, heightMeasureSpec有關。實際上添加到布局的真實寬高可能和這個值不一樣。

ListView中也有類似的方法,和開始的方法基本一樣。

private void measureItem(View child) {  
         ViewGroup.LayoutParams p = child.getLayoutParams();  
         if (p == null) {  
             p = new ViewGroup.LayoutParams(  
                     ViewGroup.LayoutParams.MATCH_PARENT,  
                     ViewGroup.LayoutParams.WRAP_CONTENT);  
         }  
   
         int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,  
                 mListPadding.left + mListPadding.right, p.width);  
         int lpHeight = p.height;  
         int childHeightSpec;  
         if (lpHeight > 0) {  
             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);  
         } else {  
             childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);  
         }  
         child.measure(childWidthSpec, childHeightSpec);  
     }  

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved