設定文字欄位

TextField 可讓使用者輸入及修改文字。您可以使用的文字欄位有兩種:狀態型文字欄位和值型文字欄位。選取要顯示內容的類型:

建議使用狀態式文字欄位,因為這種方法更完整可靠,可管理 TextField 的狀態。下表列出這些文字欄位類型的差異,並說明狀態文字欄位的主要優點:

功能

以值為準的文字欄位

依狀態顯示的文字欄位

州級福利

狀態管理

使用 onValueChange 回呼更新文字欄位狀態。您必須根據 onValueChange 回報的變更,更新自身狀態中的 value。

明確使用 TextFieldState 物件管理文字輸入狀態 (值、選取項目和組合)。這個狀態可以記住並分享。

  • onValueChange 回呼已移除,因此無法導入非同步行為。
  • 狀態在重新組成、設定和程序終止後仍會保留。

視覺化轉換

使用 VisualTransformation 修改顯示文字的外觀。這通常會在單一步驟中處理輸入和輸出格式。

使用 InputTransformation 在使用者輸入內容提交至狀態前進行修改,並使用 OutputTransformation 格式化文字欄位內容,而不變更基礎狀態資料。

  • 您不再需要使用 OutputTransformation 提供原始文字和轉換後文字之間的偏移對應。

行數限制

接受 singleLine: Boolean, maxLines: Int 和 minLines: Int,以控制行數。

使用 lineLimits: TextFieldLineLimits 設定文字欄位可占用的行數下限和上限。

  • 提供 TextFieldLineLimits 類型的 lineLimits 參數,消除設定行數限制時的模糊空間。

安全文字欄位

不適用

SecureTextField 是以狀態文字欄位為基礎建構的可組合項,用於編寫密碼欄位。

  • 可讓您在幕後最佳化安全性,並隨附 textObfuscationMode 的預先定義 UI。

本頁說明如何實作 TextField、設定 TextField 輸入樣式,以及設定其他 TextField 選項,例如鍵盤選項和視覺化轉換使用者輸入內容。

選擇 TextField 導入方式

TextField 實作分為兩個層級:

  1. TextField 是 Material Design 實作。建議您選擇此實作程序,因為符合 Material Design 準則:
    • 預設樣式為填滿
    • OutlinedTextField 是外框樣式版本
  2. BasicTextField 可讓使用者透過硬體或螢幕鍵盤編輯文字,但無法提供提示或預留位置等裝飾。

TextField(
    state = rememberTextFieldState(initialText = "Hello"),
    label = { Text("Label") }
)

含有

OutlinedTextField(
    state = rememberTextFieldState(),
    label = { Text("Label") }
)

可編輯文字欄位,帶有紫色邊框和標籤。

樣式 TextField

TextField 和 BasicTextField 有許多共同的自訂參數。TextField 原始碼內提供 TextField 的完整清單。以下僅列舉部分實用參數:

  • textStyle
  • lineLimits

TextField(
    state = rememberTextFieldState("Hello\nWorld\nInvisible"),
    lineLimits = TextFieldLineLimits.MultiLine(maxHeightInLines = 2),
    placeholder = { Text("") },
    textStyle = TextStyle(color = Color.Blue, fontWeight = FontWeight.Bold),
    label = { Text("Enter text") },
    modifier = Modifier.padding(20.dp)
)

多行 TextField,包含可編輯的兩行加上標籤

如果設計需要 Material TextField 或 OutlinedTextField,建議使用 BasicTextField 而非 TextField。不過,如果建構的設計不需要使用 Material 規格的裝飾,則應使用 BasicTextField。

設定行數限制

TextField 可組合項支援沿著單一軸線捲動。捲動行為由 lineLimits 參數決定。設定為單行的 TextField 會水平捲動,而多行 TextField 則會垂直捲動。

使用 TextFieldLineLimits 為 TextField 選擇適當的線條設定:

TextField(
    state = rememberTextFieldState(),
    lineLimits = TextFieldLineLimits.SingleLine
)

單行文字欄位,內含文字

SingleLine 設定具有下列特性:

  • 文字一律不會換行,也不允許換行。
  • TextField 的高度一律固定。
  • 如果文字溢出,則會水平捲動。

TextField(
    state = rememberTextFieldState("Hello\nWorld\nHello\nWorld"),
    lineLimits = TextFieldLineLimits.MultiLine(1, 4)
)

多行文字欄位,內含文字

MultiLine 設定具有下列特性:

  • 接受兩個參數:minHeightInLines 和 maxHeightInLines。
  • 文字欄位的高度至少為 minHeightInLines。
  • 如果文字溢出,就會自動換行。
  • 如果文字需要更多行,欄位會增加到 maxHeightInLines 高,並垂直捲動。

