Compose 动画艺术探索之可见性动画

Posted bug樱樱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Compose 动画艺术探索之可见性动画相关的知识,希望对你有一定的参考价值。

本篇文章是此专栏的第二篇文章,上一篇文章简单写了下 Compose 的动画,让大家先看了下 Compose 开箱即用的动画效果,效果还是挺好的,感兴趣的可以去看下:Compose 动画艺术探索之瞅下 Compose 的动画

从可见性动画看起

可见性动画在上一篇文章中介绍过,不过只是简单使用,没看过上一篇文章的也不用担心,给大家看下可见性动画的实际效果。

实现代码也很简单,来回顾下:

val visible = remember  mutableStateOf(true) 
AnimatedVisibility(visible = visible.value,) 
    Text(text = "天青色等烟雨,而我在等你,炊烟袅袅升起,隔江千万里")

上一篇文章主要介绍了 Compose 动画的便携之处,例如上面代码,确实非常简单就能实现之前原生安卓中比较难实现的动画效果,今天咱们就来稍微深入一点看看,从小节标题也能知道,就从可见性动画来看!

怎么看呢?直接点进去源码来看!先来看看 AnimatedVisibility 的函数定义吧!

@Composable
fun AnimatedVisibility(
    visible: Boolean,
    modifier: Modifier = Modifier,
    enter: EnterTransition = fadeIn() + expandIn(),
    exit: ExitTransition = shrinkOut() + fadeOut(),
    label: String = "AnimatedVisibility",
    content: @Composable() AnimatedVisibilityScope.() -> Unit
) 
    val transition = updateTransition(visible, label)
    AnimatedEnterExitImpl(transition,  it , modifier, enter, exit, content)

其实可组合项 AnimatedVisibility 不是只有这一个,目前 Compose 1.3.0-beta02 版本中有六个,这个咱们待会再说,先看这一个,也是第一个,可以看到函数中一共可以接收六个参数,下面先来看看这六个参数分别有什么作用吧:

  • visible:定义内容是否应该可见,true 为可见,false 为不可见
  • modifier:修饰符,这个就不多说了,在 ComposeModifier 简直随处可见
  • enter:内容出现时的动画,可以看到这个参数有默认值,默认值为 fadeIn() + expandIn() ,大致意思为淡入并扩展开
  • exit:内容由可见变为不可见时的动画,同样的,这个参数也有默认值,默认值为 shrinkOut() + fadeOut() ,大致意思为缩小并淡出消失
  • label:字面意思理解为标签,默认值为 AnimatedVisibility ,可以自定义做标记,用于区分不同动画
  • content:需要添加可见性动画的可组合项。

上面这些参数除了 enterexit 外都比较好理解,这里就不做过多解释,重点来看下 enterexit ,可以看到 enter 的类型为 EnterTransitionexit 的类型为 ExitTransition ,那么接下来咱们来分别看看 EnterTransitionExitTransition 吧!

这里其实有一个小问题,可以看到上面 Gif 图中并不是淡入并扩展和缩小并消失,这是为什么呢?继续往下看就能找到答案!

进入过渡——EnterTransition

顾名思义,这个类主要是为了做进入过渡的,来简单看下它的源码吧:

@Immutable
sealed class EnterTransition 
    internal abstract val data: TransitionData
​
    // 组合不同的进入转换。组合的顺序并不重要,因为这些将同时启动
    @Stable
    operator fun plus(enter: EnterTransition): EnterTransition 
        return EnterTransitionImpl(
            TransitionData(
                fade = data.fade ?: enter.data.fade,
                slide = data.slide ?: enter.data.slide,
                changeSize = data.changeSize ?: enter.data.changeSize,
                scale = data.scale ?: enter.data.scale
            )
        )
    

    companion object 
        // 当不需要输入转换时,可以使用此函数。
        val None: EnterTransition = EnterTransitionImpl(TransitionData())
    

可以看到 EnterTransition 是一个密封类, 类中有一个抽象的不可变值 data ,类型为 TransitionData ,这个放到下面来说;类中还有一个函数,而且该函数有 operator 前缀, 这表示运算符重载,重载了“+”号,所以就可以使用“+”来组合不同的输入动画了,函数接收的参数也是 EnterTransition ,然后直接返回 EnterTransitionImpl ,又没见过,怎么办?继续看看 EnterTransitionImpl 是个啥!

