https://kotlinlang.org logo
l

Lucien Guimaraes

02/25/2021, 5:52 PM
I'm asking for help on this one again. I'm unable to create a smooth alpha transition like this video, using NestedScrollConnection or simply with animateColorAsState. For both the value isn't updated as often as the scroll is happening in the view
1
t

Thiago

02/25/2021, 7:05 PM
Check the compose samples. There have samples using TopBar, Header image and swipe actions
l

Lucien Guimaraes

02/25/2021, 7:10 PM
I already did, but sadly they don't have such a fade animation on topAppBar
d

Doris Liu

02/25/2021, 7:17 PM
Can you post a code snippet on how you are updating the alpha now?
l

Lucien Guimaraes

02/25/2021, 7:22 PM
only the part with the animation update ? Or would you like the complete integration ?
Here is a shorten snippet using only rememberScrollState and animateColorAsState
Copy code
BoxWithConstraints {

            val scrollState = rememberScrollState()
            val maxHeight = remember {
                mutableStateOf(this@BoxWithConstraints.maxHeight.value / 3)  //We consider the image header is a third of the screen
            }
            val tobAppBarBackgroundColor = animateColorAsState(
                when (scrollState.value) {
                    0f -> {
                        Color.White.copy(alpha = 0f)
                    }
                    in 0f..maxHeight.value -> {
                        val alpha = scrollState.value/maxHeight.value
                        Color.White.copy(alpha = alpha)
                    }
                    else -> {
                        Color.White.copy(alpha = 1f)
                    }
                }
            )

            Column(modifier = Modifier
                .fillMaxSize()
                .verticalScroll(scrollState)) {
                //content
            }
            TopAppBar(
                backgroundColor = tobAppBarBackgroundColor.value,
            )
        }
I added some logs, and When I'm scrolling the scrollState.value is called only a few times
d

Doris Liu

02/25/2021, 7:30 PM
I was just looking at the code snippet that you posted in an earlier thread. 🙂 I'm curious what are the values throughout scrolling that you see from
Copy code
when (scrollState.value) {
                    0f -> {
                        Color.White.copy(alpha = 0f)
                    }
                    in 0f..maxHeight.value -> {
                        val alpha = scrollState.value/maxHeight.value
                        Color.White.copy(alpha = alpha)
                    }
                    else -> {
                        Color.White.copy(alpha = 1f)
                    }
                }
Can you add a log after this block and do a quick scroll?
l

Lucien Guimaraes

02/25/2021, 7:35 PM
Here is a look on logcat when I'm scrolling until the end of the screen.
Copy code
D/debug: scrollState is 0.0
D/debug: scrollState is 0.0
D/debug: scrollState is 16.316895
D/debug: scrollState is 76.4624
D/debug: scrollState is 480.82385
D/debug: scrollState is 674.97656
D/debug: scrollState is 674.97656
D/debug: scrollState is 674.97656
D/debug: scrollState is 702.93726
D/debug: scrollState is 758.1581
D/debug: scrollState is 866.1438
D/debug: scrollState is 978.0
that's why I'm unable to have a correct computation on the color, the scrollState values should be updated more often. But I don't know if t's intended by the API or if I'm doing something wrong
c

Colton Idle

02/25/2021, 7:37 PM
Looking forward to seeing if you can resolve this. We have a very similar requirement because iOS team can do it easily apparently.
☝🏻 1
d

Doris Liu

02/25/2021, 7:42 PM
Seems like there's a jump in the scrolling value from 76 to 480. I was trying to figure out whether the animation is too fast, or the scrolling value change happens irregularly. Out of curiosity, what if you remove
animateColorAsState
and just use the interpolated alpha directly. what do you see there? Could you also include the timestamp for the scrollState.value logging so we can see whether there's any frame skipping?
l

Lucien Guimaraes

02/25/2021, 7:46 PM
Out of curiosity, what if you remove 
animateColorAsState
 and just use the interpolated alpha directly. what do you see there?
