HarmonyOS-Java之手把手教你绘制冰墩墩
2023-02-26 12:30:51 时间
https://harmonyos.51cto.com
简介
冰墩墩买不到,金墩墩买不起,毛线墩墩不会勾,橡皮泥墩墩不会捏,雪墩墩不会堆,但我们会用程序绘制一个冰墩墩;
授人以鱼,不如授人以渔。今天手把手教大家来一个冰墩墩!
效果演示
实现思路
1 建立坐标系
工欲善其事,必先利其器,有的好的工具,冰墩墩也好,飞机坦克大炮都能绘制出来;
计算出中心点的问题,这里细节在于中线点和辅助线对齐保证美观
@Override
public boolean onEstimateSize(int widthEstimateConfig, int heightEstimateConfig) {
int componentWidth = EstimateSpec.getSize(widthEstimateConfig);
int componentHeight = EstimateSpec.getSize(heightEstimateConfig);
this.width = componentWidth;
this.height = componentHeight;
centerX = this.width / 2;
centerY = this.height / 2;
// 保证辅助线取整
centerX = ((int) (centerX / 50)) * 50;
centerY = ((int) (centerY / 50)) * 50;
Logger.d("width:" + width);
Logger.d("height:" + height);
Logger.d("centerX:" + centerX);
Logger.d("centerY:" + centerY);
recordBg();//初始化时录制坐标系和网格
setEstimatedSize(
EstimateSpec.getChildSizeWithMode(componentWidth, componentWidth, EstimateSpec.PRECISE),
EstimateSpec.getChildSizeWithMode(componentHeight, componentHeight, EstimateSpec.PRECISE)
);
return true;
}
初始化坐标系
private Point mCoo;//坐标系
private Picture mPicture;//坐标系和网格的Canvas元件
/**
* 初始化时录制坐标系和网格
*/
private void recordBg() {
//准备屏幕尺寸
Point winSize = new Point(width, height);
mCoo = new Point(centerX, centerY);
Paint gridPaint = new Paint();
mPicture = new Picture();
Canvas recordCanvas = mPicture.beginRecording(winSize.getPointXToInt(), winSize.getPointYToInt());
//绘制辅助网格
HelpDraw2.drawGrid(recordCanvas, winSize, gridPaint);
//绘制坐标系
HelpDraw2.drawCoo(recordCanvas, mCoo, winSize, gridPaint);
mPicture.endRecording();
}
onDraw方法中绘制坐标系
canvas.drawPicture(mPicture);
画布辅助类
/**
* 辅助画布
*
* @since 2022-02-09
*/
public class HelpDraw2 {
/**
* 绘制网格
*/
public static void drawGrid(Canvas recordCanvas, Point winSize, Paint paint) {
//初始化网格画笔
paint.setStrokeWidth(2);
paint.setColor(Color.GRAY);
paint.setStyle(Paint.Style.STROKE_STYLE);
//设置虚线效果new float[]{可见长度, 不可见长度},偏移值
paint.setPathEffect(new PathEffect(new float[]{10, 5}, 0));
recordCanvas.drawPath(HelpPath.gridPath(50, winSize), paint);
}
/**
* 绘制坐标系
* @param recording 画布
* @param coo 坐标系原点
* @param winSize 屏幕尺寸
* @param paint 画笔
*/
public static void drawCoo(Canvas recording, Point coo, Point winSize, Paint paint) {
//初始化网格画笔
paint.setStrokeWidth(4);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE_STYLE);
//设置虚线效果new float[]{可见长度, 不可见长度},偏移值
paint.setPathEffect(null);
//绘制直线
recording.drawPath(HelpPath.cooPath(coo, winSize), paint);
//左箭头
recording.drawLine(winSize.getPointX(), coo.getPointY(), winSize.getPointX() - 40, coo.getPointY() - 20, paint);
recording.drawLine(winSize.getPointX(), coo.getPointY(), winSize.getPointX() - 40, coo.getPointY() + 20, paint);
//下箭头
recording.drawLine(coo.getPointX(), winSize.getPointY(), coo.getPointX() - 20, winSize.getPointY() - 40, paint);
recording.drawLine(coo.getPointX(), winSize.getPointY(), coo.getPointX() + 20, winSize.getPointY() - 40, paint);
//为坐标系绘制文字
drawText4Coo(recording, coo, winSize, paint);
}
/**
* 为坐标系绘制文字
*
* @param canvas 画布
* @param coo 坐标系原点
* @param winSize 屏幕尺寸
* @param paint 画笔
*/
private static void drawText4Coo(Canvas canvas, Point coo, Point winSize, Paint paint) {
//绘制文字
paint.setTextSize(50);
canvas.drawText(paint, "x", winSize.getPointX() - 60, coo.getPointY() - 40);
canvas.drawText(paint, "y", coo.getPointX() - 40, winSize.getPointY() - 60);
paint.setTextSize(25);
//X正轴文字
for (int i = 1; i < (winSize.getPointX() - coo.getPointX()) / 50; i++) {
paint.setStrokeWidth(2);
canvas.drawText(paint, 100 * i + "", coo.getPointX() - 20 + 100 * i, coo.getPointY() + 40);
paint.setStrokeWidth(5);
canvas.drawLine(coo.getPointX() + 100 * i, coo.getPointY(), coo.getPointX() + 100 * i, coo.getPointY() - 10, paint);
}
//X负轴文字
for (int i = 1; i < coo.getPointX() / 50; i++) {
paint.setStrokeWidth(2);
canvas.drawText(paint, -100 * i + "", coo.getPointX() - 20 - 100 * i, coo.getPointY() + 40);
paint.setStrokeWidth(5);
canvas.drawLine(coo.getPointX() - 100 * i, coo.getPointY(), coo.getPointX() - 100 * i, coo.getPointY() - 10, paint);
}
//y正轴文字
for (int i = 1; i < (winSize.getPointY() - coo.getPointY()) / 50; i++) {
paint.setStrokeWidth(2);
canvas.drawText(paint, 100 * i + "", coo.getPointX() + 20, coo.getPointY() + 10 + 100 * i);
paint.setStrokeWidth(5);
canvas.drawLine(coo.getPointX(), coo.getPointY() + 100 * i, coo.getPointX() + 10, coo.getPointY() + 100 * i, paint);
}
//y负轴文字
for (int i = 1; i < coo.getPointY() / 50; i++) {
paint.setStrokeWidth(2);
canvas.drawText(paint, -100 * i + "", coo.getPointX() + 20, coo.getPointY() + 10 - 100 * i);
paint.setStrokeWidth(5);
canvas.drawLine(coo.getPointX(), coo.getPointY() - 100 * i, coo.getPointX() + 10, coo.getPointY() - 100 * i, paint);
}
}
}
2 绘制底图
我们选一张喜欢的冰墩墩作为底图,画笔设置透明度50%;
准备画笔
mPixelMapPaint = new Paint();
mPixelMapPaint.setAlpha(0.5f);
绘制图片方法
private void drawPixelMap(Canvas canvas) {
Optional<PixelMap> image = PixelMapUtil.getPixelMapFromResource(getContext(), ResourceTable.Media_bdd);
if (image.isPresent()) {
PixelMap pixelMap = image.get();
int pw = pixelMap.getImageInfo().size.width;
int ph = pixelMap.getImageInfo().size.height;
int offX = centerX - pw / 2;
int offY = centerY - ph / 2;
Logger.d("pw:" + pw + " ;ph:" + ph);
RectFloat pixelRectFloat = new RectFloat(offX, offY, pw + offX, ph + offY);
canvas.drawPixelMapHolderRect(new PixelMapHolder(pixelMap), pixelRectFloat, mPixelMapPaint);
}
}
onDraw方法中绘制图片
drawPixelMap(canvas);
3 绘制冰墩墩轮廓
贝塞尔曲线的了解和认识
1.简单认识:(图来源网络)
2.二阶贝塞尔曲线控制点寻找示例:
确定起点,终点,和任意选一个控制点
//起点
private Point start = new Point(0, 0);
//终点
private Point end = new Point(400, 0);
//控制点
private Point control = new Point(200, 200);
贝塞尔曲线
@Override
public void onDraw(Component component, Canvas canvas) {
canvas.save();
canvas.translate(mCoo.getPointX(), mCoo.getPointY());
drawHelpElement(canvas);//绘制辅助工具--控制点和基准选
// 绘制贝塞尔曲线
mBezierPath.moveTo(start.getPointX(), start.getPointY());
mBezierPath.quadTo(control.getPointX(), control.getPointY(), end.getPointX(), end.getPointY());
canvas.drawPath(mBezierPath, mPaint);
mBezierPath.reset();//清空mBezierPath
canvas.restore();
canvas.drawPicture(mPicture);
}
//绘制辅助工具--控制点和基准选
private void drawHelpElement(Canvas canvas) {
// 绘制数据点和控制点
mHelpPaint.setColor(new Color(0x8820ECE2));
mHelpPaint.setStrokeWidth(20);
canvas.drawPoint(start.getPointX(), start.getPointY(), mHelpPaint);
canvas.drawPoint(end.getPointX(), end.getPointY(), mHelpPaint);
canvas.drawPoint(control.getPointX(), control.getPointY(), mHelpPaint);
// 绘制辅助线
resetHelpPaint();
canvas.drawLine(start.getPointX(), start.getPointY(), control.getPointX(), control.getPointY(), mHelpPaint);
canvas.drawLine(end.getPointX(), end.getPointY(), control.getPointX(), control.getPointY(), mHelpPaint);
}
寻找控制点方法,这个可以找起点,终点,控制点
@Override
public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
// 根据触摸位置更新控制点,并提示重绘
MmiPoint point = touchEvent.getPointerPosition(touchEvent.getIndex());
control.modify(point.getX() -mCoo.getPointX(),point.getY() -mCoo.getPointY());
Logger.d("touch control x:" + control.getPointX() + " y:" + control.getPointY());
invalidate();
return true;
}
触摸点打印
效果和实际应用
4,绘制过程动效
开头部分动效的实现
原本是采用AnimatorValue的,发现API7才能实现,我们分段式也多;后面演变采用EventHandler,实现一步步绘制;
EventRunner runnerA = EventRunner.getMainEventRunner();
EventHandler handlerA = new EventHandler(runnerA) {
@Override
protected void processEvent(InnerEvent event) {
super.processEvent(event);
if (drawStep == 0) {
drawStep = 1;
}
invalidate();
if (!drawLast) {
handlerA.sendEvent(1, 10);
}
}
};
public void startDraw() {
Logger.d(":");
if (drawLast) {
drawStep = 0;
handlerA.sendEvent(1);
drawLast = false;
}
}
绘制分段式
不断改变childStep的步长即可改变每次绘制的效果
PathMeasure pathMeasure = new PathMeasure(mBezierPath, false);
childStep = pathMeasure.getLength();
//使用画笔虚线效果+偏移
PathEffect effect = new PathEffect(
new float[]{pathMeasure.getLength(), pathMeasure.getLength()},
childStep);
mPaint.setPathEffect(effect);
canvas.drawPath(mBezierPath, mPaint);
总结
1,中心的点的确认,坐标系的建立,可以达到快速开发的效果;
2,本文主要采用二阶贝塞尔曲线绘制;根据第一步的基础,然后再touch事件中找到相对坐标点;
相关文章
- 网友白嫖画师原作训练Stable Diffusion引正主不满:未经同意,说用就用?
- 下一个韦神?广西桂林14岁初中生保送清华丘班,明年本硕博连读!
- 以羊了个羊为例,浅谈小程序抓包与响应报文篡改
- 硬刚4090,售价腰斩老黄!苏妈发布5nm新旗舰7900XTX,光追提升50%
- 沉痛悼念!我国多媒体学科奠基人,清华计算机系教授钟玉琢因病去世,享年84岁
- 2012R2文件夹选项 → 搜索 → [始终搜索文件名和内容]灰色不可选
- 世界上最好的编程语言PHP和Facebook的感情经历
- 小冰公司完成10亿元融资!全面升级「数字员工」产品线
- 仅需10%参数量即超越SOTA!浙大、字节、港中文联合提出「类别级位姿估计」任务新框架|CoRL2022
- 股价大跌12%!马斯克没空关心特斯拉,发推站队为共和党投票
- 如何修改 node_modules 里的文件
- 技巧:如何在 Go 中编写准确的基准测试?
- 程序里对象很深很大,可以用这个设计模式缓解一下
- Go 学习:从环境搭建到写一个 Web 服务
- 英伟达确认:对华特供「低配版」A800芯片,可替代A100
- 全网最详细笔记:张益唐北大讲解火热出炉!本质上已证明「零点猜想」
- 学习 GoF 设计模式以解决软件设计中的问题
- Go 语言为什么建议定义零值可用的结构体?
- 《彻底掌握Redux》之开发一个任务管理平台
- 最晚2026!苹果研发的AR眼镜,又放鸽子了?