2小时入门 Jetpack Compose(上)

4,674 阅读5分钟

往期文章:

《00. 文章合集目录》

《10. 揭秘 Compose 原理》

《11. Google 强推的 Baseline Profiles 国内能用吗?我找 Google 工程师求证了!》

你好,我是朱涛。这是「沉思录」的第四篇文章。

最近工作有点忙的,趁着端午节3天小长假,我来写个 Compose 快速入门的教程吧!我们的目标是:「2小时入门 Compose」

怎么学?

学习 Compose 最快的方式是什么?当然是写代码呀!可是,Google 官方给出的开源项目太复杂了,初学者看了就头疼。我举个例子,Google 官方最简单的教学 App:Jetsnack,都有将近5000行代码,这怎能不让人望而却步?

因此,我们需要找一个更简单的项目,代码量还要再小一点。我个人比较喜欢 Google 官方的架构案例:TodoApp,它的功能足够简单,也比较贴近我们的实际生活。

不过,可惜的是,这个官方仓库并没有完整的 Compose 实现,GitHub上面类似的实现又太复杂了。怎么办呢?那我就仿着它来写一个吧。Logo 资源什么的,我也给扒过来了。

开始吧!

To Do App

整个项目的代码我已经写完了,它的结构非常简单,一共就只有三个页面

  • 第一个页面:开屏的 Splash 页面,也就是文章开头我放的动图,这里我们用 Compose 的动画 API 就能轻松实现。
  • 第二个页面:首页,也就是代处理任务的列表。
  • 第三个页面:任务详情。

整个工程的代码量,我统计了一下,只有1400多行。

对于一个功能完整的 App,这样的代码量已经算上很小的了,想想我们工作中的一个 Presenter 代码量都不止1400行代码吧?

到这里,你会不会觉得,这个代码量实在太小了呢?这App会不会是个 Hello World 级别的呢?让我来带你看看它的功能。

工程介绍

总的来说,核心的 UI 只有这么几个文件:

开屏页面

其中,最简单的,就是 Splash 页面,它的作用只有一个:展示 Logo,接着等待一小会,进入首页。在这里,我们会实现一个动画。

这个动画分为两个部分:Logo 的「透明度」动画,还有文字的「透明度」+「位移」动画

首页

接着,我们来看看首页的功能,它会展示当前所有的任务和状态。

这里,我写了一个进场动效,这个在 Compose 当中实现起来真没什么难度。

总的来说,就是:Index 越大的 Item,它的初始位移越大,进场的时候,再把所有 Item 挪回原处即可

拖拽删除

然后,我还实现了一下拖拽删除功能。

拖拽删除本身没什么难度,不过,在拖拽的过程中,做一些其他的事情,还是比较有意思的。这里有两个细节:

  • 第一,手指拖动到一定范围的时候,弹出一个 Toast,告诉用户,可以松手删除了。如果你看过我的博客《揭秘 Compose 原理》的话,你一定能体会到其中的难处:如何避免 Toast 反复弹出
  • 第二,手指拖动到一定范围后,垃圾桶图标还做了一个:倾倒的动画

以上这两个细节,我也会在后面的博客详细介绍。

任务详情页面

详情页,这个页面反而没什么特殊东西,就是两个输入框,分别是:任务标题、任务详情,还有一个 CheckBox,代表任务是否已经完成

这里的输入框的动画效果,是 TextField 自带的,也不需要我们自己实现,虽然有点丑,但也能用了。

Splash 代码

OK,工程介绍完了,我们就可以上代码了!这篇文章,我们先从最简单的 Splash 页面开始吧!

@Composable
fun Splash(offsetState: Dp, alphaState: Float) {
    // 1
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(MaterialTheme.colors.splashBackground),
        contentAlignment = Alignment.Center
    ) {
        // 2
        Column(
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            // 3
            Image(
                modifier = Modifier
                    .size(LOGO_HEIGHT)
                    .alpha(alpha = alphaState),
                painter = painterResource(id = getLogo()),
                contentDescription = stringResource(id = R.string.to_do_logo)
            )
            // 4
            Text(
                modifier = Modifier
                    .offset(y = offsetState)
                    .alpha(alpha = alphaState),
                text = stringResource(id = R.string.app_name),
                color = MaterialTheme.colors.splashText,
                style = MaterialTheme.typography.h5,
                fontWeight = FontWeight.Bold,
                maxLines = 1,
            )
        }
    }
}

整个 Splash 的 UI 元素其实很简单,我标记了4个注释,我们一起看看:

  • 注释1,它是一个撑满屏幕的 Box,它相当于 Android 当中的 FrameLayout。
  • 注释2,Column,它相当于 Android 当中的 LinearLayout,并且是纵向布局。
  • 注释3,Image,它其实就是开屏当中的 LOGO。
  • 注释4,Text,它其实就是开屏当中的文字:To Do。

OK,UI 元素写出来了,动画怎么搞?

@Composable
fun Splash(
    gotoHomeScreen: () -> Unit
) {
    // 1
    var start by remember { mutableStateOf(false) }
    // 2
    val offset by animateDpAsState(
        targetValue = if (start) 0.dp else 100.dp,
        animationSpec = tween(
            durationMillis = 1000
        )
    )
    val alpha by animateFloatAsState(
        targetValue = if (start) 1f else 0f,
        animationSpec = tween(
            durationMillis = 2000
        )
    )

    // 3
    LaunchedEffect(key1 = Unit) {
        start = true
        delay(SPLASH_DELAY)
        gotoHomeScreen()
    }

    Splash(offsetState = offset, alphaState = alpha)
}

其实,动画也很简单,我们通过注释来看:

  • 注释1,start 标记动画的状态。
  • 注释2,使用 animateDpAsState、animateFloatAsState 生成对应的 alpha、offset,并且,将它们传入 Splash 页面当中去使用。你可以回过头看看 Splash 页面是如何使用这两个参数的。
  • 注释3,LaunchedEffect,这里传入参数 Unit,代表它只会执行一次。它的作用,就是启动一个协程,并且在协程当中改变 start 的状态,接着延迟一小会,就可以进入首页了。

最终的效果就是这样的:

结束语

OK,恭喜你,你已经完成了一个 Compose 页面!

考虑到篇幅限制,首页、详情页,我们留到后面的博客再讲具体代码实现吧。

关于源代码,后续我会在我的公众号“朱涛的自习室”放出来,敬请关注。

好了,不多说,马上周一了,我要给公司搬砖去了。

点赞+评论+转发,请安排上吧!

感谢你的阅读,我们下次再见?[逃]。