利用Jetpack Compose繪制可愛的天氣動(dòng)畫
1. 項(xiàng)目背景
最近參加了Compose挑戰(zhàn)賽的終極挑戰(zhàn),使用Compose完成了一個(gè)天氣app。之前幾輪挑戰(zhàn)也都有參與,每次都學(xué)到不少新東西。如今迎來(lái)最終挑戰(zhàn),希望能將這段時(shí)間的積累活學(xué)活用,做出更加成熟的作品。
項(xiàng)目挑戰(zhàn)
因?yàn)闆](méi)有美工協(xié)助,所以我考慮通過(guò)代碼實(shí)現(xiàn)app中的所有UI元素例如各種icon等,這樣的UI在任何分辨率下都不會(huì)失真,跟重要的是可以靈活地實(shí)現(xiàn)各種動(dòng)畫效果。
為了降低實(shí)現(xiàn)成本,我將app中的UI元素定義成偏卡通的風(fēng)格,可以更容易地通過(guò)代繪實(shí)現(xiàn):
上面的動(dòng)畫沒(méi)有使用gif、lottie或者其他靜態(tài)資源,所有圖形都是基于Compose代碼繪制的。
2. MyApp:CuteWeather
App界面比較簡(jiǎn)潔,采用單頁(yè)面呈現(xiàn)(挑戰(zhàn)賽要求),卡通風(fēng)格的天氣動(dòng)畫算是相對(duì)于同類app的特色:
項(xiàng)目地址:https://github.com/vitaviva/compose-weather
App界面構(gòu)成
App縱向劃分為幾個(gè)功能區(qū)域,每個(gè)區(qū)域都涉及到一些不同的Compose API的使用
涉及技術(shù)點(diǎn)較多,本文主要介紹如何使用Compose繪制自定義圖形、并基于這些圖形實(shí)現(xiàn)動(dòng)畫,其他內(nèi)容有機(jī)會(huì)再單獨(dú)介紹。
3. Compose自定義繪制
像常規(guī)的Android開發(fā)一樣,除了提供各種默認(rèn)的Composable
控件以外,Compose
也提供了Canvas
用來(lái)繪制自定義UI。
其實(shí)Canvas相關(guān)API在各個(gè)平臺(tái)都大同小異,但在Compose上的使用有以下特點(diǎn):
- 用聲明式的方式創(chuàng)建和使用Canvas
- 通過(guò)DrawScope提供必要的state及各種APIs
- API更簡(jiǎn)單易用
聲明式地創(chuàng)建和使用Canvas
Compose中,Canvas
作為Composable
,可以聲明式地添加到其他Composable中,并通過(guò)Modifier
進(jìn)行配置
Canvas(modifier = Modifier.fillMaxSize()){ // this: DrawScope //內(nèi)部進(jìn)行自定義繪制 }
傳統(tǒng)方式需要獲取Canvas句柄命令式的進(jìn)行繪制,而Canvas{...}
通過(guò)狀態(tài)驅(qū)動(dòng)的方式在block內(nèi)執(zhí)行繪制邏輯、刷新UI。
強(qiáng)大的DrawScope
Canvas{...}
內(nèi)部通過(guò)DrawScope
提供必要的state用來(lái)獲取當(dāng)前繪制所需環(huán)境變量,例如我們最常用的size。DrawScope
還提了各種常用的繪制API,例如drawLine
等
Canvas(modifier = Modifier.fillMaxSize()){ //通過(guò)size獲取當(dāng)前canvas的width和height val canvasWidth = size.width val canvasHeight = size.height //繪制直線 drawLine( start = Offset(x=canvasWidth, y = 0f), end = Offset(x = 0f, y = canvasHeight), color = Color.Blue, strokeWidth = 5F //設(shè)置直線寬度 ) }
上面代碼繪制效果如下:
4.簡(jiǎn)單易用的API
傳統(tǒng)的Canvas API需要進(jìn)行Paint等配置;DrawScope提供的API更簡(jiǎn)單,使用更友好。
例如繪制一個(gè)圓,傳統(tǒng)的API是這樣:
public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) { //... }
DrawScope提供的API:
fun drawCircle( color: Color, radius: Float = size.minDimension / 2.0f, center: Offset = this.center, alpha: Float = 1.0f, style: DrawStyle = Fill, colorFilter: ColorFilter? = null, blendMode: BlendMode = DefaultBlendMode ) {...}
看起來(lái)參數(shù)變多了,但是其實(shí)已經(jīng)通過(guò)size等設(shè)置了合適的默認(rèn)值,同時(shí)省去了對(duì)Paint的創(chuàng)建和配置,使用起來(lái)更方便。
使用原生Canvas
目前DrawScope提供的API還不及原生Canvas豐富(比如不支持drawText等),當(dāng)不滿足使用需求時(shí),也可以直接使用原生Canvas對(duì)象進(jìn)行繪制
drawIntoCanvas { canvas -> //nativeCanvas是原生canvas對(duì)象,android平臺(tái)即android.graphics.Canvas val nativeCanvas = canvas.nativeCanvas }
上面介紹了Compose Canvas
的基本知識(shí),下面結(jié)合app中的具體示例看一下實(shí)際使用效果
首先,看一下雨水的繪制過(guò)程。
5. 雨天效果
雨天天氣的關(guān)鍵是如何繪制不斷下落的雨水
雨滴的繪制
我們先繪制構(gòu)成雨水的基本單元:雨滴
經(jīng)拆解后,雨水效果可由三組雨滴構(gòu)成,每一組雨滴分成上下兩端,這樣在運(yùn)動(dòng)時(shí)就可以形成接連不斷的雨水效果。我們使用drawLine繪制每一段黑線,設(shè)置適當(dāng)?shù)?code>stokeWidth,并通過(guò)cap設(shè)置端點(diǎn)的圓形效果:
@Composable fun rainDrop() { Canvas(modifier) { val x: Float = size.width / 2 //x坐標(biāo):1/2的位置 drawLine( Color.Black, Offset(x, line1y1), //line1 的起點(diǎn) Offset(x, line1y2), //line1 的終點(diǎn) strokeWidth = width, //設(shè)置寬度 cap = StrokeCap.Round//頭部圓形 ) // line2同上 drawLine( Color.Black, Offset(x, line2y1), Offset(x, line2y2), strokeWidth = width, cap = StrokeCap.Round ) } }
雨滴下落動(dòng)畫
完成基本圖形的繪制后,接下來(lái)為兩線段實(shí)現(xiàn)循環(huán)往復(fù)的位移動(dòng)畫,形成雨水的流動(dòng)效果。
以兩線段中間空隙為動(dòng)畫的錨點(diǎn),根據(jù)animationState
設(shè)置其y軸位置,讓其從繪制區(qū)域的頂端移動(dòng)到低端(0 ~ size.hight)
,然后restart這個(gè)動(dòng)畫。
以錨點(diǎn)為基準(zhǔn)繪制上下兩線段,就可以行成接連不斷的雨滴效果了
代碼如下:
@Composable fun rainDrop() { //循環(huán)播放的動(dòng)畫 ( 0f ~ 1f) val animateTween by rememberInfiniteTransition().animateFloat( initialValue = 0f, targetValue = 1f, animationSpec = infiniteRepeatable( tween(durationMillis, easing = LinearEasing), RepeatMode.Restart //start動(dòng)畫 ) ) Canvas(modifier) { // scope : 繪制區(qū)域 val width = size.width val x: Float = size.width / 2 // width/2是strokCap的寬度,scopeHeight處預(yù)留strokCap寬度,讓雨滴移出時(shí)保持正圓,提高視覺(jué)效果 val scopeHeight = size.height - width / 2 // space : 兩線段的間隙 val space = size.height / 2.2f + width / 2 //間隙size val spacePos = scopeHeight * animateTween //錨點(diǎn)位置隨animationState變化 val sy1 = spacePos - space / 2 val sy2 = spacePos + space / 2 // line length val lineHeight = scopeHeight - space // line1 val line1y1 = max(0f, sy1 - lineHeight) val line1y2 = max(line1y1, sy1) // line2 val line2y1 = min(sy2, scopeHeight) val line2y2 = min(line2y1 + lineHeight, scopeHeight) // draw drawLine( Color.Black, Offset(x, line1y1), Offset(x, line1y2), strokeWidth = width, colorFilter = ColorFilter.tint( Color.Black ), cap = StrokeCap.Round ) drawLine( Color.Black, Offset(x, line2y1), Offset(x, line2y2), strokeWidth = width, colorFilter = ColorFilter.tint( Color.Black ), cap = StrokeCap.Round ) } }
6.Compose自定義布局
上面完成了單個(gè)雨滴的圖形和動(dòng)畫,接下來(lái)我們使用三個(gè)雨滴組成雨水的效果。
首先可以使用Row+Space
的方式進(jìn)行組裝,但是這種方式缺少靈活性,僅通過(guò)Modifier
很難準(zhǔn)確布局三個(gè)雨滴的相對(duì)位置。因此考慮轉(zhuǎn)而使用Compose
的自定義布局,以提高靈活性和準(zhǔn)確性:
Layout( modifier = modifier.rotate(30f), //雨滴旋轉(zhuǎn)角度 content = { // 定義子Composable Raindrop(modifier.fillMaxSize()) Raindrop(modifier.fillMaxSize()) Raindrop(modifier.fillMaxSize()) } ) { measurables, constraints -> // List of measured children val placeables = measurables.mapIndexed { index, measurable -> // Measure each children val height = when (index) { //讓三個(gè)雨滴的height不同,增加錯(cuò)落感 0 -> constraints.maxHeight * 0.8f 1 -> constraints.maxHeight * 0.9f 2 -> constraints.maxHeight * 0.6f else -> 0f } measurable.measure( constraints.copy( minWidth = 0, minHeight = 0, maxWidth = constraints.maxWidth / 10, // raindrop width maxHeight = height.toInt(), ) ) } // Set the size of the layout as big as it can layout(constraints.maxWidth, constraints.maxHeight) { var xPosition = constraints.maxWidth / ((placeables.size + 1) * 2) // Place children in the parent layout placeables.forEachIndexed { index, placeable -> // Position item on the screen placeable.place(x = xPosition, y = 0) // Record the y co-ord placed up to xPosition += (constraints.maxWidth / ((placeables.size + 1) * 0.8f)).roundToInt() } } }
Compose中,可以通過(guò)Layout{...}
對(duì)Composable進(jìn)行自定義布局,content{...}
中定義參與布局的子Composable。
跟傳統(tǒng)Android視圖一樣,自定義布局需要先后經(jīng)歷measure
、layout
兩步。
measrue
:measurables返回所有待測(cè)量的子Composable,constraints類似于MeasureSpec,封裝父容器對(duì)子元素的布局約束。measurable.measure()
中對(duì)子元素進(jìn)行測(cè)量
layout
:placeables返回測(cè)量后的子元素,依次調(diào)用placeable.place()
對(duì)雨滴進(jìn)行布局,通過(guò)xPosition
預(yù)留雨滴在x軸的間隔
經(jīng)過(guò)layout之后,通過(guò) modifier.rotate(30f)
對(duì)Composable
進(jìn)行旋轉(zhuǎn),完成最終效果:
7.. 雪天效果
雪天效果的關(guān)鍵在于雪花的飄落。
雪花的繪制
雪花的繪制非常簡(jiǎn)單,用一個(gè)圓圈代表一個(gè)雪花
Canvas(modifier) { val radius = size / 2 drawCircle( //白色填充 color = Color.White, radius = radius, style = FILL ) drawCircle(// 黑色邊框 color = Color.Black, radius = radius, style = Stroke(width = radius * 0.5f) ) }
雪花飄落動(dòng)畫
雪花飄落的過(guò)程相對(duì)于雨滴墜落要復(fù)雜一些,由三個(gè)動(dòng)畫組成:
- 下降:通過(guò)改變y軸位置實(shí)現(xiàn) (0f ~ 2.5f)
- 左右飄移:通過(guò)該表x軸的offset實(shí)現(xiàn) (-1f ~ 1f)
- 逐漸消失:通過(guò)改變alpha實(shí)現(xiàn)(1f ~ 0f)
借助InfiniteTransition
同步控制多個(gè)動(dòng)畫,代碼如下:
@Composable private fun Snowdrop( modifier: Modifier = Modifier, durationMillis: Int = 1000 // 雪花飄落動(dòng)畫的druation ) { //循環(huán)播放的Transition val transition = rememberInfiniteTransition() //1\. 下降動(dòng)畫:restart動(dòng)畫 val animateY by transition.animateFloat( initialValue = 0f, targetValue = 2.5f, animationSpec = infiniteRepeatable( tween(durationMillis, easing = LinearEasing), RepeatMode.Restart ) ) //2\. 左右飄移:reverse動(dòng)畫 val animateX by transition.animateFloat( initialValue = -1f, targetValue = 1f, animationSpec = infiniteRepeatable( tween(durationMillis / 3, easing = LinearEasing), RepeatMode.Reverse ) ) //3\. alpha值:restart動(dòng)畫,以0f結(jié)束 val animateAlpha by transition.animateFloat( initialValue = 1f, targetValue = 0f, animationSpec = infiniteRepeatable( tween(durationMillis, easing = FastOutSlowInEasing), ) ) Canvas(modifier) { val radius = size.width / 2 // 圓心位置隨AnimationState改變,實(shí)現(xiàn)雪花飄落的效果 val _center = center.copy( x = center.x + center.x * animateX, y = center.y + center.y * animateY ) drawCircle( color = Color.White.copy(alpha = animateAlpha),//alpha值的變化實(shí)現(xiàn)雪花消失效果 center = _center, radius = radius, ) drawCircle( color = Color.Black.copy(alpha = animateAlpha), center = _center, radius = radius, style = Stroke(width = radius * 0.5f) ) } }
animateY
的targetValue
設(shè)為2.5f
,讓雪花的運(yùn)動(dòng)軌跡更長(zhǎng),看起來(lái)更加真實(shí)
雪花的自定義布局
像雨滴一樣,對(duì)雪花也使用Layout自定義布局
@Composable fun Snow( modifier: Modifier = Modifier, animate: Boolean = false, ) { Layout( modifier = modifier, content = { //擺放三個(gè)雪花,分別設(shè)置不同duration,增加隨機(jī)性 Snowdrop( modifier.fillMaxSize(), 2200) Snowdrop( modifier.fillMaxSize(), 1600) Snowdrop( modifier.fillMaxSize(), 1800) } ) { measurables, constraints -> val placeables = measurables.mapIndexed { index, measurable -> val height = when (index) { // 雪花的height不同,也是為了增加隨機(jī)性 0 -> constraints.maxHeight * 0.6f 1 -> constraints.maxHeight * 1.0f 2 -> constraints.maxHeight * 0.7f else -> 0f } measurable.measure( constraints.copy( minWidth = 0, minHeight = 0, maxWidth = constraints.maxWidth / 5, // snowdrop width maxHeight = height.roundToInt(), ) ) } layout(constraints.maxWidth, constraints.maxHeight) { var xPosition = constraints.maxWidth / ((placeables.size + 1)) placeables.forEachIndexed { index, placeable -> placeable.place(x = xPosition, y = -(constraints.maxHeight * 0.2).roundToInt()) xPosition += (constraints.maxWidth / ((placeables.size + 1) * 0.9f)).roundToInt() } } } }
最終效果如下:
8. 晴天效果
通過(guò)一個(gè)旋轉(zhuǎn)的太陽(yáng)代表晴天效果
太陽(yáng)的繪制
太陽(yáng)的圖形由中間的圓形和圍繞圓環(huán)的等分豎線組成。
@Composable fun Sun(modifier: Modifier = Modifier) { Canvas(modifier) { val radius = size.width / 6 val stroke = size.width / 20 // draw circle drawCircle( color = Color.Black, radius = radius + stroke / 2, style = Stroke(width = stroke), ) drawCircle( color = Color.White, radius = radius, style = Fill, ) // draw line val lineLength = radius * 0.2f val lineOffset = radius * 1.8f (0..7).forEach { i -> val radians = Math.toRadians(i * 45.0) val offsetX = lineOffset * cos(radians).toFloat() val offsetY = lineOffset * sin(radians).toFloat() val x1 = size.width / 2 + offsetX val x2 = x1 + lineLength * cos(radians).toFloat() val y1 = size.height / 2 + offsetY val y2 = y1 + lineLength * sin(radians).toFloat() drawLine( color = Color.Black, start = Offset(x1, y1), end = Offset(x2, y2), strokeWidth = stroke, cap = StrokeCap.Round ) } } }
均分360度,每間隔45度畫一條豎線,cos計(jì)算x軸坐標(biāo),sin計(jì)算y軸坐標(biāo)。
太陽(yáng)的旋轉(zhuǎn)
太陽(yáng)的旋轉(zhuǎn)動(dòng)畫很簡(jiǎn)單,通過(guò)Modifier.rotate
不斷轉(zhuǎn)動(dòng)Canvas即可。
@Composable fun Sun(modifier: Modifier = Modifier) { //循環(huán)動(dòng)畫 val animateTween by rememberInfiniteTransition().animateFloat( initialValue = 0f, targetValue = 360f, animationSpec = infiniteRepeatable(tween(5000), RepeatMode.Restart) ) Canvas(modifier.rotate(animateTween)) {// 旋轉(zhuǎn)動(dòng)畫 val radius = size.width / 6 val stroke = size.width / 20 val centerOffset = Offset(size.width / 30, size.width / 30) //圓心偏移量 // draw circle drawCircle( color = Color.Black, radius = radius + stroke / 2, style = Stroke(width = stroke), center = center + centerOffset //圓心偏移 ) //...略 } }
此外,DrawScope
也提供了rotate
的API,也可以實(shí)現(xiàn)旋轉(zhuǎn)效果。
最后我們給太陽(yáng)的圓心增加一個(gè)偏移量,讓轉(zhuǎn)動(dòng)更加活潑:
9. 動(dòng)畫的組合、切換
上面分別實(shí)現(xiàn)了Rain、Snow、Sun等圖形,接下來(lái)使用這些元素組合成各種天氣效果。
將圖形組合成天氣
Compose的聲明式語(yǔ)法非常有利于UI的組合:
比如,多云轉(zhuǎn)陣雨,我們擺放Sun
、Cloud
、Rain
等元素后,通過(guò)Modifier調(diào)整各自位置即可:
@Composable fun CloudyRain(modifier: Modifier) { Box(modifier.size(200.dp)){ Sun(Modifier.size(120.dp).offset(140.dp, 40.dp)) Rain(Modifier.size(80.dp).offset(80.dp, 60.dp)) Cloud(Modifier.align(Aligment.Center)) } }
讓動(dòng)畫切換更加自然
當(dāng)在多個(gè)天氣動(dòng)畫之間進(jìn)行切換時(shí),我們希望能實(shí)現(xiàn)更自然的過(guò)渡。實(shí)現(xiàn)思路是將組成天氣動(dòng)畫的各元素的Modifier
信息變量化,然后通過(guò)Animation
進(jìn)行改變state 假設(shè)所有的天氣都可以由Cloud、Sun、Rain組合而成,無(wú)非就是offset
、size
、alpha
值的不同:
ComposeInfo data class IconInfo( val size: Float = 1f, val offset: Offset = Offset(0f, 0f), val alpha: Float = 1f, ) //天氣組合信息,即Sun、Cloud、Rain的位置信息 data class ComposeInfo( val sun: IconInfo, val cloud: IconInfo, val rains: IconInfo, ) { operator fun times(float: Float): ComposeInfo = copy( sun = sun * float, cloud = cloud * float, rains = rains * float ) operator fun minus(composeInfo: ComposeInfo): ComposeInfo = copy( sun = sun - composeInfo.sun, cloud = cloud - composeInfo.cloud, rains = rains - composeInfo.rains, ) operator fun plus(composeInfo: ComposeInfo): ComposeInfo = copy( sun = sun + composeInfo.sun, cloud = cloud + composeInfo.cloud, rains = rains + composeInfo.rains, ) }
如上,ComposeInfo
中持有各種元素的位置信息,運(yùn)算符重載使其可以在Animation
中計(jì)算當(dāng)前最新值。
接下來(lái),使用ComposeInfo為不同天氣定義各元素的位置信息
//晴天 val SunnyComposeInfo = ComposeInfo( sun = IconInfo(1f), cloud = IconInfo(0.8f, Offset(-0.1f, 0.1f), 0f), rains = IconInfo(0.4f, Offset(0.225f, 0.3f), 0f), ) //多云 val CloudyComposeInfo = ComposeInfo( sun = IconInfo(0.1f, Offset(0.75f, 0.2f), alpha = 0f), cloud = IconInfo(0.8f, Offset(0.1f, 0.1f)), rains = IconInfo(0.4f, Offset(0.225f, 0.3f), alpha = 0f), ) //雨天 val RainComposeInfo = ComposeInfo( sun = IconInfo(0.1f, Offset(0.75f, 0.2f), alpha = 0f), cloud = IconInfo(0.8f, Offset(0.1f, 0.1f)), rains = IconInfo(0.4f, Offset(0.225f, 0.3f), alpha = 1f), )
ComposedIcon
接著,定義ComposedIcon,根據(jù)ComposeInfo實(shí)現(xiàn)不同的天氣組合
@Composable fun ComposedIcon(modifier: Modifier = Modifier, composeInfo: ComposeInfo) { //各元素的ComposeInfo val (sun, cloud, rains) = composeInfo Box(modifier) { //應(yīng)用ComposeInfo到Modifier val _modifier = remember(Unit) { { icon: IconInfo -> Modifier .offset( icon.size * icon.offset.x, icon.size * icon.offset.y ) .size(icon.size) .alpha(icon.alpha) } } Sun(_modifier(sun)) Rains(_modifier(rains)) AnimatableCloud(_modifier(cloud)) } }
ComposedWeather
最后,定義ComposedWeather
記錄當(dāng)前ComposedIcon
,并在其發(fā)生更新時(shí)使用動(dòng)畫進(jìn)行過(guò)度:
@Composable fun ComposedWeather(modifier: Modifier, composedIcon: ComposedIcon) { val (cur, setCur) = remember { mutableStateOf(composedIcon) } var trigger by remember { mutableStateOf(0f) } DisposableEffect(composedIcon) { trigger = 1f onDispose { } } //創(chuàng)建動(dòng)畫(0f ~ 1f),用于更新ComposeInfo val animateFloat by animateFloatAsState( targetValue = trigger, animationSpec = tween(1000) ) { //當(dāng)動(dòng)畫結(jié)束時(shí),更新ComposeWeather到最新state setCur(composedIcon) trigger = 0f } //根據(jù)AnimationState計(jì)算當(dāng)前ComposeInfo val composeInfo = remember(animateFloat) { cur.composedIcon + (weatherIcon.composedIcon - cur.composedIcon) * animateFloat }
以上就是利用Jetpack Compose繪制可愛的天氣動(dòng)畫的詳細(xì)內(nèi)容,更多關(guān)于Jetpack Compose繪制動(dòng)畫的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Android Compose 屬性動(dòng)畫使用探索詳解
- Compose開發(fā)之動(dòng)畫藝術(shù)探索及實(shí)現(xiàn)示例
- Android中分析Jetpack?Compose動(dòng)畫內(nèi)部的實(shí)現(xiàn)原理
- Jetpack Compose實(shí)現(xiàn)列表和動(dòng)畫效果詳解
- Jetpack Compose實(shí)現(xiàn)動(dòng)畫效果的方法詳解
- 通過(guò)Jetpack Compose實(shí)現(xiàn)雙擊點(diǎn)贊動(dòng)畫效果
- Android之Compose頁(yè)面切換動(dòng)畫介紹
- Compose?動(dòng)畫藝術(shù)之屬性動(dòng)畫探索
相關(guān)文章
Android 觸摸事件監(jiān)聽(Activity層,ViewGroup層,View層)詳細(xì)介紹
這篇文章主要介紹了Android 觸摸事件監(jiān)聽(Activity層,ViewGroup層,View層)詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-12-12Android點(diǎn)擊WebView實(shí)現(xiàn)圖片縮放及滑動(dòng)瀏覽效果
這篇文章主要為大家詳細(xì)介紹了Android點(diǎn)擊WebView實(shí)現(xiàn)圖片縮放及滑動(dòng)瀏覽效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12Android開發(fā)人臉識(shí)別統(tǒng)計(jì)人臉數(shù)
這篇文章主要介紹了Android開發(fā)人臉識(shí)別統(tǒng)計(jì)人臉數(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-10-10Android應(yīng)用啟動(dòng)速度優(yōu)化
這篇文章主要介紹了Android應(yīng)用啟動(dòng)速度優(yōu)化的相關(guān)資料,需要的朋友可以參考下2016-01-01Android實(shí)現(xiàn)熱門標(biāo)簽的流式布局
這篇文章主要介紹了Android實(shí)現(xiàn)熱門標(biāo)簽的流式布局的詳細(xì)方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2015-12-12Android ViewPager畫廊效果詳解及實(shí)例
這篇文章主要介紹了Android ViewPager畫廊效果詳解及實(shí)例的相關(guān)資料,這里提供實(shí)例代碼及實(shí)現(xiàn)效果圖,具有參考價(jià)值,需要的朋友可以參考下2016-12-12利用百度地圖Android sdk高仿微信發(fā)送位置功能及遇到的問(wèn)題
這篇文章給大家介紹了利用百度地圖Android sdk高仿微信發(fā)送位置功能,在實(shí)現(xiàn)此功能的時(shí)候遇到點(diǎn)小問(wèn)題,下面小編給大家列出來(lái),需要的朋友參考下吧2017-12-12Android 對(duì)Map按key和value分別排序的實(shí)例
下面小編就為大家?guī)?lái)一篇Android 對(duì)Map按key和value分別排序的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-12-12Android開發(fā)中TextView 實(shí)現(xiàn)右上角跟隨文本動(dòng)態(tài)追加圓形紅點(diǎn)
這篇文章主要介紹了android textview 右上角跟隨文本動(dòng)態(tài)追加圓形紅點(diǎn)的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11android文件存儲(chǔ)和SharedPreferences存儲(chǔ)的項(xiàng)目實(shí)例
本文主要介紹了android文件存儲(chǔ)和SharedPreferences存儲(chǔ)的項(xiàng)目實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05