設定文字欄位

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

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

功能

以值為準的文字欄位

州別文字欄位

州別福利

狀態管理

使用 onValueChange 回呼更新文字欄位狀態。您有責任根據 onValueChange 回報的變更,更新您自己狀態中的 value

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

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

視覺化轉換

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

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

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

行數限制

接受 singleLine: Boolean, maxLines: IntminLines: Int,可控制行數。

使用 lineLimits: TextFieldLineLimits 設定文字欄位可佔用的最小和最大行數。

  • 提供 TextFieldLineLimits 類型的 lineLimits 參數,設定行數限制時可避免模稜兩可。

安全文字欄位

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

  • 可讓您在幕後進行安全性最佳化,並提供預先定義的 UI (含 textObfuscationMode)。

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

選擇 TextField 實作方式

TextField 實作分為兩個層級:

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

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

含有「Hello」字詞的可編輯文字欄位。

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

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

樣式 TextField

TextFieldBasicTextField 會共用許多常見的參數以進行自訂。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 TextFieldOutlinedTextField,建議使用 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 設定具有下列特性:

  • 接受兩個參數:minHeightInLinesmaxHeightInLines
  • 文字欄位至少要有 minHeightInLines 高。
  • 如果文字溢位,系統會自動換行。
  • 如果文字需要更多行,欄位會擴大,直到達到 maxHeightInLines 高度,並垂直捲動。

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

您可以在 TextField 中使用 Brush API,進行更進階的樣式設定。下一節說明如何使用 Brush 在 TextField 輸入內容中新增彩色漸層。

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

使用 TextStyle 實作彩色漸層

如要在 TextField 中輸入文字時實作彩色漸層,請將所選筆刷設為 TextFieldTextStyle。在本範例中,我們使用內建筆刷和 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)
)

使用 buildAnnotatedString 和 SpanStyle,以及 linearGradient,只自訂部分文字。
圖 1. TextField內容的彩虹漸層效果。

管理文字欄位狀態

TextField 會使用名為 TextFieldState 的專屬狀態容器類別,儲存內容和目前選取項目。TextFieldState 的設計宗旨是盡可能在架構中提升層級。TextFieldState 提供的主要屬性有 2 種:

  • initialTextTextField 的內容。
  • 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,文字欄位內顯示「使用者名稱」文字。
圖 2. TextField,並以「使用者名稱」做為初始文字。

使用 TextFieldBuffer 修改文字

TextFieldBuffer 可做為可編輯的文字容器,功能類似於 StringBuilder。其中包含文字內容和目前所選內容的相關資訊。

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

你可以使用 appendinsertreplacedelete 等標準編輯函式修改緩衝區內容。如要變更選取狀態,請直接設定 selection: TextRange 變數,或使用 placeCursorAtEndselectAll 等公用程式函式。選取範圍本身以 TextRange 表示,其中包含開始索引,但不包含結束索引。如果 TextRange 的開始和結束值相同 (例如 (3, 3)),表示游標位置目前未選取任何字元。

val phoneNumberState = rememberTextFieldState()

LaunchedEffect(phoneNumberState) {
    phoneNumberState.edit { // TextFieldBuffer scope
        append("123456789")
    }
}

TextField(
    state = phoneNumberState,
    inputTransformation = InputTransformation { // TextFieldBuffer scope
        if (asCharSequence().isDigitsOnly()) {
            revertAllChanges()
        }
    },
    outputTransformation = OutputTransformation {
        if (length > 0) insert(0, "(")
        if (length > 4) insert(4, ")")
        if (length > 8) insert(8, "-")
    }
)

編輯「TextFieldState」中的文字

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

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

    val usernameState = rememberTextFieldState("I love Android")
    // textFieldState.text : I love Android
    // textFieldState.selection: TextRange(14, 14)
    usernameState.edit { insert(14, "!") }
    // textFieldState.text : I love Android!
    // textFieldState.selection: TextRange(15, 15)
    usernameState.edit { replace(7, 14, "Compose") }
    // textFieldState.text : I love Compose!
    // textFieldState.selection: TextRange(15, 15)
    usernameState.edit { append("!!!") }
    // textFieldState.text : I love Compose!!!!
    // textFieldState.selection: TextRange(18, 18)
    usernameState.edit { selectAll() }
    // textFieldState.text : I love Compose!!!!
    // textFieldState.selection: TextRange(0, 18)

  • setTextAndPlaceCursorAtEnd:清除目前的文字、以指定文字取代,並將游標設在結尾。

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

  • clearText:清除所有文字。

    usernameState.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 (!TextUtils.isDigitsOnly(asCharSequence())) {
            revertAllChanges()
        }
    }
}

鏈結輸入轉換

如要在文字輸入內容中新增多個篩選器,請使用 then 擴充功能函式,將 InputTransformation 串連在一起。系統會依序執行篩選器。按照最佳做法,請先套用最嚴格的篩選器,避免對最終會篩除的資料進行不必要的轉換。

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

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

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

OutputTransformation 可讓您先格式化使用者輸入內容,再顯示在畫面上。與 InputTransformation 不同,透過 OutputTransformation 完成的格式設定不會儲存在 TextFieldState 中。以上一個電話號碼範例為基礎,您需要在適當位置新增半形括號和破折號:

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

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

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

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

如要設定電話號碼格式,請在索引 0 處新增左括號、在索引 4 處新增右括號,並在索引 8 處新增破折號至 OutputTransformation

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.NextImeAction.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:隱藏所有輸入內容,但最後一個字元除外。行動裝置上的預設行為。

其他資源