国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 系統(tǒng) > Android > 正文

淺析Android Dialog中setContentView()方法

2019-12-12 02:53:17
字體:
供稿:網(wǎng)友

概述

Dialog在Android中是一個(gè)很優(yōu)秀的工具。在使用Dialog時(shí),我們一般都會(huì)自定義要顯示的內(nèi)容布局。Dialog自帶了三個(gè)方法來支持自定義內(nèi)容布局。

 public void setContentView (int layoutResID); public void setContentView (View view); public void setContentView (View view, ViewGroup.LayoutParams params);

這三個(gè)方法內(nèi)部的實(shí)現(xiàn)原理都是一樣的,只是其封裝深度不同而已。三個(gè)方法可以說分別照顧了不同定制深度的開發(fā)者。

setContentView()流程

直接查看Dialog的源代碼,如下圖1所示。

【圖1】

上圖中mWindow在Dialog類中的定義如下:

 import android.view.Window; Window mWindow;

那么,它從何而來呢?如下圖2所示。

【圖2】

由上圖2可知,在構(gòu)造Dialog對(duì)象時(shí),這個(gè)mWindow的值也被確定。它由PolicyManager提供。再往下跟系統(tǒng)代碼。

makeNewWindow(Context)方法的實(shí)現(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); }}

到這,貌似就差不多看到盡頭了,原來我們調(diào)用的setContentView就是在這個(gè)PhoneWindow類中被實(shí)現(xiàn)的。繼續(xù)跟進(jìn)。

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)

這個(gè)方法的代碼實(shí)現(xiàn)如下圖3所示。

【圖3】

整個(gè)的實(shí)現(xiàn)流程乍一看還算簡(jiǎn)單明了。我們傳入的布局參數(shù)最后就是被加載到上圖所示的那個(gè) mContentParent 中的。這個(gè)mContentParent是一個(gè)ViewGroup類對(duì)象。

在上圖所示的代碼中第367行作了空判斷,可見這個(gè)對(duì)象的實(shí)例的創(chuàng)建與installDecor()方法有關(guān)系。這個(gè)方法的實(shí)現(xiàn)較為復(fù)雜,這里我們只看mContentParent的實(shí)例化過程。

【圖4】

這個(gè)generateLayout()方法的實(shí)現(xiàn)過程很繁雜。我們沒有必要去把每一行的代碼都看懂。只需要知道在它內(nèi)部是這樣創(chuàng)建mContentParent對(duì)象的就好了

最后會(huì)把這個(gè)contentParent作為結(jié)果返回即可。然后再回到圖3,在圖中所示代碼處第378行完成了將我們傳入的布局加載進(jìn)系統(tǒng)容器中的操作。

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

這種方式設(shè)置內(nèi)容布局比較靈活。一般用于布局中有需要在Java代碼中做特殊操作的布局。如設(shè)置監(jiān)聽等。其具體實(shí)現(xiàn)代碼如下圖5所示。

【圖5】

這個(gè)代碼,并沒什么特別的,它的目的都已經(jīng)明明白白的表現(xiàn)在代碼上了,就不再贅述了。

setContentView(View)無法設(shè)置布局尺寸的問題

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

【圖6】

對(duì)于一個(gè)ViewGroup及其子類來說,它的MeasureSpec要么是EXACTLY要么是AT_MOST,我記不清這里頭的具體關(guān)系了。但上圖6所示的代碼也已經(jīng)非常直白了。對(duì)于MATCH_PARENT,它確實(shí)是按照WRAP_CONTENT的方式來處理的。這也就解釋了上面所說的“令人費(fèi)解”的情況了。雖然這個(gè)只是本人的猜測(cè),但我估計(jì)也是八九不離十了。

而使用setContentView(View)的方式時(shí),無論layout中根容器的寬高是什么,都按照WRAP_CONTENT的方式來走。這是為什么?我們先回去看看圖5所示代碼中這個(gè)方法的實(shí)現(xiàn)。可以發(fā)現(xiàn),PhoneWindow給了一個(gè)默認(rèn)的ViewGroup.LayoutParams對(duì)象。并且寬、高的值不偏不倚,正好是MATCH_PARENT。因此,當(dāng)使用這種方式時(shí),無論layout中根容器的寬高如何設(shè)置,它都表現(xiàn)成按內(nèi)容的尺寸來適配布局的效果。因此,想要能控制對(duì)話框布局的尺寸,還是老老實(shí)實(shí)自己建一個(gè)有指定寬高值的LayoutParams對(duì)象給PhoneWindow對(duì)象吧,不要指望人家?guī)湍悴疗ü桑敛桓蓛舻膥~