@Immutable
private class EnterTransitionImpl(override val data: TransitionData) : EnterTransition()

可以看到 EnterTransitionImpl 类很简单,是一个私有类,继承自 EnterTransition ,注意类上有 Immutable 注解, Immutable 注解可用于将类标记为生成不可变实例,但类的不变性没有得到验证,它是类型的一种承诺,即在构造实例之后,所有公开可访问的属性和字段都不会更改。 EnterTransitionImpl 还需要实现父类的抽象值,所有有 TransitionData 类型的参数 data ,上面咱们简单提到了 TransitionData ,这里来看下吧!

@Immutable
internal data class TransitionData(
    val fade: Fade? = null,
    val slide: Slide? = null,
    val changeSize: ChangeSize? = null,
    val scale: Scale? = null
)

可以看到 TransitionData 类也有 Immutable 注解,这里就不做过多介绍,这是一个包内可见的数据类,里面有四个不可变值,分别是 FadeSlideChangeSizeScale ,其实从名称就能看出这几个参数分别代表的意思,不过还是来看下它们的源码吧!

@Immutable
internal data class Fade(val alpha: Float, val animationSpec: FiniteAnimationSpec<Float>)
​
@Immutable
internal data class Slide(
    val slideOffset: (fullSize: IntSize) -> IntOffset,
    val animationSpec: FiniteAnimationSpec<IntOffset>
)
​
@Immutable
internal data class ChangeSize(
    val alignment: Alignment,
    val size: (fullSize: IntSize) -> IntSize =  IntSize(0, 0) ,
    val animationSpec: FiniteAnimationSpec<IntSize>,
    val clip: Boolean = true
)
​
@Immutable
internal data class Scale(
    val scale: Float,
    val transformOrigin: TransformOrigin,
    val animationSpec: FiniteAnimationSpec<Float>
)

可以看到这四个类都是不可变的数据类,分别表示颜色转变、滑动、大小变化及缩放。这几个类有一个共同点,都有一个共同的参数 animationSpec ,参数类型为 FiniteAnimationSpec ,来看看 FiniteAnimationSpec 是个啥?

interface FiniteAnimationSpec<T> : AnimationSpec<T> 
    override fun <V : AnimationVector> vectorize(
        converter: TwoWayConverter<T, V>
    ): VectorizedFiniteAnimationSpec<V>

可以看到 FiniteAnimationSpec 是一个接口,继承自 AnimationSpec ,简单理解就是有限动画规格,定义了动画的时长及动画效果等,类似于原生安卓中的什么呢?嗯。。。差值器吧!FiniteAnimationSpec 是所有非无限动画实现的接口,包括: TweenSpecSpringSpecKeyframesSpecRepeatableSpecSnapSpec 等等,上一篇文章中说到的无限循环动画 InfiniteRepeatableSpec 没有继承这个接口,其实从名字看就知道了,InfiniteRepeatableSpec 也继承自 AnimationSpec 。。。。

不行不行,扯太远了,其实看源码就是这样,看着看着一直往下看就会迷失了最初的目标,越看越多,越看越多,这里就先不接着往下看了,再看就没完没了了,这里咱们知道这四个类大概存储了什么数据就行了。动画规格咱们放到之后的文章中慢慢看,今天主要来过一遍可见性动画!

简单总结下, EnterTransition 类中有一个函数,进行了运算符重载,可以有多个动画同时进行。

关闭过渡——ExitTransition

其实看完刚才的 EnterTransition 类再来看 ExitTransition 就会觉得很简单了,不信的话咱们来看下 ExitTransition 的源码:

@Immutable
sealed class ExitTransition 
    internal abstract val data: TransitionData
​
    // 结合不同的退出转换,组合顺序并不重要
    @Stable
    operator fun plus(exit: ExitTransition): ExitTransition 
        return ExitTransitionImpl(
            TransitionData(
                fade = data.fade ?: exit.data.fade,
                slide = data.slide ?: exit.data.slide,
                changeSize = data.changeSize ?: exit.data.changeSize,
                scale = data.scale ?: exit.data.scale
            )
        )
    
