Get custom drawable parameters from xml - android

I have my custom Drawable like this:
public class SeekBarBackgroundDrawable extends Drawable {
Paint mBasePaint = null;
public SeekBarBackgroundDrawable() {
super();
mBasePaint = new Paint();
mBasePaint.setAntiAlias(true);
mBasePaint.setStyle(Paint.Style.STROKE);
mBasePaint.setStrokeCap(Paint.Cap.ROUND);
mBasePaint.setStrokeWidth(10);
mBasePaint.setColor(0xFF00FF00);
}
#Override
public void draw(Canvas canvas) {
Rect r = getBounds();
canvas.drawLine(r.left, canvas.getHeight()/2,r.right,canvas.getHeight()/2, mBasePaint);
}
Now, this drawable is used in layer-list with parameters color and width like here:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#android:id/background">
<cdev.mypreferences.SeekBarBackgroundDrawable
android:width="1dp" android:color="#color/bg_color">
</cdev.mypreferences.SeekBarBackgroundDrawable>
</item>
<item android:id="#android:id/progress">
<clip>
<shape android:shape="rectangle">
<corners android:radius="20dp"></corners>
<solid android:color="#color/seekbar_progress"></solid>
</shape>
</clip>
</item>
</layer-list>
How can I get parameters from this xml into Drawable class? I need to set mBasePaint stroke width and color?

Declaring custom drawables in xml is possible to do from API 24 onward, though I couldn't succeed to do that using the first approach mentioned in the docs.
Nevertheless, as the question relates to other aspect, I'll try to answer that part.
Adding this in your custom Drawable class would return the values you are interested in:
private final int[] attrsArray = new int[] {
android.R.attr.width,
android.R.attr.color,
};
#Override public void inflate(#NonNull Resources r, #NonNull XmlPullParser parser,
#NonNull AttributeSet attrs) throws XmlPullParserException, IOException {
super.inflate(r, parser, attrs);
final TypedArray a = r.obtainAttributes(attrs, attrsArray);
float width = a.getDimensionPixelSize(0, 0);
#SuppressLint("ResourceType")
int color = a.getColor(1, 0);
a.recycle();
}

Related

Android Layout transparent layout background with underline

I am trying to have a layout background drawable, which will be only gradient underline with 1-2 dp height and rest is transparent, so the upper part will have the parent's background.
Here is what I have.
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android>
<!-- underline color -->
<item>
<shape>
<gradient
android:startColor="#color/colorPrimaryDark"
android:endColor="#FFFFFFFF"
android:centerY="0.5"
android:angle="0"/>
</shape>
</item>
<!-- main color -->
<item android:bottom="2.5dp">
<shape android:shape="rectangle">
<solid android:color="#color/white" />
<padding
android:top="4dp"
android:bottom="4dp" />
</shape>
</item>
If I change the solid color in "main color" to transparent, whole background will be using "underline color" settings.
The technique you use to create a line on the bottom of the view works if the color of the layer overlaying the gradient layer is opaque. What you are trying to do is to apply a transparent layer that replaces (erases) the underlying gradient. That is not how it works: A transparent overlay leaves the underlying color, here a gradient, untouched.
Here is an alternate layer-list drawable that you can use for API 23+:
underline_drawable.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:gravity="bottom">
<shape>
<size android:height="2dp" />
<gradient
android:angle="0"
android:centerY="0.5"
android:endColor="#FFFFFFFF"
android:startColor="#color/colorPrimaryDark" />
</shape>
</item>
</layer-list>
Here is what it looks like:
Prior to API 23, you can use the following custom drawable, but it must be set in code.
GradientUnderline.java
public class GradientUnderline extends Drawable {
private Shader mShader;
private final Paint mPaint;
private int mHeight = -1;
private int mStartColor = Color.BLACK;
private int mEndColor = Color.WHITE;
private int mLastWidth;
public GradientUnderline() {
mPaint = new Paint();
}
public GradientUnderline(int lineHeight, int startColor, int endColor) {
mPaint = new Paint();
mHeight = lineHeight;
mStartColor = startColor;
mEndColor = endColor;
}
#Override
public void draw(#NonNull Canvas canvas) {
if (mShader == null || getBounds().width() != mLastWidth) {
mLastWidth = getBounds().width();
mShader = new LinearGradient(0, 0, getBounds().width(), mHeight, mStartColor,
mEndColor, Shader.TileMode.CLAMP);
mPaint.setShader(mShader);
}
canvas.drawRect(0, getBounds().height() - mHeight, getBounds().width(),
getBounds().height(), mPaint);
}
#Override
public void setAlpha(int alpha) {
}
#Override
public void setColorFilter(#Nullable ColorFilter colorFilter) {
}
#Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
}
I missed the availability of android:gravity initially because it is not mentioned on the "Drawable Resources" page. It is mentioned, however, in the LayerDrawable documentation.
Why problem occurs: Shape at first item will draw the gradient in entire region. After setting colour to second item will hide the top item region except ay 2.5dp at bottom. So whenever you set transparent colour to second item it automatically show the top level item that is gradient region..
Here i suggest the way to use but you can set to fixed height in view.
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:top="47dp">
<shape>
<gradient
android:startColor="#color/colorPrimaryDark"
android:endColor="#FFFFFFFF"
android:centerY="0.5"
android:angle="0"/>
</shape>
</item>
</layer-list>
View.xml
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#drawable/bottom_line">
</RelativeLayout>
Change size according to your needs..!
OUTPUT

