kuikly-expand-view
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseContents
目录
Core Guidelines
核心准则
- View 是跨平台 UI 组件的统一接口: Kuikly 已封装常用 View(Text、Image、List 等),自定义 View 用于复用已有原生 UI 组件或满足特殊需求。
- 自定义 View 需双端实现: Kuikly 侧定义组件结构(viewName、Attr、Event),Native 侧(Android/iOS/鸿蒙/H5/小程序)实现具体渲染逻辑,通过 关联。
viewName - 组件名必须全端一致: Kuikly 侧 返回值必须与 Native 侧注册的名字完全一致。
viewName() - 属性和事件走 setProp 方法: Native 侧通过 接收 Kuikly 侧设置的属性和事件。
setProp(propKey, propValue) - 方法调用走 call 方法: Native 侧通过 响应 Kuikly 侧的方法调用。
call(method, params, callback)
- View是跨平台UI组件的统一接口: Kuikly已封装常用View(Text、Image、List等),自定义View用于复用已有原生UI组件或满足特殊需求。
- 自定义View需双端实现: Kuikly侧定义组件结构(viewName、Attr、Event),原生侧(Android/iOS/鸿蒙/H5/小程序)实现具体渲染逻辑,通过 关联。
viewName - 组件名必须全端一致: Kuikly侧 返回值必须与原生侧注册的名字完全一致。
viewName() - 属性和事件走setProp方法: 原生侧通过 接收Kuikly侧设置的属性和事件。
setProp(propKey, propValue) - 方法调用走call方法: 原生侧通过 响应Kuikly侧的方法调用。
call(method, params, callback)
Workflow: Creating Expand View (Kuikly Side)
流程:在Kuikly端创建扩展View
Use this workflow to create a expand View on the Kuikly (Kotlin) side.
Kuikly 组件由四部分组成:
- : 组件对应到原生组件的名字
viewName - : 组件的属性,用于指定该组件含有哪些属性
Attr - : 用于接收来自原生组件发送的事件
Event - : 组件本身支持的方法,最终实现在原生侧
方法
步骤:
- 新建 View 类继承
DeclarativeBaseView<XxxAttr, XxxEvent> - 实现 、
viewName()、createAttr()方法createEvent() - 定义 类继承
Attr,添加属性方法Attr - 定义 类继承
Event,添加事件注册方法Event - 编写声明式 API 扩展函数
- 在业务代码中使用组件
使用以下流程在Kuikly(Kotlin)端创建扩展View。
Kuikly组件由四部分组成:
- : 组件对应到原生组件的名字
viewName - : 组件的属性,用于指定该组件含有哪些属性
Attr - : 用于接收来自原生组件发送的事件
Event - 方法: 组件本身支持的方法,最终实现在原生侧
步骤:
- 新建View类继承
DeclarativeBaseView<XxxAttr, XxxEvent> - 实现 、
viewName()、createAttr()方法createEvent() - 定义 类继承
Attr,添加属性方法Attr - 定义 类继承
Event,添加事件注册方法Event - 编写声明式API扩展函数
- 在业务代码中使用组件
Step 1: 定义 View 类
步骤1:定义View类
kotlin
import com.tencent.kuikly.core.base.DeclarativeBaseView
class MyImageView : DeclarativeBaseView<MyImageAttr, MyImageEvent>() {
override fun createAttr(): MyImageAttr {
return MyImageAttr()
}
override fun createEvent(): MyImageEvent {
return MyImageEvent()
}
// 返回 Native 侧注册的组件名
override fun viewName(): String {
return "HRImageView"
}
// 组件方法(可选),实际实现在 Native 侧
fun test() {
performTaskWhenRenderViewDidLoad {
renderView?.callMethod("test", "params")
}
}
}kotlin
import com.tencent.kuikly.core.base.DeclarativeBaseView
class MyImageView : DeclarativeBaseView<MyImageAttr, MyImageEvent>() {
override fun createAttr(): MyImageAttr {
return MyImageAttr()
}
override fun createEvent(): MyImageEvent {
return MyImageEvent()
}
// 返回原生侧注册的组件名
override fun viewName(): String {
return "HRImageView"
}
// 组件方法(可选),实际实现在原生侧
fun test() {
performTaskWhenRenderViewDidLoad {
renderView?.callMethod("test", "params")
}
}
}Step 2: 定义 Attr 类
步骤2:定义Attr类
Attrkotlin
import com.tencent.kuikly.core.base.Attr
class MyImageAttr : Attr() {
/**
* 设置图片数据源
* @param src 图片 URL
* @return this(支持链式调用)
*/
fun src(src: String): MyImageAttr {
"src" with src // 将属性透传给原生组件
return this
}
/**
* 设置占位图
*/
fun placeholder(url: String): MyImageAttr {
"placeholder" with url
return this
}
/**
* 设置缩放模式
*/
fun scaleType(type: String): MyImageAttr {
"scaleType" with type
return this
}
}Attrkotlin
import com.tencent.kuikly.core.base.Attr
class MyImageAttr : Attr() {
/**
* 设置图片数据源
* @param src 图片URL
* @return this(支持链式调用)
*/
fun src(src: String): MyImageAttr {
"src" with src // 将属性透传给原生组件
return this
}
/**
* 设置占位图
*/
fun placeholder(url: String): MyImageAttr {
"placeholder" with url
return this
}
/**
* 设置缩放模式
*/
fun scaleType(type: String): MyImageAttr {
"scaleType" with type
return this
}
}Step 3: 定义 Event 类
步骤3:定义Event类
Eventkotlin
import com.tencent.kuikly.core.base.event.Event
import com.tencent.kuikly.core.nvi.serialization.json.JSONObject
class MyImageEvent : Event() {
/**
* 图片加载成功回调
*/
fun loadSuccess(handler: (LoadSuccessParams) -> Unit) {
register(LOAD_SUCCESS) {
handler(LoadSuccessParams.decode(it))
}
}
/**
* 图片加载失败回调
*/
fun loadError(handler: (LoadErrorParams) -> Unit) {
register(LOAD_ERROR) {
handler(LoadErrorParams.decode(it))
}
}
companion object {
const val LOAD_SUCCESS = "loadSuccess"
const val LOAD_ERROR = "loadError"
}
}
// 事件参数解析类
data class LoadSuccessParams(
val src: String,
val width: Int,
val height: Int
) {
companion object {
fun decode(params: Any?): LoadSuccessParams {
val tempParams = params as? JSONObject ?: JSONObject()
return LoadSuccessParams(
src = tempParams.optString("src", ""),
width = tempParams.optInt("width", 0),
height = tempParams.optInt("height", 0)
)
}
}
}
data class LoadErrorParams(
val src: String,
val errorCode: Int
) {
companion object {
fun decode(params: Any?): LoadErrorParams {
val tempParams = params as? JSONObject ?: JSONObject()
return LoadErrorParams(
src = tempParams.optString("src", ""),
errorCode = tempParams.optInt("errorCode", 0)
)
}
}
}Eventkotlin
import com.tencent.kuikly.core.base.event.Event
import com.tencent.kuikly.core.nvi.serialization.json.JSONObject
class MyImageEvent : Event() {
/**
* 图片加载成功回调
*/
fun loadSuccess(handler: (LoadSuccessParams) -> Unit) {
register(LOAD_SUCCESS) {
handler(LoadSuccessParams.decode(it))
}
}
/**
* 图片加载失败回调
*/
fun loadError(handler: (LoadErrorParams) -> Unit) {
register(LOAD_ERROR) {
handler(LoadErrorParams.decode(it))
}
}
companion object {
const val LOAD_SUCCESS = "loadSuccess"
const val LOAD_ERROR = "loadError"
}
}
// 事件参数解析类
data class LoadSuccessParams(
val src: String,
val width: Int,
val height: Int
) {
companion object {
fun decode(params: Any?): LoadSuccessParams {
val tempParams = params as? JSONObject ?: JSONObject()
return LoadSuccessParams(
src = tempParams.optString("src", ""),
width = tempParams.optInt("width", 0),
height = tempParams.optInt("height", 0)
)
}
}
}
data class LoadErrorParams(
val src: String,
val errorCode: Int
) {
companion object {
fun decode(params: Any?): LoadErrorParams {
val tempParams = params as? JSONObject ?: JSONObject()
return LoadErrorParams(
src = tempParams.optString("src", ""),
errorCode = tempParams.optInt("errorCode", 0)
)
}
}
}Step 4: 组件方法(可选)
步骤4:组件方法(可选)
组件方法是 Kuikly 侧暴露的 API,实际实现在 Native 侧。
kotlin
class MyImageView : DeclarativeBaseView<MyImageAttr, MyImageEvent>() {
// ...
/**
* 重新加载图片(无返回值)
*/
fun reload() {
performTaskWhenRenderViewDidLoad {
renderView?.callMethod("reload", null)
}
}
/**
* 获取图片信息(异步回调)
*/
fun getImageInfo(callback: (width: Int, height: Int) -> Unit) {
performTaskWhenRenderViewDidLoad {
renderView?.callMethod("getImageInfo", null) { result ->
val json = result as? JSONObject
callback(json?.optInt("width") ?: 0, json?.optInt("height") ?: 0)
}
}
}
}⚠️ 注意:
- View 的方法支持异步回调结果,即
,但不支持同步返回结果。renderView?.callMethod("method", "params", callback)- View 方法不支持传递二进制数据,如需传输图片等二进制内容,请通过 Base64 编码后以 JSON 形式传递,或使用 Module 方法实现。
组件方法是Kuikly侧暴露的API,实际实现在原生侧。
kotlin
class MyImageView : DeclarativeBaseView<MyImageAttr, MyImageEvent>() {
// ...
/**
* 重新加载图片(无返回值)
*/
fun reload() {
performTaskWhenRenderViewDidLoad {
renderView?.callMethod("reload", null)
}
}
/**
* 获取图片信息(异步回调)
*/
fun getImageInfo(callback: (width: Int, height: Int) -> Unit) {
performTaskWhenRenderViewDidLoad {
renderView?.callMethod("getImageInfo", null) { result ->
val json = result as? JSONObject
callback(json?.optInt("width") ?: 0, json?.optInt("height") ?: 0)
}
}
}
}⚠️ 注意:
- View的方法支持异步回调结果,即
,但不支持同步返回结果。renderView?.callMethod("method", "params", callback)- View方法不支持传递二进制数据,如需传输图片等二进制内容,请通过Base64编码后以JSON形式传递,或使用Module方法实现。
Step 5: 编写声明式 API
步骤5:编写声明式API
kotlin
import com.tencent.kuikly.core.base.ViewContainer
fun ViewContainer<*, *>.MyImage(init: MyImageView.() -> Unit) {
addChild(MyImageView(), init)
}kotlin
import com.tencent.kuikly.core.base.ViewContainer
fun ViewContainer<*, *>.MyImage(init: MyImageView.() -> Unit) {
addChild(MyImageView(), init)
}Step 6: 业务使用
步骤6:业务使用
kotlin
override fun body(): ViewBuilder {
val ctx = this
return {
MyImage {
attr {
size(176f, 132f)
src("https://example.com/image.png")
placeholder("https://example.com/placeholder.png")
}
event {
loadSuccess { params ->
println("图片加载成功: ${params.src}, 尺寸: ${params.width}x${params.height}")
}
loadError { params ->
println("图片加载失败: ${params.errorCode}")
}
}
}
}
}kotlin
override fun body(): ViewBuilder {
val ctx = this
return {
MyImage {
attr {
size(176f, 132f)
src("https://example.com/image.png")
placeholder("https://example.com/placeholder.png")
}
event {
loadSuccess { params ->
println("图片加载成功: ${params.src}, 尺寸: ${params.width}x${params.height}")
}
loadError { params ->
println("图片加载失败: ${params.errorCode}")
}
}
}
}
}使用 ViewRef 获取组件引用
使用ViewRef获取组件引用
通过 可以获取组件实例,在 Pager 其他位置调用组件方法。
ViewRefkotlin
@Page("MyPage")
class MyPage : Pager() {
// 声明 ViewRef
private val imageRef = ViewRef<MyImageView>()
override fun body(): ViewBuilder {
return {
MyImage {
// 绑定 ref
ref(imageRef)
attr {
src("https://example.com/image.png")
}
}
Button {
attr { title("重新加载") }
event {
click {
// 通过 ref 调用方法
imageRef.view?.reload()
}
}
}
}
}
}通过 可以获取组件实例,在Pager其他位置调用组件方法。
ViewRefkotlin
@Page("MyPage")
class MyPage : Pager() {
// 声明ViewRef
private val imageRef = ViewRef<MyImageView>()
override fun body(): ViewBuilder {
return {
MyImage {
// 绑定ref
ref(imageRef)
attr {
src("https://example.com/image.png")
}
}
Button {
attr { title("重新加载") }
event {
click {
// 通过ref调用方法
imageRef.view?.reload()
}
}
}
}
}
}Workflow: Implementing Native Side View
流程:实现原生端View
Use this workflow to implement the Native side of a expand View.
前提: 只需在业务需要支持的平台上实现 Native 侧 View,无需实现所有平台。
步骤:
- 确定需要支持的目标平台(Android/iOS/鸿蒙/H5/小程序)。
- 在目标平台宿主工程中创建对应的 View 类。
- 实现 方法处理属性和事件。
setProp - 实现 方法处理方法调用。
call - 将 View 注册到 Kuikly 框架。
各平台实现详见 VIEW_IMPLEMENT.md
速查:各平台 View 基类与注册方式
| 平台 | 基类/协议 | 注册方式 | 注意事项 |
|---|---|---|---|
| Android | | | 继承原生 View 并实现接口 |
| iOS | | 类名必须与 viewName 一致(运行时动态创建) | 属性方法命名 |
| 鸿蒙 (ArkTS) | | | 需实现 |
| H5 | | | 重写 |
| 小程序 | | | 需创建 |
使用以下流程实现扩展View的原生端。
前提: 只需在业务需要支持的平台上实现原生侧View,无需实现所有平台。
步骤:
- 确定需要支持的目标平台(Android/iOS/鸿蒙/H5/小程序)。
- 在目标平台宿主工程中创建对应的View类。
- 实现 方法处理属性和事件。
setProp - 实现 方法处理方法调用。
call - 将View注册到Kuikly框架。
各平台实现详见 VIEW_IMPLEMENT.md
速查:各平台View基类与注册方式
| 平台 | 基类/协议 | 注册方式 | 注意事项 |
|---|---|---|---|
| Android | | | 继承原生View并实现接口 |
| iOS | | 类名必须与viewName一致(运行时动态创建) | 属性方法命名 |
| 鸿蒙 (ArkTS) | | | 需实现 |
| H5 | | | 重写 |
| 小程序 | | | 需创建 |
Native 侧 setProp 支持的数据类型
原生侧setProp支持的数据类型
| Kuikly 侧类型 | Native 侧接收类型 |
|---|---|
| |
| 对应基础类型 / |
| |
| |
| Kuikly侧类型 | 原生侧接收类型 |
|---|---|
| |
| 对应基础类型 / |
| |
| |
事件触发机制
事件触发机制
在 Native 侧,事件通过 回调给 Kuikly 侧:
KuiklyRenderCallbackkotlin
// 保存回调
private var loadSuccessCallback: KuiklyRenderCallback? = null
override fun setProp(propKey: String, propValue: Any): Boolean {
return when (propKey) {
"loadSuccess" -> {
loadSuccessCallback = propValue as KuiklyRenderCallback
true
}
else -> super.setProp(propKey, propValue)
}
}
// 触发回调
private fun onImageLoaded(src: String, width: Int, height: Int) {
loadSuccessCallback?.invoke(mapOf(
"src" to src,
"width" to width,
"height" to height
))
}在原生侧,事件通过 回调给Kuikly侧:
KuiklyRenderCallbackkotlin
// 保存回调
private var loadSuccessCallback: KuiklyRenderCallback? = null
override fun setProp(propKey: String, propValue: Any): Boolean {
return when (propKey) {
"loadSuccess" -> {
loadSuccessCallback = propValue as KuiklyRenderCallback
true
}
else -> super.setProp(propKey, propValue)
}
}
// 触发回调
private fun onImageLoaded(src: String, width: Int, height: Int) {
loadSuccessCallback?.invoke(mapOf(
"src" to src,
"width" to width,
"height" to height
))
}常见陷阱与正确做法
常见陷阱与正确做法
| ❌ 错误做法 | ✅ 正确做法 |
|---|---|
| View 名字 Kuikly 侧与 Native 侧不一致 | 确保 |
| 自定义 View 未设置宽高导致不显示 | 自定义 View 跨端侧使用时必须显式设置宽高(如 |
| iOS 侧 View 类名与 Kuikly 侧 viewName 不一致 | iOS 通过类名动态创建 View,类名必须与 |
iOS 侧用 Swift 实现但未加 | Swift 实现的 View 需要 |
鸿蒙侧忘记实现 | 鸿蒙侧必须实现此方法返回 |
| 忘记在 Native 侧注册自定义 View | 自定义 View 必须注册后才能使用 |
在 | View 方法必须在 |
| ❌ 错误做法 | ✅ 正确做法 |
|---|---|
| View名字Kuikly侧与原生侧不一致 | 确保 |
| 自定义View未设置宽高导致不显示 | 自定义View跨端侧使用时必须显式设置宽高(如 |
| iOS侧View类名与Kuikly侧viewName不一致 | iOS通过类名动态创建View,类名必须与 |
iOS侧用Swift实现但未加 | Swift实现的View需要 |
鸿蒙侧忘记实现 | 鸿蒙侧必须实现此方法返回 |
| 忘记在原生侧注册自定义View | 自定义View必须注册后才能使用 |
在 | View方法必须在 |