天天看點

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

原文位址: Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用 | Stars-One的雜貨小窩

本篇分别對常用的元件:圖示(Icon) 按鈕(Button) 輸入框(TextField)的使用方法及各參數使用進行講解,參考了不少文章,且費了不少時間去時間去一一實踐,希望對各位帶來些幫助 😊

本系列以往文章請檢視此分類連結jetpackcompose學習

圖示Icon使用

Icon接收三種參數,如下圖

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用
//第一種就不多說,就是一個drawble對象


//擷取圖檔資源,R.drawble.xx或R.mipmap.xx
Icon(painter = painterResource(id = R.drawable.head1_1024), null)

//自帶的圖示
Icon(Icons.Filled.Search, null)
           

Compose内置了幾十個常用的圖示,我們使用枚舉類型即可使用

Icons裡面定了5種類型

Outlined

Filled

Sharp

TwoTone

Rounded

,可以根據自己的需要選擇不同的類型,如填充型(Filled)或者是輪廓型(Outlined)

Icon的構造方法參數簡單說明下

contentDescription

是給無障礙人使用的文本描述,考慮到一些視覺障礙的人使用,是以有個這個屬性,會使用TTS語音播放将

contentDescription

屬性讀出來,告知使用者此按鈕的作用

tint

則是圖示顔色的設定

Row() {
    Icon(Icons.Outlined.Settings, contentDescription = null, tint = Color.Red)
    Icon(Icons.Filled.Settings, contentDescription = null, tint = Color.Blue)
    Icon(Icons.Sharp.Settings, contentDescription = null, tint = Color.Green)
    Icon(Icons.TwoTone.Settings, contentDescription = null, tint = Color.Red)
    Icon(Icons.Rounded.Settings, contentDescription = null, tint = Color.Black)
}
           

效果如下圖所示

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用
PS:具體的圖示名稱寫的時候會有代碼提示
Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

不過預設常用的就那40幾個,其他的圖示就沒有包含在内,當然,如果你想用的話,也有方法實作,需要導入

material-icons-extended

依賴即可

dependencies {
  ...
  implementation "androidx.compose.material:material-icons-extended:$compose_version"
}
           

但是全套圖示會導緻打包後的apk檔案過大,是以官方推薦使用導入圖示檔案的方法,詳情可參考官方文檔

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

按鈕 Button

Button這個元件,官方已經實作了Material Design的效果,一般來說我們直接使用這個即可

除此之外,官方也是給我們封裝了不同類型的Button,分别為

IconButton

TextButton

OutlinedButton

IconToggleButton

上面我們剛講了圖示,下面就先講些圖示按鈕IconButton的使用方式吧

基本使用

和以往我們使用的按鈕不一樣,這裡的按鈕可以看做是一個布局控件,我們需要設定文字也就是往裡面添加一個Text元件,這就是compose和傳統Android的xml的不同之處

由上面這點,是以我們在代碼層面就十分靈活,可以實作各種效果(如帶有圖示的按鈕),下面來個例子

Button(onClick = { println("點選了按鈕")}){
    Icon(Icons.Default.Search,contentDescription = null)
    Text(text = "測試")
}
           

上面的代碼實作的效果就是有個圖示在左側

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

參數講解

我們先看下Button的定義,其實封裝好的方法,代碼如下所示

fun Button(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    elevation: ButtonElevation? = ButtonDefaults.elevation(),
    shape: Shape = MaterialTheme.shapes.small,
    border: BorderStroke? = null,
    colors: ButtonColors = ButtonDefaults.buttonColors(),
    contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
    content: @Composable RowScope.() -> Unit
) 
           

Button

content

參數(也就是上面lambda),傳入多個元件,

Button

會将其按照水準方式排列(即

Button

可視為

Row

布局)

由于kotlin的文法特性,是以我們可以在後面以花括号寫個lambda函數

這裡先講下比較簡單的參數:

  • onClick

    是點選事件.也是接收一個函數
  • modifier

    是修飾符,本章先不使用,之後出個篇文章,專門講解下這個的用法
  • enabled

    按鈕是否可用(不可用預設是灰色,可用預設是藍色),當然這裡的預設的禁用和可用的顔色可可以調整,詳情請見下面的

    colors

    參數

接下來就是稍微有點複雜的參數說明了,因為用法與之前原生Button有所差別,這裡特别分成一小節講解,友善目錄查閱

1.elevation 陰影

Button的陰影參數是有有預設值的,我們也可以使用下面的方法進行數值的修改

ButtonDefaults.elevation(defaultElevation,pressedElevation,disabledElevation)
           
  • defaultElevation

    表示預設的陰影
  • pressedElevation

    表示按下時的陰影
  • disabledElevation

    表示未啟用時候的陰影
Button(
    enabled = true,
    onClick = { /*TODO*/ },
    elevation = ButtonDefaults.elevation(4.dp, 10.dp, 0.dp)
) {
    Text(text = "陰影按鈕")
}

Button(
    enabled = false,
    onClick = { /*TODO*/ },
    elevation = ButtonDefaults.elevation(4.dp, 10.dp, 0.dp)
) {
    Text(text = "禁用狀态的陰影按鈕")
}
           
Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

PS:使用的時候,發現導包會失敗,給了些奇怪的東西...建議複制下

ButtonDefaults.elevation()

,再輸入參數

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

2.shape 形狀

Android官方給我們提供了以下四種形狀,我從代碼提示裡隻看到有這四種147

  • RoundedCornerShape

    圓角形狀
  • CutCornerShape

    切角形狀
  • AbsoluteRoundedCornerShape

    絕對圓角形狀
  • AbsoluteCutCornerShape

    絕對切角形狀

這裡從字面翻譯知道其的意思,但是具體圓角形狀和絕對圓角形狀有什麼差別,實際測試也有,但是沒法看出來有什麼差別,官方的文檔也是解釋的有點模糊

後來者如果知道,可以在評論區回複下,感謝~

上面四種類的接收參數其實是一樣的,這裡就截個圖給大家看看

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

我們常用就是使用dp定位進行設定,如

RoundedCornerShape(10.dp) //設定10dp的圓角
RoundedCornerShape(topStart = 5.dp,topEnd = 6.dp,bottomEnd = 10.dp,bottomStart = 10.dp)
           
  • topStart

    左上角
  • topEnd

    右上角
  • bottomStart

    左下角
  • bottomEnd

    右下角
PS: 記住start是左,end是右,上面就比較好記了
Button(
    onClick = { /*TODO*/ },
    elevation = ButtonDefaults.elevation(4.dp, 10.dp, 0.dp),
    shape = RoundedCornerShape(topStart = 5.dp,topEnd = 6.dp,bottomEnd = 10.dp,bottomStart = 10.dp)

) {
    Text(text = "按鈕")
}
           

我們可以實作如下圖的效果

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用
Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

代碼如下:

Modifier.size(50.dp,50.dp)

是用來設定寬高的
Row() {
	//固定長寬一樣,圓角設定為50%即為圓形
    Button(
        modifier = Modifier.size(50.dp,50.dp),
        onClick = { /*TODO*/ },
        shape = RoundedCornerShape(50),
    ) {
        Text(text = "")
    }

	//固定長寬一樣,切角設定為50%即為菱形
    Button(
        modifier = Modifier.size(50.dp,50.dp),
        onClick = { /*TODO*/ },
        shape = CutCornerShape(50.dp),
    ) {
        Text(text = "")
    }

	//左上角設定圓角
    Button(
        onClick = { /*TODO*/ },
        shape = RoundedCornerShape(topStart = 20.dp),
    ) {
        Text(text = "按鈕")
    }

	//圓角設定為50%
    Button(
        onClick = { /*TODO*/ },
        shape = RoundedCornerShape(50),
        border = BorderStroke(1.dp, Color.Green),
        colors = ButtonDefaults.buttonColors(),
    ) {
        Text(text = "按鈕111")
    }

    Button(
        modifier = Modifier.size(50.dp,50.dp),
        onClick = { /*TODO*/ },
        shape = CutCornerShape(25),
        border = BorderStroke(1.dp, Color.Green),
        colors = ButtonDefaults.buttonColors(),
    ) {
        Text(text = "按鈕111")
    }

}
           

