当Java UI框架提供的组件无法满足设计需求时,可以创建自定义组件,根据设计需求添加绘制任务,并定义组件的属性及事件响应,完成组件的自定义。
常用接口
接口名 | 作用 |
---|---|
setEstimateSizeListener | 设置测量组件的侦听器。 |
onEstimateSize | 测量组件的大小以确定宽度和高度。 |
setEstimatedSize | 将测量的宽度和高度设置给组件。 |
EstimateSpec.getChildSizeWithMode | 基于指定的大小和模式为子组件创建度量规范。 |
EstimateSpec.getSize | 从提供的度量规范中提取大小。 |
EstimateSpec.getMode | 获取该组件的显示模式。 |
addDrawTask | 添加绘制任务。 |
onDraw | 通过绘制任务更新组件时调用。 |
如何实现自定义组件
下面以自定义圆环组件为例,介绍自定义组件的通用配置方法:在屏幕中绘制蓝色圆环,并实现点击变化圆环颜色的功能。
图1 在界面中显示的自定义圆环组件
- 创建自定义组件的类,并继承Component或其子类,添加构造方法。
示例代码如下:
public class CustomComponent extends Component{ public CustomComponent(Context context) { super(context); } }
- 实现Component.EstimateSizeListener接口,在onEstimateSize方法中进行组件测量,并通过setEstimatedSize方法将测量的宽度和高度设置给组件。示例代码如下:
public class CustomComponent extends Component implements Component.EstimateSizeListener { public CustomComponent(Context context) { super(context); ... // 设置测量组件的侦听器 setEstimateSizeListener(this); } ... @Override public boolean onEstimateSize(int widthEstimateConfig, int heightEstimateConfig) { int width = Component.EstimateSpec.getSize(widthEstimateConfig); int height = Component.EstimateSpec.getSize(heightEstimateConfig); setEstimatedSize( Component.EstimateSpec.getChildSizeWithMode(width, width, Component.EstimateSpec.NOT_EXCEED), Component.EstimateSpec.getChildSizeWithMode(height, height, Component.EstimateSpec.NOT_EXCEED)); return true; } }
- 注意事项
- 自定义组件测量出的大小需通过setEstimatedSize设置给组件,并且必须返回true使测量值生效。
- setEstimatedSize方法的入参携带模式信息,可使用Component.EstimateSpec.getChildSizeWithMode方法进行拼接。
- 测量模式测量组件的宽高需要携带模式信息,不同测量模式下的测量结果也不相同,需要根据实际需求选择适合的测量模式。
表2 测量模式信息 模式
作用
UNCONSTRAINT
父组件对子组件没有约束,表示子组件可以任意大小。
PRECISE
父组件已确定子组件的大小。
NOT_EXCEED
已为子组件确定了最大大小,子组件不能超过指定大小。
- 注意事项
- 实现Component.DrawTask接口,在onDraw方法中执行绘制任务,该方法提供的画布Canvas,可以精确控制屏幕元素的外观。在执行绘制任务之前,需要定义画笔Paint。
示例代码如下:
public class CustomComponent extends Component implements Component.DrawTask,Component.EstimateSizeListener { // 圆环宽度 private static final float CIRCLE_STROKE_WIDTH = 100f; // 绘制圆环的画笔 private Paint circlePaint; public CustomComponent(Context context) { super(context); // 初始化画笔 initPaint(); // 添加绘制任务 addDrawTask(this); } private void initPaint(){ circlePaint = new Paint(); circlePaint.setColor(Color.BLUE); circlePaint.setStrokeWidth(CIRCLE_STROKE_WIDTH); circlePaint.setStyle(Paint.Style.STROKE_STYLE); } @Override public void onDraw(Component component, Canvas canvas) { // 在界面中绘制一个圆心坐标为(500,500),半径为400的圆 canvas.drawCircle(500,500,400,circlePaint); } ... }
- 实现Component.TouchEventListener或其他事件的接口,使组件可响应用户输入。示例代码如下:
public class CustomComponent extends Component implements Component.DrawTask, Component.EstimateSizeListener, Component.TouchEventListener { ... public CustomComponent(Context context) { ... // 设置TouchEvent响应事件 setTouchEventListener(this); } ... @Override public boolean onTouchEvent(Component component, TouchEvent touchEvent) { switch (touchEvent.getAction()) { case TouchEvent.PRIMARY_POINT_DOWN: circlePaint.setColor(Color.GREEN); invalidate(); break; case TouchEvent.PRIMARY_POINT_UP: circlePaint.setColor(Color.YELLOW); invalidate(); break; } //允许触摸事件触发后进行回调,一定要为true,不然只会监听按下的点击事件,不会触发弹起的事件 return true; } }
- 注意事项
- 需要更新UI显示时,可调用invalidate()方法。
- 示例中展示TouchEventListener为响应触摸事件,除此之外还可实现ClickedListener响应点击事件、LongClickedListener响应长按事件等。
- 注意事项
- 在onStart()方法中,将自定义组件添加至UI界面中。
@Override protected void onStart(Intent intent) { super.onStart(intent); DirectionalLayout.LayoutConfig config = new DirectionalLayout.LayoutConfig( DirectionalLayout.LayoutConfig.MATCH_PARENT, DirectionalLayout.LayoutConfig.MATCH_PARENT); myLayout.setLayoutConfig(config); CustomComponent customComponent = new CustomComponent(this); DirectionalLayout.LayoutConfig layoutConfig = new DirectionalLayout.LayoutConfig(1080, 1000); customComponent.setLayoutConfig(layoutConfig); myLayout.addComponent(customComponent); super.setUIContent(myLayout); }
接下来是绘制矩形,三角形,线条,三角形,点,绘制文字等等效果,代码如下
@Override
public void onDraw(Component component, Canvas canvas) {
//绘制图形
// 在界面中绘制一个圆心坐标为(500,500),半径为400的圆
canvas.drawCircle(200,400,100,paint);
//左上角坐标点为(100,100),右下角坐标点为(200,200),绘制一个正方形
canvas.drawRect(100,100,200,200,paint);
//绘制文字
canvas.drawChars(paint,new char[]{'你','好','鸿','蒙'},100,700);
canvas.drawCharSequence(paint,"加油华为",100,900);
//画线
canvas.drawLine(500,100,700,500,paint);
//画点
canvas.drawPoint(900,900,paint);
//画三角形
canvas.drawLines(new float[]{400,700,600,800,
600,800,500,600,
500,600,400,700},paint);
}
其效果如下:绘制的图形效果和通过触摸事件点击之后的效果,手指离开屏幕后恢复颜色
整个自定义组件完整代码如下:
package com.example.hm_phone_java.views;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;
public class CustomComponent extends Component implements Component.EstimateSizeListener, Component.DrawTask, Component.TouchEventListener {
public CustomComponent(Context context) {
super(context);
//添加组件大小监听器
setEstimateSizeListener(this);
//初始化画笔
initPaint();
// 添加绘制任务
addDrawTask(this);
//添加触摸事件
setTouchEventListener(this);
}
@Override
public boolean onEstimateSize(int width, int height) {
int w=Component.EstimateSpec.getSize(width);
int h=Component.EstimateSpec.getSize(height);
setEstimatedSize(
//NOT_EXCEED 已为子组件确定了最大大小,子组件不能超过指定大小。
//PRECISE 父组件已确定子组件的大小。
//UNCONSTRAINT 父组件对子组件没有约束,表示子组件可以任意大小。
Component.EstimateSpec.getChildSizeWithMode(w,w, EstimateSpec.NOT_EXCEED),
Component.EstimateSpec.getChildSizeWithMode(h,h, EstimateSpec.NOT_EXCEED)
);
return true;
}
// 圆环宽度
private static final float CIRCLE_STROKE_WIDTH = 50f;
// 绘制圆环的画笔
private Paint paint;
public void initPaint(){
//创建画笔
paint=new Paint();
//设置画笔颜色
paint.setColor(Color.BLUE);
//设置线条宽度
paint.setStrokeWidth(CIRCLE_STROKE_WIDTH);
//设置线条样式
//STROKE_STYLE 空心线条
//FILL_STYLE 实心
//FILLANDSTROKE_STYLE实心和边框线条
paint.setStyle(Paint.Style.FILL_STYLE);
//设置绘制文字大小
paint.setTextSize(60);
}
@Override
public void onDraw(Component component, Canvas canvas) {
//绘制图形
// 在界面中绘制一个圆心坐标为(500,500),半径为400的圆
canvas.drawCircle(200,400,100,paint);
//左上角坐标点为(100,100),右下角坐标点为(200,200),绘制一个正方形
canvas.drawRect(100,100,200,200,paint);
//绘制文字
canvas.drawChars(paint,new char[]{'你','好','鸿','蒙'},100,700);
canvas.drawCharSequence(paint,"加油华为",100,900);
//画线
canvas.drawLine(500,100,700,500,paint);
//画点
canvas.drawPoint(900,900,paint);
//画三角形
canvas.drawLines(new float[]{400,700,600,800,
600,800,500,600,
500,600,400,700},paint);
}
@Override
public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
switch (touchEvent.getAction()){
case TouchEvent.PRIMARY_POINT_DOWN:
//自定义组件被按下,改变颜色为红色,并重新绘制,刷新图层
paint.setColor(Color.RED);
invalidate();
break;
case TouchEvent.PRIMARY_POINT_UP:
paint.setColor(Color.BLUE);
invalidate();
break;
}
//记得将返回值改为true,表示该监听事件允许回传
return true;
}
}
界面完整代码如下:
package com.example.hm_phone_java.slice;
import com.example.hm_phone_java.ResourceTable;
import com.example.hm_phone_java.views.CustomComponent;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.DirectionalLayout;
public class OneCustomComponentAbilitySlice extends AbilitySlice {
@Override
protected void onStart(Intent intent) {
super.onStart(intent);
DirectionalLayout myLayout=new DirectionalLayout(this);
DirectionalLayout.LayoutConfig config = new DirectionalLayout.LayoutConfig(
DirectionalLayout.LayoutConfig.MATCH_PARENT, DirectionalLayout.LayoutConfig.MATCH_PARENT);
myLayout.setLayoutConfig(config);
CustomComponent customComponent = new CustomComponent(this);
DirectionalLayout.LayoutConfig layoutConfig = new DirectionalLayout.LayoutConfig(1080, 1000);
customComponent.setLayoutConfig(layoutConfig);
myLayout.addComponent(customComponent);
super.setUIContent(myLayout);
}
}
并将该界面的类名添加至MainAbility进行引用,接着开启华为模拟器即可看到自定义控件的效果。
初次学习鸿蒙的自定义控件,大家如果掌握的,可以自行绘制图形,比如五边形,六边形,五角星等等
下一篇文章将带着大家使用鸿蒙开发学习自定义组件绘制五星红旗,尽请期待
今天就分享到这里,感谢大家的关注和阅读,因最近工作比较繁忙,没有及时更新文章!!!