​
    companion object 
        // 当不需要内置动画时使用
        val None: ExitTransition = ExitTransitionImpl(TransitionData())
    

是不是基本一致,连抽象不可变值都一摸一样,甚至值的名称都没变!唯一不同的是 EnterTransition 的实现类为 EnterTransitionImpl ,而 ExitTransition 的子类为 ExitTransitionImpl ,那就再看看 ExitTransitionImpl !

@Immutable
private class ExitTransitionImpl(override val data: TransitionData) : ExitTransition()

EnterTransitionImpl 不能说相似,只能说一摸一样。。。所以就不做过多介绍。。。

过渡——Transition

文章开头的时候看 AnimatedVisibility 函数中只有下面的两行代码,

val transition = updateTransition(visible, label)
AnimatedEnterExitImpl(transition,  it , modifier, enter, exit, content)

这一小节咱们先来看第一行,可以看到调用了 updateTransition 函数, 传入了当前显示状态和标签,来具体看下这个函数吧!

@Composable
fun <T> updateTransition(
    targetState: T,
    label: String? = null
): Transition<T> 
    val transition = remember  Transition(targetState, label = label) 
    transition.animateTo(targetState)
    DisposableEffect(transition) 
        onDispose 
            // 出去的时候清理干净,确保观察者不会被困在中间状态
            transition.onTransitionEnd()
        
    
    return transition

可以看到这个函数也是一个可组合项,返回值为 Transition ,函数中先记住并构建了一个 Transition ,然后调用了 TransitionanimateTo 函数来执行过渡动画,然后调用了需要清理的效应 DisposableEffect ,之后在 onDispose 中调用了 onTransitionEnd 函数,以防卡在这里,最后返回刚才构建的 Transition

函数中的内容不难理解,现在唯一困惑的是 Transition 是个什么东西!那就来看看!

@Stable
class Transition<S> @PublishedApi internal constructor(
    private val transitionState: MutableTransitionState<S>,
    val label: String? = null
)

这就是 Transition 的类声明,Transition 在状态级别上管理所有子动画。子动画可以使用Transition 以声明的方式创建。animateFloatanimateValueanimateColor animateColor等。当 targetState 改变时,Transition 将自动启动或调整其所有子动画的路线,使其动画到为每个动画定义的新目标值。

可以看到 Transition 构造函数中接收一个 MutableTransitionState 类型的参数和一个 lable ,但咱们看到上面传入的并不是 MutableTransitionState 类型的参数,肯定还有别的构造函数!

internal constructor(
    initialState: S,
    label: String?
) : this(MutableTransitionState(initialState), label)

没错,确实还有一个构造函数,接下来看下 MutableTransitionState 吧!

class MutableTransitionState<S>(initialState: S) 
    // 当前的状态
    var currentState: S by mutableStateOf(initialState)
        internal set
​
    // 过渡的目标状态
    var targetState: S by mutableStateOf(initialState)
​
    // 是否空闲
    val isIdle: Boolean
        get() = (currentState == targetState) && !isRunning
​
    // 是否运行
    internal var isRunning: Boolean by mutableStateOf(false)

可以看到 MutableTransitionState 中构建了几个需要的状态,具体表示什么在上面代码中添加了注释。

过渡动画实现——AnimatedEnterExitImpl

刚才看了 AnimatedVisibility 函数中的第一行代码,下面咱们来看下第二行代码:

AnimatedEnterExitImpl(transition,  it , modifier, enter, exit, content)

这块调用了一个实现函数,将刚构建好的 Transition 和之前的 EnterTransitionExitTransition 统统传了进去,下面就来看下 AnimatedEnterExitImpl !

@Composable
private fun <T> AnimatedEnterExitImpl(
    transition: Transition<T>,visible: (T) -> Boolean,
    modifier: Modifier,enter: EnterTransition,
    exit: ExitTransition,content: @Composable() AnimatedVisibilityScope.() -> Unit
) 
    val isAnimationVisible = remember(transition) 
        mutableStateOf(visible(transition.currentState))
    
​
    if (visible(transition.targetState) || isAnimationVisible.value || transition.isSeeking) 
        val childTransition = transition.createChildTransition(label = "EnterExitTransition") 
            transition.targetEnterExit(visible, it)
        
