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:修饰符,这个就不多说了,在
Compose
中Modifier
简直随处可见 - enter:内容出现时的动画,可以看到这个参数有默认值,默认值为
fadeIn() + expandIn()
,大致意思为淡入并扩展开 - exit:内容由可见变为不可见时的动画,同样的,这个参数也有默认值,默认值为
shrinkOut() + fadeOut()
,大致意思为缩小并淡出消失 - label:字面意思理解为标签,默认值为
AnimatedVisibility
,可以自定义做标记,用于区分不同动画 - content:需要添加可见性动画的可组合项。
上面这些参数除了 enter
和 exit
外都比较好理解,这里就不做过多解释,重点来看下 enter
和 exit
,可以看到 enter
的类型为 EnterTransition
,exit
的类型为 ExitTransition
,那么接下来咱们来分别看看 EnterTransition
和 ExitTransition
吧!
这里其实有一个小问题,可以看到上面 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
注解,这里就不做过多介绍,这是一个包内可见的数据类,里面有四个不可变值,分别是 Fade
、Slide
、ChangeSize
、Scale
,其实从名称就能看出这几个参数分别代表的意思,不过还是来看下它们的源码吧!
@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
是所有非无限动画实现的接口,包括: TweenSpec
, SpringSpec
, KeyframesSpec
, RepeatableSpec
, SnapSpec
等等,上一篇文章中说到的无限循环动画 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
,然后调用了 Transition
的 animateTo
函数来执行过渡动画,然后调用了需要清理的效应 DisposableEffect
,之后在 onDispose
中调用了 onTransitionEnd
函数,以防卡在这里,最后返回刚才构建的 Transition
。
函数中的内容不难理解,现在唯一困惑的是 Transition
是个什么东西!那就来看看!
@Stable
class Transition<S> @PublishedApi internal constructor(
private val transitionState: MutableTransitionState<S>,
val label: String? = null
)
这就是 Transition
的类声明,Transition
在状态级别上管理所有子动画。子动画可以使用Transition
以声明的方式创建。animateFloat
、animateValue
、animateColor
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
和之前的 EnterTransition
、ExitTransition
统统传了进去,下面就来看下 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)
可以看到 createChildTransition
是 Transition
的一个扩展函数,还是一个高阶函数,函数内获取了高阶函数中的返回值,然后直接返回调用了 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
来进行判断,然后根据当前的值来设置不同的 EnterExitState
。EnterExitState
又是个啥呢???接着来看!
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
效应,并使用 snapshotFlow
将 State
转为了 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
中设置了 modifier
,modifier
调用了 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
,然后调用了 slideInOut
和 shrinkExpand
,没错,这是滑动和缩小放大!接着来!
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
嗯呢,是不是豁然开朗!但是 slideInOut
和 shrinkExpand
函数也是可见性动画这里定义的 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
的使用方法在上一篇文章中也介绍过,感兴趣的可以去看一下。
剩下的两个还是 RowScope
和 ColumnScope
的扩展函数,也是参数类型改为了 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动画深入分析