要求:把WebView裝進AlertDialog,而且實現Dialog的高度根據WebView的ContentHeight高度來變化,該怎麼作?android
固然是直接經過AlertDialog的setView方法,把WebView設置進去就行了,並不須要其餘的特殊技巧,並且當WebView的ContentHeight超過屏幕高度的時候,WebView還能夠自動變成滾動模式,很是nice。bash
好的,如今產品需求變了,要在WebView下面增長一個自定義的「肯定」按鈕。
也很簡單,咱們用一個LinearLayout來裝上WebView和Button,一樣經過setView來把這個Layout設置爲Dialog視圖,運行,大功告……不對,有問題。
當WebView的ContentHeight超過屏幕高度的時候,下方的自定義「肯定」按鈕就被擠得不見了,而正常的需求都會是優先保證Button的顯示,WebView變成滾動纔對。
那麼,問題來了,怎麼把WebView和Button一塊兒裝進AlertDialog,還要實現Dialo的高度適應WebView的高度?ide
對付這種部分固定部分彈性的佈局,很容易讓人想到用RelativeLayout來解決。
在RelativeLayout佈局內,將Button設置爲alignParentBottom=true,即靠着父佈局底部放置,將WebView置於Button的上方,再將根佈局的高度設置爲wrap_content。這樣應該就能夠保證,Button是固定顯示在彈窗底部的,WebView顯示於Button的上方,使得WebView內容超高時也不會把button給擠掉。當內容超高時,能夠看到效果以下:
函數
是什麼致使的這種現象呢?一個一個屬性的查,很快就確認了,Button的alignParentBottom屬性設置爲true的時候就會致使這種現象發生。
我原本想看看RelativeLayout的源碼來找找緣由,結果剛看到這個類的註釋,緣由就找到了。且看註釋:工具
* <p>
* Note that you cannot have a circular dependency between the size of the RelativeLayout and the
* position of its children. For example, you cannot have a RelativeLayout whose height is set to
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to
* {@link #ALIGN_PARENT_BOTTOM}.
* </p>複製代碼
在RelativeLayout的size設置和子視圖的定位之中,二者不容許產生循環依賴,好比,將RelativeLayout高設置爲wrap_content,又將子視圖設置爲align_parent_bottom。
這個例子簡直就是針對我來舉的啊!註釋中說的循環依賴在哪裏呢?仔細想一想看,wrap_content致使RelativeLayout須要等待子視圖肯定好高度,它才能肯定好本身的高度;而align_parent_bottom致使子視圖須要等待父視圖RelativeLayout肯定了底部位置才能肯定本身的位置,但是父視圖如今都不知道本身多高,怎麼知道本身的底部在哪裏呢?因此父視圖乾脆宣佈wrap_content無效,用match_parent來作高度了。佈局
用RelativeLayout是作不到了,其餘的佈局彷佛都不善於應對這種「固定+浮動」高度的場景,怎麼辦呢?測試
其實,原生的Dialog實現就已經給了我答案。
Dialog#setView不管傳入多高的視圖,它老是不可能把Dialog的標題,還有「肯定」,「取消」這些按鈕給擠走的。那麼,只要按照這種原生實現方式來作,就能夠知足個人需求了。嘿,又到了「read the fucking code」的時候了。
AlertDialog負責View展現,裏面關聯一個AlertDialogController來操做邏輯,咱們要找它關聯的layout文件,直接在它的構造函數裏就能夠看到,文件名爲alert_dialog.xml。ui
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/parentPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="9dip"
android:paddingBottom="3dip"
android:paddingStart="3dip"
android:paddingEnd="1dip">
<LinearLayout android:id="@+id/topPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="54dip"
android:orientation="vertical">
<LinearLayout android:id="@+id/title_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="6dip"
android:layout_marginBottom="9dip"
android:layout_marginStart="10dip"
android:layout_marginEnd="10dip">
<ImageView android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingTop="6dip"
android:paddingEnd="10dip"
android:src="@drawable/ic_dialog_info" />
<com.android.internal.widget.DialogTitle android:id="@+id/alertTitle"
style="?android:attr/textAppearanceLarge"
android:singleLine="true"
android:ellipsize="end"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="viewStart" />
</LinearLayout>
<ImageView android:id="@+id/titleDivider"
android:layout_width="match_parent"
android:layout_height="1dip"
android:visibility="gone"
android:scaleType="fitXY"
android:gravity="fill_horizontal"
android:src="@android:drawable/divider_horizontal_dark" />
<!-- If the client uses a customTitle, it will be added here. -->
</LinearLayout>
<LinearLayout android:id="@+id/contentPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<ScrollView android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="2dip"
android:paddingBottom="12dip"
android:paddingStart="14dip"
android:paddingEnd="10dip"
android:overScrollMode="ifContentScrolls">
<TextView android:id="@+id/message"
style="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dip" />
</ScrollView>
</LinearLayout>
<FrameLayout android:id="@+id/customPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
<FrameLayout android:id="@+android:id/custom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="5dip"
android:paddingBottom="5dip" />
</FrameLayout>
<LinearLayout android:id="@+id/buttonPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="54dip"
android:orientation="vertical" >
<LinearLayout
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="4dip"
android:paddingStart="2dip"
android:paddingEnd="2dip"
android:measureWithLargestChild="true">
<LinearLayout android:id="@+id/leftSpacer"
android:layout_weight="0.25"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone" />
<Button android:id="@+id/button1"
android:layout_width="0dip"
android:layout_gravity="start"
android:layout_weight="1"
style="?android:attr/buttonBarButtonStyle"
android:maxLines="2"
android:layout_height="wrap_content" />
<Button android:id="@+id/button3"
android:layout_width="0dip"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
style="?android:attr/buttonBarButtonStyle"
android:maxLines="2"
android:layout_height="wrap_content" />
<Button android:id="@+id/button2"
android:layout_width="0dip"
android:layout_gravity="end"
android:layout_weight="1"
style="?android:attr/buttonBarButtonStyle"
android:maxLines="2"
android:layout_height="wrap_content" />
<LinearLayout android:id="@+id/rightSpacer"
android:layout_width="0dip"
android:layout_weight="0.25"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>
</LinearLayout>複製代碼
佈局元素不少,可是思路很清晰。
根元素爲LinearLayout,共有四個子視圖。
Title視圖,展現標題icon和文案;
Content視圖,展現Message,能夠經過setMessage來展現文案,爲應付文案過長問題還包了一層ScrollView;
Custom視圖,看到名字就知道,setView設置的View就塞在這裏;
Button視圖,放positive,neutral,negative三個按鈕用的。spa
再細看,能夠看出Title和Button設置了MinHeight=54dp,沒有設置weight;
而Content和Custom設置了weight=1,沒有設置MinHeight;3d
這,就是使得Title和Button視圖的高度能固定,而Content和Custom視圖的高度浮動的關鍵屬性。
在LinearLayout視圖裏,若是子視圖都設置了weight,那麼就按照weight權重來分配各自的高度(寬度);若是並非全部的子視圖都有weight,那麼,LinearLayout會優先把高度(寬度)分配給無weight的子視圖,分配完後,再把剩下的高度(寬度)按weight權重分配給其餘子視圖。
因此,最後用LinearLayout來實現咱們的需求就很簡單了:將根佈局LinearLayout高度設置爲wrap_content,裏面上下放置WebView和Button,將WebView的weight設置爲1(其實任意非0值均可以),高度能夠設置爲任意值(Android Studio會建議設置爲0dp),再將Button高度設置爲wrap_content,不設置weight值,就能夠了。
編譯運行,測試WebView高度越界和高度不越界的狀況,表現都符合需求了,完美!
綜上,本輪問題的解決涉及到兩個點:
評論區的@四月一號 同窗有一種利用負值的margin來實現的方式,也能夠解決問題,你們能夠參考一下。