Android实现特定形状的图片
https://blog.csdn.net/YX_BB/article/details/104561359
在上篇文章中,我们实现了圆形图片圆角图片,基本上已经满足了日常开发中的需要。那如果想要更多的图形效果该怎么办呢?与我们在现实中的绘图方式一致,用笔(Paint)在纸(Canvas)上按照一定的路径(Path),即可形成特定的图画。一个图形的Path是固定,所以,我们可以通过改变Paint和Canvas来实现想要的效果。如下
这里以心形图片为例,展示几种不同的实现方法,首先先实现心形图片的Path。
公式:t 表示点(x,y)在坐标系中的角度
x = 16 sin^3 t
y = 13 cos t - 5 cos 2t - 2 cos 3t - cos 4t
/*** 心形曲线*/private Path getHeartPath() {int n = 100;// 计算缩放比例float scale = getWidth() / 17f / 2f;// 将360度平均分为 100份,每个弧度对应一个点float interval = (float) (2 * Math.PI / 100);// 定义初始弧度degreefloat degree = 0;Point[] points = new Point[n];for (int i = 0; i < n; i++) {// 根据心形曲线公式,计算出每个弧度对应的点的坐标// 当degree = 90度的时候,x取最大值16。当degree = 180度的时候,y取最小值 -17。// 即保证y * scale * 17 * 2 = height的时候,曲线的与控件相切,以此计算出scale的值float x = (float) ((16 * Math.pow(Math.sin(degree), 3)) * scale);float y = (float) ((13 * Math.cos(degree) - 5 * Math.cos(2 * degree) - 2 * Math.cos(3 * degree) - Math.cos(4 * degree)) * scale);points[i] = new Point(x + getWidth() / 2f, -y + getHeight() / 2f);degree = degree + interval;}// 连线Path path = new Path();path.moveTo(points[0].x, points[0].y);for (int i = 1; i < n; i++) {path.lineTo(points[i].x, points[i].y);}path.close();return path;}
思路1:使用Canvas的clipPath方法直接剪裁Canvas。
优点简单粗暴,可以适应于各种控件且不需要考虑绘制中的各种情形。缺点是剪裁之后,Canvas的绘制区域将被Path限制,任何的内容及后续扩展都局限在了这个Path之内。且clipPath方法不支持硬件加速,当应用开启了硬件加速时,设备在4.0.4与4.0.3这样的版本上使用图片剪裁功能的时候会crash。
重写onDraw()方法
@Overrideprotected void onDraw(Canvas canvas) {// 调用父类的绘制方法之前直接根据模式裁剪Canvascanvas.clipPath(getHeartPath());super.onDraw(canvas);}
控件继承自ImageView,注意要在super.onDraw()之前剪裁Canvas。否则Canvas已经将图片绘制到了View中,这个时候再剪裁Canvas毫无意义
思路2:使用Paint.setShader方法,只给指定区域上色。
Shader在三维软件中称之为着色器,就是用来给空白图形上色用的。
关于Shader的详细介绍可以参考
https://blog.csdn.net/harvic880925/article/details/52039081
首先获取ImageView上的Drawable,将Drawable转化成Bitmap,然后给Paint设置BitmapShader着色器,Bitmap在指定Path中的区域将被上色,Path之外的区域会被忽略。这种方式不会改变Canvas,也不会对后续的绘制工作造成影响
重写onDraw()方法
@Overrideprotected void onDraw(Canvas canvas) {if (mBitmap == null) {mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);Canvas drawCanvas = new Canvas(mBitmap);Drawable drawable = getDrawable();if (drawable != null) {drawable.draw(drawCanvas);}mPaint.setShader(new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));canvas.drawPath(getHeartPath(), mPaint);}}
注意:使用这种方式,不需要再使用super.onDraw(canvas)
思路3:使用PorterDuffXfermode混合图层
分别绘制图片图层,与路径图层。然后将两个图层混合。
@Overrideprotected void onDraw(Canvas canvas) {// 图层混合绘制方式srcBmp = makeSrc();dstBmp = makeDst();int layerID = canvas.saveLayer(0, 0, getWidth(), getHeight(), mPaint, Canvas.ALL_SAVE_FLAG);canvas.drawBitmap(srcBmp, 0, 0, mPaint);mPaint.setXfermode(xfermode);canvas.drawBitmap(dstBmp, 0, 0, mPaint);mPaint.setXfermode(null);canvas.restoreToCount(layerID);}/*** 源图层*/private Bitmap makeSrc() {Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bitmap);Drawable drawable = getDrawable();if (drawable != null) {drawable.draw(canvas);}return bitmap;}/*** 目标图层*/private Bitmap makeDst() {Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bitmap);Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);canvas.drawPath(getHeartPath(), paint);return bitmap;}
注意:
int layerID = canvas.saveLayer(0, 0, getWidth(), getHeight(), mPaint, Canvas.ALL_SAVE_FLAG);
canvas.restoreToCount(layerID);
如果不加如这段代码,在进行图层混合的时候,源图层将会被直接绘制在原始Canvas上。然后再绘制目标图层的时候,会与整个原始Canvas取交集,这时会将非交集区域的颜色全部清空,从而漏出Activity的底色。
在加入这段代码之后,Canvas.saveLayer(),会生成新的全透明的Bitmap,后续的绘制都是在这个新图层上完成的。混合之后再绘制在原始Canvas上。这时边缘区域显示的颜色为ImageView父布局的背景色。简单理解两者的区别如下
未使用saveLayer时,3 - 2 - 1 = 0
而使用saveLayer时,3 - (2 - 1) = 2
显然两者的计算结果不一样了,而后者才是我们真正需要的绘制流程。
关于saveLayer()的用法可以参考
https://blog.csdn.net/harvic880925/article/details/51317746
完整代码
public class ShapeImageView extends AppCompatImageView {private Paint mPaint;private Bitmap srcBmp;private Bitmap dstBmp;private Xfermode xfermode;public ShapeImageView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);initData();}private void initData() {mPaint = new Paint();xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);}@Overrideprotected void onDraw(Canvas canvas) {// 图层混合绘制方式srcBmp = makeSrc();dstBmp = makeDst();int layerID = canvas.saveLayer(0, 0, getWidth(), getHeight(), mPaint, Canvas.ALL_SAVE_FLAG);canvas.drawBitmap(srcBmp, 0, 0, mPaint);mPaint.setXfermode(xfermode);canvas.drawBitmap(dstBmp, 0, 0, mPaint);mPaint.setXfermode(null);canvas.restoreToCount(layerID);// BitmapShader绘制方式
// if (srcBmp == null) {// srcBmp = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
// Canvas drawCanvas = new Canvas(srcBmp);
// Drawable drawable = getDrawable();
// if (drawable != null) {// drawable.draw(drawCanvas);
// }
//
// mPaint.setShader(new BitmapShader(srcBmp, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
// canvas.drawPath(getHeartPath(), mPaint);
// }// clipPath绘制方式
// canvas.clipPath(getHeartPath());
// super.onDraw(canvas);}/*** 源图层*/private Bitmap makeSrc() {Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bitmap);Drawable drawable = getDrawable();if (drawable != null) {drawable.draw(canvas);}return bitmap;}/*** 目标图层*/private Bitmap makeDst() {Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bitmap);Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);canvas.drawPath(getHeartPath(), paint);return bitmap;}/*** 画心形曲线*/private Path getHeartPath() {int n = 100;// 计算缩放比例float scale = getWidth() / 17f / 2f;// 将360度平均分为 100份,每个弧度对应一个点float interval = (float) (2 * Math.PI / 100);// 定义初始弧度degreefloat degree = 0;Point[] points = new Point[n];for (int i = 0; i < n; i++) {// 根据心形曲线公式,计算出每个弧度对应的点的坐标// 当degree = 90度的时候,x取最大值16。当degree = 180度的时候,y取最小值 -17。// 即保证y * scale * 17 * 2 = height的时候,曲线的与控件相切,以此计算出scale的值float x = (float) ((16 * Math.pow(Math.sin(degree), 3)) * scale);float y = (float) ((13 * Math.cos(degree) - 5 * Math.cos(2 * degree) - 2 * Math.cos(3 * degree) - Math.cos(4 * degree)) * scale);points[i] = new Point(x + getWidth() / 2f, -y + getHeight() / 2f);degree = degree + interval;}// 连线Path path = new Path();path.moveTo(points[0].x, points[0].y);for (int i = 1; i < n; i++) {path.lineTo(points[i].x, points[i].y);}path.close();return path;}public static class Point {float x;float y;public Point(float x, float y) {this.x = x;this.y = y;}}
}
Android实现特定形状的图片相关推荐
- Android中绘制圆角矩形图片及任意形状图片
转自http://blog.csdn.net/silangquan/article/details/8056583 圆角矩形图片在苹果的产品中很流行,相比于普通的矩形,很多人都喜欢圆角矩形的图片,因为 ...
- android百度地图覆盖物异步加载图片,Android 百度地图marker中图片不显示的解决方法(推荐)...
目的: 根据提供的多个经纬度,显示所在地的marker样式,如下: 问题: 1.发现marker中在线加载的图片无法显示出来: 2.获取多个对象后,却只显示出了一个marker: 以下为官网实现方法: ...
- Android画不规则形状
经常会在移动应用中看到类似下图的各种图片: 这样的图形在Android上要怎么实现呢?在Android系统中,目前主要有三种方式可以实现上图的形状,下面一一介绍. 一.PorterDuffXfermo ...
- 利用svg合成任意形状的图片
什么是svg: 百度百科这样说: SVG可以算是目前最最火热的图像文件格式了,它的英文全称为Scalable Vector Graphics,意思为可缩放的矢量图形.它是基于XML(Extensibl ...
- 知识卡片 生成特定形状的词云
在学会用Python简单生成词云后,我们来了解特定形状的词云如何生成. 美丽优雅的Cinderella公主是如何出现的呢? 上述词云效果使用了imageio库. imageio的含义为image in ...
- Android 自定义View 圆形圆角图片
[Android 自定义View 圆形圆角图片] 基于Xfermode 实现 1.概述 在很久以前也写过一个利用Xfermode 实现圆形.圆角图片的(Android 完美实现图片圆角和圆形(对实现进 ...
- 基于Python的特定形状透明背景词云图绘制
基于Python的特定形状透明背景词云图绘制 1.需求分析 2.前期准备 2.1文本文件准备 2.2特定形状图片准备 3.代码解析与实现 3.1模块库的导入 3.2停用词表 3.3基于TF-IDF提取 ...
- android picasso源码下载,Picasso:一个专为Android制作的强大的图片下载和缓存库
Picasso:一个专为Android打造的强大的图片下载和缓存库 简介 在Android应用中,图片消费了大量的资源,却为应用提供了很好的视觉体验.幸运的是,Picasso为你的应用提供了非常容易的 ...
- android图片gif动画效果,android中类似于gif 实现图片的动画效果
案例:实现gif动画效果,连续播放图片 由于是转载的,也就没必要多说,直接上代码 案例:在android中实现gif动态图片的效果: EarthAnimationActivity.java packa ...
最新文章
- @Mybatis传多个参数
- 安卓java增加属性_如何使用Java读取Android属性
- SAP CO模块权限控制
- 阿里云ecs实例中创建数据库
- asp.net core 拦击器制作的权限管理系统DEMO
- 大数据可视化大屏设计经验,教给你!
- 一文掌握 Docker 技术体系
- /etc/hosts/中HOSTNAME错误导致SETUP出错
- git lfs linux,Git LFS 操作指南
- java 1 20内奇数的乘积_计算所有奇数的乘积
- 三、任务切换之PendSV异常
- 计算机科学与实践,【计算机科学与技术学院|实践实况】(一)
- Mac 开启局域网smb文件共享(附全平台连接方法)
- 体验 服务器正在维护升级中 给大,阴阳师体验服9月30日维护 花合战更新
- Linux中Shell脚本编程
- 高薪程序员面试题精讲系列73之你熟悉servlet、session吗?get与post有哪些区别?
- R730调整风扇转速
- Hadoop化繁为简-从安装Linux到搭建集群环境
- qq音乐播放器2014最新版 v10.21.4270 官方版
- Java程序如何自动在线升级
热门文章
- 谷歌浏览器安装QQ旋风插件
- VC++调用libcurl开源库实现发送邮件的功能(附源码)
- 全球与中国口腔CBCT行业市场深度评估及未来发展分析报告
- window10 android studio连接不上夜神/mumu/蓝叠模拟器
- 你猜!大学里青年教师待遇真的很低吗?
- 京东 APP SIGN算法分析
- Java网络考试系统
- 代码+步骤GM(1,1)灰色预测模型-案例长江水质综合评价赛题-级比检测C的确定-matlab完整代码附送
- bC技术绑定10讲①“一元换购”的奥妙!
- 宁波大学计算机系有哪些专业,宁波大学有哪些专业