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:
viewName: the name of the component corresponding to the native componentAttr: the component's properties, used to specify which properties the component containsEvent: used to receive events sent from the native component方法: the methods supported by the component itself, ultimately implemented on the native side
Steps:
- Create a new View class that extends
DeclarativeBaseView<XxxAttr, XxxEvent> - Implement the
viewName()、createAttr()、createEvent()methods - Define the
Attrclass extendingAttr, and add property methods - Define the
Eventclass extendingEvent, and add event registration methods - Write declarative API extension functions
- 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:
- Determine the target platforms to support (Android/iOS/HarmonyOS/H5/Mini Program).
- Create the corresponding View class in the host project of the target platform.
- Implement the
setPropmethod to handle properties and events. - Implement the
callmethod to handle method calls. - 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 |