淺析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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...