【鸿蒙】HarMonyOS的自定义组件之五星好评

使用Java语言开发鸿蒙系统应用的自定义组件五角星,并实现五星好评的效果。

如果对自定义组件并不了解的同学请先看【鸿蒙】HarMonyOS的自定义组件一

我们知道所有的组件都是继承至Component类,我们绘制五角星也不例外,我们先定义一个类继承至Component类,并实现几个构造方法

public class StarsComponent extends Component{

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

     public StarsComponent(Context context, AttrSet attrSet) {
        super(context, attrSet);
    }
}

接下来设置自定义组件的宽度和高度,这个设置需要实现Component.EstimateSizeListener接口,并在构造方法中添加宽高改变事件,同时重写onEstimateSize方法

public class StarsComponent extends Component implements
        Component.EstimateSizeListener{

     public StarsComponent(Context context) {
        super(context);
        //添加组件大小监听器
        setEstimateSizeListener(this);
        
    }

     public StarsComponent(Context context, AttrSet attrSet) {
        super(context, attrSet);
    }

     @Override
    public boolean onEstimateSize(int i, int i1) {
        /*i = (int) (radius * Math.cos(degree2Radian(DEGREE) / 2) * 2);
        i1 = (int) (radius + radius
                * Math.cos(degree2Radian(DEGREE)));*/
        int w = Component.EstimateSpec.getSize(i);
        int h = Component.EstimateSpec.getSize(i1);
        setEstimatedSize(
                //NOT_EXCEED 已为子组件确定了最大大小,子组件不能超过指定大小。
                //PRECISE 父组件已确定子组件的大小。
                //UNCONSTRAINT 父组件对子组件没有约束,表示子组件可以任意大小。
                Component.EstimateSpec.getChildSizeWithMode(w, w, EstimateSpec.NOT_EXCEED),
                Component.EstimateSpec.getChildSizeWithMode(h, h, EstimateSpec.NOT_EXCEED)
        );
        return true;
    }
}

 接下来初始化画笔参数,设置绘制图形的时候所需要的颜色,线条的宽度,是否需要填充图形等等

public class StarsComponent extends Component implements
        Component.EstimateSizeListener{
    // 画笔
    private Paint paint;
     public StarsComponent(Context context) {
        super(context);
        //添加组件大小监听器
        setEstimateSizeListener(this);
         //初始化画笔
        initPaint();
        
    }

     public void initPaint() {
        //创建画笔
        paint = new Paint();
        //设置画笔颜色
        paint.setColor(Color.GRAY);
        //设置线条宽度
        paint.setStrokeWidth(20);
        //设置线条样式
        //STROKE_STYLE 空心线条
        //FILL_STYLE 实心
        //FILLANDSTROKE_STYLE实心和边框线条
        paint.setStyle(Paint.Style.FILL_STYLE);
    }

     public StarsComponent(Context context, AttrSet attrSet) {
        super(context, attrSet);
    }

     @Override
    public boolean onEstimateSize(int i, int i1) {
        /*i = (int) (radius * Math.cos(degree2Radian(DEGREE) / 2) * 2);
        i1 = (int) (radius + radius
                * Math.cos(degree2Radian(DEGREE)));*/
        int w = Component.EstimateSpec.getSize(i);
        int h = Component.EstimateSpec.getSize(i1);
        setEstimatedSize(
                //NOT_EXCEED 已为子组件确定了最大大小,子组件不能超过指定大小。
                //PRECISE 父组件已确定子组件的大小。
                //UNCONSTRAINT 父组件对子组件没有约束,表示子组件可以任意大小。
                Component.EstimateSpec.getChildSizeWithMode(w, w, EstimateSpec.NOT_EXCEED),
                Component.EstimateSpec.getChildSizeWithMode(h, h, EstimateSpec.NOT_EXCEED)
        );
        return true;
    }
}

 那么接下来开始绘制五角星,绘制五角星需要分析五角星的绘制过程,我们通过数学的几何图形这么课程可以知道,绘制五角星需要将一个圆平均切为10个圆弧作为五角星的十个连接点,所有我们需要设置圆的半径,圆弧的角度为36°

public class StarsComponent extends Component implements
        Component.EstimateSizeListener{
    private float radius = 20;//绘制五角星的圆形半径
    private int color = 0xFF0000;//默认的颜色
    private final static float DEGREE = 36; // 五角星角度
    // 画笔
    private Paint paint;
    
     public StarsComponent(Context context) {
        super(context);
        //添加组件大小监听器
        setEstimateSizeListener(this);
         //初始化画笔
        initPaint();
        
    }

    public float getRadius() {
        return radius;
    }

    public void setRadius(float radius) {
        this.radius = radius;
    }

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }

     public void initPaint() {
        //创建画笔
        paint = new Paint();
        //设置画笔颜色
        paint.setColor(Color.GRAY);
        //设置线条宽度
        paint.setStrokeWidth(20);
        //设置线条样式
        //STROKE_STYLE 空心线条
        //FILL_STYLE 实心
        //FILLANDSTROKE_STYLE实心和边框线条
        paint.setStyle(Paint.Style.FILL_STYLE);
    }

     public StarsComponent(Context context, AttrSet attrSet) {
        super(context, attrSet);
    }

     @Override
    public boolean onEstimateSize(int i, int i1) {
        /*i = (int) (radius * Math.cos(degree2Radian(DEGREE) / 2) * 2);
        i1 = (int) (radius + radius
                * Math.cos(degree2Radian(DEGREE)));*/
        int w = Component.EstimateSpec.getSize(i);
        int h = Component.EstimateSpec.getSize(i1);
        setEstimatedSize(
                //NOT_EXCEED 已为子组件确定了最大大小,子组件不能超过指定大小。
                //PRECISE 父组件已确定子组件的大小。
                //UNCONSTRAINT 父组件对子组件没有约束,表示子组件可以任意大小。
                Component.EstimateSpec.getChildSizeWithMode(w, w, EstimateSpec.NOT_EXCEED),
                Component.EstimateSpec.getChildSizeWithMode(h, h, EstimateSpec.NOT_EXCEED)
        );
        return true;
    }
}

 接下来需要实现Component.DrawTask接口,并重写onDraw方法,通过Canvas绘制图层和图形

public class StarsComponent extends Component implements
        Component.EstimateSizeListener, Component.DrawTask,{
    private float radius = 20;//绘制五角星的圆形半径
    private int color = 0xFF0000;//默认的颜色
    private final static float DEGREE = 36; // 五角星角度
    // 画笔
    private Paint paint;
    
     public StarsComponent(Context context) {
        super(context);
        //添加组件大小监听器
        setEstimateSizeListener(this);
         //初始化画笔
        initPaint();
        // 添加绘制任务
        addDrawTask(this);
    }

    public float getRadius() {
        return radius;
    }

    public void setRadius(float radius) {
        this.radius = radius;
    }

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }

     public void initPaint() {
        //创建画笔
        paint = new Paint();
        //设置画笔颜色
        paint.setColor(Color.GRAY);
        //设置线条宽度
        paint.setStrokeWidth(20);
        //设置线条样式
        //STROKE_STYLE 空心线条
        //FILL_STYLE 实心
        //FILLANDSTROKE_STYLE实心和边框线条
        paint.setStyle(Paint.Style.FILL_STYLE);
    }

     public StarsComponent(Context context, AttrSet attrSet) {
        super(context, attrSet);
    }

     @Override
    public boolean onEstimateSize(int i, int i1) {
        /*i = (int) (radius * Math.cos(degree2Radian(DEGREE) / 2) * 2);
        i1 = (int) (radius + radius
                * Math.cos(degree2Radian(DEGREE)));*/
        int w = Component.EstimateSpec.getSize(i);
        int h = Component.EstimateSpec.getSize(i1);
        setEstimatedSize(
                //NOT_EXCEED 已为子组件确定了最大大小,子组件不能超过指定大小。
                //PRECISE 父组件已确定子组件的大小。
                //UNCONSTRAINT 父组件对子组件没有约束,表示子组件可以任意大小。
                Component.EstimateSpec.getChildSizeWithMode(w, w, EstimateSpec.NOT_EXCEED),
                Component.EstimateSpec.getChildSizeWithMode(h, h, EstimateSpec.NOT_EXCEED)
        );
        return true;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        Path path = new Path();
        float radian = degree2Radian(DEGREE);
        float radius_in = (float) (radius * Math.sin(radian / 2) / Math
                .cos(radian)); // 中间五边形的半径

        path.moveTo((float) (radius * Math.cos(radian / 2)), 0);
        path.lineTo(
                (float) (radius * Math.cos(radian / 2) + radius_in
                        * Math.sin(radian)),
                (float) (radius - radius * Math.sin(radian / 2)));
        path.lineTo((float) (radius * Math.cos(radian / 2) * 2),
                (float) (radius - radius * Math.sin(radian / 2)));
        path.lineTo(
                (float) (radius * Math.cos(radian / 2) + radius_in
                        * Math.cos(radian / 2)), (float) (radius + radius_in
                        * Math.sin(radian / 2)));
        path.lineTo(
                (float) (radius * Math.cos(radian / 2) + radius
                        * Math.sin(radian)),
                (float) (radius + radius * Math.cos(radian)));
        path.lineTo((float) (radius * Math.cos(radian / 2)),
                (float) (radius + radius_in));
        path.lineTo(
                (float) (radius * Math.cos(radian / 2) - radius
                        * Math.sin(radian)),
                (float) (radius + radius * Math.cos(radian)));
        path.lineTo(
                (float) (radius * Math.cos(radian / 2) - radius_in
                        * Math.cos(radian / 2)), (float) (radius + radius_in
                        * Math.sin(radian / 2)));
        path.lineTo(0, (float) (radius - radius * Math.sin(radian / 2)));
        path.lineTo(
                (float) (radius * Math.cos(radian / 2) - radius_in
                        * Math.sin(radian)),
                (float) (radius - radius * Math.sin(radian / 2)));

        path.close();
        canvas.drawPath(path, paint);
    }

    /**
     * 角度转弧度
     *
     * @param degree
     * @return
     */
    private float degree2Radian(float degree) {
        //把一个圆分成五份
        return (float) (Math.PI * degree / 180);
    }
}

这时候一个五角星就绘制完成了,那么接下来给该五角星添加触摸事件,当该五角星被第一次点击之后被点亮为红色,第二次点击将该五角星变为灰色。该类实现Component.TouchEventListener接口,并重写onTouchEvent方法

public class StarsComponent extends Component implements
        Component.EstimateSizeListener, Component.DrawTask,Component.TouchEventListener{
    private float radius = 20;//绘制五角星的圆形半径
    private int color = 0xFF0000;//默认的颜色
    private final static float DEGREE = 36; // 五角星角度
    // 画笔
    private Paint paint;
    
     public StarsComponent(Context context) {
        super(context);
        //添加组件大小监听器
        setEstimateSizeListener(this);
         //初始化画笔
        initPaint();
        // 添加绘制任务
        addDrawTask(this);
        //添加触摸事件
        setTouchEventListener(this);
    }

    public float getRadius() {
        return radius;
    }

    public void setRadius(float radius) {
        this.radius = radius;
    }

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }

     public void initPaint() {
        //创建画笔
        paint = new Paint();
        //设置画笔颜色
        paint.setColor(Color.GRAY);
        //设置线条宽度
        paint.setStrokeWidth(20);
        //设置线条样式
        //STROKE_STYLE 空心线条
        //FILL_STYLE 实心
        //FILLANDSTROKE_STYLE实心和边框线条
        paint.setStyle(Paint.Style.FILL_STYLE);
    }

     public StarsComponent(Context context, AttrSet attrSet) {
        super(context, attrSet);
    }

     @Override
    public boolean onEstimateSize(int i, int i1) {
        /*i = (int) (radius * Math.cos(degree2Radian(DEGREE) / 2) * 2);
        i1 = (int) (radius + radius
                * Math.cos(degree2Radian(DEGREE)));*/
        int w = Component.EstimateSpec.getSize(i);
        int h = Component.EstimateSpec.getSize(i1);
        setEstimatedSize(
                //NOT_EXCEED 已为子组件确定了最大大小,子组件不能超过指定大小。
                //PRECISE 父组件已确定子组件的大小。
                //UNCONSTRAINT 父组件对子组件没有约束,表示子组件可以任意大小。
                Component.EstimateSpec.getChildSizeWithMode(w, w, EstimateSpec.NOT_EXCEED),
                Component.EstimateSpec.getChildSizeWithMode(h, h, EstimateSpec.NOT_EXCEED)
        );
        return true;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        Path path = new Path();
        float radian = degree2Radian(DEGREE);
        float radius_in = (float) (radius * Math.sin(radian / 2) / Math
                .cos(radian)); // 中间五边形的半径

        path.moveTo((float) (radius * Math.cos(radian / 2)), 0);
        path.lineTo(
                (float) (radius * Math.cos(radian / 2) + radius_in
                        * Math.sin(radian)),
                (float) (radius - radius * Math.sin(radian / 2)));
        path.lineTo((float) (radius * Math.cos(radian / 2) * 2),
                (float) (radius - radius * Math.sin(radian / 2)));
        path.lineTo(
                (float) (radius * Math.cos(radian / 2) + radius_in
                        * Math.cos(radian / 2)), (float) (radius + radius_in
                        * Math.sin(radian / 2)));
        path.lineTo(
                (float) (radius * Math.cos(radian / 2) + radius
                        * Math.sin(radian)),
                (float) (radius + radius * Math.cos(radian)));
        path.lineTo((float) (radius * Math.cos(radian / 2)),
                (float) (radius + radius_in));
        path.lineTo(
                (float) (radius * Math.cos(radian / 2) - radius
                        * Math.sin(radian)),
                (float) (radius + radius * Math.cos(radian)));
        path.lineTo(
                (float) (radius * Math.cos(radian / 2) - radius_in
                        * Math.cos(radian / 2)), (float) (radius + radius_in
                        * Math.sin(radian / 2)));
        path.lineTo(0, (float) (radius - radius * Math.sin(radian / 2)));
        path.lineTo(
                (float) (radius * Math.cos(radian / 2) - radius_in
                        * Math.sin(radian)),
                (float) (radius - radius * Math.sin(radian / 2)));

        path.close();
        canvas.drawPath(path, paint);
    }

    /**
     * 角度转弧度
     *
     * @param degree
     * @return
     */
    private float degree2Radian(float degree) {
        //把一个圆分成五份
        return (float) (Math.PI * degree / 180);
    }

     private int count=0;
    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        count++;
        //当手指触摸到该五角星时,改变颜色为红色,并刷新图层
        if (touchEvent.getAction()==TouchEvent.PRIMARY_POINT_DOWN){
            //判断点击的次数,奇数次点亮,偶数次不点亮
            if (count%2==1)paint.setColor(Color.RED);
            else paint.setColor(Color.GRAY);
            invalidate();
        }
        new ToastDialog(this.getContext()).setText("你的评价是我前进的动力").show();
        return false;
    }
}

