Search code examples
androidandroid-progressbarandroid-shape

Android Ring Shape with divisions


I need to create circular shape with gaps.

I'm creating a Progress Bar which will represent clock. 12 gaps each assigned to hours.

enter image description here

this is exactly the thing I want to achieve. (Outer ring on Death Star)

here is my code so far:

in activity.xml

<ProgressBar
    android:id="@+id/progressBar"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:indeterminate="false"
    android:progressDrawable="@drawable/circular_progress_bar"
    android:background="@drawable/circle_shape"
    style="?android:attr/progressBarStyleHorizontal"
    android:max="500"
    android:progress="65"
    android:layout_marginBottom="165dp"
    android:layout_above="@+id/button5"
    android:layout_centerHorizontal="true" />

in circular_progress_bar

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="270"
    android:toDegrees="270">
    <shape
        android:innerRadiusRatio="2.5"
        android:shape="ring"
        android:thickness="4dp"
        android:useLevel="true"><!-- this line fixes the issue for lollipop api 21 -->

        <gradient
            android:angle="0"
            android:endColor="#007DD6"
            android:startColor="#007DD6"
            android:type="sweep"
            android:useLevel="false" />
    </shape>
</rotate>

in circle_shape.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="ring"
    android:innerRadiusRatio="2.5"
    android:thickness="4dp"
    android:useLevel="false">

    <solid android:color="#CCC" />
    <stroke
        android:dashGap="10px"
        android:dashWidth="10px"
        android:width="1dp"
        android:color="#ababb2" />
</shape>

How do I separate my shape so it will look like this? Am I on correct path?


Solution

  • I had to create a custom progress bar in the past. I'm not sure about your solution so I won't comment on it. This is how I approached the issue:

    I created a custom class overriding LinearLayout (I needed to add some more views to but you can override any other view)

    Then I override onDraw and just draw an Arch over the canvas:

    ArchProgressBar.java

    public class ArchProgressBar extends LinearLayout {
    
    private static final float START_ANGLE = 130;
    private static final float ARCH_LENGTH = 50;
    
    public ArchProgressBar(Context context) {
        super(context);
        init(context);
    }
    
    public ArchProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }
    
    private void init(Context context) {
        this.setWillNotDraw(false);
    
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.arch_progress_bar, this, true);
    
        this.postInvalidate();
    }
    
    @Override
    public void onDraw(Canvas canvas) {
    
        float middleWidth = canvas.getWidth() / 2;
        float middleHeight = canvas.getHeight() / 2;
        float left = middleWidth - 105 * getResources().getDisplayMetrics().density;
        float top = middleHeight - 90 * getResources().getDisplayMetrics().density;
        float right = middleWidth + 105 * getResources().getDisplayMetrics().density;
        float bottom = middleHeight + 120 * getResources().getDisplayMetrics().density;
    
        Paint mPaintBackground = new Paint();
        mPaintBackground.setAntiAlias(true);
        mPaintBackground.setStyle(Paint.Style.STROKE);
        mPaintBackground.setStrokeWidth(35);
        mPaintBackground.setColor(Color.BLACK);
    
        RectF mRectF = new RectF(left, top, right, bottom);
    
        // draw background line
        canvas.drawArc(mRectF, START_ANGLE, ARCH_LENGTH, false, mPaintBackground);
    
        canvas.drawArc(mRectF, START_ANGLE + ARCH_LENGTH + 10, ARCH_LENGTH, false, mPaintBackground);
    
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = View.MeasureSpec.getSize(widthMeasureSpec);
        setMeasuredDimension(width, Math.max(800, heightMeasureSpec));
    }
    
    }
    

    arch_progress_bar.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
    </LinearLayout>
    

    then you can just add it to any xml you want like this:

    <com.training.archprogress.ArchProgressBar
        android:id="@+id/result_result"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true" />
    

    You can just draw 12 different segments and make the arch slightly smaller then the the size of a segment. Then just draw the number of segments you require with while the progress update.

    I put a project with this solution for you in github: https://github.com/nevoroee/ArchProgress