自定义原生标签

客户端实现自定义标签

Android

继承 CCardWidget 并实现其抽象方法,主要方法为 onCreateView,在这里返回对应的自定义 View。

public class CustomCubeWidget extends CCardWidget {
    private static final String TAG = "CustomCubeWidget";
    private Map<String, Object> createMap;
    private TextView root;

    public CustomCubeWidget(Context context) {
        super(context);
    }

    @Override
    public View onCreateView(Map<String, Object> params, int width, int height) {
        MPLogger.debug(TAG, "onCreateView:" + width + "," + height);
        createMap = params;
        for (String key : params.keySet()) {
            MPLogger.debug(TAG, key + ":" + params.get(key));
        }
        if (root == null) {
            root = new TextView(getContext());
            setText(root, params.get("value") + "");
            root.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Map<String, Object> eventParams = new HashMap<>();
                    eventParams.put("p1", "widget:" + System.currentTimeMillis());
                    CubeService.instance().getEngine().sendEvent(createMap, "on-WidgetToCube", eventParams);
                }
            });
        }
        return root;
    }

    @Override
    public void onReuse(Map<String, Object> params, int width, int height) {
        MPLogger.debug(TAG, "onReuse");
    }

    @Override
    public void onUpdateData(Map<String, Object> params) {
        MPLogger.debug(TAG, "onUpdateData");
    }

    @Override
    public void onRecycleAndCached() {
        MPLogger.debug(TAG, "onRecycleAndCached");
    }

    @Override
    public boolean canReuse() {
        MPLogger.debug(TAG, "canReuse");
        return false;
    }

    @Override
    public void onDestroy() {
        MPLogger.debug(TAG, "onDestroy");
    }

    @JsMethod
    public void cubeToWidget(JSONObject param, CubeJSCallback callback) {
        if (root != null) {
            setText(root, "接收到卡片调用自定义方法,参数:" + param.toJSONString());
        }
        callback.invoke("cubeToWidget callback data: " + System.currentTimeMillis());
    }

    private void setText(TextView tv, String msg) {
        tv.setText("我是原生组件,点我通知卡片事件回调," + msg);
    }
}

iOS

继承 CCardWidget 并实现其协议方法,其中 onCreateViewupdateData 为必须实现的方法,其他方法为可选。

#import "CustomCardView.h"

@interface CustomCardView ()

@property (nonatomic, strong) UILabel *titleLabel;

@end

@implementation CustomCardView

//必须实现
/**
 * UI 创建接口,表示当前组件要上屏的视图 UI
 *
 * @param data  map 类型,组件在卡片上声明的数据,包括样式、事件和属性,对应的 key 为DATA_KEY_STYLES、DATA_KEY_EVENTS、DATA_KEY_ATTRS;
 * @param size  View 的大小,宽高
 * @return 可用的 UIView
 */
- (UIView *)onCreateView:(NSDictionary *)data size:(CGSize)size {
    NSLog(@"数据打印:%@", data);
    
    UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height)];
    customView.backgroundColor = [UIColor redColor];
    
    self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, size.width, 30)];
    //数据来源是 data 提供
    self.titleLabel.text = @"A";
    [customView addSubview:self.titleLabel];
    
    return customView;
}

//必须实现
/**
 * 组件数据更新接口
 * @param data
 */
- (void)onUpdateData:(NSDictionary *)data {
    NSLog(@"数据打印:%@", data);
}

/**
 *  从复用池中获取的已存在控件,被使用前的数据准备。
 *  @param data 初始化数据
 *  @param size 组件复用时的大小
 */
- (void)onReuse:(NSDictionary *)data size:(CGSize)size {
    NSLog(@"数据打印:%@", data);
    NSLog(@"Size打印:%@", size);
    
    //数据来源是 data 提供
    self.titleLabel.text = @"B";

}

/**
 * 组件是否支持复用;
 * 为了提高效率,扩展组件可以支持复用。例如当某个自定义的标签组件由于数据更新被移除当前视图,此时该组件如果支持复用,那么会放入复用池内,下次该组件显示时,会直接从复用池内获取;
 * @return YES:复用;NO:不复用。
 */
- (BOOL)canReuse {
    return YES;
}

/**
 * 组件复用清理接口。如果组件支持复用(canReuse 返回 true),则组件在进入复用池后会调用
 * onRecycleAndCached 方法,表示当前组件已经离屏放入缓存内,需要清理资源。
 */
- (void)onRecycleAndCache {
    //清理自定义 View 上的 SubView 内容
    self.titleLabel.text = @"";
}

鸿蒙

调用 Component 创建自定义组件

@Component
export struct MyCustomComponent {
  @State
  node: CKNode = new CKNode();
  @State
  cardInstance: CubeCard | null = null;
  @State
  text: string = ""

  aboutToAppear(): void {
    if (this.node) {
      this.node.component = this;
      this.updateWidgetData()
      this.node.onChange = (type: NodeChangeType, changeNode: CKNode, childNode: CKNode | null) => {
        this.updateWidgetData()
      };
    }
  }

  build() {
    if (this.node) {
      Column() {
        Text("我是原生组件,点我通知卡片事件回调,"+this.text).onClick((event) => {
          let args: Record<string, object> = {}
          args["p1"] = "widget:" + systemDateTime.getTime() as ESObject
          CWidgetApi.getInstance().sendEventToJS(this.node, 'on-WidgetToCube', args)
        })
      }
      .width(this.node.size.width)
      .height(this.node.size.height)
      .position(this.node.position)
    }
  }

