JAVA自定义组件
介绍
自定义组件有三种:基本组件的组合组件、继承基本组件、自我绘制组件。前面两种比较简单,这篇文章主要讲的是自我绘制组件。
实现步骤
1、继承组件类,实现构造方法
2、测量组件大小
3、根据测量大小绘制组件
4、实现事件接口,让组件响应用户输入
组件类继承
继承组件的基类Component,将组件xml的自定义属性的值取出备用。
if(attrSet.getAttr("backgroud_color").isPresent()){
backgroud_color = attrSet.getAttr("backgroud_color").get().getColorValue();
}
if(attrSet.getAttr("circle_color").isPresent()){
circle_color = attrSet.getAttr("circle_color").get().getColorValue();
}
这里的属性在xml中定义,此处自定义属性以“cc:”开头。“cc”在哪里定义了,很多人都会很好奇,android有一个属性名称、类型设置的文件,然而这里并没有。这里是在使用页面头部:“xmlns:cc=“http://schemas.huawei.com/res/cc””,
这里是等号之前的字符串来设置的,非常方便。
CircleComponent组件使用时标签需要包含其包路径,具体组件使用代码如下:
<com.example.javacustomcomponent.component.CircleComponent
ohos:height="match_content"
ohos:width="match_content"
cc:backgroud_color = "#00008b"
cc:circle_color= "#00ffff"/>
组件测量
这一步我们需要弄懂三种测试模式,不同测量模式,测量结果也不同:
1、UNCONSTRAINT,父组件对子组件没有约束,表示子组件可以任意大小。例如,带滚动条的父布局,组件设置多大就是多大。
2、PRECISE,父组件已确定子组件的大小。例如,组件设置match_parent。
3、NOT_EXCEED,已为子组件确定了最大大小,子组件不能超过指定大小。例如,组件设置match_content,此时使用组件默认大小。
我们需要继承Component.EstimateSizeListener接口,实现onEstimateSize方法来完成测量,并把测量结果使用setEstimatedSize对组件进行设置。
这里当测试模式设置为match_content,使用默认宽高100*2,其他模式遵循组件基类原有规则。具体代码如下:
public boolean onEstimateSize(int widthEstimateSize, int heightEstimateSize) {
//获取自己xml配置的高度、模式
int width = EstimateSpec.getSize(widthEstimateSize);
int height = EstimateSpec.getSize(heightEstimateSize);
int widthMode = EstimateSpec.getMode(widthEstimateSize);
int heightMode = EstimateSpec.getMode(heightEstimateSize);
if(widthMode == EstimateSpec.NOT_EXCEED){
width = 100*2;
}
if(heightMode == EstimateSpec.NOT_EXCEED){
height = 100*2;
}
radius = Math.min(width,height) >> 1;
setEstimatedSize(EstimateSpec.getSizeWithMode(width,widthMode),EstimateSpec.getSizeWithMode(height,heightMode));
return true;
}
组件绘制
我们需要继承Component.DrawTask接口,实现onDraw方法来完成绘制。这里使用了上面测量的结果radius,来绘制一个长宽为radius*2的正方形,再绘制一个半径为radius的圆。
具体代码如下:
public void onDraw(Component component, Canvas canvas) {
Paint paint = new Paint();
paint.setColor(backgroud_color);
canvas.drawRect(0,0,radius*2,radius*2,paint);
paint.setColor(isOnTouch ? backgroud_color : circle_color);
canvas.drawCircle(radius,radius,radius,paint);
}
事件实现
这里以Touch事件为例,继承Component.TouchEventListener接口,实现onTouchEvent方法。这里实现的功能是,组件内部点击会变成一种颜色的正方形,组件外部点击会变回原来的正方形内部一个圆的形式。具体代码如下:
public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
switch (touchEvent.getAction()) {
case TouchEvent.PRIMARY_POINT_DOWN:
MmiPoint absPoint = touchEvent.getPointerPosition(touchEvent.getIndex());
if(absPoint.getX()<radius*2 && absPoint.getY()<radius*2){
isOnTouch = true;
invalidate();
}else {
isOnTouch = false;
invalidate();
}
break;
}
return false;
}
总结
实现自定义组件,测量组件大小、根据测量大小绘制组件、实现事件接口这三步,均需要在构造函数添加其监听。
完整的java代码如下:
import ohos.agp.components.AttrSet;
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.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;
public class CircleComponent extends Component implements Component.EstimateSizeListener,Component.DrawTask, Component.TouchEventListener{
private int radius = 100;
private Color backgroud_color = Color.BLACK;
private Color circle_color = Color.RED;
private boolean isOnTouch = false;
public CircleComponent(Context context) {
super(context,null);
}
public CircleComponent(Context context, AttrSet attrSet) {
super(context, attrSet);
setEstimateSizeListener(this);
addDrawTask(this);
if(attrSet.getAttr("backgroud_color").isPresent()){
backgroud_color = attrSet.getAttr("backgroud_color").get().getColorValue();
}
if(attrSet.getAttr("circle_color").isPresent()){
circle_color = attrSet.getAttr("circle_color").get().getColorValue();
}
setTouchEventListener(this);
}
@Override
public boolean onEstimateSize(int widthEstimateSize, int heightEstimateSize) {
//获取自己xml配置的高度
int width = EstimateSpec.getSize(widthEstimateSize);
int height = EstimateSpec.getSize(heightEstimateSize);
int widthMode = EstimateSpec.getMode(widthEstimateSize);
int heightMode = EstimateSpec.getMode(heightEstimateSize);
if(widthMode == EstimateSpec.NOT_EXCEED){
width = 100*2;
}
if(heightMode == EstimateSpec.NOT_EXCEED){
height = 100*2;
}
radius = Math.min(width,height) >> 1;
setEstimatedSize(EstimateSpec.getSizeWithMode(width,widthMode),EstimateSpec.getSizeWithMode(height,heightMode));
return true;
}
@Override
public void onDraw(Component component, Canvas canvas) {
Paint paint = new Paint();
paint.setColor(backgroud_color);
canvas.drawRect(0,0,radius*2,radius*2,paint);
paint.setColor(isOnTouch ? backgroud_color : circle_color);
canvas.drawCircle(radius,radius,radius,paint);
}
@Override
public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
switch (touchEvent.getAction()) {
case TouchEvent.PRIMARY_POINT_DOWN:
MmiPoint absPoint = touchEvent.getPointerPosition(touchEvent.getIndex());
if(absPoint.getX()<radius*2 && absPoint.getY()<radius*2){
isOnTouch = true;
invalidate();
}else {
isOnTouch = false;
invalidate();
}
break;
}
return false;
}
完整的xml代码如下:
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
xmlns:cc="http://schemas.huawei.com/res/cc"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical">
<DirectionalLayout
ohos:height="200vp"
ohos:width="200vp"
ohos:background_element="$ohos:color:id_color_badge_red">
<com.example.javacustomcomponent.component.CircleComponent
ohos:height="match_content"
ohos:width="match_content"
cc:backgroud_color = "#00008b"
cc:circle_color= "#00ffff"/>
</DirectionalLayout>
</DirectionalLayout>
可以通过id 获取吗
可以的