SuperAPI AI Directory

kuikly-expand-view

Frontend & websites

View repository

Updated 10-15 23:46

About this skill

Contents

  • Core Guidelines
  • Workflow: Creating Expand View (Kuikly Side)
  • Workflow: Implementing Native Side View

Core Guidelines

  • View is the unified interface for cross-platform UI components: Kuikly has already encapsulated commonly used Views (Text, Image, List, etc.). Custom Views are used to reuse existing native UI components or meet special requirements.
  • Custom Views require dual-side implementation: Define the component structure on the Kuikly side (viewName, Attr, Event), and implement the specific rendering logic on the Native side (Android/iOS/HarmonyOS/H5/Mini Program), associated through viewName .
  • The component name must be consistent across all platforms: The return value of viewName() on the Kuikly side must exactly match the name registered on the Native side.
  • Properties and events go through the setProp method: The Native side receives properties and events set by the Kuikly side through setProp(propKey, propValue) .
  • Method calls go through the call method: The Native side responds to method calls from the Kuikly side through call(method, params, callback) .

Workflow: Creating Expand View (Kuikly Side)

Use this workflow to create a expand View on the Kuikly (Kotlin) side.

A Kuikly component consists of four parts:

  1. viewName: the name of the component corresponding to the native component
  2. Attr: the component's properties, used to specify which properties the component contains
  3. Event: used to receive events sent from the native component
  4. 方法: the methods supported by the component itself, ultimately implemented on the native side

Steps:

  1. Create a new View class that extends DeclarativeBaseView<XxxAttr, XxxEvent>
  2. Implement the viewName()、createAttr()、createEvent() methods
  3. Define the Attr class extending Attr, and add property methods
  4. Define the Event class extending Event, and add event registration methods
  5. Write declarative API extension functions
  6. Use the component in business code

Step 1: Define the View class

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")
        }
    }
}

Step 2: Define the Attr class

Attr represents the set of properties supported by the component.

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: Define the Event class

Event represents the events supported by the component.

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: Component methods (optional)

Component methods are APIs exposed on the Kuikly side, with the actual implementation on the Native side.

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)
            }
        }
    }
}

⚠️ Note:

  • View methods support asynchronous callback results, i.e. renderView?.callMethod("method", "params", callback), butdo not support synchronous return results。
  • View methodsdo not support passing binary data. If you need to transmit binary content such as images, please pass it in JSON form after Base64 encoding, or use a Module method to implement it.

Step 5: Write the declarative API

import com.tencent.kuikly.core.base.ViewContainer

fun ViewContainer<*, *>.MyImage(init: MyImageView.() -> Unit) {
    addChild(MyImageView(), init)
}

Step 6: Business usage

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}")
                }
            }
        }
    }
}

Use ViewRef to get a component reference

Through ViewRef you can get a component instance and call component methods elsewhere in the Pager.

@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

Use this workflow to implement the Native side of a expand View.

Prerequisite: You only need to implement the Native-side View on the platforms the business needs to support; there is no need to implement it for all platforms.

Steps:

  1. Determine the target platforms to support (Android/iOS/HarmonyOS/H5/Mini Program).
  2. Create the corresponding View class in the host project of the target platform.
  3. Implement the setProp method to handle properties and events.
  4. Implement the call method to handle method calls.
  5. Register the View with the Kuikly framework.

For platform-specific implementations, see VIEW_IMPLEMENT.md

Quick reference: View base classes and registration methods for each platform

Platform Base class/protocol Registration method Notes
Android IKuiklyRenderViewExport registerExternalRenderView in renderViewExport(name) { View(ctx) } Inherit from the native View and implement the interface
iOS KuiklyRenderViewExportProtocol The class name must match viewName(dynamically created at runtime) Property and method naming setCss_xxx, method naming css_xxx
HarmonyOS (ArkTS) KuiklyRenderBaseView getCustomRenderViewCreatorRegisterMap or getCustomRenderViewCreatorRegisterMapV2 register in Must implement createArkUIView() return ComponentContent
H5 IKuiklyRenderViewExport registerExternalRenderView in renderViewExport(name) { View() } Override the ele property to return the DOM element
Mini Program IKuiklyRenderViewExport registerExternalRenderView + Transform.addComponentsAlias + base.wxml template Must create MiniXxxElement and configure the template

Data types supported by setProp on the Native side

Kuikly side type Native side receiving type
String String / NSString
Int / Float / Double Corresponding basic type / NSNumber
Boolean Boolean / BOOL
Event 回调 KuiklyRenderCallback

Event triggering mechanism

On the Native side, events are passed through KuiklyRenderCallback callback to the Kuikly side:

// 保存回调
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
    ))
}

Common pitfalls and correct practices

❌ Wrong practice ✅ Correct practice
View name mismatch between the Kuikly side and the Native side Ensure that viewName() return value exactly matches the Native registration name
Custom View does not display because width and height are not set When a custom View is used across platforms, width and height must be explicitly set (e.g., size(w, h)), otherwise it may not display
iOS side View class name does not match the Kuikly side viewName iOS dynamically creates Views by class name,The class name must match viewName() consistent
iOS side implemented in Swift but missing the @objc annotation A View implemented in Swift requires @objc or @objcMembers modifier
Forgot to implement on the HarmonyOS side createArkUIView() The HarmonyOS side must implement this method to return ComponentContent
Forgot to register the custom View on the Native side A custom View must be registered before it can be used
In performTaskWhenRenderViewDidLoad call outside renderView View methods must be called within the performTaskWhenRenderViewDidLoad callback