你好,我是朱濤。這是「沉思錄」的第四篇文章。
最近工作有點忙的,趁着端午節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
其實,動畫也很簡單,我們通過注釋來看:
- 注釋1,start 标記動畫的狀态。
- 注釋2,使用 animateDpAsState、animateFloatAsState 生成對應的 alpha、offset,并且,将它們傳入 Splash 頁面當中去使用。你可以回過頭看看 Splash 頁面是如何使用這兩個參數的。
- 注釋3,LaunchedEffect,這裡傳入參數 Unit,代表它隻會執行一次。它的作用,就是啟動一個協程,并且在協程當中改變 start 的狀态,接着延遲一小會,就可以進入首頁了。
最終的效果就是這樣的:
結束語
OK,恭喜你,你已經完成了一個 Compose 頁面!