Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom View drawArc,detect user touch on draw path of arc

I am creating a custom view which is a kind of arc slider progress view.I can draw more or less of the arc based on where the user touches(on the x axis) by calculating the sweep, i do this by first calculating the percetage where the user touched along the x axis..0% would be all the way to the left and 100% would be all the way to the right.

I want to take this a step further, instead off drawing the arc based on the x coordinate that the user presses, I want to make it move only when the user touches on the actual arc draw path, so its more realistic. I am still new to custom views and my maths is limited but if I get some tips I would be grateful thanks

how it looks when user moves there finger on the area of the rectangle as a percentage along the x axis

class ArcProgress extends View {

    Context cx;
    float width;

    float height;
    float center_x, center_y;
    final RectF oval = new RectF();
    final RectF touchArea = new RectF();

    float sweep = 0;
    float left, right;
    int percent = 0;

    public ArcProgress(Context context) {
        super(context);
        cx = context;

    }

    public int getPercentage() {
        return percent;
    }

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

        setBackgroundColor(0xfff0ebde);

        width = (float) getWidth();
        height = (float) getHeight();

        float radius;

        if (width > height) {
            radius = height / 3;
        } else {
            radius = width / 3;
        }

        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(0xffd2c8b6);
        paint.setStrokeWidth(35);

        paint.setStyle(Paint.Style.STROKE);

        center_x = width / 2;
        center_y = height / 2;

        left = center_x - radius;
        float top = center_y - radius;
        right = center_x + radius;
        float bottom = center_y + radius;

        oval.set(left, top, right, bottom);

            //this is the background arc, it remains constant
        canvas.drawArc(oval, 180, 180, false, paint);

        paint.setStrokeWidth(10);
        paint.setColor(0xffe0524d);
            //this is the red arc whichhas its sweep argument manipulated by on touch
        canvas.drawArc(oval, 180, sweep, false, paint);

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if (event.getAction() == MotionEvent.ACTION_MOVE) {

            float xPosition = event.getX();
            float yPosition = event.getY();
            if (oval.contains(xPosition, yPosition)) {

                float x = xPosition - left;
                float s = x * 100;
                float b = s / oval.width();
                percent = Math.round(b);
                sweep = (180 / 100.0f) * (float) percent;

                invalidate();

            } else {
                if (xPosition < left) {
                    percent = 0;

                    sweep = (180 / 100.0f) * (float) percent;
                    invalidate();
                }
                if (xPosition > right) {
                    percent = 100;

                    sweep = (180 / 100.0f) * (float) percent;
                    invalidate();
                }
            }
        }

        return true;
    }
}
like image 520
brux Avatar asked Jun 23 '13 19:06

brux


2 Answers

I want to make it move only when the user touches on the actual arc draw path

At the beginning of onTouchEvent() you need to check whether xPosition and yPosition are fulfilling some condition. If yes, you do the stuff, which you are doing now. If no, return true.

Condition:

We want to check whether x, y are in that grey arc background:

enter image description here

Let's calculate a distance from (x, y) to that point (a, b) in the center:

final dist = distance(x, y, a, b)

distance() is a simple Euclidean distance between points (x,y) and (a,b):

double distance(int x, int y, int a, int b)
{
    return Math.sqrt((x - a) * (x - a) + (y - b) * (y - b));
}

x, y are in that grey arc background, if y > Y && dist >= r && dist <= R.

like image 134
Adam Stelmaszczyk Avatar answered Nov 05 '22 08:11

Adam Stelmaszczyk


Does this work for you? You don't need a lot of Maths. You can calculate the distance of the touch point from the center of your arc (it's a circle so it's easy) and the compare that with the radius you are using. That will tell you if the point is on the arc (almost, see below for full case).

Point touchEv = ...; 
Point circleCenter = ...; 

//the radius of the circle you used to draw the arc
float circleRadius = ...; 
//how far from the arc should a touch point treated as it's on the arc
float maxDiff = getResources().getDimension(R.dimen.max_diff_dp);

//calculate the distance of the touch point from the center of your circle
float dist = Math.pow(touchEv.x-circleCenter.x,2) + Math.pow(touchEv.y-  circleCenter.y,2)
dist = Math.sqrt(dist); 

//We also need the bounding rect of the top half of the circle (the visible arc)
Rect topBoundingRect = new Rect(circleCenter.x - circleRadius,
            circleCenter.y - circleRadius, 
            circleCenter.x + circleRadius,
            circleCenter.y);


if (Math.abs(dist - circleRadius)  <= maxDiff  &&
  topBoundingRect.contains(touchEv.x, touchEv.y)) {
  // the user is touching the arc 

}
like image 44
Plato Avatar answered Nov 05 '22 10:11

Plato