​
        LaunchedEffect(childTransition) 
            snapshotFlow 
                childTransition.currentState == EnterExitState.Visible ||
                    childTransition.targetState == EnterExitState.Visible
            .collect 
                isAnimationVisible.value = it
            
        
​
        AnimatedEnterExitImpl(childTransition,modifier,
            enter = enter,exit = exit,content = content
        )
    

这块代码有点多啊,但不要害怕,可以看到基本上这里使用到的类在刚才咱们都看过了,接下来需要的就是一行一行往下看,看看哪个没见过咱们再看!

函数中先记录了当前的状态,然后判断当前的状态值是否为 true,如果为 true 的话则创建子过渡,老规矩,来看看 createChildTransition

@Composable
inline fun <S, T> Transition<S>.createChildTransition(
    label: String = "ChildTransition",
    transformToChildState: @Composable (parentState: S) -> T,
): Transition<T> 
    val initialParentState = remember(this)  this.currentState 
    val initialState = transformToChildState(if (isSeeking) currentState else initialParentState)
    val targetState = transformToChildState(this.targetState)
    return createChildTransitionInternal(initialState, targetState, label)

可以看到 createChildTransitionTransition 的一个扩展函数,还是一个高阶函数,函数内获取了高阶函数中的返回值,然后直接返回调用了 createChildTransitionInternal ,并传入获取的值,这个值目前还不知道是什么,只知道是 targetEnterExit 返回的数据,这个一会再看,先接着看 createChildTransitionInternal 函数:

@Composable
internal fun <S, T> Transition<S>.createChildTransitionInternal(
    initialState: T,
    targetState: T,
    childLabel: String,
): Transition<T> 
    val transition = remember(this) 
        Transition(MutableTransitionState(initialState), "$this.label > $childLabel")
    
    DisposableEffect(transition) 
        addTransition(transition)
        onDispose 
            removeTransition(transition)
        
    
    if (isSeeking) 
        transition.setPlaytimeAfterInitialAndTargetStateEstablished(
            initialState,
            targetState,
            this.lastSeekedTimeNanos
        )
     else 
        transition.updateTarget(targetState)
        transition.isSeeking = false
    
    return transition

可以看到 createChildTransitionInternal 也是 Transition 的一个扩展函数,然后函数中也使用了需要清理的效应,之后判断 isSeeking 的值是,isSeeking 的值在 setPlaytimeAfterInitialAndTargetStateEstablished 函数中会被置为 true,如果 isSeeking 值为 true 则调用 setPlaytimeAfterInitialAndTargetStateEstablished 函数,用来设置初始状态和目标状态建立后的时间,如果为 false,则更新状态值。

到这里 createChildTransition 函数咱们大概看了下,但刚才还有一个扣,刚才说了不知道目标值是什么,因为那是 targetEnterExit 的返回值,现在咱们来看看!

@Composable
private fun <T> Transition<T>.targetEnterExit(
    visible: (T) -> Boolean,
    targetState: T
): EnterExitState = key(this) 
​
    if (this.isSeeking) 
        if (visible(targetState)) 
            Visible
         else 
            if (visible(this.currentState)) 
                PostExit
             else 
                PreEnter
            
        
     else 
        val hasBeenVisible = remember  mutableStateOf(false) 
        if (visible(currentState)) 
            hasBeenVisible.value = true
        
        if (visible(targetState)) 
            EnterExitState.Visible
         else 
            // If never been visible, visible = false means PreEnter, otherwise PostExit
            if (hasBeenVisible.value) 
                EnterExitState.PostExit
             else 
                EnterExitState.PreEnter
            
        
    

同样的,targetEnterExit 也是一个扩展函数,返回值为 EnterExitState 。这里也使用了 isSeeking 来进行判断,然后根据当前的值来设置不同的 EnterExitStateEnterExitState 又是个啥呢???接着来看!

enum class EnterExitState 
    // 自定义进入动画的初始状态。
    PreEnter,
​
    // 自定义进入动画的目标状态,也是动画过程中自定义退出动画的初始状态。
    Visible,
​
    // 自定义退出动画的目标状态。
    PostExit

