Android最全教你如何使用Jetpack绘制天气图,史上最详细!,2024android面试题

如何做好面试突击,规划学习方向?

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

我们搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

img

我们在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

)

}

}

Compose自定义布局


上面完成了单个雨滴的图形和动画,接下来我们使用三个雨滴组成雨水的效果。

首先可以使用Row+Space的方式进行组装,但是这种方式缺少灵活性,仅通过Modifier很难准确布局三个雨滴的相对位置。因此考虑转而使用Compose的自定义布局,以提高灵活性和准确性:

Layout(

modifier = modifier.rotate(30f), //雨滴旋转角度

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) { //让三个雨滴的height不同,增加错落感

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中,可以通过Layout{…}对Composable进行自定义布局,content{…}中定义参与布局的子Composable。

跟传统Android视图一样,自定义布局需要先后经历measure、layout两步。

measrue: measurables返回所有待测量的子Composable,constraints类似于MeasureSpec,封装父容器对子元素的布局约束。measurable.measure()中对子元素进行测量

layout:placeables返回测量后的子元素,依次调用placeable.place()对雨滴进行布局,通过xPosition预留雨滴在x轴的间隔

经过layout之后,通过 modifier.rotate(30f) 对Composable进行旋转,完成最终效果:

5. 雪天效果

=================================================================

雪天效果的关键在于雪花的飘落。

雪花的绘制


雪花的绘制非常简单,用一个圆圈代表一个雪花

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)

)

}

雪花飘落动画


雪花飘落的过程相对于雨滴坠落要复杂一些,由三个动画组成:

下降:通过改变y轴位置实现 (0f ~ 2.5f)

左右飘移:通过该表x轴的offset实现 (-1f ~ 1f)

逐渐消失:通过改变alpha实现(1f ~ 0f)

借助InfiniteTransition同步控制多个动画,代码如下:

@Composable

private fun Snowdrop(

modifier: Modifier = Modifier,

durationMillis: Int = 1000 // 雪花飘落动画的druation

) {

//循环播放的Transition

val transition = rememberInfiniteTransition()

//1. 下降动画:restart动画

val animateY by transition.animateFloat(

initialValue = 0f,

targetValue = 2.5f,

animationSpec = infiniteRepeatable(

tween(durationMillis, easing = LinearEasing),

RepeatMode.Restart

)

)

//2. 左右飘移:reverse动画

val animateX by transition.animateFloat(

initialValue = -1f,

targetValue = 1f,

animationSpec = infiniteRepeatable(

tween(durationMillis / 3, easing = LinearEasing),

RepeatMode.Reverse

)

)

//3. alpha值:restart动画,以0f结束

val animateAlpha by transition.animateFloat(

initialValue = 1f,

targetValue = 0f,

animationSpec = infiniteRepeatable(

tween(durationMillis, easing = FastOutSlowInEasing),

)

)

Canvas(modifier) {

val radius = size.width / 2

// 圆心位置随AnimationState改变,实现雪花飘落的效果

val _center = center.copy(

x = center.x + center.x * animateX,

y = center.y + center.y * animateY

)

drawCircle(

color = Color.White.copy(alpha = animateAlpha),//alpha值的变化实现雪花消失效果

center = _center,

radius = radius,

)

drawCircle(

color = Color.Black.copy(alpha = animateAlpha),

center = _center,

radius = radius,

style = Stroke(width = radius * 0.5f)

)

}

}

animateY的targetValue设为2.5f,让雪花的运动轨迹更长,看起来更加真实

雪花的自定义布局


像雨滴一样,对雪花也使用Layout自定义布局

@Composable

fun Snow(

modifier: Modifier = Modifier,

animate: Boolean = false,

) {

Layout(

modifier = modifier,

content = {

//摆放三个雪花,分别设置不同duration,增加随机性

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不同,也是为了增加随机性

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()

}

}

}

}

最终效果如下:

6. 晴天效果

=================================================================

通过一个旋转的太阳代表晴天效果

太阳的绘制


太阳的图形由中间的圆形和围绕圆环的等分竖线组成。

@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计算x轴坐标,sin计算y轴坐标。

太阳的旋转


太阳的旋转动画很简单,通过Modifier.rotate不断转动Canvas即可。

@Composable

fun Sun(modifier: Modifier = Modifier) {

//循环动画

val animateTween by rememberInfiniteTransition().animateFloat(

initialValue = 0f,

targetValue = 360f,

animationSpec = infiniteRepeatable(tween(5000), RepeatMode.Restart)

)

Canvas(modifier.rotate(animateTween)) {// 旋转动画

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,也可以实现旋转效果。

最后我们给太阳的圆心增加一个偏移量,让转动更加活泼:

7. 动画的组合、切换

=====================================================================

上面分别实现了Rain、Snow、Sun等图形,接下来使用这些元素组合成各种天气效果。

将图形组合成天气


Compose的声明式语法非常有利于UI的组合:

比如,多云转阵雨,我们摆放Sun、Cloud、Rain等元素后,通过Modifier调整各自位置即可:

@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))

}

}

复制代码

让动画切换更加自然


当在多个天气动画之间进行切换时,我们希望能实现更自然的过渡。实现思路是将组成天气动画的各元素的Modifier信息变量化,然后通过Animation进行改变state

假设所有的天气都可以由Cloud、Sun、Rain组合而成,无非就是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中持有各种元素的位置信息,运算符重载使其可以在Animation中计算当前最新值。

接下来,使用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),

文末

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

.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),

文末

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家

[外链图片转存中…(img-GQunNEwC-1715244946774)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值