zoukankan      html  css  js  c++  java
  • Android 自定义控件——图片剪裁

    如图:

    Android 自定义控件——图片剪裁

    思路:在一个自定义View上绘制一张图片(参照前面提到的另一篇文章),在该自定义View上绘制一个自定义的FloatDrawable,也就是图中的浮层。绘制图片和FloatDrawable的交集的补集部分灰色阴影(这个其实很简单,就一句话)。在自定义View的touch中去处理具体的拖动事件和FloatDrawable的变换。图片的绘制和FloatDrawable的绘制以及变换最终其实就是在操作各自的Rect而已,Rect就是一个有矩形,有四个坐标,图片和FloatDrawable就是按照坐标去绘制的。

    CropImageView.java

    该类继承View

    功能:在onDraw方法中画图片、浮层,处理touch事件,最后根据坐标对图片进行剪裁。

    package com.play.playgame.cropimg;
    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Matrix;
    import android.graphics.Rect;
    import android.graphics.Region;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    
    /**
     * Created by Administrator on 2017/9/22.
     */
    
    public class CropImageView extends View {
        // 在touch重要用到的点,
        private float mX_1 = 0;
        private float mY_1 = 0;
        // 触摸事件判断
        private final int STATUS_SINGLE = 1;
        private final int STATUS_MULTI_START = 2;
        private final int STATUS_MULTI_TOUCHING = 3;
        // 当前状态
        private int mStatus = STATUS_SINGLE;
        // 默认裁剪的宽高
        private int cropWidth;
        private int cropHeight;
        // 浮层Drawable的四个点
        private final int EDGE_LT = 1;
        private final int EDGE_RT = 2;
        private final int EDGE_LB = 3;
        private final int EDGE_RB = 4;
        private final int EDGE_MOVE_IN = 5;
        private final int EDGE_MOVE_OUT = 6;
        private final int EDGE_NONE = 7;
    
        public int currentEdge = EDGE_NONE;
    
        protected float oriRationWH = 0;
        protected final float maxZoomOut = 5.0f;
        protected final float minZoomIn = 0.333333f;
    
        protected Drawable mDrawable;
        protected FloatDrawable mFloatDrawable;
    
        protected Rect mDrawableSrc = new Rect();// 图片Rect变换时的Rect
        protected Rect mDrawableDst = new Rect();// 图片Rect
        protected Rect mDrawableFloat = new Rect();// 浮层的Rect
        protected boolean isFrist = true;
        private boolean isTouchInSquare = true;
    
        protected Context mContext;
    
        public CropImageView(Context context) {
            super(context);
            init(context);
        }
    
        public CropImageView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        public CropImageView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init(context);
    
        }
    
        @SuppressLint("NewApi")
        private void init(Context context) {
            this.mContext = context;
            try {
                if (android.os.Build.VERSION.SDK_INT >= 11) {
                    this.setLayerType(LAYER_TYPE_SOFTWARE, null);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            mFloatDrawable = new FloatDrawable(context);
        }
    
        public void setDrawable(Drawable mDrawable, int cropWidth, int cropHeight) {
            this.mDrawable = mDrawable;
            this.cropWidth = cropWidth;
            this.cropHeight = cropHeight;
            this.isFrist = true;
            invalidate();
        }
    
        @SuppressLint("ClickableViewAccessibility")
        @Override
        public boolean onTouchEvent(MotionEvent event) {
    
            if (event.getPointerCount() > 1) {
                if (mStatus == STATUS_SINGLE) {
                    mStatus = STATUS_MULTI_START;
                } else if (mStatus == STATUS_MULTI_START) {
                    mStatus = STATUS_MULTI_TOUCHING;
                }
            } else {
                if (mStatus == STATUS_MULTI_START
                        || mStatus == STATUS_MULTI_TOUCHING) {
                    mX_1 = event.getX();
                    mY_1 = event.getY();
                }
    
                mStatus = STATUS_SINGLE;
            }
    
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mX_1 = event.getX();
                    mY_1 = event.getY();
                    currentEdge = getTouch((int) mX_1, (int) mY_1);
                    isTouchInSquare = mDrawableFloat.contains((int) event.getX(),
                            (int) event.getY());
    
                    break;
    
                case MotionEvent.ACTION_UP:
                    checkBounds();
                    break;
    
                case MotionEvent.ACTION_POINTER_UP:
                    currentEdge = EDGE_NONE;
                    break;
    
                case MotionEvent.ACTION_MOVE:
                    if (mStatus == STATUS_MULTI_TOUCHING) {
    
                    } else if (mStatus == STATUS_SINGLE) {
                        int dx = (int) (event.getX() - mX_1);
                        int dy = (int) (event.getY() - mY_1);
    
                        mX_1 = event.getX();
                        mY_1 = event.getY();
                        // 根據得到的那一个角,并且变换Rect
                        if (!(dx == 0 && dy == 0)) {
                            switch (currentEdge) {
                                case EDGE_LT:
                                    mDrawableFloat.set(mDrawableFloat.left + dx,
                                            mDrawableFloat.top + dy, mDrawableFloat.right,
                                            mDrawableFloat.bottom);
                                    break;
    
                                case EDGE_RT:
                                    mDrawableFloat.set(mDrawableFloat.left,
                                            mDrawableFloat.top + dy, mDrawableFloat.right
                                                    + dx, mDrawableFloat.bottom);
                                    break;
    
                                case EDGE_LB:
                                    mDrawableFloat.set(mDrawableFloat.left + dx,
                                            mDrawableFloat.top, mDrawableFloat.right,
                                            mDrawableFloat.bottom + dy);
                                    break;
    
                                case EDGE_RB:
                                    mDrawableFloat.set(mDrawableFloat.left,
                                            mDrawableFloat.top, mDrawableFloat.right + dx,
                                            mDrawableFloat.bottom + dy);
                                    break;
    
                                case EDGE_MOVE_IN:
                                    if (isTouchInSquare) {
                                        mDrawableFloat.offset((int) dx, (int) dy);
                                    }
                                    break;
    
                                case EDGE_MOVE_OUT:
                                    break;
                            }
                            mDrawableFloat.sort();
                            invalidate();
                        }
                    }
                    break;
            }
    
            return true;
        }
    
        // 根据初触摸点判断是触摸的Rect哪一个角
        public int getTouch(int eventX, int eventY) {
            if (mFloatDrawable.getBounds().left <= eventX
                    && eventX < (mFloatDrawable.getBounds().left + mFloatDrawable
                    .getBorderWidth())
                    && mFloatDrawable.getBounds().top <= eventY
                    && eventY < (mFloatDrawable.getBounds().top + mFloatDrawable
                    .getBorderHeight())) {
                return EDGE_LT;
            } else if ((mFloatDrawable.getBounds().right - mFloatDrawable
                    .getBorderWidth()) <= eventX
                    && eventX < mFloatDrawable.getBounds().right
                    && mFloatDrawable.getBounds().top <= eventY
                    && eventY < (mFloatDrawable.getBounds().top + mFloatDrawable
                    .getBorderHeight())) {
                return EDGE_RT;
            } else if (mFloatDrawable.getBounds().left <= eventX
                    && eventX < (mFloatDrawable.getBounds().left + mFloatDrawable
                    .getBorderWidth())
                    && (mFloatDrawable.getBounds().bottom - mFloatDrawable
                    .getBorderHeight()) <= eventY
                    && eventY < mFloatDrawable.getBounds().bottom) {
                return EDGE_LB;
            } else if ((mFloatDrawable.getBounds().right - mFloatDrawable
                    .getBorderWidth()) <= eventX
                    && eventX < mFloatDrawable.getBounds().right
                    && (mFloatDrawable.getBounds().bottom - mFloatDrawable
                    .getBorderHeight()) <= eventY
                    && eventY < mFloatDrawable.getBounds().bottom) {
                return EDGE_RB;
            } else if (mFloatDrawable.getBounds().contains(eventX, eventY)) {
                return EDGE_MOVE_IN;
            }
            return EDGE_MOVE_OUT;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
    
            if (mDrawable == null) {
                return;
            }
    
            if (mDrawable.getIntrinsicWidth() == 0
                    || mDrawable.getIntrinsicHeight() == 0) {
                return;
            }
    
            configureBounds();
            // 在画布上花图片
            mDrawable.draw(canvas);
            canvas.save();
            // 在画布上画浮层FloatDrawable,Region.Op.DIFFERENCE是表示Rect交集的补集
            canvas.clipRect(mDrawableFloat, Region.Op.DIFFERENCE);
            // 在交集的补集上画上灰色用来区分
            canvas.drawColor(Color.parseColor("#a0000000"));
            canvas.restore();
            // 画浮层
            mFloatDrawable.draw(canvas);
        }
    
        protected void configureBounds() {
            // configureBounds在onDraw方法中调用
            // isFirst的目的是下面对mDrawableSrc和mDrawableFloat只初始化一次,
            // 之后的变化是根据touch事件来变化的,而不是每次执行重新对mDrawableSrc和mDrawableFloat进行设置
            if (isFrist) {
                oriRationWH = ((float) mDrawable.getIntrinsicWidth())
                        / ((float) mDrawable.getIntrinsicHeight());
    
                final float scale = mContext.getResources().getDisplayMetrics().density;
                int w = Math.min(getWidth(), (int) (mDrawable.getIntrinsicWidth()
                        * scale + 0.5f));
                int h = (int) (w / oriRationWH);
    
                int left = (getWidth() - w) / 2;
                int top = (getHeight() - h) / 2;
                int right = left + w;
                int bottom = top + h;
    
                mDrawableSrc.set(left, top, right, bottom);
                mDrawableDst.set(mDrawableSrc);
    
                int floatWidth = dipTopx(mContext, cropWidth);
                int floatHeight = dipTopx(mContext, cropHeight);
    
                if (floatWidth > getWidth()) {
                    floatWidth = getWidth();
                    floatHeight = cropHeight * floatWidth / cropWidth;
                }
    
                if (floatHeight > getHeight()) {
                    floatHeight = getHeight();
                    floatWidth = cropWidth * floatHeight / cropHeight;
                }
    
                int floatLeft = (getWidth() - floatWidth) / 2;
                int floatTop = (getHeight() - floatHeight) / 2;
                mDrawableFloat.set(floatLeft, floatTop, floatLeft + floatWidth,
                        floatTop + floatHeight);
    
                isFrist = false;
            }
    
            mDrawable.setBounds(mDrawableDst);
            mFloatDrawable.setBounds(mDrawableFloat);
        }
    
        // 在up事件中调用了该方法,目的是检查是否把浮层拖出了屏幕
        protected void checkBounds() {
            int newLeft = mDrawableFloat.left;
            int newTop = mDrawableFloat.top;
    
            boolean isChange = false;
            if (mDrawableFloat.left < getLeft()) {
                newLeft = getLeft();
                isChange = true;
            }
    
            if (mDrawableFloat.top < getTop()) {
                newTop = getTop();
                isChange = true;
            }
    
            if (mDrawableFloat.right > getRight()) {
                newLeft = getRight() - mDrawableFloat.width();
                isChange = true;
            }
    
            if (mDrawableFloat.bottom > getBottom()) {
                newTop = getBottom() - mDrawableFloat.height();
                isChange = true;
            }
    
            mDrawableFloat.offsetTo(newLeft, newTop);
            if (isChange) {
                invalidate();
            }
        }
    
        // 进行图片的裁剪,所谓的裁剪就是根据Drawable的新的坐标在画布上创建一张新的图片
        public Bitmap getCropImage() {
            Bitmap tmpBitmap = Bitmap.createBitmap(getWidth(), getHeight(),
                    Bitmap.Config.RGB_565);
            Canvas canvas = new Canvas(tmpBitmap);
            mDrawable.draw(canvas);
    
            Matrix matrix = new Matrix();
            float scale = (float) (mDrawableSrc.width())
                    / (float) (mDrawableDst.width());
            matrix.postScale(scale, scale);
    
            Bitmap ret = Bitmap.createBitmap(tmpBitmap, mDrawableFloat.left,
                    mDrawableFloat.top, mDrawableFloat.width(),
                    mDrawableFloat.height(), matrix, true);
            tmpBitmap.recycle();
            tmpBitmap = null;
    
            return ret;
        }
    
        public int dipTopx(Context context, float dpValue) {
            final float scale = context.getResources().getDisplayMetrics().density;
            return (int) (dpValue * scale + 0.5f);
        }
    }

    FloatDrawable.java

    继承自Drawable

    功能:图片上面的浮动框,通过拖动确定位置

    package com.play.playgame.cropimg;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.ColorFilter;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.graphics.drawable.Drawable;
    
    public class FloatDrawable extends Drawable {
    
        private Context mContext;
        private int offset = 50;
        private Paint mLinePaint = new Paint();
        private Paint mLinePaint2 = new Paint();
        {
            mLinePaint.setARGB(200, 50, 50, 50);
            mLinePaint.setStrokeWidth(1F);
            mLinePaint.setStyle(Paint.Style.STROKE);
            mLinePaint.setAntiAlias(true);
            mLinePaint.setColor(Color.WHITE);
            //
            mLinePaint2.setARGB(200, 50, 50, 50);
            mLinePaint2.setStrokeWidth(7F);
            mLinePaint2.setStyle(Paint.Style.STROKE);
            mLinePaint2.setAntiAlias(true);
            mLinePaint2.setColor(Color.WHITE);
        }
    
        public FloatDrawable(Context context) {
            super();
            this.mContext = context;
    
        }
    
        public int getBorderWidth() {
            return dipTopx(mContext, offset);//根据dip计算的像素值,做适配用的
        }
    
        public int getBorderHeight() {
            return dipTopx(mContext, offset);
        }
    
        @Override
        public void draw(Canvas canvas) {
    
            int left = getBounds().left;
            int top = getBounds().top;
            int right = getBounds().right;
            int bottom = getBounds().bottom;
    
            Rect mRect = new Rect(left + dipTopx(mContext, offset) / 2, top
                    + dipTopx(mContext, offset) / 2, right
                    - dipTopx(mContext, offset) / 2, bottom
                    - dipTopx(mContext, offset) / 2);
            //画默认的选择框
            canvas.drawRect(mRect, mLinePaint);
            //画四个角的四个粗拐角、也就是八条粗线
            canvas.drawLine((left + dipTopx(mContext, offset) / 2 - 3.5f), top
                            + dipTopx(mContext, offset) / 2,
                    left + dipTopx(mContext, offset) - 8f,
                    top + dipTopx(mContext, offset) / 2, mLinePaint2);
            canvas.drawLine(left + dipTopx(mContext, offset) / 2,
                    top + dipTopx(mContext, offset) / 2,
                    left + dipTopx(mContext, offset) / 2,
                    top + dipTopx(mContext, offset) / 2 + 30, mLinePaint2);
            canvas.drawLine(right - dipTopx(mContext, offset) + 8f,
                    top + dipTopx(mContext, offset) / 2,
                    right - dipTopx(mContext, offset) / 2,
                    top + dipTopx(mContext, offset) / 2, mLinePaint2);
            canvas.drawLine(right - dipTopx(mContext, offset) / 2,
                    top + dipTopx(mContext, offset) / 2 - 3.5f,
                    right - dipTopx(mContext, offset) / 2,
                    top + dipTopx(mContext, offset) / 2 + 30, mLinePaint2);
            canvas.drawLine((left + dipTopx(mContext, offset) / 2 - 3.5f), bottom
                            - dipTopx(mContext, offset) / 2,
                    left + dipTopx(mContext, offset) - 8f,
                    bottom - dipTopx(mContext, offset) / 2, mLinePaint2);
            canvas.drawLine((left + dipTopx(mContext, offset) / 2), bottom
                            - dipTopx(mContext, offset) / 2,
                    (left + dipTopx(mContext, offset) / 2),
                    bottom - dipTopx(mContext, offset) / 2 - 30f, mLinePaint2);
            canvas.drawLine((right - dipTopx(mContext, offset) + 8f), bottom
                            - dipTopx(mContext, offset) / 2,
                    right - dipTopx(mContext, offset) / 2,
                    bottom - dipTopx(mContext, offset) / 2, mLinePaint2);
            canvas.drawLine((right - dipTopx(mContext, offset) / 2), bottom
                            - dipTopx(mContext, offset) / 2 - 30f,
                    right - dipTopx(mContext, offset) / 2,
                    bottom - dipTopx(mContext, offset) / 2 + 3.5f, mLinePaint2);
    
        }
    
        @Override
        public void setBounds(Rect bounds) {
            super.setBounds(new Rect(bounds.left - dipTopx(mContext, offset) / 2,
                    bounds.top - dipTopx(mContext, offset) / 2, bounds.right
                    + dipTopx(mContext, offset) / 2, bounds.bottom
                    + dipTopx(mContext, offset) / 2));
        }
    
        @Override
        public void setAlpha(int alpha) {
    
        }
    
        @Override
        public void setColorFilter(ColorFilter cf) {
    
        }
    
        @Override
        public int getOpacity() {
            return 0;
        }
    
        public int dipTopx(Context context, float dpValue) {
            final float scale = context.getResources().getDisplayMetrics().density;
            return (int) (dpValue * scale + 0.5f);
        }
    
    }

    使用

    布局中:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <com.onehead.cropimage.CropImageView
            android:id="@+id/cropimage"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    </RelativeLayout>

    Activity中:

    public class MainActivity extends ActionBarActivity {
          private CropImageView mView;
    
         @Override
          protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                mView = (CropImageView) findViewById(R.id.cropimage);
                //设置资源和默认长宽
                mView.setDrawable(getResources().getDrawable(R.drawable.test2), 300,
                300);
                //调用该方法得到剪裁好的图片
                Bitmap mBitmap= mView.getCropImage();
       }
    }
  • 相关阅读:
    swoole 的 go function 是什么
    MySQL grant之后要不要跟上 flush privileges
    操作数据库
    APP端异常情况
    评论列表优化联表问题
    tp5修改数据前过滤
    字典
    if语句
    【整理】android事件传递机制
    Linux常用操作
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/7577227.html
Copyright © 2011-2022 走看看