Android绘图及Bitmap几个知识点整理
时间:2022-07-25
本文章向大家介绍Android绘图及Bitmap几个知识点整理,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
Android Path绘制的折线如何变得平滑
多个点可以连成一个折线,如何将折线的拟合处变为曲线,使得整个线看上去更加平滑呢?
分下来有以下三种实现方法: 方法1:
Paint.setStrokeJoin(Paint.Join.ROUND)
这个方法可以将path中所有线段的Join方式设置为ROUND,实际效果就是拟合处变成了更加平滑的曲线;
方法2:
CornerPathEffect cornerPathEffect = new CornerPathEffect(200);
Paint.setPathEffect(cornerPathEffect);
此处的200就是平滑的度数;
方法3:
自己去实现,原理很简单,就是在两条线段相连的地方
原始做法:
moveTo(x1,y1);
lineTo(x2,y2);
lineTo(x3,y3);
此时就是两条线段相连;
平滑做法:
moveTo(x1,y1);
lineTo(x21,y21);(x21,y21)是(x1,y1)--(x2,y2)上的一个点,很接近(x2,y2);
quadTo(x31,y31);(x31,y31)是(x2,y2)--(x3,y3)上的一个点,很接近(x2,y2);
lineTo(x3,y3);
具体实现上还要考虑很多,例如两条线段的长度啊,形成的角度等等;
这里推荐使用第一种,实现简单,而且不需要像方法2那样设置一个固定的角度(设置固定角度的效果不是很适用于所有角度的折线);
将Bitmap中某个颜色替换成其他颜色
下面例子中,我们使用ARGB(255,0,0,0)也就是黑色替换bitmap中的0x616161,容忍度为50范围的颜色:
private Bitmap handleCrossImageBack(Bitmap crossimage){
// start with a Bitmap bmp
Bitmap newBmp = crossimage.copy(Bitmap.Config.ARGB_8888, true);
Canvas c = new Canvas(newBmp);
// get the int for the colour which needs to be removed
Paint paint = new Paint();// 去锯齿
paint.setAntiAlias(true);// 防抖动
paint.setDither(true);// 图像过滤
paint.setFilterBitmap(true);
paint.setARGB(255, 0, 0, 0); // ARGB for the color to replace,black replace gray
paint.setXfermode(new AvoidXfermode(0x616161, 50, AvoidXfermode.Mode.TARGET));
c.drawPaint(paint);
return newBmp;
}
根据现有Bitmap生成相同图案指定大小的新Bitmap
通过一张现有的Bitmap,画出一张同样的但是大小使我们指定的Bitmap; 直接createBitmap的话不允许生成的bitmap的宽高大于原始的,因此需要特定方法来将一张Bitmap的大小进行调整;
//crossImage为一张现有的bitmap
Bitmap target = Bitmap.createBitmap(MIDDLE_LINE_WIDTH, MIDDLE_LINE_WIDTH, crossImage.getConfig());
Canvas temp_canvas = new Canvas(target);
temp_canvas.drawBitmap(crossImage, null, new Rect(0, 0, target.getWidth(), target.getHeight()), null);
//此时的target就是一张指定大小,但是内容和crossImage一样的bitmap了
根据Path对Bitmap进行截取
方法1:
//mRectPath为一条现有的path
Path bitmap_path = new Path();
bitmap_path.moveTo(mPoints.get(0).x-width/2+LINE_WIDTH/2,mPoints.get(0).y-height+LINE_WIDTH/2);
bitmap_path.lineTo(mPoints.get(0).x+width/2-LINE_WIDTH/2,mPoints.get(0).y-height+LINE_WIDTH/2);
bitmap_path.lineTo(mPoints.get(0).x+width/2-LINE_WIDTH/2,mPoints.get(0).y-LINE_WIDTH/2);
bitmap_path.lineTo(mPoints.get(0).x-width/2+LINE_WIDTH/2,mPoints.get(0).y-LINE_WIDTH/2);
bitmap_path.close();
if(bitmap_path.op(mRectPath,Op.INTERSECT)){
paint.setStyle(Paint.Style.FILL);
bmpCanvas.drawPath(bitmap_path, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
bmpCanvas.drawBitmap(target, mPoints.get(0).x-width/2,mPoints.get(0).y-height, paint);
}else{
bmpCanvas.drawBitmap(target, mPoints.get(0).x-width/2,mPoints.get(0).y-height, paint);
}
问题:在同一个画布上绘制原本的图形以及截取后的bitmap,需要先对bitmap进行截取绘制,然后再绘制原本的图形,否则使用setXfermode操作会出错;
方法2:
//绘制路线
bmpCanvas.drawPath(mRectPath,paint);
//create 100,100 bitmap
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
int width = mBufferBmpRect.width();
int height = mBufferBmpRect.height();
Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig());
Canvas temp_canvas = new Canvas(target);
//TODO 做截取操作
Path bitmap_path = new Path();
bitmap_path.moveTo(mPoints.get(0).x-bitmap.getWidth()/2+LINE_WIDTH/2,mPoints.get(0).y-bitmap.getHeight()+LINE_WIDTH/2);
bitmap_path.lineTo(mPoints.get(0).x+bitmap.getWidth()/2-LINE_WIDTH/2,mPoints.get(0).y-bitmap.getHeight()+LINE_WIDTH/2);
bitmap_path.lineTo(mPoints.get(0).x+bitmap.getWidth()/2-LINE_WIDTH/2,mPoints.get(0).y-LINE_WIDTH/2);
bitmap_path.lineTo(mPoints.get(0).x-bitmap.getWidth()/2+LINE_WIDTH/2,mPoints.get(0).y-LINE_WIDTH/2);
bitmap_path.close();
if(bitmap_path.op(mRectPath,Op.INTERSECT)){
paint.setStyle(Paint.Style.FILL);
temp_canvas.drawPath(bitmap_path, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
temp_canvas.drawBitmap(bitmap,mPoints.get(0).x-bitmap.getWidth()/2,mPoints.get(0).y-bitmap.getHeight(), paint);
// bmpCanvas.drawPath(bitmap_path, paint);
// bmpCanvas.drawBitmap(target, mPoints.get(0).x-width/2,mPoints.get(0).y-height, paint);
}else{
temp_canvas.drawBitmap(bitmap,mPoints.get(0).x-bitmap.getWidth()/2,mPoints.get(0).y-bitmap.getHeight(), null);
// bmpCanvas.drawBitmap(target, mPoints.get(0).x-width/2,mPoints.get(0).y-height, paint);
}
//去除Xfermode
paint.setXfermode(null);
bmpCanvas.drawBitmap(target, 0,0, paint);
思路:
- 先绘制图形;
- 创建一个跟原本图形所在画布一样大的bitmap;
- 在该bitmap上绘制需要被截取的bitmap同时将其截取;
- 将bitmap绘制到图形所在bitmap上;
实现步骤:
- 获取到原路径的RectPath。
- 得到需要被截取的bitmap。
- 得到显示区域的大小。
- 得到路径起点。
- 根据起点以及bitmap的大小创建一个bitmapPath代表bitmap所占区域。
- 将rectPath与bitmapPath进行op运算。
- 如果有相交:
- 绘制相交运算后的bitmapPath。
- setXfermode(SRC_IN)。
- 再绘制bitmap,使用指定的paint。
- 如果没有相交:
- 直接绘制bitmap到图形所在画布上即可。
优点:最终得到的bitmap就是一个等大的bitmap,且该bitmap上只有被截取后剩余的一部分bitmap;
根据一条线的坐标求出线两侧形成的路的坐标
private void points2path() {
/**
*A(a,b) B(m,n) BC = L
*
*x1= m - (b-n) /√[(a-m)^2+(b-n)^2]
*x2= m + (b-n) /√[(a-m)^2+(b-n)^2]
*y1 = n + L(a-m) /√[(a-m)^2+(b-n)^2]
*y2 = n - L(a-m) /√[(a-m)^2+(b-n)^2]
*/
//获取所有计算后的点的集合
int path_width = 30;
//获取一侧点的集合
mLeftPoints.clear();
for(int i=0;i<mPoints.size();i++){
Point currentPoint = mPoints.get(i);
Point secondPoint;
int m = currentPoint.x;
int n = currentPoint.y;
if(i==mPoints.size()-1){
secondPoint= mPoints.get(i-1);
}else{
secondPoint= mPoints.get(i+1);
}
int a;
int b;
a = secondPoint.x;
b = secondPoint.y;
int x;
int y;
/**
*C1(x,y) c2(x3,y3) A(x2,y2) B(x1,y1) BC=a
*
*x=x1-a*sin{arctan[(y2-y1)/(x2-x1)]}
*y=y1+a*cos{arctan[(y2-y1)/(x2-x1)]}
*x3=x1+a*sin{arctan[(y2-y1)/(x2-x1)]}
*y3=y1- a*cos{arctan[(y2-y1)/(x2-x1)]}
*/
//m,n为B,a,b为A
int x1=m,y1=n;
int x2=a,y2=b;
if(y2==y1){
x = (int) (m - (b-n) / Math.sqrt(Math.pow((a-m),2)+Math.pow((b-n),2)));
y = (int) (n + (path_width/2)*(a-m) /Math.sqrt(Math.pow((a-m),2)+Math.pow((b-n),2)));
}else if(x2==x1){
x = x1+(path_width/2);
y = y1;
}else if(x2<x1 && y2>y1){
x=(int) (x1+(path_width/2)*Math.sin(Math.atan((y2-y1)/(x2-x1))));
y=(int) (y1-(path_width/2)*Math.cos(Math.atan((y2-y1)/(x2-x1))));
}else{
x=(int) (x1-(path_width/2)*Math.sin(Math.atan((y2-y1)/(x2-x1))));
y=(int) (y1+(path_width/2)*Math.cos(Math.atan((y2-y1)/(x2-x1))));
}
mLeftPoints.add(new Point(x, y));
}
//获取另一侧点的集合
mRightPoints.clear();
for(int i=0;i<mPoints.size();i++){
Point currentPoint = mPoints.get(i);
Point secondPoint;
int m = currentPoint.x;
int n = currentPoint.y;
if(i==mPoints.size()-1){
secondPoint= mPoints.get(i-1);
}else{
secondPoint= mPoints.get(i+1);
}
int a;
int b;
a = secondPoint.x;
b = secondPoint.y;
int x;
int y;
int x1=m,y1=n;
int x2=a,y2=b;
if(y2==y1){
x = (int) (m + (b-n) / Math.sqrt(Math.pow((a-m),2)+Math.pow((b-n),2)));
y = (int) (n - (path_width/2)*(a-m) /Math.sqrt(Math.pow((a-m),2)+Math.pow((b-n),2)));
}else if(x2==x1){
x = x1-(path_width/2);
y = y1;
}else if(x2<x1 && y2>y1){
x=(int) (x1-(path_width/2)*Math.sin(Math.atan((y2-y1)/(x2-x1))));
y=(int) (y1+(path_width/2)*Math.cos(Math.atan((y2-y1)/(x2-x1))));
}else{
x=(int) (x1+(path_width/2)*Math.sin(Math.atan((y2-y1)/(x2-x1))));
y=(int) (y1-(path_width/2)*Math.cos(Math.atan((y2-y1)/(x2-x1))));
}
mRightPoints.add(new Point(x, y));
}
//由于最后一个点的坐标是反向计算出来的,因此它的left和right是反的,在此做交换处理
Point temp = mLeftPoints.remove(mLeftPoints.size()-1);
mLeftPoints.add(mRightPoints.remove(mRightPoints.size()-1));
mRightPoints.add(temp);
mPointsPath.clear();
mPointsPath.addAll(mLeftPoints);
mPointsPath.addAll(mRightPoints);
//将点集合转成成矩形Path
mRectPath.reset();
Point point = mPointsPath.get(0);
mRectPath.moveTo(point.x,point.y);
for(int i=1;i<mPointsPath.size();i++){
if(i<mLeftPoints.size()){
point = mLeftPoints.get(i);
}else{
point = mRightPoints.get(mRightPoints.size()-(i-mLeftPoints.size()+1));
}
mRectPath.lineTo(point.x, point.y);
}
//将点集合转换成Path集合,Path集合个数为原始点的个数减一(此处可表示为left或者right集合长度减一)
mPaths.clear();
for(int i=0;i<mLeftPoints.size()-1;i++){
Path path = new Path();
Point leftCurrentPoint = mLeftPoints.get(i);
Point leftNextPoint = mLeftPoints.get(i+1);
Point rightCurrentPoint = mRightPoints.get(i);
Point rightNextPoint = mRightPoints.get(i+1);
path.moveTo(leftCurrentPoint.x,leftCurrentPoint.y);
path.lineTo(leftNextPoint.x,leftNextPoint.y);
path.lineTo(rightNextPoint.x,rightNextPoint.y);
path.lineTo(rightCurrentPoint.x,rightCurrentPoint.y);
path.close();
mPaths.add(path);
}
}
- C#解析JSON
- 动手写个数字输入框1:input[type=number]的遗憾
- 小猪农场获百万天使轮,六声域名源自运营主体
- Intellij idea 的maven项目自动下载jar包
- python3和python2共存
- 揭密微信跳一跳小游戏那些外挂
- 特斯拉出现人才流失潮,竟因为一些工程师认为Autopilot自动驾驶技术并不安全
- 微信又更新了,这次放出年度大招!新变化让不少人拍手叫好!
- “JINAN”:未来电动汽车边跑边充电
- Bagging算法
- 基于Region Proposal的深度学习目标检测简述(一)
- 10大数据挖掘算法及其简介
- SpringMVC返回图片的几种方式
- 区块链技术3.0来了,靠谱吗,看看区块链技术3.0能干啥
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- LeetCode题目36:有效的数独
- 你必须掌握动态规划——LeetCode题目5:最长回文子串
- 有意思的难题——LeetCode题目37:解数独
- 源码分析-分布式链路追踪:Skywalking存储插件能力-elasticsearch
- mongodb 4.0副本集搭建
- 浅析Kubernetes Pod重启策略和健康检查
- SpringBoot2 整合Ehcache组件,轻量级缓存管理
- 数据源管理 | 分布式NoSQL系统,Cassandra集群管理
- 【NPM库】- 0x03 - Express
- 数值微分|多项式的导数计算
- 让windows 10 内置ubuntu(WSL)成为扩增子分析生产力
- 手把手教你自定义Spring Boot Starter
- 高职考技能提升教程013期 冒泡排序法和选择排序法
- python带你剪辑视频
- python自制有声小说