基于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']
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现