Compose 组件学习(一)

Android Compose自推出正式版本后,google 就一直推荐使用Compose来开发。正好疫情期间,作为一个 Android 摸鱼达人,就来摸索一下Compose的开发。说实话开发了2天感觉对Android 开发人员来说变化是巨大的,但是作为从业者我们还必须学习和学会,才能不被甩开。
学习使用 Compose 我们需要坐什么呢?
1.使用 Kotlin
2.使用Android Studio 最新版本开发工具
3.去官网查看教程,跟着google 一步一个脚印的学习->官方文档速转通道

4.找其他开发者的文档,更详细更效率的学习

下载工具

使用 Android Studio Bumblebee 大黄蜂🐝版本,如果不想影响老版本 Android Studio 的运行项目,可以下载 zip 的版本。AS尽可能升级到最新版,最少也要Arctic Fox,老版本无法实现 Compose 的预览和开发
官方下载地址:https://developer.android.google.cn/studio
快速下载地址:https://www.androiddevtools.cn/
下载后创建 Compose 项目,如果你电脑配置的 java为11,那么就没问题。如果出现问题如下:

Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8.
You can try some of the following options:

  • changing the IDE settings.
  • changing the JAVA_HOME environment variable.
  • changing org.gradle.java.home in gradle.properties.
    Gradle settings
    解决方案为:
    1.更新 java 到11,完美。虽然你根本用不到11
    2.到系统设置中Preferences -> Build Tools ->Gradle , 选择 Gradle JDK 为11

Compose项目构成

在项目可以正式运行后,我们可以先打开MainActivity,继承ComponentActivity()包含了setContent()方法,如果去除setContent内的代码运行可以看到一个空页面。
然后我们添加一个文本控件Text,对应原生的TextView。

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Text("Hello world!")
        }
    }
}

项目运行起来后我们看下整个项目的代码是怎么样的。

Compose 的引入

buildscript {
    ext {
        compose_version = '1.0.1'
    }
}
//compose 核心
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"

另外 google 提供了非常多的compose 库,具体查看可以到https://developer.android.google.cn/jetpack/androidx/explorer查看。


 

初识组件

Compose 内部的组件非常少,但是合理的搭配可以做到比安卓原生更好的开发体验。大体如下:

分类Compose原生
文本TextTextView
文本TextFieldEdittext
图片IconImageView
图片ImageImageView
ButtonButtonButton
ButtonIconButtonButton
ButtonIconToggleButtonCheckBox,Switch
ButtonSwitchSwitch
ButtonRadioButtonRadioButton
ButtonCheckboxCheckBox
布局BoxFrameLayout
布局ColumnLinearLayout
布局RowLinearLayout
布局Scaffold
布局ConstraintlayoutConstraintlayout
列表LazyColumnRecyclerView,ListView
列表LazyRowRecyclerView,ListView
间距类SpacerSpace(估计很多人都没用过)

我们直接在setContent添加个 Text 和 Image 控件,下面代码就是最简单的写法。
Text、Image应该是我们最常用的控件,特别是 Text 可实现的功能特别多,具体可以去看后续文章或者看下其他博客。

 setContent {
            Text("Hello world!")
            Image(
                      painter = painterResource(R.drawable.share_calc),
                      contentDescription = null
                )
}

上面的展示代码会使内容重叠,需要使用 Row 或者 Column。
按照下面展示,我们添加了Column垂直布局,控件就往下绘制不重叠了。

Row 、Column、Box

Column 垂直布局 -- 对用 LinearLayout 的android:orientation="vertical";Row 水平布局 -- 对用 LinearLayout 的android:orientation="horizontal";Box接近FrameLayout,具体使用可以看下图的例子.
Compose 中的三个基本标准布局元素是 Column、Row 和 Box 可组合项。