Enlarge the center of an Android gradient drawable

I created an Android gradient drawable where the top and bottom are black and the center is transparent:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<gradient
android:startColor="#android:color/black"
android:centerColor="#android:color/transparent"
android:endColor="#android:color/black"
android:angle="90"/>
</shape>
The rendered gradient looks like this:
As you can see, the black parts spread to most of the screen. I want the black to show only on a small portion of the top and bottom. Is there a way I can make the transparent center larger, or make the top and bottom black stripes smaller?
I tried playing around with some of the other XML attributes mentioned in the linked GradientDrawable documentation, yet none of them seem to make and difference.
For an XML only solution, you can create a layer-list with two separate gradient objects.
The following code creates two overlapping gradient objects and uses centerY with centerColor to offset the black section. Here, the centerY attributes are set to 0.9 and 0.1, so the black color is restricted to the top and bottom 10% of the view height.
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<gradient
android:angle="90"
android:centerColor="#android:color/transparent"
android:centerY="0.9"
android:endColor="#android:color/black"
android:startColor="#android:color/transparent" />
</shape>
</item>
<item>
<shape>
<gradient
android:angle="90"
android:centerColor="#android:color/transparent"
android:centerY="0.1"
android:endColor="#android:color/transparent"
android:startColor="#android:color/black" />
</shape>
</item>
</layer-list>
For API level 23 or higher, the following solution will also work, using android:height. This solution can work even if you don't know the total height of your view, as long as you know how large you want the gradient to be.
This code creates two separate gradients, each with a height of 60sp, and then uses android:gravity to float the gradients to the top and bottom of the view.
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:height="60sp"
android:gravity="top">
<shape android:shape="rectangle">
<gradient
android:angle="90"
android:endColor="#android:color/black"
android:startColor="#android:color/transparent" />
</shape>
</item>
<item
android:height="65sp"
android:gravity="bottom">
<shape android:shape="rectangle">
<gradient
android:angle="90"
android:endColor="#android:color/transparent"
android:startColor="#android:color/black" />
</shape>
</item>
</layer-list>
Thank you #Luksprog for the code help, and #thenaoh for the start of the idea.
The above solutions work and it is nice that they are pure XML. If your gradient is showing with stripes, you may want to try a programmatic solution, like shown in #lelloman's answer, to create a smoother gradient.
Here is how it could be done with a custom Drawable. You can tune the LinearGradient as you want, and then set it as the view's background with view.setBackground(new CustomDrawable());.
public class CustomDrawable extends Drawable {
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private int[] colors;
private float[] positions;
public CustomDrawable() {
paint.setStyle(Paint.Style.FILL);
this.colors = new int[]{0xff000000, 0xffaaaaaa, 0xffffffff, 0xffaaaaaa, 0xff000000};
this.positions = new float[]{.0f, .2f, .5f, .8f, 1.f};
}
#Override
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
LinearGradient linearGradient = new LinearGradient(left, top,left, bottom, colors, positions, Shader.TileMode.CLAMP);
paint.setShader(linearGradient);
}
#Override
public void draw(#NonNull Canvas canvas) {
canvas.drawRect(getBounds(), paint);
}
#Override
public void setAlpha(#IntRange(from = 0, to = 255) int alpha) {
paint.setAlpha(alpha);
}
#Override
public void setColorFilter(#Nullable ColorFilter colorFilter) {
paint.setColorFilter(colorFilter);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSPARENT;
}
}
There is a solution, assuming that you know in advance the height of your view (let's say here 60dp):
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:bottom="40dp">
<shape android:shape="rectangle">
<gradient
android:type="linear"
android:angle="90"
android:startColor="#FFFFFF"
android:endColor="#000000"/>
</shape>
</item>
<item
android:top="20dp"
android:bottom="20dp"
android:gravity="center_vertical">
<shape android:shape="rectangle">
<solid android:color="#FFFFFF"/>
</shape>
</item>
<item
android:top="40dp"
android:gravity="bottom">
<shape android:shape="rectangle">
<gradient
android:type="linear"
android:angle="90"
android:startColor="#000000"
android:endColor="#FFFFFF"/>
</shape>
</item>
</layer-list>
But if you don't know the height in advance, another solution would be to make your own custom view, like this:
public class MyView extends ImageView
{
private Paint paint = null;
public MyView(Context context, AttributeSet attrs)
{
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint.setShader(getLinearGradient(0, getHeight()));
canvas.drawPaint(paint);
}
private LinearGradient getLinearGradient(float y0, float y1)
{
// colors :
int[] listeColors = new int[3];
listeColors[0] = 0xFF000000;
listeColors[1] = 0xFFFFFFFF;
listeColors[2] = 0xFFFFFFFF;
// positions :
float[] listPositions = new float[3];
listPositions[0] = 0;
listPositions[1] = 0.25F;
listPositions[2] = 1;
// gradient :
return new LinearGradient(0, y0, 0, y0 + (y1 - y0) / 2, listeColors, listPositions, Shader.TileMode.MIRROR);
}
}
Hope it helps.

Vertical progressbar in Android [duplicate]

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!
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!
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.
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.
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
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"/>
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:
Simple and Easy way:
Just add a view to a LinearLayout and scale it.
<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()
Here is a simple solution, just rotate your progress bar
android:rotation="270"
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" />
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.
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.
Create the tag in your layout xml like normal horizontal progress bar.
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.
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);
}
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();
}
}
}
<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
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 **
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.
Vertical progress bars are not supported by default.

