JAVA自定义组件

cqyin
发布于 2021-8-24 19:47
浏览
1收藏

介绍

自定义组件有三种:基本组件的组合组件、继承基本组件、自我绘制组件。前面两种比较简单,这篇文章主要讲的是自我绘制组件。

实现步骤

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>

已于2021-8-24 19:47:48修改
3
收藏 1
回复
举报
2条回复
按时间正序
/
按时间倒序
BLUESKYHOST
BLUESKYHOST

可以通过id 获取吗

回复
2021-9-29 14:13:01
cqyin
cqyin 回复了 BLUESKYHOST
可以通过id 获取吗

可以的

回复
2021-10-12 15:44:46
回复
    相关推荐