SUBSCRIBE NOW
I always learn something just by skimming it that makes me want to bookmark the issue now and dig deeper later
SUBSCRIBE NOW
Keep up the good work with the newsletter 💪 I really enjoy it
SUBSCRIBE NOW
Dispatch is a must read for Android devs today and my go-to for keeping up with all things Jetpack Compose
SUBSCRIBE NOW
Dispatch has been my go-to resource as it's packed with useful information while being fun at the same time
SUBSCRIBE NOW
The content is light, fun, and still useful. I especially appreciate the small tips that are in each issue
SUBSCRIBE NOW
I truly love this newsletter ❤️🔥 Spot on content and I know there's a lot of effort that goes behind it
SUBSCRIBE NOW
Thanks for taking the time and energy to do it so well
JetpackCompose.app's Newsletter
I always learn something just by skimming it that makes me want to bookmark the issue now and dig deeper later
JetpackCompose.app's Newsletter
Keep up the good work with the newsletter 💪 I really enjoy it
JetpackCompose.app's Newsletter
Dispatch is a must read for Android devs today and my go-to for keeping up with all things Jetpack Compose
JetpackCompose.app's Newsletter
Dispatch has been my go-to resource as it's packed with useful information while being fun at the same time
JetpackCompose.app's Newsletter
The content is light, fun, and still useful. I especially appreciate the small tips that are in each issue
JetpackCompose.app's Newsletter
I truly love this newsletter ❤️🔥 Spot on content and I know there's a lot of effort that goes behind it
JetpackCompose.app's Newsletter
Thanks for taking the time and energy to do it so well
Partial Border
Author: Andrey Mischenko
Sometimes you want to draw a border around a Composable function, but only on one or two sides. This snippet provides a Modifier that allows you to specify which sides of the border should be drawn.
import androidx.compose.runtime.Stable | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.drawBehind | |
import androidx.compose.ui.graphics.Color | |
import androidx.compose.ui.graphics.Path | |
import androidx.compose.ui.graphics.drawscope.DrawScope | |
import androidx.compose.ui.unit.Dp | |
/** | |
* Border definition can be extended to provide border style or [androidx.compose.ui.graphics.Brush] | |
* One more way is make it sealed class and provide different implementations: | |
* SolidBorder, DashedBorder etc | |
*/ | |
data class Border(val strokeWidth: Dp, val color: Color) | |
@Stable | |
fun Modifier.border( | |
start: Border? = null, | |
top: Border? = null, | |
end: Border? = null, | |
bottom: Border? = null, | |
) = | |
drawBehind { | |
start?.let { | |
drawStartBorder(it, shareTop = top != null, shareBottom = bottom != null) | |
} | |
top?.let { | |
drawTopBorder(it, shareStart = start != null, shareEnd = end != null) | |
} | |
end?.let { | |
drawEndBorder(it, shareTop = top != null, shareBottom = bottom != null) | |
} | |
bottom?.let { | |
drawBottomBorder(border = it, shareStart = start != null, shareEnd = end != null) | |
} | |
} | |
private fun DrawScope.drawTopBorder( | |
border: Border, | |
shareStart: Boolean = true, | |
shareEnd: Boolean = true | |
) { | |
val strokeWidthPx = border.strokeWidth.toPx() | |
if (strokeWidthPx == 0f) return | |
drawPath( | |
Path().apply { | |
moveTo(0f, 0f) | |
lineTo(if (shareStart) strokeWidthPx else 0f, strokeWidthPx) | |
val width = size.width | |
lineTo(if (shareEnd) width - strokeWidthPx else width, strokeWidthPx) | |
lineTo(width, 0f) | |
close() | |
}, | |
color = border.color | |
) | |
} | |
private fun DrawScope.drawBottomBorder( | |
border: Border, | |
shareStart: Boolean, | |
shareEnd: Boolean | |
) { | |
val strokeWidthPx = border.strokeWidth.toPx() | |
if (strokeWidthPx == 0f) return | |
drawPath( | |
Path().apply { | |
val width = size.width | |
val height = size.height | |
moveTo(0f, height) | |
lineTo(if (shareStart) strokeWidthPx else 0f, height - strokeWidthPx) | |
lineTo(if (shareEnd) width - strokeWidthPx else width, height - strokeWidthPx) | |
lineTo(width, height) | |
close() | |
}, | |
color = border.color | |
) | |
} | |
private fun DrawScope.drawStartBorder( | |
border: Border, | |
shareTop: Boolean = true, | |
shareBottom: Boolean = true | |
) { | |
val strokeWidthPx = border.strokeWidth.toPx() | |
if (strokeWidthPx == 0f) return | |
drawPath( | |
Path().apply { | |
moveTo(0f, 0f) | |
lineTo(strokeWidthPx, if (shareTop) strokeWidthPx else 0f) | |
val height = size.height | |
lineTo(strokeWidthPx, if (shareBottom) height - strokeWidthPx else height) | |
lineTo(0f, height) | |
close() | |
}, | |
color = border.color | |
) | |
} | |
private fun DrawScope.drawEndBorder( | |
border: Border, | |
shareTop: Boolean = true, | |
shareBottom: Boolean = true | |
) { | |
val strokeWidthPx = border.strokeWidth.toPx() | |
if (strokeWidthPx == 0f) return | |
drawPath( | |
Path().apply { | |
val width = size.width | |
val height = size.height | |
moveTo(width, 0f) | |
lineTo(width - strokeWidthPx, if (shareTop) strokeWidthPx else 0f) | |
lineTo(width - strokeWidthPx, if (shareBottom) height - strokeWidthPx else height) | |
lineTo(width, height) | |
close() | |
}, | |
color = border.color | |
) | |
} |
import android.os.Bundle | |
import androidx.appcompat.app.AppCompatActivity | |
import androidx.compose.foundation.Text | |
import androidx.compose.foundation.layout.* | |
import androidx.compose.runtime.Composable | |
import androidx.compose.ui.Alignment | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.graphics.Color | |
import androidx.compose.ui.platform.setContent | |
import androidx.compose.ui.unit.dp | |
import androidx.ui.tooling.preview.Preview | |
class MainActivity : AppCompatActivity() { | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContent { | |
BordersDemo() | |
} | |
} | |
} | |
data class Borders( | |
val start: Border? = null, | |
val end: Border? = null, | |
val top: Border? = null, | |
val bottom: Border? = null | |
) | |
@Preview(showBackground = true) | |
@Composable | |
fun BordersDemo() { | |
Column( | |
horizontalGravity = Alignment.CenterHorizontally, | |
verticalArrangement = Arrangement.Center, | |
modifier = Modifier.fillMaxSize() | |
) { | |
val strokeWidth = 8.dp | |
val start = Border(strokeWidth, Color.Cyan) | |
val end = Border(strokeWidth, Color.Green) | |
val bottom = Border(strokeWidth, Color.Blue) | |
val top = Border(strokeWidth, Color.Red) | |
val samples = listOf( | |
Borders(bottom = bottom, end = end), | |
Borders(bottom = bottom, top = top), | |
Borders(start = start, end = end), | |
Borders(top = top, start = start, end = end), | |
Borders(bottom = bottom, top = top, start = start, end = end), | |
) | |
samples.forEach { borders -> | |
Text( | |
text = "Hello Android!", | |
modifier = Modifier | |
.border( | |
bottom = borders.bottom, | |
end = borders.end, | |
start = borders.start, | |
top = borders.top | |
) | |
.padding(strokeWidth + 8.dp) | |
) | |
Spacer(modifier = Modifier.height(24.dp)) | |
} | |
} | |
} |
Have a project you'd like to submit? Fill this form, will ya!
If you like this snippet, you might also like:
Maker OS is an all-in-one productivity system for developers
I built Maker OS to track, manage & organize my life. Now you can do it too!