从json动态配置compose布局

需求

首页内容可动态变化,需要从json读取布局配置,然后再组织内容

项目参考
参考地址

json 结构设计

例如将整个区域,4*3(横屏)个单元格。

{
  "listItems": [
    {
      "type": "COLUMN",
      "listItems": [
        {
          "type": "CLASSINFO",
          "wCell": 2,
          "hCell": 1
        },
        {
          "type": "CLASSGALLERY",
          "wCell": 2,
          "hCell": 2
        }
      ],
      "wCell": 2,
      "hCell": 3
    },
    //...略
 }

listItems:表示该区域包含的子模块,自由第一层和 type是 COLUMN或者 ROW的模块才有该属性。
上述json表示,该区域,包含一个类型为column的区域,column的大小为 23,column里面的又包含两个子区域,一个是 CLASSINFO,大小 21,另外一个 CLASSGALLERY,大小2*2。

解析json文件

那我们怎么解析这个文件呢,就需要导入 kotlin友好的现代 JSON 库 moshi。

1. 导入依赖

kapt 'com.squareup.moshi:moshi-kotlin-codegen:1.13.0'

 implementation "com.squareup.moshi:moshi:1.13.0"
    implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
    implementation "com.squareup.moshi:moshi-adapters:1.13.0"

2. 读取文件

fun readAssetsFile(context: Context, fileName: String): String {
    try {
        val inputStream = context.assets.open(fileName)
        val filelength = inputStream.available()
        val byteArray = ByteArray(filelength)
        inputStream.read(byteArray)
        inputStream.close()
        return String(byteArray, Charsets.UTF_8)

    } catch (e: Exception) {
        e.printStackTrace()
    }
    return "read json file error"
}

3. 解析string

fun loadHomeComponents(jsonComponents: String): HomeComponent? {

    val moshi = Moshi.Builder()
        .add(
            PolymorphicJsonAdapterFactory.of(ComponentItem::class.java, "type")
               
                .withSubtype(
                    Row::class.java,
                    ComponentType.ROW.name
                )
                .withSubtype(
                    Column::class.java,
                    ComponentType.COLUMN.name
                )
               
                .withSubtype(
                    ClassInfoCard::class.java,
                    ComponentType.CLASSINFO.name
                )
                .withSubtype(
                    ClassGalleryCard::class.java,
                    ComponentType.CLASSGALLERY.name
                )
                .....扩展
        )
        .add(KotlinJsonAdapterFactory())
        .build()

    val adapter: JsonAdapter<HomeComponent> = moshi.adapter(HomeComponent::class.java)
    return adapter.fromJson(jsonComponents)
}

其中

class HomeComponent(val listItems: List<ComponentItem>)

sealed class ComponentItem(
    val type: ComponentType,
    var weight: Float = 0f,
    var parent: ComponentType,
    var wCell: Int = 1,
    var hCell: Int = 1,
    var parentCell: Int = 1,
    var isEnd: Boolean = false
)

enum class ComponentType {
    CARD,//空白卡片
   
    CLASSGALLERY,//班级相册
    CLASSINFO,//班级信息
    
    COLUMN,//列容器,容器中的内容排成一列
    ROW,//行容器,容器中的内容排成一行
}

根据 HomeComponent的内容解析 Json文件里面的内容是根据ComponentItem定义的属性来解析的,其中,解析到 type的时候,添加withSubtype,不断扩展,根据类型名称,找到对应的 ClassInfoCard,和 ClassGalleryCard等。

这样就将json里面的内容读取到 HomeComponent中了。接下来就是根据 HomeComponent来进行内容布局。

4. 遍历布局

         Row(
            modifier = Modifier.fillMaxSize(),
            horizontalArrangement = Arrangement.Start,
            verticalAlignment = Alignment.Top
        ) {

            loadHomeComponents?.listItems?.map {
                ConstructComponent(it, navController,modifier = Modifier.weight(it.weight))
            }
        }

5. 根据布局类型进行构建

@Composable
fun ConstructComponent(
    componentItem: ComponentItem,
    navController: NavHostController,
    modifier: Modifier = Modifier
) {


    when (componentItem.type) {
        ComponentType.COLUMN -> {
            val column = componentItem as Column
            Column(
                modifier = modifier
            ) {
                for (item in column.listItems) {
                    ConstructComponent(item, navController, createModifier(item))
                }
            }
        }

        ComponentType.ROW -> {
            val row = componentItem as Row
            Row(
                modifier = modifier
            ) {
                for (item in row.listItems) {
                    ConstructComponent(item, navController, createModifier(item))
                }
            }
        }

        ComponentType.CARD -> {
            val baseCard = componentItem as BaseCard
            BaseCardView(
                fillSizeModifier(modifier, baseCard)
            )
        }

       

        ComponentType.CLASSGALLERY -> {
            val classGalleryCard = componentItem as ClassGalleryCard
            ClassGalleryCard2X2(
                modifier = fillSizeModifier(modifier, classGalleryCard),
                navController
            )
        }

         ComponentType.CLASSINFO -> {
            val classInfoCard = componentItem as ClassInfoCard
            ClassInfoCard2X1(
                fillSizeModifier(modifier, classInfoCard),
                navController
            )
        }

       ........
    }
}

fun RowScope.createModifier(
    componentItem: ComponentItem
): Modifier {

    val modifier = if (componentItem.weight > 0) {
        Modifier.weight(componentItem.weight)
    } else Modifier


    return modifier
}

fun ColumnScope.createModifier(
    componentItem: ComponentItem
): Modifier {
    val modifier = if (componentItem.weight > 0) {
        Modifier.weight(componentItem.weight)
    } else Modifier

    return modifier
}

fun RowScope.createModifier(
    componentItem: ComponentItem
): Modifier {

    val modifier = if (componentItem.weight > 0) {
        Modifier.weight(componentItem.weight)
    } else Modifier


    return modifier
}

fun ColumnScope.createModifier(
    componentItem: ComponentItem
): Modifier {
    val modifier = if (componentItem.weight > 0) {
        Modifier.weight(componentItem.weight)
    } else Modifier

    return modifier
}

6. 根据具体类型尺寸,构建内容

@Composable
fun ClassInfoCard2X1(
    modifier: Modifier = Modifier,
    navHostController: NavHostController? = null
) {
    val classInfo = ClassInfo(
        "三年级(2)班", 49, R.drawable.class_info_image, "" +
                "三分天注定,七分靠打拼!", "胡玲玲"
    )
    BaseCardViewDark(
        modifier = modifier
    ) {
        val context = LocalContext.current

        if (classInfo == null) {
            EmptyView()
        } else {
            BoxWithConstraints(modifier = Modifier.fillMaxSize().padding(20.dp)) {
                Column(
                    verticalArrangement = Arrangement.Top,
                    horizontalAlignment = Alignment.Start,
                    modifier = Modifier.clickable {
//                        navHostController?.navigateOrPop(NavHomeItem.Video.name)
//                        navHostController?.navigateOrPop(NavHomeItem.VERIFY.name)
//                        context.startActivity(Intent(context, SettingsActivity::class.java))
                    }
                ) {

                    Text(
                        text = classInfo.className,
                        style = MaterialTheme.typography.headlineLarge,
                        textAlign = TextAlign.Center,
                    )

                    Text(
                        text = classInfo.label,
                        style = MaterialTheme.typography.labelLarge,
                        modifier = Modifier.padding(top = 10.dp, bottom = 10.dp)
                            .weight(1f),
                    )

                    Text(
                        text = "班主任:" + classInfo.headerTeacher,
                        style = MaterialTheme.typography.titleMedium,
                    )
                    SpacerHeight5()
                    Text(
                        text = "班级人数:" + classInfo.classStudentCount.toString(),
                        style = MaterialTheme.typography.titleMedium,
                    )
                }

                Image(
                    painter = painterResource(id = classInfo.image),
                    contentDescription = classInfo.className,
                    modifier = Modifier.align(Alignment.CenterEnd),
                    contentScale = ContentScale.FillHeight
                )
            }
        }
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值