1.概述

在之前的博客中,我已经介绍了Compose 的基础UI和布局组件,现在咱们就利用这些基础UI和布局组件去做一个实战项目。Bloom是Google供给的一个设想产品,咱们能够作为练手项目运用,这个产品的具体UI规划稿大家能够自行去百度下,个人决议这儿首要是熟练去运用Compose UI,不必要纠结规划稿,文末我会贴出项目的github地址,供读者参考。

2.作用图展现

2.1 亮色主题作用:

Android Compose UI实战练手----Google Bloom欢迎页

2.2 深色主题作用

Android Compose UI实战练手----Google Bloom欢迎页

3.项目结构解析

项目的大致结构就是一个简单的Android项目,如下图所示:

Android Compose UI实战练手----Google Bloom欢迎页

在上图中是这个项目的结构图,咱们首要介绍的是ui下theme包类的几个Kotlin装备类,当咱们创立一个新项目是,Compose会在项目中生产ui/theme目录,包含四个文件,别离是Color.kt、Shape.kt、Theme.kt、Type.kt;官方主张我没将色彩、字体、形状等装备信息放到这四个装备文件中,便于一致维护办理

3.1 色彩装备Color.kt

装备的色彩信息是一个四字节的Int整形数字,每个字节保存着ARGB对应的信息。例如Pink-100对应的值是0xFFF1F1,这儿表明的仅仅低位RGB三种色彩对应的信息,假如希望添加透明度,那么就在最高位添加一个字节表明透明度,比方希望透明度是100%,那么就添加一个0xFF,最终的色彩值是0xFFFFF1F10xFF对应的十进制是255,表明100%的透明度,假如要表明85%的透明度,则255x85%对应的16进制是0xD8,所以0xFFFFF1F1对应的透明度为85%的色彩值为0xD8FFF1F1

在本项目中用到的色彩值界说如下:

val white = Color(0xFFFFFFFF)
val white150 = Color(0x26FFFFFF)
val white850 = Color(0xD9FFFFFF)
val pink100 = Color(0xFFFFF1F1)
val pink900 = Color(0xFF3F2C2C)
val green300 = Color(0xFFB8C9B8)
val green900 = Color(0xFF2D3B2D)

3.2 形状装备Shape.kt

现在的主流APP中咱们都能够看到很多的UI控件都运用了圆角,圆角的大小在传统的View开发时通常都是在drawable中新建一个xml界说按钮的圆角款式,这样很不便利,因为假如咱们仅仅想改变圆角的大小时,就需求咱们再界说一个xml款式,但是运用Shape.kt后就不存在这个问题,因为圆角大小的装备放到了一个一致的当地。运用的时分直接引用就行了

本项目中的圆角大小界说:

val Shapes = Shapes(
    small = RoundedCornerShape(4.dp),
    medium = RoundedCornerShape(4.dp),
    large = RoundedCornerShape(0.dp)
)

3.3 主题装备Theme.kt

主题的装备相对要麻烦一些,因为篇幅原因,这儿不多介绍,在后面会有专门的一篇博客对主题进行介绍,这儿咱们就先按照我供给的主题装备上就行了。本项目的主题装备如下:

private val BloomDarkColorPalette = darkColors(
    primary = white,
    secondary = green300,
    background = green900,
    surface = white150,
    onPrimary =  white850,
    onSecondary = green900,
    onBackground = pink900,
    onSurface = white850,
)
private val BloomLightColorPalette = lightColors(
    primary = pink900,
    secondary = pink900,
    background = pink100,
    surface = white850,
    onPrimary = pink100,
    onSecondary = pink100,
    onBackground = pink900,
    onSurface = white150
)
open class WelcomePageAssets(var background:Int,var illos:Int,var logo:Int)
//亮色主题资源
object LightWelcomeAssets : WelcomePageAssets(
    background = R.drawable.ic_light_welcome_bg,
    illos = R.drawable.ic_light_welcome_illos,
    logo = R.drawable.ic_light_logo
)
// 暗色主提资源
object DarkWelcomeAssets : WelcomePageAssets(
    background = R.drawable.ic_dark_welcome_bg,
    illos = R.drawable.ic_dark_welcome_illos,
    logo = R.drawable.ic_dark_logo
)
internal var LocalWelcomeAssets = staticCompositionLocalOf { LightWelcomeAssets as WelcomePageAssets }
val welcomeAssets
@Composable
@ReadOnlyComposable
get() = LocalWelcomeAssets.current
enum class BloomTheme{
    LIGHT,DARK
}
@Composable
fun GoogleBloomTheme(theme:BloomTheme = BloomTheme.LIGHT,content:@Composable ()->Unit){
    val welcomeAssets = if(theme == BloomTheme.DARK) DarkWelcomeAssets else LightWelcomeAssets
    CompositionLocalProvider(
        LocalWelcomeAssets provides welcomeAssets
    ) {
        MaterialTheme(colors = if (theme == BloomTheme.DARK)
            BloomDarkColorPalette else BloomLightColorPalette,
        typography = bloomTypoGraphy,
        shapes = shapes,
        content = content,
        )
    }
}

3.4 字体装备 Type.kt

咱们想用的字体能够在Type.kt中装备,假如要引进新字体,能够将下载下来的字体文件放到res/font目录下,假如没有这个目录能够自己建一个,然后运用如下的办法引进字体


val nunitoSansFamily = FontFamily(
    Font(R.font.nunitosans_light, FontWeight.Light),
    Font(R.font.nunitosans_semibold, FontWeight.SemiBold),
    Font(R.font.nunitosans_bold, FontWeight.Bold)
)

本项目的字体装备

// Set of Material typography styles to start with
val Typography = Typography(
    body1 = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Normal,
        fontSize = 16.sp
    )
)
val nunitoSansFamily = FontFamily(
    Font(R.font.nunitosans_light, FontWeight.Light),
    Font(R.font.nunitosans_semibold, FontWeight.SemiBold),
    Font(R.font.nunitosans_bold, FontWeight.Bold)
)
val bloomTypoGraphy = Typography(
    h1 = TextStyle(
        fontSize = 18.sp,
        fontFamily = nunitoSansFamily,
        fontWeight = FontWeight.Bold
    ),
    h2 = TextStyle(
        fontSize = 14.sp,
        letterSpacing = 0.15.sp,
        fontFamily = nunitoSansFamily,
        fontWeight = FontWeight.Bold
    ),
    subtitle1 = TextStyle(
        fontSize = 16.sp,
        fontFamily = nunitoSansFamily,
        fontWeight = FontWeight.Light
    ),
    body1 = TextStyle(
        fontSize = 14.sp,
        fontFamily = nunitoSansFamily,
        fontWeight = FontWeight.Light
    ),
    body2 = TextStyle(
        fontSize = 12.sp,
        fontFamily = nunitoSansFamily,
        fontWeight = FontWeight.Light
    ),
    button = TextStyle(
        fontSize = 14.sp,
        letterSpacing = 1.sp,
        fontFamily = nunitoSansFamily,
        fontWeight = FontWeight.SemiBold,
        color = white
    ),
    caption = TextStyle(
        fontSize = 12.sp,
        fontFamily = nunitoSansFamily,
        fontWeight = FontWeight.SemiBold
    )
)

4.沉浸式状态栏适配

所谓沉浸式主题栏适配就是指手机状态栏的色彩和咱们的运用布景色相同,看起来感觉状态栏和咱们的页面就像是一体的相同,有的做法是给标题栏设置一个透明的色彩,而本项目中运用的办法是将标题栏的色彩设置成和页面的布景色相同就能够了。设置标题栏的色彩需求用到一个库:

implementation "com.google.accompanist:accompanist-systemuicontroller:0.31.0-alpha"

引进这个库后,运用下面的办法设置状态栏的色彩:

    @Composable
    fun TransparentSystemBars(color: Color) {
        val systemUiController = rememberSystemUiController()
        val useDarkIcons = !isSystemInDarkTheme()
        SideEffect {
            systemUiController.setSystemBarsColor(
                color = color,
                darkIcons = useDarkIcons,
                isNavigationBarContrastEnforced = false,
            )
        }
    }

然后在对应的Activity中设置好主题,代码如下:

class MainActivity : ComponentActivity() {
    private lateinit var theme: BloomTheme
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        actionBar?.apply { hide() }
        setContent {
            theme = if(isSystemInDarkTheme()){
                BloomTheme.DARK
            }else {
                BloomTheme.LIGHT
            }
            GoogleBloomTheme(theme) {
                val color = MaterialTheme.colors.background
                TransparentSystemBars(color)
                // 展现界面,例如本项目中展现欢迎页:WelcomePage()
            }
        }
    }
}

