I am trying the change a button background from a solid color to a dawable image with transparent background to make sure I can see the pattern
I moved to jetpack so I have created
Button(onClick = { /*TODO*/ },
colors = ButtonDefaults.buttonColors(
backgroundColor = colorResource(id = R.color.gainsboro_00)),
modifier = Modifier
.fillMaxWidth()
.height(60.dp),
shape = RoundedCornerShape(0.dp)) {
Text(text = stringResource(id = R.string.login),
color = colorResource(id = R.color.gainsboro_05),
style = MaterialTheme.typography.body1)
}
This button has a grey background.
I would like to apply the drawable below:
<?xml version="1.0" encoding="UTF-8" ?>
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/pattern_stripe_loose_gainsboro_05"
android:tileMode="repeat"/>
as a background instead of the colored one and have the pattern displayed. the above xml is just repeating a pattern to create a background
So I expect this:
using the pattern:
When using the traditional way with layout and so on it works but I can't make it work on jetpack
Any idea ?
Compose does not have such a feature yet. You can create a feature request on issue tracker.
Before this is implemented, you could create a pure Compose solution that draws a lot of images, but I think there is no point in doing that when there is ImageView already optimized by engineers. In such cases you can use AndroidView for interop with old views.
#Composable
fun TileAndroidImage(
#DrawableRes drawableId: Int,
contentDescription: String,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
val drawable = remember(drawableId) {
BitmapDrawable(
context.resources,
BitmapFactory.decodeResource(
context.resources,
drawableId
)
).apply {
tileModeX = Shader.TileMode.REPEAT
tileModeY = Shader.TileMode.REPEAT
}
}
AndroidView(
factory = {
ImageView(it)
},
update = { imageView ->
imageView.background = drawable
},
modifier = modifier
.semantics {
this.contentDescription = contentDescription
role = Role.Image
}
)
}
Next part, is puttin it in background of the button. In compose we use containers to do so. You can create your TiledButton. I pass zero padding to container button and add real padding manually so it'll not affect the background:
#Composable
fun TiledButton(
onClick: () -> Unit,
#DrawableRes backgroundDrawableId: Int,
modifier: Modifier = Modifier,
enabled: Boolean = true,
shape: Shape = MaterialTheme.shapes.small,
border: BorderStroke? = null,
contentColor: Color = MaterialTheme.colors.primary,
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
content: #Composable RowScope.() -> Unit
) {
Button(
onClick = onClick,
contentPadding = PaddingValues(0.dp),
enabled = enabled,
shape = shape,
border = border,
elevation = null,
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Transparent,
contentColor = contentColor,
disabledBackgroundColor = Color.Transparent,
disabledContentColor = contentColor.copy(alpha = ContentAlpha.disabled),
),
modifier = modifier
) {
Box(
contentAlignment = Alignment.Center,
) {
TileAndroidImage(
drawableId = backgroundDrawableId,
contentDescription = "...",
modifier = Modifier.matchParentSize()
)
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(contentPadding),
content = content,
)
}
}
}
Usage:
TiledButton(
onClick = { /*TODO*/ },
backgroundDrawableId = R.drawable.tile,
border = BorderStroke(1.dp, Color.Blue),
) {
Text("Apple")
}
Why not just create a box with that background and place a text while making the entire box clickable? The box will act as the entire button. I mean that's what a button essentially is, isn't it? A box containing some text? Oh, if you cannot figure out a way to get the drawable set as a background on your Box, just use something like an Image combined with the fillMaxSize() Modifier.
Something like
Box(Modifier.fillMaxWidth().height(...),
horizontalArrangement = Arrangement.CenterHorizontally){
Image(painter = painterResource(R.drawable.b_pattern),
contentDescription = "Lorem Ipsum")
Text(modifier = Modifier.align(Alignment.CenterVertically),
text = "Lorem Ipsum")
}
Just try it out and let me know please.
Related
I am trying to show initials when the user does not upload an icon, or set an icon, my only problem is our code uses jetpack compose and I have not found a better way to display this. My code is below but what this code does is it draws the name not in the card.
I have the ProfileCard, which has an Image and two text see image
my challenge now is how do I center the initials, and have a color in the background. I think draw Text is not working. In short I am wondering why it is not drawing on my Image in the card.
How to make initials icons using Coil in Jetpack Compose
I want to achieve something like this with text on the side
My code.
val painter = rememberAsyncImagePainter(model = getProfileAvatar(e.id))
val errorState = painter.state is AsyncImagePainter.State.Error
val emptyState = painter.state is AsyncImagePainter.State.Empty
val isErrorState = painter.state is AsyncImagePainter.State.Error
val textMeasure = rememberTextMeasurer()
val textLayoutResult = textMeasure.measure(text = buildAnnotatedString { append(personName) },
style = TextStyle(color = Color.White,
fontSize = 16.sp))
// the composable Profile Card that has the image and text. Hence the painter is what I //am trying to draw to.
ProfileCard( modifier = Modifier.drawBehind
{ if (errorState || emptyState)
{ drawText(textLayoutResult = textLayout) }
},
painter = painter,
onCardClick = {
})
// My Coil Loader
private fun getProfileAvatar(id: String) : ImageRequest {
val url = ServiceAPI.photoUrl(id)
return ImageRequest.Builder(requireContext())
.data(url)
.addHeader() )
.build() }
This is how it looks drawing at the back.
Put in your Card a Row with a Alignment.CenterVertically.
Something like:
Card(
modifier = Modifier.fillMaxWidth().height(100.dp),
elevation = 2.dp
){
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
){
Text(
modifier = Modifier
.padding(16.dp)
.drawBehind {
drawCircle(
color = Teal200,
radius = this.size.maxDimension
)
},
text = "NG",
style = TextStyle(color = Color.White, fontSize = 20.sp)
)
Column(
Modifier.padding(start = 20.dp),
verticalArrangement = Arrangement.spacedBy(4.dp, CenterVertically),
){
Text("Name Surname",style = TextStyle(fontSize = 14.sp))
Text("Active Now",style = TextStyle(fontSize = 14.sp))
}
}
}
In my Compose app I need to create a circle checkboxes. I've already achieved that with the code below:
#Composable
fun CircleCheckBox(
isChecked: Boolean,
modifier: Modifier = Modifier,
onChecked: () -> Unit = {},
checkedBackgroundColor: Color,
unCheckedBackgroundColor: Color,
checkedIconColor: Color,
unCheckedIconColor: Color
) {
Box(
modifier = modifier
.clip(CircleShape)
.clickable { onChecked() }
.border(
width = if (!isChecked) 1.dp else 0.dp,
color = if (!isChecked) checkedBackgroundColor else Color.Transparent,
shape = CircleShape
)
.background(
color = if (isChecked) checkedBackgroundColor else unCheckedBackgroundColor,
shape = CircleShape
),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = stringResource(R.string.icon_check),
modifier = Modifier.padding(3.dp),
tint = if (isChecked) checkedIconColor else unCheckedIconColor
)
}
}
But in my app I have a gradient backgrounds on cards, so I want to make checkmarks transparent, but in this realization it's impossible because of the background of the Box. Is there any way to achieve it, like on image below?
You can find an appropriate default icons instead of drawing on your own. Icons.Default.CheckCircle is what you're looking for - it has transparent checkmark inside a filled circle. And you can use Icons.Outlined.Circle instead of border modifier:
#Composable
fun CircleCheckBox(
isChecked: Boolean,
modifier: Modifier = Modifier,
onChecked: () -> Unit = {},
color: Color,
) {
Box(
contentAlignment = Alignment.Center,
modifier = modifier
.clip(CircleShape)
.clickable { onChecked() }
) {
Icon(
imageVector = if (isChecked) Icons.Default.CheckCircle else Icons.Outlined.Circle,
contentDescription = stringResource(R.string.icon_check),
tint = color
)
}
}
Result:
I think you can achieve this by importing custom icon vector like this
Here, the check is transparent so, it will show the gradient background.
I want to add a Floating Action Button with a gradient background in Jetpack Compose. I have the following snippet to do so:
FloatingActionButton(
onClick = {
coroutineScope.safeLaunch {
navController.navigate("AddTodoPage") {
launchSingleTop = true
}
}
},
shape = RoundedCornerShape(14.dp),
backgroundColor = Color.Transparent,
modifier = Modifier
.constrainAs(addFab) {
bottom.linkTo(parent.bottom)
end.linkTo(parent.end)
}
.offset(x = (-16).dp, y = (-24).dp)
.background(
brush = Brush.verticalGradient(
colors = BluePinkGradient()
),
shape = RoundedCornerShape(14.dp)
)
) {
Icon(
painter = painterResource(id = R.drawable.ic_add),
contentDescription = "Add icon",
tint = Color.White
)
}
fun BluePinkGradient(inverse: Boolean = false) = when (inverse) {
true -> listOf(
MutedBlue,
MutedPink
)
false -> listOf(
MutedPink,
MutedBlue
)
}
val MutedBlue = Color(0xFF26A69A)
val MutedPink = Color(0xFFEC407A)
But from the image below, the button has a "Whitish" shade on the plus icon. How can I remove that shade or a better way to set the FAB background to a gradient?
Fab Image
'"Whitish" shade on the plus icon' is the result of elevation parameter. You can zero it, but it doesn't looks like you need FAB in the first place.
As you need to custom the button that much, you can use IconButton instead:
IconButton(
onClick = {
},
modifier = Modifier
.background(
brush = Brush.verticalGradient(
colors = BluePinkGradient()
),
shape = RoundedCornerShape(14.dp)
)
) {
Icon(
painter = painterResource(id = R.drawable.ic_undo),
contentDescription = "Add icon",
tint = Color.White
)
}
FloatingActionButton is only applying some Material defaults to the content, it doesn't make it really floating, it has to be done with the container.
I have developed the following solution, which I have confirmed as working:
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun CrazyFloatingActionButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
gradient: List<Color>,
contentColor: Color = contentColorFor(gradient[0]),
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
content: #Composable () -> Unit
) {
Surface(
modifier = modifier,
shape = shape,
contentColor = contentColor,
elevation = elevation.elevation(interactionSource).value,
onClick = onClick,
role = Role.Button,
interactionSource = interactionSource,
indication = rememberRipple()
) {
CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
ProvideTextStyle(MaterialTheme.typography.button) {
Box(
modifier = Modifier
.defaultMinSize(minWidth = 56.dp, minHeight = 56.dp)
.background(brush = Brush.verticalGradient(gradient)),
contentAlignment = Alignment.Center
) { content() }
}
}
}
}
Just prepend Crazy to your Composable and you should be good to go.
Android Jetpack compose Card draws a border around the card when background color has some transparency. This is how it looks in AS:
But this is how it looks in the app:
If I set background to a solid color it works, but by default backgroundColor is a surface color from material (in my app val white850 = Color(0xD9FFFFFF)) and it looks like on the picture above.
#Composable
fun TraitCard(trait: Trait) {
Card(
shape = MaterialTheme.shapes.small,
modifier = Modifier.size(width = 192.dp, height = 56.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Icon(
imageVector = Icons.Rounded.ChildFriendly,
contentDescription = "",
modifier = Modifier
.fillMaxHeight()
.background(color = MaterialTheme.colors.background)
.aspectRatio(1f)
.padding(8.dp),
tint = MaterialTheme.colors.onBackground
)
Text(
text = trait.name,
style = MaterialTheme.typography.h3,
modifier = Modifier.padding(horizontal = 16.dp),
)
}
}
}
Does anyone have a clue why it's happening?
This is because of the elevation that Card has by default (and how shadows are drawn), if you remove the elevation this won't happen.
You can try to convert the semitransparent color to the non transparent one with something like:
backgroundColor = Color(0xD9FFFFFF).compositeOver(Color.White),
I'm new to Jetpack compose, I have create a composable like this.
Column(
Modifier.clickable(onClick = onclick)
.fillMaxWidth().background(pastelGray)
.padding(16.dp)
) {
Card(backgroundColor = Color.Cyan) {
// Image(asset = vectorResource(id = R.drawable.ic_coupon_back), modifier = Modifier.fillMaxWidth())
Column(modifier = Modifier.padding(8.dp)) {
Text(text = coupon.couponTitle, color = Color.Red, fontSize = 20.sp)
Text(text = coupon.couponSubTitle, color = Color.Black, fontSize = 13.sp)
Text(text = coupon.couponDateTitle, color = Color.Gray, fontSize = 11.sp)
}
}
}
When I add that commented image I got this error:
Surface can have only one direct measurable child!
I could not find the reason till now.
Because Card() composable used in your code uses Surface under the hood:
#Composable
fun Card(
modifier: Modifier = Modifier,
shape: Shape = MaterialTheme.shapes.medium,
backgroundColor: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(backgroundColor),
border: BorderStroke? = null,
elevation: Dp = 1.dp,
content: #Composable () -> Unit
) {
Surface(
modifier = modifier,
shape = shape,
color = backgroundColor,
contentColor = contentColor,
elevation = elevation,
border = border,
content = content
)
}
source: Official Card Implementation code
And Surface is a kind like ScrollView doesn't accept more than one direct child. So you need to wrap your code inside Card() into a single parent that is a direct child of Card or in other words direct child of Surface, example:
Card(backgroundColor = Color.Cyan) {
CardContent()
}
private fun CardContent() {
Column() {
// Image(asset = vectorResource(id = R.drawable.ic_coupon_back), modifier = Modifier.fillMaxWidth())
Column(modifier = Modifier.padding(8.dp)) {
Text(text = coupon.couponTitle, color = Color.Red, fontSize = 20.sp)
Text(text = coupon.couponSubTitle, color = Color.Black, fontSize = 13.sp)
Text(text = coupon.couponDateTitle, color = Color.Gray, fontSize = 11.sp)
}
}
}
}