2013-07-24 17 views
14

Pracuję nad aplikacją na Androida, która powinna umożliwić użytkownikowi narysowanie okręgu na płótnie i przeciągnięcie go. Byłem w stanie narysować na nim linie i okręgi. Ale nie mogę tego przeciągnąć.Przeciągnij i przesuń kółko rysowane na płótnie

Czy można przeciągnąć obiekt narysowany na płótnie w systemie Android?. Jeśli na przykład wykryję dotyk w kole, jak usunąć krąg z poprzedniej pozycji i przenieść go do bieżącej pozycji? Próbowałem invalidate(). Ale nie będzie działać, jeśli użytkownik narysował wiele kół i chce przenieść wszystkie z nich.

+0

W jaki sposób przechowujesz informacje o narysowanych okręgach? Czy to jest dokładne koło (rysowane przez drawCircle())? – sandrstar

+0

Utworzono klasę ziarna dla okręgu, w którym są zapisane współrzędne X, Y środka i promienia. – Shravz

+1

Niektóre kody mogą być przydatne. Zakładam, że masz jakiś widok rysunku, który przechowuje i rysuje te "okręgi" w metodzie onDraw(). Jeśli tak, to unieważnij() powinno działać (jak w tym pytaniu z jednym kółkiem http://stackoverflow.com/q/17289576/657487). Czy mam rację? Czy problem dotyczy wielu wskaźników dotykowych? – sandrstar

Odpowiedz

35

Wygląda na to, że mogą wystąpić problemy z obsługą funkcji multi-touch/drawing. Jest kilka przydatnych tutoriali na ten temat: Android Developer site i Android Blog.

Na tej podstawie udało mi się stworzyć przykład co moim zdaniem całkiem podobna do tej Starasz się osiągnąć (bez kompletnego koła rysunku - okręgi generowana przez jednego przycisku):

public class CirclesDrawingView extends View { 

    private static final String TAG = "CirclesDrawingView"; 

    /** Main bitmap */ 
    private Bitmap mBitmap = null; 

    private Rect mMeasuredRect; 

    /** Stores data about single circle */ 
    private static class CircleArea { 
     int radius; 
     int centerX; 
     int centerY; 

     CircleArea(int centerX, int centerY, int radius) { 
      this.radius = radius; 
      this.centerX = centerX; 
      this.centerY = centerY; 
     } 

     @Override 
     public String toString() { 
      return "Circle[" + centerX + ", " + centerY + ", " + radius + "]"; 
     } 
    } 

    /** Paint to draw circles */ 
    private Paint mCirclePaint; 

    private final Random mRadiusGenerator = new Random(); 
    // Radius limit in pixels 
    private final static int RADIUS_LIMIT = 100; 

    private static final int CIRCLES_LIMIT = 3; 

    /** All available circles */ 
    private HashSet<CircleArea> mCircles = new HashSet<CircleArea>(CIRCLES_LIMIT); 
    private SparseArray<CircleArea> mCirclePointer = new SparseArray<CircleArea>(CIRCLES_LIMIT); 

    /** 
    * Default constructor 
    * 
    * @param ct {@link android.content.Context} 
    */ 
    public CirclesDrawingView(final Context ct) { 
     super(ct); 

     init(ct); 
    } 

    public CirclesDrawingView(final Context ct, final AttributeSet attrs) { 
     super(ct, attrs); 

     init(ct); 
    } 

    public CirclesDrawingView(final Context ct, final AttributeSet attrs, final int defStyle) { 
     super(ct, attrs, defStyle); 

     init(ct); 
    } 

    private void init(final Context ct) { 
     // Generate bitmap used for background 
     mBitmap = BitmapFactory.decodeResource(ct.getResources(), R.drawable.up_image); 

     mCirclePaint = new Paint(); 

     mCirclePaint.setColor(Color.BLUE); 
     mCirclePaint.setStrokeWidth(40); 
     mCirclePaint.setStyle(Paint.Style.FILL); 
    } 

    @Override 
    public void onDraw(final Canvas canv) { 
     // background bitmap to cover all area 
     canv.drawBitmap(mBitmap, null, mMeasuredRect, null); 

     for (CircleArea circle : mCircles) { 
      canv.drawCircle(circle.centerX, circle.centerY, circle.radius, mCirclePaint); 
     } 
    } 

    @Override 
    public boolean onTouchEvent(final MotionEvent event) { 
     boolean handled = false; 

     CircleArea touchedCircle; 
     int xTouch; 
     int yTouch; 
     int pointerId; 
     int actionIndex = event.getActionIndex(); 

     // get touch event coordinates and make transparent circle from it 
     switch (event.getActionMasked()) { 
      case MotionEvent.ACTION_DOWN: 
       // it's the first pointer, so clear all existing pointers data 
       clearCirclePointer(); 

       xTouch = (int) event.getX(0); 
       yTouch = (int) event.getY(0); 

       // check if we've touched inside some circle 
       touchedCircle = obtainTouchedCircle(xTouch, yTouch); 
       touchedCircle.centerX = xTouch; 
       touchedCircle.centerY = yTouch; 
       mCirclePointer.put(event.getPointerId(0), touchedCircle); 

       invalidate(); 
       handled = true; 
       break; 

      case MotionEvent.ACTION_POINTER_DOWN: 
       Log.w(TAG, "Pointer down"); 
       // It secondary pointers, so obtain their ids and check circles 
       pointerId = event.getPointerId(actionIndex); 

       xTouch = (int) event.getX(actionIndex); 
       yTouch = (int) event.getY(actionIndex); 

       // check if we've touched inside some circle 
       touchedCircle = obtainTouchedCircle(xTouch, yTouch); 

       mCirclePointer.put(pointerId, touchedCircle); 
       touchedCircle.centerX = xTouch; 
       touchedCircle.centerY = yTouch; 
       invalidate(); 
       handled = true; 
       break; 

      case MotionEvent.ACTION_MOVE: 
       final int pointerCount = event.getPointerCount(); 

       Log.w(TAG, "Move"); 

       for (actionIndex = 0; actionIndex < pointerCount; actionIndex++) { 
        // Some pointer has moved, search it by pointer id 
        pointerId = event.getPointerId(actionIndex); 

        xTouch = (int) event.getX(actionIndex); 
        yTouch = (int) event.getY(actionIndex); 

        touchedCircle = mCirclePointer.get(pointerId); 

        if (null != touchedCircle) { 
         touchedCircle.centerX = xTouch; 
         touchedCircle.centerY = yTouch; 
        } 
       } 
       invalidate(); 
       handled = true; 
       break; 

      case MotionEvent.ACTION_UP: 
       clearCirclePointer(); 
       invalidate(); 
       handled = true; 
       break; 

      case MotionEvent.ACTION_POINTER_UP: 
       // not general pointer was up 
       pointerId = event.getPointerId(actionIndex); 

       mCirclePointer.remove(pointerId); 
       invalidate(); 
       handled = true; 
       break; 

      case MotionEvent.ACTION_CANCEL: 
       handled = true; 
       break; 

      default: 
       // do nothing 
       break; 
     } 

     return super.onTouchEvent(event) || handled; 
    } 

    /** 
    * Clears all CircleArea - pointer id relations 
    */ 
    private void clearCirclePointer() { 
     Log.w(TAG, "clearCirclePointer"); 

     mCirclePointer.clear(); 
    } 

    /** 
    * Search and creates new (if needed) circle based on touch area 
    * 
    * @param xTouch int x of touch 
    * @param yTouch int y of touch 
    * 
    * @return obtained {@link CircleArea} 
    */ 
    private CircleArea obtainTouchedCircle(final int xTouch, final int yTouch) { 
     CircleArea touchedCircle = getTouchedCircle(xTouch, yTouch); 

     if (null == touchedCircle) { 
      touchedCircle = new CircleArea(xTouch, yTouch, mRadiusGenerator.nextInt(RADIUS_LIMIT) + RADIUS_LIMIT); 

      if (mCircles.size() == CIRCLES_LIMIT) { 
       Log.w(TAG, "Clear all circles, size is " + mCircles.size()); 
       // remove first circle 
       mCircles.clear(); 
      } 

      Log.w(TAG, "Added circle " + touchedCircle); 
      mCircles.add(touchedCircle); 
     } 

     return touchedCircle; 
    } 

    /** 
    * Determines touched circle 
    * 
    * @param xTouch int x touch coordinate 
    * @param yTouch int y touch coordinate 
    * 
    * @return {@link CircleArea} touched circle or null if no circle has been touched 
    */ 
    private CircleArea getTouchedCircle(final int xTouch, final int yTouch) { 
     CircleArea touched = null; 

     for (CircleArea circle : mCircles) { 
      if ((circle.centerX - xTouch) * (circle.centerX - xTouch) + (circle.centerY - yTouch) * (circle.centerY - yTouch) <= circle.radius * circle.radius) { 
       touched = circle; 
       break; 
      } 
     } 

     return touched; 
    } 

    @Override 
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 

     mMeasuredRect = new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight()); 
    } 
} 

aktywny zawiera tylko setContentView(R.layout.main) tam plik main.xml jest następujący:

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_height="match_parent" 
    android:layout_width="match_parent" 
    android:id="@+id/scroller"> 
    <com.example.TestApp.CirclesDrawingView 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" /> 
</RelativeLayout> 
+0

Utknąłem w podobny problem, próbuję to również wdrożyć. Przekreślony palec. –

+0

Otrzymuję te błędy podczas wdrażania tego kodu http://pastebin.com/4mHdHh6W: –

+0

Problemy są teraz rozwiązywane. –