刚开始接触 kotlin + Jetpack Compose 开发 Android应用, 这里记录下自己的理解
建议看官方中文文档:
https://developers.google.cn/codelabs/jetpack-compose-basics#0
https://developer.android.google.cn/codelabs/jetpack-compose-state#0
1. 概念
1.1 Jetpack Compose 是Android中一个取代xml的UI框架, 与Kotlin语法配合使用, Compose隶属于Jetpack
1.2 Volley 是一个轻量级的Http请求库, 谷歌官方的, 使用起来比较简单(听说适合纯数据请求, 不太适合图片请求)
1.3 ViewModel 保存数据用, 用来把volley从服务端请求的数据, 传递给compose在页面中显示出来
1.4 ViewModel + Compose的最佳实践是, 将数据的获取逻辑和UI的渲染分开在两个文件中编写, 数据有变化, UI自动变化
1.5 Json解析使用了官方的 json.org 库, 没有使用gson
2. 代码细节
2.1 Compose:
2.1.1 用Android Studio 创建应用的时候, 要选择 "Compose Activity", 这样才能使用Compose相关特性
2.1.2 Compose 中列表UI是 Column(){} 其中小括号中声明这个列表的样式, 花括号中写子控件(其他UI控件也是这个规律), 比如列表中有文本控件(Text(){}) 或 按钮控件(Button(){}) 或 行控件(Row(){})
2.1.3 可以将不同的控件写在一个函数中, 在MainActivity::onCreate()中被调用, 这个函数前边必须用 @Composable 注解
2.1.4 代码举例
@Composable
fun Greeting(name: String) {
Column(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
.background(Color.LightGray)
.padding(5.dp)
, verticalArrangement = Arrangement.Top
, horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Hello $name! 111", Modifier.padding(5.dp).background(Color.White).border(1.dp, Color.Green, RoundedCornerShape(4.dp)))
Text(text = name, Modifier.padding(5.dp), fontSize = 18.sp, maxLines = 2, overflow = TextOverflow.Ellipsis, fontWeight = FontWeight.Bold)
Divider(color = Color.Black)
Button(onClick = {}) {
Text(text = "点击")
}
}
}
2.2 Volley
2.2.1 他获取数据的时候, 必须传入上下文, 所以要在 MainActivity中调用并把 this传给他 (不知道还有没有其他调用方式)
2.2.2 用他获取数据, 以及处理数据的代码最好是写在ViewModel类中
2.3 ViewModel
2.3.1 需要新建一个MyxxxModel 继承自 ViewModel; 注意Kotlin继承的两种写法
1. calss MyModel : ViewModel {}
2. class MyModel : ViewModel() {}
(一个有小括号, 一个没有. 若没有小括号, 就得手工调用父类的构造方法)
2.3.2 用来存储服务端数据的成员变量(假如叫: data)要定义为 MutableStat 或 MutableStatList 前一个不是列表, 后一个可以放列表数据, 这样当这个数据改变的时候, UI才会跟着改变
2.3.3 代码举例:
public class NewsListModel : ViewModel() {
public var data = mutableStateListOf<News>(); //UI中的数据来源
//获取远程数据,给 data 赋值
public fun setData(ctx: Context) {
val url = "https://xxx.com/json_data";
val queue = Volley.newRequestQueue(ctx);
val stringRequest = StringRequest(
url,
Response.Listener<String>{ response ->
val obj = JSONObject(response)
//Log.i("111-msg", obj.getString("msg"))
//Log.i("111-msg", obj.getString("code"))
val arr = obj.getJSONArray("data");
for (i in 0 until arr.length()) {
val item = arr.getJSONObject(i);
data.add(News(item.getString("title"), item.getString("desc"))); //News类的定义在下边
}
},
Response.ErrorListener { Log.i("1111", "error") }
)
queue.add(stringRequest); //通过volley获得服务端数据
}
}
data class News(val title:String, val desc:String); //就这样写, 不用写花括号
2.3.4 在MainActivity中初始化ViewModel获取数据, 这样写:
class MainActivity : xxxx () {
override fun onCreate(xxx) {
.....
val vm:NewsListModel = viewModel(); //单例写法
vm.setData(this) //这个方法用Volley去请求服务端数据, 然后给vm.data赋值
.....
}
}
2.4 最后, 在UI控件中与ViewModel绑定
@Composable
fun NewsList(vm:NewsListModel = viewModel()) {
Column() {
vm.data.forEach {
Row() {
Text(text = "${it.title} : ${it.desc}")
}
}
}
}
2.4.1 此方法会在MainActivity中setContent()直接或间接调用, 调用时, 不用传入viewModel参数, 系统会自动实例化vm
2.4.2 调用了被@Composable注解的方法也必须有@Composable注解
2.4.3 其中的it, 是forEach这个lamda方法的默认参数, 代指循环中的每一个item