Cutom ImageView像Whatsapp一样裁剪

时间:2019-05-13 13:49:37

标签: java android view imageview crop

我尝试创建类似whatsapp的自定义图像裁剪,用户上传个人资料图片,然后用户获得矩形裁剪器,以便用户随意裁剪图像。我不想使用任何库

根据this answer,我成功地在imageview上获得了可拖动的imageview,并且工作正常,但是此代码存在一些我无法解决的问题。我对他的github作了评论,但他不再维护此代码。

到目前为止我尝试过的事情:

初始化指向构造函数

public DrawView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        setFocusable(true); // necessary for getting the touch events
        canvas = new Canvas();
points[0] = new Point();
points[0].x = 150;
points[0].y = 20;
points[1] = new Point();
points[1].x = 150;
points[1].y = 20;
points[2] = new Point();
points[2].x = 150;
points[2].y = 20;
points[3] = new Point();
points[3].x = 150;
points[3].y = 20;
    }

在左侧,右侧,顶部,底部变量的onDraw上手动设置值

// the method that draws the balls

    @Override
    protected void onDraw(Canvas canvas) {
        if(points[3]==null) //point4 null when user did not touch and move on screen.
            return;
        int left, top, right, bottom;
        left = 150;
        top = 50;
        right = 150;
        bottom = 50;
        for (int i = 1; i < points.length; i++) {
            left = left > points[i].x ? points[i].x:left;
            top = top > points[i].y ? points[i].y:top;
            right = right < points[i].x ? points[i].x:right;
            bottom = bottom < points[i].y ? points[i].y:bottom;
        }
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeWidth(5);

        //draw stroke
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.parseColor("#AADB1255"));
        paint.setStrokeWidth(2);
        canvas.drawRect(
                    left + colorballs.get(0).getWidthOfBall() / 2,
                    top + colorballs.get(0).getWidthOfBall() / 2, 
                    right + colorballs.get(2).getWidthOfBall() / 2, 
                    bottom + colorballs.get(2).getWidthOfBall() / 2, paint);
        //fill the rectangle
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.parseColor("#55DB1255"));
        paint.setStrokeWidth(0);
        canvas.drawRect(
                left + colorballs.get(0).getWidthOfBall() / 2,
                top + colorballs.get(0).getWidthOfBall() / 2, 
                right + colorballs.get(2).getWidthOfBall() / 2, 
                bottom + colorballs.get(2).getWidthOfBall() / 2, paint);

        //draw the corners
        BitmapDrawable bitmap = new BitmapDrawable();
        // draw the balls on the canvas
        paint.setColor(Color.BLUE);
        paint.setTextSize(18);
        paint.setStrokeWidth(0);
        for (int i =0; i < colorballs.size(); i ++) {
            ColorBall ball = colorballs.get(i);
            canvas.drawBitmap(ball.getBitmap(), ball.getX(), ball.getY(),
                    paint);

            canvas.drawText("" + (i+1), ball.getX(), ball.getY(), paint);
        }
    }

所以我需要解决一些问题。请帮助

  1. 所有4个点在首次启动时以及在用户触摸屏幕后可见之后才相互重叠。我试图在背景ImageView的50%处拉伸4个点,但没有这样做。

  2. 成功选择要种植的特定区域后。如何获取该区域的背景图像视图(请给我一个想法,怎么做)。

1 个答案:

答案 0 :(得分:2)

1. All 4 dots overlapping each other at first launch and only after visible after user touch the screen. I tried to make 4 dots stretch at 50% of background ImageView but failed to do so.

回答:您应该为点数组设置一个适当的init值,origin answer代码逻辑是在手指触摸屏int onTouchEvent() MotionEvent.ACTION_DOWN类型并使视图无效时分配点数组。视图将执行onDraw()绘制矩形。

因此,如果您想在触摸屏之前显示裁剪矩形,则应为点数组设置适当的值(也许应该使用具有固定宽度和高度正方形的视图中心进行计算)并手动添加绘制矩形代码使用onDraw()方法。

2. After successfully selecting particular area to crop. How to get background imageview of that region(please just give me idea how to do this).