奥,EnterExitState 只是一个枚举类,定义了三种状态:初始状态、进入动画的状态和退出动画的状态。

下面接着来看 AnimatedEnterExitImpl

LaunchedEffect(childTransition) 
            snapshotFlow 
                childTransition.currentState == EnterExitState.Visible ||
                    childTransition.targetState == EnterExitState.Visible
            .collect 
                isAnimationVisible.value = it
            
        
​
AnimatedEnterExitImpl(childTransition,modifier,
            enter = enter,exit = exit,content = content
        )

这里使用了 LaunchedEffect 效应,并使用 snapshotFlowState 转为了 Flow ,然后将值设置到 isAnimationVisible

后面又调用了相同名字的一个函数 AnimatedEnterExitImpl

@Composable
private inline fun AnimatedEnterExitImpl(
    transition: Transition<EnterExitState>,
    modifier: Modifier,
    enter: EnterTransition,
    exit: ExitTransition,
    content: @Composable AnimatedVisibilityScope.() -> Unit
) 
    if (transition.currentState == EnterExitState.Visible ||
        transition.targetState == EnterExitState.Visible
    ) 
        val scope = remember(transition)  AnimatedVisibilityScopeImpl(transition) 
        Layout(
            content =  scope.content() ,
            modifier = modifier.then(transition.createModifier(enter, exit, "Built-in")),
            measurePolicy = remember  AnimatedEnterExitMeasurePolicy(scope) 
        )
    

函数名字一样,但参数不同,这里将刚才构建好的 Transition<EnterExitState> 传了进来,然后函数内先对状态进行了过滤,然后构建了 Layout

Layout 中设置了 modifiermodifier 调用了 then 函数,then 函数用于将此修饰符与另一个修饰符连接,然后连接了 transition.createModifier(enter, exit, "Built-in") ,大家发现点什么没有,这里使用到了上面咱们构建好了所有东西。。。那么。。。。哈哈哈哈!

柳暗花明

下面咱们就来看看 transition.createModifier 这个函数,先来看下函数体:

@Composable
internal fun Transition<EnterExitState>.createModifier(
    enter: EnterTransition,
    exit: ExitTransition,
    label: String
): Modifier

嗯,没错,是 Transition<EnterExitState> 的一个扩展函数,也是一个可组合项!接着往下看几行代码:

var modifier: Modifier = Modifier
​
modifier = modifier.slideInOut(
    this,
    rememberUpdatedState(enter.data.slide),
    rememberUpdatedState(exit.data.slide),
    label
).shrinkExpand(
    this,
    rememberUpdatedState(enter.data.changeSize),
    rememberUpdatedState(exit.data.changeSize),
    label
)

构建了一个 Modifier ,然后调用了 slideInOutshrinkExpand ,没错,这是滑动和缩小放大!接着来!

var shouldAnimateAlpha by remember(this)  mutableStateOf(false) 
var shouldAnimateScale by remember(this)  mutableStateOf(false) 
if (currentState == targetState && !isSeeking) 
    shouldAnimateAlpha = false
    shouldAnimateScale = false
 else 
    if (enter.data.fade != null || exit.data.fade != null) 
        shouldAnimateAlpha = true
    
    if (enter.data.scale != null || exit.data.scale != null) 
        shouldAnimateScale = true
    

创建两个值来记录是否需要透明度的转换和缩放!下面来看下执行的代码:

if (shouldAnimateScale) 
    ......
    modifier = modifier.graphicsLayer 
        this.alpha = alpha
        this.scaleX = scale
        this.scaleY = scale
        this.transformOrigin = transformOrigin
    
 else if (shouldAnimateAlpha) 
    modifier = modifier.graphicsLayer 
        this.alpha = alpha
    

嗯呢,是不是豁然开朗!但是 slideInOutshrinkExpand 函数也是可见性动画这里定义的 Modifier 的扩展函数,里面还有一些自定义的东西,但这不是本文的重点了。

别的可见性动画

文章开头说了,可见性动画目前一共有六个,听着很吓人,其实大同小异,来简单看下不同吧!

