一、相关接口类
| 接口类名 | 描述 |
| TAVEditor | SDK 操作入口类。 |
| TAVWidgetContentView | SDK 手势操作封装基类。 |
二、挂件编辑过程
当手势点击某个挂件时,TAVWidgetContentView 会调用 ITAVWidgetEditViewFactory 中的方法去创建挂件编辑视图,目前内置的基础编辑视图有两种:
·贴纸基础编辑视图:TAVStickerEditView
·画中画基础编辑视图:TAVPipEditView
挂件边框和操作按钮的绘制都在相应的 EditView 进行绘制,可以通过继承相应的 EditView 对不同挂件绘制不同的编辑框。
三、主要接口介绍
| 接口名 | 描述 |
| ITAVWidgetEventListener | 挂件操作的事件接口,挂件激活、取消激活、拖动、点击等事件都会在该接口回调。 |
| ITAVWidgetTouchOutsideListener | 没有点中任何挂件的回调。 |
| ITAVWidgetEditViewFactory | 创建自定义挂件编辑视图的工厂。 |
| ITAVWidgetMoveLimitProvider | 挂件移动策略提供者。 |
四、主要方法介绍
| 方法名 | 描述 |
| setTouchable | 设置是否可以进行手势操作。 |
| setNoFocusGestureEnabled | 设置是否可以直接拖动挂件,无需先选中挂件。 |
| setWidgetType | 设置可以操作的挂件类型。 |
| setMoveRegionRect | 设置挂件拖动区域。 |
| activeWidget | 激活指定挂件。 |
| resignCurrentActive | 取消激活当前激活的挂件。 |
| addWidgetEventListener | 添加挂件操作事件监听。 |
| setWidgetTouchOutsideListener | 设置没有点到挂件的回调。 |
| setEditViewFactory | 设置自定义的挂件编辑视图工厂。如果认为 SDK 内部默认的编辑视图不足以满足业务需求,可以自行实现创建编辑视图。 |
| setSyncWithRender | 设置操作挂件是否与渲染同步。 |
| setMoveLimitProvider | 设置挂件移动的限制策略,可为每个挂件指定移动限制方式和移动限制区域。 |
五、设置自定义的贴纸编辑视图
1. 先自行实现贴纸编辑视图。
public class MyStickerEditView extends TAVStickerEditView { public MyStickerEditView(Context context, TAVSticker sticker) { super(context, sticker); // 自己的逻辑 // 如果需要获取挂件在View中的顶点位置,可以使用 getVertexPoints 方法获得 } }
2. 然后为 TAVWidgetContentView 设置挂件编辑视图工厂:
public class MyWidgetContentView extends TAVWidgetContentView { public MyWidgetContentView(Context context) { super(context); setEditViewFactory(new ITAVWidgetEditViewFactory() { @Override public TAVStickerEditView createStickerEditView(TAVSticker sticker) { return new MyStickerEditView(context, sticker); } @Override public TAVPipEditView createPipEditView(TAVPip pip) { // 不需要自定义画中画编辑视图时,返回 SDK 默认实现 return new TAVPipEditView(context, pip); } }); } }
3. 在 TAVEditor initWithPreview 的时候传入自定义的 TAVWidgetContentView:
// 通过 TAVMagicFactory 创建编辑器实例 TAVEditor editor = TAVMagicFactory.createEditor(); TAVEditorConstants.PreviewParam param = new TAVEditorConstants.PreviewParam(); param.videoView = findViewById(R.id.preview_container); // FrameLayout,承载预览画面 // 传入自定义的TAVWidgetContentView param.contentView = new MyWidgetContentView(context); param.loopPlay = true; param.autoPlay = true; editor.initWithPreview(param);
六、监听挂件手势事件
public class MyWidgetContentView extends TAVWidgetContentView { public MyWidgetContentView(Context context) { super(context); // 添加挂件事件监听 addWidgetEventListener(new ITAVWidgetEventListener() { @Override public void onWidgetClick(TAVWidget widget, MotionEvent event) { // 挂件被点击 } @Override public void onWidgetTouchBegin(TAVWidget widget, MotionEvent event) { // 挂件开始拖动 } @Override public void onWidgetTouchEnd(TAVWidget widget, MotionEvent event) { // 挂件拖动结束 } @Override public void onWidgetAdjust(TAVWidget widget, TAVWidgetOperationMode operationMode) { // 挂件正在拖动 } @Override public void onWidgetActive(TAVWidget widget) { // 挂件被激活编辑 } @Override public void onWidgetResign(TAVWidget widget) { // 挂件被取消激活 } }); }}
七、挂件手势层可配置项
TAVWidgetContentView 的公开配置能力,可在自定义子类的构造函数中调用。
挂件类型常量(位掩码)
| 常量 | 值 | 说明 |
| WIDGET_TYPE_NONE | 0 | 不响应任何挂件。 |
| WIDGET_TYPE_STICKER | 1 | 贴纸。 |
| WIDGET_TYPE_PIP | 2 | 画中画(1 << 1)。 |
| WIDGET_TYPE_ALL | 3 | 贴纸 + 画中画,默认值。 |
手势事件常量(位掩码)
| 常量 | 值 | 说明 |
| EVENT_TYPE_NONE | 0 | 不响应任何手势。 |
| EVENT_TYPE_DRAG | 1 | 拖动。 |
| EVENT_TYPE_DOUBLE_ZOOM_ROTATE | 2 | 双指缩放/旋转。 |
| EVENT_TYPE_SINGLE_ZOOM_ROTATE | 4 | 单指缩放/旋转。 |
| EVENT_TYPE_CLICK | 8 | 点击。 |
| EVENT_TYPE_ALL | 15 | 全部事件,默认值。 |
配置方法
| 方法 | 说明 | 默认 |
| bindEditor(TAVEditor editor) | 绑定编辑器并注册贴纸/画中画观察者;重复调用抛 IllegalStateException。 | – |
| setWidgetType(int widgetType) / getWidgetType() | 参与手势的挂件类型,取值用 WIDGET_TYPE_* 组合;设置后立即刷新编辑视图可见性。 | WIDGET_TYPE_ALL |
| setEventType(int eventType) | 响应的手势事件类型,取值用 EVENT_TYPE_* 组合。 | EVENT_TYPE_ALL |
| setTouchable(boolean touchable) | 是否响应触摸。 | true |
| setNoFocusGestureEnabled(boolean enabled) | 无焦点手势开关。 | false |
| setSyncWithRender(boolean syncWithRender) | 是否与渲染同步。 | true |
| setMoveRegionRect(Rect rect) | 限制挂件可拖动区域。 | – |
| setMoveLimit(TAVWidgetMoveLimit moveLimit) | 统一设置挂件移动限制模式。 | – |
| setMoveLimitProvider(ITAVWidgetMoveLimitProvider provider) | 按挂件分别指定移动限制模式与限制区域。 | – |
状态查询与激活
| 方法 | 说明 |
| activeWidget(TAVWidget widget) / activeWidget(int id) | 激活指定挂件,进入可编辑状态。 |
| resignCurrentActive() | 取消当前挂件的激活状态。 |
| getActiveWidget() / getActiveEditView() | 获取当前激活的挂件与其编辑视图。 |
| getWidgetEditView(int id) | 按挂件 ID 取对应的编辑视图。 |
| forEachEditView(TAVConsumer<TAVWidgetEditView> consumer) | 遍历所有编辑视图。 |
可重写钩子(子类覆盖)
| 方法 | 说明 |
| positionInterceptor(float x, float y) | 挂件拖动位置拦截,返回最终绘制坐标。 |
| rotateInterceptor(float degree) | 旋转角度拦截,返回最终角度。 |
| scaleInterceptor(float scale) | 缩放比例拦截,返回最终比例;覆盖后可能导致最大/最小缩放限制失效。 |
| shouldShowEditView() | 控制编辑视图是否展示。 |
| updateWidgetEditViewVisibility() | 编辑视图可见性更新逻辑。 |
| isTouchOnSticker(TAVSticker sticker, float x, float y) / isTouchOnPip(TAVPip pip, float x, float y) | 自定义命中判定。 |
挂件状态与移动限制枚举
| 枚举 | 取值 | 说明 |
| TAVWidgetEditMode | DEFAULT / ACTIVE / INACTIVE | 默认状态 / 激活(可交互编辑) / 非激活(不可交互)。 |
| TAVWidgetMoveLimit | LIMIT_CENTER_POINT / LIMIT_VERTEX | 限制挂件中心点 / 限制挂件顶点。 |
public class MyWidgetContentView extends TAVWidgetContentView { public MyWidgetContentView(Context context) { super(context); // 只让贴纸响应拖动与点击 setWidgetType(WIDGET_TYPE_STICKER); setEventType(EVENT_TYPE_DRAG | EVENT_TYPE_CLICK); // 移动时限制顶点,避免拖出画面 setMoveLimit(TAVWidgetMoveLimit.LIMIT_VERTEX); } @Override protected PointF positionInterceptor(float x, float y) { // 返回拦截处理后的坐标 return super.positionInterceptor(x, y); }}
八、高级操作
需要重写 TAVWidgetContentView 相关方法。
九、自定义 TAVWidgetContentView 展示逻辑
@Override protected boolean shouldShowEditView() { // 增加自己的展示判断逻辑 return super.shouldShowEditView(); } @Override protected void updateWidgetEditViewVisibility() { if (null != currentEditView && null != currentWidget) { final boolean shouldShow = shouldShowEditView(); // 根据展示条件修改TAVWidgetEditView可见性 } }
十、操作拦截
TAVWidgetContentView 提供了下列接口实现对挂件的拦截:
| 接口名 | 描述 |
| positionInterceptor | 挂件位置拦截。 |
| rotateInterceptor | 挂件旋转角度拦截。 |
| scaleInterceptor | 挂件缩放拦截。 |
示例代码展示了通过重写 positionInterceptor 方法来实现移动时的位置吸附效果:
@Override protected PointF positionInterceptor(float x, float y) { PointF translation = new PointF(x, y); PointF[] previewPoints = currentEditView.computeVertexPoints(translation); float previewCenterX = (previewPoints[0].x + previewPoints[2].x) / 2f; float previewCenterY = (previewPoints[0].y + previewPoints[2].y) / 2f; float viewCenterX = getWidth() / 2f; float viewCenterY = getHeight() / 2f; float deltaX = viewCenterX – previewCenterX; float deltaY = viewCenterY – previewCenterY; // 小于10像素吸附 if (Math.abs(deltaX) <= 10) { translation.x += deltaX; } if (Math.abs(deltaY) <= 10) { translation.y += deltaY; } return translation; }