android 自定义圆形头像组件

版权声明:本文为博主原创文章,未经博主允许不得转载。 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

猜你喜欢

转载自blog.csdn.net/gogo_wei/article/details/48504977