3.border 邊框

邊框就簡單了,使用

BorderStroke

,接收兩個參數,一個是邊框的寬度,另外一個則是邊框的顔色

BorderStroke(1.dp,color = Color.Black)
           
Button(
    onClick = { /*TODO*/ },
    elevation = ButtonDefaults.elevation(4.dp, 10.dp, 0.dp),
    shape = RoundedCornerShape(topStart = 5.dp,topEnd = 6.dp,bottomEnd = 10.dp,bottomStart = 10.dp),
    border = BorderStroke(1.dp, Color.Green)
) {
    Text(text = "邊框按鈕")
}
           
Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

4.colors 顔色

可以通過下面的方法進行顔色的參數的設定

ButtonDefaults.buttonColors(backgroundColor,contentColor,disabledBackgroundColor,disabledContentColor)
           
  • backgroundColor

    表示設定背景顔色
  • contentColor

    表示設定内容顔色這裡比如說是登入文本的顔色
  • disabledBackgroundColor

    表示

    enable

    等于

    false

    的時候的背景顔色
  • disabledContentColor

    enable

    false

    時候的内容的顔色
PS:這個和之前的一樣,直接導包會報錯,使用複制大法

ButtonDefaults.buttonColors()

解決

5.contentPadding 内容内邊距

contentPadding參數接收一個PaddingValues對象,這個對象的構造方法如下:

  • PaddingValues(all)

  • PaddingValues(horizontal: Dp, vertical: Dp)

  • PaddingValues(start: Dp = 0.dp,top: Dp = 0.dp,end: Dp = 0.dp,bottom: Dp = 0.dp)

PaddingValues(10.dp) //所有内邊距為10dp

PaddingValues(10.dp,20.dp) //左右内邊距ge10dp,上下内邊距各20dp

PaddingValues(10.dp,15.dp,20.dp,25.dp) //左内邊距10dp,上内邊距15dp,右内邊距20dp,下内邊距25dp
           

6.interactionSource 狀态變化

這個主要是用來按鈕的狀态說明,我們可以使用這個來達到動态切換按鈕樣式的效果(如按下按鈕的樣式效果,松開後按鈕的樣式),類似我們之前常用

selector

的xml檔案給按鈕設定樣式

可以處理狀态的,比如按下的時候什麼效果,正常時候什麼效果。類似之前再布局檔案裡寫Selector

interactionSource是一個接口,我們需要使用其的實作類

MutableInteractionSource

MutableInteractionSource中提供了三個屬性用來擷取狀态

  • collectIsPressedAsState

    按壓狀态
  • collectIsDraggedAsState

    拖動狀态
  • collectIsFocusedAsState

    焦點狀态

我們可以可以此狀态來動态更改按鈕的樣式,如下面的代碼

@Preview(showBackground = true)
@Composable
fun DefaultPreview2() {

    val myInteractionSource = remember {
        MutableInteractionSource()
    }

    val pressState = myInteractionSource.collectIsPressedAsState()
    //如果是按壓狀态則是切角形狀,否則則是圓角形狀
    val myShape = if(pressState.value) CutCornerShape(10.dp) else RoundedCornerShape(10.dp)

    Column(
        Modifier.padding(20.dp)
    ) {
        Button(
            onClick = { /*TODO*/ },
            //設定我們定義的shape
            shape = myShape,
            //設定建立的MutableInteractionSource對象
            interactionSource = myInteractionSource
        ) {
            Text("你好")
        }
    }
}
           

效果如下(要按住才會變化):

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

補充:

構造一個可觀察的狀态對象可以使用下面的三種方法,唯一有所差別的是,傳回值不一樣

