基于Canvas的MyMathDraw[我的数学板][底部弹窗][API V6]

1. 名称

  • 本次使用Canvas实现了一个绘制数学函数的app,命名为:我的数学板,MyMathDraw。
  • 项目采用了parsii-4.0库,特此声明
  • 项目已经放置在Gitee仓库中:MyMathDraw
  • 本专栏关于使用开发语言的声明:全专栏专注于Java语言开发模式!
  • app图标:

在这里插入图片描述

2. 功能描述

  • 具有一个底部弹窗,可以在弹出的设置小窗口中进行设置。
  • 具有绘制函数的功能,输入数学函数,点击表情,即可在TextField中绘制图像。

在这里插入图片描述

3. app实现关键技巧

  • 实现底部弹窗:本次app使用了StackLayout,最底层放置绘制的界面,最上层放置弹窗界面,其中弹窗界面上部分是灰色透明,下部分是圆角长方形弹窗,用于设置。默认情况,将其设置为HIDE即可,当用户点击左上角文字时,设置为VISIBLE,即可达到“弹窗”功能。
  • 实现函数绘制:采用描点法绘制函数,密集的描点即可。其中对函数表达式采用parsii库提供的表达式解析功能,生成每个x对应的y值,放入ArrayList中。

4. 源代码

  4.1 java源代码

    4.1.1 MainAbilitySlice.java

package com.tdtxdcxm.mymathdraw.slice;

import com.tdtxdcxm.mymathdraw.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import parsii.eval.Expression;
import parsii.eval.Parser;
import parsii.eval.Scope;
import parsii.eval.Variable;
import parsii.tokenizer.ParseException;

import java.util.ArrayList;

public class MainAbilitySlice extends AbilitySlice {

    DirectionalLayout rootdl_stkl_ddl2,rootdl_stkl_ddl2_set;
    Button topdl_but;

    TextField bottomdl_tfd;

    Text topdl_txt,paintswitch_text,uiswitch_text;
    Switch switch_paintcolor,switch_uicolor;

    Paint bluepaint = new Paint();
    Paint purplepaint = new Paint();

    Paint paint = bluepaint;//用于绘制任务的默认画笔(蓝色)
    int origin_x,origin_y;

    boolean isgeneratexy = false;

    public void initPaint(){
        bluepaint.setColor(Color.BLUE);
        bluepaint.setStrokeWidth(8);

        purplepaint.setColor(new Color(Color.rgb(160, 32, 240)));
        purplepaint.setStrokeWidth(8);
    }