答案:当您要裁剪某个特定区域时,您必须知道该区域的坐标信息。

(1)获取源位图,您已经在背景图像视图中显示了一张图片,因此您可以获取源位图,或者可以从文件或资源中解码位图

(2)执行裁剪操作

Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.xyz);

//you also can use other logic to get a bitmap, this according to your app logic

//create a crop area rectangle
//this crop area rectangle should calculate from the points array
Bitmap croppedBitmap = Bitmap.createBitmap(bmp, x, y, cropWidth, cropHeight);

然后您可以获取裁剪的位图。

希望这些技巧可以给您一个归档所需内容的想法。

这里是Demo linkGIF link


package com.image.crop;

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;


public class DrawView extends View {

    Point[] points = new Point[4];

    /**
     * point1 and point 3 are of same group and same as point 2 and point4
     */
    int groupId = -1;
    private ArrayList<ColorBall> colorballs = new ArrayList<>();

    private int mStrokeColor = Color.parseColor("#AADB1255");
    private int mFillColor = Color.parseColor("#55DB1255");
    private Rect mCropRect = new Rect();

    // array that holds the balls
    private int balID = 0;
    // variable to know what ball is being dragged
    Paint paint;

    public DrawView(Context context) {
        this(context, null);
    }

    public DrawView(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public DrawView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        paint = new Paint();
        setFocusable(true); // necessary for getting the touch events
    }

    private void initRectangle(int X, int Y) {
        //initialize rectangle.
        points[0] = new Point();
        points[0].x = X;
        points[0].y = Y;

        points[1] = new Point();
        points[1].x = X;
        points[1].y = Y + 30;

        points[2] = new Point();
        points[2].x = X + 30;
        points[2].y = Y + 30;

        points[3] = new Point();
        points[3].x = X +30;
        points[3].y = Y;

        balID = 2;
        groupId = 1;
        // declare each ball with the ColorBall class
        for (int i = 0; i < points.length; i++) {
            colorballs.add(new ColorBall(getContext(), R.drawable.gray_circle, points[i], i));
        }
    }

    // the method that draws the balls
    @Override
    protected void onDraw(Canvas canvas) {
        if(points[3]==null) {
            //point4 null when view first create
            initRectangle(getWidth() / 2, getHeight() / 2);
        }

        int left, top, right, bottom;
        left = points[0].x;
        top = points[0].y;
        right = points[0].x;
        bottom = points[0].y;
        for (int i = 1; i < points.length; i++) {
            left = left > points[i].x ? points[i].x : left;
            top = top > points[i].y ? points[i].y : top;
            right = right < points[i].x ? points[i].x : right;
            bottom = bottom < points[i].y ? points[i].y : bottom;
        }
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeWidth(5);

        //draw stroke
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(mStrokeColor);
        paint.setStrokeWidth(2);

        mCropRect.left = left + colorballs.get(0).getWidthOfBall() / 2;
        mCropRect.top = top + colorballs.get(0).getWidthOfBall() / 2;
        mCropRect.right = right + colorballs.get(2).getWidthOfBall() / 2;
        mCropRect.bottom = bottom + colorballs.get(3).getWidthOfBall() / 2;
        canvas.drawRect(mCropRect, paint);

        //fill the rectangle
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(mFillColor);
        paint.setStrokeWidth(0);
        canvas.drawRect(mCropRect, paint);

        // draw the balls on the canvas
        paint.setColor(Color.RED);
        paint.setTextSize(18);
        paint.setStrokeWidth(0);
        for (int i =0; i < colorballs.size(); i ++) {
            ColorBall ball = colorballs.get(i);
            canvas.drawBitmap(ball.getBitmap(), ball.getX(), ball.getY(),
                    paint);

            canvas.drawText("" + (i+1), ball.getX(), ball.getY(), paint);
        }
    }

