淺析Android Dialog中setContentView()方法

来源:http://www.cnblogs.com/chorm590/archive/2017/05/15/6854531.html
-Advertisement-
Play Games

2017-05-15 概述 Dialog在Android中是一個很優秀的工具。在使用Dialog時,我們一般都會自定義要顯示的內容佈局。Dialog自帶了三個方法來支持自定義內容佈局。 這三個方法內部的實現原理都是一樣的,只是其封裝深度不同而已。三個方法可以說分別照顧了不同定製深度的開發者。 set ...


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 objects
2     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 supplied
10 params = root.generateLayoutParams(attrs);
11 
12 // ...
13 
14 // Temp is the root view that was found in the xml
15 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種方式來實現背景透明的目的。

 

此至,祝順!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/70854942 本文出自 "【趙彥軍的博客】" GPS_Presenter GPS_Interface 回調介面 在 Activity 中使用 ...
  • 轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/71750907 本文出自 "【趙彥軍的博客】" 什麼是 Monkey Monkey 是一個 Android 自動化測試小工具。主要用於Android 的壓力測試, 主要目的就是為了測試 ...
  • Unity項目,需要用Xcode運行,結果報了錯誤。 解決方案: 1、打開終端, 2、輸入以下命令: chmod +x /Users/......./MapFileParser.sh (MapFileParser.sh所在的目錄) ...
  • https://developer.apple.com/contact/phone/ ...
  • 一,效果圖。 二,工程圖。 三,代碼。 RootViewController.h RootViewController.m ...
  • 首頁中點擊城市TextView調轉到當前城市選擇Activity fragment_city.xml ...
  • 一 fragment_home.xml 二 home_head_page.xml banner 兩頁Bar標誌 熱門電影三個作為一體addHeaderView(headView) 進RefreshListView 三 GoodsListAdapter: 1.SimpleDraweeView實現圓角圖 ...
  • 引言 在項目中,為了使用GreenDAO的自動生成DAO class的功能,我們必須建立entity,該entity通過java註解標識。 Schema是資料庫對象集合,我們可以通過gradle插件配置GreenDAO,除此之外,我們至少需要配置Schema的版本: 我們不僅可以配置schemaVe ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...