简体   繁体   English

在Android中使Canvas Draggable圈变成

[英]Make circle of Canvas Draggable in android

I am learning Custom Views and succeeded in creating three circle and lines between them . 我正在学习自定义视图,成功three circle and lines between them创建了three circle and lines between them How could I make those circle's draggable. 我该如何使这些圈子成为可拖动对象。

First of all I want to know that I click on inside the circle using onTouch() and then update these circle position accordingly. 首先,我想知道我使用onTouch()在圆内单击,然后相应地更新了这些圆的位置。

MyDrawingView MyDrawingView

public class CustomDrawing extends View {

    private static final String TAG = "CustomDrawing";

    private Paint circlePaint;
    private Paint linePaint;
    private Paint textPaint;

    private int centerX,centerY;

    private float circleSize = 80;

    public CustomDrawing(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setFocusable(true);
        setFocusableInTouchMode(true);
        setupPaint();
    }

    private void setupPaint() {
        circlePaint = new Paint();
        circlePaint.setColor(Color.BLACK);
        circlePaint.setAntiAlias(true);
        circlePaint.setStrokeWidth(5);
        circlePaint.setStyle(Paint.Style.STROKE);
        circlePaint.setStrokeJoin(Paint.Join.ROUND);
        circlePaint.setStrokeCap(Paint.Cap.ROUND);

        linePaint = new Paint();
        linePaint.setColor(Color.WHITE);
        linePaint.setAntiAlias(true);
        linePaint.setStrokeWidth((float) 1.5);;

        textPaint = new Paint();
        textPaint.setColor(Color.WHITE);
        textPaint.setTextSize(60);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setFakeBoldText(true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        ///super.onDraw(canvas);

        centerX = canvas.getWidth()/2;
        centerY = canvas.getHeight()/2;

        //Top Left Circle
        canvas.drawCircle(circleSize, circleSize, 80, circlePaint);
        canvas.drawText("LC",circleSize,getyPositionOfText(circleSize,textPaint),textPaint);

        //Center Circle
        circlePaint.setColor(Color.GREEN);
        canvas.drawCircle(centerX, centerY, circleSize, circlePaint);
        ////int yPos = (int) ((canvas.getHeight() / 2) - ((textPaint.descent() + textPaint.ascent()) / 2)) ;
        //((textPaint.descent() + textPaint.ascent()) / 2) is the distance from the baseline to the center.
        canvas.drawText("CC",centerX,getyPositionOfText(canvas.getHeight()/2,textPaint),textPaint);
        ///canvas.drawText("CC",50,50,20,20,textPaint);

        //Bottom Right Circle
        circlePaint.setColor(Color.BLACK);
        canvas.drawCircle(canvas.getWidth() - circleSize, canvas.getHeight() - circleSize, 80, circlePaint);

        //Center to Left TOP and Center to Right TOP LINE
        canvas.drawLine(centerX,centerY,circleSize,circleSize,linePaint);//center to top left
        canvas.drawLine(centerX,centerY,canvas.getWidth() - circleSize,circleSize,linePaint);//center to top right

        //Center to Left BOTTOM and Center to Right BOTTOM LINE
        linePaint.setColor(Color.BLACK);
        canvas.drawLine(centerX,centerY, circleSize,
                canvas.getHeight() - circleSize,linePaint);// center to bottom left
        canvas.drawLine(centerX,centerY,canvas.getWidth() - circleSize,
                canvas.getHeight() - circleSize,linePaint);// center to bottom right

        linePaint.setColor(Color.WHITE);
        canvas.drawLine(centerX,centerY,circleSize,canvas.getHeight()/2,linePaint);
        linePaint.setColor(Color.BLACK);
        canvas.drawLine(centerX,centerY,canvas.getWidth() - circleSize,canvas.getHeight()/2,linePaint);

        //Left top to left bottom
        canvas.drawLine(circleSize,circleSize,circleSize,canvas.getHeight() - circleSize,linePaint);
        //Right t top to Right bottom
        canvas.drawLine(canvas.getWidth() - circleSize,circleSize,canvas.getWidth() - circleSize,canvas.getHeight() - circleSize,linePaint);


        linePaint.setColor(Color.GREEN);
        canvas.drawLine(circleSize,circleSize,canvas.getWidth()-circleSize,circleSize,linePaint);
        canvas.drawLine(circleSize,canvas.getHeight() -circleSize,canvas.getWidth()-circleSize,canvas.getHeight() -circleSize,linePaint);
    }

    private int getyPositionOfText(float yPositionOfText,Paint mPaint){
        return (int) ((yPositionOfText) - ((mPaint.descent() + mPaint.ascent()) / 2)) ;
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float pointX = event.getX();
        float pointY = event.getY();
        // Checks for the event that occurs
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                return true;
            case MotionEvent.ACTION_MOVE:
                break;
            default:
                return false;
        }
        // Force a view to draw again
        postInvalidate();
        return true;
    }
}

Also give suggestion to improve.. 也给改善的建议。

To make a View draggable I use the below code.. 为了使视图可拖动,我使用以下代码。

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                dX = v.getX() - event.getRawX();
                dY = v.getY() - event.getRawY();
                break;
            case MotionEvent.ACTION_POINTER_UP:
                break;
            case MotionEvent.ACTION_MOVE:
                v.animate()
                        .x(event.getRawX() + dX)
                        .y(event.getRawY() + dY)
                        .setDuration(0)
                        .start();
                break;
        }
        invalidate();//reDraw 
        return true;
    }

The above code working fine for View. 上面的代码对于View来说工作正常。 How could I use it for animating(Dragging) Circle? 如何将其用于动画(拖动)圆?

And in order to detect any position inside circle ... 为了检测圆内的任何位置...

Math.sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)) < r

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. Android Developer网站和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: 活动仅包含setContentView(R.layout.main),其中main.xml如下:

  <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" />

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM