41

I am trying to use a ProgressBar as a metering like display. I thought it was going to be an easy task and thought that ProgressBar had a property to set to be vertical, but I'm not seeing anything.

Additionally I'd like to be able to show ruler like indicator along the side of the bar to clearly indicate the current level.

Pointers appreciated - Thanks!

bursk
  • 1,647
  • 7
  • 25
  • 39
  • http://android-coding.blogspot.co.uk/2013/02/implement-vertical-progressbar.html – M-- Jul 14 '17 at 18:02

16 Answers16

73

I had recently come across the need for a vertical progress bar but was unable to find a solution using the existing Progress Bar widget. The solutions I came across generally required an extension of the current Progress Bar or a completely new class in it self. I wasn't convinced rolling out a new class to achieve a simple orientation change was necessary.

This article presents a simple, elegant, and most importantly, a no-hack solution to achieving a vertical progress bar. I'm going to skip the explanation and simply provide a cookie cutter solution. If you require further details feel free to contact me or leave a comment below.

Create an xml in your drawable folder (not drawable-hdpi or drawable-mdpi -- place it in drawable). For this example I call my xml vertical_progress_bar.xml

Here's what to place in the xml file:

<?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:startColor="#ff9d9e9d"
        android:centerColor="#ff5a5d5a"
        android:centerY="0.75"
        android:endColor="#ff747674"
        android:angle="180"
      />
    </shape>
  </item>
  <item android:id="@android:id/secondaryProgress">
    <clip android:clipOrientation="vertical" android:gravity="bottom">
      <shape>
        <corners android:radius="5dip" />
        <gradient
          android:startColor="#80ffd300"
          android:centerColor="#80ffb600"
          android:centerY="0.75"
          android:endColor="#a0ffcb00"
          android:angle="180"
        />
      </shape>
    </clip>
  </item>
  <item android:id="@android:id/progress">
    <clip android:clipOrientation="vertical" android:gravity="bottom">
      <shape>
        <corners android:radius="5dip" />
        <gradient
          android:startColor="#ffffd300"
          android:centerColor="#ffffb600"
          android:centerY="0.75"
          android:endColor="#ffffcb00"
          android:angle="180"
        />
      </shape>
    </clip>
</item>
</layer-list>

Create an xml file called styles.xml and place it in res/values. If your project already contains styles.xml in res/values then skip this step.

Modify your styles.xml file and append the following code to the end of the file:

<style name="Widget">
</style>
<style name="Widget.ProgressBar">
  <item name="android:indeterminateOnly">true</item>
  <item name="android:indeterminateBehavior">repeat</item>
  <item name="android:indeterminateDuration">3500</item>
  <item name="android:minWidth">48dip</item>
  <item name="android:maxWidth">48dip</item>
  <item name="android:minHeight">48dip</item>
  <item name="android:maxHeight">48dip</item>
</style>
<style name="Widget.ProgressBar.Vertical">
  <item name="android:indeterminateOnly">false</item>
  <item name="android:progressDrawable">@drawable/progress_bar_vertical</item>
  <item name="android:indeterminateDrawable">@android:drawable/progress_indeterminate_horizontal</item>
  <item name="android:minWidth">1dip</item>
  <item name="android:maxWidth">12dip</item>
</style>

Add your new vertical progress bar to your layout. Here's an example:

<ProgressBar
  android:id="@+id/vertical_progressbar"
  android:layout_width="12dip"
  android:layout_height="300dip"
  style="@style/Widget.ProgressBar.Vertical"
/>

That should be all you need to do to make use of a vertical progress bar in your project. Optionally, you might have custom drawable nine-patch images that you are using for the progress bar. You should make the appropriate changes in the progress_bar_vertical.xml file. I hope this helps you out in your project!

jagsaund
  • 2,389
  • 18
  • 16
  • the example in your blog post is incomplete, there are users asking questions to which you have not responded. – LairdPleng Apr 16 '13 at 04:39
  • 3
    @jagsaund there is no file on given link – yuva ツ Jan 08 '14 at 07:26
  • 1
    @jagasund: the link is DEAD – Phantômaxx May 25 '14 at 19:04
  • Sorry for the dead link. My blog has been down for a while but I found an old copy. So this is just a copy of that. Hope it helps! – jagsaund Jun 02 '14 at 01:17
  • With the themes used in Lollipop, I'm afraid this probably won't be consistent with the default behavior of horizontal progress bars. Probably not a great idea to use it :( – Martin Devillers Jun 25 '15 at 21:17
  • solution not usable. the drawable can not be drawn. i get error. "color format should start with #". applied with success the solution of Daniel S. in this thread – Gaucho Jan 12 '16 at 14:59
  • quick fix, thanks, but doesn't work on Android 4.1 for some reason :( – Advanced Oct 18 '16 at 12:35
  • You don't need to create the styles, remove them and only set android:indeterminateOnly="false" on the ProgressBar directly. –  Oct 19 '18 at 14:00
33

You have to create your own custom progressbar.

In your xml add this layout:

 <com.example.component.VerticalProgressBar 
        style="?android:attr/progressBarStyleHorizontal"
        android:id="@+id/verticalRatingBar1"
        android:layout_width="wrap_content"
        android:progress="50"
        android:layout_height="fill_parent" />

VerticalProgressBar.java

public class VerticalProgressBar extends ProgressBar{
    private int x, y, z, w;

    @Override
    protected void drawableStateChanged() {
        // TODO Auto-generated method stub
        super.drawableStateChanged();
    }

    public VerticalProgressBar(Context context) {
        super(context);
    }

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

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

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(h, w, oldh, oldw);
        this.x = w;
        this.y = h;
        this.z = oldw;
        this.w = oldh;
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec,
            int heightMeasureSpec) {
        super.onMeasure(heightMeasureSpec, widthMeasureSpec);
        setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
    }

    protected void onDraw(Canvas c) {
        c.rotate(-90);
        c.translate(-getHeight(), 0);
        super.onDraw(c);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isEnabled()) {
            return false;
        }

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:

            setSelected(true);
            setPressed(true);
            break;
        case MotionEvent.ACTION_MOVE:
            setProgress(getMax()
                    - (int) (getMax() * event.getY() / getHeight()));
            onSizeChanged(getWidth(), getHeight(), 0, 0);

            break;
        case MotionEvent.ACTION_UP:
            setSelected(false);
            setPressed(false);
            break;

        case MotionEvent.ACTION_CANCEL:
            break;
        }
        return true;
    }

    @Override
    public synchronized void setProgress(int progress) {

        if (progress >= 0)
            super.setProgress(progress);

        else
            super.setProgress(0);
        onSizeChanged(x, y, z, w);

    }
}

Or : Jagsaund solution is also being perfect.

Community
  • 1
  • 1
Ramesh Akula
  • 5,720
  • 4
  • 43
  • 67
  • 1
    Unfortunately the progress bar is not updated correctly after reaching a height that is equal to the width of the bar (starting from the top), I think some x/y got mixed up but I couldn't find an easy fix, used jagsaund solution – SAKIROGLU Koray Feb 01 '13 at 18:59
  • 2
    Add invalidate(); after onSizeChanged(); to avoid the mentioned rendering issues. – Oliver Jonas Sep 23 '14 at 07:06
26

I know that it´s an old post but I found a very simple solution to this problem that maybe can help somebody. First at all create a progress_drawable_vertical.xml like this:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
    <color android:color="#777" />
</item>
<item android:id="@android:id/progress">
    <clip
        android:clipOrientation="vertical"
        android:gravity="bottom">
        <shape>
            <gradient
                android:startColor="#00FF00"
                android:centerColor="#FFFF00"
                android:endColor="#FF0000"
                android:angle="90" />
        </shape>
    </clip>
</item>
</layer-list>

Then just use this in your progressBar:

<ProgressBar
    android:id="@+id/progress_bar"
    style="@android:style/Widget.ProgressBar.Horizontal"
    android:layout_centerInParent="true"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:max="100"
    android:progress="33"
    android:progressDrawable="@drawable/progress_drawable_vertical" />

