Android Compose 入門,深入底層源碼分析 我是跟著AS官網學習的,但是官方的教程寫的不是很詳細.官網鏈接 首先創建一個Compose項目,目錄結構是這樣: ui -> theme -> -> Color.kt -> -> Theme.kt -> -> Type.kt MainActiv ...
Android Compose 入門,深入底層源碼分析
我是跟著AS官網學習的,但是官方的教程寫的不是很詳細.官網鏈接
首先創建一個Compose項目,目錄結構是這樣:
ui
-> theme
-> -> Color.kt
-> -> Theme.kt
-> -> Type.kt
MainActivity.kt
通過閱讀源碼,發現實際上還少了一個Shapes.kt,我手動添加了.
Type.kt
/**
* 存放組件的Style
* Typography全部是文字的屬性
* 點開Typography的代碼,結構很簡單.
* 存了一些TextStyle,提供以下功能:
* 主構造函數: 使用一些預設參數初始化各個TextStyle
* copy: 複製一份
* equals: 比較每一個TextStyle
* hashCode: 計算hash,把每個TextStyle都計算進去.
* fromToken: internal修飾,為Typography類增加拓展函數,根據傳進來的enum,確認獲取哪個TextStyle.
* 在class外部還有一個對象:
* LocalTypography: internal修飾,被MaterialTheme作為預設參數使用
*/
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
)
Color.kt
/**
* 存放顏色值
* darkColorScheme和lightColorScheme都屬於ColorScheme
* 點開ColorScheme的代碼,結構很簡單.
* 存了一些Color,提供以下功能:
* 主構造函數: 顏色委托給mutableStateOf,使顏色值的變化可以被Compose觀察.
* copy: 複製一份
* 剩下的是一些函數:
* lightColorScheme: 提供預設的白天模式顏色
* darkColorScheme: 提供預設的夜間模式顏色
* ColorScheme.contentColorFor: 為ColorScheme類增加拓展函數,根據背景色使用對應的前景色,如果顏色不匹配,返回透明色.
* contentColorFor: 提供一個函數,import這個函數來使用,如果顏色不匹配,返回黑色.
* applyTonalElevation: internal修飾,為ColorScheme類增加拓展函數,返回新背景色,傳入的背景色加上高度.
* surfaceColorAtElevation: 為ColorScheme類增加拓展函數,計算不同高度的surface錶面色調.
* updateColorSchemeFrom: internal修飾,為ColorScheme類增加拓展函數,更新顏色,成本很高,但顏色委托給了mutableStateOf,忽略不變化的顏色值,提高運行效率.
* fromToken: internal修飾,為ColorScheme類增加拓展函數,根據傳進來的enum,確認獲取哪個Color.
* toColor: internal修飾,為ColorSchemeKeyTokens類增加拓展函數,將enum轉換為對應的顏色,調用fromToken.
* 在class外部還有兩個對象:
* LocalColorScheme: internal修飾,被MaterialTheme作為預設參數使用,預設使用lightColorScheme.
* DisabledAlpha: internal修飾,禁用狀態的前景色.比如禁用按鈕的文字顏色.
*/
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)
//深色模式
val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
//淺色模式
val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40,
)
Shapes.kt
/**
* 預設創建的項目里沒有創建Shapes
* 這裡也簡單介紹一下
* 點開Shapes代碼,結構很簡單.
* 存放了一些形狀,提供以下功能:
* 主構造函數: 存放一些CornerBasedShape
* CornerBasedShape基於角的形狀,子類有: AbsoluteCutCornerShape,AbsoluteRoundedCornerShape,CutCornerShape,RoundedCornerShape.
* https://developer.android.com/reference/kotlin/androidx/compose/foundation/shape/CornerBasedShape * copy: 複製一份
* equals: 比較每個shape
* hashCode: 計算hash,把每個shape都計算進去.
* 類外部:
* ShapeDefaults: 提供CornerBasedShape的預設參數
* top,bottom,start,end: internal修飾,幫助組件獲取Shape.
* fromToken: internal修飾,根據傳入的enum,返回對應的Shape.
* toShape: internal修飾,為ShapeKeyTokens增加擴展函數,把enum轉為Shape,調用fromToken.
* LocalShapes: internal修飾,被MaterialTheme作為預設參數使用,預設使用ShapeDefaults.
*/val shapes = Shapes(
extraSmall = ShapeDefaults.ExtraSmall
)
Theme.kt
/**
* 構建一個Theme用來使用
* 如果不用自己構建的theme,會使用預設的theme.
* Theme很簡單,分為兩部分.
* 由Compose托管的:
* 使用colorScheme,shapes,typography,content創建一個MaterialTheme.
* content使用這個MaterialTheme,並且這個MaterialTheme會遞歸傳遞給content內的@Composable修飾的函數.
* 非Compose托管的:
* 一些不屬於View的,屬於window的.如狀態欄顏色,導航欄是否顯示等.
* 然後是MaterialTheme的源碼
* MaterialTheme是一個@Composable修飾的函數,按照順序拆解:
* rememberedColorScheme,用來更新顏色.調用updateColorSchemeFrom.使用remember讓currentComposer緩存colorScheme.copy()返回的對象,下次重組時繼續使用該值,涉及的內容太多,這裡不再深入.
* rippleIndication,波紋動畫,預設使用透明色,也就是沒有波紋動畫.
* selectionColors,文字選中顏色,預設使用:rememberedColorScheme.primary
* CompositionLocalProvider,一個@Composable修飾的函數,使用上面的參數構建一個ProvidedValue對象,調用currentComposer.startProviders保存這些對象,然後調用content繪製,繪製時會使用這些ProvidedValue對象,然後調用currentComposer.endProviders()終止記錄.
*/
/**
* @param darkTheme 是否是深色模式
* @param dynamicColor 動態顏色 安卓12(api31) 新增,會基於系統壁紙的顏色使用對應的顏色,https://developer.android.com/develop/ui/views/theming/dynamic-colors
* @param content Composable代碼塊,使用colorScheme作為主題色
*/
@Composable
fun Study1Theme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
//Material Design 需要的顏色
val colorScheme = when {
//使用動態顏色,跟隨壁紙,只有大於api31才能使用
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
//獲取View,@Composable的組合函數實際上是一個View
val view = LocalView.current
//不是編輯模式的情況下,設置一些參數
if (!view.isInEditMode) {
//window不是Compose管理的對象,需要用SideEffect來共用Compose狀態,SideEffect保證每次重組後都會執行
SideEffect {
//設置status bar 顏色
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}
//顏色,字體,代碼塊構建一個MaterialTheme對象,代碼塊的MaterialTheme對象會使用這個構建的對象
MaterialTheme(
colorScheme = colorScheme,
typography = typography,
shapes = shapes,
content = content
)
}
MainActivity.kt
這個沒什麼好說的,官方的教程說的很明白了.這裡簡單貼一下代碼.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContent是Kotlin的擴展函數,使用Compose創建視窗
setContent {
//Study1Theme在theme/Theme.kt里,生成MaterialTheme給整個代碼塊用
Study1Theme(dynamicColor = false) {
//使用一個@Composable函數來作為界面的入口
MyApp(Modifier.fillMaxSize())
}
} }
//界面入口,用來複用函數
@Composable
fun MyApp(modifier: Modifier = Modifier){
//Surface,一般顯示組件的顏色,如卡片,表格,菜單的背景色
Surface(
//Modifier.fillMaxSize() 鋪滿父組件
modifier = modifier,
//使用Study1Theme里創建的MaterialTheme的colorScheme
//colorScheme現在有深色淺色兩種模式,也可以添加更多風格.
color = MaterialTheme.colorScheme.primary
) {
//Surface函數的最後一個參數是content: @Composable () -> Unit
//@Composable註解修飾的函數只能被同樣@Composable修飾的函數調用
SayHello("Android")
}
}
//顯示一個文本
@Composable
fun SayHello(name: String, modifier: Modifier = Modifier) {
Surface(color = MaterialTheme.colorScheme.primary) {
Text(
text = "Hello $name!",
modifier = modifier.padding(24.dp)
)
}
}
//Preview可以預覽無參或者有預設參數的Compose函數
@Preview(showBackground = true, name = "Say Hello Preview")
@Composable
fun SayHelloPreview(name: String = "Compose") {
Study1Theme(dynamicColor = false) {
MyApp()
}
}
}