https://kotlinlang.org logo
#compose
Title
# compose
a

Andrew Hughes

01/28/2022, 12:45 AM
Does anyone know if it's possible to programmatically determine what the current color is beneath/behind a composable? The scenario I'm trying to solve is that I want to composite a transparent overlay color on top of the background color and use the resulting composite color in order to create a cut-out appearance. For example, see the area around the check mark in the attached image. I, of course, can pass the background color to composite over into the composable, but that would require passing this down through several composables (which other than to pass it on, shouldn't need to know the background color).
a

Adam Powell

01/28/2022, 2:08 AM
generally no, but this is fairly close to what LocalContentColor is used for; containers publish colors down to children that may want compatible colors to use in their own content.
see also the surface/onSurface material theme colors
a

Andrew Hughes

01/28/2022, 4:59 AM
Thanks Adam! I understand what you're saying about
LocalContentColor
, however, that doesn't really help me in this case since what I want is the color that this composable is going to be sitting on. This will most likely be
background
or
surface
, so it'll probably fine to just hard code it to one of those. I was just hoping to not have to do that and make it a little more robust (without having to pass the value in either). I know I could also create my own
CompositionLocal
instead of passing it down, but that's probably overkill.
m

myanmarking

01/28/2022, 10:05 AM
you can capture a bitmap of the screen and use palette to analyse the colors. But i guess that’s not ideal :p
a

Adam Powell

01/28/2022, 2:45 PM
Reading back from gpu memory to process that data on the CPU is generally quite bad for performance, plus if the screen is already drawn to be available for a traditional screenshot then you missed your chance to draw your overlay content on the same frame
You can have both the background and this overlay appeal to the same source of truth to determine their color, whether that's the same parameter passed to a shared parent or a CompositionLocal
c

Chris Sinco [G]

01/28/2022, 6:10 PM
Might also be simpler to have two versions of the folder shape: whole one and one with cutout? Are you drawing that folder shape programmatically?
a

Andrew Hughes

01/29/2022, 10:33 PM
Thanks for the suggestions. The folder started as an `Icon`/`VectorDrawable` but I just converted it into a
Shape
so I can clip the touch ripple to the shape. I could create a separate version of the folder with the cutout, but the folder can scale to different sizes while the checkmark circle should stay the same size, so I don't think that will work. On a separate note, is there a way to convert SVGs or VDs into Shapes? I just manually converted the path data into Path commands, but it'd be helpful if there was a tool to do this.
It just occurred to me that I could possibly create a folder-with-cutout shape by programmatically subtracting a fixed-size circle path from the folder path.
🤔 1
c

Chris Sinco [G]

01/30/2022, 1:50 AM
is there a way to convert SVGs or VDs into Shapes?
Not in Android Studio, but I did write this Figma plugin that helps do the conversion. One could write an Android Studio plugin as well since what is happening is it's just transforming SVG paths to Android paths
🙌 1
a

Andrew Hughes

01/31/2022, 11:38 PM
Just discovered
PathParser
which lets me easily convert the path data to a
Path
for a
Shape
! Obviously it's more efficient to do this conversion once rather than every time, but it definitely makes it easier for prototyping.
🎉 1
c

Chris Sinco [G]

02/01/2022, 1:10 AM
Nice! Would curious to see how it fits into your prototyping flow
a

Andrew Hughes

02/02/2022, 12:32 AM
Well my hope was to make it so I could simply replace the path data provided to
PathParser().parsePathString()
in order to build the
Shape
. However, it appears I can't use
PathParser
as I hoped because the
Path
for a
Shape
needs to be adjusted to match the size of the shape. I'm doing this currently by calculating the scaleX and scaleY between the shape size and path size and then multiplying each point in the path by the appropriate scale when manually calling each
Path
command. However, as far as I can tell, there's no way to simply scale a
Path
. 😕 I see that
Vector.kt
is using
PathParser
, but I assume it's just scaling the
Canvas
when drawing the path/vector to change it's size.
@Chris Sinco [G] I've tried your Figma plugin a few times, but I haven't gotten it to work. I'm seeing "Shape generated and copied to clipboard 🎉 " but then there's nothing on the clipboard. Any suggestions (or is there any way to submit a bug report)? Thanks!
c

Chris Sinco [G]

02/02/2022, 1:07 AM
Oh I see. Seems the copying to clipboard isn’t working when you run the plugin in Figma in the browser 🤦 Works fine in the desktop app
I’ll look into it. For now though, using the plugin in Figma desktop should work
FYI to scale a Path, I used this snippet:
Copy code
val bounds = RectF()
val aPath = path.asAndroidPath()
aPath.computeBounds(bounds, true)
val scaleMatrix = Matrix()
scaleMatrix.setScale(
    size.width / baseWidth,
    size.height / baseHeight,
    0f,
    0f
)
aPath.transform(scaleMatrix)
a

Andrew Hughes

02/02/2022, 1:49 AM
Ah, got it. I'm on Ubuntu and there doesn't appear to be a desktop app 😞 Thanks for the tip on converting to an Android path! I should have checked that. I assumed the compose path would have the same capabilities as the Android path since it's using an Android path underneath. I did see that there's a
Path.translate()
but no
Path.scale()
(or just
Path.transform()
). Do you know if there's any reason these are not exposed by the compose
Path
or is it just a matter of it hasn't been implemented yet?
Works like a charm! 🙏
Copy code
class PathShape(
    private val pathData: String,
    private val pathDataWidth: Float,
    private val pathDataHeight: Float,
) : Shape {
    override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline {
        return Outline.Generic(
            PathParser()
                .parsePathString(pathData)
                .toPath()
                .asAndroidPath()
                .apply {
                    transform(Matrix().apply {
                        setScale(size.width / pathDataWidth, size.height / pathDataHeight)
                    })
                }
                .asComposePath())
    }
}
🎉 1
c

Chris Sinco [G]

02/02/2022, 8:28 PM
Do you know if there’s any reason these are not exposed by the compose 
Path
 or is it just a matter of it hasn’t been implemented yet?
Not sure - maybe @Nader Jawad can comment
n

Nader Jawad

02/02/2022, 8:35 PM
Just hasn't been implemented yet. I believe there's a tracking bug for this as well
👍 2
a

Andrew Hughes

02/02/2022, 9:50 PM
Awesome, thanks!
2 Views