淺析Android Dialog中setContentView()方法,setcontentview

來源:互聯網
上載者:User

淺析Android Dialog中setContentView()方法,setcontentview

2017-05-15

概述

  Dialog在Android中是一個很優秀的工具。在使用Dialog時,我們一般都會自訂要顯示的內容布局。Dialog內建了三個方法來支援自訂內容布局。

1 public void setContentView (int layoutResID);2 3 public void setContentView (View view);4 5 public void setContentView (View view, ViewGroup.LayoutParams params);

這三個方法內部的實現原理都是一樣的,只是其封裝深度不同而已。三個方法可以說分別照顧了不同定製深度的開發人員。

 

setContentView()流程

  直接查看Dialog的原始碼,如1所示。

【圖1】

mWindowDialog類中的定義如下:

1 import android.view.Window;2 3 Window mWindow;

那麼,它從何而來呢?如2所示。

【圖2】

由2可知,在構造Dialog對象時,這個mWindow的值也被確定。它由PolicyManager提供。再往下跟系統代碼。

makeNewWindow(Context)方法的實現如下:

1     // The static methods to spawn new policy-specific objects2     public static Window makeNewWindow(Context context) {3         return sPolicy.makeNewWindow(context);4     }

還得繼續往下跟。

 1 import com.andorid.policy.internal.policy.impl.Policy; 2 /** 3  * {@hide} 4  */ 5 public class Policy implements IPolicy { 6     //... 7  8     public Window makeNewWindow(Context context) { 9         return new PhoneWindow(context);10     }11 }

到這,貌似就差不多看到盡頭了,原來我們調用的setContentView就是在這個PhoneWindow類中被實現的。繼續跟進。

 1 import com.android.internal.policy.impl.PhoneWindow; 2 /** 3  * Android-specific Window. 4  * <p> 5  * todo: need to pull the generic functionality out into a base class 6  * in android.widget. 7  */ 8 public class PhoneWindow extends Window implements MenuBuilder.Callback { 9     //...10 }

 

setContentView(int)

這個方法的代碼實現如3所示。

【圖3】

 整個的實現流程乍一看還算簡單明了。我們傳入的布局參數最後就是被載入到所示的那個 mContentParent 中的。這個mContentParent是一個ViewGroup類對象。在所示的代碼中第367行作了空判斷,可見這個對象的執行個體的建立與installDecor()方法有關係。這個方法的實現較為複雜,這裡我們只看mContentParent的執行個體化過程。

【圖4】

  這個generateLayout()方法的實現過程很繁雜。我們沒有必要去把每一行的代碼都看懂。只需要知道在它內部是這樣建立mContentParent對象的就好了,。最後會把這個contentParent作為結果返回即可。然後再回到圖3,在圖中所示代碼處第378行完成了將我們傳入的布局載入進系統容器中的操作。

setContentView(View)與setContentView(View, ViewGroup.LayoutParams)

   這種方式設定內容布局比較靈活。一般用於布局中有需要在Java代碼中做特殊操作的布局。如設定監聽等。其具體實現代碼如5所示。

【圖5】

  這個代碼,並沒什麼特別的,它的目的都已經明明白白的表現在代碼上了,就不再贅述了。

 

setContentView(View)無法設定布局尺寸的問題

  使用setContentView(int)的方式時,可以直接通過在layout的根容器中指定寬、高來設定布局的尺寸。這裡得注意,我指的是在根視圖中直接指定寬度多少像素,高度多少像素這種直白的寫法才可以控制布局的尺寸。若直接設為MATCH_PARENT,那麼它的效果等同於WRAP_CONTENT。為什麼會是這樣的結果呢?本人並沒有深究它的原因,但本人猜測(且後續並未去證實)這與mContentParent載入了布局後重新確定整個視圖的尺寸的過程脫不了干係。我們來翻翻ViewGroup的代碼,在ViewGroup中,有如6所示的一段代碼。

【圖6】

  對於一個ViewGroup及其子類來說,它的MeasureSpec要麼是EXACTLY要麼是AT_MOST,我記不清這裡頭的具體關係了。但6所示的代碼也已經非常直白了。對於MATCH_PARENT,它確實是按照WRAP_CONTENT的方式來處理的。這也就解釋了上面所說的“令人費解”的情況了。雖然這個只是本人的猜測,但我估計也是八九不離十了。

  而使用setContentView(View)的方式時,無論layout中根容器的寬高是什麼,都按照WRAP_CONTENT的方式來走。這是為什嗎?我們先回去看看圖5所示代碼中這個方法的實現。可以發現,PhoneWindow給了一個預設的ViewGroup.LayoutParams對象。並且寬、高的值不偏不倚,正好是MATCH_PARENT。因此,當使用這種方式時,無論layout中根容器的寬高如何設定,它都表現成按內容的尺寸來適配布局的效果。因此,想要能控制對話方塊布局的尺寸,還是老老實實自己建一個有指定寬高值的LayoutParams對象給PhoneWindow對象吧,不要指望人家幫你擦屁股,擦不乾淨的~~

  那麼,到了這裡,我們再來簡單探究一下,setContentView(int)又是如何做到可以直接設定尺寸的。我們回去圖3,看看這個方法的實現代碼中,第378行。它在映射xml時,第二個參數傳的直接是mContentParent。比較一下我們平時使用映射布局函數時,講道理,直接傳一個null的比較多吧,或者說,或許我們平時都很少注意到這個參數。我們去LayoutInflater中轉轉。

1 import android.view.LayoutInflater;

inflate()方法的代碼還是挺長的,這裡就不詳細貼了,我們只挑有代表性的來看。

 1 public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) 2  3 // ... 4  5 final AttributeSet attrs = Xml.asAttributeSet(parser); 6  7 // ... 8  9 // Create layout params that match root, if supplied10 params = root.generateLayoutParams(attrs);11 12 // ...13 14 // Temp is the root view that was found in the xml15 final View temp = createViewFromTag(root, name, attrs, false);16 17 // ...18 19 if (root != null && attachToRoot) {20     root.addView(temp, params);21 }22 23 // ...

  夠清晰了吧。在這裡,LayoutInflater在映射xml布局時主動去解析了所有的屬性。當然會包括外層容器的屬性。然後根據解析的結果產生一個LayoutParams對象,最後,再將要內容布局聯合這個即時建立的LayoutParams對象一同添加到mContentParent容器中去,其實就相當於調用setContentView(View, LayoutParams)方法。所以在文章開頭我才說到Dialog的三個設定內容布局的方法本質是一樣的,只是其封裝深度不同而已。

 

設定Dialog的背景為完全透明

  Dialog預設有一個灰色的背景,首先這個背景巨醜,其次背景的存在還影響我們對對話方塊UI的定製。

     

  在Activity中,可以通過在建立Dialog時傳入一個無背景對話方塊的風格樣式給構造器,以構造出無灰色背景的對話方塊出來。也可以通過Java代碼控制對話方塊的背景色為透明色。還可以先show()對話方塊,然後再給它setContentView()來達到無背景色的對話方塊的目的。

1、 通過風格樣式

  

1 <style name="transBg" parent="@android:Theme.Dialog">2     <item name="android:windowBackground">@android:color/transparent"</item>3 </style>

 

1 AlertDialog dialog = new AlertDialog.Builder(mContext, R.style.transBg).create();2 //或3 Dialog dlg = new Dialog(mContext, R.style.transBg);4 //等

 

2、通過Java代碼控制

  所謂背景,其實就是PhoneWindow的背景。我們只需要設定PhoneWindow的背景為透明,就能達到我們想要的結果了。

1 // ...2 AlertDialog dialog = builder.create();3 dialog.show();4 dialog.setContentView(view);5 //方式1,使用透明的ColorDrawable對象。6 dialog.getWindow().setBackgroundDrawable(new ColorDrawable(0));7 //方式2,使用一張透明的Drawable圖片。8 dialog.getWindow().setBackgroundDrawableResource(R.drawable.transparent);

 

3、先顯示對話方塊再設定布局

  這種方式只在Activity中有效果。

1 AlertDialog dialog = builder.create();2 dialog.show();3 dialog.setContentView(view);

  至於Service中為什麼沒有效果,本人懷疑是由於在Service中要想彈出對話方塊,只能將它設為系統級對話方塊,需要加多的一段代碼導致的。但其具體原理還沒有去研究過。

1 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

 

  在Service中。只能通過上述第1、第2種方式來實現背景透明的目的。

 

此至,祝順!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.