Wow just tried this and it's already much better
I have way more scrolling value change
d

Doris Liu

02/25/2021, 7:48 PM
🙂 Can you share the logging with the more frequent scrolling value change? Does it happen every frame?
l

Lucien Guimaraes

02/25/2021, 7:50 PM
Does it happen every frame ?
Yes, it seems so. Here is the logging without
animateColorAsState
Copy code
2021-02-25 19:49:19.449 D/debug: scrollState is 0.0
2021-02-25 19:49:19.715 D/debug: scrollState is 0.0
2021-02-25 19:49:22.178 D/debug: scrollState is 8.751709
2021-02-25 19:49:22.225 D/debug: scrollState is 36.629395
2021-02-25 19:49:22.278 D/debug: scrollState is 66.92615
2021-02-25 19:49:22.291 D/debug: scrollState is 68.18396
2021-02-25 19:49:22.302 D/debug: scrollState is 76.79932
2021-02-25 19:49:22.343 D/debug: scrollState is 93.759766
2021-02-25 19:49:22.402 D/debug: scrollState is 126.45117
2021-02-25 19:49:22.422 D/debug: scrollState is 134.1903
2021-02-25 19:49:22.439 D/debug: scrollState is 140.88184
2021-02-25 19:49:22.492 D/debug: scrollState is 157.72766
2021-02-25 19:49:22.538 D/debug: scrollState is 168.83215
2021-02-25 19:49:22.553 D/debug: scrollState is 177.0935
2021-02-25 19:49:22.613 D/debug: scrollState is 209.2351
2021-02-25 19:49:22.632 D/debug: scrollState is 215.45056
2021-02-25 19:49:22.651 D/debug: scrollState is 227.0415
2021-02-25 19:49:22.750 D/debug: scrollState is 271.698
2021-02-25 19:49:22.768 D/debug: scrollState is 274.1543
2021-02-25 19:49:22.814 D/debug: scrollState is 297.47107
2021-02-25 19:49:22.865 D/debug: scrollState is 319.48877
2021-02-25 19:49:22.924 D/debug: scrollState is 339.22827
2021-02-25 19:49:22.939 D/debug: scrollState is 348.00037
2021-02-25 19:49:23.001 D/debug: scrollState is 372.81177
2021-02-25 19:49:23.047 D/debug: scrollState is 384.83008
2021-02-25 19:49:23.092 D/debug: scrollState is 400.29297
2021-02-25 19:49:23.171 D/debug: scrollState is 419.08154
2021-02-25 19:49:23.224 D/debug: scrollState is 429.19495
2021-02-25 19:49:23.284 D/debug: scrollState is 447.0946
2021-02-25 19:49:23.336 D/debug: scrollState is 465.09326
2021-02-25 19:49:23.379 D/debug: scrollState is 476.05554
2021-02-25 19:49:23.391 D/debug: scrollState is 481.49835
2021-02-25 19:49:23.455 D/debug: scrollState is 489.9375
2021-02-25 19:49:23.511 D/debug: scrollState is 494.52356
2021-02-25 19:49:23.557 D/debug: scrollState is 508.80078
2021-02-25 19:49:23.567 D/debug: scrollState is 513.7958
2021-02-25 19:49:23.612 D/debug: scrollState is 524.1341
2021-02-25 19:49:23.637 D/debug: scrollState is 534.9395
2021-02-25 19:49:23.697 D/debug: scrollState is 553.46844
2021-02-25 19:49:23.754 D/debug: scrollState is 579.5058
2021-02-25 19:49:23.782 D/debug: scrollState is 590.0153
2021-02-25 19:49:23.795 D/debug: scrollState is 596.7677
2021-02-25 19:49:23.818 D/debug: scrollState is 605.4359
2021-02-25 19:49:23.836 D/debug: scrollState is 613.4224
2021-02-25 19:49:23.891 D/debug: scrollState is 630.97266
2021-02-25 19:49:23.955 D/debug: scrollState is 659.7693
2021-02-25 19:49:23.974 D/debug: scrollState is 666.88965
2021-02-25 19:49:23.988 D/debug: scrollState is 672.5115
2021-02-25 19:49:24.003 D/debug: scrollState is 679.4973
2021-02-25 19:49:24.073 D/debug: scrollState is 705.7602
2021-02-25 19:49:24.128 D/debug: scrollState is 726.7422
2021-02-25 19:49:24.214 D/debug: scrollState is 755.17145
2021-02-25 19:49:24.278 D/debug: scrollState is 775.1311
2021-02-25 19:49:24.347 D/debug: scrollState is 802.93066
2021-02-25 19:49:24.400 D/debug: scrollState is 818.94055
2021-02-25 19:49:24.460 D/debug: scrollState is 829.8714
2021-02-25 19:49:24.521 D/debug: scrollState is 845.0254
2021-02-25 19:49:24.541 D/debug: scrollState is 852.99146
2021-02-25 19:49:24.594 D/debug: scrollState is 870.84784
2021-02-25 19:49:24.641 D/debug: scrollState is 892.7049
2021-02-25 19:49:24.686 D/debug: scrollState is 917.4961
2021-02-25 19:49:24.774 D/debug: scrollState is 959.5512
2021-02-25 19:49:24.831 D/debug: scrollState is 978.0
d

