自定义 ImageView 裁剪,如 Whatsapp
Posted
技术标签:
【中文标题】自定义 ImageView 裁剪,如 Whatsapp【英文标题】:Cutom ImageView Cropping like Whatsapp 【发布时间】:2019-05-13 13:49:37 【问题描述】:我正在尝试创建自定义图像裁剪,例如 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);
所以我需要解决一些问题。请帮忙
所有 4 个点在首次启动时相互重叠,并且仅在用户触摸屏幕后可见。我尝试在背景 ImageView 的 50% 处拉伸 4 个点,但没有这样做。
成功选择要裁剪的特定区域后。如何获取该区域的背景图像视图(请告诉我如何执行此操作)。
【问题讨论】:
【参考方案1】: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.
Answer: 你应该为points数组设置一个合适的init值,origin answer代码逻辑是在手指触摸屏int onTouchEvent()
MotionEvent.ACTION_DOWN
类型时赋值points数组并使视图无效然后视图将执行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 link 和GIF 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;
【讨论】:
感谢您的回答,但正如您所见,我已经尝试过onDraw
方法,但它根本不起作用。如果您能告诉我如何正确操作,将对我有所帮助。
@Magic 我尝试了上面的解决方案,它给了我预期的结果,但是用上面的方法裁剪图像不起作用,因为上面的代码永远不会满足if (backgroundDrawable instanceof BitmapDrawable)
@androidXP你可以查看demo代码,图片被设置为视图背景,所以doTheCrop()
方法从视图背景中获取BitmapDrawable。您可以根据需要自定义逻辑解码位图或其他内容,但想法是获取位图并根据裁剪矩形创建裁剪位图。
感谢回复以上是关于自定义 ImageView 裁剪,如 Whatsapp的主要内容,如果未能解决你的问题,请参考以下文章
java 自定义Android ImageView,用于包含drawable的顶部裁剪。
如何为 UITableViews 裁剪 cell.imageview.image 中的图像?