参考链接
Kuikly - 组件
Pager
Pager为Kuikly页面的入口类,类似iOS UI中的VC, Pager也有类似VC的生命周期:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
// 已创建 override fun created() { super.created() } // 页面在屏幕上可见 override fun pageDidAppear() { super.pageDidAppear() }
// 页面在屏幕上消失 override fun pageDidDisappear() { super.pageDidDisappear()
}
// 页面即将消失 override fun pageWillDestroy() { super.pageWillDestroy()
}
|
一个常见的Page
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
// 设置路由跳转名 @Page("my_ctstom_name") // 定义一个自己的类, 继承于 BasePager internal class MyCustomPage : BasePager() { // 伴生对象, Kotlin 中,类没有静态成员的概念,但通过 companion object,你可以模拟静态成员的行为 companion object { // 定义一个静态常量 TAG private const val TAG = "MyCustomPageTag" }
// 定义 currentName 是个可观察的属性, 设置给 UI 组件后, 值变化会直接更新 UI 组件的内容, 类似 OC 的 RAC var currentName by observable("")
// 构造 self.view override fun build(): ViewBuilder { // 使用 this 会导致循环引用, 所以一般在这里定义一个 ctx val ctx = this return { // 布局属性 attr { backgroundColor(Color.WHITE) } // 子 View View { } } } }
|
数据刷新
by observable
变量被设置为 by observable
后, UI 绑定这个变量会直接变化
1 2 3 4 5 6 7
|
var isOpen by observable(false)
Switch { attr { isOn(ctx.isOpen) } }
|
vbind
组件使用vbind包裹,当vbind内属性发生改变时,整个组件会重新绘制
1 2 3 4 5 6 7 8 9 10 11
|
vbind({ ctx.displaySomething }) { if ( xx ) { View { } } else if ( xx ) { View { } } }
|
vfor
对于数量可变的列表数据,使用vfor包裹来实现cell,列表变化时会重新绘制
1 2 3 4 5 6 7
|
vfor({ ctx.dataList }) { itemData -> PersonInfoCustomizeCell { attr { item = itemData } } }
|
vif
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
vif({ xxx }) { View { } } velseif({ }) { View { } } velse { View { } }
|
ComposeView
1 2 3 4 5
|
class CustomView : ComposeView<>() { } internal fun ViewContainer<*, *>.Custom(init: CustomView.() -> Unit) { addChild(CustomView(), init) }
|
外部设置值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
class CustomView : ComposeView<CustomViewAttr>() {
override fun body(): ViewBuilder { val ctx = this val bottomArrowX = ctx.attr.bottomArrowX // ... } override fun createAttr(): CustomViewAttr { return CustomViewAttr() } }
internal class CustomViewAttr : ComposeAttr() { var bottomArrowX = 0f // ... }
// 其他类调用 class OtherClassView : ComposeView() {
override fun body(): ViewBuilder { val ctx = this
View { Custom { attr { bottomArrowX = showX } } } } }
|
事件处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
class CustomView : ComposeView<CustomViewEvent>() { override fun body(): ViewBuilder { val ctx = this View { event { click { KLog.i(TAG, "onSendClick") ctx.event.onSendClick?.invoke(ctx.attr.customViewParam) } } } } }
class CustomViewEvent : ComposeEvent() { var onSendClick: ((CustomViewParam) -> Unit)? = null } // 其他类调用 class OtherClassView : ComposeView() {
override fun body(): ViewBuilder { val ctx = this
View { Custom { event { onSendClick = { customViewParam -> // 事件处理 } } } } } }
|
布局
布局属性
Flex 布局
flexDirection 主轴
- flexDirectionColumn(默认): 主轴方向为竖直方向,子孩子从上往下布局
- flexDirectionRow: 主轴方向为水平方向,子孩子从左往右布局
- flexDirectionColumnReverse: 主轴方向为竖直方向,子孩子从下往上布局
- flexDirectionRowReverse: 主轴方向为水平方向,子孩子从右往左布局
justifyContent 主轴分布模式
- justifyContentFlexStart(默认): 主轴开始的位置进行对齐
- justifyContentFlexEnd: 主轴结束的位置进行对齐
- justifyContentCenter: 主轴的中间位置进行对齐
- justifyContentSpaceBetween: 主轴两端对齐,子孩子之间的间隔都相等
- justifyContentSpaceAround: 每个项目两侧的间隔相等。所以,子孩子之间的间隔比项目与边框的间隔大一倍
- justifyContentSpaceEvenly: Flex Item之间的间距相等,包括与边缘位置的距离

alignItems 交叉轴
- alignItemsFlexStart: 交叉轴的起点对齐
- alignItemsFlexEnd: 交叉轴的终点位置对齐
- alignItemsCenter: 交叉轴的中点位置对齐
- alignItemsStretch(默认): 如果 Flex 容器的孩子没有指定大小(高度或者宽度,取决于交叉轴是水平还是竖直)的话,将占满 Flex 容器
- 当 flexDirection 为 flexDirectionRow 时,交叉轴的方向为竖直方向,此时 alignItems 各个属性的效果为
- 当 flexDirection 为 flexDirectionColumn 时,交叉轴的方向为水平方向,此时 alignItems 的各个属性效果为:
Flex Item 布局属性
上面讲述的 Flex Container 属性,是针对 Flex Container 下的所有孩子生效。Flex Item 也可自己设置布局属性,覆盖 Flex Container 的属性。
alignSelf
alignSelf属性是控制Flex Item自身在 Flex 容器的交叉轴上的对齐方式,会覆盖 Flex 容器指定的 alignItems 属性,可选值为:
- alignSelfFlexStart: Flex Item 自身在 Flex 容器的交叉轴的起点对齐
- alignSelfCenter: Flex Item 自身在 Flex 容器的交叉轴中点对齐
- alignSelfFlexEnd: Flex Item 自身在 Flex 容器的交叉轴终点对齐
- alignItemsStretch: Flex Item 自身在交叉轴方向上铺满Flex 容器
- alignSelf 与 alignItems 差不多,具体的对齐方向与 flexDirection 的值有关
- 当 flexDirection 的值为 flexDirectionColumn 时,Flex Item 设置 alignSelf 的效果如下:
flex
Flex Item 在主轴上,占据 Flex 容器的剩余可用空间的比例。 可用空间是指 Flex 容器除去已经被占用的空间,剩下的空间大小, 比如: 顶部是一个 titlebar,剩下的空间分配给列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
View { attr { size(screenWidth, screenHeight) flexDirectionColumn() // 主轴方向为竖直方向,孩子从上往下排列 // alignItems默认值为 alignItemsStretch, 因此在交叉轴,即水平方向上 // 其孩子的宽度与父亲一样大 } View { // title bar attr { height(56f) // 宽度为父亲的宽度, 因为父亲的alignItems 默认为 alignItemsStretch, // 在交叉轴上的大小会占满父亲 } } List { attr { flex(1f) // 表示占满父容器主轴上的可用空间,即占满父亲可用的高度(screenHeight - 56f) } } }
|
绝对布局
与 superView 一样大
1 2 3 4 5
|
View { attr { absolutePositionAllZero() } }
|
保持间距
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
View { attr { size(screenWidth, screenHeight) } Image { attr { positionAbsolute() // 表示宽高与父容器的宽高一样大 top(0f) bottom(0f) right(0f) left(0f) } } }
|
常用UI组件
设置背景色/圆角
1 2 3 4
|
attr { backgroundColor(ctx.buildThemedColor("skin_floor_color")) borderRadius(10f) }
|
文本
1 2 3 4 5 6 7 8 9 10
|
Text { attr { fontSize(36f.pxw) fontWeight500() marginTop(40f.pxw) marginBottom(10f.pxw) text(itemData.title) color(ctx.buildThemedColor("skin_text_main_color")) } }
|
加载图片
1 2 3 4 5 6 7
|
Image { attr { src("https://musicx.y.qq.com/kuikly/assets/ic_svip_quality_guide_check.png") size(DESIGN_DESC_ICON_SIZE.pxh(), DESIGN_DESC_ICON_SIZE.pxh()) marginRight(DESIGN_DESC_ICON_MARGIN_RIGHT.pxh()) } }
|
列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
List { attr { positionAbsolute() } vfor({ ctx.dataList }) { itemData -> View { attr { Size(pagerWidth, 40F) flexDirectionRow() }
Image { attr { size(30F, 30F) src(itemData.avatarUrl) } }
Text { attr { fontSize(36f.pxw) fontWeight500() marginTop(40f.pxw) marginBottom(10f.pxw) text(itemData.title) color(ctx.buildThemedColor("skin_text_main_color")) } } } } }
|
列表 cell 高度设置(在 cell 设置)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
List { ref { // it 是一个隐式名称(implicit name)的参数,用于表示单个参数的 lambda 表达式中的参数。当你在 lambda 表达式中只有一个参数时,可以使用 it 来代替显式声明参数名称 ctx.listViewRef = it } attr { absolutePositionAllZero() } vfor({ ctx.dataList }) { itemData -> PersonInfoCustomizeCell { attr { item = itemData } } } } // cell 单独设置 override fun body(): ViewBuilder { val ctx = this return { attr { height(120F) } View { attr { backgroundColor(Color.WHITE) borderRadius(10F) positionAbsolute() top(0F) left(20F) right(20F) bottom(10F) flexDirectionRow() } } } }
|
列表调用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
import com.tencent.kuikly.core.views.ListView
internal class xxxPager : BasePager() { var listViewRef : ViewRef<ListView<*, *>>? = null override fun build(): ViewBuilder { val ctx = this return { List { ref { ctx.listViewRef = it } attr { absolutePositionAllZero() } } } } override fun viewDidLayout() { super.viewDidLayout()
this.listViewRef?.view?.setContentInset(top = 500F) this.listViewRef?.view?.setContentOffset(0F, -500F) // 设置 inset 后 自动产生了一个 offsetY 偏移 } }
|
subView 回调
1 2 3 4 5 6 7 8
|
// subView 先获取到 pager var pager = getPager() // 判定 pager 类型, if (pager is SomePager) { // 调用被标记为 observable 的对象, 达到调用的目的 pager.isShowAutoTranslateWarningDialog = true }
|
弹窗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
// 是否需要展示自动翻译弹窗 var isShowAutoTranslateWarningDialog by observable(false)
return { View { // 自动翻译弹窗 ctx.buildAutoTanslateWarningDialog().invoke(this); } }
private fun buildAutoTanslateWarningDialog(): ViewBuilder { val ctx = this return { SendMsgWarningDialog { attr { showDialog = ctx.isShowAutoTranslateWarningDialog title = StringConst.AUTOTRANSLATE_DIALOG_TITLE content = StringConst.AUTOTRANSLATE_DIALOG_CONTENT } event { onButtonSureClick = { ctx.isShowAutoTranslateWarningDialog = false KLog.i(TAG, "[clickedTranslateBtnForAlert] click sure ") } onButtonCancelClick = { // 取消 ctx.isShowAutoTranslateWarningDialog = false KLog.i(TAG, "[clickedTranslateBtnForAlert] click cancel ") } } } } } // 最后 在需要的时候 this.isShowAutoTranslateWarningDialog = true
|
逻辑
日志
1 2 3 4
|
KLog.i( TAG, "[onMessageBodyLongPress] msgFrame=$msgFrame, msgBodyFrame=$msgBodyFrame, clickX=$clickX, clickY=$clickY, msgBodyLeft=$msgBodyLeft, messageModel=$messageModel" )
|
缓存
1
|
bubblePlugin?.read(key = KEY_HAVE_DISPLAYED_GUIDE_VIEW + userId + myOwnEncryptUin)
|
判空(null 执行 toInt() 会崩溃)
1
|
!clickCountBefore.isNullOrEmpty()
|
类型转换
1 2
|
// toLong() 在遇到 "abc" 这样的字符串 会崩溃, 要用 toLongOrNull, toInt() 也有类似问题 var clickTs = clickTsBefore?.toLongOrNull() ?: 0L
|
延时处理
1 2 3
|
setTimeout(500) { // xxx }
|