那么,到了這里,我們?cè)賮砗?jiǎn)單探究一下,setContentView(int)又是如何做到可以直接設(shè)置尺寸的。我們回去圖3,看看這個(gè)方法的實(shí)現(xiàn)代碼中,第378行。它在映射xml時(shí),第二個(gè)參數(shù)傳的直接是mContentParent。比較一下我們平時(shí)使用映射布局函數(shù)時(shí),講道理,直接傳一個(gè)null的比較多吧,或者說,或許我們平時(shí)都很少注意到這個(gè)參數(shù)。我們?nèi)ayoutInflater中轉(zhuǎn)轉(zhuǎn)。

 import android.view.LayoutInflater;

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

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)// ...final AttributeSet attrs = Xml.asAttributeSet(parser);// ...// Create layout params that match root, if suppliedparams = root.generateLayoutParams(attrs);// ...// Temp is the root view that was found in the xmlfinal View temp = createViewFromTag(root, name, attrs, false);// ...if (root != null && attachToRoot) { root.addView(temp, params);}// ...

夠清晰了吧。在這里,LayoutInflater在映射xml布局時(shí)主動(dòng)去解析了所有的屬性。當(dāng)然會(huì)包括外層容器的屬性。然后根據(jù)解析的結(jié)果生成一個(gè)LayoutParams對(duì)象,最后,再將要內(nèi)容布局聯(lián)合這個(gè)即時(shí)創(chuàng)建的LayoutParams對(duì)象一同添加到mContentParent容器中去,其實(shí)就相當(dāng)于調(diào)用setContentView(View, LayoutParams)方法。所以在文章開頭我才說到Dialog的三個(gè)設(shè)置內(nèi)容布局的方法本質(zhì)是一樣的,只是其封裝深度不同而已。

設(shè)置Dialog的背景為完全透明

Dialog默認(rèn)有一個(gè)灰色的背景,首先這個(gè)背景巨丑,其次背景的存在還影響我們對(duì)對(duì)話框UI的定制。

   

在Activity中,可以通過在創(chuàng)建Dialog時(shí)傳入一個(gè)無背景對(duì)話框的風(fēng)格樣式給構(gòu)造器,以構(gòu)造出無灰色背景的對(duì)話框出來。也可以通過Java代碼控制對(duì)話框的背景色為透明色。還可以先show()對(duì)話框,然后再給它setContentView()來達(dá)到無背景色的對(duì)話框的目的。

1、 通過風(fēng)格樣式

 <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代碼控制

所謂背景,其實(shí)就是PhoneWindow的背景。我們只需要設(shè)置PhoneWindow的背景為透明,就能達(dá)到我們想要的結(jié)果了。

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

3、先顯示對(duì)話框再設(shè)置布局

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

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

至于Service中為什么沒有效果,本人懷疑是由于在Service中要想彈出對(duì)話框,只能將它設(shè)為系統(tǒng)級(jí)對(duì)話框,需要加多的一段代碼導(dǎo)致的。但其具體原理還沒有去研究過。

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

在Service中。只能通過上述第1、第2種方式來實(shí)現(xiàn)背景透明的目的。

以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時(shí)也希望多多支持武林網(wǎng)!

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 岗巴县| 麻栗坡县| 蒙山县| 陆丰市| 北碚区| 海门市| 大兴区| 泰来县| 岳阳市| 南平市| 五常市| 朝阳区| 惠东县| 霍山县| 秦皇岛市| 洛隆县| 夏津县| 施秉县| 平安县| 黎川县| 郓城县| 定远县| 漯河市| 错那县| 鹤峰县| 贵阳市| 加查县| 沐川县| 内江市| 庆云县| 民丰县| 开封市| 靖边县| 华安县| 惠东县| 易门县| 即墨市| 安徽省| 邯郸县| 剑阁县| 土默特右旗|