Combo Breaker
Composable widget for Jetpack Compose that allows to flow text around arbitrary shapes over
multiple columns. The TextFlow
composable behaves as a Box
layout and will automatically
flow the text content around its children.
For instance, the following code:
TextFlow(
SampleText,
style = TextStyle(fontSize = 14.sp),
columns = 2
) {
Image(
bitmap = letterT.asImageBitmap(),
contentDescription = "",
modifier = Modifier
.flowShape(FlowType.OutsideEnd)
)
Image(
bitmap = badgeBitmap.asImageBitmap(),
contentDescription = "",
modifier = Modifier
.align(Alignment.Center)
.flowShape(margin = 6.dp)
)
}
will produce this result:
As you can see, any child of TextFlow
allows text to flow around a rectangular shape of the same
dimensions of the child. The flowShape
modifier is used to control where text flows around the
shape (to the right/end of the T) and around both the left and right sides of the landscape photo
(default behavior). In addition, you can define a margin around the shape.
The flowShape
modifier also lets you specify a specific shape instead of a default rectangle.
This can be done by passing a Path
or a lambda that returns a Path
. The lambda alternative
is useful when you need to create a Path
based on the dimensions of the TextFlow
or the
dimensions of its child.
Here is an example of a TextFlow
using non-rectangular shapes:
val microphoneShape = microphoneBitmap.toContour(alphaThreshold = 0.5f).asComposePath()
val badgeShape = badgeShape.toContour(alphaThreshold = 0.5f).asComposePath()
TextFlow(
SampleText,
style = TextStyle(fontSize = 14.sp),
columns = 2
) {
Image(
bitmap = microphoneBitmap.asImageBitmap(),
contentDescription = "",
modifier = Modifier
.offset { Offset(-microphoneBitmap.width / 4.5f, 0.0f).round() }
.flowShape(FlowType.OutsideEnd, 6.dp, microphoneShape)
)
Image(
bitmap = badgeBitmap.asImageBitmap(),
contentDescription = "",
modifier = Modifier
.align(Alignment.Center)
.flowShape(FlowType.Outside, 6.dp, badgeShape)
)
}
Using the extension Bitmap.toContour
provided by this library, a shape can be extracted from a
bitmap and used as the flow shape for the desired child:
Combo Breaker is compatible with API 29+.
Maven
repositories {
// ...
mavenCentral()
}
dependencies {
implementation 'dev.romainguy:combo-breaker:0.2.0'
}
Limitations and planned work
- Backport to earlier API levels.
- Optimizations!
- More comprehensive
TextFlowLayoutResult
. - Paths with multiple contours are treated as a single shape. A future feature will allow such paths to be treated as multiple shapes.
- Add support to ellipsize the last line when the entire text cannot fit in the layout area.
- Add support for text-relative placement of flow shapes.
- Implement margins support without relying on
Path.op
which can be excessively expensive with complex paths. - Reduce allocations during the layout phase.
- BiDi text hasn’t been tested yet, and probably doesn’t work properly (RTL layouts are however supported for the placement of flow shapes and the handling of columns).
- Improve performance of contours extraction from an image (could be multi-threaded for instance).
- Investigate an alternative and simpler way to handle placement around shapes (beam cast instead of the purely geometric approach that currently requires a lot of intersection work).
- Support flowing text inside shapes.
License
Please see LICENSE.
Attribution
The render of the microphone was made possible thanks to RCA 44-BX Microphone by Tom Seddon, licensed under Creative Commons Attribution.
Sample text taken from the Wikipedia Hyphen article.