原文地址: Jetpack Compose學習(3)——圖示(Icon) 按鈕(Button) 輸入框(TextField) 的使用 | Stars-One的雜貨小窩
本篇分別對常用的元件:圖示(Icon) 按鈕(Button) 輸入框(TextField)的使用方法及各引數使用進行講解,參考了不少文章,且費了不少時間去時間去一一實踐,希望對各位帶來些幫助
本系列以往文章請檢視此分類連結jetpackcompose學習
圖示Icon使用
Icon接收三種引數,如下圖
//第一種就不多說,就是一個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)
}
效果如下圖所示
PS:具體的圖示名稱寫的時候會有程式碼提示
不過預設常用的就那40幾個,其他的圖示就沒有包含在內,當然,如果你想用的話,也有方法實現,需要匯入material-icons-extended
依賴即可
dependencies {
...
implementation "androidx.compose.material:material-icons-extended:$compose_version"
}
但是全套圖示會導致打包後的apk檔案過大,所以官方推薦使用匯入圖示檔案的方法,詳情可參考官方文件
按鈕 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 = "測試")
}
上面的程式碼實現的效果就是有個圖示在左側
引數講解
我們先看下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 = "禁用狀態的陰影按鈕")
}
PS:使用的時候,發現導包會失敗,給了些奇怪的東西...建議複製下ButtonDefaults.elevation()
,再輸入引數
2.shape 形狀
Android官方給我們提供了以下四種形狀,我從程式碼提示裡只看到有這四種
RoundedCornerShape
圓角形狀CutCornerShape
切角形狀AbsoluteRoundedCornerShape
絕對圓角形狀AbsoluteCutCornerShape
絕對切角形狀
這裡從字面翻譯知道其的意思,但是具體圓角形狀和絕對圓角形狀有什麼區別,實際測試也有,但是沒法看出來有什麼區別,官方的文件也是解釋的有點模糊
後來者如果知道,可以在評論區回覆下,感謝~
上面四種類的接收引數其實是一樣的,這裡就截個圖給大家看看
我們常用就是使用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 = "按鈕")
}
我們可以實現如下圖的效果
程式碼如下:
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 = "邊框按鈕")
}
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("你好")
}
}
}
效果如下(要按住才會變化):
補充:
構造一個可觀察的狀態物件可以使用下面的三種方法,唯一有所區別的是,返回值不一樣
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)
}
其實這裡裡面也可以傳多個元件,但是效果可能會變得怪怪的,所以我們就是按照規範來使用吧
TextButton
這個其實是扁平按鈕,之前有個FlatButton
,然後改名成這個了,用法和Button一樣,就是樣式有所調整
TextButton(onClick = { /*TODO*/ }) {
Icon(Icons.Default.Search,contentDescription = null)
Text(text = "測試")
}
OutlinedButton
這個的話,看效果覺得應該是帶有邊框的按鈕,我們也可以根據實際需求改造
OutlinedButton(onClick = { /*TODO*/ }) {
Text(text = "測試")
}
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("使用者名稱")})
效果如下圖所示
2.leadingIcon
輸入框左邊顯示內容,leadingIcon
接收來自一個元件的lambda
表示式,可以是圖示、文字或者其他元件
TextField(
value = text,
onValueChange = {
text = it
},
leadingIcon = {
Icon(Icons.Filled.Search, null)
},
)
3.trailingIcon
輸入框右邊的內容,和上面的leadingIcon一樣的使用,這裡不再贅述
PS:可以在右邊放個x的圖示,點選刪除輸入全部文字功能哦
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
)
)
效果:
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-阿里巴巴向量圖示庫上找,效果如下:
補充(自定義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
)
效果如下:
可以看見,每輸入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-2406
// 14796112406
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)
}
}
效果如下所示:
參考
- TextField - Jetpack Compose
- Jetpack Compose - Button_樂翁龍-CSDN部落格
- Compose Button - 簡書
- Jetpack Compose Button,IconButton等各種Button的講解 - 掘金
- API文件——PasswordVisualTransformation
- 狀態和Jetpack Compose