I also have created an progress_drawable_horizontal.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
    <color android:color="#777" />
</item>
<item android:id="@android:id/progress">
    <clip
        android:clipOrientation="horizontal"
        android:gravity="left">
        <shape>
            <gradient
                android:startColor="#00FF00"
                android:centerColor="#FFFF00"
                android:endColor="#FF0000" />
        </shape>
    </clip>
</item>
</layer-list>

with the objetive of mantain the same style defined in progress_drawable_vertical.xml

The key here is the correct use of android:clipOrientation and android:gravity.

I found this solution here and the core of the solution is similar to jagsaund but a little bit more simple.

Community
  • 1
  • 1
Daniel S.
  • 2,629
  • 2
  • 21
  • 19
13

I found the probably best(easiest & most versatile) solution:)

This is an old post, but it was so hard for me to find this so easy solution so I thought I should post it..

Just use a scale-drawable (or a 9-patch if you want), no need for ANY OTHER code.

Example:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">  

    <item android:id="@android:id/background" android:drawable="@color/transparent"/>  

    <item android:id="@android:id/progress">
        <scale android:scaleGravity="bottom" android:scaleWidth="0%" android:scaleHeight="100%">
            <shape >
                <solid android:color="@color/blue"/>
                <corners android:topRightRadius="1dp" android:topLeftRadius="1dp"/>
            </shape>
        </scale>
    </item>
</layer-list>  

And of course the normal code:

<ProgressBar
    android:id="@+id/progress_bar"
    style="@android:style/Widget.ProgressBar.Horizontal"
    android:layout_width="24dp"
    android:layout_height="match_parent"
    android:max="1000"
    android:progress="200"
    android:progressDrawable="@drawable/progress_scale_drawable" />  

Notice the scale-drawable's xml lines (the magic lines):

android:scaleGravity="bottom"  //scale from 0 in y axis (default scales from center Y)  
android:scaleWidth="0%"        //don't scale width (according to 'progress')
android:scaleHeight="100%"     //do scale the height of the drawable
guy_m
  • 3,110
  • 2
  • 19
  • 33
8
This perfectly works

<?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>
            <gradient
                android:startColor="#DDDDDD"
                android:centerColor="#DDDDDD"
                android:centerY="0.75"
                android:endColor="#DDDDDD"
                android:angle="270"
                />
        </shape>
    </item>

    <item
        android:id="@android:id/progress">
        <clip
            android:clipOrientation="vertical"
            android:gravity="top">
            <shape>
                <gradient
                    android:angle="0"
                    android:startColor="#302367"
                    android:centerColor="#7A5667"
                    android:endColor="#C86D67"/>
            </shape>
        </clip>
    </item>
</layer-list>

    <ProgressBar
            android:id="@+id/progress_bar"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="5dp"
            android:layout_height="match_parent"`enter code here`
            android:progressDrawable="@drawable/progress_dialog"/>
Anupam
  • 2,845
  • 2
  • 16
  • 30
7

Simple and Easy way:

Just add a view to a LinearLayout and scale it.

enter image description here

<LinearLayout
        android:layout_width="4dp"
        android:layout_height="300dp"
        android:background="@color/md_green_50"
        android:orientation="vertical">
    <View
            android:id="@+id/progressView"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_gravity="top"
            android:layout_weight="1"
            android:background="@color/md_green_500"
            android:scaleY="0.0" />
</LinearLayout>

Set View's pivotY to zero:

progressView.pivotY = 0F

Now you can fill the progress using scaleY between 0F and 1F:

progressView.scaleY = 0.3F

Bonus: Animate progress using animate():

progressView.animate().scaleY(0.3F).start()
Amir
  • 1,628
  • 20
  • 28
  • Can you provide a complete example? – TomSelleck May 18 '22 at 21:06
  • 1
    @TomSelleck The example is complete! Just add the xml codes anywhere in your view, and use `progressView.animate().scaleY(0.3F).start()` to adjust the progress. where `0.3F` is the progress value. – Amir May 21 '22 at 02:57
6

