Jetpack Compose实战教程(三)

Jetpack Compose实战教程(三)

第三章 实现一个带接口数据请求的界面



一、本章目标

在这里插入图片描述
功能还是很简单,界面上头部一个横向布局,加上一个请求接口之后得到的列表,那么直接开干。

友情提醒,如果各位看官有不懂的代码可以先看一下之前的章节,循序渐进,如果还是有不懂的,可以给我留言

二、开始编码

2.1 先把基本布局写好

class IncomeActivity: ComponentActivity() {
	override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
        WindowCompat.setDecorFitsSystemWindows(window, false)
        setContent {
            contentUI()
        }
    }

	@Composable
    fun contentUI(){
        BaseTheme {
            Surface(
                modifier = Modifier.fillMaxSize(),
                color = Color.Black
            ) {
                Box(Modifier.fillMaxSize()) {
                    Image(
                        painter = painterResource(id = R.mipmap.ic_common_bg),
                        contentDescription = null,
                        Modifier
                            .fillMaxWidth()
                            .fillMaxHeight()
                    )
                    Column {
                        Spacer(
                        //这里的DimenUtil和StatusBarUtils都是工具类,px2dp就是将px转dp的
                        //getStatusBarHeight就是获取状态栏的高度,大家可以自行百度找代码
                            modifier = Modifier.height(
                                DimenUtil.px2dp(this@IncomeActivity,
                                    StatusBarUtils.getStatusBarHeight(this@IncomeActivity).toFloat()).dp)
                        )
                        title()
                        content()
                    }
                }
            }
        }
    }

	@Composable
    fun title(){
        Box(Modifier.fillMaxWidth()){
            Image(painter = painterResource(id = R.mipmap.ic_back_white2),
                null,
                modifier = Modifier
                    .size(48.dp)
                    .padding(12.dp)
                    .clickable {
                        finish()
                    }
            )

            Text(
                text = "收益明细",
                color = Color(0xffffffff),
                fontSize = 18.sp,
                fontWeight = FontWeight.Bold,
                modifier = Modifier.align(alignment = Alignment.Center)
            )
        }
    }

	@Composable
    fun content(){
        Column(
            Modifier
                .padding(16.dp, 16.dp, 16.dp, 0.dp)
                .fillMaxSize()
                .background(Color.Transparent)) {
        }
    }

	@Preview(showBackground = true)
    @Composable
    fun previewUI(){
        contentUI()
    }
}

运行结果如图:
在这里插入图片描述

2.2 编写列表布局

