天天看點

2小時入門 Jetpack Compose(上)

你好,我是朱濤。這是「沉思錄」的第四篇文章。

最近工作有點忙的,趁着端午節3天小長假,我來寫個 Compose 快速入門的教程吧!我們的目标是:「2小時入門 Compose」!

2小時入門 Jetpack Compose(上)

怎麼學?

學習 Compose 最快的方式是什麼?當然是寫代碼呀!可是,Google 官方給出的開源項目太複雜了,初學者看了就頭疼。我舉個例子,Google 官方最簡單的教學 App:​​Jetsnack​​,都有将近5000行代碼,這怎能不讓人望而卻步?

是以,我們需要找一個更簡單的項目,代碼量還要再小一點。我個人比較喜歡 Google 官方的架構案例:​​TodoApp​​,它的功能足夠簡單,也比較貼近我們的實際生活。

不過,可惜的是,這個官方倉庫并沒有完整的 Compose 實作,GitHub上面類似的實作又太複雜了。怎麼辦呢?那我就仿着它來寫一個吧。Logo 資源什麼的,我也給扒過來了。

開始吧!

To Do App

整個項目的代碼我已經寫完了,它的結構非常簡單,一共就隻有三個頁面:

  • 第一個頁面:開屏的 Splash 頁面,也就是文章開頭我放的動圖,這裡我們用 Compose 的動畫 API 就能輕松實作。
  • 第二個頁面:首頁,也就是代處理任務的清單。
  • 第三個頁面:任務詳情。

整個工程的代碼量,我統計了一下,隻有1400多行。

2小時入門 Jetpack Compose(上)

對于一個功能完整的 App,這樣的代碼量已經算上很小的了,想想我們工作中的一個 Presenter 代碼量都不止1400行代碼吧?

到這裡,你會不會覺得,這個代碼量實在太小了呢?這App會不會是個 Hello World 級别的呢?讓我來帶你看看它的功能。

工程介紹

總的來說,核心的 UI 隻有這麼幾個檔案:

2小時入門 Jetpack Compose(上)

開屏頁面

其中,最簡單的,就是 Splash 頁面,它的作用隻有一個:展示 Logo,接着等待一小會,進入首頁。在這裡,我們會實作一個動畫。

2小時入門 Jetpack Compose(上)

這個動畫分為兩個部分:Logo 的「透明度」動畫,還有文字的「透明度」+「位移」動畫。

首頁

接着,我們來看看首頁的功能,它會展示目前所有的任務和狀态。

這裡,我寫了一個進場動效,這個在 Compose 當中實作起來真沒什麼難度。

2小時入門 Jetpack Compose(上)

總的來說,就是:Index 越大的 Item,它的初始位移越大,進場的時候,再把所有 Item 挪回原處即可。

拖拽删除

然後,我還實作了一下拖拽删除功能。

2小時入門 Jetpack Compose(上)

拖拽删除本身沒什麼難度,不過,在拖拽的過程中,做一些其他的事情,還是比較有意思的。這裡有兩個細節:

  • 第一,手指拖動到一定範圍的時候,彈出一個 Toast,告訴使用者,可以松手删除了。如果你看過我的部落格《揭秘 Compose 原理》的話,你一定能體會到其中的難處:如何避免 Toast 反複彈出。
  • 第二,手指拖動到一定範圍後,垃圾桶圖示還做了一個:傾倒的動畫。

以上這兩個細節,我也會在後面的部落格詳細介紹。

任務詳情頁面

詳情頁,這個頁面反而沒什麼特殊東西,就是兩個輸入框,分别是:任務标題、任務詳情,還有一個 CheckBox,代表任務是否已經完成。

2小時入門 Jetpack Compose(上)

這裡的輸入框的動畫效果,是 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      

其實,動畫也很簡單,我們通過注釋來看:

  • 注釋1,start 标記動畫的狀态。
  • 注釋2,使用 animateDpAsState、animateFloatAsState 生成對應的 alpha、offset,并且,将它們傳入 Splash 頁面當中去使用。你可以回過頭看看 Splash 頁面是如何使用這兩個參數的。
  • 注釋3,LaunchedEffect,這裡傳入參數 Unit,代表它隻會執行一次。它的作用,就是啟動一個協程,并且在協程當中改變 start 的狀态,接着延遲一小會,就可以進入首頁了。

最終的效果就是這樣的:

2小時入門 Jetpack Compose(上)

結束語

OK,恭喜你,你已經完成了一個 Compose 頁面!