Compose?動畫藝術(shù)探索之可見性動畫示例詳解
正文
本篇文章是此專欄的第二篇文章,上一篇文章簡單寫了下 Compose
的動畫,讓大家先看了下 Compose
開箱即用的動畫效果,效果還是挺好的,感興趣的可以去看下:Compose 動畫藝術(shù)探索之瞅下 Compose 的動畫
從可見性動畫看起
可見性動畫在上一篇文章中介紹過,不過只是簡單使用,沒看過上一篇文章的也不用擔(dān)心,給大家看下可見性動畫的實際效果。
實現(xiàn)代碼也很簡單,來回顧下:
val visible = remember { mutableStateOf(true) } AnimatedVisibility(visible = visible.value,) { Text(text = "天青色等煙雨,而我在等你,炊煙裊裊升起,隔江千萬里") }
上一篇文章主要介紹了 Compose
動畫的便攜之處,例如上面代碼,確實非常簡單就能實現(xiàn)之前原生安卓中比較難實現(xiàn)的動畫效果,今天咱們就來稍微深入一點看看,從小節(jié)標(biāo)題也能知道,就從可見性動畫來看!
怎么看呢?直接點進去源碼來看!先來看看 AnimatedVisibility
的函數(shù)定義吧!
@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
版本中有六個,這個咱們待會再說,先看這一個,也是第一個,可以看到函數(shù)中一共可以接收六個參數(shù),下面先來看看這六個參數(shù)分別有什么作用吧:
- visible:定義內(nèi)容是否應(yīng)該可見,true 為可見,false 為不可見
- modifier:修飾符,這個就不多說了,在
Compose
中Modifier
簡直隨處可見 - enter:內(nèi)容出現(xiàn)時的動畫,可以看到這個參數(shù)有默認值,默認值為
fadeIn() + expandIn()
,大致意思為淡入并擴展開 - exit:內(nèi)容由可見變?yōu)椴豢梢姇r的動畫,同樣的,這個參數(shù)也有默認值,默認值為
shrinkOut() + fadeOut()
,大致意思為縮小并淡出消失 - label:字面意思理解為標(biāo)簽,默認值為
AnimatedVisibility
,可以自定義做標(biāo)記,用于區(qū)分不同動畫 - content:需要添加可見性動畫的可組合項。
上面這些參數(shù)除了 enter
和 exit
外都比較好理解,這里就不做過多解釋,重點來看下 enter
和 exit
,可以看到 enter
的類型為 EnterTransition
,exit
的類型為 ExitTransition
,那么接下來咱們來分別看看 EnterTransition
和 ExitTransition
吧!
這里其實有一個小問題,可以看到上面 Gif 圖中并不是淡入并擴展和縮小并消失,這是為什么呢?繼續(xù)往下看就能找到答案!
進入過渡——EnterTransition
顧名思義,這個類主要是為了做進入過渡的,來簡單看下它的源碼吧:
@Immutable sealed class EnterTransition { internal abstract val data: TransitionData // 組合不同的進入轉(zhuǎn)換。組合的順序并不重要,因為這些將同時啟動 @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 { // 當(dāng)不需要輸入轉(zhuǎn)換時,可以使用此函數(shù)。 val None: EnterTransition = EnterTransitionImpl(TransitionData()) } }
可以看到 EnterTransition
是一個密封類, 類中有一個抽象的不可變值 data
,類型為 TransitionData
,這個放到下面來說;類中還有一個函數(shù),而且該函數(shù)有 operator
前綴, 這表示運算符重載,重載了“+”號,所以就可以使用“+”來組合不同的輸入動畫了,函數(shù)接收的參數(shù)也是 EnterTransition
,然后直接返回 EnterTransitionImpl
,又沒見過,怎么辦?繼續(xù)看看 EnterTransitionImpl
是個啥!
@Immutable private class EnterTransitionImpl(override val data: TransitionData) : EnterTransition()
可以看到 EnterTransitionImpl
類很簡單,是一個私有類,繼承自 EnterTransition
,注意類上有 Immutable
注解, Immutable
注解可用于將類標(biāo)記為生成不可變實例,但類的不變性沒有得到驗證,它是類型的一種承諾,即在構(gòu)造實例之后,所有公開可訪問的屬性和字段都不會更改。 EnterTransitionImpl
還需要實現(xiàn)父類的抽象值,所有有 TransitionData
類型的參數(shù) 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
注解,這里就不做過多介紹,這是一個包內(nèi)可見的數(shù)據(jù)類,里面有四個不可變值,分別是 Fade
、Slide
、ChangeSize
、Scale
,其實從名稱就能看出這幾個參數(shù)分別代表的意思,不過還是來看下它們的源碼吧!
@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> )
可以看到這四個類都是不可變的數(shù)據(jù)類,分別表示顏色轉(zhuǎn)變、滑動、大小變化及縮放。這幾個類有一個共同點,都有一個共同的參數(shù) animationSpec
,參數(shù)類型為 FiniteAnimationSpec
,來看看 FiniteAnimationSpec
是個啥?
interface FiniteAnimationSpec<T> : AnimationSpec<T> { override fun <V : AnimationVector> vectorize( converter: TwoWayConverter<T, V> ): VectorizedFiniteAnimationSpec<V> }
可以看到 FiniteAnimationSpec
是一個接口,繼承自 AnimationSpec
,簡單理解就是有限動畫規(guī)格,定義了動畫的時長及動畫效果等,類似于原生安卓中的什么呢?嗯。。。差值器吧!FiniteAnimationSpec
是所有非無限動畫實現(xiàn)的接口,包括: TweenSpec
, SpringSpec
, KeyframesSpec
, RepeatableSpec
, SnapSpec
等等,上一篇文章中說到的無限循環(huán)動畫 InfiniteRepeatableSpec
沒有繼承這個接口,其實從名字看就知道了,InfiniteRepeatableSpec
也繼承自 AnimationSpec
。。。。
不行不行,扯太遠了,其實看源碼就是這樣,看著看著一直往下看就會迷失了最初的目標(biāo),越看越多,越看越多,這里就先不接著往下看了,再看就沒完沒了了,這里咱們知道這四個類大概存儲了什么數(shù)據(jù)就行了。動畫規(guī)格咱們放到之后的文章中慢慢看,今天主要來過一遍可見性動畫!
簡單總結(jié)下, EnterTransition
類中有一個函數(shù),進行了運算符重載,可以有多個動畫同時進行。
關(guān)閉過渡——ExitTransition
其實看完剛才的 EnterTransition
類再來看 ExitTransition
就會覺得很簡單了,不信的話咱們來看下 ExitTransition
的源碼:
@Immutable sealed class ExitTransition { internal abstract val data: TransitionData // 結(jié)合不同的退出轉(zhuǎn)換,組合順序并不重要 @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 { // 當(dāng)不需要內(nèi)置動畫時使用 val None: ExitTransition = ExitTransitionImpl(TransitionData()) } }
是不是基本一致,連抽象不可變值都一摸一樣,甚至值的名稱都沒變!唯一不同的是 EnterTransition
的實現(xiàn)類為 EnterTransitionImpl
,而 ExitTransition
的子類為 ExitTransitionImpl
,那就再看看 ExitTransitionImpl
!
@Immutable private class ExitTransitionImpl(override val data: TransitionData) : ExitTransition()
和 EnterTransitionImpl
不能說相似,只能說一摸一樣。。。所以就不做過多介紹。。。
過渡——Transition
文章開頭的時候看 AnimatedVisibility
函數(shù)中只有下面的兩行代碼,
val transition = updateTransition(visible, label) AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content)
這一小節(jié)咱們先來看第一行,可以看到調(diào)用了 updateTransition
函數(shù), 傳入了當(dāng)前顯示狀態(tài)和標(biāo)簽,來具體看下這個函數(shù)吧!
@Composable fun <T> updateTransition( targetState: T, label: String? = null ): Transition<T> { val transition = remember { Transition(targetState, label = label) } transition.animateTo(targetState) DisposableEffect(transition) { onDispose { // 出去的時候清理干凈,確保觀察者不會被困在中間狀態(tài) transition.onTransitionEnd() } } return transition }
可以看到這個函數(shù)也是一個可組合項,返回值為 Transition
,函數(shù)中先記住并構(gòu)建了一個 Transition
,然后調(diào)用了 Transition
的 animateTo
函數(shù)來執(zhí)行過渡動畫,然后調(diào)用了需要清理的效應(yīng) DisposableEffect
,之后在 onDispose
中調(diào)用了 onTransitionEnd
函數(shù),以防卡在這里,最后返回剛才構(gòu)建的 Transition
。
函數(shù)中的內(nèi)容不難理解,現(xiàn)在唯一困惑的是 Transition
是個什么東西!那就來看看!
@Stable class Transition<S> @PublishedApi internal constructor( private val transitionState: MutableTransitionState<S>, val label: String? = null )
這就是 Transition
的類聲明,Transition
在狀態(tài)級別上管理所有子動畫。子動畫可以使用Transition
以聲明的方式創(chuàng)建。animateFloat
、animateValue
、animateColor
animateColor
等。當(dāng) targetState
改變時,Transition
將自動啟動或調(diào)整其所有子動畫的路線,使其動畫到為每個動畫定義的新目標(biāo)值。
可以看到 Transition
構(gòu)造函數(shù)中接收一個 MutableTransitionState
類型的參數(shù)和一個 lable
,但咱們看到上面?zhèn)魅氲牟⒉皇?MutableTransitionState
類型的參數(shù),肯定還有別的構(gòu)造函數(shù)!
internal constructor( initialState: S, label: String? ) : this(MutableTransitionState(initialState), label)
沒錯,確實還有一個構(gòu)造函數(shù),接下來看下 MutableTransitionState
吧!
class MutableTransitionState<S>(initialState: S) { // 當(dāng)前的狀態(tài) var currentState: S by mutableStateOf(initialState) internal set // 過渡的目標(biāo)狀態(tài) var targetState: S by mutableStateOf(initialState) // 是否空閑 val isIdle: Boolean get() = (currentState == targetState) && !isRunning // 是否運行 internal var isRunning: Boolean by mutableStateOf(false) }
可以看到 MutableTransitionState
中構(gòu)建了幾個需要的狀態(tài),具體表示什么在上面代碼中添加了注釋。
過渡動畫實現(xiàn)——AnimatedEnterExitImpl
剛才看了 AnimatedVisibility
函數(shù)中的第一行代碼,下面咱們來看下第二行代碼:
AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content)
這塊調(diào)用了一個實現(xiàn)函數(shù),將剛構(gòu)建好的 Transition
和之前的 EnterTransition
、ExitTransition
統(tǒng)統(tǒng)傳了進去,下面就來看下 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 ) } }
這塊代碼有點多啊,但不要害怕,可以看到基本上這里使用到的類在剛才咱們都看過了,接下來需要的就是一行一行往下看,看看哪個沒見過咱們再看!
函數(shù)中先記錄了當(dāng)前的狀態(tài),然后判斷當(dāng)前的狀態(tài)值是否為 true,如果為 true 的話則創(chuàng)建子過渡,老規(guī)矩,來看看 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
的一個擴展函數(shù),還是一個高階函數(shù),函數(shù)內(nèi)獲取了高階函數(shù)中的返回值,然后直接返回調(diào)用了 createChildTransitionInternal
,并傳入獲取的值,這個值目前還不知道是什么,只知道是 targetEnterExit
返回的數(shù)據(jù),這個一會再看,先接著看 createChildTransitionInternal
函數(shù):
@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
的一個擴展函數(shù),然后函數(shù)中也使用了需要清理的效應(yīng),之后判斷 isSeeking
的值是,isSeeking
的值在 setPlaytimeAfterInitialAndTargetStateEstablished
函數(shù)中會被置為 true,如果 isSeeking
值為 true 則調(diào)用 setPlaytimeAfterInitialAndTargetStateEstablished
函數(shù),用來設(shè)置初始狀態(tài)和目標(biāo)狀態(tài)建立后的時間,如果為 false,則更新狀態(tài)值。
到這里 createChildTransition
函數(shù)咱們大概看了下,但剛才還有一個扣,剛才說了不知道目標(biāo)值是什么,因為那是 targetEnterExit
的返回值,現(xiàn)在咱們來看看!
@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
也是一個擴展函數(shù),返回值為 EnterExitState
。這里也使用了 isSeeking
來進行判斷,然后根據(jù)當(dāng)前的值來設(shè)置不同的 EnterExitState
。EnterExitState
又是個啥呢???接著來看!
enum class EnterExitState { // 自定義進入動畫的初始狀態(tài)。 PreEnter, // 自定義進入動畫的目標(biāo)狀態(tài),也是動畫過程中自定義退出動畫的初始狀態(tài)。 Visible, // 自定義退出動畫的目標(biāo)狀態(tài)。 PostExit }
奧,EnterExitState
只是一個枚舉類,定義了三種狀態(tài):初始狀態(tài)、進入動畫的狀態(tài)和退出動畫的狀態(tài)。
下面接著來看 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
效應(yīng),并使用 snapshotFlow
將 State
轉(zhuǎn)為了 Flow
,然后將值設(shè)置到 isAnimationVisible
。
后面又調(diào)用了相同名字的一個函數(shù) 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) } ) } }
函數(shù)名字一樣,但參數(shù)不同,這里將剛才構(gòu)建好的 Transition<EnterExitState>
傳了進來,然后函數(shù)內(nèi)先對狀態(tài)進行了過濾,然后構(gòu)建了 Layout
。
Layout
中設(shè)置了 modifier
,modifier
調(diào)用了 then
函數(shù),then
函數(shù)用于將此修飾符與另一個修飾符連接,然后連接了 transition.createModifier(enter, exit, "Built-in")
,大家發(fā)現(xiàn)點什么沒有,這里使用到了上面咱們構(gòu)建好了所有東西。。。那么。。。。哈哈哈哈!
柳暗花明
下面咱們就來看看 transition.createModifier
這個函數(shù),先來看下函數(shù)體:
@Composable internal fun Transition<EnterExitState>.createModifier( enter: EnterTransition, exit: ExitTransition, label: String ): Modifier
嗯,沒錯,是 Transition<EnterExitState>
的一個擴展函數(shù),也是一個可組合項!接著往下看幾行代碼:
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 )
構(gòu)建了一個 Modifier
,然后調(diào)用了 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 } }
創(chuàng)建兩個值來記錄是否需要透明度的轉(zhuǎn)換和縮放!下面來看下執(zhí)行的代碼:
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
函數(shù)也是可見性動畫這里定義的 Modifier
的擴展函數(shù),里面還有一些自定義的東西,但這不是本文的重點了。
別的可見性動畫
文章開頭說了,可見性動畫目前一共有六個,聽著很嚇人,其實大同小異,來簡單看下不同吧!
@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
的擴展函數(shù),另一個是 ColumnScope
的擴展函數(shù),并且在默認動畫上還有些區(qū)別,RowScope
默認是橫向的擴展和收縮,ColumnScope
是縱向的擴展和收縮,更加方便咱們?nèi)粘U{(diào)用!這也就解釋了文章開頭提出的小問題!
接著再來看別的!
@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) }
這個和之前的就有點區(qū)別了,第一個參數(shù)就不同了,參數(shù)類型為 MutableTransitionState
,其實是一樣的,咱們上面也都說到了,MutableTransitionState
的使用方法在上一篇文章中也介紹過,感興趣的可以去看一下。
剩下的兩個還是 RowScope
和 ColumnScope
的擴展函數(shù),也是參數(shù)類型改為了 MutableTransitionState
,這里也就不做過多介紹。
結(jié)尾
本篇文章帶大家看了可見性動畫的一些源碼,很多其實都是點到為止,并沒有一致不斷深入,一直深入就會陷入其中,忘了看源碼的本意,本文所有源碼基于 Compose 1.3.0-beta02
。
本文至此結(jié)束,有用的地方大家可以參考,當(dāng)然如果能幫助到大家,更多關(guān)于Compose 可見性動畫的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android游戲開發(fā):實現(xiàn)手勢操作切換圖片的實例
本文主要介紹 Android游戲開發(fā)實現(xiàn)手勢操作切換圖片的實例,這里整理了詳細的資料和示例代碼,有開發(fā)Android游戲應(yīng)用的小伙伴可以參考下2016-08-08Android 實現(xiàn)把bitmap圖片的某一部分的顏色改成其他顏色
這篇文章主要介紹了Android 實現(xiàn)把bitmap圖片的某一部分的顏色改成其他顏色,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-04-04android基礎(chǔ)總結(jié)篇之八:創(chuàng)建及調(diào)用自己的ContentProvider
這篇文章主要介紹了android基礎(chǔ)總結(jié)篇之八:創(chuàng)建及調(diào)用自己的ContentProvider,有興趣的可以了解一下。2016-11-11發(fā)布?Android?library?到?Maven?解析
這篇文章主要介紹了發(fā)布?Android?library到Maven解析,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09Android實現(xiàn)ListView的A-Z字母排序和過濾搜索功能 實現(xiàn)漢字轉(zhuǎn)成拼音
這篇文章主要為大家詳細介紹了Android實現(xiàn)ListView的A-Z字母排序和過濾搜索功能,實現(xiàn)漢字轉(zhuǎn)成拼音功能2017-06-06