Doris Liu

02/25/2021, 7:54 PM
There's still some irregularities even in the last logging, sometimes subsequent value changes are 50-100ms apart, which indicates a low frame rate.
👀 1
This is alpha 12 or beta01?
l

Lucien Guimaraes

02/25/2021, 7:55 PM
This is alpha12
I haven't migrated to beta01 yet, is there any performance improvement in the beta01 release ?
d

Doris Liu

02/25/2021, 7:59 PM
It'd be helpful if you could try this on beta01 and file a bug if you still see the same issue. We have made some changes in pointer input and Scrollable since alpha12. @matvei would be able to tell you more about that. BTW, is this on an emulator or an actual device?
l

Lucien Guimaraes

02/25/2021, 8:03 PM
Ok, I'm going to do the migration and check if there is any performance improvement 👍 And it's from an emulator. But the issue was also on my Pixel 3 (when using
animateColorAsState
, I haven't try without it on my device yet)
d

Doris Liu

02/25/2021, 8:11 PM
I don't expect
animateColorAsState
to have an impact on frame rate since it's fairly lightweight, if it does on beta01 please file a bug. I'd be curious to see what's going on. 🙂
t

Thiago

02/25/2021, 8:15 PM
Doris, just a doubt, migrating this Lucien sample to use animation with
withNanoFrames
you think that should solve frame drops? As the
TargetBasedAnimation
sample on official docs: https://developer.android.com/jetpack/compose/animation#targetbasedanimation
l

Lucien Guimaraes

02/25/2021, 8:19 PM
migration done on beta01, I still have some value changes that are 50-100ms apart while using the interpolated alpha directly. And using
animateColorAsState
it's the same issue as it was in alpha12. Both were tested on emulator
d

Doris Liu

02/25/2021, 8:26 PM
Doris, just a doubt, migrating this Lucien sample to use animation with 
withNanoFrames
 you think that should solve frame drops?
I doubt that would fix the frame drop. If you scroll up to the diagram here: https://developer.android.com/jetpack/compose/animation#low-level-apis , you'll see that
animate*AsState
uses
TargetBasedAnimation
under the hood. 🙂
😯 1
migration done on beta01, I still have some value changes that are 50-100ms apart while using the interpolated alpha directly. And using 
animateColorAsState
 it's the same issue as it was in alpha12. Both were tested on emulator
Please file a bug and include the snippet. We'll investigate. Thank you
l

Lucien Guimaraes

02/25/2021, 8:34 PM
I will, thanks for helping!
👍 1
t

Timo Drick

02/26/2021, 12:40 AM
As a workaround until the bug is fixed for me animateColorAsState(value, animationSpec = tween(1000)) makes it smooth. Of course it is not exactly what you want.
d

Doris Liu

02/26/2021, 12:47 AM
if you had to work around the imperfect input target values to the animation, I'd recommend a low stiffness
spring
over a
tween
, at least
spring
keeps the velocity continuous. 🙂
👍 4
t

Timo Drick

03/02/2021, 12:53 AM
Using lazyColumnListState.firstVisibleItemScrollOffset works for me fine (smooth animation) not sure but maybe LazyColumn works better? https://github.com/timo-drick/compose_dev_challenge_1/blob/main/app/src/main/java/com/example/androiddevchallenge/PuppyDetail.kt
😍 1
👍 1
d

Doris Liu

03/02/2021, 1:09 AM
Very nice animation! 👆🥰 Thanks for the follow-up, @Timo Drick . Do you also use the
firstVisibleItemScrollOffset
in combination w/
animate*AsState
?
Nvm. The code linked above answered that question for me. 🙂
t

Timo Drick

03/02/2021, 8:17 AM
So it is not an animation. I just use the scrollOffset to calculate the bottom padding of the image.
l

Lucien Guimaraes

03/02/2021, 1:27 PM
I didn't follow up last days, but I was able to have something smooth (using the interpolated alpha directly)
👍 1
c

Colton Idle

03/02/2021, 2:11 PM
Nice! Please share a gist if possible. Very curious about the end result.
l

Lucien Guimaraes

03/02/2021, 2:48 PM
Here is the computation for the color update:
Copy code
@Composable
fun topAppBarBackgroundColor(
    scrollState: ScrollState,
    pictureHeight: Dp,
): Color {
    val topBarAnimationLimit = (scrollState.maxValue / HikeDetailScreen.HEADER_PART_SCREEN) + pictureHeight.value
    return when (scrollState.value) {
        0 -> {
            MaterialTheme.colors.surface.copy(alpha = 0f)
        }
        in 0..topBarAnimationLimit.roundToInt() -> {
            var alpha = scrollState.value / topBarAnimationLimit
            if (alpha > 1) //to be sure we never have an alpha float exceeding 1f
                alpha = 1f
            MaterialTheme.colors.surface.copy(alpha = alpha)
        }
        else -> {
            MaterialTheme.colors.surface.copy(alpha = 1f)
        }
    }
}
the
HEADER_PART_SCREEN
is just an Int ratio of the screen. In this case I want my picture to take a third of the bloc visible, so it's 3. But it could be whatever you want. And for the statusBar I just created a Box that take a modifier with the statusBarHeight (using Inset library) and with a background being the same as the topBar
t

Thiago

03/02/2021, 6:27 PM
First: congrats for the animation. Very cool. I have one question: why your
topAppBarBackgroundColor
is annotaded with @Composable if that contains logic only? I think your code can be improved to:
Copy code
@Composable
fun topAppBarBackgroundColor(
    scrollState: ScrollState,
    pictureHeight: Dp,
): Color {
    val topBarAnimationLimit = (scrollState.maxValue / HikeDetailScreen.HEADER_PART_SCREEN) + pictureHeight.value
    val alpha = (scrollState.value / topBarAnimationLimit).coercIn(0f, 1f)
    return MaterialTheme.colors.surface.copy(alpha = alpha)
}
l

Lucien Guimaraes

03/03/2021, 9:12 AM
I have one question: why your 
topAppBarBackgroundColor
 is annotaded with @Composable if that contains logic only
MaterialTheme.colors.surface
has to be called from a Composable so that's why! And yeah it can totally be improved to be cleaner 👍 from your example you can even just return the alpha, and you apply the
MaterialTheme.colors.surface
where the function is called. Which remove the Compose annotation from
topAppBarBackgroundColor
9 Views