淺析Android Dialog中setContentView()方法
概述
Dialog在Android中是一個很優(yōu)秀的工具。在使用Dialog時,我們一般都會自定義要顯示的內容布局。Dialog自帶了三個方法來支持自定義內容布局。
public void setContentView (int layoutResID); public void setContentView (View view); public void setContentView (View view, ViewGroup.LayoutParams params);
這三個方法內部的實現(xiàn)原理都是一樣的,只是其封裝深度不同而已。三個方法可以說分別照顧了不同定制深度的開發(fā)者。
setContentView()流程
直接查看Dialog的源代碼,如下圖1所示。
【圖1】
上圖中mWindow在Dialog類中的定義如下:
import android.view.Window; Window mWindow;
那么,它從何而來呢?如下圖2所示。
【圖2】
由上圖2可知,在構造Dialog對象時,這個mWindow的值也被確定。它由PolicyManager提供。再往下跟系統(tǒng)代碼。
makeNewWindow(Context)方法的實現(xiàn)如下:
// The static methods to spawn new policy-specific objects public static Window makeNewWindow(Context context) { return sPolicy.makeNewWindow(context); }
還得繼續(xù)往下跟。
import com.andorid.policy.internal.policy.impl.Policy; /** * {@hide} */ public class Policy implements IPolicy { //... public Window makeNewWindow(Context context) { return new PhoneWindow(context); } }
到這,貌似就差不多看到盡頭了,原來我們調用的setContentView就是在這個PhoneWindow類中被實現(xiàn)的。繼續(xù)跟進。
import com.android.internal.policy.impl.PhoneWindow; /** * Android-specific Window. * <p> * todo: need to pull the generic functionality out into a base class * in android.widget. */ public class PhoneWindow extends Window implements MenuBuilder.Callback { //... }
setContentView(int)
這個方法的代碼實現(xiàn)如下圖3所示。
【圖3】
整個的實現(xiàn)流程乍一看還算簡單明了。我們傳入的布局參數最后就是被加載到上圖所示的那個 mContentParent 中的。這個mContentParent是一個ViewGroup類對象。
在上圖所示的代碼中第367行作了空判斷,可見這個對象的實例的創(chuàng)建與installDecor()方法有關系。這個方法的實現(xiàn)較為復雜,這里我們只看mContentParent的實例化過程。
【圖4】
這個generateLayout()方法的實現(xiàn)過程很繁雜。我們沒有必要去把每一行的代碼都看懂。只需要知道在它內部是這樣創(chuàng)建mContentParent對象的就好了
,
。
最后會把這個contentParent作為結果返回即可。然后再回到圖3,在圖中所示代碼處第378行完成了將我們傳入的布局加載進系統(tǒng)容器中的操作。
setContentView(View)與setContentView(View, ViewGroup.LayoutParams)
這種方式設置內容布局比較靈活。一般用于布局中有需要在Java代碼中做特殊操作的布局。其具體實現(xiàn)代碼如下圖5所示。
【圖5】
這個代碼,并沒什么特別的,它的目的都已經明明白白的表現(xiàn)在代碼上了,就不再贅述了。
setContentView(View)無法設置布局尺寸的問題
使用setContentView(int)的方式時,可以直接通過在layout的根容器中指定寬、高來設置布局的尺寸。這里得注意,我指的是在根視圖中直接指定寬度多少像素,高度多少像素這種直白的寫法才可以控制布局的尺寸。若直接設為MATCH_PARENT,那么它的效果等同于WRAP_CONTENT。為什么會是這樣的結果呢?本人并沒有深究它的原因,但本人猜測(且后續(xù)并未去證實)這與mContentParent加載了布局后重新確定整個視圖的尺寸的過程脫不了干系。我們來翻翻ViewGroup的代碼,在ViewGroup中,有如下圖6所示的一段代碼。
【圖6】
對于一個ViewGroup及其子類來說,它的MeasureSpec要么是EXACTLY要么是AT_MOST,我記不清這里頭的具體關系了。但上圖6所示的代碼也已經非常直白了。對于MATCH_PARENT,它確實是按照WRAP_CONTENT的方式來處理的。這也就解釋了上面所說的“令人費解”的情況了。雖然這個只是本人的猜測,但我估計也是差不多了。
而使用setContentView(View)的方式時,無論layout中根容器的寬高是什么,都按照WRAP_CONTENT的方式來走。這是為什么?我們先回去看看圖5所示代碼中這個方法的實現(xiàn)??梢园l(fā)現(xiàn),PhoneWindow給了一個默認的ViewGroup.LayoutParams對象。并且寬、高的值不偏不倚,正好是MATCH_PARENT。因此,當使用這種方式時,無論layout中根容器的寬高如何設置,它都表現(xiàn)成按內容的尺寸來適配布局的效果。因此,想要能控制對話框布局的尺寸,還是老老實實自己建一個有指定寬高值的LayoutParams對象給PhoneWindow對象吧
那么,到了這里,我們再來簡單探究一下,setContentView(int)又是如何做到可以直接設置尺寸的。我們回去圖3,看看這個方法的實現(xiàn)代碼中,第378行。它在映射xml時,第二個參數傳的直接是mContentParent。比較一下我們平時使用映射布局函數時,講道理,直接傳一個null的比較多吧,或者說,或許我們平時都很少注意到這個參數。我們去LayoutInflater中轉轉。
import android.view.LayoutInflater;
inflate()方法的代碼還是挺長的,這里就不詳細貼了,我們只挑有代表性的來看。
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) // ... final AttributeSet attrs = Xml.asAttributeSet(parser); // ... // Create layout params that match root, if supplied params = root.generateLayoutParams(attrs); // ... // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, attrs, false); // ... if (root != null && attachToRoot) { root.addView(temp, params); } // ...
夠清晰了吧。在這里,LayoutInflater在映射xml布局時主動去解析了所有的屬性。當然會包括外層容器的屬性。然后根據解析的結果生成一個LayoutParams對象,最后,再將要內容布局聯(lián)合這個即時創(chuàng)建的LayoutParams對象一同添加到mContentParent容器中去,其實就相當于調用setContentView(View, LayoutParams)方法。所以在文章開頭我才說到Dialog的三個設置內容布局的方法本質是一樣的,只是其封裝深度不同而已。
設置Dialog的背景為完全透明
Dialog默認有一個灰色的背景,首先這個背景巨丑,其次背景的存在還影響我們對對話框UI的定制。
在Activity中,可以通過在創(chuàng)建Dialog時傳入一個無背景對話框的風格樣式給構造器,以構造出無灰色背景的對話框出來。也可以通過Java代碼控制對話框的背景色為透明色。還可以先show()對話框,然后再給它setContentView()來達到無背景色的對話框的目的。
1、 通過風格樣式
<style name="transBg" parent="@android:Theme.Dialog"> <item name="android:windowBackground">@android:color/transparent"</item> </style> AlertDialog dialog = new AlertDialog.Builder(mContext, R.style.transBg).create(); //或 Dialog dlg = new Dialog(mContext, R.style.transBg); //等
2、通過Java代碼控制
所謂背景,其實就是PhoneWindow的背景。我們只需要設置PhoneWindow的背景為透明,就能達到我們想要的結果了。
// ... AlertDialog dialog = builder.create(); dialog.show(); dialog.setContentView(view); //方式1,使用透明的ColorDrawable對象。 dialog.getWindow().setBackgroundDrawable(new ColorDrawable(0)); //方式2,使用一張透明的Drawable圖片。 dialog.getWindow().setBackgroundDrawableResource(R.drawable.transparent);
3、先顯示對話框再設置布局
這種方式只在Activity中有效果。
AlertDialog dialog = builder.create(); dialog.show(); dialog.setContentView(view);
至于Service中為什么沒有效果,本人懷疑是由于在Service中要想彈出對話框,只能將它設為系統(tǒng)級對話框,需要加多的一段代碼導致的。但其具體原理還沒有去研究過。
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
在Service中。只能通過上述第1、第2種方式來實現(xiàn)背景透明的目的。
以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!
相關文章
android中ListView多次刷新重復執(zhí)行getView的解決方法
以前倒是沒有注意listview的getView會重復執(zhí)行多次,在測試的時候去斷點跟蹤,發(fā)現(xiàn)同一條數據不斷的重復執(zhí)行,下面與大家分享下正確的解決方法,希望對你有所幫助2013-06-06詳解RxJava2 Retrofit2 網絡框架簡潔輕便封裝
本篇文章主要介紹了詳解RxJava2 Retrofit2 網絡框架簡潔輕便封裝,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12Android實現(xiàn)音頻條形圖效果(仿音頻動畫無監(jiān)聽音頻輸入)
這篇文章主要介紹了Android實現(xiàn)音頻條形圖效果(仿音頻動畫無監(jiān)聽音頻輸入)的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-09-09