How to properly handle loading initials as place holders in Jetpack Compose - android

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))
}
}
}

Related

How to make all images from API be in one size in Jetpack Compose?

I have the following composable that represents a list model -
#Composable
fun DashboardCard(
modifier: Modifier = Modifier,
model: DashboardCardModel,
onCardClicked: (model: DashboardCardModel) -> Unit
) {
Column(
modifier = modifier
.size(200.dp, 200.dp)
.background(Color.Transparent)
.padding(16.dp)
.clickable {
onCardClicked(model)
},
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceAround
) {
if (model.showDefaultThumbnail) {
AsyncImage(
modifier = Modifier
.size(90.dp)
.clip(RoundedCornerShape(10.dp)),
model = model.thumbnailUrl, contentDescription = ""
)
} else {
Image(
modifier = Modifier
.size(90.dp)
.clip(RoundedCornerShape(10.dp)),
painter = painterResource(id = com.tinytap.R.drawable.tinytap),
contentDescription = ""
)
}
Image(
modifier = Modifier
.size(25.dp)
.padding(top = 10.dp)
.alpha(if (model.isCurrentPostOfInterest) 1f else 0f),
painter = painterResource(id = com.tinytap.R.drawable.post_of_interest),
contentDescription = null
)
Text(
modifier = Modifier.padding(top = 10.dp),
fontSize = 16.sp,
color = Color.White,
text = model.title,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = model.author,
fontSize = 12.sp,
color = Color.LightGray,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
The issue is that I am using a fixed size for both my Image and AsyncImage but sometimes the API gives me images that are very wide, causing me to have the following inconsistency in the UI -
How can I make it that my images show up exactly the same? I tried using all kind of crops but the results ended up messing my image
Putting into consideration that image might be shorter, taller, wider or smaller.
To solve this issue, I recommend you to use Coil here is a sample of code that will solve your issue :
Card(
modifier = Modifier.size(119.dp, 92.dp),
shape = RoundedCornerShape(10.dp)
) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(imageURL)
.build(),
placeholder = painterResource(R.drawable.complex_placeholder),
error = painterResource(R.drawable.complex_placeholder),
contentDescription = "complex image",
contentScale = ContentScale.Crop,//The line that will affect your image size and help you solve the problem.
)
}
The above code will always show fully covered box with image for any case, also don't forget to determine size of container (Card in my example '119,92' ).
To know more about different attributes and their effect on your code, select what suits you the best
Check more here (reference of image attached) : Content scaletype fully illustrated

HorizontalPager with lazy column in a lazy column

I'm trying to make a UI similar to the YouTube channel UI. I've got two lazy columns (I don't know if it's the correct way or not) in a column, in which the first lazy column consists of data regarding the second lazy column and a sticky header. I've added a horizontal pager from the accompanist library in which the second lazy column exists. As expected, the first lazy column doesn't scroll. Is there any way to scroll these two lazy columns simultaneously or as a single UI component?
PS:- I've checked the related question but it doesn't solve the problem
Code:-
Column(modifier = Modifier.background(md_theme_dark_surface)) {
LazyColumn{
item {
SmallTopAppBar(
title = {
Text(
text = headerText,
style = MaterialTheme.typography.titleMedium,
fontSize = 24.sp,
color = md_theme_dark_onSurface
)
},
colors = TopAppBarDefaults.smallTopAppBarColors(containerColor = md_theme_dark_surface)
)
AsyncImage(
model = ImageRequest.Builder(context).crossfade(true)
.data("")
.build(),
contentDescription = "null",
modifier = Modifier
.fillMaxWidth()
.height(100.dp),
onError = painterResource(id = R.drawable.image),
contentScale = ContentScale.Crop
)
Box(
modifier = Modifier
.padding(top = 15.dp)
.fillMaxWidth()
.wrapContentHeight(),
contentAlignment = Alignment.Center
) {
Text(
text = "Cover-art stolen from xyz from abc",
style = MaterialTheme.typography.titleMedium,
fontSize = 16.sp,
color = md_theme_dark_onSurface
)
}
}
stickyHeader {
ScrollableTabRow(
selectedTabIndex = pagerState.currentPage,
containerColor = md_theme_dark_surface
) {
tabsList.forEachIndexed { index, tabsData ->
Tab(
selected = pagerState.currentPage == index, onClick = {
coroutineScope.launch {
pagerState.animateScrollToPage(index)
}.start()
}, modifier = Modifier.padding(20.dp)
) {
Text(
text = tabsData.name,
style = MaterialTheme.typography.titleMedium,
color = md_theme_dark_onSurface,
fontSize = 17.sp
)
}
}
}
}
}
HorizontalPager(count = tabsList.size, state = pagerState) {
LazyColumn{
itemsIndexed(fetchedData) { index, dataItem ->
if (!dataItem.data.is_video && dataItem.data.url.contains(
regex = Regex(
"/i.redd.it"
)
)
) {
val screensList = subHomeScreensList
screensList[pagerState.currentPage].composable()
}
}
}
}
}
Screen recording of current code output.
How can I solve this problem, Thank you :)

Gradient backround for FAB in Jetpack compose

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.

Use drawable pattern as button background

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.

Android compose: Surface can have only one direct measurable child

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)
}
}
}
}

Categories

Resources