kuikly-expand-view

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Contents

目录

Core Guidelines

核心准则

  • View 是跨平台 UI 组件的统一接口: Kuikly 已封装常用 View(Text、Image、List 等),自定义 View 用于复用已有原生 UI 组件或满足特殊需求。
  • 自定义 View 需双端实现: Kuikly 侧定义组件结构(viewName、Attr、Event),Native 侧(Android/iOS/鸿蒙/H5/小程序)实现具体渲染逻辑,通过
    viewName
    关联。
  • 组件名必须全端一致: Kuikly 侧
    viewName()
    返回值必须与 Native 侧注册的名字完全一致。
  • 属性和事件走 setProp 方法: Native 侧通过
    setProp(propKey, propValue)
    接收 Kuikly 侧设置的属性和事件。
  • 方法调用走 call 方法: Native 侧通过
    call(method, params, callback)
    响应 Kuikly 侧的方法调用。

  • View是跨平台UI组件的统一接口: Kuikly已封装常用View(Text、Image、List等),自定义View用于复用已有原生UI组件或满足特殊需求。
  • 自定义View需双端实现: Kuikly侧定义组件结构(viewName、Attr、Event),原生侧(Android/iOS/鸿蒙/H5/小程序)实现具体渲染逻辑,通过
    viewName
    关联。
  • 组件名必须全端一致: Kuikly侧
    viewName()
    返回值必须与原生侧注册的名字完全一致。
  • 属性和事件走setProp方法: 原生侧通过
    setProp(propKey, propValue)
    接收Kuikly侧设置的属性和事件。
  • 方法调用走call方法: 原生侧通过
    call(method, params, callback)
    响应Kuikly侧的方法调用。

Workflow: Creating Expand View (Kuikly Side)

流程:在Kuikly端创建扩展View

Use this workflow to create a expand View on the Kuikly (Kotlin) side.
Kuikly 组件由四部分组成:
  1. viewName
    : 组件对应到原生组件的名字
  2. Attr
    : 组件的属性,用于指定该组件含有哪些属性
  3. Event
    : 用于接收来自原生组件发送的事件
  4. 方法
    : 组件本身支持的方法,最终实现在原生侧
步骤:
  1. 新建 View 类继承
    DeclarativeBaseView<XxxAttr, XxxEvent>
  2. 实现
    viewName()
    createAttr()
    createEvent()
    方法
  3. 定义
    Attr
    类继承
    Attr
    ,添加属性方法
  4. 定义
    Event
    类继承
    Event
    ,添加事件注册方法
  5. 编写声明式 API 扩展函数
  6. 在业务代码中使用组件
使用以下流程在Kuikly(Kotlin)端创建扩展View。
Kuikly组件由四部分组成:
  1. viewName
    : 组件对应到原生组件的名字
  2. Attr
    : 组件的属性,用于指定该组件含有哪些属性
  3. Event
    : 用于接收来自原生组件发送的事件
  4. 方法: 组件本身支持的方法,最终实现在原生侧
步骤:
  1. 新建View类继承
    DeclarativeBaseView<XxxAttr, XxxEvent>
  2. 实现
    viewName()
    createAttr()
    createEvent()
    方法
  3. 定义
    Attr
    类继承
    Attr
    ,添加属性方法
  4. 定义
    Event
    类继承
    Event
    ,添加事件注册方法
  5. 编写声明式API扩展函数
  6. 在业务代码中使用组件

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类

Attr
表示组件支持的属性集。
kotlin
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
    }
}
Attr
表示组件支持的属性集。
kotlin
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类

Event
表示组件支持的事件。
kotlin
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)
            )
        }
    }
}
Event
表示组件支持的事件。
kotlin
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获取组件引用

通过
ViewRef
可以获取组件实例,在 Pager 其他位置调用组件方法。
kotlin
@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()
                    }
                }
            }
        }
    }
}

通过
ViewRef
可以获取组件实例,在Pager其他位置调用组件方法。
kotlin
@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,无需实现所有平台。
步骤:
  1. 确定需要支持的目标平台(Android/iOS/鸿蒙/H5/小程序)。
  2. 在目标平台宿主工程中创建对应的 View 类。
  3. 实现
    setProp
    方法处理属性和事件。
  4. 实现
    call
    方法处理方法调用。
  5. 将 View 注册到 Kuikly 框架。
