Custom progress bar with images or Image view with clipdrawable - android

I have been looking for tutorials on custom progress bars and 95% of what I found was how to customize using colors and gradients NOT with images (basically an image of empty bar with image or full bar on top). When I try to use images for progress bar, the dimensions are wrong ( wrap content does not work properly and it is half truncated).
I was able to achieve a successful bar with images using image with clipdrawble and level setting.
SOOO, is ProgressBar with images used for its background/progress frowned upon and I should use imageview instead?

The key is to make sure ProgressBar accounts for your custom drawable's dimensions. One way to do it is to override the onMeasure. Here is a rough sketch of your custom class's onMeasure implementation (compare this against ProgressBar's implementation - you will notice the subtle changes) :
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
// the super's onMeasure method ignores the dimensions of the custom progress drawable
// if they are greater than a specified height & width (mMaxHeight & mMaxWidth). It simply uses those
// default dimensions for the drawable, consequently resizing them; which is not suitable for larger drawables.
// So, it is necessary to override this method to allow the ProgressBar to account for the drawable's original
// dimensions and draw the image/drawable accordingly.
Drawable d = getProgressDrawable();
int dw = 0;
int dh = 0;
if (d != null) {
dw = d.getIntrinsicWidth();
dh = d.getIntrinsicHeight();
}
int[] state = getDrawableState();
if(mProgressDrawable != null && mProgressDrawable.isStateful())
mProgressDrawable.setState(state);
dw += getPaddingLeft() + getPaddingRight();
dh += getPaddingTop() + getPaddingBottom();
setMeasuredDimension(resolveSize(dw, widthMeasureSpec), resolveSize(dh, heightMeasureSpec));
}
You can then set your empty bar as the background for the custom ProgressBar like you would usually do for a view - android:background="#drawable/empty_bar"
The next part is to set the progressDrawable, for which you will have to use a <layer-list>, as we want to closely match the progress bar's drawable structure (default drawable). Here is a sample:
<?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="#00000000"/>
</shape>
</item>
<item android:id="#android:id/progress">
<clip
android:clipOrientation="vertical"
android:gravity="bottom"
android:drawable="#drawable/full_bar">
</clip>
</item>
</layer-list>
And finally to animate the progressbar you could use an ObjectAnimator:
final ObjectAnimator animator = ObjectAnimator
.ofInt(progressBar, "progress", 0, 100)
.setDuration(2000);
animator.start();

Related

How to indent the divider in a linear layout RecyclerView (ie, add padding, margin, or an inset only to the ItemDecoration)

Following this answer I was able to get a divider between the items of a vertical RecyclerView. However, I also wanted to slightly indent the divider lines.
I was able to do it by hard coding in an INDENT value in the RecyclerView.ItemDecoration subclass.
int INDENT = 20;
#Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
int left = parent.getPaddingLeft() + INDENT;
int right = parent.getWidth() - parent.getPaddingRight() - INDENT;
// ...
divider.setBounds(left, top, right, bottom);
// ...
}
However, then I would have had to also mess with density independant pixels.
I finally found a solution similar to how it was done with ListView so I am sharing that as an answer below.
Use inset
drawable/my_divider.xml
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="40dp"
android:insetRight="40dp" >
<shape>
<size android:height="1dp"/>
<solid android:color="#color/recyclerview_divider" />
</shape>
</inset>
Using the constructor that takes a resource id as shown in this answer, we can supply the id of our custom divider xml file.
Update:
We can't add drawable in DividerItemDecoration constructor, we need to set drawable after created like in the example below
ItemDecoration dividerItemDecoration = new DividerItemDecoration(
getActivity(),
RecyclerView.VERTICAL
)
dividerItemDecoration.setDrawable(drawable)
recyclerView.addItemDecoration(decorator);
Drawable drawable =

Android - Multiple colors & size in drawable shape style