  updateWidgetData(): void {
    this.text = this.node.attributes.get("value") as string
  }

  public cubeToWidget(instance: FalconInstance, args: object, func: IFalconCallback): void {
    this.text = "接收到卡片调用自定义方法,参数:"+JSON.stringify(args)
    func.invoke("cubeToWidget callback data: " + systemDateTime.getTime())
  }
}

创建 ComponentBuilder

@Builder
export function buildMyCustomComponent(builder: ComponentBuilder) {
  MyCustomComponent({
    node: builder.node,
    cardInstance: builder.cardInstance
  })
}

客户端注册自定义标签

Android

第一个参数是自定义的标签,可随意定制,在卡片侧会写在 <> 内。建议加上前缀,防止和动态卡片本身的标签冲突。第二个参数是自定义标签实现类的全路径,注意不要混淆。

Collection<CubeWidgetInfo> widgetInfos = new LinkedList<>();
widgetInfos.add(new CubeWidgetInfo("custom-widget", CustomCubeWidget.class.getName()));
CubeService.instance().getEngine().registerWidgets(widgetInfos);

iOS

第一个参数是自定义的标签,需要与卡片侧约定好,在卡片侧会写到 <> 内。建议加上前缀,防止和动态卡片本身的标签冲突。第二个参数是自定义标签实现类的类名。

CubeWidgetInfo *widgetInfo = [CubeWidgetInfo new];
widgetInfo.tag = @"custom-widget";
widgetInfo.className = [CustomCardView class];
    
NSMutableArray *widgetArray = [NSMutableArray array];
[widgetArray addObject:widgetInfo];
[[[CubeService sharedInstance] getEngine] registerWidgets:[NSArray arrayWithArray:widgetArray]];

鸿蒙

CWidgetInfo 的第一个参数是自定义的标签,可随意定制,在卡片侧会写在 <> 内。建议加上前缀,防止和动态卡片本身的标签冲突。第二个是参数是 WrappedBuilder

let customBuilder: WrappedBuilder<[ComponentBuilder]> = wrapBuilder(buildMyCustomComponent)
let customWidgetInfo: CWidgetInfo = new CWidgetInfo("custom-widget", customBuilder)
CubeEngine.getInstance().registerWidgets([customWidgetInfo])

卡片侧调用自定义标签

在标签中,写入自定义的标签,此处为 custom-widget

<template>
    <div class="root">
        <text class="message" :value="message" @click="onClick()"></text>
        <custom-widget ref="widget" class="custom" :value="message2" @on-WidgetToCube="clientToCube(param)">
        </custom-widget>
    </div>
</template>

<script>

    export default {
        data: {
            message : '我是卡片的text,点我调用自定义组件',
            message2 : '我是传给自定义组件的value'
        },
        beforeCreate() {
            this.message = '我是卡片的text,点我调用自定义组件'
            this.message2 = '我是传给自定义组件的value'
        },
        didAppear() {
            
        },        
        methods: {
            onClick() {
                console.info('invoke on-click event');
                this.$refs.widget.cubeToWidget({
                    'auto':"我是发给自定义组件方法的参数"
                },ret=>{
                    this.message = "接收到自定义组件方法的回调,参数:"+JSON.stringify(ret);
                });
            },
            //原生调用自定义标签的方法(调用JS)
            clientToCube(param){
                console.info(param.p1)
                this.message2 = "接收到自定义组件的事件回调,参数:"+JSON.stringify(param)
            }
        }
    }
</script>

<style>
    .root {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        background-color: white;
        width: 100%;
        height: 800rpx;
    }
    .custom {
        color: black;
        font-size: 20rpx;
        width: 100%;
        height: 600rpx;
    }
    .message {
        color: black;
        font-size: 20rpx;
        width: 100%;
        height: 200rpx;
    }
</style>

卡片侧调用自定义标签的对应方法

卡片调用

this.$refs.widget.cubeToWidget({
  'auto':"我是发给自定义组件方法的参数"
},ret=>{
  this.message = "接收到自定义组件方法的回调,参数:"+JSON.stringify(ret);
});

客户端接收

Android

@JsMethod
public void cubeToWidget(JSONObject param, CubeJSCallback callback) {}

iOS

//配置约定的方法
CK_EXPORT_METHOD(@selector(cubeToWidget:callBack:))
    
//自定义标签调用原生方法
- (void)cubeToWidget:(NSDictionary *)params callBack:(CubeModuleMethodCallback)callback {
    
}

鸿蒙

public cubeToWidget(instance: FalconInstance, args: object, func: IFalconCallback): void {}

客户端通知卡片侧回调

客户端通知

Android

CubeService.instance().getEngine().sendEvent(Map<String, Object> componentData, String eventName, @Nullable Map<String, Object> eventParams)

iOS

/**
 * self.data :onCreateView方法中的data
 * eventName :卡片侧的方法名,例如:@"on-WidgetToCube" 
 * eventParams : 传给卡片的参数,例如:@{@"p1":@"自定义标签调用cube方法"}
 */
[[[CubeService sharedInstance] getEngine] 
     sendEvent:self.data 
     eventName:eventName
     eventParams:eventParams];

鸿蒙

CWidgetApi.getInstance().sendEventToJS(node: CKNode, methodName: string, args: Record<string, Object> | null)

卡片接收通知

此时卡片中可以用 @eventName 来接收客户端的通知,具体可以参考以下代码示例。

<custom-widget ref="widget" class="custom" :value="message2" @on-WidgetToCube="clientToCube(param)">