    // events when touching the screen
    public boolean onTouchEvent(MotionEvent event) {
        int eventAction = event.getAction();

        int X = (int) event.getX();
        int Y = (int) event.getY();

        switch (eventAction) {

            case MotionEvent.ACTION_DOWN: // touch down so check if the finger is on
                // a ball
                if (points[0] == null) {
                    initRectangle(X, Y);
                } else {
                    //resize rectangle
                    balID = -1;
                    groupId = -1;
                    for (int i = colorballs.size()-1; i>=0; i--) {
                        ColorBall ball = colorballs.get(i);
                        // check if inside the bounds of the ball (circle)
                        // get the center for the ball
                        int centerX = ball.getX() + ball.getWidthOfBall();
                        int centerY = ball.getY() + ball.getHeightOfBall();
                        paint.setColor(Color.CYAN);
                        // calculate the radius from the touch to the center of the
                        // ball
                        double radCircle = Math
                                .sqrt((double) (((centerX - X) * (centerX - X)) + (centerY - Y)
                                        * (centerY - Y)));

                        if (radCircle < ball.getWidthOfBall()) {

                            balID = ball.getID();
                            if (balID == 1 || balID == 3) {
                                groupId = 2;
                            } else {
                                groupId = 1;
                            }
                            invalidate();
                            break;
                        }
                        invalidate();
                    }
                }
                break;

            case MotionEvent.ACTION_MOVE: // touch drag with the ball

                if (balID > -1) {
                    // move the balls the same as the finger
                    colorballs.get(balID).setX(X);
                    colorballs.get(balID).setY(Y);

                    paint.setColor(Color.CYAN);
                    if (groupId == 1) {
                        colorballs.get(1).setX(colorballs.get(0).getX());
                        colorballs.get(1).setY(colorballs.get(2).getY());
                        colorballs.get(3).setX(colorballs.get(2).getX());
                        colorballs.get(3).setY(colorballs.get(0).getY());
                    } else {
                        colorballs.get(0).setX(colorballs.get(1).getX());
                        colorballs.get(0).setY(colorballs.get(3).getY());
                        colorballs.get(2).setX(colorballs.get(3).getX());
                        colorballs.get(2).setY(colorballs.get(1).getY());
                    }

                    invalidate();
                }

                break;

            case MotionEvent.ACTION_UP:
                // touch drop - just do things here after dropping
                // doTheCrop()
                break;
        }
        // redraw the canvas
        invalidate();
        return true;
    }

    public void doTheCrop() {
        Bitmap sourceBitmap = null;
        Drawable backgroundDrawable = getBackground();
        if (backgroundDrawable instanceof BitmapDrawable) {
            BitmapDrawable bitmapDrawable = (BitmapDrawable) backgroundDrawable;
            if(bitmapDrawable.getBitmap() != null) {
                sourceBitmap = bitmapDrawable.getBitmap();
            }
        }
        if (sourceBitmap != null) {
            //source bitmap was scaled, you should calculate the rate
            float widthRate = ((float) sourceBitmap.getWidth()) / getWidth();
            float heightRate =  ((float) sourceBitmap.getHeight()) / getHeight();

            //crop the source bitmap with rate value
            int left = (int) (mCropRect.left * widthRate);
            int top = (int) (mCropRect.top * heightRate);
            int right = (int) (mCropRect.right * widthRate);
            int bottom = (int) (mCropRect.bottom * heightRate);
            Bitmap croppedBitmap = Bitmap.createBitmap(sourceBitmap, left, top, right - left, bottom - top);
            BitmapDrawable drawable = new BitmapDrawable(getResources(), croppedBitmap);
            setBackground(drawable);
        }
    }

    public static class ColorBall {

        Bitmap bitmap;
        Context mContext;
        Point point;
        int id;

        public ColorBall(Context context, int resourceId, Point point, int id) {
            this.id = id;
            bitmap = BitmapFactory.decodeResource(context.getResources(),
                    resourceId);
            mContext = context;
            this.point = point;
        }

        public int getWidthOfBall() {
            return bitmap.getWidth();
        }

        public int getHeightOfBall() {
            return bitmap.getHeight();
        }

        public Bitmap getBitmap() {
            return bitmap;
        }

        public int getX() {
            return point.x;
        }

        public int getY() {
            return point.y;
        }

        public int getID() {
            return id;
        }

        public void setX(int x) {
            point.x = x;
        }

        public void setY(int y) {
            point.y = y;
        }
    }
}