Column(){
Text("Hello world!")
Image(
            painter = painterResource(R.drawable.share_calc),
            contentDescription = null
        )
}
Row(){
Text(" Row W")
Image(
            painter = painterResource(R.drawable.share_calc),
            contentDescription = null
        )
}
Box(
            modifier = Modifier
                .background(Color.Cyan)
                .size(180.dp)
        ) {
            Box(
                modifier = Modifier
                    .align(Alignment.TopCenter)
                    .size(60.dp, 60.dp)
                    .background(Color.Red)
            ) {
            }
            Box(
                modifier = Modifier
                    .align(Alignment.BottomEnd)
                    .size(60.dp, 60.dp)
                    .background(Color.Blue)
            ) {

            }
        }

 

Scaffold脚手架和Constraintlayout布局

Scaffold脚手架我们先看下源码,可以看到包含了topBar、bottomBar、floatingActionButton等控件的页面。这是个固定布局,当我们的首页符合该页面固定样式时,可以直接使用该布局。
Constraintlayout布局和Android 原生一样

@Composable
fun Scaffold(
    modifier: Modifier = Modifier,
    scaffoldState: ScaffoldState = rememberScaffoldState(),
    topBar: @Composable () -> Unit = {},
    bottomBar: @Composable () -> Unit = {},
    snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
    floatingActionButton: @Composable () -> Unit = {},
    floatingActionButtonPosition: FabPosition = FabPosition.End,
    isFloatingActionButtonDocked: Boolean = false,
    drawerContent: @Composable (ColumnScope.() -> Unit)? = null,
    drawerGesturesEnabled: Boolean = true,
    drawerShape: Shape = MaterialTheme.shapes.large,
    drawerElevation: Dp = DrawerDefaults.Elevation,
    drawerBackgroundColor: Color = MaterialTheme.colors.surface,
    drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
    drawerScrimColor: Color = DrawerDefaults.scrimColor,
    backgroundColor: Color = MaterialTheme.colors.background,
    contentColor: Color = contentColorFor(backgroundColor),
    content: @Composable (PaddingValues) -> Unit
) 

TextField文本输入控件

@Composable
fun InputText() {
    val input = remember { mutableStateOf("") }
    TextField(value = input.value, onValueChange = { input.value = it })
}

Icon 和 Image图片类

      Row {
            Icon(painter = painterResource(R.drawable.database_set), contentDescription = null)
            Spacer(modifier = Modifier.width(8.dp))
            Image(
                painter = painterResource(R.drawable.share_calc),
                contentDescription = null
            )    
        }

 

Button 类

Compose Button多种多样,具体的大家可以先看 UI 展示,就能明白在具体场景下使用什么 button。估计是参考的H5前端的按钮样式
Button 就是带背景色的按钮,OutlinedButton是带边框绘制的,TextButton是无背景色的按钮。其他都可以参考原生的样式

Row() {
            //普通按钮
            Button(onClick = { /*TODO*/ }) {
                Text(text = "Button")
            }
            Spacer(modifier = Modifier.width(8.dp))
            //带边框绘制的 button
            OutlinedButton(onClick = { /*TODO*/ }) {
                Text(text = "OutlinedButton")
            }
            Spacer(modifier = Modifier.width(8.dp))
            //纯文字按钮
            TextButton(onClick = {}) {
                Text("TextButton")
            }
        }
Row() {
            //带Icon的按钮
            //圆形水波纹和 Checkbox,Switch 水波纹效果一致,
            //android 可以看下设置android:background="?attr/selectableItemBackgroundBorderless"("?attr/actionBarItemBackground")的效果
            IconButton(modifier = Modifier.width(120.dp), onClick = { /*TODO*/ }) {
                Row() {
                    Icon(
                        painter = painterResource(id = R.drawable.database_set),
                        contentDescription = null
                    )
                    Spacer(modifier = Modifier.width(8.dp))
                    Text("IconButton")
                }
            }
            //图标切换 button,类似于switch,但是更接近于CheckBox
            //圆形水波纹和Checkbox,Switch 水波纹效果一致,
            //android 可以看下设置android:background="?attr/selectableItemBackgroundBorderless"("?attr/actionBarItemBackground")的效果
            IconToggleButton(checked = false, onCheckedChange = {}) {
                Text(text = "IconToggleButton")
            }
            //悬浮按钮
            ExtendedFloatingActionButton(text = { Text(text = "FAB") }, onClick = { })
        }