Creating the progress bar (I converted my code from c# to java so might not be 100% correct)

ProgressBar progBar = new ProgressBar(Context, null, Android.resource.attribute.progressDrawable);
progBar.progressDrawable = ContextCompat.getDrawable(Context, resource.drawable.vertical_progress_bar);
progBar.indeterminate = false;

vertical_progress_bar.xml

<?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>
          <solid android:color="@color/grey" />

          <corners android:radius="20dip" />
        </shape>
      </item>
      <item android:id="@android:id/progress">
        <scale
            android:drawable="@drawable/vertical_progress_bar_blue_progress"
            android:scaleHeight="100%"
            android:scaleGravity="bottom"/>
      </item>
    </layer-list>

vertical_progress_bar_blue_progress.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >

  <corners
      android:radius="20dip" />

  <solid android:color="@color/ProgressBarFourth" />

</shape>

What's going to make your bar vertical is the scaleHeight and scaleGravity attributes in vertical_progress_bar.xml.

It ends up looking something like this:

enter image description here

Met-u
  • 555
  • 7
  • 18
5

Here is a simple solution, just rotate your progress bar

android:rotation="270"
  • I was able to get this to work after some experimentation. Inside a LinearLayout, android:layout_width has to be "fill_parent", and I had to fiddle a bit with layout_weight to make it expand to where I want it. But hey - it works! And is far simpler than making custom stuffs. Thx! – Mark Phillips May 13 '20 at 00:24
  • It works perfect only with square layout. – Nikunj Paradva Mar 09 '21 at 09:46
  • But you need to declare scaleX to manage the height of the progressbar. – Er. Amreesh Arya Jul 19 '23 at 11:36
2

Add this to the xml code android:rotation="90" android:transformPivotX="0dp" So this is how your Progress Bar xml should look

<ProgressBar
    android:id="@+id/progressBar6"
    style="?android:attr/progressBarStyleHorizontal"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:rotation="90"
    android:transformPivotX="0dp"
    tools:layout_editor_absoluteX="101dp"
    tools:layout_editor_absoluteY="187dp" />
1

I have the exact problem. Making a custom class (extending ProgressBar) will create code that are hard to maintain. Using a custom style will cause compatibility issue with different theme from new OS (e.g. lollipop)

Eventually, I just apply a rotation animation to an horizontal progress bar. Inspired by Pete.

  1. Create the tag in your layout xml like normal horizontal progress bar.
  2. Make sure that the size and position of the ProgressBar is what you want after rotation. (Perhaps setting negative margin will help). In my code I rotate the view from 0,0.
  3. Use the method below to rotate and set new progress.

Code:

private void setProgress(final ProgressBar progressBar, int progress) {
    progressBar.setWillNotDraw(true);
    progressBar.setProgress(progress);
    progressBar.setWillNotDraw(false);
    progressBar.invalidate();
}

private void rotateView(final View v, float degree) {
    Animation an = new RotateAnimation(0.0f, degree);
    an.setDuration(0);
    an.setRepeatCount(0);
    an.setFillAfter(true);               // keep rotation after animation
    v.setAnimation(an);
}
Community
  • 1
  • 1
Pan Ng
  • 86
  • 1
  • 7
1

To utilize the ProgressBar and make it vertical, you would have to create your own custom View extending the ProgressBar view and override the onDraw() method. This will allow you to draw it in a reverse orientation. Take a look at the source code of the ProgressBar.onDraw() (located at the bottom of the link) for help on how to do this. Best case scenario, you'll just have to swap a few x and y variables.

John Leehey
  • 22,052
  • 8
  • 61
  • 88
0

Simple progrebar image view

example

viewHolder.proBarTrueImage.setMaximalValue(147);
viewHolder.proBarTrueImage.setLevel(45);
viewHolder.proBarTrueImage.setColorResource(R.color.corner_blue);

simple class

public class ProgressImageView extends ImageView {

private Context mContext;
private Paint paint;
private RectF rectf;

private int maximalValue = 1;
private int level = 0;
private int width;
private int height;

public ProgressImageView(Context context) {
    super(context);
    init(context, null, 0);
}

public ProgressImageView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context, attrs, 0);
}

