Jetpack Compose:聲明式UI框架
(Composition over inheritance) 組合優於繼承
1. 什麼是Compose,有什麼優勢?
Jetpack Compose 是一個現代工具包,旨在簡化UI開發。它結合了反應式編程模型和Kotlin編程語言的簡潔性和易用性。它是完全聲明性的,Compose的口號就是消滅xml,在擼過代碼之後總結總體上UI的結構和Flutter一致,只有在語法上存在部分差異,因爲我是基於dev2的版本體驗的,所以在頁面的體驗上並不是很流暢,
而且Compose是沒有view複用的,可能也是較早期的版本可用的資源也不是很多
在使用上Compose可以讓你更專心的處理業務,因起特性聲明UI組件和業務都在class中,較現在的xml模式可以省去很多findViewbyId等一系列綁定的操作
在更新UI上Compose也有類似Flutter的Provide一樣的更新機制,當基礎數據更改時,框架會自動調用這些功能,從而更新視圖層次結構
Compose在UI上改動是非常大的不是採用了傳統UI的多層繼承結構,而是多個View組合成一個View
更新數據只要在使用的實體上面加註解@Model在更新實體的同時會通知UI進行修改
2. 使用
我是基於Android Studio 3.5.1 / build:gralde 3.5.1 / gradle 5.4.1 / compse 0.1.0-dev02 / kotlin 1.3.60-eap-76 版本進行開發的
基本核心庫 (其中androidx.ui爲Compose.UI庫)
androidx.compose:compose-runtime
androidx.ui:ui-framework
androidx.ui:ui-layout
androidx.ui:ui-material
androidx.ui:ui-tooling
androidx.appcompat:appcompat
androidx.activity:activity-ktx
androidx.core:core-ktx
com.pinterest:ktlint
Android studio 4.0以上要打開開關 (Compose對4.0以下版本部分功能不支持)
buildFeatures {
compose true
}
3. 基本UI和使用
VerticalScroller - 縱向滑動
HorizontalScroller - 橫向滑動
Column - 縱向佈局
Row - 橫向佈局
MaterialTheme - 主題
Surface - 背景色
FlexColumn - 權重佈局
TopAppBar - 標題欄
DrawImage - 圖片
Clip - 圓角
Container - 容器
Text - 文本
Divider - 線
HeightSpacer - 高度填充
WidthSpacer - 寬度填充
TabRow - 導航欄
Tab - 導航標題
Button - 按鈕
Checkbox - 選項框
Card - 卡片佈局
Ripple - 水波紋
repeat - 循環
VectorImageButton - Vector圖片按鈕
代碼
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.Composable
import androidx.compose.Model
import androidx.compose.unaryPlus
import androidx.ui.core.*
import androidx.ui.foundation.DrawImage
import androidx.ui.foundation.HorizontalScroller
import androidx.ui.foundation.VerticalScroller
import androidx.ui.foundation.shape.corner.RoundedCornerShape
import androidx.ui.graphics.Color
import androidx.ui.layout.*
import androidx.ui.material.*
import androidx.ui.material.ripple.Ripple
import androidx.ui.material.surface.Card
import androidx.ui.material.surface.Surface
import androidx.ui.res.imageResource
import androidx.ui.text.TextStyle
import androidx.ui.text.style.TextOverflow
import androidx.ui.tooling.preview.Preview
import com.example.jetnews.R
/**
* @fileName: YTestActivity
* @date: 2019/11/21 18:12
* @auther: YuanShuai
* @tag: class//
* @describe:
**/
class YTestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp {
layout()
}
}
}
@Composable //ComposeUI
fun layout(
nums: List<String> = listOf("Hello Compose !", "Two Container !"),
counterState: NumState = NumState()
) {
//縱向滑動佈局
VerticalScroller {
//縱向佈局
Column {
//循環添加
for (num in nums) {
Greeting(num, counterState)
}
}
}
}
@Composable //ComposeUI
fun MyApp(children: @Composable() () -> Unit) {
//Material主題
MaterialTheme {
//自定義主題
//CustomTheme {
//背景色
Surface(color = Color(0xffffffff)) {
//權重佈局
FlexColumn {
inflexible {
//標題欄
TopAppBar(
title = { Text("AppBar") },
navigationIcon = {
//Vector圖片按鈕
VectorImageButton(R.drawable.ic_back) {
Toast.makeText(this@YTestActivity, "back", Toast.LENGTH_SHORT)
.show()
}
}
)
}
flexible(flex = 1f) {
children()
}
}
}
}
}
@Preview("Test Preview") //瀏覽視圖
@Composable //ComposeUI
private fun Greeting(
text: String,
counterState: NumState
) {
val image = +imageResource(R.drawable.duia_bg)
val image2 = +imageResource(R.drawable.duia)
//縱向佈局
Column(
crossAxisSize = LayoutSize.Wrap,
modifier = Spacing(10.dp)
) {
//容器
Container(expanded = true, height = 220.dp) {
//圓角
Clip(shape = RoundedCornerShape(8.dp)) {
//圖片
DrawImage(image)
Container(expanded = true, height = 100.dp, width = 100.dp) {
DrawImage(image2)
}
}
}
//高度填充
HeightSpacer(10.dp)
//文本
Text(text, style = (+themeTextStyle { h6 }).withOpacity(0.8f))
HeightSpacer(10.dp)
//分割線
Divider(color = Color(0x33333333))
HeightSpacer(10.dp)
Container(expanded = true) {
//文本
Text(
"Kotlin Compose !",
style = (+themeTextStyle { body2 }).withOpacity(0.47f)
)
}
HeightSpacer(10.dp)
Divider(color = Color(0x23330033))
HeightSpacer(10.dp)
Container(expanded = true, alignment = Alignment.CenterRight) {
Text(
"Alignment !",
style = TextStyle(color = Color(0xFF936112), fontSize = 18.sp)
)
}
HeightSpacer(10.dp)
Divider(color = Color(0x23330033))
HeightSpacer(10.dp)
Text("設置文本行數////設置文本行數////設置文本行數////設置文本行數////設置文本行數////設置文本行數////設置文本行數" +
"////設置文本行數////設置文本行數////設置文本行數////設置文本行數////設置文本行數////設置文本行數////設置文本行數" +
"////設置文本行數////設置文本行數//",
maxLines = 2,
overflow = TextOverflow.Ellipsis,
style = +themeTextStyle { body2 })
HeightSpacer(10.dp)
Divider(color = Color(0x23330033))
HeightSpacer(10.dp)
//橫向佈局
Row(crossAxisAlignment = CrossAxisAlignment.Center) {
Surface(color = Color.Blue) {
Clip(shape = RoundedCornerShape(8.dp)) {
Text(
"背景色",
modifier = Spacing(20.dp),
style = TextStyle(color = Color(0xFFFFFFFF), fontSize = 20.sp)
)
}
}
//寬度填充
WidthSpacer(10.dp)
Counter(counterState)
WidthSpacer(10.dp)
Form(true)
Form(false)
}
HeightSpacer(10.dp)
Divider(color = Color(0x23330033))
HeightSpacer(10.dp)
Row(modifier = ExpandedWidth, crossAxisAlignment = CrossAxisAlignment.Center) {
textView(Flexible(1f), Color.Magenta, "權重-1")
textView(Flexible(2f), Color.DarkGray, "權重-2")
textView(Flexible(5f), Color.Red, "權重-5")
}
HeightSpacer(10.dp)
Divider(color = Color(0x23330033))
HeightSpacer(10.dp)
//橫向滑動佈局
HorizontalScroller {
Row {
//循環5次
repeat(5) {
//卡片佈局
Card(shape = RoundedCornerShape(10.dp)) {
//水波紋
Ripple(bounded = true) {
Surface(color = Color.LightGray) {
Container(width = 150.dp, height = 150.dp) {
Column {
Container(height = 110.dp, expanded = true) {
DrawImage(image)
}
Text(
text = "TitleTitleTitleTitleTitleTitleTitleTitleTitleTitleTitleTitleTitleTitleTitleTitleTitleTitleTitleTitleTitleTitleTitle",
style = (+themeTextStyle { subtitle1 }).withOpacity(
0.87f
),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
}
}
}
}
WidthSpacer(16.dp)
}
}
}
HeightSpacer(10.dp)
//導航欄
TabRow(items = listOf("導", "航", "欄"), selectedIndex = 0) { index, text ->
//標題
Tab(text = text, selected = 0 == index) {
Toast.makeText(this@YTestActivity, "index=$index", Toast.LENGTH_SHORT).show()
}
}
}
}
@Composable //ComposeUI
fun textView(flex: Modifier, color: Color, text: String) {
Column(modifier = flex) {
Surface(color = color) {
Text(
text,
style = TextStyle(color = Color(0xFFFFFFFF), fontSize = 20.sp)
)
}
}
}
/**
* 按鈕點擊
*/
@Composable //ComposeUI
fun Counter(state: NumState) {
//按鈕
Button(
text = "已經點了${state.count}次了",
onClick = {
state.count++
},
style = ContainedButtonStyle(color = if (state.count > 0) Color.Green else +themeColor { primary })
)
}
/**
* 選擇框
*/
@Composable
fun Form(state: Boolean) {
var formState = FormState(state)
//選擇框
Checkbox(
checked = formState.optionChecked,
onCheckedChange = { newState -> formState.optionChecked = newState }
)
}
@Model //@Model修飾的實體修改的同時會通知UI自動更新
class FormState(var optionChecked: Boolean)
@Model
class NumState(var count: Int = 0)
val green = Color(0xFF1EB90)
val grey = Color(0xFF26282F)
private val themeColros = MaterialColors(
primary = green,
surface = grey,
onSurface = Color.White
)
/**
* 自定義主題
*/
@Composable
fun CustomTheme(children: @Composable() () -> Unit) {
MaterialTheme(colors = themeColros) {
val textStyle = TextStyle(color = Color.Red)
CurrentTextStyleProvider(value = textStyle) {
children()
}
}
}
}