使用 Brush API 設定輸入內容樣式

您可以使用 Brush API,在 TextField 中套用更進階的樣式。以下章節說明如何使用 Brush 為 TextField 輸入內容加上彩色漸層。

如要進一步瞭解如何使用 Brush API 設定文字樣式,請參閱「使用 Brush API 啟用進階樣式設定」。

使用 TextStyle 實作彩色漸層

如要在 TextField 中輸入文字時套用彩色漸層,請將所選筆刷設為 TextField 的 TextStyle。在本例中,我們使用內建筆刷和 linearGradient,在 TextField 中輸入文字時,查看彩虹漸層效果。

val brush = remember {
    Brush.linearGradient(
        colors = listOf(Color.Red, Color.Yellow, Color.Green, Color.Blue, Color.Magenta)
    )
}
TextField(
    state = rememberTextFieldState(), textStyle = TextStyle(brush = brush)
)

在文字欄位中輸入文字,顯示彩虹漸層效果。
圖 1. TextField內容的彩虹漸層效果。

管理文字欄位狀態

TextField 會使用名為 TextFieldState 的專屬狀態容器類別,來處理內容和選取項目。TextFieldState 的設計可提升至架構中的任何位置。TextFieldState 提供 2 項主要屬性:

  • initialText:TextField 的內容。
  • initialSelection::指出游標或選取範圍的位置。

TextFieldState 與其他方法 (例如 onValueChange 回呼) 的不同之處在於,TextFieldState 會完整封裝整個輸入流程。包括使用正確的備份資料結構、內嵌篩選器和格式器,以及同步處理來自不同來源的所有編輯內容。

您可以使用 TextFieldState() 提升 TextField 中的狀態。建議使用 rememberTextFieldState() 函式。rememberTextFieldState() 會在可組合函式中建立 TextFieldState 例項,確保狀態物件已記住,並提供內建的儲存和還原功能:

val usernameState = rememberTextFieldState()
TextField(
    state = usernameState,
    lineLimits = TextFieldLineLimits.SingleLine,
    placeholder = { Text("Enter Username") }
)

rememberTextFieldState 可以有空白參數,也可以傳入初始值,代表初始化時的文字值。如果在後續重組中傳遞不同的值,狀態值不會更新。如要在初始化後更新狀態,請在 TextFieldState 上呼叫編輯方法。

TextField(
    state = rememberTextFieldState(initialText = "Username"),
    lineLimits = TextFieldLineLimits.SingleLine,
)

TextField,文字欄位中顯示「Username」文字。
圖 2. TextField,初始文字為「Username」。

使用 TextFieldBuffer 修改文字

TextFieldBuffer 是可編輯的文字容器,功能與 StringBuilder 類似。其中包含文字內容和選取範圍的相關資訊。

您經常會在 TextFieldState.edit、InputTransformation.transformInput 或 OutputTransformation.transformOutput 等函式中,遇到 TextFieldBuffer 做為接收器範圍。在這些函式中,您可以視需要讀取或更新 TextFieldBuffer。之後,這些變更會提交至 TextFieldState,或在 OutputTransformation 的情況下傳遞至轉譯管道。

您可以使用 append、insert、replace 或 delete 等標準編輯功能修改緩衝區內容。如要變更選取狀態,請直接設定其 selection: TextRange 變數,或使用 placeCursorAtEnd 或 selectAll 等公用函式。選取範圍本身由 TextRange 表示,其中起始索引為包含,結束索引為排除。如果 TextRange 的起始值和結束值相同 (例如 (3, 3)),代表游標位置未選取任何字元。

val phoneNumberState = rememberTextFieldState("1234567890")

TextField(
    state = phoneNumberState,
    keyboardOptions = KeyboardOptions(
        keyboardType = KeyboardType.Phone
    ),
    inputTransformation = InputTransformation.maxLength(10).then {
        if (!asCharSequence().isDigitsOnly()) {
            revertAllChanges()
        }
    },
    outputTransformation = OutputTransformation {
        if (length > 0) insert(0, "(")
        if (length > 4) insert(4, ")")
        if (length > 8) insert(8, "-")
    }
)

編輯「TextFieldState」中的文字