val mutableState = remember { mutableStateOf(default) }
var value by remember { mutableStateOf(default) }
val (value, setValue) = remember { mutableStateOf(default) }
           

如下面有個例子:

val mutableState = remember { mutableStateOf("") } //mutableState是State<String>對象

var value by remember { mutableStateOf("") } //value是String對象
val (value, setValue) = remember { mutableStateOf("") } 
           

一般選用by關鍵字的那種,代碼就比較友善,如果是第一種的話,需要通過

mutableState.value

才能拿到其儲存的數值

這裡強烈建議看下官方的文檔狀态和Jetpack Compose

圖示按鈕IconButton

IconButton 可以幫助我們生成一個可點選的圖示按鈕,點選按鈕預設會有水波漣漪的點選效果

IconButton(onClick = { /*TODO*/ }) {
    Icon(Icons.Filled.Search, null)
}
           
Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

其實這裡裡面也可以傳多個元件,但是效果可能會變得怪怪的,是以我們就是按照規範來使用吧

TextButton

這個其實是扁平按鈕,之前有個

FlatButton

,然後改名成這個了,用法和Button一樣,就是樣式有所調整

TextButton(onClick = { /*TODO*/ }) {
    Icon(Icons.Default.Search,contentDescription = null)
    Text(text = "測試")
}
           

OutlinedButton

這個的話,看效果覺得應該是帶有邊框的按鈕,我們也可以根據實際需求改造

OutlinedButton(onClick = { /*TODO*/ }) {
    Text(text = "測試")
}
           
Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

TextField

TextField在第一篇登入頁面也是有提及到,這裡再深入了解下各個屬性

TextField 實作分為兩個級别:

1.TextField 是 Material Design 實作。我們建議您選擇此實作,因為它遵循的是 Material Design 指南:

  • 預設樣式為填充
  • OutlinedTextField

    是輪廓樣式版本

    2.BasicTextField 允許使用者通過硬體或軟體鍵盤編輯文字,但沒有提供提示或占位符等裝飾

TextField(value = "", onValueChange = {},label = {Text("使用者名")})

OutlinedTextField(value = "", onValueChange = {},label = {Text("使用者名")})

BasicTextField(value = "", onValueChange = {})
           

簡單來說,就是

BasicTextField

是超級原生的輸入框,其什麼樣式都沒有,可以讓我們進行高度的自定義,而

TextField

OutlinedTextField

則是Android官方給我們封裝好Material Design樣式的控件

1.label

獲得輸入焦點,頂頭的文字提示,接收一個元件的

lambda

表達式,一般傳

Text

,示例代碼如下

TextField(value = "", onValueChange = {},label = {Text("使用者名")})
           
Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

2.leadingIcon

輸入框左邊顯示内容,

leadingIcon

接收來自一個元件的

lambda

表達式,可以是圖示、文本或者其他元件

TextField(
    value = text,
    onValueChange = {
        text = it
    },
    leadingIcon = {
        Icon(Icons.Filled.Search, null)
    },
)
           
Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

3.trailingIcon

輸入框右邊的内容,和上面的leadingIcon一樣的使用,這裡不再贅述

PS:可以在右邊放個x的圖示,點選删除輸入全部文本功能哦 😉
Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

4.singleLine

設定是否單行,接收一個boolean值

注: 此參數不能和

maxLines

參數聯用
TextField(
    value = text,
    onValueChange = {
        text = it
    },
    singleLine =true,
)
           

5.color

設定各種顔色,參數如下(參數真的多,應該夠靈活了吧🤣)