    public void initMASComponents(){
        topdl_txt = (Text) findComponentById(ResourceTable.Id_topdl_txt);
        topdl_but = (Button) findComponentById(ResourceTable.Id_topdl_but);
        bottomdl_tfd = (TextField) findComponentById(ResourceTable.Id_bottomdl_tfd);


        rootdl_stkl_ddl2 = (DirectionalLayout) findComponentById(ResourceTable.Id_rootdl_stkl_ddl2);
        rootdl_stkl_ddl2_set = (DirectionalLayout) findComponentById(ResourceTable.Id_rootdl_stkl_ddl2_set);

        paintswitch_text = (Text) findComponentById(ResourceTable.Id_paintswitch_text);
        uiswitch_text = (Text) findComponentById(ResourceTable.Id_uiswitch_text);

        switch_paintcolor = (Switch) findComponentById(ResourceTable.Id_switch_paintcolor);
        switch_uicolor = (Switch) findComponentById(ResourceTable.Id_switch_uicolor);

        bottomdl_tfd.setHint("输入函数>>>"+"\n【右上角表情】:\n1.单击—绘制"+"\n2.长按-清空");


        topdl_txt.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                bottomdl_tfd.clearFocus();
                rootdl_stkl_ddl2.setVisibility(Component.VISIBLE);
                if(topdl_but.getText().equals("😁")){
                    topdl_but.setText("🤣");
                }
            }
        });

        topdl_but.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                bottomdl_tfd.clearFocus();
                String function = bottomdl_tfd.getText();

                ArrayList<float[]> xylist = generateXY(function);//根据表达式生成许多x、y点坐标
                if(xylist == null){
                    return;
                }
                bottomdl_tfd.addDrawTask(new Component.DrawTask() {
                    @Override
                    public void onDraw(Component component, Canvas canvas) {
                        if(isgeneratexy) {
                            for (float[] xyfloats : xylist) {
                                canvas.drawPoint(xyfloats[0], xyfloats[1], paint);
                            }
                        }
                    }
                }, Component.DrawTask.BETWEEN_BACKGROUND_AND_CONTENT);
            }
        });
        topdl_but.setLongClickedListener(new Component.LongClickedListener() {
            @Override
            public void onLongClicked(Component component) {
                bottomdl_tfd.setText("");
                bottomdl_tfd.clearFocus();
                bottomdl_tfd.addDrawTask(new Component.DrawTask() {
                    @Override
                    public void onDraw(Component component, Canvas canvas) {
                        //用于清空,画一个空白
                    }
                }, Component.DrawTask.BETWEEN_BACKGROUND_AND_CONTENT);
            }
        });


        rootdl_stkl_ddl2.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                //弹出设置面板后,单击其余区域会退出(收回)设置状态
                rootdl_stkl_ddl2.setVisibility(Component.HIDE);
                topdl_but.setText("😁");
            }
        });
        rootdl_stkl_ddl2_set.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                //这里用来“吸收”单击事件,否则当单击设置面板时,也会退出设置状态
            }
        });


        switch_paintcolor.setCheckedStateChangedListener(new AbsButton.CheckedStateChangedListener() {
            @Override
            public void onCheckedChanged(AbsButton absButton, boolean b) {
                if(b == true){
                    paint = purplepaint;//把紫色画笔给绘制任务画笔
                    paintswitch_text.setText("画笔:紫色");
                    paintswitch_text.setTextColor(new Color(Color.rgb(160, 32, 240)));
                }
                else{
                    paint = bluepaint;//把蓝色画笔给绘制任务画笔,还原默认
                    paintswitch_text.setText("画笔:蓝色");
                    paintswitch_text.setTextColor(Color.BLUE);
                }
            }
        });
        switch_uicolor.setCheckedStateChangedListener(new AbsButton.CheckedStateChangedListener() {
            @Override
            public void onCheckedChanged(AbsButton absButton, boolean b) {
                if(b == true){
                    uiswitch_text.setText("界面:黑暗");
                    uiswitch_text.setTextColor(new Color(Color.rgb(54, 54, 54)));


                    ShapeElement shape = new ShapeElement();
                    shape.setRgbColor(new RgbColor(105,105,105));
                    bottomdl_tfd.setBackground(shape);

                }
                else{
                    uiswitch_text.setText("界面:明亮");
                    uiswitch_text.setTextColor(new Color(Color.rgb(238, 99, 99)));

                    ShapeElement shape = new ShapeElement();
                    shape.setRgbColor(new RgbColor(255,255,255));
                    bottomdl_tfd.setBackground(shape);
                }
            }
        });
    }

    public ArrayList<float[]> generateXY(String function){
        if(function.equals("")){
            return null;
        }
        origin_x = bottomdl_tfd.getWidth() / 2;//将绘图坐标远点的x设定在此
        origin_y = bottomdl_tfd.getHeight() / 2;//将绘图坐标远点的y设定在此
        System.out.println(origin_x);
        System.out.println(origin_y);


        ArrayList<float[]> xylist = new ArrayList<>();

        StringBuilder stringBuilder = new StringBuilder(function);
        Scope scope = new Scope();
        Variable var_x = scope.getVariable("x");
        Expression expression = null;
        try {
            expression = Parser.parse(function, scope);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        if(expression == null){
            return null;
        }

        for(float x = -400f;x <= 400.0f;x = x + 0.001f){
            var_x.setValue((double) x);
            double y = expression.evaluate();
            //将图形默认放大
            float[] xyfloats = {x*140+origin_x,-140*((float) y)+origin_y};

            xylist.add(xyfloats);
        }
        isgeneratexy = true;
        return xylist;
    }

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
        initPaint();
        initMASComponents();

    }

    @Override
    public void onInactive() {
        super.onInactive();
    }

    @Override
    public void onActive() {
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }

    @Override
    public void onBackground() {
        super.onBackground();
    }

    @Override
    public void onStop() {
        super.onStop();
    }
}

    4.1.2 MainAbility.java

package com.tdtxdcxm.mymathdraw;

import com.tdtxdcxm.mymathdraw.slice.MainAbilitySlice;
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(MainAbilitySlice.class.getName());
    }
}

    4.1.3 MyApplication.java

package com.tdtxdcxm.mymathdraw;

import ohos.aafwk.ability.AbilityPackage;

public class MyApplication extends AbilityPackage {
    @Override
    public void onInitialize() {
        super.onInitialize();
    }
}

  4.2 UI背景XML代码

    4.2.1 background_ability_ddl2_set.xml

<?xml version="1.0" encoding="UTF-8" ?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
       ohos:shape="rectangle">
    <corners
        ohos:radius="20vp"/>
    <solid
        ohos:color="#B2F1EDED"/>
</shape>

    4.2.2 background_ability_main.xml

<?xml version="1.0" encoding="UTF-8" ?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
       ohos:shape="rectangle">
    <solid
        ohos:color="#FFFFFF"/>
</shape>

  4.3 主页面XML代码

    4.3.1 ability_main.xml

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:id="$+id:mymathdraw_rootdl"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:alignment="center"
    ohos:orientation="vertical">
    <StackLayout
        ohos:id="$+id:rootdl_stkl"
        ohos:height="match_parent"
        ohos:width="match_parent">
        <DirectionalLayout
            ohos:id="$+id:rootdl_stkl_ddl1"
            ohos:height="match_parent"
            ohos:width="match_parent"

            ohos:alignment="center"
            ohos:orientation="vertical"

            ohos:background_element="white">
            <DirectionalLayout
                ohos:id="$+id:rootdl_stkl_ddl1_topdl"
                ohos:height="0"
                ohos:weight="0.7"
                ohos:width="match_parent"

                ohos:alignment="center"
                ohos:orientation="horizontal"

                ohos:background_element="#70B3ECF3">
                <Text
                    ohos:id="$+id:topdl_txt"
                    ohos:height="match_parent"
                    ohos:width="0"
                    ohos:weight="8"

                    ohos:text="MathPad"
                    ohos:text_size="35vp"
                    ohos:text_color="#FF2DDE53"
                    ohos:text_alignment="vertical_center"
                    >
                </Text>
                <Button
                    ohos:id="$+id:topdl_but"
                    ohos:height="match_parent"
                    ohos:width="0"
                    ohos:weight="2"

                    ohos:text="😁"
                    ohos:text_size="30vp"
                    ohos:text_alignment="center"

                    ohos:background_element="#9FA9EFE5">
                </Button>
            </DirectionalLayout>
            <DirectionalLayout
                ohos:id="$+id:rootdl_stkl_ddl1_bottomdl"
                ohos:height="0"
                ohos:weight="9.3"
                ohos:width="match_parent"

                ohos:alignment="center"
                ohos:orientation="vertical"
                ohos:background_element="#FFFFFFFF">
                <TextField
                    ohos:id="$+id:bottomdl_tfd"
                    ohos:height="match_parent"
                    ohos:width="match_parent"

                    ohos:hint="输入函数>>>"
                    ohos:text_size="25vp"
                    ohos:text_alignment="start"

                    ohos:background_element="#FFFFFFFF">
                </TextField>
            </DirectionalLayout>
        </DirectionalLayout>
        <DirectionalLayout
            ohos:id="$+id:rootdl_stkl_ddl2"
            ohos:height="match_parent"
            ohos:width="match_parent"

            ohos:visibility="hide"

            ohos:alignment="bottom"
            ohos:orientation="vertical"

            ohos:background_element="#00FFFFFF">
            <DirectionalLayout
                ohos:id="$+id:rootdl_stkl_ddl2_set"

                ohos:height="220vp"
                ohos:width="300vp"

                ohos:bottom_margin="20vp"

                ohos:layout_alignment="center"
                ohos:background_element="$graphic:background_ability_ddl2_set"

                ohos:orientation="vertical"
                ohos:alignment="center">
                <DirectionalLayout
                    ohos:height="0"
                    ohos:weight="1"
                    ohos:width="match_parent"

                    ohos:alignment="bottom"
                    ohos:orientation="horizontal">
                    <Text
                        ohos:id="$+id:paintswitch_text"
                        ohos:height="30vp"
                        ohos:width="0"
                        ohos:weight="1"

                        ohos:text_alignment="center"
                        ohos:auto_font_size="true"
                        ohos:text="画笔:蓝色"
                        ohos:text_color="blue"
                        >
                    </Text>
                    <Text
                        ohos:id="$+id:uiswitch_text"
                        ohos:height="30vp"
                        ohos:width="0"
                        ohos:weight="1"

                        ohos:text_alignment="center"
                        ohos:auto_font_size="true"
                        ohos:text="界面:明亮"
                        ohos:text_color="#FFEE6363"
                        >
                    </Text>
                </DirectionalLayout>
                <DirectionalLayout
                    ohos:height="0"
                    ohos:weight="1"
                    ohos:width="match_parent"

                    ohos:alignment="center"
                    ohos:orientation="horizontal">
                    <Switch
                        ohos:id="$+id:switch_paintcolor"
                        ohos:height="25vp"
                        ohos:width="90vp"

                        ohos:right_margin="30vp"

                        ohos:auto_font_size="true"

                        ohos:text_state_on="紫色"
                        ohos:text_color_on="red"

                        ohos:text_state_off="蓝色"
                        ohos:text_color_off="black"
                        >
                    </Switch>
                    <Switch
                        ohos:id="$+id:switch_uicolor"
                        ohos:height="25vp"
                        ohos:width="90vp"

                        ohos:left_margin="30vp"

                        ohos:auto_font_size="true"

                        ohos:text_state_on="黑暗"
                        ohos:text_color_on="red"

                        ohos:text_state_off="明亮"
                        ohos:text_color_off="black"
                        >
                    </Switch>
                </DirectionalLayout>
            </DirectionalLayout>
        </DirectionalLayout>
    </StackLayout>

</DirectionalLayout>

5. build.gradle(Entry模块中的)

apply plugin: 'com.huawei.ohos.hap'
apply plugin: 'com.huawei.ohos.decctest'
//For instructions on signature configuration, see https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ide_debug_device-0000001053822404#section1112183053510
ohos {
    compileSdkVersion 6
    defaultConfig {
        compatibleSdkVersion 6
    }
    buildTypes {
        release {
            proguardOpt {
                proguardEnabled false
                rulesFiles 'proguard-rules.pro'
            }
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
    testImplementation 'junit:junit:4.13.1'
    ohosTestImplementation 'com.huawei.ohos.testkit:runner:2.0.0.200'
    implementation 'com.scireum:parsii:4.0'
}
decc {
    supportType = ['html', 'xml']
}

6. app截图

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

7. app运行视频(本地模拟器运行)

基于Canvas的MyMathDraw[我的数学板][底部弹窗][API V6]

posted @ 2022-03-20 17:18  TDTX  阅读(70)  评论(0编辑  收藏  举报