I'm trying to have a circle background for my TextView, so I created a shape style as below.
But I need to have multiple colors with multiple sizes (while the textSize stays constant), so I need to set the width/height in the style.
From my understanding..Layer List puts all the shapes on top of one another? Because I need to call it 12 times at different places, so it seems quite cumbersome to have 12 shape style xmls.
Is there a better way to have all the different shape/size combinations inside one XML?
Shape Style:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<corners android:radius="10dp"/>
<solid android:color="#color/girl_orange"/>
<size
android:width="84dp"
android:height="84dp" />
</shape>
Called in layout xml by:
android:background="#drawable/skills_circle"
Thanks in advance!!
create a custom Drawable, this way you can have milions combinations of size/color:
class CircleDrawable extends Drawable {
...
}
So I followed the advice from pskink and created a CircleDrawable class.
It works quite nicely for my application (although I don't know if it's the right way...), so I thought I'd share it.
public CircleDrawable(Bitmap bitmap, Context context) {
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
CircleDrawable.context = context;
drawable = new ShapeDrawable(new OvalShape());
setColor(); // supports multiple color
setSize(); //supports multiple size
}
private void setColor() {
// some algorithm to pick the right color...
if (...)
int color = context.getResources().getColor(R.color.pale_blue);
paint.setColor(color);
}
/*
* algorithm to set size here...
*/
#Override
public void draw(Canvas canvas) {
//draw circle in the middle of the TextView
canvas.drawCircle(textViewSize, textViewSize, circleSize, paint);
}
And in the main code where I need to dynamically draw the circles:
final float scale = getApplicationContext().getResources().getDisplayMetrics().density;
int pixels = (int) (107.0f * scale + 0.5f);
skills.setWidth(pixels);
skills.setHeight(pixels);
skills.setBackground(new CircleDrawable(null, getApplicationContext()));
And I ended up with a bunch of circles with different shapes and colors.

How I can set partly the background color