@Composable
fun textFieldColors(
    // 輸入的文字顔色
    textColor: Color = LocalContentColor.current.copy(LocalContentAlpha.current),

    // 禁用 TextField 時,已有的文字顔色
    disabledTextColor: Color = textColor.copy(ContentAlpha.disabled),

    // 輸入框的背景顔色,當設定為 Color.Transparent 時,将透明
    backgroundColor: Color = MaterialTheme.colors.onSurface.copy(alpha = BackgroundOpacity),

    // 輸入框的光标顔色
    cursorColor: Color = MaterialTheme.colors.primary,

    // 當 TextField 的 isError 參數為 true 時,光标的顔色
    errorCursorColor: Color = MaterialTheme.colors.error,

    // 當輸入框處于焦點時,底部訓示器的顔色
    focusedIndicatorColor: Color = MaterialTheme.colors.primary.copy(alpha = ContentAlpha.high),

    // 當輸入框不處于焦點時,底部訓示器的顔色
    unfocusedIndicatorColor: Color = MaterialTheme.colors.onSurface.copy(alpha = UnfocusedIndicatorLineOpacity),

    // 禁用 TextField 時,底部訓示器的顔色
    disabledIndicatorColor: Color = unfocusedIndicatorColor.copy(alpha = ContentAlpha.disabled),

    // 當 TextField 的 isError 參數為 true 時,底部訓示器的顔色
    errorIndicatorColor: Color = MaterialTheme.colors.error,

    // TextField 輸入框前頭的顔色
    leadingIconColor: Color = MaterialTheme.colors.onSurface.copy(alpha = IconOpacity),

    // 禁用 TextField 時 TextField 輸入框前頭的顔色
    disabledLeadingIconColor: Color = leadingIconColor.copy(alpha = ContentAlpha.disabled),

    // 當 TextField 的 isError 參數為 true 時 TextField 輸入框前頭的顔色
    errorLeadingIconColor: Color = leadingIconColor,

    // TextField 輸入框尾部的顔色
    trailingIconColor: Color = MaterialTheme.colors.onSurface.copy(alpha = IconOpacity),

    // 禁用 TextField 時 TextField 輸入框尾部的顔色
    disabledTrailingIconColor: Color = trailingIconColor.copy(alpha = ContentAlpha.disabled),

    // 當 TextField 的 isError 參數為 true 時 TextField 輸入框尾部的顔色
    errorTrailingIconColor: Color = MaterialTheme.colors.error,

    // 當輸入框處于焦點時,Label 的顔色
    focusedLabelColor: Color = MaterialTheme.colors.primary.copy(alpha = ContentAlpha.high),

    // 當輸入框不處于焦點時,Label 的顔色
    unfocusedLabelColor: Color = MaterialTheme.colors.onSurface.copy(ContentAlpha.medium),

    // 禁用 TextField 時,Label 的顔色
    disabledLabelColor: Color = unfocusedLabelColor.copy(ContentAlpha.disabled),

    // 當 TextField 的 isError 參數為 true 時,Label 的顔色
    errorLabelColor: Color = MaterialTheme.colors.error,

    // Placeholder 的顔色
    placeholderColor: Color = MaterialTheme.colors.onSurface.copy(ContentAlpha.medium),

    // 禁用 TextField 時,placeholder 的顔色
    disabledPlaceholderColor: Color = placeholderColor.copy(ContentAlpha.disabled)
)
           

代碼使用:

TextField(
    value = text,
    onValueChange = {
        text = it
    },
    leadingIcon = {
        Icon(Icons.Filled.Search, null)
    },
    colors = TextFieldDefaults.textFieldColors(
        textColor = Color(0xFF0079D3),
        backgroundColor = Color.Transparent
    )
)
           

效果:

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

6.visualTransformation 視圖變化

視圖變化是我自己翻譯出來的,也不知道準不準确,個人更傾向于了解成輸入類型(inputType) 😃

這個有點類似之前原生的inputType,可以改變輸入的字元串(如密碼或者是輸入手機号時候多個

-

),不過官方目前隻實作了

PasswordVisualTransformation

,其他的需要我們自定義

使用的話也很簡單

var inputText by remember { mutableStateOf("") }
	
TextField(value = inputText, onValueChange = {value-> inputText= value},visualTransformation = PasswordVisualTransformation())
           

我們如果想實作Android那種帶有個圖示,點選可以顯示密碼的輸入框,該怎麼實作呢?

