Android XML靈活佈局之 EditText實現自適應高度同時限制最小和最大高度

喵星人萊恩發表於2019-06-14

Android中使用XML佈局應該是所有安卓開發者最熟悉的操作了,各種佈局的特性想必大家也都瞭如指掌。但是,真正利用好各個佈局以達到效能最優,或者配合實現一些單個佈局無法實現的特性,在筆者看來是一個非常值得花心思去研究的問題。本文提供的解題思路並不複雜,如果你有其他方案(只通過xml實現),歡迎在評論中留言。

效果展示

不多廢話,直接一個圖看下要達到的效果⬇️

Jun-06-2019 19-34-17

在這個佈局中,EditText實現了高度自適配,但限制於一屏內,文字超過一屏時則在EditText控制元件內進行滑動。充分利用了EditText的特性,避免了ScrollView的使用。

實現思路

為了實現圖中的效果,分析可知最關鍵的點有三個:

  • 有最低高度。這個最簡單,用EditText自帶的minLines就可以達成效果

  • 高度要設為 android:layout_height="wrap_content"

  • EditText所佔的空間只有一屏內的空白區域,並且不能超出此區域

於是我開始了不斷嘗試:

簡單的LinearLayoutFrameLayout都會出現當文字超出一屏高度時,直接就往螢幕外部繼續延長了。因為沒辦法將其限制在某個區域裡,如果用LinearLayout的權重效果,那等於是直接佔滿空白區域了,明顯也不是我們想要的。

那麼需要一個可以限制其所在範圍的佈局,是不是一下就想到了用的最多也最方便的ConstrainLayout。可惜ConstrainLayout也不行,哪怕限制了底邊相關聯,但如果不設定高度為0dp,wrap_content的情況仍然會超出螢幕,而高度設為0時等於直接佔滿了。

同樣還有使用Chains鏈,不僅位置無法固定,而且也解決不了高度越出的問題。最後我甚至還嘗試了使用兩個EditText,同時輸入同樣的文字,一個隱藏掉只用來確定高度,另一個按它的高度進行適配。結果當然也失敗了,至少只用ConstrainLayout我是沒有達成效果。

解決方案

在多次失敗後,我一度以為是不是無法實現這樣的效果了,要麼妥協將EditText設為固定高度,或者使用ScrollView來配合,或者通過程式碼動態獲取文字高度來設定控制元件高度。

但總不能那麼輕易妥協,然後我想起了RelativeLayout,這個自從有了ConstrainLayout之後就已經被冷落了的佈局。本來我以為後者是前者的"plus版",大哥都做不到的,小弟怎麼能做到呢?

結果我還真被打臉了,測試後發現,EditTextRelativeLayout佈局內,高度設為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>
複製程式碼

最後

一番實驗下來我發現雖然ConstrainLayoutRelativeLayout的很多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不太靈活的一點,雖然需要設負值的情況很少見。

以上就是我在專案中自己發現的比較有意思的地方,雖然簡單,但如果能幫助到別人就挺好的啦。

相關文章