Android round edges on ring shaped progressbar

Just create class called MyProgress in your package .. and paste the following code..

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;

public class MyProgress extends View {

    private Paint mPrimaryPaint;
    private Paint mSecondaryPaint;
    private RectF mRectF;
    private TextPaint mTextPaint;
    private Paint mBackgroundPaint;

    private boolean mDrawText = false;

    private int mSecondaryProgressColor;
    private int mPrimaryProgressColor;
    private int mBackgroundColor;

    private int mStrokeWidth;

    private int mProgress;
    private int mSecodaryProgress;

    private int mTextColor;

    private int mPrimaryCapSize;
    private int mSecondaryCapSize;
    private boolean mIsPrimaryCapVisible;
    private boolean mIsSecondaryCapVisible;

    private int x;
    private int y;
    private int mWidth = 0, mHeight = 0;


    public MyProgress(Context context) {
        super(context);
        init(context, null);
    }

    public MyProgress(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public MyProgress(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    void init(Context context, AttributeSet attrs) {
        TypedArray a;
        if (attrs != null) {
            a = context.getTheme().obtainStyledAttributes(
                    attrs,
                    R.styleable.MyProgress,
                    0, 0);
        } else {
            throw new IllegalArgumentException("Must have to pass the attributes");
        }

        try {
            mDrawText = a.getBoolean(R.styleable.MyProgress_showProgressText, false);

            mBackgroundColor = a.getColor(R.styleable.MyProgress_backgroundColor, android.R.color.darker_gray);
            mPrimaryProgressColor = a.getColor(R.styleable.MyProgress_progressColor, android.R.color.darker_gray);
            mSecondaryProgressColor = a.getColor(R.styleable.MyProgress_secondaryProgressColor, android.R.color.black);

            mProgress = a.getInt(R.styleable.MyProgress_progress, 0);
            mSecodaryProgress = a.getInt(R.styleable.MyProgress_secondaryProgress, 0);

            mStrokeWidth = a.getDimensionPixelSize(R.styleable.MyProgress_strokeWidth, 20);
            mTextColor = a.getColor(R.styleable.MyProgress_textColor, android.R.color.black);

            mPrimaryCapSize = a.getInt(R.styleable.MyProgress_primaryCapSize, 20);
            mSecondaryCapSize = a.getInt(R.styleable.MyProgress_secodaryCapSize, 20);

            mIsPrimaryCapVisible = a.getBoolean(R.styleable.MyProgress_primaryCapVisibility, true);
            mIsSecondaryCapVisible = a.getBoolean(R.styleable.MyProgress_secodaryCapVisibility, true);
        } finally {
            a.recycle();
        }

        mBackgroundPaint = new Paint();
        mBackgroundPaint.setAntiAlias(true);
        mBackgroundPaint.setStyle(Paint.Style.STROKE);
        mBackgroundPaint.setStrokeWidth(mStrokeWidth);
        mBackgroundPaint.setColor(mBackgroundColor);

        mPrimaryPaint = new Paint();
        mPrimaryPaint.setAntiAlias(true);
        mPrimaryPaint.setStyle(Paint.Style.STROKE);
        mPrimaryPaint.setStrokeWidth(mStrokeWidth);
        mPrimaryPaint.setColor(mPrimaryProgressColor);

        mSecondaryPaint = new Paint();
        mSecondaryPaint.setAntiAlias(true);
        mSecondaryPaint.setStyle(Paint.Style.STROKE);
        mSecondaryPaint.setStrokeWidth(mStrokeWidth - 2);
        mSecondaryPaint.setColor(mSecondaryProgressColor);

        mTextPaint = new TextPaint();
        mTextPaint.setColor(mTextColor);

        mRectF = new RectF();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mRectF.set(getPaddingLeft(), getPaddingTop(), w - getPaddingRight(), h - getPaddingBottom());
        mTextPaint.setTextSize(w / 5);
        x = (w / 2) - ((int) (mTextPaint.measureText(mProgress + "%") / 2));
        y = (int) ((h / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));
        mWidth = w;
        mHeight = h;
        invalidate();
    }

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

        mPrimaryPaint.setStyle(Paint.Style.STROKE);
        mSecondaryPaint.setStyle(Paint.Style.STROKE);

        // for drawing a full progress .. The background circle
        canvas.drawArc(mRectF, 0, 360, false, mBackgroundPaint);

        // for drawing a secondary progress circle
        int secondarySwipeangle = (mSecodaryProgress * 360) / 100;
        canvas.drawArc(mRectF, 270, secondarySwipeangle, false, mSecondaryPaint);

        // for drawing a main progress circle
        int primarySwipeangle = (mProgress * 360) / 100;
        canvas.drawArc(mRectF, 270, primarySwipeangle, false, mPrimaryPaint);

        // for cap of secondary progress
        int r = (getHeight() - getPaddingLeft() * 2) / 2;      // Calculated from canvas width
        double trad = (secondarySwipeangle - 90) * (Math.PI / 180d); // = 5.1051
        int x = (int) (r * Math.cos(trad));
        int y = (int) (r * Math.sin(trad));
        mSecondaryPaint.setStyle(Paint.Style.FILL);
        if (mIsSecondaryCapVisible)
            canvas.drawCircle(x + (mWidth / 2), y + (mHeight / 2), mSecondaryCapSize, mSecondaryPaint);

        // for cap of primary progress
        trad = (primarySwipeangle - 90) * (Math.PI / 180d); // = 5.1051
        x = (int) (r * Math.cos(trad));
        y = (int) (r * Math.sin(trad));
        mPrimaryPaint.setStyle(Paint.Style.FILL);
        if (mIsPrimaryCapVisible)
            canvas.drawCircle(x + (mWidth / 2), y + (mHeight / 2), mPrimaryCapSize, mPrimaryPaint);


        if (mDrawText)
            canvas.drawText(mProgress + "%", x, y, mTextPaint);
    }

    public void setDrawText(boolean mDrawText) {
        this.mDrawText = mDrawText;
        invalidate();
    }

    public void setBackgroundColor(int mBackgroundColor) {
        this.mBackgroundColor = mBackgroundColor;
        invalidate();
    }

    public void setSecondaryProgressColor(int mSecondaryProgressColor) {
        this.mSecondaryProgressColor = mSecondaryProgressColor;
        invalidate();
    }

    public void setPrimaryProgressColor(int mPrimaryProgressColor) {
        this.mPrimaryProgressColor = mPrimaryProgressColor;
        invalidate();
    }

    public void setStrokeWidth(int mStrokeWidth) {
        this.mStrokeWidth = mStrokeWidth;
        invalidate();
    }

    public void setProgress(int mProgress) {
        this.mProgress = mProgress;
        invalidate();
    }

    public void setSecondaryProgress(int mSecondaryProgress) {
        this.mSecodaryProgress = mSecondaryProgress;
        invalidate();
    }

    public void setTextColor(int mTextColor) {
        this.mTextColor = mTextColor;
        invalidate();
    }

    public void setPrimaryCapSize(int mPrimaryCapSize) {
        this.mPrimaryCapSize = mPrimaryCapSize;
        invalidate();
    }

    public void setSecondaryCapSize(int mSecondaryCapSize) {
        this.mSecondaryCapSize = mSecondaryCapSize;
        invalidate();
    }

    public boolean isPrimaryCapVisible() {
        return mIsPrimaryCapVisible;
    }

    public void setIsPrimaryCapVisible(boolean mIsPrimaryCapVisible) {
        this.mIsPrimaryCapVisible = mIsPrimaryCapVisible;
    }

    public boolean isSecondaryCapVisible() {
        return mIsSecondaryCapVisible;
    }

    public void setIsSecondaryCapVisible(boolean mIsSecondaryCapVisible) {
        this.mIsSecondaryCapVisible = mIsSecondaryCapVisible;
    }


    public int getSecondaryProgressColor() {
        return mSecondaryProgressColor;
    }

    public int getPrimaryProgressColor() {
        return mPrimaryProgressColor;
    }

    public int getProgress() {
        return mProgress;
    }

    public int getBackgroundColor() {
        return mBackgroundColor;
    }

    public int getSecodaryProgress() {
        return mSecodaryProgress;
    }

    public int getPrimaryCapSize() {
        return mPrimaryCapSize;
    }

    public int getSecondaryCapSize() {
        return mSecondaryCapSize;
    }
}

and add the following line in res->values->attr.xml under a tag and build it

<declare-styleable name="MyProgress">
    <attr name="showProgressText" format="boolean" />
    <attr name="progress" format="integer" />
    <attr name="secondaryProgress" format="integer" />
    <attr name="progressColor" format="color" />
    <attr name="secondaryProgressColor" format="color" />
    <attr name="backgroundColor" format="color" />
    <attr name="primaryCapSize" format="integer" />
    <attr name="secodaryCapSize" format="integer" />
    <attr name="primaryCapVisibility" format="boolean" />
    <attr name="secodaryCapVisibility" format="boolean" />
    <attr name="strokeWidth" format="dimension" />
    <attr name="textColor" format="color" />
</declare-styleable>

that's it .... and to use in your layout ..

<Your_Package_Name.MyProgress
    android:padding="20dp"
    android:id="@+id/timer1"
    app:strokeWidth="10dp"
    app:progress="30"
    app:secondaryProgress="50"
    app:backgroundColor="@android:color/black"
    app:progressColor="@android:color/holo_blue_bright"
    app:secondaryProgressColor="@android:color/holo_blue_dark"
    app:primaryCapSize="30"
    app:secodaryCapSize="40"
    app:primaryCapVisibility="true"
    app:secodaryCapVisibility="true"
    android:layout_width="200dp"
    android:layout_height="200dp" />

You can also change all property progrmatically using setMethods()... enter image description here

feel free to ask anything .. best of luck

[Update 23-01-2016]

finally I uploaded code on github. You can refer it from here https://github.com/msquare097/MProgressBar

Now You can use this ProgressBar by simply writing following line in your app build.gradle file. You don't have to copy above code.

compile 'com.msquare.widget.mprogressbar:mprogressbar:1.0.0'

I was able to achieve this using a layer list, and adding a dot to each side of the line. The first one will be stuck at the top, while the second one will follow the progress as an inset is added inside a rotate element. You will have to adjust it according to the size of your progressbar layout though. Mine is 250dp x 250dp.

progress_drawable.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <rotate android:fromDegrees="270" android:toDegrees="270">
        <shape
            android:innerRadiusRatio="2.55"
            android:shape="ring"
            android:thickness="15dp"
            android:useLevel="true">
            <solid android:color="@color/main_color" />
        </shape>
        </rotate>
    </item>
    <item android:bottom="211dp">
        <shape
            android:innerRadiusRatio="1000"
            android:shape="ring"
            android:thickness="7dp"
            android:useLevel="false">
            <solid android:color="@color/main_color" />
        </shape>
    </item>
    <item>
        <rotate>
            <inset android:insetBottom="211dp">
                <shape
                    android:innerRadiusRatio="1000"
                    android:shape="ring"
                    android:thickness="7dp"
                    android:useLevel="false">
                    <solid android:color="@color/main_color" />
                </shape>
            </inset>
        </rotate>
    </item>
</layer-list>

I don't have a secondary process here, but you should be able to adjust it for that as well.

enter image description hereenter image description here