美文网首页互联网科技Android技术知识Kotlin编程
[KtJC] KTJC 再探,数据绑定与常用 UI

[KtJC] KTJC 再探,数据绑定与常用 UI

作者: 何晓杰Dev | 来源:发表于2019-12-18 17:57 被阅读0次

上一篇讲了 KtJC 的入门(点此进入),这一篇就讲一些好玩的吧,毕竟真要上项目了就完全不是那些 Demo 里面搞的东西了,虽然官方一再强调千万不要用来做为生产用 :(

一、ListView

可能你直接就发现了,在 JC 里面是没有 ListView 的,要实现的话只能自己去搭,先套一个垂直滚动容器,再套一个 Column,然后再按列表内容来套一大堆的声明,代码类似于这样:

VerticalScroller {
    Column {
        (0 until 20).forEachIndexed { index, _ ->
            FlexRow(crossAxisAlignment = CrossAxisAlignment.Center) {
                expanded(1.0f) {
                    Text("Item $itemIndex")
                }
                inflexible {
                    Button(
                        "Button $itemIndex",
                        style = ContainedButtonStyle(),
                        onClick = { }
                    )
                }
            }
            Divider(color = Color.Blue, height = 1.dp) 
        }
    }
}

总感觉很怪,嵌套层次过深,我希望将它写得更简单并且好理解一些,如:

@Composable
fun ListView(
    itemCount: Int,
    children: @Composable() (index: Int) -> Unit) {
    VerticalScroller {
        Column {
            for (index in 0 until itemCount) {
                children(index)
            }
        }
    }
}

这样我就可以直接使用 ListView 组件了:

ListView(itemCount = 20) { index ->
    FlexRow(crossAxisAlignment = CrossAxisAlignment.Center) {
        expanded(1.0f) {
            Text("Item $index")
        }
        inflexible {
            Button("Button $index",
                style = ContainedButtonStyle(),
                onClick = { }
            )
        }
    }               
    Divider(color = Color.LightGray, height = 1.dp)
}

二、数据绑定

在上面的代码中,用了一个循环来代替真实的数据列表,我们也可以很轻松的将真实数据绑定上去。在 JC 中已经提供了简便的数据绑定方案:

@Model
class State(
    val list: MutableList<String> = mutableListOf()
)
val state = State(list = mutableListOf(
    "a", "b","c","d","e","f","g"
))

看出来了没,只要把数据标识为 @Model,然后在界面的任意地方使用它,都可以实现数据绑定,当数据被修改时,界面也会自动刷新。

所以现在可以把上面的 ListView 代码改成这样了:

ListView(itemCount = state.list.size) { index ->
    FlexRow(crossAxisAlignment = CrossAxisAlignment.Center) {
        expanded(1.0f) {
            Text("Item ${state.list[index]}")
        }
        inflexible {
            Button("Button ${state.list[index]}",
                style = ContainedButtonStyle(),
                onClick = { }
            )
        }
    }               
    Divider(color = Color.LightGray, height = 1.dp)
}

三、GridView

之前就说了没有 ListView,一试之下果然也没有 GridView,查一了圈文档发现只能用 Table 来实现,非常的蛋疼,代码像这样:

Table(columns = 8) {
    repeat(8) { i ->
        tableRow {
            repeat(8) { j ->
                // Cell
                Text("${i * 8 + j}")
            }
        }
    }
}

可以看到,其中还必须使用 tableRow 来标识出每一行,这个实现与 Android 原生的 GridView 差得太远了!

所以也必须找一个办法来解决之,所幸知道以上代码后,要搞定并不难。

首先我们需要一个将 itemCount 转换为二维数组的方法,即需要知道一个 itemCount 可以被渲染成多少行,以及每一行内有多少列:

fun Int.toGridData(columns: Int = 1) = mutableListOf<List<Int>>().apply {
    var count = 0
    var sub = mutableListOf<Int>()
    for (item in 0 until this@toGridData) {
        if (count == columns) {
            add(sub.toList())
            sub = mutableListOf()
            sub.add(item)
            count = 1
            continue
        }
        sub.add(item)
        count++
    }
    add(sub.toList())
}.toList()

随手写一下就这样吧,最终得到一个二维数组。然后就可以完成 GridView 了:

@Composable
fun GridView(
    columns: Int,
    itemCount: Int,
    alignment: (columnIndex: Int) -> Alignment = { Alignment.TopLeft },
    columnWidth: (columnIndex: Int) -> TableColumnWidth = { TableColumnWidth.Flex(1f) },
    children: @Composable() (index: Int) -> Unit) {

    VerticalScroller {
        Table(columns = columns, alignment = alignment, columnWidth = columnWidth) {
            val gridIndex = itemCount.toGridData(columns)
            gridIndex.forEach { list ->
                tableRow {
                    list.forEach { index ->
                        children(index)
                    }
                }
            }
        }
    }
}

此时要生成一个 GridView 就变得简单了:

GridView(columns = 3, itemCount = state.list.size) { index ->                    
    Text(text = state.list[index])                
}

四、组件位于 Activity 底部

到目前为止,还没有找到如何使组件对于页面做底部对齐的方法,唯有自己计算,计算方法如下:

@Composable
fun sampleUI(ctx: Context?, safeHeight: Dp = Dp(0f)) {
    val hhDp = if (safeHeight == Dp(0f)) { ctx?.safeHeightDp() ?: Dp(0f) } else safeHeight
    MaterialTheme {
        Column {
            Container(height = 40.dp, alignment = Alignment.CenterLeft, expanded = true) {
                Text(text = "times: ${state.count}")
            }
            // 这个 Container 撑满剩余空间
            Container(height = hhDp - 80.dp, expanded = true) {
            }
            // 这个 Container 底部对齐
            Container(height = 40.dp, alignment = Alignment.BottomCenter, expanded = true) {
                Button(text = "Click",
                    onClick = {
                        ctx?.toast("${state.itemIndex}", dark = false)
                    }
                )
            }
        }
    }
}

关键来了,这个 safeHeightDp 怎么来的呢?

fun Context.safeHeightDp(): Dp {
    val nav = if (hasNavigationBar()) navigationBarHeight() else 0
    val space = UI.height - actionBarHeight() - statusBarHeight() - nav
    return Dp(space.px2dip())
}

由此我们就可以算出用于撑开空间的 Container 有多高了。

对于这类 Activity,JC 无法在预览时就计算出高度,只能手动传一个,否则高度会变 0,经过实际测试,预览界面的 safeHeight 值为 603.dp,一个挺奇怪的数字,记住就好了,这样就可以正常的预览界面了:

@Preview
@Composable
fun samplePreview() {
    sampleUI(null, safeHeight = 603.dp)
}

简单探了一些,下面该探的可能就是下刷上滑刷新这类的实现了,要实际用于项目还需要很多的储备,慢慢探完吧。本篇先到此结束了 :)

相关文章

  • [KtJC] KTJC 再探,数据绑定与常用 UI

    上一篇讲了 KtJC 的入门(点此进入),这一篇就讲一些好玩的吧,毕竟真要上项目了就完全不是那些 Demo 里面搞...

  • [KtJC] Drawer 和 Tabber

    Drawer 在 JC 里要实现侧滑抽屉(Drawer)是比较方便的,代码非常直观,其函数定义为: 所以我们可以在...

  • [KtJC] Jetpack Compose 入门

    Jetpack Compose 是隶属于 AndroidX 的组件之一,目前还处于 Preview 版本,因此暂时...

  • RxSwift-MVVM

    MVVM核心在于数据与UI的双向绑定,数据的变化会更新UI,UI变化会更新我们的数据。那这种绑定操作谁来做呢?当然...

  • AppWorker教程-数据绑定

    数据绑定 数据绑定是在应用程序 UI 与数据源建立连接的过程。如果绑定正确数据,则当数据更改其值时,绑定到数据的U...

  • Vue 简单语法

    动态绑定数据message是动态的 判断语句 循环语句 事件监听 UI与数据双向绑定 数据只绑定一次,后续数据改变...

  • [KtJC] 迁移至 compose dev06

    不得不说 Jetpack Compose 发版的速度实在是快,一不留神居然已经到了 0.1.0-dev06 版本了...

  • 数据驱动之 UITableViewController

    数据驱动:即使用数据驱动ui的展示,而不需要手动去调整ui,从而将数据与ui进行绑定,界面的布局全部由数据来控制;...

  • Android Jetpack DataBinding原理浅析(

    前言 DataBinding是JetPack系列的架构组件,它的作用时实现数据与UI的绑定,支持单向绑定或者双向绑...

  • [转] DataBinding 数据绑定

    数据绑定分为单项绑定和双向绑定两种。单向绑定上,数据的流向是单方面的,只能从代码流向 UI;双向绑定的数据是双向的...

网友评论

    本文标题:[KtJC] KTJC 再探,数据绑定与常用 UI

    本文链接:https://www.haomeiwen.com/subject/oyvunctx.html