如上面代码所示,咱们依据当时的系统是否是深色主题判断运用的主题款式,然后把运用的当时的布景色传给设置状态栏色彩的办法,其他页面也是相同的做法。

5.UI界面分化及完成

首先咱们能够先看下欢迎页:

Android Compose UI实战练手----Google Bloom欢迎页

咱们能够将这个页面看成是布景加上内容,然后咱们对显现的内容区分下,如下所示:

Android Compose UI实战练手----Google Bloom欢迎页

依据前面所学的常识,咱们能够运用一个Column组件搞定这个页面的内容布局。而布景和内容的结合,咱们能够运用Box组件,代码如下:

5.1 欢迎页布景+内容

运用Box组件将内容和布景融合到一同,内容咱们能够抽成一个组件持续去区分完成

@Composable
fun WelcomePage(){
    Box(modifier = Modifier
        .fillMaxSize()
        .background(MaterialTheme.colors.background)
    ){
        Image(
            painter = rememberVectorPainter(
                image = ImageVector.vectorResource(id = welcomeAssets.background)
            ),
            contentDescription = "welcome page",
            modifier = Modifier.fillMaxSize()
        )
        WelcomeContent()
    }
}

5.2 欢迎页内容组件完成

内容组件又包含了一张叶子款式的图片,标题和两个按钮,咱们能够别离抽成组件

@Composable
fun WelcomeContent(){
    Column(modifier = Modifier.fillMaxSize()) {
        Spacer(modifier = Modifier.height(72.dp))
        LeafImage()
        Spacer(modifier = Modifier.height(48.dp))
        WelcomeTitle()
        Spacer(modifier = Modifier.height(40.dp))
        WelcomeButtons()
    }
}

5.3 欢迎页内容的各个小组件完成

上面的WelcomeContent()函数即将显现的内容拆分成了三个更小的组件,完成如下所示:

@Composable
fun LeafImage(){
    Image(painter = rememberVectorPainter(
        image = ImageVector.vectorResource(id = welcomeAssets.illos)
    ),
        contentDescription = "welcome leaf image",
    modifier = Modifier
        .wrapContentSize()
        .padding(start = 88.dp))
}
@Composable
fun WelcomeTitle(){
    Column(horizontalAlignment = Alignment.CenterHorizontally,
    modifier = Modifier.fillMaxWidth()) {
        Image(painter = rememberVectorPainter(
            image = ImageVector.vectorResource(id = welcomeAssets.logo)),
            contentDescription = "welcome logo",
        modifier = Modifier
            .wrapContentSize()
            .height(32.dp))
        Box(modifier = Modifier
            .fillMaxWidth()
            .height(32.dp),
            contentAlignment = Alignment.BottomCenter){
            Text(text = "Beautiful home garden solutions",
            textAlign = TextAlign.Center,
            style = MaterialTheme.typography.subtitle1,
            color = MaterialTheme.colors.primary)
        }
    }
}
@Composable
fun WelcomeButtons(){
    Column(horizontalAlignment = Alignment.CenterHorizontally,
    modifier = Modifier.fillMaxWidth()) {
        Button(onClick = { /*TODO*/ },
        modifier = Modifier
            .height(48.dp)
            .padding(horizontal = 16.dp)
            .fillMaxWidth()
            .clip(MaterialTheme.shapes.medium),
            colors = ButtonDefaults.buttonColors(backgroundColor = MaterialTheme.colors.secondary)
        ) {
            Text(
                text = "Create account",
                style = MaterialTheme.typography.button,
                color = MaterialTheme.colors.onSecondary
            )
        }
        Spacer(modifier = Modifier.height(24.dp))
        TextButton(onClick = { /*TODO*/ }) {
            Text(
                text = "Log in",
                style = MaterialTheme.typography.button,
                color = MaterialTheme.colors.primary            )
        }
    }
}

至此,一个运用Compose完成的欢迎页就完成了。是不是很简单,代码也很少。

6.代码地址

主张读者将源码下载下来,跟着敲一遍,不明白的能够查官网,百度下,首要是为了了解怎么运用Compose的开发。有问题也能够在谈论区一同交流。

源码地址