2012-01-23 7 views
17

Chcę utworzyć prostokątny kształt, który zostanie przeskalowany za pomocą dotknięć użytkownika. Poniżej obrazu jest dobrym przykładem tego, co chcę zrobić:Jak utworzyć skalowalny prostokąt z wydarzeniami dotykowymi na Androidzie?

Resizable Rectangle

Czy istnieje jakiś przykład takiego? Co muszę zbadać, aby to wdrożyć?

Dzięki z góry,

+0

Odpowiedź będzie zależała od tego, co już wiesz. Czy masz jakieś wrażenia z Androida? Dowolna obsługa Java? Systemy GUI w ogóle? Jakiekolwiek doświadczenie w programowaniu? Nie jestem snarky, to po prostu nie jest oczywiste z tego pytania. –

+0

to funkcja przycinania, którą chcesz osiągnąć? – ariefbayu

+0

@SevaAlekseyev Mam doświadczenie z Androida, ale nigdy nie zaimplementowałem niestandardowego widoku. – ipman

Odpowiedz

5

wdrożyć własny widok, czerpać klasa z widokiem :) Przestawianie onDraw() dla wyglądu, zastępują onTouchEvent() do przetwarzania sygnału. Pamiętaj, że w systemie Android nie możesz narysować widoku poza numerem onDraw(); jeśli chcesz odświeżyć widok, zadzwoń pod numer invalidate().

Narożniki przeciągalne można implementować jako osobne widoki. Aby uzyskać wygląd, po prostu użyj gotowych zdjęć (zachęcamy do wypróbowania od ImageView). Przeciąganie jest zaimplementowane jako przeniesienie widoku w odpowiedzi na zdarzenia dotykowe. RelativeLayout jest twoim przyjacielem dla arbitralnego poglądu na stanowisko.

Do widoku można dodać widoki domowej roboty; po prostu przejdź do edycji XML i wpisz element <com.mypackage.MyViewClass>.

+0

Dobre rozwiązanie, ale utrudnia implementację powiększania niestandardowego widoku, ponieważ widoki podrzędne w układzie względnym nie będą odpowiednio zmieniane/powiększane. Dlatego większość użytkowników implementuje onDraw na dotyk w onTouchEvent ręcznie - tak jak w innym przykładzie z DrawView i ColorBalls. – Matthias

4

Poniższy kod służy do rysowania prostokąta na podstawie dotykowej. służy do sklepu

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); 
    } 
} 

następujące klasy obiektów odpowiedź

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; 
    } 
} 
+0

id zmienia się dynamicznie to tak ...? –

+0

@ chintan-rathod Dzięki za tę odpowiedź. Proszę dać mi znać, jak zatrzymać nakładanie się i przekraczanie boków. – Tauqir

+0

@Tauqir Właśnie odłożyłem rozwój w tych dniach dla tego problemu. Więc musisz sprawdzić mojego przyjaciela. –

17

Chintan Rathod było świetne rozwiązanie, ale jest coś nie tak, kiedy rysuje prostokąt. Właśnie edytuję niektóre linie kodu, aby działało poprawnie z wydarzeniem dotykowym użytkownika. Teraz możesz dodać ten widok do swojego układu, a następnie dotknąć, aby narysować.

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; 
     } 
    } 
} 
+0

+1 za uaktualnienie odpowiedzi. Po prostu to odkładam, ponieważ nie mam czasu na podkreślanie problemu. :) –

+1

Powinieneś używać 'ball.getWidthOfBall()/2' i' ball.getHeightOfBall()/2' podczas obliczania centerX i centerY. – vamsiampolu

+0

Czy możesz podać mi bardziej rozsądne wyjaśnienie, user2309862? –

0

Poniższy kod jest wersja C# (jestem obecnie rozwija aplikację na MonoDroid/Xamarin) żądanego kodu, ale z pewnych ulepszeń i zdolności, aby przeciągnąć prostokąt. Nadal chcesz dodać niektóre funkcje, będzie edytować później.

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; 
     } 
    } 
    } 
    } 
0

Edytowana odpowiedź Nguyen Minh Binh zadziałała dla mnie. Ale musiałem dodać lite fix, aby uniemożliwić identyfikację kulki poza zasięgiem. Miało to miejsce, gdy musiałem ponownie otworzyć działanie hostujące niestandardowy widok. Naprawiłem te linie:

this.id = count ++;

do:

if (count > 3) count = 0; 
this.id = count++; 
Powiązane problemy