RadioButton\Checkbox和原生对应,但是没 text 属性,如果需要显示文本需要和 Text 控件组合使用

       RadioDefault()
        RadioText()
        RadioButtonGroup()
        Row() {
            CheckboxDefault()
            SwitchDefault()
        }

@Composable
fun RadioDefault() {
    val isSelected = remember { mutableStateOf(false) }
    RadioButton(selected = isSelected.value, onClick = {
        isSelected.value = !isSelected.value
        Log.e(TAG, "默认Checkbox,同RadioButton不带text文本控件")
    })
}

@Composable
fun RadioText() {
    val isSelected = remember { mutableStateOf(false) }
    // 如果需要 text 需要和 Text 组合使用,click处理在row上
    Row(modifier = Modifier.clickable {
        isSelected.value = !isSelected.value
    }) {
        RadioButton(
            selected = isSelected.value, onClick = null,
            colors = RadioButtonDefaults.colors(
                selectedColor = MaterialTheme.colors.primary,
                unselectedColor = MaterialTheme.colors.error,
                disabledColor = MaterialTheme.colors.secondary
            )
        )
        Text("选项②")
    }
}
@Composable
fun RadioButtonGroup() {
    val options = listOf("选项③", "选项④", "选项⑤", "选项⑥")
    val selectedButton = remember { mutableStateOf(options.first()) }
    //RadioButton 不带 text 文本控件,
    Row() {
        options.forEach {
            val isSelected = it == selectedButton.value
            Row(verticalAlignment = Alignment.CenterVertically,
                modifier = Modifier.clickable {
                    selectedButton.value = it
                }) {
                RadioButton(
                    selected = isSelected, onClick = null,
                )
                Text(it, textAlign = TextAlign.Justify)
            }
        }
    }
}

@Composable
fun CheckboxDefault() {
    val isSelected = remember { mutableStateOf(false) }
    Checkbox(checked = isSelected.value, onCheckedChange = {
        isSelected.value = it
        Log.e(TAG, "默认Checkbox,同RadioButton不带text文本控件")
    })
}

@Composable
fun SwitchDefault() {
    val isSelected = remember { mutableStateOf(false) }
    Switch(checked = isSelected.value, onCheckedChange = {
        isSelected.value = it
        Log.e(TAG, "默认Checkbox,同RadioButton不带text文本控件")
    })
}

定义一个带 Text 的 RadioButton

@Composable
fun RadioText() {
    val isSelected = remember { mutableStateOf(false) }
    // 如果需要 text 需要和 Text 组合使用,click处理在row上
    Row(modifier = Modifier.clickable {
        isSelected.value = !isSelected.value
    }) {
        RadioButton(
            selected = isSelected.value, onClick = null,
            colors = RadioButtonDefaults.colors(
                selectedColor = MaterialTheme.colors.primary,
                unselectedColor = MaterialTheme.colors.error,
                disabledColor = MaterialTheme.colors.secondary
            )
        )
        Text("选项②")
    }
}

Compose 无RadioGroup 组件,如果需要实现RadioGroup也需要组合使用
定义 RadioGroup 组件,在循环中改变状态

