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()
}
}
}