我想创建一个矩形形状,它将随着用户的触摸而调整大小。下图是我想做的一个很好的例子:
有没有这样的例子?我需要学习什么来实现这一点?
提前致谢,
我想创建一个矩形形状,它将随着用户的触摸而调整大小。下图是我想做的一个很好的例子:
有没有这样的例子?我需要学习什么来实现这一点?
提前致谢,
Chintan Rathod 的回答是很好的解决方案,但是当他绘制矩形时出现了问题。我只是编辑了一些代码行以使其与用户触摸事件一起正常工作。现在,您可以将此视图添加到您的布局中,然后触摸进行绘制。
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.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.ihnel.englishpronounciation.R;
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<ColorBall>();
// array that holds the balls
private int balID = 0;
// variable to know what ball is being dragged
Paint paint;
Canvas canvas;
public DrawView(Context context) {
super(context);
paint = new Paint();
setFocusable(true); // necessary for getting the touch events
canvas = new Canvas();
}
public DrawView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
setFocusable(true); // necessary for getting the touch events
canvas = new Canvas();
}
// 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 = 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(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);
}
}
// 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) {
//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 (Point pt : points) {
colorballs.add(new ColorBall(getContext(), R.drawable.ic_circle, pt));
}
} 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
break;
}
// redraw the canvas
invalidate();
return true;
}
public static class ColorBall {
Bitmap bitmap;
Context mContext;
Point point;
int id;
static int count = 0;
public ColorBall(Context context, int resourceId, Point point) {
this.id = count++;
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;
}
}
}
要实现自定义视图,您从 View 派生一个类 :) 覆盖onDraw()
外观,覆盖onTouchEvent()
输入处理。请注意,在 Android 中,您不能在外部视图上绘制onDraw()
;如果要刷新视图,请调用invalidate()
.
您可以将可拖动的角实现为单独的视图。对于外观,只需使用现成的图像(随意派生自ImageView
)。拖动实现为响应触摸事件移动视图。RelativeLayout
是您任意视图定位的朋友。
您可以将自制视图添加到布局中;只需转到 XML 编辑并键入一个<com.mypackage.MyViewClass>
元素。
以下代码是在触摸底座上绘制矩形。
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.common.Utils;
import com.example.rectangleoverlay.R;
public class DrawView extends View {
Point point1, point3;
Point point2, point4;
/**
* point1 and point 3 are of same group and same as point 2 and point4
*/
int groupId = -1;
private ArrayList<ColorBall> colorballs = new ArrayList<ColorBall>();
// array that holds the balls
private int balID = 0;
// variable to know what ball is being dragged
Paint paint;
Canvas canvas;
public DrawView(Context context) {
super(context);
paint = new Paint();
setFocusable(true); // necessary for getting the touch events
canvas = new Canvas();
// setting the start point for the balls
point1 = new Point();
point1.x = 50;
point1.y = 20;
point2 = new Point();
point2.x = 150;
point2.y = 20;
point3 = new Point();
point3.x = 150;
point3.y = 120;
point4 = new Point();
point4.x = 50;
point4.y = 120;
// declare each ball with the ColorBall class
colorballs.add(new ColorBall(context, R.drawable.gray_circle, point1));
colorballs.add(new ColorBall(context, R.drawable.gray_circle, point2));
colorballs.add(new ColorBall(context, R.drawable.gray_circle, point3));
colorballs.add(new ColorBall(context, R.drawable.gray_circle, point4));
}
public DrawView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
setFocusable(true); // necessary for getting the touch events
canvas = new Canvas();
// setting the start point for the balls
point1 = new Point();
point1.x = 50;
point1.y = 20;
point2 = new Point();
point2.x = 150;
point2.y = 20;
point3 = new Point();
point3.x = 150;
point3.y = 120;
point4 = new Point();
point4.x = 50;
point4.y = 120;
// declare each ball with the ColorBall class
colorballs.add(new ColorBall(context, R.drawable.gray_circle, point1));
colorballs.add(new ColorBall(context, R.drawable.gray_circle, point2));
colorballs.add(new ColorBall(context, R.drawable.gray_circle, point3));
colorballs.add(new ColorBall(context, R.drawable.gray_circle, point4));
}
// the method that draws the balls
@Override
protected void onDraw(Canvas canvas) {
// canvas.drawColor(0xFFCCCCCC); //if you want another background color
paint.setAntiAlias(true);
paint.setDither(true);
paint.setColor(Color.parseColor("#55000000"));
paint.setStyle(Paint.Style.FILL);
paint.setStrokeJoin(Paint.Join.ROUND);
// mPaint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(5);
canvas.drawPaint(paint);
paint.setColor(Color.parseColor("#55FFFFFF"));
if (groupId == 1) {
canvas.drawRect(point1.x + colorballs.get(0).getWidthOfBall() / 2,
point3.y + colorballs.get(2).getWidthOfBall() / 2, point3.x
+ colorballs.get(2).getWidthOfBall() / 2, point1.y
+ colorballs.get(0).getWidthOfBall() / 2, paint);
} else {
canvas.drawRect(point2.x + colorballs.get(1).getWidthOfBall() / 2,
point4.y + colorballs.get(3).getWidthOfBall() / 2, point4.x
+ colorballs.get(3).getWidthOfBall() / 2, point2.y
+ colorballs.get(1).getWidthOfBall() / 2, paint);
}
BitmapDrawable mBitmap;
mBitmap = new BitmapDrawable();
// draw the balls on the canvas
for (ColorBall ball : colorballs) {
canvas.drawBitmap(ball.getBitmap(), ball.getX(), ball.getY(),
new 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
balID = -1;
groupId = -1;
for (ColorBall ball : colorballs) {
// check if inside the bounds of the ball (circle)
// get the center for the ball
Utils.logd("Id : " + ball.getID());
Utils.logd("getX : " + ball.getX() + " getY() : " + ball.getY());
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)));
Utils.logd("X : " + X + " Y : " + Y + " centerX : " + centerX
+ " CenterY : " + centerY + " radCircle : " + radCircle);
if (radCircle < ball.getWidthOfBall()) {
balID = ball.getID();
Utils.logd("Selected ball : " + balID);
if (balID == 1 || balID == 3) {
groupId = 2;
canvas.drawRect(point1.x, point3.y, point3.x, point1.y,
paint);
} else {
groupId = 1;
canvas.drawRect(point2.x, point4.y, point4.x, point2.y,
paint);
}
invalidate();
break;
}
invalidate();
}
break;
case MotionEvent.ACTION_MOVE: // touch drag with the ball
// move the balls the same as the finger
if (balID > -1) {
Utils.logd("Moving Ball : " + balID);
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());
canvas.drawRect(point1.x, point3.y, point3.x, point1.y,
paint);
} 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());
canvas.drawRect(point2.x, point4.y, point4.x, point2.y,
paint);
}
invalidate();
}
break;
case MotionEvent.ACTION_UP:
// touch drop - just do things here after dropping
break;
}
// redraw the canvas
invalidate();
return true;
}
public void shade_region_between_points() {
canvas.drawRect(point1.x, point3.y, point3.x, point1.y, paint);
}
}
以下类用于存储对象
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
public class ColorBall {
Bitmap bitmap;
Context mContext;
Point point;
int id;
static int count = 0;
public ColorBall(Context context, int resourceId, Point point) {
this.id = count++;
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;
}
}
工作演示 https://www.youtube.com/watch?v=BfYd7Xa-tCc
出于几个原因,我对评分最高的答案不满意。
它们不容易用作 xml 中的视图——缺少属性,因此视图不容易回收。
在 xml 中放入可绘制对象更容易时制作位图似乎很愚蠢
他们不考虑视图的边缘
它们不会通过向左或顶部拖动太远来防止矩形反转
他们不考虑从触摸位置到角点中心的偏移量
它们都依赖于折旧的位图方法
最重要的是,我无法让他们中的任何一个真正工作
我承认我的解决方案专门用于方形视图,但它可以很容易地调整为具有独立的 X 和 Y 边长。
将其放在 res/values 文件夹中:
custom_attributes.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="IconCropView">
<attr name="minimumSide" format="dimension"/>
<attr name="resizeCornerDrawable" format="reference"/>
<attr name="moveCornerDrawable" format="reference"/>
<attr name="cornerColor" format="color"/>
<attr name="edgeColor" format="color" />
<attr name="outsideCropColor" format="color" />
<attr name="cornerSize" format="dimension" />
</declare-styleable>
</resources>
将此添加到 java 包 IconCropView.java
package your_package;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import your_package.R;
public class IconCropView extends View {
//contants strings
private static final String TAG = "IconCropView";
//drawing objects
private Paint paint;
//point objects
private Point[] points;
private Point start;
private Point offset;
//variable ints
private int minimumSideLength;
private int side;
private int halfCorner;
private int cornerColor;
private int edgeColor;
private int outsideColor;
private int corner = 5;
//variable booleans
private boolean initialized = false;
//drawables
private Drawable moveDrawable;
private Drawable resizeDrawable1, resizeDrawable2, resizeDrawable3;
//context
Context context;
public IconCropView(Context context) {
super(context);
this.context = context;
init(null);
}
public IconCropView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context;
init(attrs);
}
public IconCropView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
init(attrs);
}
public IconCropView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
this.context = context;
init(attrs);
}
private void init(@Nullable AttributeSet attrs){
paint = new Paint();
start = new Point();
offset = new Point();
TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.IconCropView, 0, 0);
//initial dimensions
minimumSideLength = ta.getDimensionPixelSize(R.styleable.IconCropView_minimumSide, 20);
side = minimumSideLength;
halfCorner = (ta.getDimensionPixelSize(R.styleable.IconCropView_cornerSize, 20))/2;
//colors
cornerColor = ta.getColor(R.styleable.IconCropView_cornerColor, Color.BLACK);
edgeColor = ta.getColor(R.styleable.IconCropView_edgeColor, Color.WHITE);
outsideColor = ta.getColor(R.styleable.IconCropView_outsideCropColor, Color.parseColor("#00000088"));
//initialize corners;
points = new Point[4];
points[0] = new Point();
points[1] = new Point();
points[2] = new Point();
points[3] = new Point();
//init corner locations;
//top left
points[0].x = 0;
points[0].y = 0;
//top right
points[1].x = minimumSideLength;
points[1].y = 0;
//bottom left
points[2].x = 0;
points[2].y = minimumSideLength;
//bottom right
points[3].x = minimumSideLength;
points[3].y = minimumSideLength;
//init drawables
moveDrawable = ta.getDrawable(R.styleable.IconCropView_moveCornerDrawable);
resizeDrawable1 = ta.getDrawable(R.styleable.IconCropView_resizeCornerDrawable);
resizeDrawable2 = ta.getDrawable(R.styleable.IconCropView_resizeCornerDrawable);
resizeDrawable3 = ta.getDrawable(R.styleable.IconCropView_resizeCornerDrawable);
//set drawable colors
moveDrawable.setTint(cornerColor);
resizeDrawable1.setTint(cornerColor);
resizeDrawable2.setTint(cornerColor);
resizeDrawable3.setTint(cornerColor);
//recycle attributes
ta.recycle();
//set initialized to true
initialized = true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//set paint to draw edge, stroke
if(initialized) {
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setColor(edgeColor);
paint.setStrokeWidth(4);
//crop rectangle
canvas.drawRect(points[0].x + halfCorner,points[0].y + halfCorner, points[0].x + halfCorner + side, points[0].y + halfCorner + side, paint);
//set paint to draw outside color, fill
paint.setStyle(Paint.Style.FILL);
paint.setColor(outsideColor);
//top rectangle
canvas.drawRect(0, 0, canvas.getWidth(), points[0].y + halfCorner, paint);
//left rectangle
canvas.drawRect(0, points[0].y + halfCorner, points[0].x + halfCorner, canvas.getHeight(), paint);
//right rectangle
canvas.drawRect(points[0].x + halfCorner + side, points[0].y + halfCorner, canvas.getWidth(), points[0].y + halfCorner + side, paint);
//bottom rectangle
canvas.drawRect(points[0].x + halfCorner, points[0].y + halfCorner + side, canvas.getWidth(), canvas.getHeight(), paint);
//set bounds of drawables
moveDrawable.setBounds(points[0].x, points[0].y, points[0].x + halfCorner*2, points[0].y + halfCorner*2);
resizeDrawable1.setBounds(points[1].x, points[1].y, points[1].x + halfCorner*2, points[1].y + halfCorner*2);
resizeDrawable2.setBounds(points[2].x, points[2].y, points[2].x + halfCorner*2, points[2].y + halfCorner*2);
resizeDrawable3.setBounds(points[3].x, points[3].y, points[3].x + halfCorner*2, points[3].y+ halfCorner*2);
//place corner drawables
moveDrawable.draw(canvas);
resizeDrawable1.draw(canvas);
resizeDrawable2.draw(canvas);
resizeDrawable3.draw(canvas);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//return super.onTouchEvent(event);
switch(event.getActionMasked()){
case MotionEvent.ACTION_DOWN:{
//get the coordinates
start.x = (int)event.getX();
start.y = (int)event.getY();
//get the corner touched if any
corner = getCorner(start.x, start.y);
//get the offset of touch(x,y) from corner top-left point
offset = getOffset(start.x, start.y, corner);
//account for touch offset in starting point
start.x = start.x - offset.x;
start.y = start.y - offset.y;
break;
}
case MotionEvent.ACTION_UP:{
}
case MotionEvent.ACTION_MOVE:{
if(corner == 0) {
points[0].x = Math.max(points[0].x + (int) Math.min(Math.floor((event.getX() - start.x - offset.x)), Math.floor(getWidth() - points[0].x - 2*halfCorner - side)), 0);
points[1].x = Math.max(points[1].x + (int) Math.min(Math.floor((event.getX() - start.x - offset.x)), Math.floor(getWidth() - points[1].x - 2*halfCorner)), side);
points[2].x = Math.max(points[2].x + (int) Math.min(Math.floor((event.getX() - start.x - offset.x)), Math.floor(getWidth() - points[2].x - 2*halfCorner - side)), 0);
points[3].x = Math.max(points[3].x + (int) Math.min(Math.floor((event.getX() - start.x - offset.x)), Math.floor(getWidth() - points[3].x - 2*halfCorner)), side);
points[0].y = Math.max(points[0].y + (int) Math.min(Math.floor((event.getY() - start.y - offset.y)), Math.floor(getHeight() - points[0].y - 2*halfCorner - side)), 0);
points[1].y = Math.max(points[1].y + (int) Math.min(Math.floor((event.getY() - start.y - offset.y)), Math.floor(getHeight() - points[1].y - 2*halfCorner - side)), 0);
points[2].y = Math.max(points[2].y + (int) Math.min(Math.floor((event.getY() - start.y - offset.y)), Math.floor(getHeight() - points[2].y - 2*halfCorner)), side);
points[3].y = Math.max(points[3].y + (int) Math.min(Math.floor((event.getY() - start.y - offset.y)), Math.floor(getHeight() - points[3].y - 2*halfCorner)), side);
start.x = points[0].x;
start.y = points[0].y;
invalidate();
}else if (corner == 1){
side = Math.min((Math.min((Math.max(minimumSideLength, (int)(side + Math.floor(event.getX()) - start.x - offset.x))), side + (getWidth() - points[1].x - 2* halfCorner ))),side + (getHeight() - points[2].y - 2* halfCorner ));
points[1].x = points[0].x + side;
points[3].x = points[0].x + side;
points[3].y = points[0].y + side;
points[2].y = points[0].y + side;
start.x = points[1].x;
invalidate();
}else if (corner == 2){
side = Math.min((Math.min((Math.max(minimumSideLength, (int)(side + Math.floor(event.getY()) - start.y - offset.y))), side + (getHeight() - points[2].y - 2* halfCorner ))),side + (getWidth() - points[1].x - 2* halfCorner ));
points[2].y = points[0].y + side;
points[3].y = points[0].y + side;
points[3].x = points[0].x + side;
points[1].x = points[0].x + side;
start.y = points[2].y;
invalidate();
}else if (corner == 3){
side = Math.min((Math.min((Math.min((Math.max(minimumSideLength, (int)(side + Math.floor(event.getX()) - start.x - offset.x))), side + (getWidth() - points[3].x - 2* halfCorner ))),side + (getHeight() - points[3].y - 2* halfCorner ))), Math.min((Math.min((Math.max(minimumSideLength, (int)(side + Math.floor(event.getY()) - start.y - offset.y))), side + (getHeight() - points[3].y - 2* halfCorner ))),side + (getWidth() - points[3].x - 2* halfCorner )));
points[1].x = points[0].x + side;
points[3].x = points[0].x + side;
points[3].y = points[0].y + side;
points[2].y = points[0].y + side;
start.x = points[3].x;
points[2].y = points[0].y + side;
points[3].y = points[0].y + side;
points[3].x = points[0].x + side;
points[1].x = points[0].x + side;
start.y = points[3].y;
invalidate();
}
break;
}
}
return true;
}
private int getCorner(float x, float y){
int corner = 5;
for (int i = 0; i < points.length; i++){
float dx = x - points[i].x;
float dy = y - points[i].y;
int max = halfCorner * 2;
if(dx <= max && dx >= 0 && dy <= max && dy >= 0){
return i;
}
}
return corner;
}
private Point getOffset(int left, int top, int corner){
Point offset = new Point();
if(corner == 5){
offset.x = 0;
offset.y = 0;
}else{
offset.x = left - points[corner].x;
offset.y = top - points[corner].y;
}
return offset;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
在您的布局 xml 中:
您必须指定一个可绘制的移动角
您必须为调整角指定一个可绘制对象
角大小默认为 20px
minimumSide必须至少为角尺寸——默认值为 20px
您必须在根视图中包含命名空间“http://schemas.android.com/apk/res-auto”(我使用了别名 xlmn:app="http://schemas.android.com/apk/res -汽车”)
示例 xml 布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:background="@color/primary"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/crop_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:srcCompat="@mipmap/ic_launcher" />
<your_package.IconCropView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/transparent"
app:minimumSide="60dp"
app:resizeCornerDrawable="@drawable/adjust_edge_circle"
app:moveCornerDrawable="@drawable/move_box_circle"
app:cornerColor="@color/turq"
app:edgeColor="@color/colorPrimary"
app:outsideCropColor="@color/transparent_50"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:cornerSize="20dp"/>
Nguyen Minh Binh 的编辑答案对我有用。但我需要添加一个精简版修复程序以防止球 id 超出范围。如果我必须重新打开托管自定义视图的活动,就会发生这种情况。我修复了这些行:
this.id = 计数++;
到:
if (count > 3) count = 0;
this.id = count++;
以下代码是所请求代码的 C# 版本(我目前正在 MonoDroid/Xamarin 上开发应用程序),但有一些改进和拖动矩形的能力。还想增加一些功能,以后再编辑。
namespace ImagePlayground
{
[Activity (Label = "MyView2")]
public class MyView2 : View
{
Graphics.Point[] points = new Graphics.Point[4];
// Array that hold the circle
private List<ResizeCircle> circles = new List<ResizeCircle>();
// Variable to keep tracking of which circle is being dragged
private int circleId = -1;
// Points are grouped in groups of two so there's always only one fixed point
// groupId = 0 > Touch Inside the Rectangle
// groupId = 1 > Points 0 and 2
// groupId = 2 > Points 1 and 3
int groupId = -1;
// FirstTouch's Coordinate for Tracking on Dragging
int xFirstTouch = 0;
int yFirstTouch = 0;
/** Main Bitmap **/
private Bitmap mBitmap = null;
/** Measured Size of the View **/
private Rect mMeasuredRect;
/** Paint to Draw Rectangles **/
private Paint mRectPaint;
public MyView2(Context ctx) : base (ctx){
init (ctx);
}
public MyView2 (Context ctx, IAttributeSet attrs) : base (ctx, attrs){
init (ctx);
}
public MyView2 (Context ctx, IAttributeSet attrs, int defStyle) : base(ctx,attrs,defStyle){
init (ctx);
}
private void init(Context ctx){
// For Touch Events
Focusable = true;
// Draw the Image on the Background
mBitmap = BitmapFactory.DecodeResource(ctx.Resources, Resource.Drawable.bg);
// Sets up the paint for the Drawable Rectangles
mRectPaint = new Paint ();
mRectPaint.Color = Android.Graphics.Color.Aqua;
mRectPaint.StrokeWidth = 4;
mRectPaint.SetStyle (Paint.Style.Stroke);
}
protected override void OnDraw(Canvas canvas){
// Background Bitmap to Cover all Area
canvas.DrawBitmap(mBitmap, null, mMeasuredRect, null);
// Just draw the points only if it has already been initiated
if (points [3] != null) {
int left, top, right, bottom;
left = points [0].X;
top = points [0].Y;
right = points [0].X;
bottom = points [0].Y;
// Sets the circles' locations
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;
}
mRectPaint.AntiAlias = true;
mRectPaint.Dither = true;
mRectPaint.StrokeJoin = Paint.Join.Round;
mRectPaint.StrokeWidth = 5;
mRectPaint.SetStyle (Paint.Style.Stroke);
mRectPaint.Color = Graphics.Color.ParseColor ("#0079A3");
canvas.DrawRect (
left + circles [0].GetCircleWidth () / 2,
top + circles [0].GetCircleWidth () / 2,
right + circles [2].GetCircleWidth () / 2,
bottom + circles [2].GetCircleWidth () / 2, mRectPaint);
// Fill The Rectangle
mRectPaint.SetStyle (Paint.Style.Fill);
mRectPaint.Color = Graphics.Color.ParseColor ("#B2D6E3");
mRectPaint.Alpha = 75;
mRectPaint.StrokeWidth = 0;
canvas.DrawRect (
left + circles [0].GetCircleWidth () / 2,
top + circles [0].GetCircleWidth () / 2,
right + circles [2].GetCircleWidth () / 2,
bottom + circles [2].GetCircleWidth () / 2, mRectPaint);
// DEBUG
mRectPaint.Color = Graphics.Color.Red;
mRectPaint.TextSize = 18;
mRectPaint.StrokeWidth = 0;
// Draw every circle on the right position
for (int i = 0; i < circles.Count (); i++) {
ResizeCircle circle = circles [i];
float x = circle.GetX ();
float y = circle.GetY ();
canvas.DrawBitmap (circle.GetBitmap (), x, y,
mRectPaint);
// DEBUG
// canvas.DrawText ("" + (i + 1), circle.GetX (), circle.GetY (), mRectPaint);
}
}
}
public override bool OnTouchEvent(MotionEvent e){
// Get the Coordinates of Touch
int xTouch = (int) e.GetX ();
int yTouch = (int) e.GetY ();
int actionIndex = e.ActionIndex;
switch (e.ActionMasked) {
// In case user touch the screen
case MotionEventActions.Down:
// If no points were created
if (points [0] == null) {
// Offset to create the points
int offset = 60;
// Initialize a new Rectangle.
points [0] = new Graphics.Point ();
points [0].X = xTouch;
points [0].Y = yTouch;
points [1] = new Graphics.Point ();
points [1].X = xTouch;
points [1].Y = yTouch + offset;
points [2] = new Graphics.Point ();
points [2].X = xTouch + offset;
points [2].Y = yTouch + offset;
points [3] = new Graphics.Point ();
points [3].X = xTouch + offset;
points [3].Y = yTouch;
// Add each circle to circles array
foreach (Graphics.Point pt in points) {
circles.Add (new ResizeCircle (Context, Resource.Drawable.circle, pt));
}
} else {
// Register Which Circle (if any) th user has touched
groupId = getTouchedCircle (xTouch, yTouch);
xFirstTouch = xTouch;
yFirstTouch = yTouch;
}
break;
case MotionEventActions.PointerDown:
break;
case MotionEventActions.Move:
if (groupId == 1 || groupId == 2) {
// Move touched Circle as the finger moves
circles[circleId].SetX(xTouch);
circles[circleId].SetY(yTouch);
// Move the two other circles accordingly
if (groupId == 1) {
circles[1].SetX(circles[0].GetX());
circles[1].SetY(circles[2].GetY());
circles[3].SetX(circles[2].GetX());
circles[3].SetY(circles[0].GetY());
} else {
circles[0].SetX(circles[1].GetX());
circles[0].SetY(circles[3].GetY());
circles[2].SetX(circles[3].GetX());
circles[2].SetY(circles[1].GetY());
}
Invalidate();
} else if (groupId == 0){
// Calculate the delta for the dragging
int xDelta = (xTouch-xFirstTouch);
int yDelta = (yTouch-yFirstTouch);
xFirstTouch = xTouch;
yFirstTouch = yTouch;
// Move each circle accordingly
foreach (ResizeCircle circle in circles) {
circle.SetX (circle.GetX () + xDelta);
circle.SetY (circle.GetY () + yDelta);
}
// Redraw the view
Invalidate ();
}
break;
case MotionEventActions.Up:
break;
default:
break;
}
Invalidate ();
return true;
}
private int getTouchedCircle(int xTouch, int yTouch){
int groupId = -1;
for (int i = 0; i < circles.Count; i++) {
ResizeCircle circle = circles [i];
// Check if the touch was inside the bounds of the circle
int centerX = circle.GetX () + circle.GetCircleWidth ();
int centerY = circle.GetY () + circle.GetCircleHeight ();
// Calculate the radius from the touch to the center of the circle
double radCircle = Math.Sqrt ((double)(((centerX - xTouch) * (centerX - xTouch)) + (centerY - yTouch)
* (centerY - yTouch)));
// If the touch was on one of the circles
if (radCircle < circle.GetCircleWidth ()) {
circleId = circle.GetID ();
if (circleId == 1 || circleId == 3) {
groupId = 2;
break;
} else {
groupId = 1;
break;
}
} else {
// User didn't touch any of the circles nor the inside area
groupId = -1;
}
}
// If the touch wasn't on one of the circles, check if it was inside the rectangle
if (groupId == -1) {
List<int> xCoords = new List<int> ();
List<int> yCoords = new List<int> ();
// Gather Coordinates from all circles
foreach (ResizeCircle circle in circles){
xCoords.Add (circle.GetX());
yCoords.Add (circle.GetY());
}
// Store the max and min coordinates
int minX = xCoords.Min ();
int maxX = xCoords.Max ();
int minY = yCoords.Min ();
int maxY = yCoords.Max ();
// Check if user has touched inside the rectangle
if ((xTouch > minX && xTouch < maxX) && (yTouch > minY && yTouch < maxY)) {
// User has touched inside the Rectangle
groupId = 0;
}
}
return groupId;
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec){
base.OnMeasure (widthMeasureSpec, heightMeasureSpec);
mMeasuredRect = new Rect (0, 0, MeasuredWidth, MeasuredHeight);
}
public class ResizeCircle {
Bitmap bitmap;
Graphics.Point point;
int id;
static int count = 0;
public ResizeCircle(Context context, int resourceId, Graphics.Point point) {
this.id = count++;
bitmap = BitmapFactory.DecodeResource(context.Resources,
resourceId);
Log.Debug("BITMAP" , bitmap.Height.ToString());
this.point = point;
}
public int GetCircleWidth() {
return bitmap.Width;
}
public int GetCircleHeight() {
return bitmap.Height;
}
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;
}
}
}
}