Android Compose 入門,深入底層原始碼分析

laomuji666發表於2024-04-10

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

相關文章