I have a class that implement a view
**DrawView.class**
public class DrawView extends View {
Paint paint = new Paint();
public DrawView(Context context, AttributeSet attrs){
super(context, attrs);
}
and my file.xml
<com.example.sliding.DrawView
android:id="#+id/tv_listRow_item1"
android:tag="tv_listRow_item1_1"
android:layout_height="0dip"
android:layout_width="fill_parent"
android:layout_weight="1"
android:gravity="center"
android:width="100dip"
android:height="30dip"
android:background="#drawable/textview_listrow_border"/>
This view have 30 dip of height. How can i color only 30% of this 30 dip?
Anyone can give me an example?
Thanks for your time and help.
I'm not entirely sure this will work, but you could make a 9 patch that is 30% color and 70% transparent, then define two stretchable areas (one for each) in the appropriate percentages. When 9 patches are stretched, they're supposed to respect the ratios of multiple stretch zones, so I think it would work.
http://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch
One way is by using a LayerDrawable
But this would only work when the heigt of the view is fixed at 80dp.
Create an xml file in your drawable folder.
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="#FFFFFFFF" />
</shape>
</item>
<item android:top="10dp">
<shape >
<solid android:color="#FF000000"/>
</shape>
</item>
<item android:top="60dp">
<shape>
<solid android:color="#FFFFFFFF"/>
</shape>
</item>
</layer-list>
And set this as the background to the view.
I NEED A FUNCTION THAT HAVE 2 PARAMETERS. This FIRST INDICATE THE BEGIN OF COLOR. THE SECOND INDICATE THE END. hEIGHT=80dp. THE FIRST PARAMETER FOR EXAMPLE IS 20 THE SECOND IS 30 FOR EXAMPLE. The pixels inside this interval have a color…..
I guess that the easiest way to do it is to override the onDraw(Canvas canvas) function and draw a rectangle like this.
double mStart = -1;
double mEnd = -1;
public void addRectangle( double startInPercent, double endInPercent ) {
if( startInPercent < 0 || endInPercent > 1 || endInPercent > startInPercent )
return;
mStart = startInPercent;
mEnd = endInPercent;
//this will make the view to refresh the UI
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if( mStart >= 0 && mEnd >= 0)
canvas.drawRect(0, getHeight() * mStart, getWidth(), getHeight() * mEnd, mPaint);
}
this code will draw a rectangle as specified in the addRectangle() method. In my implementation I intend the parameters of that function to per % of the Height of the view.
here is the documentation of that drawRect( ... ) call. Changing the parameters you can draw at the top, bottom left and right as you prefer.
In order to get the color you want you have to init mPaint in the view constructor like this:
Paint mPaint = new Paint();
mPaint.setColor( Color.RED );
of course this is the dumbest code possible, you can play around this 2 concept to basically get what you want.
the main class to do this ( and almost everything in the Andorid UI ) are:
Canvas
View
Paint

Android Circle button

How I can to make custom circle button? (only make clicks on circle )
Is any way to make it with circle png file?
I tried with imageView override onTouch method but it works very badly because view.getWidth(), view.getHeight() and view.getTop... methods works very bad
public boolean inCircle(MotionEvent e, int radius, int x, int y) {
int dx = (int) (e.getX() - x);
int dy = (int) (e.getY() - y);
double d = Math.sqrt((dx * dx) + (dy * dy));
if (d < radius)
return true;
return false;
}
Thanks.
Its very simple. Create a custom shape drawable and set that as the background of your view. Example:
round_button_drawable.xml in drawable/
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
<solid android:color="#android:color/holo_orange_dark"/>
</shape>
set this drawable as the background of any view.
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/round_button_drawable"
android:text="btn"
/>
Another way would be to extend Button and override its onDraw method
You can then draw a circle on the canvas using canvas.drawCircle method.
You can also draw the circle.png file on the canvas using Drawable.draw method
Dont go for complex logic just select any rounded image and set that image as a background of your simple button it will look like simple round button and it will accept click only on that round shape.
there is ImageButton class which can serve ur purpose..

Unexpected LayerDrawable behavior when drawing layers containing InsetDrawable's

I am trying to build a LayerDrawable in xml where upper layers will occasionally completely obscure lower layers. To make the lower layers smaller, I am using an InsetDrawable to wrap another drawable to make it smaller than the full size of the view. I find unexpectedly, however, that any layers placed on top of the layer containing the inset also has the inset applied to it. I can't find documentation supporting this behavior, and am confused why this would be the case.
In the example below, I make a LayerDrawable with 3 layers. The bottom and top layers contain oval shape drawables that are meant to take up the entire view. The middle layer is a rectangle drawable inside of an InsetDrawable. The code is below:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape android:shape="oval" >
<solid android:color="#00ff00" />
</shape>
</item>
<item>
<inset
android:insetBottom="4dp"
android:insetLeft="4dp"
android:insetRight="4dp"
android:insetTop="4dp" >
<shape android:shape="rectangle" >
<solid android:color="#ff0000" />
</shape>
</inset>
</item>
<item>
<shape android:shape="oval" >
<solid android:color="#0000ff" />
</shape>
</item>
</layer-list>
Calling setBackgroundDrawable(getResources().getDrawable(drawableId)); in my view produces a green oval that fills the entire view as expected, with a red rectangle inset 4dp as expected, but the blue oval on the top layer is also inset 4dp and drawn completely within the bounds of the red rectangle.
I would expect the blue oval to completely obscure the green oval and most of the red rectangle, but instead it is inset inside the red rectangle. Is there any way to make the blue circle fill the view yet keep it on top?
I also don't see where it is documented, but padding in a LayerDrawable is cumulative. That is, padding at one layer affects the bounds of all higher layers. This is from the source for LayerDrawable:
#Override
protected void onBoundsChange(Rect bounds) {
final ChildDrawable[] array = mLayerState.mChildren;
final int N = mLayerState.mNum;
int padL=0, padT=0, padR=0, padB=0;
for (int i=0; i<N; i++) {
final ChildDrawable r = array[i];
r.mDrawable.setBounds(bounds.left + r.mInsetL + padL,
bounds.top + r.mInsetT + padT,
bounds.right - r.mInsetR - padR,
bounds.bottom - r.mInsetB - padB);
padL += mPaddingL[i];
padR += mPaddingR[i];
padT += mPaddingT[i];
padB += mPaddingB[i];
}
}
(LayerDrawable.getPadding(Rect) follows the same logic.) Since an InsetDrawable uses its insets as padding (as documented), this explains the behavior you're seeing.
I think this is a poor design decision, but you're kind of stuck with it, I'm afraid. I don't think it can be overridden.
Ted's answer is the best answer, but I'll share this workaround that helped me. I was specifically having padding problems with a TextView, so I made a custom TextView instead which ignores the background drawable's padding.
public class HackTextView extends TextView {
public HackTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public HackTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HackTextView(Context context) {
super(context);
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
#Override
public void setBackground(Drawable background) {
super.setBackground(hackDrawable(background));
}
#Override
public void setBackgroundDrawable(Drawable background) {
super.setBackgroundDrawable(hackDrawable(background));
}
private Drawable hackDrawable(Drawable background){
return new LayerDrawable(new Drawable[]{background}){
#Override
public boolean getPadding(Rect padding) {
padding.set(0, 0, 0, 0);
return false;
}
};
}
}

Categories

Resources