• android可扩展自己的定义,运动图像裁剪框


    在实际项目中,常常要制作一个简易的图像裁剪功能,即获取一张图片。并用一个遮罩层选择目标范围并截取保存的功能。例如以下图所看到的:


    在此分享下该自己定义视图的制作过程。

    需求说明

    整一个视图包括一个透明的遮罩层,一个透明带白色边框的矩形。要实现的功能是:

    • 点击矩形框外围:无不论什么响应
    • 点击矩形框内部:可随手指移动而移动
    • 点击矩形框的4个顶点:可进行对角顶点坐标不变的情况下的矩形的缩放,同一时候边框变色

    以下是实现该功能的完整源代码

    /**
     * Created by Farble on 2015/3/10.
     */
    public class PhotoCropView extends View {
        private static final String TAG = "PhotoCropView";
    
        private onLocationListener locationListener;/*listen to the Rect */
        private onChangeLocationlistener changeLocationlistener;/*listening position changed */
    
        private int MODE;
        private static final int MODE_OUTSIDE = 0x000000aa;/*170*/
        private static final int MODE_INSIDE = 0x000000bb;/*187*/
        private static final int MODE_POINT = 0X000000cc;/*204*/
        private static final int MODE_ILLEGAL = 0X000000dd;/*221*/
    
        private static final int minWidth = 100;/*the minimum width of the rectangle*/
        private static final int minHeight = 200;/*the minimum height of the rectangle*/
    
        private static final int START_X = 200;
        private static final int START_Y = 200;
    
        private static final float EDGE_WIDTH = 1.8f;
        private static final int ACCURACY= 15;/*touch accuracy*/
    
        private int pointPosition;/*vertex of a rectangle*/
    
        private int sX;/*start X location*/
        private int sY;/*start Y location*/
        private int eX;/*end X location*/
        private int eY;/*end Y location*/
    
        private int pressX;/*X coordinate values while finger press*/
        private int pressY;/*Y coordinate values while finger press*/
    
        private int memonyX;/*the last time the coordinate values of X*/
        private int memonyY;/*the last time the coordinate values of Y*/
    
        private int coverWidth = 300;/*width of selection box*/
        private int coverHeight = 400;/*height of selection box*/
    
        private Paint mPaint;
        private Paint mPaintLine;
        private Bitmap mBitmapCover;
        private Bitmap mBitmapRectBlack;
        private PorterDuffXfermode xfermode;/*paint mode*/
    
        public PhotoCropView(Context context) {
            super(context);
            init();
        }
    
        public PhotoCropView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public PhotoCropView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        @SuppressWarnings("deprecation")
        private void init() {
            sX = START_X;
            sY = START_Y;
            WindowManager manager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
            int width = manager.getDefaultDisplay().getWidth();
            int height = manager.getDefaultDisplay().getHeight();
            mBitmapCover = makeBitmap(width, height, 0x5A000000, 0, 0);
            mBitmapRectBlack = makeBitmap(coverWidth, coverHeight, 0xff000000, coverWidth, coverHeight);
    
            eX = sX + coverWidth;
            eY = sY + coverHeight;
            pressX = 0;
            pressY = 0;
    
            xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
    
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
    
            mPaintLine = new Paint();
            mPaintLine.setColor(Color.WHITE);
            mPaintLine.setStrokeWidth(2.0f);
        }
    
        /*生成bitmap*/
        private Bitmap makeBitmap(int mwidth, int mheight, int resource, int staX, int staY) {
            Bitmap bm = Bitmap.createBitmap(mwidth, mheight, Bitmap.Config.ARGB_8888);
            Canvas c = new Canvas(bm);
            Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
    
            p.setColor(resource);
            c.drawRect(staX, staY, mwidth, mheight, p);
            return bm;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            mPaint.setFilterBitmap(false);
            int sc = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null,
                    Canvas.MATRIX_SAVE_FLAG |
                            Canvas.CLIP_SAVE_FLAG |
                            Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
                            Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
                            Canvas.CLIP_TO_LAYER_SAVE_FLAG);
    
            canvas.drawBitmap(mBitmapCover, 0, 0, mPaint);
            mPaint.setXfermode(xfermode);
            canvas.drawBitmap(mBitmapRectBlack, sX, sY, mPaint);
            if (locationListener != null) {
                locationListener.locationRect(sX, sY, eX, eY);
            }
            mPaint.setXfermode(null);
            canvas.restoreToCount(sc);
            canvas.drawLine((float) sX - EDGE_WIDTH, (float) sY - EDGE_WIDTH, (float) eX + EDGE_WIDTH, (float) sY - EDGE_WIDTH, mPaintLine);/*up -*/
            canvas.drawLine((float) sX - EDGE_WIDTH, (float) eY + EDGE_WIDTH, (float) eX + EDGE_WIDTH, (float) eY + EDGE_WIDTH, mPaintLine);/*down -*/
            canvas.drawLine((float) sX - EDGE_WIDTH, (float) sY - EDGE_WIDTH, (float) sX - EDGE_WIDTH, (float) eY + EDGE_WIDTH, mPaintLine);/*left |*/
            canvas.drawLine((float) eX + EDGE_WIDTH, (float) sY - EDGE_WIDTH, (float) eX + EDGE_WIDTH, (float) eY + EDGE_WIDTH, mPaintLine);/*righ |*/
            
        }
    
        @SuppressWarnings("NullableProblems")
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    if (changeLocationlistener != null) {
                        changeLocationlistener.locationChange("change self");
                    } else {
                        changeLocationlistener = null;
                    }
    
                    memonyX = (int) event.getX();
                    memonyY = (int) event.getY();
                    checkMode(memonyX, memonyY);
                    break;
                case MotionEvent.ACTION_MOVE: {
                    switch (MODE) {
                        case MODE_ILLEGAL:
                            pressX = (int) event.getX();
                            pressY = (int) event.getY();
                            recoverFromIllegal(pressX, pressY);
                            postInvalidate();
                            break;
                        case MODE_OUTSIDE:
                            //do nothing;
                            break;
                        case MODE_INSIDE:
                            pressX = (int) event.getX();
                            pressY = (int) event.getY();
                            moveByTouch(pressX, pressY);
                            postInvalidate();
                            break;
                        default:
                            /*MODE_POINT*/
                            pressX = (int) event.getX();
                            pressY = (int) event.getY();
                            mPaintLine.setColor(getContext().getResources().getColor(R.color.orange));
                            moveByPoint(pressX, pressY);
                            postInvalidate();
                            break;
                    }
                }
                break;
                case MotionEvent.ACTION_UP:
                    mPaintLine.setColor(Color.WHITE);
                    postInvalidate();
                    break;
                default:
                    break;
            }
            return true;
        }
    
        /*从非法状态恢复,这里处理的是达到最小值后能拉伸放大*/
        private void recoverFromIllegal(int rx, int ry) {
            if ((rx > sX && ry > sY) && (rx < eX && ry < eY)) {
                MODE = MODE_ILLEGAL;
            } else {
                MODE = MODE_POINT;
            }
        }
    
        private void checkMode(int cx, int cy) {
            if (cx > sX && cx < eX && cy > sY && cy < eY) {
                MODE = MODE_INSIDE;
            } else if (nearbyPoint(cx, cy) < 4) {
                MODE = MODE_POINT;
            } else {
                MODE = MODE_OUTSIDE;
            }
        }
    
        /*推断点(inX,inY)是否靠近矩形的4个顶点*/
        private int nearbyPoint(int inX, int inY) {
            if ((Math.abs(sX - inX) <= ACCURACY && (Math.abs(inY - sY) <= ACCURACY))) {/*left-up angle*/
                pointPosition = 0;
                return 0;
            }
            if ((Math.abs(eX - inX) <= ACCURACY && (Math.abs(inY - sY) <= ACCURACY))) {/*right-up  angle*/
                pointPosition = 1;
                return 1;
            }
            if ((Math.abs(sX - inX) <= ACCURACY && (Math.abs(inY - eY) <= ACCURACY))) {/*left-down angle*/
                pointPosition = 2;
                return 2;
            }
            if ((Math.abs(eX - inX) <= ACCURACY && (Math.abs(inY - eY) <= ACCURACY))) {/*right-down angle*/
                pointPosition = 3;
                return 3;
            }
            pointPosition = 100;
            return 100;
        }
    
        /*刷新矩形的坐标*/
        private void refreshLocation(int isx, int isy, int iex, int iey) {
            this.sX = isx;
            this.sY = isy;
            this.eX = iex;
            this.eY = iey;
        }
    
        /*矩形随手指移动*/
        private void moveByTouch(int mx, int my) {/*move center point*/
            int dX = mx - memonyX;
            int dY = my - memonyY;
    
            sX += dX;
            sY += dY;
    
            eX = sX + coverWidth;
            eY = sY + coverHeight;
    
            memonyX = mx;
            memonyY = my;
    
        }
    
        /*检測矩形是否达到最小值*/
        private boolean checkLegalRect(int cHeight, int cWidth) {
            return (cHeight > minHeight && cWidth > minWidth);
        }
    
        /*点击顶点附近时的缩放处理*/
        @SuppressWarnings("SuspiciousNameCombination")
        private void moveByPoint(int bx, int by) {
            switch (pointPosition) {
                case 0:/*left-up*/
                    coverWidth = Math.abs(eX - bx);
                    coverHeight = Math.abs(eY - by);
                    //noinspection SuspiciousNameCombination
                    if (!checkLegalRect(coverWidth, coverHeight)) {
                        MODE = MODE_ILLEGAL;
                    } else {
                        mBitmapRectBlack = null;
                        mBitmapRectBlack = makeBitmap(coverWidth, coverHeight, 0xff000000, coverWidth, coverHeight);
                        refreshLocation(bx, by, eX, eY);
                    }
                    break;
                case 1:/*right-up*/
                    coverWidth = Math.abs(bx - sX);
                    coverHeight = Math.abs(eY - by);
                    if (!checkLegalRect(coverWidth, coverHeight)) {
                        MODE = MODE_ILLEGAL;
                    } else {
                        mBitmapRectBlack = null;
                        mBitmapRectBlack = makeBitmap(coverWidth, coverHeight, 0xff000000, coverWidth, coverHeight);
                        refreshLocation(sX, by, bx, eY);
                    }
                    break;
                case 2:/*left-down*/
                    coverWidth = Math.abs(eX - bx);
                    coverHeight = Math.abs(by - sY);
                    if (!checkLegalRect(coverWidth, coverHeight)) {
                        MODE = MODE_ILLEGAL;
                    } else {
                        mBitmapRectBlack = null;
                        mBitmapRectBlack = makeBitmap(coverWidth, coverHeight, 0xff000000, coverWidth, coverHeight);
                        refreshLocation(bx, sY, eX, by);
                    }
                    break;
                case 3:/*right-down*/
                    coverWidth = Math.abs(bx - sX);
                    coverHeight = Math.abs(by - sY);
                    if (!checkLegalRect(coverWidth, coverHeight)) {
                        MODE = MODE_ILLEGAL;
                    } else {
                        mBitmapRectBlack = null;
                        mBitmapRectBlack = makeBitmap(coverWidth, coverHeight, 0xff000000, coverWidth, coverHeight);
                        refreshLocation(sX, sY, bx, by);
                    }
                    break;
                default:
                    break;
            }
        }
    
        public void setLocationListener(onLocationListener locationListener) {
            this.locationListener = locationListener;
        }
    
        public interface onLocationListener {
            public void locationRect(int startX, int startY, int endX, int endY);
        }
    
        public interface onChangeLocationlistener {
            @SuppressWarnings("SameParameterValue")
            public void locationChange(String msg);
        }
    }
    

    简要说明

    1.可移动的透明矩形框通过PorterDuffXfermode(在还有一篇博文中有介绍,可点击这里查看)来实现

    2.矩形边框的移动。缩放主要由onTouch事件做处理

    3.onLocationListener 用于侦听矩形的坐标(终于可通过实现内部方法确定图像须要截取的位置)

    3.onLocationListener 用于侦听矩形的坐标(终于可通过实现内部方法确定图像须要截取的位置)

    怎样使用PhotoCropView

    在布局文件里导入

    <com.xxx.PhotoCropView
            android:id="@+id/test"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"/>

    绑定并设置侦听器:

    mCropView = (PhotoCropView)mActivity.findViewById(R.id.photo_crop_photocrop);
    mCropView .setLocationListener(this);

    获取坐标信息:

    @Override
        public void locationRect(int startX, int startY, int endX, int endY) {
            Log.d("[ "+startX+"--"+startY+"--"+endX+"--"+endY+" ]");
        }

    扩展该视图

    在此基础上可进行进一步的扩展,如:

    1.进一步改动边框的色值做警示之用

    2.舍弃边框改为添加4个边角

    等等,可自行在onDraw()方法及外部方法中进行扩展如:

    @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            mPaint.setFilterBitmap(false);
            int sc = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null,
                    Canvas.MATRIX_SAVE_FLAG |
                            Canvas.CLIP_SAVE_FLAG |
                            Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
                            Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
                            Canvas.CLIP_TO_LAYER_SAVE_FLAG);
    
            canvas.drawBitmap(mBitmapCover, 0, 0, mPaint);
            mPaint.setXfermode(xfermode);
            canvas.drawBitmap(mBitmapRectBlack, sX, sY, mPaint);
            if (locationListener != null) {
                locationListener.locationRect(sX, sY, eX, eY);
            }
            mPaint.setXfermode(null);
            canvas.restoreToCount(sc);
            /*在此加入4个边角...*/
        }

    致读者

    首先感谢您阅读此文,因为本人能力有限。不免出现一些疏漏,错误。假设您有发现不论什么错误。不妥,或有更好的实现方式,可私信我。

    Thanks advanced






    版权声明:本文博主原创文章。博客,未经同意不得转载。

  • 相关阅读:
    Schedular
    CronTrigger
    SimpleTrigger
    Quartz代码示例
    quartz
    gson and json
    json数据结构和gson的比较
    stackApp符号匹配
    Git命令
    org.apache.maven.archiver.mavenarchiver.getmanifest怎么解决
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4833388.html
Copyright © 2020-2023  润新知