Android Circle Menu Like Catch Notes

2019-03-08 16:41发布

问题:


I try to do circle menu like in this app.

In "expanded" mode i draw this component like follows:

<RelativeLayout android:id="@+id/bigCircle">
<!--color full borders-->
    <my.custom.component android:id="@+id/middleCircle">
    <!--circle for buttons-->
         <RelativeLayout android:id="@+id/smallCircle">
           <!--minus button-->
         </RelativeLayout>
    </my.custom.component>
</RelativeLayout>

In onDraw method of my.custom.component i divide circle on 8 parts by using android.graphics.Path with android.graphics.Paint and some math.
Visually i have exactly as shown in the screenshot. But when i press on part of circle, i need redraw this part in another color to show user what something going on.

How i can redraw part of component's canvas cutting off from another part of canvas by android.graphics.Path for example.
In another word i know what redraw canvas i should do in onDraw method, i know that i can show some bitmap from drawables painted in photoshop and have some "multiscreen trouble", i know how i can determine part which user pressed. But i don't know how i can select part of canvas and redraw it.

回答1:

Developer of Catch here. If I'm understanding your issue, you're having trouble understanding how to specifically draw the highlight/selection indicator on a section of your circular menu.

While there are plenty of different ways one could implement it, what you're leaning towards (using android.graphics.Path) is how we did it. In the view hierarchy of our capture button, there's an element that serves as the canvas on which the selection highlight color (if there is an active selection) is drawn.

If you had a similar custom View in your layout, you could duplicate this behavior like so. First, you'll need the Path that defines the selection for a particular circle segment. Using Path.addArc(RectF, float, float) we can get the pizza-slice-shaped path we need:

private Path getPathForSegment(float startAngle, float sweep) {
    Point center = new Point(getWidth() / 2, getHeight() / 2);
    RectF rect = new RectF(0f, 0f, getWidth(), getHeight());
    Path selection = new Path();
    selection.addArc(rect, startAngle, sweep);
    selection.lineTo(center.x, center.y);
    selection.close();
    return selection;
}

The getWidth() and getHeight() above are for the enclosing custom view object, so they define the bounding box that contains the circle on which the selection is drawn.

Then, in your custom view's onDraw(Canvas), if your code has determined a selection should be drawn for a segment:

@Override
protected void onDraw(Canvas canvas) {
    // Assume one has the rest of these simple helper functions defined
    if (shouldDrawSelection()) {
        float startAngle = getStartAngleOfSelectedSegment();
        float sweep = getSweepAngle();
        Paint paint = getPaintStyleForSelectedSegment();
        Path path = getPathForSegment(startAngle, sweep);
        canvas.drawPath(path, paint);
    }

    // ...

    super.onDraw(canvas);
}

In the other areas of your code that are tracking touches, just call invalidate() on the custom view so that it will redraw (or not) the selection path based on changes in input or state.

Remember that it's good practice to avoid newing objects in onDraw(), so most of these building blocks (Paths, Paints, etc.) can be constructed ahead of time (or once, on first occurrence) and reused.

Hope this is close to what you were asking!