class HelloViewModel:ViewModel() {
private val _name = MutableLiveData("")
val name : LiveData = _name
fun onNameChanged(newName:String){
_name.value = newName
}
}
MainActivity中
private val viewModel:HelloViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
Surface(color = MaterialTheme.colors.background) {
Column {
HelloScreen(viewModel)
}
}
}
}
}
@Composable
fun HelloScreen(viewModel: HelloViewModel){
val name:String by viewModel.name.observeAsState("")
HelloContent(name = name, onNmeChange = { viewModel.onNameChanged(it) })
}
@Composable
fun HelloScreen(){
var name = remember{ mutableStateOf("")}
HelloContent(name = name.value, onNmeChange = { name.value = it })
}
@Composable
fun HelloContent(name: String,onNmeChange:(String)->Unit){
Column(modifier = Modifier.padding(16.dp)) {
if(name.isNotEmpty()){
Text(text = “Hello,${name}”,
modifier = Modifier.padding(8.dp),
style = MaterialTheme.typography.h5)
}
OutlinedTextField(value = name, onValueChange = onNmeChange,label = {Text(“Name”)})
}
}
上面代码中 observeAsState可以观察LiveData,并返回State ,State Jetpack Compose 可以直接使用的可观察类型。前面说了observeAsState内部也是封装了remember函数。使用的时候需要引入下面的依赖
implementation “androidx.compose.runtime:runtime-livedata:1.0.0-beta01”
Jetpack Compose是通过各个组件的组合来描述一个UI界面,当应用的状态发生变化的时候,Jetpack Compose 会安排重组,重组就是重新执行可能因状态改变而发生变化的组件。重组是更新界面的唯一方式。
也就是说一个组合就可以代表一个界面,其内部的可组合项的生命周期就是:进入组合、执行0次或者多次重组、退出组合。
Compose中的重组一般是由State
接口触发,Compose会跟踪使用State
数据的可组合项,Compose在重组的时候只会更改发生变化的部分。
下面来了解一下常用的布局、列表、动画、手势等操作在Compose中的使用。
Compose中的布局最常用的有三个:Column、Row和Box
@Composable
fun LayoutTest(){
Column() {
Row(modifier = Modifier.padding(10.dp),verticalAlignment = Alignment.CenterVertically) {
Image(painter = painterResource(id = R.drawable.ic_launcher_background),
contentDescription = “头像”,
Modifier
.width(60.dp)
.height(60.dp)
.clip(RoundedCornerShape(50)))
Column(modifier = Modifier.padding(10.dp)) {
Text(text = “名字”)
Text(text = “2 minute ago”)
}
}
}
}
上面的代码轻松实现了左边头像右边竖向排列的两个文本的效果。
Modifier是修饰符,专门用来控制视图的各种属性如padding、offset、background、圆角、大小、外观、行为、互动等,属性有好多,用的时候直接查文档或者点进去看看源码
注意:modifier的顺序对结果也有影响,比如下面的代码中,padding在clickable前面,那么padding部分是不能点击的padding如果在clickable后面,那么整个Column布局都可以点击
@Composable
fun ArtistCard(name: String,onClick: () -> Unit){
Column(modifier = Modifier
.padding(16.dp) //设置16dp的padding
.clickable(onClick = onClick) //让改控件拥有点击属性和点击水波纹效果
.fillMaxWidth() //宽度填充父控件
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Spacer(Modifier.size(20.dp)) //设置一个20dp的空间占位 宽高都是20的正方形
Card(elevation = 4.dp) {
Image(painter = painterResource(id = R.drawable.img_bg_2), contentDescription = “pic”)
}
}
}
}
如果想要Column或者Row可以滚动,直接添加下面的属性就可以了。Modifier.verticalScroll(rememberScrollState())
或者horizontalScroll(rememberScrollState())
Modifier.size 可以设置一个容器的宽高 其子布局默认不能超出他的范围,如果想要子布局可以超出它的范围,子布局可以使用Modifier.requiredSize方法就可以了
Box(
Modifier
.size(90.dp, 150.dp)
.background(Color.Green)) {
Box(
Modifier
.requiredSize(100.dp, 100.dp)
.background(Color.Red)) {
}
}
也可以使用weight属性按比例分布局的大小入下
@Composable
fun FlexibleComposable() {
Row(Modifier.fillMaxWidth()) {
Box(
Modifier
.weight(2f)
.height(50.dp)
.background(Color.Blue))
Box(
Modifier
.weight(1f)
.height(50.dp)
.background(Color.Red))
}
}
除了前面的三个布局容器,如果想要在Compose中的使用相对布局,可以使用ConstraintLayout,在实现对其方式比较复杂的布局时比较有用。
虽然在用xml布局的时候为了更好的减少布局层级,推荐优先使用ConstraintLayout布局,不过在Compose中不用担心布局层级的问题,所以创建布局时还是首选Column、Row、Box
使用constraintlayout的时候需要单独引入
implementation “androidx.constraintlayout:constraintlayout-compose:1.0.0-alpha03”
下面的代码实现了一个text在button的下面,距离button底部16dp
@Composable
fun ConstraintLayoutContent() {
ConstraintLayout {
// 给可组合对象创建引用
val (button