这时候这个自定义的五角星就完成了,那么接下来我们需要AbilitySlice类展示组件,然后动态创建一个线性布局,并设置宽度和高度为填充整个手机屏幕高宽,设置布局摆放顺序为横向摆放。然后循环遍历五次,创建五个五角星组件对象,设置其五角星的高宽和绘制半径,以及五角星的大小,并将绘制的五角星添加至现形布局,最后将布局加载至界面进行渲染即可。

package com.example.hm_phone_java.slice;

import com.example.hm_phone_java.views.CustomComponent;
import com.example.hm_phone_java.views.StarsComponent;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.element.ShapeElement;

public class StarsAbilitySlice extends AbilitySlice {

    @Override
    protected void onStart(Intent intent) {
        super.onStart(intent);
        //创建线性布局
        DirectionalLayout myLayout=new DirectionalLayout(this);
        //设置布局方向为横向对齐
        myLayout.setOrientation(DirectionalLayout.HORIZONTAL);
        //设置线性布局高宽
        DirectionalLayout.LayoutConfig config = new DirectionalLayout.LayoutConfig(
                DirectionalLayout.LayoutConfig.MATCH_PARENT, DirectionalLayout.LayoutConfig.MATCH_PARENT);
        //将设置的布局参数添加至线性布局对象
        myLayout.setLayoutConfig(config);

        for (int i = 0; i < 5; i++) {
            //创建五角星组件对象
            StarsComponent component = new StarsComponent(this);
            //设置五角星的绘制半径
            component.setRadius(80);
            //设置五角星的高宽
            DirectionalLayout.LayoutConfig lc=new DirectionalLayout.LayoutConfig(200,200);
            lc.setMargins(10,10,10,10);
            //将参数添加至五角星组件对象
            component.setLayoutConfig(lc);
            //将五角星添加至线性布局
            myLayout.addComponent(component);
        }
        //将线性布局加载至界面显示
        super.setUIContent(myLayout);
    }
}

将该StarsAbilitySlice添加至主界面中

package com.example.hm_phone_java;

import com.example.hm_phone_java.slice.*;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;

public class MainAbility extends Ability {
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        //设置启动页
        super.setMainRoute(StarsAbilitySlice.class.getName());
    }
}

最后启动华为模拟器,即可看到效果。

下一篇 【鸿蒙】HarMonyOS的自定义组件之五星红旗

猜你喜欢

转载自blog.csdn.net/u010321564/article/details/118754827