android 自定义圆形头像组件

来源:互联网 发布:反抄袭软件 编辑:程序博客网 时间:2024/06/15 05:49

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(){@Overridepublic 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;}}};@Overrideprotected 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;}@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();// 注册global监听getViewTreeObserver().addOnGlobalLayoutListener(this);}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();getViewTreeObserver().removeGlobalOnLayoutListener(this);}/** * 获取imageview加载完成的图片 */@Overridepublic 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];}@Overridepublic 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;}@Overridepublic boolean onScaleBegin(ScaleGestureDetector detector) {// 返回truereturn true;}@Overridepublic void onScaleEnd(ScaleGestureDetector detector) {// TODO Auto-generated method stub}@Overridepublic 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;}// 返回truereturn 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

0 0