这期内容当中小编将会给大家带来有关怎么在Android中使用PhotoView实现头像/圆形裁剪控件,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
创新互联专注于沁水网站建设服务及定制,我们拥有丰富的企业做网站经验。 热诚为您提供沁水营销型网站建设,沁水网站制作、沁水网页设计、沁水网站官网定制、微信平台小程序开发服务,打造沁水网络公司原创品牌,更为您提供沁水网站排名全网营销落地服务。public class CircleCropView extends View { public final int CIRCLE_MARGIN = 50; public CircleCropView(Context context) { super(context); } public CircleCropView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public CircleCropView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public CircleCropView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, widthMeasureSpec); } @RequiresApi(api = Build.VERSION_CODES.O) @Override protected void onDraw(Canvas canvas) { canvas.save(); Path path = new Path(); Rect viewDrawingRect = new Rect(); getDrawingRect(viewDrawingRect); float radius = viewDrawingRect.width() / 2 - CIRCLE_MARGIN; path.addCircle(viewDrawingRect.left + radius + CIRCLE_MARGIN, viewDrawingRect.top + radius + CIRCLE_MARGIN, radius, Path.Direction.CW); Paint outsidePaint = new Paint(); outsidePaint.setAntiAlias(true); outsidePaint.setARGB(151, 0, 0, 0); canvas.clipPath(path, Region.Op.DIFFERENCE); canvas.drawRect(viewDrawingRect, outsidePaint); canvas.restore(); } }
SquarePhotoView只是在PhotoView的基础上改了长宽,重写一下onMeasure方法即可:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, widthMeasureSpec); }
那么现在最关键的一步,就是从PhotoView获取当前图片显示区域的Drawable或Bitmap了。粗略看了一下PhotoView的函数,并没有找到能用的(囧)。解决第一个坑的笨办法就是,自己动手丰衣足食——直接拿原图的bitmap,然后问PhotoView要当前图片的变形矩阵,自个儿通过矩阵一步步变形拿到对应的位图。
思路其实是没问题的,然而第二个坑又出现了(囧)。这里的变形矩阵,我最早百度的结果是getSuppMatrix,源码我没有细看,但掉坑的过程中据我观察,猜测应该是对应最新一次的手势变形结果(不确定= =,也可能是其他坑综合导致的错误结果)。总之最后我查了一会源码,最终确定用的是getDisplayMatrix。
紧接着是第三个坑,坑多了就习惯了。矩阵中的XY位移量,我起初以为是显示区域中心相对于原图中心的位移,即如果仅有缩放操作的话,位移应该为0。但实际通过特殊位置(例如取四个顶点)的裁剪结果来看,这里的XY位移量实际最后显示区域左上角的点相对原点(即原图左上角)的位移,简单点说,可以把位移量作为最终显示区域左上角的坐标。
然后我就迎来了第四个坑(?)。这个坑现在回头看其实是很简单不应该栽进去的,然而当时还没想通的时候确实很慌(唉)。这个坑的问题就出在,Matrix里的值是基于手势的,也就是说,是基于屏幕像素(换句话说,是基于实际显示的图片)的。而对位图进行裁剪时,是基于原图像素的。那么这里还存在一个为了正常显示而导致的缩放比例的问题,例如原图是3000x4000,由于屏幕分辨率是1080*1920,那么实际显示时,图片是缩小了的,这个比例是9/25。所以在裁剪的过程中,需要把位移量再放大25/9倍进行还原。
下面是裁剪部分的关键代码(最后偷了一下懒,没有裁圆形,只是用CIrcleImageView显示):
fun cropImage(){ var degree = ImageUtils.readPictureDegree(imagePath) var bitmap = ImageUtils.getRotatedBitmap(BitmapFactory.decodeFile(imagePath),degree) var width: Int = 0 var startX: Int = 0 var startY: Int = 0 if (bitmap.width < bitmap.height){ startY = (bitmap.height - bitmap.width) / 2 width = bitmap.width }else{ startX = (bitmap.width - bitmap.height) / 2 width = bitmap.height } var matrix = Matrix() photo_preview.getDisplayMatrix(matrix)//获取变形矩阵,直接取scaleX或translationX没用 var values = FloatArray(9, {0.0f}) matrix.getValues(values) var expWidth = Math.round(bitmap.width * values[0])//缩放x var expHeight = Math.round(bitmap.height * values[4])//缩放y var bitmap1 = Bitmap.createScaledBitmap(bitmap, expWidth, expHeight, false) val ratio = width * 1.0f / photo_preview.width startX = Math.round(startX * values[0] - values[2] * ratio) startY = Math.round(startY * values[4] - values[5] * ratio) var bitmap2 = Bitmap.createBitmap(bitmap1, startX, startY, width, width, null, false) saveImage(bitmap2) }
这里还有几个小坑需要解释一下:
读取bitmap时需要注意一下角度。这个是我在裁剪本地图片和网络图片的时候发现的,有些是正的有些就是转了90度。每个手机也不一定一样,所以保险起见,需要从图片的EXIF信息里面获取需要旋转的角度,然后再进一步处理。
我这里因为最终显示的是正方形,而且选的scaleType是centerCrop。所以默认就是显示中间的那一块。所以裁减时的原点也需要从正方形的左上角开始。这里是计算两种情况下的原点坐标:
var startX: Int = 0 var startY: Int = 0 if (bitmap.width < bitmap.height){ startY = (bitmap.height - bitmap.width) / 2 width = bitmap.width }else{ startX = (bitmap.width - bitmap.height) / 2 width = bitmap.height }
上述就是小编为大家分享的怎么在Android中使用PhotoView实现头像/圆形裁剪控件了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注创新互联行业资讯频道。