zoukankan      html  css  js  c++  java
  • AndroidClipSquare安卓实现方形头像裁剪

    安卓实现方形头像裁剪

    实现思路。界面可见区域为2层View

    最顶层的View是显示层,主要绘制半透明边框区域和白色裁剪区域,代码比較easy。

    第二层继承ImageView,使用ImageView的Matrix实现显示部分图片,及挪动,放大缩小等操作。

    比較复杂的地方在于多指操作对ImageView的影响,详见代码:

    ClipSquareImageView.java

    package com.h3c.androidclipsquare;
    
    import android.annotation.TargetApi;
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.graphics.Matrix;
    import android.graphics.RectF;
    import android.graphics.drawable.Drawable;
    import android.os.Build;
    import android.util.AttributeSet;
    import android.view.GestureDetector;
    import android.view.MotionEvent;
    import android.view.ScaleGestureDetector;
    import android.view.VelocityTracker;
    import android.view.View;
    import android.view.ViewConfiguration;
    import android.view.ViewTreeObserver;
    import android.widget.ImageView;
    
    /**
     * Created by H3c on 12/13/14.
     */
    public class ClipSquareImageView extends ImageView implements View.OnTouchListener, ViewTreeObserver.OnGlobalLayoutListener {
        private static final int BORDERDISTANCE = ClipSquareView.BORDERDISTANCE;
    
        public static final float DEFAULT_MAX_SCALE = 4.0f;
        public static final float DEFAULT_MID_SCALE = 2.0f;
        public static final float DEFAULT_MIN_SCALE = 1.0f;
    
        private float minScale = DEFAULT_MIN_SCALE;
        private float midScale = DEFAULT_MID_SCALE;
        private float maxScale = DEFAULT_MAX_SCALE;
    
        private MultiGestureDetector multiGestureDetector;
        private boolean isIniting;// 正在初始化
    
    
        private Matrix defaultMatrix = new Matrix();// 初始化的图片矩阵,控制图片撑满屏幕及显示区域
        private Matrix dragMatrix = new Matrix();// 拖拽放大过程中动态的矩阵
        private Matrix finalMatrix = new Matrix();// 终于显示的矩阵
        private final RectF displayRect = new RectF();// 图片的真实大小
        private final float[] matrixValues = new float[9];
    
        private int borderlength;
    
        public ClipSquareImageView(Context context, AttributeSet attrs) {
            super(context, attrs);
            super.setScaleType(ScaleType.MATRIX);
            setOnTouchListener(this);
            multiGestureDetector = new MultiGestureDetector(context);
        }
    
        @Override
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();
            getViewTreeObserver().addOnGlobalLayoutListener(this);
        }
    
        @SuppressWarnings("deprecation")
        @Override
        protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            getViewTreeObserver().removeGlobalOnLayoutListener(this);
        }
    
        @Override
        public void onGlobalLayout() {
            if(isIniting) {
                return;
            }
            // 调整视图位置
            initBmpPosition();
        }
    
        /**
         * 初始化图片位置
         */
        private void initBmpPosition() {
            isIniting = true;
            super.setScaleType(ScaleType.MATRIX);
            Drawable drawable = getDrawable();
    
            if(drawable == null) {
                return;
            }
    
            final float viewWidth = getWidth();
            final float viewHeight = getHeight();
            final int drawableWidth = drawable.getIntrinsicWidth();
            final int drawableHeight = drawable.getIntrinsicHeight();
            if(viewWidth < viewHeight) {
                borderlength = (int) (viewWidth - 2 * BORDERDISTANCE);
            } else {
                borderlength = (int) (viewHeight - 2 * BORDERDISTANCE);
            }
    
            float screenScale = 1f;
            // 小于屏幕的图片会被撑满屏幕
            if(drawableWidth <= drawableHeight) {// 竖图片
                screenScale = (float) borderlength / drawableWidth;
            } else {// 横图片
                screenScale = (float) borderlength / drawableHeight;
            }
    
            defaultMatrix.setScale(screenScale, screenScale);
    
            if(drawableWidth <= drawableHeight) {// 竖图片
                float heightOffset = (viewHeight - drawableHeight * screenScale) / 2.0f;
                if(viewWidth <= viewHeight) {// 竖照片竖屏幕
                    defaultMatrix.postTranslate(BORDERDISTANCE, heightOffset);
                } else {// 竖照片横屏幕
                    defaultMatrix.postTranslate((viewWidth - borderlength) / 2.0f, heightOffset);
                }
            } else {
                float widthOffset = (viewWidth - drawableWidth * screenScale) / 2.0f;
                if(viewWidth <= viewHeight) {// 横照片,竖屏幕
                    defaultMatrix.postTranslate(widthOffset, (viewHeight - borderlength) / 2.0f);
                } else {// 横照片,横屏幕
                    defaultMatrix.postTranslate(widthOffset, BORDERDISTANCE);
                }
            }
    
            resetMatrix();
        }
    
        /**
         * Resets the Matrix back to FIT_CENTER, and then displays it.s
         */
        private void resetMatrix() {
            if(dragMatrix == null) {
                return;
            }
    
            dragMatrix.reset();
            setImageMatrix(getDisplayMatrix());
        }
    
        private Matrix getDisplayMatrix() {
            finalMatrix.set(defaultMatrix);
            finalMatrix.postConcat(dragMatrix);
            return finalMatrix;
        }
    
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            return multiGestureDetector.onTouchEvent(motionEvent);
        }
    
        private class MultiGestureDetector extends GestureDetector.SimpleOnGestureListener implements
                ScaleGestureDetector.OnScaleGestureListener {
    
            private final ScaleGestureDetector scaleGestureDetector;
            private final GestureDetector gestureDetector;
            private final float scaledTouchSlop;
    
            private VelocityTracker velocityTracker;
            private boolean isDragging;
    
            private float lastTouchX;
            private float lastTouchY;
            private float lastPointerCount;// 上一次是几个手指事件
    
            public MultiGestureDetector(Context context) {
                scaleGestureDetector = new ScaleGestureDetector(context, this);
                gestureDetector = new GestureDetector(context, this);
                gestureDetector.setOnDoubleTapListener(this);
    
                final ViewConfiguration configuration = ViewConfiguration.get(context);
                scaledTouchSlop = configuration.getScaledTouchSlop();
            }
    
            @Override
            public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
                float scale = getScale();
                float scaleFactor = scaleGestureDetector.getScaleFactor();
                if(getDrawable() != null && ((scale < maxScale && scaleFactor > 1.0f) || (scale > minScale && scaleFactor < 1.0f))){
                    if(scaleFactor * scale < minScale){
                        scaleFactor = minScale / scale;
                    }
                    if(scaleFactor * scale > maxScale){
                        scaleFactor = maxScale / scale;
                    }
                    dragMatrix.postScale(scaleFactor, scaleFactor, getWidth() / 2, getHeight() / 2);
                    checkAndDisplayMatrix();
                }
                return true;
            }
    
            @Override
            public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
                return true;
            }
    
            @Override
            public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {}
    
            public boolean onTouchEvent(MotionEvent event) {
                if (gestureDetector.onTouchEvent(event)) {
                    return true;
                }
    
                scaleGestureDetector.onTouchEvent(event);
    
                /*
                 * Get the center x, y of all the pointers
                 */
                float x = 0, y = 0;
                final int pointerCount = event.getPointerCount();
                for (int i = 0; i < pointerCount; i++) {
                    x += event.getX(i);
                    y += event.getY(i);
                }
                x = x / pointerCount;
                y = y / pointerCount;
    
                /*
                 * If the pointer count has changed cancel the drag
                 */
                if (pointerCount != lastPointerCount) {
                    isDragging = false;
                    if (velocityTracker != null) {
                        velocityTracker.clear();
                    }
                    lastTouchX = x;
                    lastTouchY = y;
                    lastPointerCount = pointerCount;
                }
    
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        if (velocityTracker == null) {
                            velocityTracker = VelocityTracker.obtain();
                        } else {
                            velocityTracker.clear();
                        }
                        velocityTracker.addMovement(event);
    
                        lastTouchX = x;
                        lastTouchY = y;
                        isDragging = false;
                        break;
                    case MotionEvent.ACTION_UP:
                    case MotionEvent.ACTION_CANCEL:
                        lastPointerCount = 0;
                        if (velocityTracker != null) {
                            velocityTracker.recycle();
                            velocityTracker = null;
                        }
                        break;
                    case MotionEvent.ACTION_MOVE: {
                        final float dx = x - lastTouchX, dy = y - lastTouchY;
    
                        if (isDragging == false) {
                            // Use Pythagoras to see if drag length is larger than
                            // touch slop
                            isDragging = Math.sqrt((dx * dx) + (dy * dy)) >= scaledTouchSlop;
                        }
    
                        if (isDragging) {
                            if (getDrawable() != null) {
                                dragMatrix.postTranslate(dx, dy);
                                checkAndDisplayMatrix();
                            }
    
                            lastTouchX = x;
                            lastTouchY = y;
    
                            if (velocityTracker != null) {
                                velocityTracker.addMovement(event);
                            }
                        }
                        break;
                    }
                }
    
                return true;
            }
    
            @Override
            public boolean onDoubleTap(MotionEvent event) {
                try {
                    float scale = getScale();
                    float x = getWidth() / 2;
                    float y = getHeight() / 2;
    
                    if (scale < midScale) {
                        post(new AnimatedZoomRunnable(scale, midScale, x, y));
                    } else if ((scale >= midScale) && (scale < maxScale)) {
                        post(new AnimatedZoomRunnable(scale, maxScale, x, y));
                    } else {// 双击缩小小于最小值
                        post(new AnimatedZoomRunnable(scale, minScale, x, y));
                    }
                } catch (Exception e) {
                    // Can sometimes happen when getX() and getY() is called
                }
    
                return true;
            }
        }
    
        private class AnimatedZoomRunnable implements Runnable {
            // These are 'postScale' values, means they're compounded each iteration
            static final float ANIMATION_SCALE_PER_ITERATION_IN = 1.07f;
            static final float ANIMATION_SCALE_PER_ITERATION_OUT = 0.93f;
    
            private final float focalX, focalY;
            private final float targetZoom;
            private final float deltaScale;
    
            public AnimatedZoomRunnable(final float currentZoom, final float targetZoom,
                                        final float focalX, final float focalY) {
                this.targetZoom = targetZoom;
                this.focalX = focalX;
                this.focalY = focalY;
    
                if (currentZoom < targetZoom) {
                    deltaScale = ANIMATION_SCALE_PER_ITERATION_IN;
                } else {
                    deltaScale = ANIMATION_SCALE_PER_ITERATION_OUT;
                }
            }
    
            @Override
            public void run() {
                dragMatrix.postScale(deltaScale, deltaScale, focalX, focalY);
                checkAndDisplayMatrix();
    
                final float currentScale = getScale();
    
                if (((deltaScale > 1f) && (currentScale < targetZoom))
                        || ((deltaScale < 1f) && (targetZoom < currentScale))) {
                    // We haven't hit our target scale yet, so post ourselves
                    // again
                    postOnAnimation(ClipSquareImageView.this, this);
    
                } else {
                    // We've scaled past our target zoom, so calculate the
                    // necessary scale so we're back at target zoom
                    final float delta = targetZoom / currentScale;
                    dragMatrix.postScale(delta, delta, focalX, focalY);
                    checkAndDisplayMatrix();
                }
            }
        }
    
        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
        private void postOnAnimation(View view, Runnable runnable) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                view.postOnAnimation(runnable);
            } else {
                view.postDelayed(runnable, 16);
            }
        }
    
        /**
         * Returns the current scale value
         *
         * @return float - current scale value
         */
        public final float getScale() {
            dragMatrix.getValues(matrixValues);
            return matrixValues[Matrix.MSCALE_X];
        }
    
        /**
         * Helper method that simply checks the Matrix, and then displays the result
         */
        private void checkAndDisplayMatrix() {
            checkMatrixBounds();
            setImageMatrix(getDisplayMatrix());
        }
    
        private void checkMatrixBounds() {
            final RectF rect = getDisplayRect(getDisplayMatrix());
            if (null == rect) {
                return;
            }
    
            float deltaX = 0, deltaY = 0;
            final float viewWidth = getWidth();
            final float viewHeight = getHeight();
            // 推断移动或缩放后,图片显示是否超出裁剪框边界
            final float heightBorder = (viewHeight - borderlength) / 2;
            final float weightBorder = (viewWidth - borderlength) / 2;
            if(rect.top > heightBorder){
                deltaY = heightBorder - rect.top;
            }
            if(rect.bottom < (viewHeight - heightBorder)){
                deltaY = viewHeight - heightBorder - rect.bottom;
            }
            if(rect.left > weightBorder){
                deltaX = weightBorder - rect.left;
            }
            if(rect.right < viewWidth - weightBorder){
                deltaX = viewWidth - weightBorder - rect.right;
            }
            // Finally actually translate the matrix
            dragMatrix.postTranslate(deltaX, deltaY);
    
    
        }
    
        /**
         * 获取图片相对Matrix的距离
         *
         * @param matrix
         *            - Matrix to map Drawable against
         * @return RectF - Displayed Rectangle
         */
        private RectF getDisplayRect(Matrix matrix) {
            Drawable d = getDrawable();
            if (null != d) {
                displayRect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
                matrix.mapRect(displayRect);
                return displayRect;
            }
    
            return null;
        }
    
        /**
         * 剪切图片,返回剪切后的bitmap对象
         *
         * @return
         */
        public Bitmap clip(){
            Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(bitmap);
            draw(canvas);
            return Bitmap.createBitmap(bitmap, (getWidth() - borderlength) / 2, (getHeight() - borderlength) / 2, borderlength, borderlength);
        }
    }
    


    代码下载:https://github.com/h3clikejava/AndroidClipSquare

  • 相关阅读:
    解决vs2017创建.net core失败以及不能登录问题
    ocx控件的坑
    Sqlserver 高并发和大数据存储方案
    远程连接ejabberd的mnesia数据库
    基于ejabberd实现各个客户端消息同步
    小诗一首《钗头凤·七夕夜》
    微软office web apps 服务器搭建之在线文档预览(二)
    微软office web apps 服务器搭建之在线文档预览(一)
    基于ejabberd简单实现xmpp群聊离线消息
    yiwoSDK2.0完整版
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5058011.html
Copyright © 2011-2022 走看看