温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何在Android中使用PhotoView实现头像/圆形裁剪控件

发布时间:2022-04-19 11:08:03 来源:亿速云 阅读:176 作者:iii 栏目:开发技术

这篇文章主要介绍“如何在Android中使用PhotoView实现头像/圆形裁剪控件”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“如何在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实现头像/圆形裁剪控件”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注亿速云行业资讯频道,小编每天都会为大家更新不同的知识点。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI