Darko Petkovski
Darko Petkovski

Reputation: 3912

Horizontal progressbar add circle indicators

Im trying to create a custom horizontal progressBar that will have certain amount of circle indicators in it, like this one: enter image description here

Can anyone tell me how can I achieve this effect(adding of the circle indicators)?

Upvotes: 6

Views: 1426

Answers (4)

Nick Cardoso
Nick Cardoso

Reputation: 21773

Presuming your circle indicators are all at a set interval (the same distance apart) the following seems to me to be the most logical solution

  1. Create a custom view
  2. Extend ProgressBar so that you inherit all of the current functionality and don't have to make your own setProgress methods (etc.)
  3. Create a custom property 'tickInterval' (which I would suggest as being % points of value rather than in dp of view, but you could use either)
  4. Create an XML file for the custom attr if you want to use your view from xml
  5. When the view is drawn, paint your indicator at every interval

You might need to adjust padding in your view to make sure there is room for your circles. Below is a brief idea of what you need:

public class TickedProgressBarView extends ProgressBar {

    private static final float DEFAULT_INTERVAL = 25f;
    private float INDICATOR_RADIUS;
    private Paint mTickPaint;
    private float mInterval; //%

    public TickedProgressBarView(Context context) {
        super(context);
        initPainters(context, null); //because draw is called a lot of times, don't want to do loads of allocations in onDraw
    }

    public TickedProgressBarView(Context context, AttributeSet attrs) {
        super(context, attrs);
         initPainters(context, attrs);
    }

    public TickedProgressBarView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initPainters(context, attrs);
    }

    private void initPainters(Context context, @Nullable AttributeSet attrs) {
        if (attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TickedProgressBarView, 0, 0);
            mInterval = a.getFloat(R.styleable.TickedProgressBarView_tickInterval, DEFAULT_INTERVAL);
        } else {
            mInterval = DEFAULT_INTERVAL;
        }
        //5 on the line below is HALF how many Dp wide you want the circles - ie a 10 Dp circle results from this
        INDICATOR_RADIUS = 5 * getResources().getDisplayMetrics().density + 0.5f;
        mTickPaint = new Paint();
        mTickPaint.setColor(ContextCompat.getColor(getContext(), R.color.my_color));
        mTickPaint.setStyle(Paint.Style.FILL);
        mTickPaint.setStrokeCap(Paint.Cap.ROUND);
    }

    public void setTickInterval(float intervalPercentage) {
        mInterval = intervalPercentage;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mInterval > 0f) {
            final float midHeight = canvas.getHeight() / 2f;
            final int end = canvas.getWidth();
            final int intervalPx = (int) ((end / 100f) * mInterval);
            int nextInterval = intervalPx;
            while (nextInterval <= end) {
                canvas.drawCircle(nextInterval, midHeight, INDICATOR_RADIUS, mTickPaint);
                nextInterval += intervalPx;
            }
        }
    }

}

attrs.xml

<declare-styleable name="TickedProgressBarView">
    <attr name="tickInterval" format="float" />
</declare-styleable>

The attr declaration allows you to use your view from xml

<!-- draw a circle every 10% along the bar -->
<com.my.packge.TickedProgressBarView
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:tickInterval="10.0"
/>

Upvotes: 2

Vaibhav Sharma
Vaibhav Sharma

Reputation: 2319

Try this-

Your xml with the progressbar-

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  android:id="@+id/progress_frame" xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:paddingEnd="16dp"
  android:layout_marginTop="10dp"
  android:paddingLeft="16dp"
  android:paddingRight="16dp"
  android:paddingStart="16dp">
  <ProgressBar
    android:id="@+id/flight_progress"
    style="@style/flght.progress.style"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_marginBottom="10dp"
    android:layout_marginTop="10dp"
    android:max="100"
    android:visibility="visible"/>
  <LinearLayout android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:orientation="horizontal">
    <LinearLayout android:layout_width="0dp"
                  android:layout_weight="1"
                  android:layout_height="wrap_content">
      <ImageView android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="right|end"
                 android:src="@drawable/blue_circle"/>
    </LinearLayout>
    <LinearLayout android:layout_width="0dp"
                  android:layout_weight="1"
                  android:layout_height="wrap_content">
      <ImageView android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="right|end"
                 android:src="@drawable/blue_circle"/>
    </LinearLayout>

    <LinearLayout android:layout_width="0dp"
                  android:layout_weight="1"
                  android:layout_height="wrap_content">
      <ImageView android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="right|end"
                 android:src="@drawable/blue_circle"/>
    </LinearLayout>
    <LinearLayout android:layout_width="0dp"
                  android:layout_weight="1"
                  android:layout_height="wrap_content">
      <ImageView android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="right|end"
                 android:src="@drawable/blue_circle"/>
    </LinearLayout>

    <LinearLayout android:layout_width="0dp"
                  android:layout_weight="1"
                  android:layout_height="wrap_content">
      <ImageView android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="right|end"
                 android:src="@drawable/blue_circle"/>
    </LinearLayout>
    <ImageView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:layout_gravity="right|end"
               android:src="@drawable/blue_circle"/>

  </LinearLayout>
</RelativeLayout>

Add this to styles.xml-

<style name="flght.progress.style" parent="android:Widget.ProgressBar.Horizontal">
    <item name="android:indeterminateOnly">false</item>
    <item name="android:progressDrawable">@drawable/flight_cylinder_progress_bar</item>
    <item name="android:minHeight">8dip</item>
    <item name="android:maxHeight">24dip</item>
    <item name="android:minWidth">20dip</item>
    <item name="android:maxWidth">80dip</item>
  </style>

Next add this flight_cylinder_progress_bar in the drawable folder-

<?xml version="1.0" encoding="UTF-8"?>
<layer-list
  xmlns:android="http://schemas.android.com/apk/res/android">
  <item
    android:id="@android:id/background">
    <shape>
      <corners
        android:radius="5dip"/>
      <gradient
        android:angle="270"
        android:centerColor="#ffdddddd"
        android:centerY="0.50"
        android:endColor="#ffdddddd"
        android:startColor="#ffdddddd"/>
    </shape>
  </item>
  <item
    android:id="@android:id/secondaryProgress">
    <clip>
      <shape>
        <corners
          android:radius="5dip"/>
        <gradient
          android:angle="90"
          android:endColor="@color/orange_dark"
          android:startColor="@color/my_orange"/>
      </shape>
    </clip>
  </item>
  <item
    android:id="@android:id/progress">
    <clip>
      <shape>
        <corners
          android:radius="5dip"/>
        <gradient
          android:angle="90"
          android:endColor="@color/my_orange"
          android:startColor="@color/orange_dark"/>
      </shape>
    </clip>
  </item>
</layer-list>

And finally the blue_circle.xml in the drawable folder-

<?xml version="1.0" encoding="utf-8"?>
<shape
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:shape="oval">
  <solid android:color="@color/goibibo_blue"/>
  <size android:width="10dp"
        android:height="10dp"/>
</shape>

Now you can simply use the progressbar to setprogress. Output of the above code

The above is the output. You can simply adjust colors now.

Upvotes: 0

fractalwrench
fractalwrench

Reputation: 4076

It's probably best to achieve this using a custom view. To set the progress you would define a setter like below, which invalidates the view and calls onDraw().

public class CustomProgressBar extends View {

    // constructors etc...

    void setProgress(int progress) {
        if (progress < 0 || progress > 100) {
            throw new IllegalArgumentException("Progress must be between 0-100!");
        }

        this.progress = progress.
        invalidate(); // tell view to redraw itself
    }
}

The next step is to draw the portion of the view which indicates progress, and the portion which indicates remaining work.

final Paint progressPaint = new Paint();
progressPaint.setColor(Color.RED); // could draw a bitmap or whatever, this is a simple example

@Override protected void onDraw(Canvas canvas) {
    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); // clear previous contents
    float progressW = getWidth() * (progress / 100);
    float remainingW = getWidth() - progressPortion;

    canvas.drawRect(0, 0, progressW, getHeight(), progressPaint);
    canvas.drawRect(progressW, 0, remainingW, getHeight(), remainingPaint);

}

Finally, you can calculate the positions of your circle indicators, within onDraw() draw them onto the canvas.

final float radius = 5;
final float circleCount = 6;

float unit = getWidth() / circleCount;
float offset = 0;

for (int i=0; i<6; i++) {
    canvas.drawCircle(offset + (radius / 2), getHeight() / 2, radius, circlePaint);
}

If you need to conditionally display indicators, you can simply add in a check during the onDraw() method to see whether the progress is past a certain threshold.

Upvotes: 0

Michael
Michael

Reputation: 596

You need to create a custom xml layout for your progressbar and do your calculations in Runnable or using AsyncTask

Upvotes: 0

Related Questions