Reputation: 3912
Im trying to create a custom horizontal progressBar that will have certain amount of circle indicators in it, like this one:
Can anyone tell me how can I achieve this effect(adding of the circle indicators)?
Upvotes: 6
Views: 1426
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
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
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.
The above is the output. You can simply adjust colors now.
Upvotes: 0
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
Reputation: 596
You need to create a custom xml layout for your progressbar and do your calculations in Runnable or using AsyncTask
Upvotes: 0