如果内置元件无法满足你的需求,你可以通过自定义元件来扩展 Lynx 的能力。本节将向你展示如何在 Android 和 iOS 平台上创建和注册自定义元件。
自定义元件的实现分为几个步骤,包括:声明并注册元件、创建原生视图、处理样式与属性、事件绑定等。接下来以一个简单的自定义输入框元件 <input>
为例,简要介绍自定义元件的实现流程。
完整实现参见 LynxExplorer/input 模块查看。通过编译运行 LynxExplorer 示例项目可实时预览自定义元件效果。
下面是 <input>
自定义元件的实现,需要继承自 LynxUI
。
#import <Lynx/LynxUI.h>
NS_ASSUME_NONNULL_BEGIN
@interface LynxTextField : UITextField
@property(nonatomic, assign) UIEdgeInsets padding;
@end
@interface LynxExplorerInput : LynxUI <LynxTextField *> <UITextFieldDelegate>
@end
NS_ASSUME_NONNULL_END
#import "LynxExplorerInput.h"
@implementation LynxExplorerInput
//...
@end
@implementation LynxTextField
- (UIEditingInteractionConfiguration)editingInteractionConfiguration API_AVAILABLE(ios(13.0)) {
return UIEditingInteractionConfigurationNone;
}
- (void)setPadding:(UIEdgeInsets)padding {
_padding = padding;
[self setNeedsLayout];
}
- (CGRect)textRectForBounds:(CGRect)bounds {
CGFloat x = self.padding.left;
CGFloat y = self.padding.top;
CGFloat width = bounds.size.width - self.padding.left - self.padding.right;
CGFloat height = bounds.size.height - self.padding.top - self.padding.bottom;
return CGRectMake(x, y, width, height);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
@end
元件注册有两种方式:全局注册和局部注册。
全局注册的元件可以在多个 LynxView
实例中共享。
#import "LynxExplorerInput.h"
#import <Lynx/LynxComponentRegistry.h>
@implementation LynxExplorerInput
LYNX_LAZY_REGISTER_UI("input")
@end
@implementation LynxTextField
- (UIEditingInteractionConfiguration)editingInteractionConfiguration API_AVAILABLE(ios(13.0)) {
return UIEditingInteractionConfigurationNone;
}
- (void)setPadding:(UIEdgeInsets)padding {
_padding = padding;
[self setNeedsLayout];
}
- (CGRect)textRectForBounds:(CGRect)bounds {
CGFloat x = self.padding.left;
CGFloat y = self.padding.top;
CGFloat width = bounds.size.width - self.padding.left - self.padding.right;
CGFloat height = bounds.size.height - self.padding.top - self.padding.bottom;
return CGRectMake(x, y, width, height);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
@end
局部注册的元件仅适用于当前 LynxView
实例。
#import <Lynx/LynxEnv.h>
#import <Lynx/LynxView.h>
LynxView *lynxView = [[LynxView alloc] initWithBuilderBlock:^(LynxViewBuilder *builder) {
builder.config =
[[LynxConfig alloc] initWithProvider:[LynxEnv sharedInstance].config.templateProvider];
[builder.config registerUI:[LynxExplorerInput class] withName:@"input"];
}];
其中,"input"
对应前端 DSL 的标签名称。当 Lynx Engine 解析到该标签时,会查找已注册的原生元件并创建实例。
View
实例每个自定义元件都需要实现 createView
方法,该方法返回一个与之对应的原生 View
实例。
以下是 <input>
元件的实现:
#import "LynxExplorerInput.h"
#import <Lynx/LynxComponentRegistry.h>
@implementation LynxExplorerInput
LYNX_LAZY_REGISTER_UI("input")
- (UITextField *)createView {
UITextField *textField = [[LynxTextField alloc] init];
//...
textField.delegate = self;
return textField;
}
@end
@implementation LynxTextField
- (UIEditingInteractionConfiguration)editingInteractionConfiguration API_AVAILABLE(ios(13.0)) {
return UIEditingInteractionConfigurationNone;
}
- (void)setPadding:(UIEdgeInsets)padding {
_padding = padding;
[self setNeedsLayout];
}
- (CGRect)textRectForBounds:(CGRect)bounds {
CGFloat x = self.padding.left;
CGFloat y = self.padding.top;
CGFloat width = bounds.size.width - self.padding.left - self.padding.right;
CGFloat height = bounds.size.height - self.padding.top - self.padding.bottom;
return CGRectMake(x, y, width, height);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
@end
你可以使用 LYNX_PROP_SETTER
宏来监听前端传入的属性变化,并更新原生视图。例如,处理 <input>
元件的 value 属性:
#import "LynxExplorerInput.h"
#import <Lynx/LynxComponentRegistry.h>
#import <Lynx/LynxPropsProcessor.h>
@implementation LynxExplorerInput
LYNX_LAZY_REGISTER_UI("input")
LYNX_PROP_SETTER("value", setValue, NSString *) {
self.view.text = value;
}
- (UITextField *)createView {
UITextField *textField = [[LynxTextField alloc] init];
//...
textField.delegate = self;
return textField;
}
@end
@implementation LynxTextField
- (UIEditingInteractionConfiguration)editingInteractionConfiguration API_AVAILABLE(ios(13.0)) {
return UIEditingInteractionConfigurationNone;
}
- (void)setPadding:(UIEdgeInsets)padding {
_padding = padding;
[self setNeedsLayout];
}
- (CGRect)textRectForBounds:(CGRect)bounds {
CGFloat x = self.padding.left;
CGFloat y = self.padding.top;
CGFloat width = bounds.size.width - self.padding.left - self.padding.right;
CGFloat height = bounds.size.height - self.padding.top - self.padding.bottom;
return CGRectMake(x, y, width, height);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
@end
通常,Lynx Engine 会自动计算并更新 View
的排版信息,无需开发者手动处理。但在某些特殊情况下,例如需要对 View
进行额外调整时,
可以在 layoutDidFinished
回调中获取最新的排版信息,并应用自定义逻辑。
#import "LynxExplorerInput.h"
#import <Lynx/LynxComponentRegistry.h>
#import <Lynx/LynxPropsProcessor.h>
@implementation LynxExplorerInput
LYNX_LAZY_REGISTER_UI("input")
- (void)layoutDidFinished {
self.view.padding = self.padding;
}
LYNX_PROP_SETTER("value", setValue, NSString \*) {
self.view.text = value;
}
- (UITextField *)createView {
UITextField *textField = [[LynxTextField alloc] init];
//...
textField.delegate = self;
return textField;
}
@end
@implementation LynxTextField
- (UIEditingInteractionConfiguration)editingInteractionConfiguration API_AVAILABLE(ios(13.0)) {
return UIEditingInteractionConfigurationNone;
}
- (void)setPadding:(UIEdgeInsets)padding {
\_padding = padding;
[self setNeedsLayout];
}
- (CGRect)textRectForBounds:(CGRect)bounds {
CGFloat x = self.padding.left;
CGFloat y = self.padding.top;
CGFloat width = bounds.size.width - self.padding.left - self.padding.right;
CGFloat height = bounds.size.height - self.padding.top - self.padding.bottom;
return CGRectMake(x, y, width, height);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
@end
在某些场景中,前端可能需要响应自定义元件的事件。例如,当用户在文本框中输入内容时,前端可能需要获取并处理这些输入数据。
以下示例演示了如何实现从 <input>
元件向前端发送文本输入事件,以及前端如何监听该事件。
客户端通过监听原生视图的文本输入回调,当文本变更时使用 [self.context.eventEmitter dispatchCustomEvent:eventInfo]
将事件发送到前端,以便前端进行相应的处理。
#import "LynxExplorerInput.h"
#import <Lynx/LynxComponentRegistry.h>
#import <Lynx/LynxPropsProcessor.h>
@implementation LynxExplorerInput
LYNX_LAZY_REGISTER_UI("input")
- (UITextField *)createView {
UITextField *textField = [[LynxTextField alloc] init];
//...
textField.delegate = self;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(textFieldDidChange:)
name:UITextFieldTextDidChangeNotification
object:textField];
return textField;
}
- (void)emitEvent:(NSString *)name detail:(NSDictionary *)detail {
LynxCustomEvent *eventInfo = [[LynxDetailEvent alloc] initWithName:name
targetSign:[self sign]
detail:detail];
[self.context.eventEmitter dispatchCustomEvent:eventInfo];
}
- (void)textFieldDidChange:(NSNotification *)notification {
[self emitEvent:@"input"
detail:@{
@"value": [self.view text] ?: @"",
}];
}
- (void)layoutDidFinished {
self.view.padding = self.padding;
}
LYNX_PROP_SETTER("value", setValue, NSString *) {
self.view.text = value;
}
@end
@implementation LynxTextField
- (UIEditingInteractionConfiguration)editingInteractionConfiguration API_AVAILABLE(ios(13.0)) {
return UIEditingInteractionConfigurationNone;
}
- (void)setPadding:(UIEdgeInsets)padding {
_padding = padding;
[self setNeedsLayout];
}
- (CGRect)textRectForBounds:(CGRect)bounds {
CGFloat x = self.padding.left;
CGFloat y = self.padding.top;
CGFloat width = bounds.size.width - self.padding.left - self.padding.right;
CGFloat height = bounds.size.height - self.padding.top - self.padding.bottom;
return CGRectMake(x, y, width, height);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
@end
在前端,需要绑定相应的文本框输入事件。通过以下代码,前端将监听客户端发送的事件,并根据需要处理输入的数据。
const handleInput = (e) => {
const currentValue = e.detail.value.trim();
setInputValue(currentValue);
};
<input className="input-box" bindinput={handleInput} value={inputValue} />;
注意:前端 DSL 使用
bindxxx
进行事件绑定,例如bindinput
绑定input
事件。
在某些情况下,前端可能需要通过命令式 API 直接操作自定义元件,你可以通过 LYNX_UI_METHOD
让元件支持这些操作。
以下代码展示了如何在前端通过 SelectorQuery 调用 focus
方法让 <input>
元件获取焦点:
lynx
.createSelectorQuery()
.select('#input-id')
.invoke({
method: 'focus',
params: {},
success: function (res) {
console.log('lynx', 'request focus success');
},
fail: function (res : {code: number, data: any}) {
console.log('lynx', 'request focus fail');
},
})
.exec();
在客户端,需要使用 LYNX_UI_METHOD
为自定义元件添加 focus
方法,确保它能够正确处理前端的调用,
#import "LynxExplorerInput.h"
#import <Lynx/LynxComponentRegistry.h>
#import <Lynx/LynxPropsProcessor.h>
#import <Lynx/LynxUIMethodProcessor.h>
@implementation LynxExplorerInput
LYNX_LAZY_REGISTER_UI("input")
LYNX_UI_METHOD(focus) {
if ([self.view becomeFirstResponder]) {
callback(kUIMethodSuccess, nil);
} else {
callback(kUIMethodUnknown, @"fail to focus");
}
}
- (UITextField *)createView {
UITextField *textField = [[LynxTextField alloc] init];
//...
textField.delegate = self;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(textFieldDidChange:)
name:UITextFieldTextDidChangeNotification
object:textField];
return textField;
}
- (void)emitEvent:(NSString *)name detail:(NSDictionary *)detail {
LynxCustomEvent *eventInfo = [[LynxDetailEvent alloc] initWithName:name
targetSign:[self sign]
detail:detail];
[self.context.eventEmitter dispatchCustomEvent:eventInfo];
}
- (void)textFieldDidChange:(NSNotification *)notification {
[self emitEvent:@"input"
detail:@{
@"value": [self.view text] ?: @"",
}];
}
- (void)layoutDidFinished {
self.view.padding = self.padding;
}
LYNX_PROP_SETTER("value", setValue, NSString *) {
self.view.text = value;
}
@end
@implementation LynxTextField
- (UIEditingInteractionConfiguration)editingInteractionConfiguration API_AVAILABLE(ios(13.0)) {
return UIEditingInteractionConfigurationNone;
}
- (void)setPadding:(UIEdgeInsets)padding {
_padding = padding;
[self setNeedsLayout];
}
- (CGRect)textRectForBounds:(CGRect)bounds {
CGFloat x = self.padding.left;
CGFloat y = self.padding.top;
CGFloat width = bounds.size.width - self.padding.left - self.padding.right;
CGFloat height = bounds.size.height - self.padding.top - self.padding.bottom;
return CGRectMake(x, y, width, height);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
@end
在实现 focus
方法时,元件开发者需要向前端返回一个状态码,以表明操作是否成功。例如,前端调用可能会失败,此时应返回相应的错误状态,以便前端在 fail
回调进行处理。
Lynx Engine 预定义了一些常见的错误码,元件开发者可以在方法回调中返回相应的状态码:
enum LynxUIMethodErrorCode {
kUIMethodSuccess = 0, // 调用成功
kUIMethodUnknown, // 未知错误
kUIMethodNodeNotFound, // 无法找到对应的元件
kUIMethodMethodNotFound, // 该元件上没有对应的 Method
kUIMethodParamInvalid, // 方法参数无效
kUIMethodSelectorNotSupported, // 该选择器暂时不支持
};
自定义元件的实现分为几个步骤,包括:声明并注册元件、创建原生视图、处理样式与属性、事件绑定等。接下来以一个简单的自定义输入框元件 <input>
为例,简要介绍自定义元件的实现流程。
完整实现参见 LynxExplorer/input 模块查看。通过编译运行 LynxExplorer 示例项目可实时预览自定义元件效果。
下面是 <input>
自定义元件的实现,需要继承自 LynxUI
。
import com.lynx.tasm.behavior.LynxContext;
import com.lynx.tasm.behavior.ui.LynxUI;
import androidx.appcompat.widget.AppCompatEditText;
public class LynxExplorerInput extends LynxUI<AppCompatEditText> {
public LynxExplorerInput(LynxContext context) {
super(context);
}
//...
}
元件注册有两种方式:全局注册和局部注册。
全局注册的元件可以在多个 LynxView
实例中共享。
import com.lynx.tasm.LynxEnv;
import com.lynx.tasm.behavior.Behavior;
LynxEnv.inst().addBehavior(new Behavior("input"){
@Override
public LynxExplorerInput createUI(LynxContext context) {
return new LynxExplorerInput(context);
}
});
局部注册的元件仅适用于当前 LynxView
实例。
LynxViewBuilder lynxViewBuilder = new LynxViewBuilder();
lynxViewBuilder.addBehavior(new Behavior("input") {
@Override
public LynxExplorerInput createUI(LynxContext context) {
return new LynxExplorerInput(context);
}
});
其中,"input"
对应前端 DSL 的标签名称。当 Lynx Engine 解析到该标签时,会查找已注册的原生元件并创建实例。
View
实例每个自定义元件都需要实现 createView
方法,该方法返回一个与之对应的原生 View
实例。
以下是 <input>
元件的实现:
import android.content.Context;
import androidx.appcompat.widget.AppCompatEditText;
import com.lynx.tasm.behavior.LynxContext;
import com.lynx.tasm.behavior.ui.LynxUI;
public class LynxExplorerInput extends LynxUI<AppCompatEditText> {
public LynxExplorerInput(LynxContext context) {
super(context);
}
@Override
protected AppCompatEditText createView(Context context) {
AppCompatEditText view = new AppCompatEditText(context);
//...
return view;
}
}
你可以使用 @LynxProp
注解来监听前端传入的属性变化,并更新原生视图。例如,处理 <input>
元件的 value 属性:
import android.content.Context;
import androidx.appcompat.widget.AppCompatEditText;
import com.lynx.tasm.behavior.LynxContext;
import com.lynx.tasm.behavior.LynxProp;
import com.lynx.tasm.behavior.ui.LynxUI;
public class LynxExplorerInput extends LynxUI<AppCompatEditText> {
public LynxExplorerInput(LynxContext context) {
super(context);
}
@LynxProp(name = "value")
public void setValue(String value) {
if (!value.equals(mView.getText().toString())) {
mView.setText(value);
}
}
@Override
protected AppCompatEditText createView(Context context) {
AppCompatEditText view = new AppCompatEditText(context);
//...
return view;
}
}
通常,Lynx Engine 会自动计算并更新 View
的排版信息,无需开发者手动处理。但在某些特殊情况下,例如需要对 View
进行额外调整时,
可以在 onLayoutUpdated
回调中获取最新的排版信息,并应用自定义逻辑。
import android.content.Context;
import androidx.appcompat.widget.AppCompatEditText;
import com.lynx.tasm.behavior.LynxContext;
import com.lynx.tasm.behavior.LynxProp;
import com.lynx.tasm.behavior.ui.LynxUI;
public class LynxExplorerInput extends LynxUI<AppCompatEditText> {
public LynxExplorerInput(LynxContext context) {
super(context);
}
@Override
public void onLayoutUpdated() {
super.onLayoutUpdated();
int paddingTop = mPaddingTop + mBorderTopWidth;
int paddingBottom = mPaddingBottom + mBorderBottomWidth;
int paddingLeft = mPaddingLeft + mBorderLeftWidth;
int paddingRight = mPaddingRight + mBorderRightWidth;
mView.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
}
@Override
protected AppCompatEditText createView(Context context) {
AppCompatEditText view = new AppCompatEditText(context);
//...
return view;
}
@LynxProp(name = "value")
public void setValue(String value) {
if (!value.equals(mView.getText().toString())) {
mView.setText(value);
}
}
}
在某些场景中,前端可能需要响应自定义元件的事件。例如,当用户在文本框中输入内容时,前端可能需要获取并处理这些输入数据。
以下示例演示了如何实现从 <input>
元件向前端发送文本输入事件,以及前端如何监听该事件。
客户端通过监听原生视图的文本输入回调,当文本变更时使用 getEventEmitter().sendCustomEvent(detail)
将事件发送到前端,以便前端进行相应的处理。
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import androidx.appcompat.widget.AppCompatEditText;
import com.lynx.tasm.behavior.LynxContext;
import com.lynx.tasm.behavior.LynxProp;
import com.lynx.tasm.behavior.ui.LynxUI;
import com.lynx.tasm.event.LynxCustomEvent;
import java.util.HashMap;
import java.util.Map;
public class LynxExplorerInput extends LynxUI<AppCompatEditText> {
private void emitEvent(String name, Map<String, Object> value) {
LynxCustomEvent detail = new LynxCustomEvent(getSign(), name);
if (value != null) {
for (Map.Entry<String, Object> entry : value.entrySet()) {
detail.addDetail(entry.getKey(), entry.getValue());
}
}
getLynxContext().getEventEmitter().sendCustomEvent(detail);
}
@Override
protected AppCompatEditText createView(Context context) {
AppCompatEditText view = new AppCompatEditText(context);
view.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
emitEvent("input", new HashMap<String, Object>() {
{
put("value", s.toString());
}
});
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
return view;
}
public LynxExplorerInput(LynxContext context) {
super(context);
}
@Override
public void onLayoutUpdated() {
super.onLayoutUpdated();
int paddingTop = mPaddingTop + mBorderTopWidth;
int paddingBottom = mPaddingBottom + mBorderBottomWidth;
int paddingLeft = mPaddingLeft + mBorderLeftWidth;
int paddingRight = mPaddingRight + mBorderRightWidth;
mView.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
}
@LynxProp(name = "value")
public void setValue(String value) {
if (!value.equals(mView.getText().toString())) {
mView.setText(value);
}
}
}
在前端,需要绑定相应的文本框输入事件。通过以下代码,前端将监听客户端发送的事件,并根据需要处理输入的数据。
const handleInput = (e) => {
const currentValue = e.detail.value.trim();
setInputValue(currentValue);
};
<input className="input-box" bindinput={handleInput} value={inputValue} />;
注意:前端 DSL 使用
bindxxx
进行事件绑定,例如bindinput
绑定input
事件。
在某些情况下,前端可能需要通过命令式 API 直接操作自定义元件,你可以通过 @LynxUIMethod
让元件支持这些操作。
以下代码展示了如何在前端通过 SelectorQuery 调用 focus
方法让 <input>
元件获取焦点:
lynx
.createSelectorQuery()
.select('#input-id')
.invoke({
method: 'focus',
params: {},
success: function (res) {
console.log('lynx', 'request focus success');
},
fail: function (res) {
console.log('lynx', 'request focus fail');
},
})
.exec();
在客户端,需要使用 @LynxUIMethod
为自定义元件添加 focus
方法,确保它能够正确处理前端的调用,
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.inputmethod.InputMethodManager;
import androidx.appcompat.widget.AppCompatEditText;
import com.lynx.react.bridge.Callback;
import com.lynx.react.bridge.ReadableMap;
import com.lynx.tasm.behavior.LynxContext;
import com.lynx.tasm.behavior.LynxProp;
import com.lynx.tasm.behavior.LynxUIMethod;
import com.lynx.tasm.behavior.LynxUIMethodConstants;
import com.lynx.tasm.behavior.ui.LynxUI;
import com.lynx.tasm.event.LynxCustomEvent;
import java.util.HashMap;
import java.util.Map;
public class LynxExplorerInput extends LynxUI<AppCompatEditText> {
private boolean showSoftInput() {
InputMethodManager imm = (InputMethodManager) getLynxContext().getSystemService(Context.INPUT_METHOD_SERVICE);
return imm.showSoftInput(mView,
InputMethodManager.SHOW_IMPLICIT, null);
}
@LynxUIMethod
public void focus(ReadableMap params, Callback callback) {
if (mView.requestFocus()) {
if (showSoftInput()) {
callback.invoke(LynxUIMethodConstants.SUCCESS);
} else {
callback.invoke(LynxUIMethodConstants.UNKNOWN, "fail to show keyboard");
}
} else {
callback.invoke(LynxUIMethodConstants.UNKNOWN, "fail to focus");
}
}
private void emitEvent(String name, Map<String, Object> value) {
LynxCustomEvent detail = new LynxCustomEvent(getSign(), name);
if (value != null) {
for (Map.Entry<String, Object> entry : value.entrySet()) {
detail.addDetail(entry.getKey(), entry.getValue());
}
}
getLynxContext().getEventEmitter().sendCustomEvent(detail);
}
@Override
protected AppCompatEditText createView(Context context) {
AppCompatEditText view = new AppCompatEditText(context);
view.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
emitEvent("input", new HashMap<String, Object>() {
{
put("value", s.toString());
}
});
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
return view;
}
public LynxExplorerInput(LynxContext context) {
super(context);
}
@Override
public void onLayoutUpdated() {
super.onLayoutUpdated();
int paddingTop = mPaddingTop + mBorderTopWidth;
int paddingBottom = mPaddingBottom + mBorderBottomWidth;
int paddingLeft = mPaddingLeft + mBorderLeftWidth;
int paddingRight = mPaddingRight + mBorderRightWidth;
mView.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
}
@LynxProp(name = "value")
public void setValue(String value) {
if (!value.equals(mView.getText().toString())) {
mView.setText(value);
}
}
}
在实现 focus
方法时,元件开发者需要向前端返回一个状态码,以表明操作是否成功。例如,前端调用可能会失败,此时应返回相应的错误状态,以便前端在 fail
回调进行处理。
Lynx Engine 预定义了一些常见的错误码,元件开发者可以在方法回调中返回相应的状态码:
enum LynxUIMethodErrorCode {
kUIMethodSuccess, // 调用成功
kUIMethodUnknown, // 未知错误
kUIMethodNodeNotFound, // 无法找到对应的元件
kUIMethodMethodNotFound, // 该元件上没有对应的 Method
kUIMethodParamInvalid, // 方法参数无效
kUIMethodSelectorNotSupported, // 该选择器暂时不支持
}
自定义 web 中元件的方式可以直接参考 Web Components
一旦你完成了自定义元件的开发,你可以像使用内置元件一样使用它。下面是一个 <input>
元件的简单使用示例: