Let's Create a Custom Percentage ProgressBar using LinearLayout

| Page Views: 111

Android Custom Percentage ProgressBar Creation Tutorial based on LinearLayout.

Sometimes you just want to churn out your own solutions instead of using classes that have been supplied. Those supplied classes are always generic, attempting to cover for everybody. Hence many times you need to be able to create a custom widget yourself.

In this tutorial we see how to do a custom percentage progressbar.

Demo

Here's the gif demo of the project:

Video Tutorial

Here's the video tutorial. It contains more detailed step by step explanations.:

Gradle Scripts

(a) Build.gradle

Nothing special. No third party dependency.

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    testImplementation 'junit:junit:4.12'
    implementation 'com.android.support:appcompat-v7:28.0.0'

}

Layouts

Lets start by exploring the layouts in this project.

We have seven layouts:

1. activity_main.xml

This is our main and only layout.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <info.camposha.creatingpercentageprogressbar.PercentageProgressBar
        android:id="@+id/mPercentageProgressBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:hint="Percentage ProgressBar"
        app:showHint="true"/>

</RelativeLayout>

NB/= Remember to Replace info.camposha.creatingpercentageprogressbar with your own package name.

(b). percentage_progressbar.xml

This is the XML that will define for us our custom percentage progressbar. You can redefine it as you want.

<?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="match_parent"
              android:orientation="vertical">

    <RelativeLayout
        android:id="@+id/wrap_hint"
        android:visibility="gone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp">

        <TextView
            android:id="@+id/text_hint"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Percentage"
            android:textColor="@color/colorAccentProgress"
            android:textSize="12sp"/>

        <TextView
            android:id="@+id/text_percent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_alignParentRight="true"
            android:text="0%"
            android:textColor="@color/colorAccentProgress"/>
    </RelativeLayout>

    <EditText
        android:id="@+id/percentageTxt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Percentage"
        android:inputType="number"
        android:visibility="visible"/>

    <LinearLayout
        android:id="@+id/wrap_percent"
        android:layout_width="match_parent"
        android:layout_height="5dp"
        android:layout_margin="5dp"
        android:layout_marginTop="10dp"
        android:orientation="horizontal"
        android:visibility="gone"
        android:weightSum="100">

        <LinearLayout
            android:id="@+id/start_percent"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="0"
            android:background="@color/colorAccentProgress"
            android:orientation="horizontal"/>

        <LinearLayout
            android:id="@+id/end_percent"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="100"
            android:background="@android:color/darker_gray"
            android:orientation="horizontal"/>
    </LinearLayout>

</LinearLayout>

Styles

(a). attrs.xml

Create a value resource file. I named mine attrs.xml. Then add the following code. You can see it will contain the attributes for our PercentageProgressBar.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="PercentageProgressBar">
        <attr name="hint" format="string"/>
        <attr name="showHint" format="boolean"/>
        <attr name="heightPercent" format="dimension"/>
        <attr name="hideInputPercent" format="boolean"/>
        <attr name="percent" format="integer"/>
    </declare-styleable>
</resources>

Java Code.

We have the following java classes for this project:

  1. PercentageProgressBar.java
  2. MainActivity.java
PercentageProgressBar.java

This is how we create our PercentageProgressBar.

package info.camposha.creatingpercentageprogressbar;

import android.content.Context;
import android.content.res.TypedArray;
import android.os.Handler;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import static android.view.Gravity.START;

public class PercentageProgressBar extends LinearLayout {

    private RelativeLayout hintRelativeLayout;
    private TextView hintTV;
    private TextView percentageTV;
    private EditText percentageTxt;

    private LinearLayout mWrapPercent;
    private LinearLayout mStartPercent;
    private LinearLayout mEndPercent;

    private boolean showHint;
    private boolean hide_input_percent;

    /**
     * We have two constructors.
     * @param context
     */
    public PercentageProgressBar(Context context) {
        this(context, null);
    }
    public PercentageProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOrientation(VERTICAL);
        setGravity(START);
        init(context, attrs);
        this.isInEditMode();
    }

    /**
     * We reference widgets used in this custom percentageProgressBar we
     * are creating.
     */
    private void referenceWidgets(){
        percentageTxt = findViewById(R.id.percentageTxt);
        hintTV = findViewById(R.id.text_hint);
        percentageTV = findViewById(R.id.text_percent);
        mWrapPercent = findViewById(R.id.wrap_percent);
        mStartPercent = findViewById(R.id.start_percent);
        mEndPercent = findViewById(R.id.end_percent);
        hintRelativeLayout = findViewById(R.id.wrap_hint);
    }

    /**
     * We can now use our LayoutInflater class to inflate our Percentage
     * ProgressBar from a layout.
     * @param context
     */
    private void inflatePercentageProgressBar(Context context){
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.percentage_progressbar, this, true);
    }

    /**
     * Let's obtain our style from attrs.xml, hold it in a typed array,
     * obtain several attributes like height,percent,hint etc from that
     * typedArray before recyc;ing it.
     * @param context
     * @param attrs
     */
    private void resolveAttributes(Context context,AttributeSet attrs){
        TypedArray typedArray = context.obtainStyledAttributes(attrs,
        R.styleable.PercentageProgressBar, 0, 0);

        String hint = typedArray.getString(R.styleable.PercentageProgressBar_hint);
        showHint = typedArray.getBoolean(R.styleable.PercentageProgressBar_showHint,
         false);
        hide_input_percent = typedArray.getBoolean(
            R.styleable.PercentageProgressBar_hideInputPercent, false);

        float height = typedArray.getDimension(
            R.styleable.PercentageProgressBar_heightPercent, 5f);
        int percent = typedArray.getInteger(R.styleable.PercentageProgressBar_percent, 0);

        typedArray.recycle();
        this.referenceWidgets();

        percentageTxt.setHint(hint);
        hintTV.setText(hint);

        if (height > 5) {setHeightPercent(Math.round(height));}
        if (hide_input_percent) {hideInputPercent();}
        if (percent > 0) {setPercent(percent);}
    }

    /**
     * Let's listen to input events to our edittext, thus updating our
     * percentage progressbar
     */
    private void handlePercetTextChangeEvents(){
        percentageTxt.addTextChangedListener(
                new TextWatcher() {
                    @Override public void afterTextChanged(Editable s) {
                        if (s.length() > 0) {
                            showPercent(percentageTxt.getText().toString());
                        } else {
                            showPercent("0");
                        }
                    }
                    @Override public void beforeTextChanged(CharSequence s, int start,
                    int count, int after) {
                    }
                    @Override public void onTextChanged(CharSequence s, int start,
                    int before, int count) {
                    }
                });
    }

    /**
     * Let's invoke the methods we've created above
     * @param context
     * @param attrs
     */
    private void init(Context context, AttributeSet attrs) {
        this.inflatePercentageProgressBar(context);
        this.resolveAttributes(context,attrs);
        this.handlePercetTextChangeEvents();
    }

    /**
     * Let's create a method to set progressbar height. Height as in vertical
     * height, like thickness.
     * @param height
     */
    public void setHeightPercent(int height) {
        //LayoutParams is a static class that holds information associated
        //with a given viewgroup. In this case our viewgroup is LinearLayout
        LayoutParams height_params = (LayoutParams) mWrapPercent.getLayoutParams();
        height_params.height = height;
        mWrapPercent.setLayoutParams(height_params);
    }

    /**
     * Let;s create a method to receive a percentage as a string, convert it
     * to integer then show it.
     * @param percentageString
     */
    private void showPercent(String percentageString) {
        //let's turn percentage string to integer
        int percentage = Integer.parseInt(percentageString);
        if (percentage == 0) {
            mWrapPercent.setVisibility(GONE);
            hintRelativeLayout.setVisibility(GONE);
        } else {
            setAnimatePercent(percentage, 0, mStartPercent);
            LayoutParams percent_end = (LayoutParams) mEndPercent.getLayoutParams();
            percent_end.weight = 100;
            mEndPercent.setLayoutParams(percent_end);
            mWrapPercent.setVisibility(VISIBLE);
            if (showHint) {
                hintRelativeLayout.setVisibility(VISIBLE);
            }
        }
    }

    /**
     * Let's create a method to set our percentage to our edittext
     * @param percent
     */
    public void setPercent(int percent) {
        showPercent("" + percent);
        percentageTxt.setText("" + percent);
    }

    /**
     * Let's create a method to set animated percentage to a textview
     * @param percent
     * @param position
     * @param view
     */
    private void setAnimatePercent(final int percent, final int position,
    final LinearLayout view) {
        final LayoutParams percent_start = (LayoutParams) mStartPercent.getLayoutParams();
        final int index = position + 1;
        new Handler().postDelayed(new Runnable() {
            @Override public void run() {
                if (position < percent) {
                    percent_start.weight = index;
                    view.setLayoutParams(percent_start);
                    percentageTV.setText("" + index + "%");
                    setAnimatePercent(percent, index, view);
                }
            }
        }, 1);
    }

    /**
     * Method to allow us show hint
     */
    public void showHint() {
        this.showHint = true;
    }

    /**
     * Method to allow us hide percent
     */
    public void hideInputPercent() {
        percentageTxt.setVisibility(GONE);
    }
}
//end
MainActivity.java

Our main activity class. It is here where we use our LoveView we had created.

package info.camposha.creatingpercentageprogressbar;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        PercentageProgressBar percentageProgressBar = findViewById(
            R.id.mPercentageProgressBar);
        percentageProgressBar.setPercent(30);
        percentageProgressBar.setHeightPercent(15);

    }
}
Download

You can download the full source code below or watch the video from the link provided.

No. Location Link
1. GitHub Direct Download
2. GitHub Browse
3. YouTube Video Tutorial
4. YouTube ProgrammingWizards TV Channel

How do You Feel after reading this?

According to scientists, we humans have 8 primary innate emotions: joy, acceptance, fear, surprise, sadness, disgust, anger, and anticipation. Feel free to tell us how you feel about this article using these emotes or via the comment section.

About Me.

After completing his Software Engineering bachelors program, Oclemy(Clement Ochieng) these days is a man of two lives. At day he works for a startup in Nairobi, Kenya. At night he works tirelessly on building ProgrammingWizards TV, a tv channel for student coders and this website to help share the source code. In between he practices Meditation and Self actualization to help him keep balance. He also likes going for long solo walks to connect more with nature.




Recommendations


What do You Think


Previous Post Next Post