@Composable
fun RowScope.AnimatedVisibility(
    visible: Boolean,
    modifier: Modifier = Modifier,
    enter: EnterTransition = fadeIn() + expandHorizontally(),
    exit: ExitTransition = fadeOut() + shrinkHorizontally(),
    label: String = "AnimatedVisibility",
    content: @Composable() AnimatedVisibilityScope.() -> Unit
) 
    val transition = updateTransition(visible, label)
    AnimatedEnterExitImpl(transition,  it , modifier, enter, exit, content)

​
@Composable
fun ColumnScope.AnimatedVisibility(
    visible: Boolean,
    modifier: Modifier = Modifier,
    enter: EnterTransition = fadeIn() + expandVertically(),
    exit: ExitTransition = fadeOut() + shrinkVertically(),
    label: String = "AnimatedVisibility",
    content: @Composable AnimatedVisibilityScope.() -> Unit
) 
    val transition = updateTransition(visible, label)
    AnimatedEnterExitImpl(transition,  it , modifier, enter, exit, content)

上面这两个和咱们上面说的基本一样,只不过一个是 RowScope 的扩展函数,另一个是 ColumnScope 的扩展函数,并且在默认动画上还有些区别,RowScope 默认是横向的扩展和收缩,ColumnScope 是纵向的扩展和收缩,更加方便咱们日常调用!这也就解释了文章开头提出的小问题!

接着再来看别的!

@Composable
fun AnimatedVisibility(
    visibleState: MutableTransitionState<Boolean>,
    modifier: Modifier = Modifier,
    enter: EnterTransition = fadeIn() + expandIn(),
    exit: ExitTransition = fadeOut() + shrinkOut(),
    label: String = "AnimatedVisibility",
    content: @Composable() AnimatedVisibilityScope.() -> Unit
) 
    val transition = updateTransition(visibleState, label)
    AnimatedEnterExitImpl(transition,  it , modifier, enter, exit, content)

这个和之前的就有点区别了,第一个参数就不同了,参数类型为 MutableTransitionState ,其实是一样的,咱们上面也都说到了,MutableTransitionState 的使用方法在上一篇文章中也介绍过,感兴趣的可以去看一下。

剩下的两个还是 RowScopeColumnScope 的扩展函数,也是参数类型改为了 MutableTransitionState ,这里也就不做过多介绍。

突兀的结尾

本篇文章带大家看了可见性动画的一些源码,很多其实都是点到为止,并没有一致不断深入,一直深入就会陷入其中,忘了看源码的本意,本文所有源码基于 Compose 1.3.0-beta02

本文至此结束,有用的地方大家可以参考,当然如果能帮助到大家,哪怕是一点也足够了。就这样。

作者:Zhujiang
链接:https://juejin.cn/post/7146034360670486542

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。

相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

一、架构师筑基必备技能

1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……

二、Android百大框架源码解析

1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程

三、Android性能优化实战解析

  • 腾讯Bugly:对字符串匹配算法的一点理解
  • 爱奇艺:安卓APP崩溃捕获方案——xCrash
  • 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
  • 百度APP技术:Android H5首屏优化实践
  • 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
  • 携程:从智行 Android 项目看组件化架构实践
  • 网易新闻构建优化:如何让你的构建速度“势如闪电”?

四、高级kotlin强化实战

1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》

  • 从一个膜拜大神的 Demo 开始

  • Kotlin 写 Gradle 脚本是一种什么体验?

  • Kotlin 编程的三重境界

  • Kotlin 高阶函数

  • Kotlin 泛型

  • Kotlin 扩展

  • Kotlin 委托

  • 协程“不为人知”的调试技巧

  • 图解协程:suspend

五、Android高级UI开源框架进阶解密

1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南

六、NDK模块开发

1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习

七、Flutter技术进阶

1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)

八、微信小程序开发

1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……

全套视频资料:

一、面试合集

二、源码解析合集


三、开源框架合集


欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证卡片免费领取↓↓↓

以上是关于Compose 动画艺术探索之可见性动画的主要内容,如果未能解决你的问题,请参考以下文章

Android开发艺术探索——第七章:Android动画深入分析

《android开发艺术探索》读书笔记--动画

Android动画(开发艺术探索读书笔记)

Compose 动画 : 高可定制性的动画 Animatable

Android动画问题——可见性变化比动画快

Android LinearLayout 动画的可见性问题