Как создать изменяемый размер прямоугольника с пользовательскими событиями касания на Android?
Я хочу создать прямоугольную форму, которая будет изменяться с помощью касаний пользователя. Ниже изображение является хорошим примером того, что я хочу сделать:
![Resizable Rectangle]()
Есть ли какой-нибудь пример? Что мне нужно для изучения, чтобы реализовать это?
Спасибо заранее,
Ответы
Ответ 1
Чтобы реализовать пользовательский вид, вы получаете класс из View:) Переопределите onDraw()
для внешнего вида, переопределите onTouchEvent()
для обработки ввода. Обратите внимание, что в Android вы не можете рисовать поверх onDraw()
; если вы хотите обновить представление, вызовите invalidate()
.
Вы можете использовать перетаскиваемые углы как отдельные виды. Для взглядов просто используйте готовые изображения (не стесняйтесь извлекать из ImageView
). Перетаскивание осуществляется как перемещение вашего представления в ответ на события касания. RelativeLayout
- ваш друг для позитивного позиционирования.
Вы можете добавить домашние макеты к макету; просто перейдите к редактированию XML и введите элемент <com.mypackage.MyViewClass>
.
Ответ 2
Ответ Chintan Rathod был отличным решением, но есть что-то не так, когда Он рисует прямоугольник. Я просто редактирую некоторые строки кода, чтобы он работал правильно с событием touch touch. Теперь вы можете добавить это представление в свой макет, а затем нажать, чтобы нарисовать.
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;
}
}
}
Ответ 3
Следующий код - рисовать прямоугольник на сенсорной базе.
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;
}
}
Ответ 4
Следующий код - это версия С# (в настоящее время я разрабатываю приложение на 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 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 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;
}
}
}
}
Ответ 5
Отредактированный ответ Нгуен Минь Бинь работал у меня. Но мне нужно было добавить облегченное исправление, чтобы не допустить, чтобы идентификатор мяча вышел из диапазона. Это произошло для меня, если мне пришлось повторно открыть активность, на которой размещается пользовательский вид. Я исправил эту строку:
this.id = count ++;
в
if (count > 3) count = 0;
this.id = count++;