@Composable
fun RadioButtonGroup() {
    val options = listOf("选项③", "选项④", "选项⑤", "选项⑥")
    val selectedButton = remember { mutableStateOf(options.first()) }
    //RadioButton 不带 text 文本控件,
    Row() {
        options.forEach {
            val isSelected = it == selectedButton.value
            Row(verticalAlignment = Alignment.CenterVertically,
                modifier = Modifier.clickable {
                    selectedButton.value = it
                }) {
                RadioButton(
                    selected = isSelected, onClick = null,
                )
                Text(it, textAlign = TextAlign.Justify)
            }
        }
    }
}

同理Checkbox和Switch控件就不细说了。对应的参数改成了checked和onCheckedChange,也是没有 text 属性,需要组合使用。

@Composable
fun CheckboxDefault() {
    val isSelected = remember { mutableStateOf(false) }
    Checkbox(checked = isSelected.value, onCheckedChange = {
        isSelected.value = it
        Log.e(TAG, "默认Checkbox,同RadioButton不带text文本控件")
    })
}
@Composable
fun SwitchDefault() {
    val isSelected = remember { mutableStateOf(false) }
    Switch(checked = isSelected.value, onCheckedChange = {
        isSelected.value = it
        Log.e(TAG, "默认Checkbox,同RadioButton不带text文本控件")
    })
}

LazyColumn、LazyRow

LazyColumn 和 LazyRow 相当于 Android View 中的 RecyclerView。它们只会渲染屏幕上可见的内容,从而在渲染大型列表时提升效率。

注意:LazyColumn 不会像 RecyclerView 一样回收其子级。它会在您滚动它时发出新的可组合项,并保持高效运行,因为与实例化 Android Views 相比,发出可组合项的成本相对较低。

@Composable
fun RecyclerView(names: List<String> = List(1000) { "$it" }) {
    LazyColumn() {
        items(items = names) { item ->
            MessageCard(item)
        }
    }
}

@Composable
fun MessageCard(msg: String) {
    var isExpanded by remember { mutableStateOf(false) }
    val surfaceColor: Color by animateColorAsState(
        if (isExpanded) MaterialTheme.colors.primary else MaterialTheme.colors.surface,
    )
    Row(modifier = Modifier
        .fillMaxWidth()
        .clickable {
            isExpanded = !isExpanded
        }
        .padding(8.dp)
    ) {
        Image(
            painter = painterResource(R.drawable.share_calc),
            contentDescription = null,
            modifier = Modifier
                .size(40.dp)
                .clip(CircleShape)
                .border(1.5.dp, MaterialTheme.colors.secondaryVariant, CircleShape)
        )
        Spacer(modifier = Modifier.width(8.dp))
        // We toggle the isExpanded variable when we click on this Column
        Column() {
            Text("当前索引:")
            Spacer(modifier = Modifier.height(4.dp))
            Surface(
                shape = MaterialTheme.shapes.medium,
                elevation = 1.dp,
                // surfaceColor color will be changing gradually from primary to surface
                color = surfaceColor,
                // animateContentSize will change the Surface size gradually
                modifier = Modifier
                    .animateContentSize()
                    .padding(1.dp)
            ) {
                Text(
                    text = "索引为--------> $msg ,这是一个可展开和关闭的 Text 控件:" +
                            "微凉的晨露 沾湿黑礼服\n" +
                            "石板路有雾 父在低诉\n" +
                            "无奈的觉悟 只能更残酷\n" +
                            "一切都为了通往圣堂的路",
                    modifier = Modifier.padding(all = 4.dp),
                    maxLines = if (isExpanded) Int.MAX_VALUE else 1,
                    style = MaterialTheme.typography.body2
                )
            }
        }
    }
}

这边定义了一个内容为1000行的 List,使用LazyColumn包裹,同时在 MessageCard 中对点击坐了处理,点击后放开 Text 的行数限制。具体效果如下

 

这篇博客只是只是展示了控件的简单使用,快速的了解控件的创建及展示效果。
下一篇我们介绍如何跳转页面。 

 



作者:丿歌吟有梦
链接:https://www.jianshu.com/p/a86e86473421
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值