其實也很簡單,設定個可觀察的boolean值,點選圖示改變數值即可,具體可參考下面代碼

//密碼内容
var inputText by remember { mutableStateOf("") }
//是否展示密碼(預設是false)
var isShowPwd by remember { mutableStateOf(false) }

//顯示效果(true:顯示内偶然你 false:顯示密碼的"*"好
val myVisualTransformation =
    if (isShowPwd) VisualTransformation.None else PasswordVisualTransformation()

TextField(

    value = inputText,
    colors=TextFieldDefaults.textFieldColors(backgroundColor = Color.Transparent),
    onValueChange = { value -> inputText = value },
    visualTransformation = myVisualTransformation,
    trailingIcon = {
        //根據表示不同,顯示不同的圖示
        if (isShowPwd) {
            //目前是顯示密碼,則圖示為眼睛
            IconButton(onClick = {
                //更改标志
                isShowPwd = !isShowPwd
            }) {
                Icon(painter = painterResource(id = R.drawable.eye_show), null)
            }
        } else {
            //目前是隐藏密碼,則圖示為眼睛禁止
            IconButton(onClick = {
                //更改标志
                isShowPwd = !isShowPwd
            }) {
                Icon(painter = painterResource(id = R.drawable.eye_hide), null)
            }
        }
    })
           

上面的兩個圖示是我自己去iconfont-阿裡巴巴矢量圖示庫上找,效果如下:

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

補充(自定義VisualTransformation)

注意: 經過實踐發現,這個隻是改變了顯示的數值而已😅,實際上你輸入什麼,儲存的數值還是那個,單純隻是TextField沒顯示而已,不是很清楚這個操作,那這樣是不能實作限制長度的功能,密碼顯示星号這種效果應該沒啥問題

此功能有待讨論,或者是可能官方後面會更新長度限制等功能?

上面說到官方隻實作了一個簡單的密碼輸入類型,那如果我們想自定義該如何實作呢?

好在官方也是在API文檔中給了個例子,可以實作輸入信用卡号,以

-

隔開的效果

我們先看下官方的代碼及效果(有點坑,官方隻給出了一部分代碼,稍微琢磨了一番才知道它是實作了

VisualTransformation

接口,并重寫了

filter()

方法)

class CardVisualTransformation : VisualTransformation{
    override fun filter(text: AnnotatedString): TransformedText {
        // Making XXXX-XXXX-XXXX-XXXX string.
        val trimmed = if (text.text.length >= 16) text.text.substring(0..15) else text.text
        var out = ""
        for (i in trimmed.indices) {
            out += trimmed[i]
            if (i % 4 == 3 && i != 15) out += "-"
        }

        /**
         * The offset translator should ignore the hyphen characters, so conversion from
         *  original offset to transformed text works like
         *  - The 4th char of the original text is 5th char in the transformed text.
         *  - The 13th char of the original text is 15th char in the transformed text.
         *  Similarly, the reverse conversion works like
         *  - The 5th char of the transformed text is 4th char in the original text.
         *  - The 12th char of the transformed text is 10th char in the original text.
         */
        val creditCardOffsetTranslator = object : OffsetMapping {
            override fun originalToTransformed(offset: Int): Int {
                if (offset <= 3) return offset
                if (offset <= 7) return offset + 1
                if (offset <= 11) return offset + 2
                if (offset <= 16) return offset + 3
                return 19
            }

            override fun transformedToOriginal(offset: Int): Int {
                if (offset <= 4) return offset
                if (offset <= 9) return offset - 1
                if (offset <= 14) return offset - 2
                if (offset <= 19) return offset - 3
                return 16
            }
        }

        return TransformedText(AnnotatedString(out), creditCardOffsetTranslator)
    }
}
           

之後我們将TextField設定為上面的對象,代碼如下

//密碼内容
var inputText by remember { mutableStateOf("") }

//我們定義的卡号VisualTransformation
val myVisualTransformation = CardVisualTransformation()