Reusing layout with different values using styles and attrs - Android

I have a layout border_bottom_black_bluegrey_background.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape android:shape="rectangle" >
<stroke
android:width="1dp"
android:color="#color/black" />
</shape>
</item>
<item android:bottom="1dp">
<shape android:shape="rectangle" >
<solid android:color="#color/blue_grey" />
</shape>
</item>
</layer-list>
I'm trying to reuse this layout so that I don't have to create one for each different border and background color.
I know this can be accomplished with styles and attrs, I had a look on the internet for good tutorial but with no avail.
This is what i've got so far:
attrs.xml
<resources>
<declare-styleable>
<attr name="backgroundColor" format="reference"/>
<attr name="borderColor" format="reference"/>
</declare-styleable>
</resources>
styles.xml
<style name="BorderBottom">
<item name="android:background">#drawable/border_bottom</item>
</style>
<style name="BlackBorderBlueGreyBackground" parent="BorderBottom">
<item name="borderColor">#color/black</item>
<item name="backgroundColor">#color/blue_grey</item>
</style>
border_bottom.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape
android:shape="rectangle">
<stroke android:width="1dp" android:color="#color/black" />
<solid android:color="?attr/borderColor" />
</shape>
</item>
<item android:bottom="1dp">
<shape
android:shape="rectangle">
<solid android:color="?attr/backgroundColor" />
</shape>
</item>
</layer-list>
My implementation of the style
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/BlackBorderBlueGreyBackground"
android:orientation="vertical" >
</RelativeLayout>
Can someone point me in the right direction?
I think the issue is that when you use the attrs.xml that is for use in creating custom views. The problem I see with your code above is that border_bottom.xml is pointing to color attributes that have not been explicitly declared and it has no way of knowing what color it should be. If want to create a layout that you can continuously use my suggestion would be to create a class that creates backgrounds for you and if need be you can just save the drawable created. This is a class that I use to draw underlines under views I think it should suffice.
public final class Background extends ShapeDrawable {
public final static int RECTANGLE = 0x00;
public final static int ROUND_RECTANGLE = 0x01;
public final static int UNDERLINE = 0x02;
//For future use
//public final static int CIRCLE = 0x03;
private int shape_type;
private final Paint fill_paint, stroke_paint;
private int stroke_width;
private static RectShape rectangle = new RectShape();
private static RoundRectShape round_rectangle;
//private static OvalShape circle = new OvalShape();
public static final Background newInstance(int shape, int background_color, int stroke_color, int stroke_width) {
switch (shape) {
case UNDERLINE:
case RECTANGLE:
return new Background(shape, rectangle, background_color, stroke_color, stroke_width);
case ROUND_RECTANGLE:
round_rectangle = new RoundRectShape(new float[] { stroke_width, stroke_width,
stroke_width, stroke_width, stroke_width, stroke_width, stroke_width, stroke_width }, null, null);
return new Background(shape, round_rectangle, background_color, stroke_color, stroke_width);
default:
return null;
}
}
private Background(int shape_type, Shape shape, int fill_color, int stroke_color, int stroke_width) {
super(shape);
this.shape_type = shape_type;
this.stroke_width = stroke_width;
//set the fill_paint
fill_paint = new Paint(getPaint());
fill_paint.setColor(fill_color);
stroke_paint = new Paint(fill_paint);
//set the stroke paint
stroke_paint.setStyle(Paint.Style.STROKE);
stroke_paint.setAntiAlias(false);
stroke_paint.setStrokeWidth(stroke_width);
stroke_paint.setColor(stroke_color);
}
#Override
protected void onDraw(Shape shape, Canvas canvas, Paint paint) {
switch(shape_type) {
case UNDERLINE:
final RectF underline = new RectF(getBounds().left - stroke_width, getBounds().top - stroke_width,
getBounds().right + stroke_width,
getBounds().bottom - stroke_width);
canvas.drawRect(underline, stroke_paint);
break;
case RECTANGLE:
final RectF rectangle = new RectF(stroke_width, stroke_width, getBounds().right - stroke_width,
getBounds().bottom - stroke_width);
canvas.drawRect(rectangle, fill_paint);
canvas.drawRect(rectangle, stroke_paint);
break;
}
}
}
Then you can just save the drawable created and use it on multiple layouts in code.
//untested code
Layout layout = (RelativeLayout)findViewById(R.id.your_layout_id);
Background background = Background.newInstance(Background.RECTANGLE, Color.GRAY, Color.BLUE, 1);
layout.setBackground(background);

Android - set a ProgressBar to be a vertical bar instead of horizontal?

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!
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!
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.
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.
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
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"/>
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:
Simple and Easy way:
Just add a view to a LinearLayout and scale it.
<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()
Here is a simple solution, just rotate your progress bar
android:rotation="270"
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" />
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.
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.
Create the tag in your layout xml like normal horizontal progress bar.
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.
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);
}
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();
}
}
}
<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
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 **
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.
Vertical progress bars are not supported by default.

Categories

Resources