Skip to content

Instantly share code, notes, and snippets.

@syarihu
Created December 25, 2023 05:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save syarihu/d0ae4d103b7c492b2e1bb5949f9ba779 to your computer and use it in GitHub Desktop.
Save syarihu/d0ae4d103b7c492b2e1bb5949f9ba779 to your computer and use it in GitHub Desktop.
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.KeyboardArrowDown
import androidx.compose.material.icons.outlined.KeyboardArrowUp
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
private const val MAX_LINES = 2
private val textStyle = TextStyle(
fontSize = 12.sp,
lineHeight = 16.sp,
fontWeight = FontWeight.W600,
)
@Preview
@Composable
private fun ExpandableTextPreview() {
ExpandableText(text = "これはとても長いテキストです。これはとても長いテキストです。これはとても長いテキストです。これはとても長いテキストです。これはとても長いテキストです。")
}
@Composable
fun ExpandableText(text: String) {
var expanded: Boolean by remember { mutableStateOf(false) }
BoxWithConstraints(
modifier = Modifier
.fillMaxWidth()
.background(Color.White)
.padding(top = 8.dp, start = 8.dp, end = 8.dp, bottom = 4.dp)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(),
onClick = {
expanded = expanded.not()
},
),
) {
val maxWidth = with(LocalDensity.current) {
maxWidth.toPx().toInt()
}
val maxWidthWithIcon = with(LocalDensity.current) {
maxWidth - 24.dp.toPx().toInt()
}
val textMeasurer = rememberTextMeasurer()
val textLayoutResult by remember(text) {
textMeasurer.measure(
text = text,
style = textStyle,
constraints = Constraints(maxWidth = maxWidth),
).let {
mutableStateOf(it)
}
}
val textWithEllipsis by remember(text) {
val lastIndex = (textLayoutResult.lineCount - 1).coerceIn(0, MAX_LINES - 1)
(0..lastIndex).map { lineIndex ->
if (lineIndex < lastIndex) {
textLayoutResult.getLineText(lineIndex)
} else {
textLayoutResult.getLineText(lineIndex).let { lineText ->
textMeasurer.measure(
text = lineText,
style = textStyle,
constraints = Constraints(maxWidth = maxWidthWithIcon),
).getLineText(0)
}
}
}.let {
mutableStateOf(it.joinToString("").plus("…"))
}
}
Text(
text = if (expanded.not() && textLayoutResult.lineCount > MAX_LINES) {
textWithEllipsis
} else {
text
},
modifier = Modifier.padding(bottom = 4.dp),
style = textStyle,
)
Icon(
imageVector = if (expanded) Icons.Outlined.KeyboardArrowUp else Icons.Outlined.KeyboardArrowDown,
contentDescription = null,
tint = Color.Cyan,
modifier = Modifier
.size(20.dp)
.align(Alignment.BottomEnd),
)
}
}
private fun TextLayoutResult.getLineText(lineIndex: Int): String {
if (lineIndex < 0 || lineCount <= lineIndex) {
return ""
}
val lineTextStartIndex = if (lineIndex == 0) {
0
} else {
getLineStart(lineIndex)
}
val lineTextEndIndex = getLineEnd(lineIndex)
val originalText = layoutInput.text.toString()
return originalText.substring(
lineTextStartIndex,
lineTextEndIndex,
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment