22 October 2015

Make Custom Circular progress bar with percentage in android

What we will do?
-> We will make a circle progress bar with increasing percentage in between the circle.

What we need?
  • attrs.xml
  • MainActivity.java
  • MeterView.java
  • activity_main.java
Step 1 : Create a new Project. Go to File->New->Android Application Project
file new android project

Step 2 : Give a name to your application as CircleProgress and package name as com.mia.circleprogress. Click Next continuously
circleprogress com.mia.circleprogress
Click Finish
finish

Step 3 :  Create a new xml file in res->values and name it as attrs. Right click on values folder->New->Android XML File and name it as attrs.

attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MeterViewAttr">
        <attr name="angle" format="integer" />
        <attr name="spacing" format="integer" />
        <attr name="lineWidth" format="integer" />
        <attr name="lineColor" format="color" />
        <attr name="pathColor" format="color" />
    </declare-styleable>
</resources>

Step 4 : Make a new class in src->com.mia.circleprogress. Right click on com.mia.circleprogress->New->Class and name it as MeterView.

MeterView.java
package com.mia.circleprogress;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

interface OnFinishListener {
 public void onFinish();
}

public class MeterView extends View implements Runnable {
 
 private int angle = 0, spacing = 0, lineWidth = 0;
 private int lineColor = 0, pathColor = 0;
 private boolean runasync = true, count = false, done = false;
 private int start = 0, stop;
 private int interval = 1000, value = 0, total = 1;
 private Thread thread = new Thread(this);
 private Handler handler = new Handler();
 private OnFinishListener f = new OnFinishListener() {
  public void onFinish() { }
 };
 
 public MeterView(Context context) {
  super(context);
 }
 
 public MeterView(Context context, AttributeSet attributes) {

//store values from attrs.xml in TypedArray a
  super(context, attributes);
  TypedArray a = context.obtainStyledAttributes(attributes, R.styleable.MeterViewAttr);
  this.angle = a.getInteger(R.styleable.MeterViewAttr_angle, 0);
  this.spacing = a.getInteger(R.styleable.MeterViewAttr_spacing, 0);
  this.lineWidth = a.getInteger(R.styleable.MeterViewAttr_lineWidth, 0);
  this.lineColor = a.getInteger(R.styleable.MeterViewAttr_lineColor, 0);
  this.pathColor = a.getInteger(R.styleable.MeterViewAttr_pathColor, 0);
  a.recycle();
 }
 
 public void runAsync(boolean runasync) {
  this.runasync = runasync;
 }
 
 public void setValue(int value, int total) {
  this.angle = (value * 360) / total;                        //angle at any instance
  this.invalidate();
 }
 
 public int getValue() {  return this.angle; }
 
 public void onDraw(Canvas canvas) {
  int width = this.getWidth();                                               //width of screen
  int height = this.getHeight();                                             //height of screen
  int minLength = (width < height) ? width : height;
  int textSize = minLength / 4;                                             //size of number percentage
  
  int left = (width > minLength) ?  spacing + (width - minLength) / 2 : spacing;
  int top = (height > minLength) ? spacing + (height - minLength) / 2 : spacing;
  int right = (width > minLength) ? width - left : minLength - left;
  int bottom = (height > minLength) ? height - top : minLength - top;
  
  Paint p = new Paint();
  p.setTextSize(textSize);
  p.setStrokeWidth(lineWidth);
  p.setAntiAlias(true);
  p.setStyle(Paint.Style.STROKE);
  p.setTextAlign(Paint.Align.CENTER);
  
  RectF area = new RectF(left, top, right, bottom);           //rectangle whose all sides are tangent to the circle
  
  // Draw path
  p.setColor(pathColor);
  canvas.drawArc(area, -90, 360, false, p);
  
  // Draw actual value
  p.setColor(lineColor);
  canvas.drawArc(area, -90, angle, false, p);
  
  int offsetX = getWidth() / 2;
  int offsetY = (getHeight() - ((int) p.ascent() + (int) p.descent())) / 2;
  int num = (angle * 100) / 360;
  String value = (num == 0 || num == 100) ? String.valueOf("") : String.valueOf(num);
  p.setStyle(Paint.Style.FILL);
  if(minLength >= textSize || !value.matches("0") || !value.matches("100"))
   canvas.drawText(value, offsetX, offsetY, p);
  
  if(num >= 100 && !this.runasync) {
   this.setValue(0, 1);
   this.f.onFinish();
  }
 }
 