各平台实现详见 VIEW_IMPLEMENT.md
速查:各平台 View 基类与注册方式
平台基类/协议注册方式注意事项
Android
IKuiklyRenderViewExport
registerExternalRenderView
renderViewExport(name) { View(ctx) }
继承原生 View 并实现接口
iOS
KuiklyRenderViewExportProtocol
类名必须与 viewName 一致(运行时动态创建)属性方法命名
setCss_xxx
,方法命名
css_xxx
鸿蒙 (ArkTS)
KuiklyRenderBaseView
getCustomRenderViewCreatorRegisterMap
getCustomRenderViewCreatorRegisterMapV2
中注册
需实现
createArkUIView()
返回
ComponentContent
H5
IKuiklyRenderViewExport
registerExternalRenderView
renderViewExport(name) { View() }
重写
ele
属性返回 DOM 元素
小程序
IKuiklyRenderViewExport
registerExternalRenderView
+
Transform.addComponentsAlias
+ base.wxml 模板
需创建
MiniXxxElement
并配置模板
使用以下流程实现扩展View的原生端。
前提: 只需在业务需要支持的平台上实现原生侧View,无需实现所有平台。
步骤:
  1. 确定需要支持的目标平台(Android/iOS/鸿蒙/H5/小程序)。
  2. 在目标平台宿主工程中创建对应的View类。
  3. 实现
    setProp
    方法处理属性和事件。
  4. 实现
    call
    方法处理方法调用。
  5. 将View注册到Kuikly框架。
各平台实现详见 VIEW_IMPLEMENT.md
速查:各平台View基类与注册方式
平台基类/协议注册方式注意事项
Android
IKuiklyRenderViewExport
registerExternalRenderView
renderViewExport(name) { View(ctx) }
继承原生View并实现接口
iOS
KuiklyRenderViewExportProtocol
类名必须与viewName一致(运行时动态创建)属性方法命名
setCss_xxx
,方法命名
css_xxx
鸿蒙 (ArkTS)
KuiklyRenderBaseView
getCustomRenderViewCreatorRegisterMap
getCustomRenderViewCreatorRegisterMapV2
中注册
需实现
createArkUIView()
返回
ComponentContent
H5
IKuiklyRenderViewExport
registerExternalRenderView
renderViewExport(name) { View() }
重写
ele
属性返回DOM元素
小程序
IKuiklyRenderViewExport
registerExternalRenderView
+
Transform.addComponentsAlias
+ base.wxml模板
需创建
MiniXxxElement
并配置模板

Native 侧 setProp 支持的数据类型

原生侧setProp支持的数据类型

Kuikly 侧类型Native 侧接收类型
String
String
/
NSString
Int
/
Float
/
Double
对应基础类型 /
NSNumber
Boolean
Boolean
/
BOOL
Event 回调
KuiklyRenderCallback
Kuikly侧类型原生侧接收类型
String
String
/
NSString
Int
/
Float
/
Double
对应基础类型 /
NSNumber
Boolean
Boolean
/
BOOL
Event回调
KuiklyRenderCallback

事件触发机制

事件触发机制

在 Native 侧,事件通过
KuiklyRenderCallback
回调给 Kuikly 侧:
kotlin
// 保存回调
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
    ))
}

在原生侧,事件通过
KuiklyRenderCallback
回调给Kuikly侧:
kotlin
// 保存回调
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 侧不一致确保
viewName()
返回值与 Native 注册名完全一致
自定义 View 未设置宽高导致不显示自定义 View 跨端侧使用时必须显式设置宽高(如
size(w, h)
),否则可能不会显示
iOS 侧 View 类名与 Kuikly 侧 viewName 不一致iOS 通过类名动态创建 View,类名必须与
viewName()
一致
iOS 侧用 Swift 实现但未加
@objc
注解
Swift 实现的 View 需要
@objc
@objcMembers
修饰
鸿蒙侧忘记实现
createArkUIView()
鸿蒙侧必须实现此方法返回
ComponentContent
忘记在 Native 侧注册自定义 View自定义 View 必须注册后才能使用
performTaskWhenRenderViewDidLoad
之外调用
renderView
View 方法必须在
performTaskWhenRenderViewDidLoad
回调中调用

❌ 错误做法✅ 正确做法
View名字Kuikly侧与原生侧不一致确保
viewName()
返回值与原生注册名完全一致
自定义View未设置宽高导致不显示自定义View跨端侧使用时必须显式设置宽高(如
size(w, h)
),否则可能不会显示
iOS侧View类名与Kuikly侧viewName不一致iOS通过类名动态创建View,类名必须与
viewName()
一致
iOS侧用Swift实现但未加
@objc
注解
Swift实现的View需要
@objc
@objcMembers
修饰
鸿蒙侧忘记实现
createArkUIView()
鸿蒙侧必须实现此方法返回
ComponentContent
忘记在原生侧注册自定义View自定义View必须注册后才能使用
performTaskWhenRenderViewDidLoad
之外调用
renderView
View方法必须在
performTaskWhenRenderViewDidLoad
回调中调用