版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gogo_wei/article/details/48504977
APP中很常见的一个功能,用户可以通过滑动或缩放图片来获取图片特定的区域的图片,常用于自定义头像;
下面这个组件是截取一个圆形区域的图像,如想改为方形只需在drawSelecteMould()方法中将drawCircle方法改为对应的drawRect方法即可;
在initCircle()方法中设置圆形区域的圆心和半径,通过getHeaderBitmap()方法即可拿到从该区域截取的bitmap。
实现方法详见代码,在后面会贴出DEMO的下载地址。
package com.example.headerimgmaker;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Paint.Style;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.AnalogClock;
import android.widget.ImageView;
public class MyImageView extends ImageView implements OnGlobalLayoutListener,
OnScaleGestureListener, OnTouchListener {
private boolean mOnce;
private float mInitScale;
private float mMidScale;
private float mMaxScale;
private Matrix mMatrix;
/**
* 捕获用户多点触控是缩放比例
*/
private ScaleGestureDetector mScaleGestureDetector;
// 上一次操作的触点数和中心点坐标
private int mLastPointerCount;
private float mLastX;
private float mLastY;
/**
* 触发位移的最小距离
*/
private int mTouchSlop;
private boolean mIfCanDrag;
private GestureDetector mGestureDetector;
/**
* 是否正在缩放
*/
private boolean isAutoScaling;
//选择头像的模板的bitmap和画笔
private Bitmap mBitmap;
private Paint mPaint;
/**
* 圆形头像半径
*/
private int mRadius;
/**
* 圆形头像中心点
*/
private Point mCenterPoint;
public MyImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyImageView(Context context) {
this(context, null);
}
public MyImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
super.setScaleType(ScaleType.MATRIX);
mMatrix = new Matrix();
mScaleGestureDetector = new ScaleGestureDetector(context, this);
this.setOnTouchListener(this);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener(){
@Override
public boolean onDoubleTap(MotionEvent e) {
if(isAutoScaling) return true;//如果正在缩放则不允许双击缩放操作
float x = e.getX();
float y = e.getY();
if(getScale() < mMidScale){
//开始放大
postDelayed(new AutoScaleRunnable(mMidScale, x, y), 16);
isAutoScaling = true;
}else{
//开始缩小
postDelayed(new AutoScaleRunnable(mInitScale, x, y), 16);
isAutoScaling = true;
}
return true;
}
});
}
private class AutoScaleRunnable implements Runnable{
private float targetScale;//缩放的目标值
//缩放中心点坐标
private float x;
private float y;
//每次缩放的比例
private final float BIGGER = 1.07f;
private final float SMALLER = 0.93f;
private float tmpScale;
public AutoScaleRunnable(float targetScale, float x, float y){
this.targetScale = targetScale;
this.x = x;
this.y = y;
if(getScale() < targetScale){
tmpScale = BIGGER;
}
if(getScale() > targetScale){
tmpScale = SMALLER;
}
}
public void run() {
mMatrix.postScale(tmpScale, tmpScale, x, y);
checkBorderAndCenterWhenScale();
setImageMatrix(mMatrix);
float currentScale = getScale();
if((tmpScale > 1.0f && currentScale < targetScale) || (tmpScale < 1.0f && currentScale > targetScale)){
//若未达到目标值每隔16毫秒缩放一次
postDelayed(this, 16);
}else{
float scale = targetScale/currentScale;
mMatrix.postScale(scale, scale, x, y);
checkBorderAndCenterWhenScale();
setImageMatrix(mMatrix);
isAutoScaling = false;
}
}
};
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制透明的头像选择模板
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setAlpha(100);
canvas.drawBitmap(mBitmap, 0, 0, paint);
}
/**
* 在内存中绘制选择头像的模板
* @param height
* @param width
*/
private void drawSelecteMould() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.BLACK);
mBitmap = android.graphics.Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(mBitmap);
canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));//取非相交的区域
// canvas2.drawRect(getWidth()/4, getHeight()/2 - getWidth()/4, 3*getWidth()/4, getHeight()/2+getWidth()/4, paint);//正方形头像
canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, mRadius, mPaint);//圆形头像
}
/**
* 获取最终截取的头像bitmap
* @return
*/
public Bitmap getHeaderBitmap(){
Bitmap bitmap = android.graphics.Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(((BitmapDrawable)getDrawable()).getBitmap(), mMatrix, null);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
bitmap = Bitmap.createBitmap(bitmap, mCenterPoint.x - mRadius, mCenterPoint.y - mRadius, mRadius*2, mRadius*2);
return bitmap;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// 注册global监听
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
/**
* 获取imageview加载完成的图片
*/
@Override
public void onGlobalLayout() {
// 全局布局完成后调用
initCircle();
if (!mOnce) {
mOnce = true;
// 得到控件宽高
int width = getWidth();
int height = getHeight();
// 得到图片以及宽和高
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
int dw = drawable.getIntrinsicWidth();
int dh = drawable.getIntrinsicHeight();
float scale = 1.0f;
//初始时保证图片包裹住头像区域
if (dw > 2*mRadius && dh < 2*mRadius) {
scale = 2*mRadius * 1.0f / dh;
}
if (dw < 2*mRadius && dh > 2*mRadius) {
scale = 2*mRadius * 1.0f / dw;
}
if ((dw < 2*mRadius && dh < 2*mRadius)) {
scale = Math.max(2*mRadius * 1.0f / dw, 2*mRadius * 1.0f / dh);
}
//初始时保证图片不超出控件范围
if (dw > 2*mRadius && dh > 2*mRadius) {
if (dw > width && dh < height) {
scale = width * 1.0f / dw;
}
if (dw < width && dh > height) {
scale = height * 1.0f / dh;
}
if ((dw > width && dh > height) || (dw < width && dh < height)) {
scale = Math.min(width * 1.0f / dw, height * 1.0f / dh);
}
}
// 得到初始化时缩放的比例
mInitScale = scale;
mMidScale = mInitScale * 2;
mMaxScale = mInitScale * 4;
// 将图片移动至控件的中心位置
int dx = mCenterPoint.x - dw / 2;
int dy = mCenterPoint.y - dh / 2;
mMatrix.postScale(mInitScale, mInitScale, dw / 2, dh / 2);
mMatrix.postTranslate(dx, dy);
setImageMatrix(mMatrix);
}
}
//初始化显示头像选择时的参数
private void initCircle() {
int width = getWidth();
int height = getHeight();
mRadius = width/4;
mCenterPoint = new Point(width/2, height/2);
drawSelecteMould();
}
/**
* 获取当前图片的缩放值
*
* @return
*/
private float getScale() {
float[] values = new float[9];
mMatrix.getValues(values);
return values[Matrix.MSCALE_X];
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
float scale = getScale();
float scaleFactor = detector.getScaleFactor();
if (getDrawable() == null) {
return true;
}
if ((scale < mMaxScale && scaleFactor > 1.0f)
|| (scale > mInitScale && scaleFactor < 1.0f)) {
if (scale * scaleFactor < mInitScale) {
scaleFactor = mInitScale / scale;
}
if (scale * scaleFactor > mMaxScale) {
scaleFactor = mMaxScale / scale;
}
mMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(),
detector.getFocusY());
checkBorderAndCenterWhenScale();
setImageMatrix(mMatrix);
}
return true;
}
/**
* 缩放时控制边界以及位置,保证包裹住头像区域
*/
private void checkBorderAndCenterWhenScale() {
RectF rectF = getMatrixRectF();
float offsetX = 0;
float offsetY = 0;
float width = getWidth();
float height = getHeight();
// 图片宽度或高度大于头像区域时,可能需要平移的距离
if (rectF.left > mCenterPoint.x - mRadius) {
offsetX = mCenterPoint.x - mRadius - rectF.left;
}
if (rectF.right < mCenterPoint.x + mRadius) {
offsetX = mCenterPoint.x + mRadius - rectF.right;
}
if (rectF.top > mCenterPoint.y - mRadius) {
offsetY = mCenterPoint.y - mRadius - rectF.top;
}
if (rectF.bottom < mCenterPoint.y + mRadius) {
offsetY = mCenterPoint.y + mRadius - rectF.bottom;
}
mMatrix.postTranslate(offsetX, offsetY);
}
/**
* 获取图片缩放后的范围
*
* @return
*/
private RectF getMatrixRectF() {
Matrix matrix = mMatrix;
Drawable drawable = getDrawable();
RectF rectF = new RectF();
if (drawable != null) {
rectF.set(0, 0, drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
matrix.mapRect(rectF);
}
return rectF;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
// 返回true
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if(mGestureDetector.onTouchEvent(event)){
//双击时避免触发移动操作
return true;
}
mScaleGestureDetector.onTouchEvent(event);
// 触控点个数
int pointCount = event.getPointerCount();
float x = 0;
float y = 0;
for (int i = 0; i < pointCount; i++) {
x += event.getX(i);
y += event.getY(i);
}
// 触控中心点得X,Y坐标
x /= pointCount;
y /= pointCount;
if (mLastPointerCount != pointCount) {
mIfCanDrag = false;
mLastX = x;
mLastY = y;
mLastPointerCount = pointCount;
}
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - mLastX;
float dy = y - mLastY;
if(!mIfCanDrag){
mIfCanDrag = isMoveAction(dx, dy);
}
if(mIfCanDrag){
RectF rectF = getMatrixRectF();
if(getDrawable() != null){
mMatrix.postTranslate(dx, dy);
checkBorderAndCenterWhenTranslate();
setImageMatrix(mMatrix);
}
}
mLastX = x;
mLastY = y;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mLastPointerCount = 0;
break;
}
// 返回true
return true;
}
/**
* 移动时控制边界以及位置
*/
private void checkBorderAndCenterWhenTranslate() {
RectF rectF = getMatrixRectF();
float offsetX = 0;
float offsetY = 0;
float width = getWidth();
float height = getHeight();
// 图片宽度或高度大于头像区域时,可能需要平移的距离
if (rectF.left > mCenterPoint.x - mRadius) {
offsetX = mCenterPoint.x - mRadius - rectF.left;
}
if (rectF.right < mCenterPoint.x + mRadius) {
offsetX = mCenterPoint.x + mRadius - rectF.right;
}
if (rectF.top > mCenterPoint.y - mRadius) {
offsetY = mCenterPoint.y - mRadius - rectF.top;
}
if (rectF.bottom < mCenterPoint.y + mRadius) {
offsetY = mCenterPoint.y + mRadius - rectF.bottom;
}
mMatrix.postTranslate(offsetX, offsetY);
}
/**
* 判断是否足以触发移动
*
* @param dx
* @param dy
* @return
*/
private boolean isMoveAction(float dx, float dy) {
return Math.sqrt(dx * dx + dy * dy) > mTouchSlop;
}
}
DEMO地址:DEMO