 public int hex2rgb(String hex) {
  int r = (hex.length() < 6) ? Integer.valueOf(hex.substring(1, 2) + hex.substring(1, 2), 16) : Integer.valueOf(hex.substring(1, 3), 16);
  int g = (hex.length() < 6) ? Integer.valueOf(hex.substring(2, 3) + hex.substring(2, 3), 16) : Integer.valueOf(hex.substring(3, 5), 16);
  int b = (hex.length() < 6) ? Integer.valueOf(hex.substring(3, 4) + hex.substring(3, 4), 16) : Integer.valueOf(hex.substring(5, 7), 16);
  return Color.rgb(r, g, b);
 }
 
 public void run() {                //run thread to count value at each instance
  while (this.runasync) {
   try {
    if (this.count) this.value += 1;
    
    if (this.value >= this.total) {
     this.count = false;
     this.done = true;
    }
    
    if (this.done) {
     this.handler.post(new Runnable() {
      public void run() {
       value = start;
       total = stop;
       f.onFinish();
      }
     });
     this.done = false;
    }
    
    Thread.sleep(this.interval);
   } catch (InterruptedException e) {
    Log.i("HIR", e.getMessage());
    e.printStackTrace();
   } catch (Exception e) {
    Log.i("HIR", "Error!");
   }

   this.handler.post(new Runnable() {
    public void run() {
     setValue(value, total);
    }
   });
  } }
 
 public void prepare(int stop, int interval) {
  this.stop = stop;
  this.value = this.start;
  this.total = this.stop;
  this.interval  = interval;
  if (this.thread.isAlive()) {
   this.thread = new Thread(this);
   this.done = false;
  }
  this.thread.start(); 
 }
 
 public void start() { this.count = true; }
 public void pause() { this.count = false; }
 public void stop() { this.count = false; this.done = false; setValue(0, 1); }
 public void setOnFinishListener(OnFinishListener f) { this.f = f; }
}

Step 5 : Go to activity_main.xml found in res->layout and copy the below code in it.

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:meterview="http://schemas.android.com/apk/res/com.mia.circleprogress"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >
 
    <LinearLayout
     android:layout_width="match_parent"
     android:layout_height="wrap_content" >
        <Button
         android:id="@+id/run"
         android:text="@string/run"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:layout_margin="5dp" />

    </LinearLayout>
    
    <com.mia.circleprogress.MeterView
     android:id="@+id/meter"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="5"
        meterview:spacing="10"
        meterview:lineWidth="5"
        meterview:lineColor="#0af"
        meterview:pathColor="#ccc" />
 
</LinearLayout>

Step 6 : Now open MainActivity.java from src->com.mia.circleprogress. Copy the below code into it.

MainActivity.java
package com.mia.circleprogress;

import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {
 
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.progress_layout);
  
  final MeterView meter = (MeterView) this.findViewById(R.id.meter);
  meter.runAsync(false);
  meter.setOnFinishListener(new OnFinishListener() {
   public void onFinish() {
    Toast.makeText(getApplicationContext(), "Done", Toast.LENGTH_LONG).show();
   }
  });
  
  final Button run = (Button) this.findViewById(R.id.run);  
  run.setOnClickListener(new OnClickListener() {
   public void onClick(View v) {
    
    run.setEnabled(false);
    
    new Thread(new Runnable() {
     public void run() {
      final int stop = 100;
      for (int i = 1; i <= stop; i++) {
       try {
        Thread.sleep(100);
       } catch (InterruptedException e) { }
       
       final int n = i;
       runOnUiThread(new Runnable() {
        public void run() {
         meter.setValue(n, stop);
        }
       });
      }
      runOnUiThread(new Runnable() {
       public void run() {
        run.setEnabled(true);
       }
      });      
     }
    }).start(); 
   }
  }); 
 }}

Step 7 : Run the application.

Explanation of the code:
Explanation from activity_main.xml :
  • First we define a LinearLayout under which we have another Linear Layout and a meterview widget which we made it ourselves.
  • In the inner LinearLayout we have a button with id=run. When we click the button the progress will start.
  • In the widget of meterview having id=meter we also have some attributes of type meterview which we have defined in attrs.xml file in res->values
Explanation from MeterView.java :
  • This class creates the widget MeterView which we have used in activity_main.xml.
  •  Explanation commented in the code above
Explanation from MainActivity,java :
  • The onFinish() method of the MeterView we will make a Toast displaying done.
  • In onClick() method of the Button run we will start a Thread which will run from 0 to 100.
Github download
Stay Tuned with Made In Android

Previous Page Next Page Home
Top