这期内容当中小编将会给大家带来有关怎么在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实现头像/圆形裁剪控件了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。