//将所有的数据存放到一个列表里面
private val mData = mutableListOf<UnionIncomeBean>()

	@Composable
    fun setListData(){
    LazyColumn(
            Modifier
                .fillMaxSize()
                .background(Color.Transparent)
                ) {
                //遍历数据列表
            items(mData.size) {
                Column {
                    Box(
                        Modifier
                            .fillMaxWidth()
                            .height(113.dp)
                            .padding(start = 16.dp, end = 16.dp)){
                            //这里下面的Image,我是根据UI效果,自己自定义实现的View,所以用了一下这种写法,pixelBlockDrawable的代码我就不贴了,各位看官可以直接用一张灰白色图片替代
                        Image(bitmap = drawableToBitamp(pixelBlockDrawable,
                            DimenUtil.getScreenWidth(this@IncomeActivity)-
                                    DimenUtil.dp2px(this@IncomeActivity,32f),
                            DimenUtil.dp2px(this@IncomeActivity,113f)),
                            contentDescription = "" ,
                            modifier = Modifier.fillParentMaxSize())
                            //这里加载网络图片,我用到了coil的compose,如果发现AsyncImage没法使用的
                            //请先在build.gradle下面添加coil的compose依赖:api 'io.coil-kt:coil-compose:2.4.0'
                        AsyncImage(model = ImageRequest.Builder(LocalContext.current)
                            .data(mData[it].img)
                            .transformations(RoundedCornersTransformation(DimenUtil.dp2px(this@IncomeActivity,8f).toFloat()))
                            .scale(Scale.FILL)
                            .build(), contentDescription = "",
                            Modifier
                                .padding(top = 12.dp)
                                .width(56.dp)
                                .height(56.dp)
                        )

                        Text(text = mData[it].name, Modifier
                            .padding(top = 15.dp,start = 68.dp),
                            color = Color(0xffffffff),
                            fontSize = 16.sp
                        )

                        Text(text = "ID: ${mData[it].uid}",
                            Modifier.padding(start = 68.dp,top = 45.dp),
                            color = Color(0x99ffffff),
                            fontSize = 14.sp)

                        Row(
                            Modifier
                                .fillParentMaxWidth()
                                .padding(start = 12.dp, top = 80.dp)){

                            Text(text = "今日流水:",
                                color = Color(0x99ffffff),
                                fontSize = 12.sp
                            )

                            Text(text = mData[it].todayIncome,
                                Modifier.padding(start = 5.dp),
                                color = Color(0xffaffba1),
                                fontSize = 12.sp
                            )

                            Text(text = "今日流水:",
                                Modifier.padding(start = 16.dp),
                                color = Color(0x99ffffff),
                                fontSize = 12.sp
                            )

                            Text(text = mData[it].yesterdayIncome,
                                Modifier.padding(start = 5.dp),
                                color = Color(0xffaffba1),
                                fontSize = 12.sp
                            )
                        }

                    }
                    //每条渲染完毕之后,加个间距
                    Spacer(modifier = Modifier.height(12.dp))
                }
                
            }
        }
    }

然后将这个布局添加到内容中去

	@Composable
    fun content(){
        Column(
            Modifier
                .padding(16.dp, 16.dp, 16.dp, 0.dp)
                .fillMaxSize()
                .background(Color.Transparent)) {
            setListData()
        }
    }

接着我们请求接口获取一下数据,这里我模拟了请求接口,就用假数据生成了:

fun getData(){
        for(i in 1..10){
            mData.add(UnionIncomeBean("xxxxxx图片地址","测试房间$i",1183301+i,
            "9999$i","4444$i"))
        }
    }
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
        WindowCompat.setDecorFitsSystemWindows(window, false)
        setContent {
            contentUI()
        }
		//在onCreate里面获取初始数据就可以了,发现了compose的好处了吗?先生成的UI,
		//UI里面遍历了mData,那么只要mData的值改变,UI就会自动改变,这就是声明式UI
        getData()
    }

运行结果如图:
在这里插入图片描述

2.3 加载更多数据

上面显示了10条数据,但实际我们的开发场景中,往往会有更多数据,这里就像是分页,每页获取10条,当滑动到底部的时候,我们就得加载下一页,那么我们来改动一下代码。

//首先,将mutableListOf 改成mutableStateListOf,用于可记录数据状态
private val mData = mutableStateListOf<UnionIncomeBean>()

接着,在我们的布局列表里面使用状态

@Composable
    fun setListData(){
    	//创建一个可记录状态的参数
        val listState = rememberLazyListState()
        //设置滑动监听事件
        val scrollListener = remember {
            object :NestedScrollConnection{
                override fun onPostScroll(
                    consumed: Offset,
                    available: Offset,
                    source: NestedScrollSource
                ): Offset {
//当第一条可见的下标 + 界面上一共可见的item数量 >总数量-2时,就去再获取下一页的数据
//这里的判断基本表现为是滑动到倒数第三条时开始加载下一页
                    if(listState.firstVisibleItemIndex+listState.layoutInfo.visibleItemsInfo.size>mData.size-2){
                        getData()
                    }
                    return super.onPostScroll(consumed, available, source)
                }
            }
        }
        LazyColumn(
            Modifier
                .fillMaxSize()
                .background(Color.Transparent)
                .nestedScroll(scrollListener), //这里添加监听
            state = listState //记录状态
                ) {
                //.....上面的那些代码
                }
}

运行结果如图:

在这里插入图片描述
可以看到我们已经可以无限加载数据了

2.4 没有更多数据

我们的数据总有加载完的一刻,那么当数据加载完毕之后,上下滚动一下列表又会去继续请求接口,这肯定不行,用户也没法感知没有更多数据了,所以我们要再改进一下代码

首先,我们添加一个参数来记录是否还有数据:

private var isNoMoreDataShow = mutableStateOf(false)

然后,我们为没有更多数据,添加一个UI布局,并且加入参数判断

@Composable
    fun setListData(){
    	LazyColumn(
            Modifier
                .fillMaxSize()
                .background(Color.Transparent)
                .nestedScroll(scrollListener),
            state = listState
                ) {
                items(mData.size){
                //....上面已经有了,省略掉这些代码
				}

if(isNoMoreDataShow.value) { //当我们上面记录的状态为true,代表没有更多数据了
                item {
                    Box(Modifier.fillMaxWidth().height(40.dp)) {
                        Text(
                            text = "没有更多数据啦",
                            modifier = Modifier.align(alignment = Alignment.Center),
                            color = Color(0x99ffffff),
                            fontSize = 16.sp
                        )
                    }
                }
            }

                }
    }

接下来我们假设加载完第二页就没有数据了

fun getData(){
//当数据达到20条之后,就不加载更多数据了,并且将isNoMoreDataShow设置为true
        if(mData.size>=20){ 
            isNoMoreDataShow.value =true
            return
        }
        for(i in 1..10){
            //省略代码
        }
    }

运行结果如图:
在这里插入图片描述
至此,我们实现了一个开发过程中常见的请求接口 并且列表显示数据的代码逻辑

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Jetpack Compose是一个全新的Android UI工具包,可以帮助开发者更快速、更简单地构建Android应用程序的用户界面。以下是一个Jetpack Compose实战项目的简介: 项目名称:Compose Countdown Timer 项目描述:这是一个基于Jetpack Compose的倒计时计时器应用程序。用户可以设置计时器的时间,并在计时器倒计时时观看动画。 实现步骤: 1.创建一个Compose项目并添加所需的依赖项。 2.创建一个计时器组件,该组件将显示计时器的当前时间,并在计时器倒计时时触发动画。 3.创建一个设置计时器时间的组件,该组件将允许用户设置计时器的时间。 4.将计时器组件和设置时间组件组合在一起,以创建一个完整的倒计时计时器应用程序。 代码示例: ```kotlin @Composable fun CountdownTimer() { var time by remember { mutableStateOf(0) } var isRunning by remember { mutableStateOf(false) } LaunchedEffect(isRunning) { while (isRunning && time > 0) { delay(1000) time-- } } Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { if (time > 0) { Text( text = time.toString(), fontSize = 60.sp, fontWeight = FontWeight.Bold ) } else { Text( text = "Time's up!", fontSize = 60.sp, fontWeight = FontWeight.Bold ) } } } @Composable fun SetTime(onTimeSelected: (Int) -> Unit) { var time by remember { mutableStateOf(0) } Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally ) { Text( text = "Set time", fontSize = 24.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(vertical = 16.dp) ) Row( modifier = Modifier.padding(vertical = 16.dp) ) { Text( text = "Minutes:", fontSize = 18.sp, modifier = Modifier.padding(end = 8.dp) ) OutlinedTextField( value = time.toString(), onValueChange = { time = it.toIntOrNull() ?: 0 }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), modifier = Modifier.width(100.dp) ) } Button( onClick = { onTimeSelected(time) }, modifier = Modifier.padding(vertical = 16.dp) ) { Text(text = "Start timer") } } } @Composable fun ComposeCountdownTimer() { var time by remember { mutableStateOf(0) } var isRunning by remember { mutableStateOf(false) } if (time == 0) { SetTime(onTimeSelected = { selectedTime -> time = selectedTime * 60 isRunning = true }) } else { CountdownTimer() } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值