您可以透過多種方法,直接使用狀態變數編輯狀態:

  • edit:可讓您編輯狀態內容,並提供 TextFieldBuffer 函式,以便您使用 insert、replace、append 等方法。

    // Initial textFieldState text passed in is "I love Android"
    // textFieldState.text : I love Android
    // textFieldState.selection: TextRange(14, 14)
    textFieldState.edit { insert(14, "!") }
    // textFieldState.text : I love Android!
    // textFieldState.selection: TextRange(15, 15)
    textFieldState.edit { replace(7, 14, "Compose") }
    // textFieldState.text : I love Compose!
    // textFieldState.selection: TextRange(15, 15)
    textFieldState.edit { append("!!!") }
    // textFieldState.text : I love Compose!!!!
    // textFieldState.selection: TextRange(18, 18)
    textFieldState.edit { selectAll() }
    // textFieldState.text : I love Compose!!!!
    // textFieldState.selection: TextRange(0, 18)

  • setTextAndPlaceCursorAtEnd:清除目前的文字,並以指定文字取代,然後將游標置於結尾。

    textFieldState.setTextAndPlaceCursorAtEnd("I really love Android")
    // textFieldState.text : I really love Android
    // textFieldState.selection : TextRange(21, 21)

  • clearText:清除所有文字。

    textFieldState.clearText()
    // textFieldState.text :
    // textFieldState.selection : TextRange(0, 0)

如需其他 TextFieldState 函式,請參閱 TextFieldState 參考資料。

修改使用者輸入內容

以下各節說明如何修改使用者輸入內容。輸入轉換可讓您在使用者輸入時篩選 TextField 輸入內容,而輸出轉換則會在使用者輸入內容顯示在畫面上之前設定格式。

使用輸入轉換功能篩選使用者輸入內容

輸入內容轉換功能可篩選使用者輸入內容。舉例來說,如果 TextField 接受美國電話號碼,您可能只希望接受 10 位數。InputTransformation 的結果會儲存在 TextFieldState 中。

系統內建篩選器,可因應常見的 InputTransformation 用途。如要限制長度,請呼叫 InputTransformation.maxLength():

TextField(
    state = rememberTextFieldState(),
    lineLimits = TextFieldLineLimits.SingleLine,
    inputTransformation = InputTransformation.maxLength(10)
)

自訂輸入轉換

InputTransformation 是單一函式介面。實作自訂 InputTransformation 時,您需要覆寫 TextFieldBuffer.transformInput:

class CustomInputTransformation : InputTransformation {
    override fun TextFieldBuffer.transformInput() {
    }
}

如果是電話號碼,請新增自訂輸入轉換,只允許在 TextField 中輸入數字:

class DigitOnlyInputTransformation : InputTransformation {
    override fun TextFieldBuffer.transformInput() {
        if (!asCharSequence().isDigitsOnly()) {
            revertAllChanges()
        }
    }
}

鏈結輸入轉換

如要在文字輸入中加入多個篩選器,請使用 then 擴充功能函式鏈結 InputTransformation。篩選器會依序執行。最佳做法是先套用最精細的篩選器,避免對最終會篩除的資料進行不必要的轉換。

TextField(
    state = rememberTextFieldState(),
    inputTransformation = InputTransformation.maxLength(6)
        .then(CustomInputTransformation()),
)

新增輸入轉換後,TextField 輸入最多可接受 10 位數。

在顯示前設定輸入內容格式

OutputTransformation 可讓您在使用者輸入內容顯示在畫面上之前,先設定其格式。與 InputTransformation 不同,透過 OutputTransformation 完成的格式設定不會儲存在 TextFieldState 中。以上述電話號碼為例,您需要在適當位置加上括號和連字號:

美國電話號碼,格式正確,包含括號、破折號和對應的索引。
圖 3. 格式正確且有對應索引的美國電話號碼。

這是處理以值為基礎的 TextField 中 VisualTransformation 的更新方式,主要差異在於您不必計算其偏移對應。

OutputTransformation 是單一抽象方法介面。如要實作自訂 OutputTransformation,您必須覆寫 transformOutput 方法:

class CustomOutputTransformation : OutputTransformation {
    override fun TextFieldBuffer.transformOutput() {
    }
}

如要設定電話號碼格式,請在 OutputTransformation 的索引 0 處加上左括號、在索引 4 處加上右括號,並在索引 8 處加上破折號:

class PhoneNumberOutputTransformation : OutputTransformation {
    override fun TextFieldBuffer.transformOutput() {
        if (length > 0) insert(0, "(")
        if (length > 4) insert(4, ")")
        if (length > 8) insert(8, "-")
    }
}

接著,將 OutputTransformation 新增至 TextField:

TextField(
    state = rememberTextFieldState(),
    outputTransformation = PhoneNumberOutputTransformation()
)

轉換如何搭配運作

下圖顯示從文字輸入到轉換再到輸出的流程:

圖表:文字輸入內容經過轉換後,成為文字輸出內容。
圖 4. 圖表:文字輸入內容經過轉換後,變成文字輸出內容。
  1. 從輸入來源接收輸入內容。
  2. 輸入內容會經過 InputTransformation 篩選,並儲存在 TextFieldState 中。
  3. 輸入內容會透過 OutputTransformation 進行格式設定。
  4. 輸入內容會顯示在 TextField 中。

設定鍵盤選項

TextField 可讓您設定鍵盤設定選項 (例如鍵盤配置);或是啟用自動更正功能 (如果鍵盤有支援)。如果螢幕鍵盤不符合此處提供的選項,則可能無法保證某些選項可以使用。以下是支援的鍵盤選項清單:

  • capitalization
  • autoCorrect
  • keyboardType
  • imeAction

KeyboardOptions 類別現在包含新的布林值參數 showKeyboardOnFocus,專門用於與 TextFieldState 整合的 TextField 元件。當 TextField 透過使用者直接互動以外的方式 (例如透過程式輔助) 取得焦點時,這個選項會控管螢幕鍵盤的行為。

如果將 KeyboardOptions.showKeyboardOnFocus 設為 true,當 TextField 間接取得焦點時,螢幕鍵盤不會自動顯示。在這種情況下,使用者必須明確輕觸 TextField 本身,才能顯示鍵盤。

定義鍵盤互動邏輯

Android 螢幕鍵盤上的動作按鈕可讓使用者在應用程式中進行互動式回應。如要進一步瞭解如何設定動作按鈕,請參閱「設定鍵盤選項」一節。

螢幕鍵盤動作按鈕 (勾號圖示) 圈上紅圈。
圖 5. 螢幕鍵盤動作按鈕。

如要定義使用者輕觸這個動作按鈕時會發生的情況,請使用 onKeyboardAction 參數。這項參數接受名為 KeyboardActionHandler 的選用功能介面。KeyboardActionHandler 介面包含單一方法 onKeyboardAction(performDefaultAction: () -> Unit)。只要為這個 onKeyboardAction 方法提供實作,即可導入自訂邏輯,在使用者按下鍵盤的動作按鈕時執行。

有幾種標準鍵盤動作類型內建預設行為。舉例來說,如果選取 ImeAction.Next 或 ImeAction.Previous 做為動作類型,系統預設會分別將焦點移至後續或前一個輸入欄位。同樣地,設為 ImeAction.Done 的動作按鈕通常會關閉螢幕鍵盤。這些預設功能會自動執行,您不需要提供 KeyboardActionHandler。

除了這些預設動作外,您也可以實作自訂行為。提供 KeyboardActionHandler 時,其 onKeyboardAction 方法會接收 performDefaultAction 函式。您可以在自訂邏輯中的任何時間點呼叫這個 performDefaultAction() 函式,同時觸發與目前 IME 動作相關聯的標準預設行為。

TextField(
    state = textFieldViewModel.usernameState,
    keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
    onKeyboardAction = { performDefaultAction ->
        textFieldViewModel.validateUsername()
        performDefaultAction()
    }
)

這個程式碼片段說明註冊畫面上的常見用途,其中包含使用者名稱欄位。這個欄位的鍵盤動作按鈕已選取 ImeAction.Next。這樣一來,使用者就能快速順暢地前往下一個密碼欄位。

除了這項標準導覽外,還必須在使用者輸入密碼時,啟動使用者名稱的背景驗證程序。為確保 ImeAction.Next 固有的預設焦點切換行為與這項自訂驗證邏輯一併保留,系統會叫用 performDefaultAction() 函式。呼叫 performDefaultAction() 會情境式觸發基礎焦點管理系統,將焦點移至下一個適當的 UI 元素,保留預期的導覽流程。

建立安全的密碼欄位

SecureTextField 是以狀態文字欄位為基礎建構的可組合函式,用於編寫密碼欄位。建議使用 SecureTextField 建立密碼文字欄位,因為該欄位預設會隱藏輸入的字元,並停用剪下和複製動作。

SecureTextField 具有 textObfuscationMode,可控制使用者如何查看字元輸入。textObfuscationMode 有下列選項:

  • Hidden:隱藏所有輸入內容。電腦平台的預設行為。

    安全的文字欄位,輸入的字元會隱藏。

  • Visible:顯示所有輸入內容。

    輸入的字元會顯示在安全文字欄位中。

  • RevealLastTyped:隱藏最後一個字元以外的所有輸入內容。行動裝置上的預設行為。

    安全的文字欄位,只會短暫顯示最後輸入的字元。

其他資源