Android中使用XML佈局應該是所有安卓開發者最熟悉的操作了,各種佈局的特性想必大家也都瞭如指掌。但是,真正利用好各個佈局以達到效能最優,或者配合實現一些單個佈局無法實現的特性,在筆者看來是一個非常值得花心思去研究的問題。本文提供的解題思路並不複雜,如果你有其他方案(只通過xml實現),歡迎在評論中留言。
效果展示
不多廢話,直接一個圖看下要達到的效果⬇️
在這個佈局中,EditText
實現了高度自適配,但限制於一屏內,文字超過一屏時則在EditText
控制元件內進行滑動。充分利用了EditText
的特性,避免了ScrollView
的使用。
實現思路
為了實現圖中的效果,分析可知最關鍵的點有三個:
-
有最低高度。這個最簡單,用
EditText
自帶的minLines
就可以達成效果 -
高度要設為
android:layout_height="wrap_content"
-
EditText所佔的空間只有一屏內的空白區域,並且不能超出此區域
於是我開始了不斷嘗試:
簡單的LinearLayout
和FrameLayout
都會出現當文字超出一屏高度時,直接就往螢幕外部繼續延長了。因為沒辦法將其限制在某個區域裡,如果用LinearLayout
的權重效果,那等於是直接佔滿空白區域了,明顯也不是我們想要的。
那麼需要一個可以限制其所在範圍的佈局,是不是一下就想到了用的最多也最方便的ConstrainLayout
。可惜ConstrainLayout
也不行,哪怕限制了底邊相關聯,但如果不設定高度為0dp,wrap_content
的情況仍然會超出螢幕,而高度設為0時等於直接佔滿了。
同樣還有使用Chains鏈,不僅位置無法固定,而且也解決不了高度越出的問題。最後我甚至還嘗試了使用兩個EditText
,同時輸入同樣的文字,一個隱藏掉只用來確定高度,另一個按它的高度進行適配。結果當然也失敗了,至少只用ConstrainLayout
我是沒有達成效果。
解決方案
在多次失敗後,我一度以為是不是無法實現這樣的效果了,要麼妥協將EditText
設為固定高度,或者使用ScrollView
來配合,或者通過程式碼動態獲取文字高度來設定控制元件高度。
但總不能那麼輕易妥協,然後我想起了RelativeLayout
,這個自從有了ConstrainLayout
之後就已經被冷落了的佈局。本來我以為後者是前者的"plus版",大哥都做不到的,小弟怎麼能做到呢?
結果我還真被打臉了,測試後發現,EditText
在RelativeLayout
佈局內,高度設為wrap_content
時,既可以自適應高度,而且控制元件高度會被直接限制在根佈局RelativeLayout
內,也就是不會越出螢幕!
這下就真的能實現了,下面直接貼上程式碼:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white_fff"
android:fitsSystemWindows="true"
android:focusable="true"
android:focusableInTouchMode="true">
<com.widget.TitleBar
android:id="@+id/titlebar"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_42"
app:leftText="返回"
app:leftTextDrawableLeft="@mipmap/top_return" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/dp_42">
<EditText
android:id="@+id/et_notice"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="14dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="@dimen/dp_62"
android:background="@drawable/guild_rect_solid_f2f2f2_radius_3"
android:gravity="start"
android:hint="請輸入公會公告"
android:includeFontPadding="false"
android:lineSpacingMultiplier="1.2"
android:maxLength="1000"
android:minLines="8"
android:paddingStart="14dp"
android:paddingTop="12dp"
android:paddingEnd="14dp"
android:paddingBottom="12dp"
android:textAppearance="@style/guild_TextAppearance.262122_14"/>
<TextView
android:id="@+id/tv_word_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@+id/et_notice"
android:layout_alignTop="@+id/tv_push"
android:layout_alignBottom="@+id/tv_push"
android:gravity="center"
android:includeFontPadding="false"
android:text="@string/guild_notice_tip"
android:textColor="@color/grey_999"
android:textSize="11sp" />
<TextView
android:id="@+id/tv_push"
android:layout_width="wrap_content"
android:layout_height="22dp"
android:layout_alignEnd="@+id/et_notice"
android:layout_alignBottom="@+id/et_notice"
android:layout_marginTop="@dimen/dp_14"
android:layout_marginBottom="-36dp"
android:background="@drawable/guild_rect_solid_f81a1a_radius_11"
android:gravity="center"
android:includeFontPadding="false"
android:paddingStart="9dp"
android:paddingEnd="9dp"
android:text="釋出"
android:textColor="@color/white_fff"
android:textSize="12sp" />
</RelativeLayout>
</FrameLayout>
複製程式碼
最後
一番實驗下來我發現雖然ConstrainLayout
和RelativeLayout
的很多api效果是差不多的,但實際上確實在一些臨界情況上還是有不一樣的表現。
比如RelativeLayout的layout_alignBottom="@+id/et_notice"
、layout_alignParentBottom="true"
的效果就是"底部對齊於其他控制元件的底部"和"底部對齊於根佈局View的底部"。在ConstrainLayout
中對應layout_constraintBottom_toBottomOf="@+id/et_notice"
和layout_constraintBottom_toBottomOf="parent"
。
雖然好像效果一樣,不過ConstrainLayout
中的margin
是無法設定負值的,而其他佈局可以,這一點是我覺得是ConstrainLayout
不太靈活的一點,雖然需要設負值的情況很少見。
以上就是我在專案中自己發現的比較有意思的地方,雖然簡單,但如果能幫助到別人就挺好的啦。