I was using Jetpack Compose version 1.0.0-alpha01 and LazyColumnFor to display some items in a list. After updating to 1.0.0-beta06 I noticed that LazyColumnFor had been deprecated in favor of LazyColumn. After migrating however, the items in the list draw on top of each other instead of being listed vertically with some spacing in between.
What's odd here is that the Preview renders the items as expected, while the app installed on the device(s) does not.
Here's the activity:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val textStyle = TextStyle(color = Color.White, fontSize = 16.sp)
val boldStyle = textStyle.merge(TextStyle(fontWeight = FontWeight.Bold))
ComposeListTestTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
LazyColumn(modifier = Modifier.fillMaxHeight()) {
items(listOf("Row1", "Row2")) { day ->
Text(
text = day,
style = boldStyle,
modifier = Modifier.padding(top = 8.dp),
color = Color.Green
)
}
}
}
}
}
}
}
And here's the preview:
#Preview(showBackground = true)
#Composable
fun DefaultPreview() {
val textStyle = TextStyle(color = Color.White, fontSize = 16.sp)
val boldStyle = textStyle.merge(TextStyle(fontWeight = FontWeight.Bold))
ComposeListTestTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
LazyColumn(modifier = Modifier.fillMaxHeight()) {
items(listOf("Row1", "Row2")) { day ->
Text(
text = day,
style = boldStyle,
modifier = Modifier.padding(top = 8.dp),
color = Color.Green
)
}
}
}
}
}
Unfortunately I haven't been able to get the new layout inspector to work on any of my devices, or I suspect I could've seen some clues as to why this happens.
ComposeListTestTheme:
#Composable
fun ComposeListTestTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: #Composable() () -> Unit
) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}
The issue happened because the app was not hardware accelerated. This was fixed in https://issuetracker.google.com/issues/187853238.
Related
I'm trying to have a white top app bar using Jetpack Compose but the color is grey instead of white.
Here is my code:
#Composable
fun AppTheme(content: #Composable () -> Unit) {
MaterialTheme(
colors = Colors(
primary = colorResource(R.color.colorPrimary),
primaryVariant = colorResource(R.color.colorPrimaryVariant),
secondary = colorResource(R.color.colorSecondary),
secondaryVariant = colorResource(R.color.colorSecondaryVariant),
background = colorResource(R.color.white),
surface = colorResource(R.color.white),
error = colorResource(R.color.failure),
onPrimary = colorResource(R.color.colorOnPrimary),
onSecondary = colorResource(R.color.colorOnSecondary),
onBackground = colorResource(R.color.colorOnBackground),
onSurface = colorResource(R.color.colorOnSurface),
onError = colorResource(R.color.white),
isLight = false
),
typography = Typography(),
shapes = Shapes(),
content = content
)
}
#Composable
#Preview
fun TestTopBar() {
AppTheme {
TopBar(title = R.string.common_country, actions = actions())
}
}
#Composable
fun actions(): #Composable RowScope.() -> Unit = {
IconButton(onClick = {}) { Icon(Icons.Filled.Search, "Search") }
}
#Composable
fun TopBar(title: Int, actions: #Composable (RowScope.() -> Unit) = {}) {
TopAppBar(backgroundColor = Color.White) {
//CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) {
Box {
// navigation icon
Row(Modifier.align(Alignment.CenterStart), verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = { }) {
Icon(Icons.Filled.ArrowBack, "Back")
}
}
// title
Row(Modifier.fillMaxSize(), verticalAlignment = Alignment.CenterVertically) {
Text(
modifier = Modifier
.fillMaxWidth()
.alpha(1f),
textAlign = TextAlign.Center,
maxLines = 1,
text = stringResource(title),
style = MaterialTheme.typography.h6
)
}
// actions
Row(
Modifier.align(Alignment.CenterEnd),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically,
content = actions
)
}
//}
}
}
I've set the surface as white in my theme. I've also set the background color of the top app bar as white. Still, the preview display a light grey instead of white.
var noteListState by remember { mutableStateOf(listOf("Drink water", "Walk")) }
This is my list of items. Any leads would be appreciated
To Get a Main UI with list and searchview
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MainScreen()
}
}
For Main()
#Composable
fun MainScreen() {
val textState = remember { mutableStateOf(TextFieldValue("")) }
Column {
SearchView(textState)
ItemList(state = textState)
}
}
For Serchview and list
#Composable
fun SearchView(state: MutableState<TextFieldValue>) {
TextField(
value = state.value,
onValueChange = { value ->
state.value = value
},
modifier = Modifier.fillMaxWidth(),
textStyle = TextStyle(color = Color.White, fontSize = 18.sp),
leadingIcon = {
Icon(
Icons.Default.Search,
contentDescription = "",
modifier = Modifier
.padding(15.dp)
.size(24.dp)
)
},
trailingIcon = {
if (state.value != TextFieldValue("")) {
IconButton(
onClick = {
state.value =
TextFieldValue("") // Remove text from TextField when you press the 'X' icon
}
) {
Icon(
Icons.Default.Close,
contentDescription = "",
modifier = Modifier
.padding(15.dp)
.size(24.dp)
)
}
}
},
singleLine = true,
shape = RectangleShape, // The TextFiled has rounded corners top left and right by default
colors = TextFieldDefaults.textFieldColors(
textColor = Color.White,
cursorColor = Color.White,
leadingIconColor = Color.White,
trailingIconColor = Color.White,
backgroundColor = MaterialTheme.colors.primary,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent
)
)
}
#Composable
fun ItemList(state: MutableState<TextFieldValue>) {
val items by remember { mutableStateOf(listOf("Drink water", "Walk")) }
var filteredItems: List<String>
LazyColumn(modifier = Modifier.fillMaxWidth()) {
val searchedText = state.value.text
filteredItems = if (searchedText.isEmpty()) {
items
} else {
val resultList = ArrayList<String>()
for (item in items) {
if (item.lowercase(Locale.getDefault())
.contains(searchedText.lowercase(Locale.getDefault()))
) {
resultList.add(item)
}
}
resultList
}
items(filteredItems) { filteredItem ->
ItemListItem(
ItemText = filteredItem,
onItemClick = { /*Click event code needs to be implement*/
}
)
}
}
}
#Composable
fun ItemListItem(ItemText: String, onItemClick: (String) -> Unit) {
Row(
modifier = Modifier
.clickable(onClick = { onItemClick(ItemText) })
.background(colorResource(id = R.color.purple_700))
.height(57.dp)
.fillMaxWidth()
.padding(PaddingValues(8.dp, 16.dp))
) {
Text(text = ItemText, fontSize = 18.sp, color = Color.White)
}
}
For More Detailed answer you can refer this link
Please could you explain more, this question is a bit short and unclear.
One way (i think you could do) is just to loop through the list and check if a element (of your list) contains that text.
val filterPattern = text.toString().lowercase(Locale.getDefault()) // text you are looking for
for (item in copyList) { // first make a copy of the list
if (item.name.lowercase(Locale.getDefault()).contains(filterPattern)) {
filteredList.add(item) // if the item contains that text add it to the list
}
}
... // here you then override noteListState
After you have made the filteredList you can just override the noteListState. Make sure to make a copy of that list beforehand and set it back when you don't want to show the filtered results.
New to Compose and struggling hard with more complex state cases.
I cant seem to change the text dynamically in these cards, only set the text manually. Each button press should change the text in a box, starting left to right, leaving the next boxes empty.
What is wrong here?
UI:
val viewModel = HomeViewModel()
val guessArray = viewModel.guessArray
#Composable
fun HomeScreen() {
Column(
modifier = Modifier
.fillMaxWidth()
) {
WordGrid()
Keyboard()
}
}
#Composable
fun WordGrid() {
CardRow()
}
#Composable
fun Keyboard() {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
MyKeyboardButton(text = "A", 35)
MyKeyboardButton(text = "B", 35)
}
}
#Composable
fun MyCard(text: String) {
Card(
modifier = Modifier
.padding(4.dp, 8.dp)
.height(55.dp)
.aspectRatio(1f),
backgroundColor = Color.White,
border = BorderStroke(2.dp, Color.Black),
) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(
text = text,
fontSize = 20.sp,
)
}
}
}
#Composable
fun CardRow() {
guessArray.forEach { rowCards ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
rowCards.forEach {
MyCard(it)
println(it)
}
}
}
}
#Composable
fun MyKeyboardButton(text: String, width: Int) {
Button(
onClick = {
guessArray[viewModel.currentRow][viewModel.column] = text
viewModel.column += 1
},
modifier = Modifier
.width(width.dp)
.height(60.dp)
.padding(0.dp, 2.dp)
) {
Text(text = text, textAlign = TextAlign.Center)
}
}
ViewModel:
class HomeViewModel : ViewModel() {
var currentRow = 0
var guessArray = Array(5) { Array(6) { "" }.toMutableList() }
var column = 0
}
The grid is created, but the text is never changed.
To make Compose view recompose with the new value, a mutable state should be used.
You're already using it with remember in your composable, but it also needs to be used in your view model for properties, which should trigger recomposition.
class HomeViewModel : ViewModel() {
var currentRow = 0
val guessArray = List(5) { List(6) { "" }.toMutableStateList() }
var column = 0
}
I need to implement LazyColumn with top fading edge effect. On Android I use fade gradient for ListView or RecyclerView, but couldn't find any solution for Jetpack Compose!
I tried to modify canvas:
#Composable
fun Screen() {
Box(
Modifier
.fillMaxWidth()
.background(color = Color.Yellow)
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.drawWithContent {
val colors = listOf(Color.Transparent, Color.Black)
drawContent()
drawRect(
brush = Brush.verticalGradient(colors),
blendMode = BlendMode.DstIn
)
}
) {
itemsIndexed((1..1000).toList()) { item, index ->
Text(
text = "Item $item: $index value",
modifier = Modifier.padding(12.dp),
color = Color.Red,
fontSize = 24.sp
)
}
}
}
}
But have wrong result:
What you could do is place a Spacer on top of the list, and draw a gradient on that Box. Make the Box small so only a small portion of the list has the overlay. Make the color the same as the background of the screen, and it will look like the content is fading.
val screenBackgroundColor = MaterialTheme.colors.background
Box(Modifier.fillMaxSize()) {
LazyColumn(Modifier.fillMaxSize()) {
//your items
}
//Gradient overlay
Spacer(
Modifier
.fillMaxWidth()
.height(32.dp)
.background(
brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent,
screenBackgroundColor
)
)
)
//.align(Alignment) to control the position of the overlay
)
}
Here's how it would look like:
However, this doesn't seem like quite what you asked for since it seems like you want the actual list content to fade out.
I don't know how you would apply an alpha to only a portion of a view. Perhaps try to dig into the .alpha sources to figure out.
Quick hack which fixes the issue: add .graphicsLayer { alpha = 0.99f } to your modifer
By default Jetpack Compose disables alpha compositing for performance reasons (as explained here; see the "Custom Modifier" section). Without alpha compositing, blend modes which affect transparency (e.g. DstIn) don't have the desired effect. Currently the best workaround is to add .graphicsLayer { alpha = 0.99F } to the modifier on the LazyColumn; this forces Jetpack Compose to enable alpha compositing by making the LazyColumn imperceptibly transparent.
With this change, your code looks like this:
#Composable
fun Screen() {
Box(
Modifier
.fillMaxWidth()
.background(color = Color.Yellow)
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
// Workaround to enable alpha compositing
.graphicsLayer { alpha = 0.99F }
.drawWithContent {
val colors = listOf(Color.Transparent, Color.Black)
drawContent()
drawRect(
brush = Brush.verticalGradient(colors),
blendMode = BlendMode.DstIn
)
}
) {
itemsIndexed((1..1000).toList()) { item, index ->
Text(
text = "Item $item: $index value",
modifier = Modifier.padding(12.dp),
color = Color.Red,
fontSize = 24.sp
)
}
}
}
}
which produces the correct result
Just a little nudge in the right direction. What this piece of code does is place a Box composable at the top of your LazyColumn with an alpha modifier for fading. You can make multiple of these Box composables in a Column again to create a smoother effect.
#Composable
fun FadingExample() {
Box(
Modifier
.fillMaxWidth()
.requiredHeight(500.dp)) {
LazyColumn(Modifier.fillMaxSize()) {
}
Box(
Modifier
.fillMaxWidth()
.height(10.dp)
.alpha(0.5f)
.background(Color.Transparent)
.align(Alignment.TopCenter)
) {
}
}
}
I optimised the #user3872620 solution. You have just to put this lines below your LazyColumn, VerticalPager.. and just adapt your offset / height, usually offset = height
Box(
Modifier
.fillMaxWidth()
.offset(y= (-10).dp)
.height(10.dp)
.background(brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent,
MaterialTheme.colors.background
)
))
)
You will got this render:
There is the render
This is a very simple implementation of FadingEdgeLazyColumn using AndroidView. Place AndroidView with gradient background applied to the top and bottom of LazyColumn.
#Stable
object GradientDefaults {
#Stable
val Color = androidx.compose.ui.graphics.Color.Black
#Stable
val Height = 30.dp
}
#Stable
sealed class Gradient {
#Immutable
data class Top(
val color: Color = GradientDefaults.Color,
val height: Dp = GradientDefaults.Height,
) : Gradient()
#Immutable
data class Bottom(
val color: Color = GradientDefaults.Color,
val height: Dp = GradientDefaults.Height,
) : Gradient()
}
#Composable
fun FadingEdgeLazyColumn(
modifier: Modifier = Modifier,
gradients: Set<Gradient> = setOf(Gradient.Top(), Gradient.Bottom()),
contentGap: Dp = 0.dp,
state: LazyListState = rememberLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
userScrollEnabled: Boolean = true,
content: LazyListScope.() -> Unit,
) {
val topGradient =
remember(gradients) { gradients.find { it is Gradient.Top } as? Gradient.Top }
val bottomGradient =
remember(gradients) { gradients.find { it is Gradient.Bottom } as? Gradient.Bottom }
ConstraintLayout(modifier = modifier) {
val (topGradientRef, lazyColumnRef, bottomGradientRef) = createRefs()
GradientView(
modifier = Modifier
.constrainAs(topGradientRef) {
top.linkTo(parent.top)
width = Dimension.matchParent
height = Dimension.value(topGradient?.height ?: GradientDefaults.Height)
}
.zIndex(2f),
colors = intArrayOf(
(topGradient?.color ?: GradientDefaults.Color).toArgb(),
Color.Transparent.toArgb()
),
visible = topGradient != null
)
LazyColumn(
modifier = Modifier
.constrainAs(lazyColumnRef) {
top.linkTo(
anchor = topGradientRef.top,
margin = when (topGradient != null) {
true -> contentGap
else -> 0.dp
}
)
bottom.linkTo(
anchor = bottomGradientRef.bottom,
margin = when (bottomGradient != null) {
true -> contentGap
else -> 0.dp
}
)
width = Dimension.matchParent
height = Dimension.fillToConstraints
}
.zIndex(1f),
state = state,
contentPadding = contentPadding,
reverseLayout = reverseLayout,
verticalArrangement = verticalArrangement,
horizontalAlignment = horizontalAlignment,
flingBehavior = flingBehavior,
userScrollEnabled = userScrollEnabled,
content = content
)
GradientView(
modifier = Modifier
.constrainAs(bottomGradientRef) {
bottom.linkTo(parent.bottom)
width = Dimension.matchParent
height = Dimension.value(bottomGradient?.height ?: GradientDefaults.Height)
}
.zIndex(2f),
colors = intArrayOf(
Color.Transparent.toArgb(),
(bottomGradient?.color ?: GradientDefaults.Color).toArgb(),
),
visible = bottomGradient != null
)
}
}
#Composable
private fun GradientView(
modifier: Modifier = Modifier,
#Size(value = 2) colors: IntArray,
visible: Boolean = true,
) {
AndroidView(
modifier = modifier,
factory = { context ->
val gradientBackground = GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
colors
).apply {
cornerRadius = 0f
}
View(context).apply {
layoutParams = LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT
)
background = gradientBackground
visibility = when (visible) {
true -> View.VISIBLE
else -> View.INVISIBLE
}
}
}
)
}
I have a transparent status/navigation bars, and when I place a compose element with default layout(top/left), it's placed under the status bar. In xml I use fitsSystemWindows to fix this, how can I get same effect in jetpack compose?
This works for me, In Activity I have :
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
JetpackComposePlaygroundTheme {
val controller = rememberAndroidSystemUiController()
CompositionLocalProvider(LocalSystemUiController provides controller) {
ProvideWindowInsets {
ComposeAppPlayground()
}
}
}
}
Then In compose app playground I have something like this:
Surface {
var topAppBarSize by remember { mutableStateOf(0) }
val contentPaddings = LocalWindowInsets.current.systemBars.toPaddingValues(
top = false,
additionalTop = with(LocalDensity.current) { topAppBarSize.toDp() }
)
Column(modifier = Modifier.navigationBarsPadding().padding(top = contentPaddings.calculateTopPadding())) {
// content can go here forexample...
// if you want the content go below status bar
// you can remove the top padding for column
}
InsetAwareTopAppBar(
title = { Text(stringResource(R.string.home)) },
backgroundColor = MaterialTheme.colors.surface.copy(alpha = 0.9f),
modifier = Modifier
.fillMaxWidth()
.onSizeChanged { topAppBarSize = it.height }
)
}
}
Also the InsetAwareTopAppBar I found from guids mentioned in https://google.github.io/accompanist/insets/
#Composable
fun InsetAwareTopAppBar(
title: #Composable () -> Unit,
modifier: Modifier = Modifier,
navigationIcon: #Composable (() -> Unit)? = null,
actions: #Composable RowScope.() -> Unit = {},
backgroundColor: Color = MaterialTheme.colors.primarySurface,
contentColor: Color = contentColorFor(backgroundColor),
elevation: Dp = 4.dp
) {
Surface(
color = backgroundColor,
elevation = elevation,
modifier = modifier
) {
TopAppBar(
title = title,
navigationIcon = navigationIcon,
actions = actions,
backgroundColor = Color.Transparent,
contentColor = contentColor,
elevation = 0.dp,
modifier = Modifier
.statusBarsPadding()
.navigationBarsPadding(bottom = false)
)
}
}