Drag and move a circle drawn on canvas

前端 未结 1 1195
夕颜
夕颜 2021-01-31 00:19

I am working on an Android application which should allow user to draw a circle on canvas and drag it . I have been able to draw lines and circles on it.But I cant seem to be a

相关标签:
1条回答
  • 2021-01-31 00:42

    It seems that you might have issues with handling of multi-touch / drawing. There's some usefull tutorials about it on Android Developer site and on Android Blog.

    Based on this I was able to create an example which I think quite similar to that You're trying to achieve (without complete circle drawing - circles get generated by single touch):

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

    Activity contains only setContentView(R.layout.main) there main.xml is the following:

    <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 讨论(0)
提交回复
热议问题