I want to create a progress bar for Android. I have four images for my square shaped progress bar.
I am using the android defined progress bar:
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/Widget.ProgressBar.Small"
android:layout_marginRight="5dp" />
But if I want to make a square instead of the circle how can I do it? How do I pass my 4 images to the progress bar?
example:
Android App Development for Beginners This example demonstrates how to create a custom Progress Bar in Android using Kotlin. Step 1 − Create a new project in Android Studio, go to File ⇒ New Project and fill all required details to create a new project. Step 2 − Add the following code to res/layout/activity_main. xml.
Android ProgressBar is a graphical view indicator that shows some progress. Android progress bar displays a bar representing the completing of the task. Progress bar in android is useful since it gives the user an idea of time to finish its task.
app. ProgressDialog class to show the progress bar. Android ProgressDialog is the subclass of AlertDialog class. The ProgressDialog class provides methods to work on progress bar like setProgress(), setMessage(), setProgressStyle(), setMax(), show() etc.
Generally you do have 2 options:
1. As already mentioned, use an animation-list
and just swap pictures.
This one is probably the easier solution, since they can relatively easy be animated with AnimationDrawable. The only drawback would be that you need at least 16 images (in all resolutions) for your given result.
2. Use a custom drawable.
This is the more complicated approach. You will have to do the drawing and animating yourself, which is a hard task for most people with little good documentation.
Therefore you have to extends Drawable implements Runnable, Animatable
and supply some good implementations.
The following is a basic impelmentation, calculating the positions once, then drawing them. The animation (the size of the individual circles) can and should be further tweaked ;)
Result in 3 variants:
public class RectProgressDrawable extends Drawable implements Runnable, Animatable {
private static final long FRAME_DELAY = 1000 / 60;
private static final String TAG = "RectProgressDrawable";
private boolean mRunning = false;
private long mStartTime;
private int mDuration = 1000;
private Paint mPaint;
private float[] posX;
private float[] posY;
private float mSize;
private int mPoints = 5;
/**
* The padding in px.
*/
private int mPadding = 4;
private int mAnimatedPoints = 5;
public void setPoints(int points) {
if (points != mPoints) {
mPoints = points;
init();
}
}
private void init() {
if (mPaint == null) {
mPaint = new Paint();
mPaint.setColor(Color.WHITE);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
}
posX = new float[(mPoints - 1) * 4];
posY = new float[(mPoints - 1) * 4];
Rect bounds = new Rect();
bounds.set(getBounds());
bounds.inset(mPadding, mPadding);
float cellWidth = ((float) bounds.width()) / ((float) mPoints);
float cellHeight = ((float) bounds.height()) / ((float) mPoints);
float min = Math.min(cellWidth, cellHeight);
mSize = min / (mPoints - 1);
for (int i = 0; i < mPoints; i++) { // top row
posX[i] = bounds.left + cellWidth * (float) i + cellWidth / 2;
posY[i] = bounds.top + cellHeight / 2;
}
for (int i = 0; i < mPoints - 2; i++) { // sides
// right side top bottom
posX[mPoints + i] = bounds.left + cellWidth * (mPoints - 1) + cellWidth / 2;
posY[mPoints + i] = bounds.top + cellHeight * (i + 1) + cellHeight / 2;
//left side bottom top
posX[3 * mPoints - 2 + i] = bounds.left + cellWidth / 2;
posY[3 * mPoints - 2 + i] = bounds.top + cellHeight * (mPoints - 2 - i) + cellHeight / 2;
}
for (int i = 0; i < mPoints; i++) { // bottom from right to left
posX[2 * mPoints - 2 + i] = bounds.left + cellWidth * (mPoints - 1 - i) + cellWidth / 2;
posY[2 * mPoints - 2 + i] = bounds.top + cellHeight * (mPoints - 1) + cellHeight / 2;
}
}
@Override
public void draw(Canvas canvas) {
if (isRunning()) {
// animation in progress
final int save = canvas.save();
long timeDiff = SystemClock.uptimeMillis() - mStartTime;
float progress = ((float) timeDiff) / ((float) mDuration); // 0..1
int level = ((int) (progress * posX.length)) % posX.length; // current value 0..posX.length
for (int i = 0; i < posX.length; i++) {
if ((i >= level && i < level + mAnimatedPoints) || level + mAnimatedPoints > posX.length && i < (level + mAnimatedPoints) % posX.length) {
float num = (i - level + posX.length) % posX.length; // 0..5
float size = mSize * (1 + (num * (1f / mAnimatedPoints)));
float sizeNext = mSize * (1 + ((num + 1) * (1f / mAnimatedPoints)));
float levelProgress = progress * posX.length - (int) (progress * posX.length);
float currentSize;
if (num == (mAnimatedPoints - 1)) {
// grow to next size
currentSize = mSize + (size - mSize) * levelProgress;
} else {
// shrink
currentSize = size + (sizeNext - size) * (1 - levelProgress);
}
canvas.drawCircle(posX[i], posY[i], currentSize, mPaint);
} else {
canvas.drawCircle(posX[i], posY[i], mSize, mPaint);
}
}
canvas.restoreToCount(save);
} else {
// draw normal
for (int i = 0; i < posX.length; i++) {
canvas.drawCircle(posX[i], posY[i], mSize, mPaint);
}
}
}
@Override
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
init();
}
@Override
public void setAlpha(int alpha) {
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
}
@Override
public int getOpacity() {
return 0;
}
@Override
public void start() {
if (mRunning) stop();
mRunning = true;
mStartTime = SystemClock.uptimeMillis();
invalidateSelf();
scheduleSelf(this, SystemClock.uptimeMillis() + FRAME_DELAY);
}
@Override
public void stop() {
unscheduleSelf(this);
mRunning = false;
}
@Override
public boolean isRunning() {
return mRunning;
}
@Override
public void run() {
invalidateSelf();
long uptimeMillis = SystemClock.uptimeMillis();
if (uptimeMillis + FRAME_DELAY < mStartTime + mDuration) {
scheduleSelf(this, uptimeMillis + FRAME_DELAY);
} else {
mRunning = false;
start();
}
}
public void setAnimatedPoints(int animatedPoints) {
mAnimatedPoints = animatedPoints;
}
}
Use with
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress);
progressBar.setIndeterminateDrawable(new RectProgressDrawable());
progressBar.setIndeterminate(true);
Alternatively you can see the full source code in a working project here
I'm doing it with bunch of images and animation-list
:
<?xml version="1.0" encoding="utf-8"?>
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/loadingAnimationImageView"
android:layout_width="36dp"
android:layout_height="36dp"
android:background="@drawable/loading_progress_indicator_animation" />
And res\drawable\loading_progres_indicator_animation.xml
:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/selected"
android:oneshot="false">
<item
android:drawable="@drawable/loading_progress_indicator_0"
android:duration="40" />
<item
android:drawable="@drawable/loading_progress_indicator_1"
android:duration="40" />
<item
android:drawable="@drawable/loading_progress_indicator_2"
android:duration="40" />
.....
<item
android:drawable="@drawable/loading_progress_indicator_11"
android:duration="40" />
<item
android:drawable="@drawable/loading_progress_indicator_12"
android:duration="40" />
</animation-list>
Where every loading_progress_indicator_XX
image is a state of progress indicator.
The custom view with indicator:
public final class LoadingAnimationView extends FrameLayout {
ImageView loadingAnimationImageView;
AnimationDrawable loadingProgressAnimation;
Handler handler = new Handler(Looper.getMainLooper());
public LoadingAnimationView(Context context) {
super(context);
initialize();
}
private void initialize() {
LayoutInflater.from(getContext()).inflate(R.layout.view_loading_videoview, this);
loadingAnimationImageView = (ImageView)getView().findViewById(R.id.loadingAnimationImageView);
loadingProgressAnimation = (AnimationDrawable) loadingAnimationImageView.getBackground();
adaptToVisibility(getVisibility());
}
@Override
public void setVisibility(int visibility) {
super.setVisibility(visibility);
adaptToVisibility(visibility);
}
void adaptToVisibility(final int visibility) {
if (visibility == VISIBLE) {
loadingProgressAnimation.start();
//This is to avoid "blinking" of progress indicator (if page is loading from cache)
handler.postDelayed(new Runnable() {
@Override
public void run() {
loadingAnimationImageView.setVisibility(visibility);
}
}, 200);
} else {
loadingProgressAnimation.stop();
loadingAnimationImageView.setVisibility(visibility);
}
}
}
As a result, in my case it looks like:
So all you will need is the states of your indicator & custom view like the one above.
To get states of your indicator, you can convert gif
to list of png
s I'd suggest to use EzGif service:
Another option - you can re-use one of dozens custom implementations of loading indicator like this one (it has some close enough to yours indicators) or this one (though, most of opensource indicators are circular).
I hope it helps.
Yes, you need to create a custom view for this, but there is an additional Android library which could be helpful for you.
Please check: https://github.com/mrwonderman/android-square-progressbar
Examples of using this library:
Check also this: How to make a Square progress-bar with changing color in certain time interval?
Here you would find how to create your own implementation of this lib.
Hope it help
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With