Я разрабатываю приложение для Android с помощью JetPack Compose. < /p>
Я хочу реализовать текстовое поле. Это текстовое поле должно отображать небольшой смайлик или изображение вместо текста при введении конкретного токена. < /P>
Вы можете найти такой случай в приложении Slack. Slack App поддерживает пользовательские смайлики в дополнение к обычным смайкам.
val inlineContentMap = mapOf(
"ab_12" to emojiImage("https://c.tenor.com/Rd6ULrCRvlQAAAAd/tenor.gif")
)
var inputText by remember { mutableStateOf("") }
BasicTextField(
value = inputText,
onValueChange = {
inputText = it
},
modifier = Modifier
.fillMaxWidth()
.border(1.dp, Color.LightGray, RoundedCornerShape(4.dp))
.padding(8.dp),
textStyle = TextStyle(fontSize = 16.sp).copy(color = Color.Transparent),
visualTransformation = EmojiVisualTransformation,
decorationBox = { innerTextField ->
Box(
modifier = Modifier,
contentAlignment = Alignment.TopStart
) {
val annotatedString = replaceTokens(inputText)
Text(
text = annotatedString,
style = TextStyle(fontSize = 16.sp),
inlineContent = inlineContentMap
)
innerTextField()
}
}
)
< /code>
А вот и другие функции: < /p>
@Composable
private fun emojiImage(imgUrl: String) =
InlineTextContent(Placeholder(16.sp, 16.sp, PlaceholderVerticalAlign.TextCenter)) {
val context = LocalContext.current
val imageLoader = ImageLoader.Builder(context)
.components {
add(ImageDecoderDecoder.Factory())
}
.build()
Image(
painter = rememberAsyncImagePainter(
model = ImageRequest.Builder(context)
.data(data = imgUrl)
.build(),
imageLoader = imageLoader
),
contentDescription = null,
modifier = Modifier.size(16.dp),
)
}
private fun replaceTokens(input: String): AnnotatedString {
val annotatedString = buildAnnotatedString {
val regex = Regex("\\{:(\\w+_\\d+):\\}")
var currentIndex = 0
regex.findAll(input).forEach { matchResult ->
val token = matchResult.groupValues[1]
val tokenIndex = matchResult.range.first
val tokenLength = matchResult.value.length
append(input.substring(currentIndex, tokenIndex))
appendInlineContent(id = token)
currentIndex = tokenIndex + tokenLength
}
append(input.substring(currentIndex, input.length))
}
return annotatedString
}
< /code>
Вот визуальная трансформация: < /p>
object EmojiVisualTransformation: VisualTransformation {
override fun filter(text: AnnotatedString): TransformedText {
val originText = text.text
val annotatedString = replaceTokens(originText)
return TransformedText(annotatedString, EmojiOffsetMapping(originText, annotatedString.text))
}
}
data class EmojiOffsetMapping(
private val origin: String,
private val transformed: String,
): OffsetMapping {
private val tokenPositionRanges = mutableListOf()
init {
val regex = Regex("\\{:(\\w+_\\d+):\\}")
regex.findAll(origin).forEach { matchResult ->
val tokenPosition = matchResult.range
tokenPositionRanges.add(tokenPosition)
}
}
private fun getTransformedOffset(offset: Int): Int {
if (offset == 0) {
return 0
}
var newOffset = offset
tokenPositionRanges.forEachIndexed { index, tokenRange ->
if (tokenRange.first + 1
Это делает поведение курсора очень неустойчивым. При использовании InlineTextContent и Visualtransformation больше подход. < /p>
Как я могу эффективно реализовать это текстовое поле? [Редактировать 1] < /h3>
Еще один вопрос: < /p>
в приложении Slack, размещая курсор непосредственно после смайлика и нажатия Ключ Backspace удаляет смайлики. быть обнаженным. < /p>
Например, вместо того, чтобы удалить весь ток Последний кудрявый скоба '}' удаляется, оставляя "{: ab_12:" expened. < /p>
Как мы должны обрабатывать удаление всего токена при нажатии клавиши Backspace? < /p>
[edit 2] < /h3>
, чтобы разрешить правильно удалить эмодзи, когда пользователь помещает Курсор непосредственно после него и нажимает клавишу Backspace, необходимо более сложное управление курсором. < /p>
Я не уверен, что это даже возможно. Как правило, в BasicTextfield, когда нажата клавиша BackSpace, текст уже удален, что затрудняет определение того, какой символ был удален. Представление одного пустого символа (Unicode, например, U+0020 ), а не назначение строки, содержащей токен, значению BasicTextField может быть лучшим способом обработки списка Ключ. < /p>
Я также попытался передать TextFieldValue вместо строки значения BasicTextField, но это также не смог достичь цели. < /p>
Хотя я удалось заменить токен изображением в аннотированном строительстве при создании TextFieldValue в OnValueChange, когда строка заменит пустой символ Unicode, снова вызывается в OnValueChange, информация о позиции Аннотированный в AnnotatedString сброшен. Как они могли это реализовать?
Подробнее здесь: [url]https://stackoverflow.com/questions/78145214/android-compose-textfield-with-image-using-inlinetextcontent[/url]
Я разрабатываю приложение для Android с помощью JetPack Compose. < /p> Я хочу реализовать текстовое поле. Это текстовое поле должно отображать небольшой смайлик или изображение вместо текста при введении конкретного токена. < /P> Вы можете найти такой случай в приложении Slack. Slack App поддерживает пользовательские смайлики в дополнение к обычным смайкам.[code]val inlineContentMap = mapOf( "ab_12" to emojiImage("https://c.tenor.com/Rd6ULrCRvlQAAAAd/tenor.gif") )
var inputText by remember { mutableStateOf("") } BasicTextField( value = inputText, onValueChange = { inputText = it }, modifier = Modifier .fillMaxWidth() .border(1.dp, Color.LightGray, RoundedCornerShape(4.dp)) .padding(8.dp), textStyle = TextStyle(fontSize = 16.sp).copy(color = Color.Transparent), visualTransformation = EmojiVisualTransformation, decorationBox = { innerTextField -> Box( modifier = Modifier, contentAlignment = Alignment.TopStart ) { val annotatedString = replaceTokens(inputText) Text( text = annotatedString, style = TextStyle(fontSize = 16.sp), inlineContent = inlineContentMap ) innerTextField() } } ) < /code> А вот и другие функции: < /p> @Composable private fun emojiImage(imgUrl: String) = InlineTextContent(Placeholder(16.sp, 16.sp, PlaceholderVerticalAlign.TextCenter)) { val context = LocalContext.current val imageLoader = ImageLoader.Builder(context) .components { add(ImageDecoderDecoder.Factory()) } .build() Image( painter = rememberAsyncImagePainter( model = ImageRequest.Builder(context) .data(data = imgUrl) .build(), imageLoader = imageLoader ), contentDescription = null, modifier = Modifier.size(16.dp), ) }
private fun replaceTokens(input: String): AnnotatedString { val annotatedString = buildAnnotatedString { val regex = Regex("\\{:(\\w+_\\d+):\\}") var currentIndex = 0 regex.findAll(input).forEach { matchResult -> val token = matchResult.groupValues[1] val tokenIndex = matchResult.range.first val tokenLength = matchResult.value.length append(input.substring(currentIndex, tokenIndex)) appendInlineContent(id = token) currentIndex = tokenIndex + tokenLength } append(input.substring(currentIndex, input.length)) } return annotatedString } < /code> Вот визуальная трансформация: < /p> object EmojiVisualTransformation: VisualTransformation {
override fun filter(text: AnnotatedString): TransformedText { val originText = text.text
data class EmojiOffsetMapping( private val origin: String, private val transformed: String, ): OffsetMapping {
private val tokenPositionRanges = mutableListOf()
init { val regex = Regex("\\{:(\\w+_\\d+):\\}") regex.findAll(origin).forEach { matchResult -> val tokenPosition = matchResult.range tokenPositionRanges.add(tokenPosition) } }
private fun getTransformedOffset(offset: Int): Int { if (offset == 0) { return 0 }
var newOffset = offset tokenPositionRanges.forEachIndexed { index, tokenRange -> if (tokenRange.first + 1 Это делает поведение курсора очень неустойчивым. При использовании InlineTextContent и Visualtransformation больше подход. < /p> Как я могу эффективно реализовать это текстовое поле? [Редактировать 1] < /h3> Еще один вопрос: < /p> в приложении Slack, размещая курсор непосредственно после смайлика и нажатия Ключ Backspace удаляет смайлики. быть обнаженным. < /p> Например, вместо того, чтобы удалить весь ток Последний кудрявый скоба '}' удаляется, оставляя "{: ab_12:" expened. < /p> Как мы должны обрабатывать удаление всего токена при нажатии клавиши Backspace? < /p>
[edit 2] < /h3> , чтобы разрешить правильно удалить эмодзи, когда пользователь помещает Курсор непосредственно после него и нажимает клавишу Backspace, необходимо более сложное управление курсором. < /p> Я не уверен, что это даже возможно. Как правило, в BasicTextfield, когда нажата клавиша BackSpace, текст уже удален, что затрудняет определение того, какой символ был удален. Представление одного пустого символа (Unicode, например, U+0020 ), а не назначение строки, содержащей токен, значению BasicTextField может быть лучшим способом обработки списка Ключ. < /p> Я также попытался передать TextFieldValue вместо строки значения BasicTextField, но это также не смог достичь цели. < /p> Хотя я удалось заменить токен изображением в аннотированном строительстве при создании TextFieldValue в OnValueChange, когда строка заменит пустой символ Unicode, снова вызывается в OnValueChange, информация о позиции Аннотированный в AnnotatedString сброшен. Как они могли это реализовать?