public ProgressImageView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs, defStyleAttr);
}

private  void init(Context context, AttributeSet attrs, int defStyleAttr){

    mContext = context;
    paint = new Paint();
    rectf = new RectF();
    paint.setColor(Color.GRAY);
    paint.setStyle(Paint.Style.FILL);
};

@Override
protected void onDraw(Canvas canvas) {

    float dif = (float) height / (float) maximalValue;
    int newHeight = height - (int) (dif * level);

    rectf.set(0,newHeight, width, height);
    canvas.drawRect(rectf, paint);

    super.onDraw(canvas);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    this.width = w;
    this.height = h;
}

public void setMaximalValue(int maximalValue) {
    this.maximalValue = maximalValue;
    invalidate();
}

public void setLevel(int level) {
    this.level = level;
    invalidate();
}

public void setColorResource(int color) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        color = mContext.getResources().getColor(color,mContext.getTheme());
    }else {
        color = mContext.getResources().getColor(R.color.corner_blue);
    }

    setColor(color);
}

public void setColor(int color){
    if (paint != null){
        paint.setColor(color);
        invalidate();
    }
}

}

Vahe Gharibyan
  • 5,277
  • 4
  • 36
  • 47
0
<ProgressBar
        android:id="@+id/battery_pb"
        android:rotation="270"
        android:progress="100"
...
/>

Use android:rotation="270" to 100% be like bottom to top or android:rotation="90" to 100% be like top to bottom

Beto Caldas
  • 2,198
  • 2
  • 23
  • 23
  • 1
    Does not work, the progress bar is rotated but height remains small – htafoya Apr 12 '19 at 05:28
  • 1
    It took some experimentation but I was able to make this work. Inside a LinearLayout, android:layout_width has to be "fill_parent", and I had to fiddle a bit with layout_weight to make it expand to where I want it. But, it does in fact work. Thanks! This is much simpler than programming custom widgets. – Mark Phillips May 13 '20 at 00:25
0

enter link description here

**Check this link out, I was trying to use a similar thing and also you can use stepper for your requirement, few projects are available on Github about HOW TO USE STEPPER IN ANDROID STUDIO **

  • 2
    Welcome to Stack Overflow! While links are great way of sharing knowledge, they won't really answer the question if they get broken in the future. Add to your answer the essential content of the link which answers the question. In case the content is too complex or too big to fit here, describe the general idea of the proposed solution. Remember to always keep a link reference to the original solution's website. See: [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer) – sɐunıɔןɐqɐp Sep 27 '18 at 06:32
0

For making a vertical ProgressBar, The way that I solved it was first rotating it by 90 degrees, then scaling it with a value entered by hand.

scaleX = layout_height/layout_width

Here's an example of my attributes on the ProgressBar

android:layout_width="20dp"
android:layout_height="233dp"
android:rotation="-90"
android:scaleX="11.65"

This can be a little manual, but because they don't have a vertical progress bar by default, this was a pretty good workaround for me. The scaleX could be calculated automatically, but it would have to be after everything is drawn on the screen.

jhwblender
  • 779
  • 6
  • 12
-19

Vertical progress bars are not supported by default.

Romain Guy
  • 97,993
  • 18
  • 219
  • 200
  • 42
    **@Romain Guy** Aren't you an Android developer at Google? Can't you *please* make all kinds of progress bars support vertical placement? I think it's a pretty obvious expectation from UI developers. – java.is.for.desktop Jun 14 '11 at 22:58
  • 6
    this is not the answer to the question. It's just saying "too hard". – baash05 Jan 15 '15 at 22:16
  • I think you are just not good enough in Android to provide a solution. Not an answer actually at all. This is just your an oppinion. – Vladyslav Matviienko May 15 '16 at 10:43
  • 1
    See my answer below, it's pretty easy to accomplish (apparently) – guy_m Feb 21 '19 at 16:06
  • I got to know by this accepted answer what kind of developers are there in google ! –  Oct 09 '20 at 07:54