TextField(
    value = inputText,
    label={
          Text(text = "卡号")
    },
    colors=TextFieldDefaults.textFieldColors(backgroundColor = Color.Transparent),
    onValueChange = { value -> inputText = value },
    visualTransformation = myVisualTransformation
)
           

效果如下:

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

可以看見,每輸入4個字元,後面會自動加上

-

,且輸入了16個字元後,就無法繼續輸入了,删除的時候,也會自動将

-

删除,我們分析下代碼

//這裡的text是之前提到的AnnotatedString類型
//最大程度過濾,隻要16個字元,大于16個字元,後面的字元就忽略掉
val trimmed = if (text.text.length >= 16) text.text.substring(0..15) else text.text
//TextFiel顯示的資料,滿足條件即追加"-"
var out = ""
for (i in trimmed.indices) {
    out += trimmed[i]
    //最後個字元不需要加"-"(即中間每隔四個字元追加"-")
    if (i % 4 == 3 && i != 15) out += "-"
}
           

接下來是實作了一個接口

OffsetMapping

官方文檔關于此類說明: 提供原始文本和轉換文本(transformed text)之間的雙向偏移映射

看到這裡,相信各位對原理已經有了一定的了解,

VisualTransformation

這個類其實就是将原始文本轉為轉換文本,是以我們看到

filter(text: AnnotatedString)

最後是傳回的一個

TransformedText

對象

val creditCardOffsetTranslator = object : OffsetMapping {
    //原始文本對應的轉換文本的下标映射
    override fun originalToTransformed(offset: Int): Int {
        if (offset <= 3) return offset
        if (offset <= 7) return offset + 1
        if (offset <= 11) return offset + 2
        if (offset <= 16) return offset + 3
        //轉換文本最大長度為19
        return 19
    }
    
    //轉換文本對應的原始文本下标映射
    override fun transformedToOriginal(offset: Int): Int {
        //4 9 14都是"-"的下标位置
        if (offset <= 4) return offset
        if (offset <= 9) return offset - 1
        if (offset <= 14) return offset - 2
        if (offset <= 19) return offset - 3
        //原始文本的最大長度為16
        return 16
    }
}
           
映射這裡稍微想下就明白了,如有個

abcdefgh

,其對應的轉換文本就為

abcd-efgh

,其中,

a-d

是下标沒變,都對應得上,但從

e

開始,由于多了個

-

,是以原始文本中

e

的下标為

4

,而在轉換文本中,

e

的下标變為了

5

,後面的以此類推,反過來也是同理

我們根據官方的,改下手機号的,代碼如下

class PhoneVisualTransformation : VisualTransformation{
    override fun filter(text: AnnotatedString): TransformedText {

        val trimmed = if (text.text.length >= 11) text.text.substring(0,11) else text.text
        var out = ""
        for (i in trimmed.indices) {
            out += trimmed[i]
            if (i==2 || i==6 ) out += "-"
        }

        // 147-9611-3406
        // 14796113406
        val creditCardOffsetTranslator = object : OffsetMapping {
            override fun originalToTransformed(offset: Int): Int {
                if (offset <= 2) return offset
                if(offset<=6) return offset + 1
                if (offset <= 11) return offset + 2
                return 13
            }

            override fun transformedToOriginal(offset: Int): Int {
                if (offset <= 3) return offset
                if (offset <= 8) return offset - 1
                if (offset <= 14) return offset - 2
                return 11
            }
        }

        return TransformedText(AnnotatedString(out), creditCardOffsetTranslator)
    }
}
           

效果如下所示:

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

參考

  • TextField - Jetpack Compose
  • Jetpack Compose - Button_樂翁龍-CSDN部落格
  • Compose Button - 簡書
  • Jetpack Compose Button,IconButton等各種Button的講解 - 掘金
  • API文檔——PasswordVisualTransformation
  • 狀态和Jetpack Compose

提問之前,請先看提問須知

點選右側圖示發起提問

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

或者加入QQ群一起學習

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

TornadoFx學習交流群:1071184701

Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用

繼續閱讀