Related
[Please note that I use Xamarin.Droid which is a cross-platform framework using Mono.NET C#, the code is really close to Android's Java and I do accept Java responses as it is easy to translate to C# ]
I subclassed a Button and I'm applying a shape with a color and a leftDrawable. Everything is working fine except the fact that I have lost the Ripple effect when the button is pressed.
I've tried to add ?attr/selectableItemBackground but with code since I don't have any XML for this subclass, and the ripple effect still doesn't show up.
Here's my button subclass
public class LTButton : Android.Support.V7.Widget.AppCompatButton
{
Context context;
public LTButton(Context pContext, IAttributeSet attrs) : base(pContext, attrs)
{
context = pContext;
Elevation = 0;
}
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
Init();
}
void Init()
{
// Adding a left drawable
Drawable img = ContextCompat.GetDrawable(context, Resource.Drawable.chevron_right);
img.SetBounds(0, 0, 70, 70);
SetCompoundDrawables(img, null, null, null);
TextSize = 16;
// The shape with background color and rounded corners
var shape = new GradientDrawable();
// This is a full rounded button
shape.SetCornerRadius(Height / 2);
shape.SetColor(ContextCompat.GetColor(context, Resource.Color.LTGreen));
Background = shape;
// Foreground => To have a ripple effect
int[] attrs = { Android.Resource.Attribute.SelectableItemBackground };
TypedArray ta = context.ObtainStyledAttributes(attrs);
Drawable selectableDrawable = ta.GetDrawable(0);
ta.Recycle();
Foreground = selectableDrawable;
}
}
Create an XML file for a ripple drawable something like this :
my_button_background_v21.xml
<?xml version="1.0" encoding="UTF-8" ?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:color="#color/black"
tools:targetApi="lollipop">
<item>
<shape android:shape="rectangle">
<corners android:radius="100dp" />
<solid android:color="#color/LTGreen" />
</shape>
</item>
<item android:id="#android:id/mask">
<shape android:shape="rectangle">
<corners android:radius="100dp" />
<solid android:color="#color/black" />
</shape>
</item>
Then in your custom button's onDraw method add the ripple effect something like this:
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
Drawable img = ContextCompat.GetDrawable(context, Resource.Drawable.chevron_right);
img.SetBounds(0, 0, 70, 70);
SetCompoundDrawables(img, null, null, null);
TextSize = 16;
if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop)
{
SetBackgroundResource(Resource.Layout.my_button_background);
}
else
{
SetBackgroundResource(Resource.Layout.my_button_background_v21);
}
}
Use java you can do like this:
Custom a RippleButton
public class RippleButton extends Button {
private RippleDrawable rippleDrawable;
private Paint paint;
public RippleButton(Context context) {
this(context,null);
}
public RippleButton(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public RippleButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paint = new Paint();
rippleDrawable = new RippleDrawable();
//Set refresh interface, View has been implemented ---> source button inheritance child drawable.callback
rippleDrawable.setCallback(this);
//rippleDrawable
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//Set the refresh area ---> source code
rippleDrawable.setBounds(0,0,getWidth(),getHeight());
}
#Override
protected boolean verifyDrawable(Drawable who) {
return who == rippleDrawable || super.verifyDrawable(who);
}
#Override
protected void onDraw(Canvas canvas) {
rippleDrawable.draw(canvas);
super.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
rippleDrawable.onTouch(event);
return true;
}
}
Custom a RippleDrawable :
public class RippleDrawable extends Drawable {
private Paint mPaint;
private Bitmap bitmap;
private int rippleColor;
private float mRipplePointX = 0;
private float mRipplePointY = 0;
private float mRippleRadius = 0;
private int mAlpha = 200;
private float mCenterPointX,mCenterPointY;
private float mClickPointX;
private float mClickPointY;
//Maximum radius
private float MaxRadius;
//Starting radius
private float startRadius;
//End radius
private float endRadius;
//Record whether to raise your finger--->boolean
private boolean mUpDone;
//Record whether the animation is finished
private boolean mEnterDone;
/**Enter animation*/
//Enter the progress value of the animation
private float mProgress;
//Each incremental time
private float mEnterIncrement = 16f/360;
//Enter the animation add interpolator
private DecelerateInterpolator mEnterInterpolator = new DecelerateInterpolator(2);
private Runnable runnable = new Runnable() {
#Override
public void run() {
mEnterDone = false;
mCircleAlpha = 255;
mProgress = mProgress + mEnterIncrement;
if (mProgress > 1){
onEnterPrograss(1);
enterDone();
return;
}
float interpolation = mEnterInterpolator.getInterpolation(mProgress);
onEnterPrograss(interpolation);
scheduleSelf(this, SystemClock.uptimeMillis() + 16);
}
};
/**How to enter the animation refresh
* #parms realProgress */
public void onEnterPrograss(float realPrograss){
mRippleRadius = getCenter(startRadius,endRadius,realPrograss);
mRipplePointX = getCenter(mClickPointX,mCenterPointX,realPrograss);
mRipplePointY = getCenter(mClickPointY,mCenterPointY,realPrograss);
mBgAlpha = (int) getCenter(0,182,realPrograss);
invalidateSelf();
}
private void enterDone() {
mEnterDone = true;
if(mUpDone)
startExitRunnable();
}
/**Exit animation*/
//Exit the progress value of the animation
private float mExitProgress;
//Each incremental time
private float mExitIncrement = 16f/280;
//Exit animation add interpolator
private AccelerateInterpolator mExitInterpolator = new AccelerateInterpolator(2);
private Runnable exitRunnable = new Runnable() {
#Override
public void run() {
if (!mEnterDone){
return;
}
mExitProgress = mExitProgress + mExitIncrement;
if (mExitProgress > 1){
onExitPrograss(1);
exitDone();
return;
}
float interpolation = mExitInterpolator.getInterpolation(mExitProgress);
onExitPrograss(interpolation);
scheduleSelf(this, SystemClock.uptimeMillis() + 16);
}
};
/**Exit animation refresh method
* #parms realProgress */
public void onExitPrograss(float realPrograss){
//Set background
mBgAlpha = (int) getCenter(182,0,realPrograss);
//Set the circular area
mCircleAlpha = (int) getCenter(255,0,realPrograss);
invalidateSelf();
}
private void exitDone() {
mEnterDone = false;
}
//Set the gradient effect including radius / bg color / center position, etc.
public float getCenter(float start,float end,float prograss){
return start + (end - start)*prograss;
}
public RippleDrawable() {
//Anti-aliased brush
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//Set anti-aliasing
mPaint.setAntiAlias(true);
//Set anti-shake
mPaint.setDither(true);
setRippleColor(0x60000000);
}
//private int mPaintAlpha = 255;
//Background transparency
private int mBgAlpha;
//Transparency of the circular area
private int mCircleAlpha;
#Override
public void draw(Canvas canvas) {
//Get the transparency of user settings
int preAlpha = mPaint.getAlpha();
//current background
int bgAlpha = (int) (preAlpha * (mBgAlpha/255f));
//bg + prebg Operational background
int maxCircleAlpha = getCircleAlpha(preAlpha,bgAlpha);
int circleAlpha = (int) (maxCircleAlpha * (mCircleAlpha/255f));
mPaint.setAlpha(bgAlpha);
canvas.drawColor(mPaint.getColor());
mPaint.setAlpha(circleAlpha);
canvas.drawCircle(mRipplePointX,mRipplePointY,mRippleRadius,mPaint);
//Set the initial transparency to ensure that the next entry into the operation will not go wrong
mPaint.setAlpha(preAlpha);
}
public int getCircleAlpha(int preAlpha,int bgAlpha){
int dAlpha = preAlpha - bgAlpha;
return (int) ((dAlpha*255f)/(255f - bgAlpha));
}
public void onTouch(MotionEvent event){
switch (event.getActionMasked()){
case MotionEvent.ACTION_DOWN:
//press
mClickPointX = event.getX();
mClickPointY = event.getY();
onTouchDown(mClickPointX, mClickPointY);
break;
case MotionEvent.ACTION_MOVE:
//move
//onTouchMove(moveX,moveY);
break;
case MotionEvent.ACTION_UP:
//up
onTouchUp();
break;
case MotionEvent.ACTION_CANCEL:
//exit
//onTouchCancel();
break;
}
}
public void onTouchDown(float x,float y){
//Log.i("onTouchDown====",x + "" + y );
//unscheduleSelf(runnable);
mUpDone = false;
mRipplePointX = x;
mRipplePointY = y;
mRippleRadius = 0;
startEnterRunnable();
}
public void onTouchMove(float x,float y){
}
public void onTouchUp(){
mUpDone = true;
if (mEnterDone){
startExitRunnable();
}
}
public void onTouchCancel(){
mUpDone = true;
if (mEnterDone){
startExitRunnable();
}
}
/**
* Open to enter animation
* */
public void startEnterRunnable(){
mProgress = 0;
//mEnterDone = false;
unscheduleSelf(exitRunnable);
unscheduleSelf(runnable);
scheduleSelf(runnable,SystemClock.uptimeMillis());
}
/**
* Open exit animation
* */
public void startExitRunnable(){
mExitProgress = 0;
unscheduleSelf(runnable);
unscheduleSelf(exitRunnable);
scheduleSelf(exitRunnable,SystemClock.uptimeMillis());
}
public int changeColorAlpha(int color,int alpha){
//Set transparency
int a = (color >> 24) & 0xFF;
a = a * alpha/255;
int red = Color.red(color);
int green = Color.green(color);
int blue = Color.blue(color);
int argb = Color.argb(a, red, green, blue);
return argb;
}
//Take the center point of the drawn area
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mCenterPointX = bounds.centerX();
mCenterPointY = bounds.centerY();
MaxRadius = Math.max(mCenterPointX,mCenterPointY);
startRadius = MaxRadius * 0.1f;
endRadius = MaxRadius * 0.8f;
}
#Override
public void setAlpha(int alpha) {
mAlpha = alpha;
onColorOrAlphaChange();
}
#Override
public int getAlpha() {
return mAlpha;
}
#Override
public void setColorFilter(ColorFilter colorFilter) {
//Filter effect
if (mPaint.getColorFilter() != colorFilter){
mPaint.setColorFilter(colorFilter);
invalidateSelf();
}
}
#Override
public int getOpacity() {
//Return transparency
if (mAlpha == 255){
return PixelFormat.OPAQUE;
}else if (mAlpha == 0){
return PixelFormat.TRANSPARENT;
}else{
return PixelFormat.TRANSLUCENT;
}
}
public void setRippleColor(int rippleColor) {
this.rippleColor = rippleColor;
onColorOrAlphaChange();
}
private void onColorOrAlphaChange() {
//Set brush color
mPaint.setColor(rippleColor);
if (mAlpha != 255){
int pAlpha = mPaint.getAlpha();
int realAlpha = (int) (pAlpha * (mAlpha/255f));
//Set transparency
mPaint.setAlpha(realAlpha);
}
invalidateSelf();
}
}
If be helpful ,please mark answer to help others.
Thanks to #G.Hakim and this thread, I was able to reproduce my background, and add the ripple effect with just XML.
my_button_background.xml
<?xml version="1.0" encoding="UTF-8" ?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:color="#color/black"
tools:targetApi="lollipop">
<item>
<shape android:shape="rectangle">
<corners android:radius="100dp" />
<solid android:color="#color/LTGreen" />
</shape>
</item>
<item android:id="#android:id/mask">
<shape android:shape="rectangle">
<corners android:radius="100dp" />
<solid android:color="#color/black" />
</shape>
</item>
</ripple>
MyButton.cs
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
Drawable img = ContextCompat.GetDrawable(context, Resource.Drawable.chevron_right);
img.SetBounds(0, 0, 70, 70);
SetCompoundDrawables(img, null, null, null);
TextSize = 16;
if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop)
{
SetBackgroundResource(Resource.Layout.my_button_background);
}
else
{
SetBackgroundResource(Resource.Layout.my_button_background_v21);
}
}
i want to custom progress bar in circle shape like same as download blazer app in google play shop.i am expected like below screen shot:
any one help me how to do that?
The best two libraries I found on the net are on github:
https://github.com/Todd-Davies/ProgressWheel
https://github.com/f2prateek/progressbutton?source=c
Hope that will help you
I've encountered same problem and not found any appropriate solution for my case, so I decided to go another way. I've created custom drawable class. Within this class I've created 2 Paints for progress line and background line (with some bigger stroke). First of all set startAngle and sweepAngle in constructor:
mSweepAngle = 0;
mStartAngle = 270;
Here is onDraw method of this class:
#Override
public void draw(Canvas canvas) {
// draw background line
canvas.drawArc(mRectF, 0, 360, false, mPaintBackground);
// draw progress line
canvas.drawArc(mRectF, mStartAngle, mSweepAngle, false, mPaintProgress);
}
So now all you need to do is set this drawable as a backgorund of the view, in background thread change sweepAngle:
mSweepAngle += 360 / totalTimerTime // this is mStep
and directly call InvalidateSelf() with some interval (e.g every 1 second or more often if you want smooth progress changes) on the view that have this drawable as a background. Thats it!
P.S. I know, I know...of course you want some more code. So here it is all flow:
Create XML view :
<View
android:id="#+id/timer"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Create and configure Custom Drawable class (as I described above). Don't forget to setup Paints for lines. Here paint for progress line:
mPaintProgress = new Paint();
mPaintProgress.setAntiAlias(true);
mPaintProgress.setStyle(Paint.Style.STROKE);
mPaintProgress.setStrokeWidth(widthProgress);
mPaintProgress.setStrokeCap(Paint.Cap.ROUND);
mPaintProgress.setColor(colorThatYouWant);
Same for backgroung paint (set width little more if you want)
In drawable class create method for updating (Step calculation described above)
public void update() {
mSweepAngle += mStep;
invalidateSelf();
}
Set this drawable class to YourTimerView (I did it in runtime) - view with #+id/timer from xml above:
OurSuperDrawableClass superDrawable = new OurSuperDrawableClass();
YourTimerView.setBackgroundDrawable(superDrawable);
Create background thread with runnable and update view:
YourTimerView.post(new Runnable() {
#Override
public void run() {
// update progress view
superDrawable.update();
}
});
Thats it ! Enjoy your cool progress bar. Here screenshot of result if you're too bored of this amount of text.
for more information on How to create Circle Android Custom Progress Bar view this link
Step 01
You should create an xml file on drawable file for configure the appearance of progress bar . So Im creating my xml file as circular_progress_bar.xml.
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="120"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="140">
<item android:id="#android:id/background">
<shape
android:innerRadiusRatio="3"
android:shape="ring"
android:useLevel="false"
android:angle="0"
android:type="sweep"
android:thicknessRatio="50.0">
<solid android:color="#000000"/>
</shape>
</item>
<item android:id="#android:id/progress">
<rotate
android:fromDegrees="120"
android:toDegrees="120">
<shape
android:innerRadiusRatio="3"
android:shape="ring"
android:angle="0"
android:type="sweep"
android:thicknessRatio="50.0">
<solid android:color="#ffffff"/>
</shape>
</rotate>
</item>
</layer-list>
Step 02
Then create progress bar on your xml file
Then give the name of xml file on your drawable folder as the parth of android:progressDrawable
<ProgressBar
android:id="#+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_marginLeft="0dp"
android:layout_centerHorizontal="true"
android:indeterminate="false"
android:max="100"
android:progressDrawable="#drawable/circular_progress_bar" />
Step 03
Visual the progress bar using thread
package com.example.progress;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.view.Menu;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity {
private ProgressBar progBar;
private TextView text;
private Handler mHandler = new Handler();
private int mProgressStatus=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progBar= (ProgressBar)findViewById(R.id.progressBar);
text = (TextView)findViewById(R.id.textView1);
dosomething();
}
public void dosomething() {
new Thread(new Runnable() {
public void run() {
final int presentage=0;
while (mProgressStatus < 63) {
mProgressStatus += 1;
// Update the progress bar
mHandler.post(new Runnable() {
public void run() {
progBar.setProgress(mProgressStatus);
text.setText(""+mProgressStatus+"%");
}
});
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
A very useful lib for custom progress bar in android.
In your layout file
<com.lylc.widget.circularprogressbar.example.CircularProgressBar
android:id="#+id/mycustom_progressbar"
.
.
.
/>
and Java file
CircularProgressBar progressBar = (CircularProgressBar) findViewById(R.id.mycustom_progressbar);
progressBar.setTitle("Circular Progress Bar");
Try this piece of code to create circular progress bar(pie chart). pass it integer value to draw how many percent of filling area. :)
private void circularImageBar(ImageView iv2, int i) {
Bitmap b = Bitmap.createBitmap(300, 300,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(b);
Paint paint = new Paint();
paint.setColor(Color.parseColor("#c4c4c4"));
paint.setStrokeWidth(10);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(150, 150, 140, paint);
paint.setColor(Color.parseColor("#FFDB4C"));
paint.setStrokeWidth(10);
paint.setStyle(Paint.Style.FILL);
final RectF oval = new RectF();
paint.setStyle(Paint.Style.STROKE);
oval.set(10,10,290,290);
canvas.drawArc(oval, 270, ((i*360)/100), false, paint);
paint.setStrokeWidth(0);
paint.setTextAlign(Align.CENTER);
paint.setColor(Color.parseColor("#8E8E93"));
paint.setTextSize(140);
canvas.drawText(""+i, 150, 150+(paint.getTextSize()/3), paint);
iv2.setImageBitmap(b);
}
I have solved this cool custom progress bar by creating the custom view. I have overriden the onDraw() method to draw the circles, filled arc and text on the canvas.
following is the custom progress bar
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import com.investorfinder.utils.UiUtils;
public class CustomProgressBar extends View {
private int max = 100;
private int progress;
private Path path = new Path();
int color = 0xff44C8E5;
private Paint paint;
private Paint mPaintProgress;
private RectF mRectF;
private Paint textPaint;
private String text = "0%";
private final Rect textBounds = new Rect();
private int centerY;
private int centerX;
private int swipeAndgle = 0;
public CustomProgressBar(Context context) {
super(context);
initUI();
}
public CustomProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
initUI();
}
public CustomProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initUI();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initUI();
}
private void initUI() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(UiUtils.dpToPx(getContext(), 1));
paint.setStyle(Paint.Style.STROKE);
paint.setColor(color);
mPaintProgress = new Paint();
mPaintProgress.setAntiAlias(true);
mPaintProgress.setStyle(Paint.Style.STROKE);
mPaintProgress.setStrokeWidth(UiUtils.dpToPx(getContext(), 9));
mPaintProgress.setColor(color);
textPaint = new Paint();
textPaint.setAntiAlias(true);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setColor(color);
textPaint.setStrokeWidth(2);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int viewWidth = MeasureSpec.getSize(widthMeasureSpec);
int viewHeight = MeasureSpec.getSize(heightMeasureSpec);
int radius = (Math.min(viewWidth, viewHeight) - UiUtils.dpToPx(getContext(), 2)) / 2;
path.reset();
centerX = viewWidth / 2;
centerY = viewHeight / 2;
path.addCircle(centerX, centerY, radius, Path.Direction.CW);
int smallCirclRadius = radius - UiUtils.dpToPx(getContext(), 7);
path.addCircle(centerX, centerY, smallCirclRadius, Path.Direction.CW);
smallCirclRadius += UiUtils.dpToPx(getContext(), 4);
mRectF = new RectF(centerX - smallCirclRadius, centerY - smallCirclRadius, centerX + smallCirclRadius, centerY + smallCirclRadius);
textPaint.setTextSize(radius * 0.5f);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path, paint);
canvas.drawArc(mRectF, 270, swipeAndgle, false, mPaintProgress);
drawTextCentred(canvas);
}
public void drawTextCentred(Canvas canvas) {
textPaint.getTextBounds(text, 0, text.length(), textBounds);
canvas.drawText(text, centerX - textBounds.exactCenterX(), centerY - textBounds.exactCenterY(), textPaint);
}
public void setMax(int max) {
this.max = max;
}
public void setProgress(int progress) {
this.progress = progress;
int percentage = progress * 100 / max;
swipeAndgle = percentage * 360 / 100;
text = percentage + "%";
invalidate();
}
public void setColor(int color) {
this.color = color;
}
}
In layout XML
<com.your.package.name.CustomProgressBar
android:id="#+id/progress_bar"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_alignParentRight="true"
android:layout_below="#+id/txt_title"
android:layout_marginRight="15dp" />
in activity
CustomProgressBar progressBar = (CustomProgressBar)findViewById(R.id.progress_bar);
progressBar.setMax(9);
progressBar.setProgress(5);
I'd make a new view class and derive from the existing ProgressBar. Then override the onDraw function. You're going to need to make direct draw calls to the canvas for this, since its so custom- a combination of drawText, drawArc, and drawOval should do it- an oval for the outer ring and empty portions, and an arc for the colored in parts. You may end up needing to override onMeasure and onLayout as well. Then in your xml, reference this view by class name like this when you want to use it.
I did a simple class which u can use to make custom ProgressBar dialog.
Actually it has 2 default layouts:
- First dialog with no panel with progress bar and animated text centered over it
- Second normal dialog with panel, progress bar, title and msg
It is just a class, so not a customizable library which u can import in your project, so you need to copy it and change it how you want.
It is a DialogFragment class, but you can use it inside an activity as a normal fragment just like you do with classic fragment by using FragmentManager.
Code of the dialog class:
public class ProgressBarDialog extends DialogFragment {
private static final String TAG = ProgressBarDialog.class.getSimpleName();
private static final String KEY = TAG.concat(".key");
// Argument Keys
private static final String KEY_DIALOG_TYPE = KEY.concat(".dialogType");
private static final String KEY_TITLE = KEY.concat(".title");
private static final String KEY_PROGRESS_TEXT = KEY.concat(".progressText");
private static final String KEY_CUSTOM_LAYOUT_BUILDER = KEY.concat(".customLayoutBuilder");
// Class Names
private static final String CLASS_GRADIENT_STATE = "GradientState";
// Field Names
private static final String FIELD_THICKNESS = "mThickness";
private static final String FIELD_INNER_RADIUS = "mInnerRadius";
/** Dialog Types **/
private static final int TYPE_PROGRESS_BAR_ONLY_NO_ANIM = 0x0;
private static final int TYPE_PROGRESS_BAR_ONLY_ROTATE_ANIM = 0x1;
private static final int TYPE_PROGRESS_BAR_ONLY_FADE_ANIM = 0x2;
private static final int TYPE_PROGRESS_BAR_AND_MSG = 0xF;
/** Animations Values **/
private static final long CENTER_TEXT_VIEWS_ANIMATION_DURATION = 250L;
private static final long CENTER_TEXT_VIEWS_START_OFFSET_MULTIPLIER = 250L;
private MaterialProgressBar mProgressBar;
private LinearLayout mllTextContainer;
private TextView mtvTitle;
private TextView mtvProgressText;
private List<TextView> mCenterTextViews;
private int mDialogType;
private String mTitle;
private String mProgressText;
private CustomLayoutBuilder mCustomLayoutBuilder;
/** Public Static Factory Methods **/
public static ProgressBarDialog initLayoutProgressBarOnlyNoAnim(String text, CustomLayoutBuilder builder){
return initLayoutProgressBarOnly(TYPE_PROGRESS_BAR_ONLY_NO_ANIM, text, builder);
}
public static ProgressBarDialog initLayoutProgressBarOnlyRotateAnim(String text, CustomLayoutBuilder builder){
return initLayoutProgressBarOnly(TYPE_PROGRESS_BAR_ONLY_ROTATE_ANIM, text, builder);
}
public static ProgressBarDialog initLayoutProgressBarOnlyFadeAnim(String text, CustomLayoutBuilder builder){
return initLayoutProgressBarOnly(TYPE_PROGRESS_BAR_ONLY_FADE_ANIM, text, builder);
}
public static ProgressBarDialog initLayoutProgressBarAndMsg(String title, String text, CustomLayoutBuilder builder){
ProgressBarDialog mInstance = new ProgressBarDialog();
Bundle args = new Bundle();
args.putInt(KEY_DIALOG_TYPE, TYPE_PROGRESS_BAR_AND_MSG);
args.putString(KEY_TITLE, title);
args.putString(KEY_PROGRESS_TEXT, text);
args.putParcelable(KEY_CUSTOM_LAYOUT_BUILDER, builder);
mInstance.setArguments(args);
return mInstance;
}
/** Private Static Factory Methods **/
private static ProgressBarDialog initLayoutProgressBarOnly(int animation, String text, CustomLayoutBuilder builder){
ProgressBarDialog mInstance = new ProgressBarDialog();
Bundle args = new Bundle();
args.putInt(KEY_DIALOG_TYPE, animation);
args.putString(KEY_PROGRESS_TEXT, text);
args.putParcelable(KEY_CUSTOM_LAYOUT_BUILDER, builder);
mInstance.setArguments(args);
return mInstance;
}
/** Override Lifecycle Methods **/
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initData();
}
#Override #Nullable
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.dialog_progress_bar, container, false);
initViews(view);
if(getContext() != null && mCustomLayoutBuilder != null) {
mProgressBar.setIndeterminateDrawable(getResources().getDrawable(mCustomLayoutBuilder.getLayoutResID()));
initShapes();
}
return view;
}
private void initShapes(){
if(mProgressBar.getIndeterminateDrawable() instanceof LayerDrawable) {
LayerDrawable layerDrawable = (LayerDrawable) mProgressBar.getIndeterminateDrawable();
for (int i = 0; i < layerDrawable.getNumberOfLayers(); i++) {
if(layerDrawable.getDrawable(i) instanceof RotateDrawable) {
RotateDrawable rotateDrawable = (RotateDrawable) layerDrawable.getDrawable(i);
int[] fromToDeg = mCustomLayoutBuilder.getDegreesMatrixRow(i);
if(fromToDeg.length > 0){
rotateDrawable.setFromDegrees(fromToDeg[CustomLayoutBuilder.INDEX_FROM_DEGREES]);
rotateDrawable.setToDegrees(fromToDeg[CustomLayoutBuilder.INDEX_TO_DEGREES]);
}
if(rotateDrawable.getDrawable() instanceof GradientDrawable){
GradientDrawable gradientDrawable = (GradientDrawable) rotateDrawable.getDrawable();
int innerRadius = getResources().getDimensionPixelSize(mCustomLayoutBuilder.getInnerRadius(i));
if(mDialogType == TYPE_PROGRESS_BAR_AND_MSG){
innerRadius /= 3;
}
int thickness = getResources().getDimensionPixelSize(mCustomLayoutBuilder.getThickness(i));
int[] colors = mCustomLayoutBuilder.getColorsMatrixRow(i);
if(colors.length > 0x0){
gradientDrawable.setColors(DataUtils.resourcesIDsToColors(this.getContext(), colors));
}
if(innerRadius != -0x1){
DataUtils.setSubClassFieldIntValue(gradientDrawable.getConstantState(), gradientDrawable.getClass(), CLASS_GRADIENT_STATE, FIELD_INNER_RADIUS, innerRadius);
}
if(thickness != -0x1){
DataUtils.setSubClassFieldIntValue(gradientDrawable.getConstantState(), gradientDrawable.getClass(), CLASS_GRADIENT_STATE, FIELD_THICKNESS, thickness);
}
}
}
}
}
}
#Override
public void onStart() {
super.onStart();
setWindowSize();
startAnimation();
}
/** Public Methods **/
public void changeTextViews(String progressText){
mProgressText = progressText;
initTextViews();
startAnimation();
}
public String getProgressText(){
return mProgressText;
}
/** Private Methods **//** Init Methods **/
private void initData(){
if(getArguments() != null) {
if (getArguments().containsKey(KEY_DIALOG_TYPE)) {
mDialogType = getArguments().getInt(KEY_DIALOG_TYPE);
}
if(getArguments().containsKey(KEY_TITLE)){
mTitle = getArguments().getString(KEY_TITLE);
}
if (getArguments().containsKey(KEY_PROGRESS_TEXT)) {
mProgressText = getArguments().getString(KEY_PROGRESS_TEXT);
}
if (getArguments().containsKey(KEY_CUSTOM_LAYOUT_BUILDER)){
mCustomLayoutBuilder = getArguments().getParcelable(KEY_CUSTOM_LAYOUT_BUILDER);
}
}
mCenterTextViews = new ArrayList<>();
}
private void initViews(View layout){
if(layout != null){
switch(mDialogType){
case TYPE_PROGRESS_BAR_ONLY_NO_ANIM:
case TYPE_PROGRESS_BAR_ONLY_ROTATE_ANIM:
case TYPE_PROGRESS_BAR_ONLY_FADE_ANIM:
if(getDialog() != null && getDialog().getWindow() != null) {
getDialog().getWindow().setBackgroundDrawableResource(android.R.color.transparent);
}
LinearLayout mLayoutProgressBarOnly = layout.findViewById(R.id.layout_progress_bar_only);
mLayoutProgressBarOnly.setVisibility(LinearLayout.VISIBLE);
mProgressBar = layout.findViewById(R.id.dpb_progress_bar);
if(mCustomLayoutBuilder.getProgressBarWidthDimen() != -0x1){
ConstraintLayout.LayoutParams lp = (ConstraintLayout.LayoutParams) mProgressBar.getLayoutParams();
lp.width = getResources().getDimensionPixelSize(mCustomLayoutBuilder.getProgressBarWidthDimen());
lp.height = getResources().getDimensionPixelSize(mCustomLayoutBuilder.getProgressBarHeightDimen());
mProgressBar.setLayoutParams(lp);
}
mllTextContainer = layout.findViewById(R.id.dpb_text_container);
initTextViews();
break;
case TYPE_PROGRESS_BAR_AND_MSG:
LinearLayout mLayoutProgressBarAndMsg = layout.findViewById(R.id.layout_progress_bar_and_msg);
mLayoutProgressBarAndMsg.setVisibility(LinearLayout.VISIBLE);
mProgressBar = layout.findViewById(R.id.dpb_progress_bar_and_msg);
mtvTitle = layout.findViewById(R.id.pbd_title);
mtvProgressText = layout.findViewById(R.id.dpb_progress_msg);
initProgressMsg();
break;
}
}
}
private void initTextViews(){
if(!TextUtils.isEmpty(mProgressText)){
clearTextContainer();
for(char digit : mProgressText.toCharArray()){
TextView tv = new TextView(getContext(), null, 0x0, R.style.PBDCenterTextStyleWhite);
if(mCustomLayoutBuilder.getProgressMsgColor() != CustomLayoutBuilder.DEFAULT_COLOR && getContext() != null){
tv.setTextColor(ActivityCompat.getColor(getContext(), mCustomLayoutBuilder.getProgressMsgColor()));
}
if(mCustomLayoutBuilder.getProgressMsgDimen() != CustomLayoutBuilder.DEFAULT_DIMEN){
tv.setTextSize(getResources().getDimension(mCustomLayoutBuilder.getProgressMsgDimen()));
}
tv.setText(String.valueOf(digit));
mCenterTextViews.add(tv);
mllTextContainer.addView(tv);
}
}
}
private void initProgressMsg(){
mtvTitle.setText(mTitle);
mtvProgressText.setText(mProgressText);
if(mCustomLayoutBuilder.getProgressMsgColor() != CustomLayoutBuilder.DEFAULT_COLOR){
mtvProgressText.setTextColor(mCustomLayoutBuilder.getProgressMsgColor());
}
if(mCustomLayoutBuilder.getProgressMsgDimen() != CustomLayoutBuilder.DEFAULT_DIMEN){
mtvProgressText.setTextSize(getResources().getDimension(mCustomLayoutBuilder.getProgressMsgDimen()));
}
}
private void clearTextContainer(){
if(mllTextContainer.getChildCount() >= 0x0){
for(int i=0; i < mllTextContainer.getChildCount(); i++){
View v = mllTextContainer.getChildAt(i);
if(v instanceof TextView){
TextView tv = (TextView) v;
if(tv.getAnimation() != null){
tv.clearAnimation();
}
}
}
}
mllTextContainer.removeAllViews();
}
private void setWindowSize(){
Dialog dialog = getDialog();
if(dialog != null && dialog.getWindow() != null){
int width = 0x0, height = 0x0;
switch(mDialogType){
case TYPE_PROGRESS_BAR_ONLY_NO_ANIM:
case TYPE_PROGRESS_BAR_ONLY_ROTATE_ANIM:
case TYPE_PROGRESS_BAR_ONLY_FADE_ANIM:
width = ViewGroup.LayoutParams.WRAP_CONTENT; //getResources().getDimensionPixelSize(R.dimen.pbd_window_width);
height = ViewGroup.LayoutParams.WRAP_CONTENT; //getResources().getDimensionPixelSize(R.dimen.pbd_window_height);
break;
case TYPE_PROGRESS_BAR_AND_MSG:
width = ViewGroup.LayoutParams.MATCH_PARENT;
height = ViewGroup.LayoutParams.WRAP_CONTENT; //getResources().getDimensionPixelSize(R.dimen.pbd_textual_window_height);
break;
}
dialog.getWindow().setLayout(width, height);
}
}
/** Animation Methods **/
private void startAnimation(){
switch(mDialogType){
case TYPE_PROGRESS_BAR_ONLY_ROTATE_ANIM:
startRotateAnimations();
break;
case TYPE_PROGRESS_BAR_ONLY_FADE_ANIM:
startFadeInAnimations();
break;
}
}
private void startRotateAnimations(){
for(TextView tv : mCenterTextViews){
if(tv != null && tv.getText() != null && !TextUtils.isEmpty(tv.getText().toString().trim())) {
int i = mCenterTextViews.indexOf(tv);
RotateAnimation anim = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(CENTER_TEXT_VIEWS_ANIMATION_DURATION);
anim.setFillAfter(true);
anim.setStartOffset(CENTER_TEXT_VIEWS_START_OFFSET_MULTIPLIER * i);
if (i == (mCenterTextViews.size() - 0x1)) {
anim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
startRotateAnimations();
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
}
tv.startAnimation(anim);
}
}
}
private void startFadeInAnimations(){
for(TextView tv : mCenterTextViews){
if(tv != null && tv.getText() != null && !TextUtils.isEmpty(tv.getText().toString().trim())) {
int i = mCenterTextViews.indexOf(tv);
AlphaAnimation anim = new AlphaAnimation(0x1, 0x0);
anim.setDuration(CENTER_TEXT_VIEWS_ANIMATION_DURATION);
anim.setFillAfter(true);
anim.setStartOffset(CENTER_TEXT_VIEWS_START_OFFSET_MULTIPLIER * i);
if (i == (mCenterTextViews.size() - 0x1)) {
anim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
startFadeOutAnimations();
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
}
tv.startAnimation(anim);
}
}
}
private void startFadeOutAnimations(){
for(TextView tv : mCenterTextViews){
int i = mCenterTextViews.indexOf(tv);
AlphaAnimation anim = new AlphaAnimation(0x0, 0x1);
anim.setDuration(CENTER_TEXT_VIEWS_ANIMATION_DURATION);
anim.setFillAfter(true);
anim.setStartOffset(CENTER_TEXT_VIEWS_START_OFFSET_MULTIPLIER * i);
if(i == (mCenterTextViews.size() - 0x1)){
anim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
startFadeInAnimations();
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
}
tv.startAnimation(anim);
}
}
/** Progress Bar Custom Layout Builder Class **/
public static class CustomLayoutBuilder implements Parcelable {
/** Shapes **/
private static final int RING = GradientDrawable.RING;
/** Colors **/
private static final int[][] COLORS_MATRIX_RYGB = new int[][]{
new int[]{ R.color.transparent, R.color.transparent, R.color.material_red_A700 },
new int[]{ R.color.transparent, R.color.transparent, R.color.material_amber_A700 },
new int[]{ R.color.transparent, R.color.transparent, R.color.material_light_green_A700 },
new int[]{ R.color.transparent, R.color.transparent, R.color.material_blue_A700 }
};
private static final int DEFAULT_COLOR = -0x1;
/** Dimens **/
private static final int DEFAULT_DIMEN = -0x1;
private static final int[] DEFAULT_PROGRESS_BAR_DIMEN = new int[]{};
/** Indexes **/
private static final int INDEX_PROGRESS_BAR_WIDTH = 0x0;
private static final int INDEX_PROGRESS_BAR_HEIGHT = 0x1;
private static final int INDEX_FROM_DEGREES = 0x0;
private static final int INDEX_TO_DEGREES = 0x1;
/** Arrays Sizes **/
private static final int SIZE_PROGRESS_BAR_DIMENS_ARRAY = 0x2;
/** Matrix Columns Number **/
private static final int NUM_COLUMNS_DEGREES_MATRIX = 0x2; /* Degrees Matrix Index -> degrees[3] = { fromDegrees, toDegrees } */
private static final int NUM_COLUMNS_COLORS_MATRIX = 0x3; /* GradientDrawable Colors Matrix Index -> colors[3] = { startColor, centerColor, endColor } */
/** Drawables Layout Resource IDs **/
private static final int LAYOUT_RES_PROGRESS_BAR_RINGS = R.drawable.progress_bar_rings;
/** Layout Data: Four Rings Overlaid **/
private static final int RINGS_OVERLAID_LAYERS = 0x4;
private static final int[][] RINGS_OVERLAID_DEGREES = new int[][]{ new int[]{ 300, 660 }, new int[]{ 210, 570 }, new int[]{ 120, 480 }, new int[]{ 30, 390 } };
private static final int[] RINGS_OVERLAID_SHAPES = new int[]{ RING, RING, RING, RING };
private static final int[] RINGS_OVERLAID_INNER_RADIUS = new int[]{ R.dimen.pbd_inner_radius_60dp, R.dimen.pbd_inner_radius_60dp, R.dimen.pbd_inner_radius_60dp, R.dimen.pbd_inner_radius_60dp};
private static final int[] RINGS_OVERLAID_THICKNESS = new int[]{ R.dimen.pbd_thickness_40dp, R.dimen.pbd_thickness_30dp, R.dimen.pbd_thickness_20dp, R.dimen.pbd_thickness_10dp };
/** Layout Data: Four Rings Spaced **/
private static final int RINGS_SPACED_LAYERS = 0x4;
private static final int[][] RINGS_SPACED_DEGREES = new int[][]{ new int[]{ 180, 540 }, new int[]{ 0, 360 }, new int[]{ 90, 450 }, new int[]{ 270, 630 } };
private static final int[] RINGS_SPACED_SHAPES = new int[]{ RING, RING, RING, RING };
private static final int[] RINGS_SPACED_INNER_RADIUS = new int[]{ R.dimen.pbd_inner_radius_30dp, R.dimen.pbd_inner_radius_60dp, R.dimen.pbd_inner_radius_90dp, R.dimen.pbd_inner_radius_120dp };
private static final int[] RINGS_SPACED_THICKNESS = new int[]{ R.dimen.pbd_thickness_10dp, R.dimen.pbd_thickness_15dp, R.dimen.pbd_thickness_20dp, R.dimen.pbd_thickness_25dp };
private int mLayoutResID;
private int[] mProgressBarDimens;
private int mNumLayers;
private int[][] mRotateDegrees;
private int[] mShapes;
private int[] mInnerRadius;
private int[] mThickness;
private int[][] mColors;
private int mProgressMsgColor;
private int mProgressMsgDimen;
public static Parcelable.Creator CREATOR = new CreatorCustomLayoutBuilder();
/** Constructors **/
private CustomLayoutBuilder(int layoutResID, int[] progressBarDimens, int numLayers, int[][] degreesMatrix, int[] shapes, int[] innerRadius, int[] thickness,
int[][] colorsMatrix, int msgColor, int progressMsgDimen){
mLayoutResID = layoutResID;
mProgressBarDimens = progressBarDimens;
mNumLayers = numLayers;
mRotateDegrees = degreesMatrix;
mShapes = shapes;
mInnerRadius = innerRadius;
mThickness = thickness;
mColors = colorsMatrix;
mProgressMsgColor = msgColor;
mProgressMsgDimen = progressMsgDimen;
}
private CustomLayoutBuilder(Parcel in){
mLayoutResID = in.readInt();
mProgressBarDimens = new int[SIZE_PROGRESS_BAR_DIMENS_ARRAY];
in.readIntArray(mProgressBarDimens);
mNumLayers = in.readInt();
int[] tempArray = new int[NUM_COLUMNS_DEGREES_MATRIX * mNumLayers];
in.readIntArray(tempArray);
mRotateDegrees = DataUtils.arrayToMatrix(tempArray, NUM_COLUMNS_DEGREES_MATRIX);
mShapes = new int[mNumLayers];
in.readIntArray(mShapes);
mInnerRadius = new int[mNumLayers];
in.readIntArray(mInnerRadius);
mThickness = new int[mNumLayers];
in.readIntArray(mThickness);
tempArray = new int[NUM_COLUMNS_COLORS_MATRIX * mNumLayers];
in.readIntArray(tempArray);
mColors = DataUtils.arrayToMatrix(tempArray, NUM_COLUMNS_COLORS_MATRIX);
mProgressMsgColor = in.readInt();
mProgressMsgDimen = in.readInt();
}
/** Public Static Factory Methods **/
public static CustomLayoutBuilder initLayoutRingsOverlaid(){
return new CustomLayoutBuilder(LAYOUT_RES_PROGRESS_BAR_RINGS, DEFAULT_PROGRESS_BAR_DIMEN, RINGS_OVERLAID_LAYERS, RINGS_OVERLAID_DEGREES, RINGS_OVERLAID_SHAPES,
RINGS_OVERLAID_INNER_RADIUS, RINGS_OVERLAID_THICKNESS, COLORS_MATRIX_RYGB, R.color.material_white, DEFAULT_DIMEN);
}
public static CustomLayoutBuilder initLayoutRingsOverlaid(int[] resProgBarDimens, int resProgMsgColor, int resProgMsgDimen){
return new CustomLayoutBuilder(LAYOUT_RES_PROGRESS_BAR_RINGS, resProgBarDimens, RINGS_OVERLAID_LAYERS, RINGS_OVERLAID_DEGREES, RINGS_OVERLAID_SHAPES,
RINGS_OVERLAID_INNER_RADIUS, RINGS_OVERLAID_THICKNESS, COLORS_MATRIX_RYGB, resProgMsgColor, resProgMsgDimen);
}
public static CustomLayoutBuilder initLayoutRingsSpaced(){
return new CustomLayoutBuilder(LAYOUT_RES_PROGRESS_BAR_RINGS, DEFAULT_PROGRESS_BAR_DIMEN, RINGS_SPACED_LAYERS, RINGS_SPACED_DEGREES, RINGS_SPACED_SHAPES,
RINGS_SPACED_INNER_RADIUS, RINGS_SPACED_THICKNESS, COLORS_MATRIX_RYGB, DEFAULT_COLOR, DEFAULT_DIMEN);
}
/** Override Parcelable Methods **/
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mLayoutResID);
out.writeIntArray(mProgressBarDimens);
out.writeInt(mNumLayers);
out.writeIntArray(DataUtils.matrixToArray(mRotateDegrees));
out.writeIntArray(mShapes);
out.writeIntArray(mInnerRadius);
out.writeIntArray(mThickness);
out.writeIntArray(DataUtils.matrixToArray(mColors));
out.writeInt(mProgressMsgColor);
out.writeInt(mProgressMsgDimen);
}
/** Getter & Setter Methods **/
private int getLayoutResID() {
return mLayoutResID;
}
private void setLayoutResID(int layoutResID) {
this.mLayoutResID = layoutResID;
}
private int[] getProgressBarDimens() {
return mProgressBarDimens;
}
private void setProgressBarDimens(int[] progressBarDimens) {
this.mProgressBarDimens = progressBarDimens;
}
private int getProgressBarWidthDimen(){ // Used to check if 'mProgressBarDimens' array is set.
if(mProgressBarDimens != null && mProgressBarDimens.length == SIZE_PROGRESS_BAR_DIMENS_ARRAY){
return mProgressBarDimens[INDEX_PROGRESS_BAR_WIDTH];
} else {
return -0x1;
}
}
private int getProgressBarHeightDimen(){
return mProgressBarDimens[INDEX_PROGRESS_BAR_HEIGHT];
}
private int getNumLayers() {
return mNumLayers;
}
private void setNumLayers(int numLayers) {
this.mNumLayers = numLayers;
}
private int[][] getRotateDegrees() {
return mRotateDegrees;
}
private void setRotateDegrees(int[][] rotateDegrees) {
this.mRotateDegrees = rotateDegrees;
}
private int[] getShapes() {
return mShapes;
}
private void setShapes(int[] shapes) {
this.mShapes = shapes;
}
private int[] getInnerRadius() {
return mInnerRadius;
}
private void setInnerRadius(int[] innerRadius) {
this.mInnerRadius = innerRadius;
}
private int[] getThickness() {
return mThickness;
}
private void setThickness(int[] thickness) {
this.mThickness = thickness;
}
private int[][] getColorsMatrix() {
return mColors;
}
private void setColorsMatrix(int[][] colorsMatrix) {
this.mColors = colorsMatrix;
}
private int getProgressMsgColor() {
return mProgressMsgColor;
}
private void setProgressMsgColor(int progressMsgColor) {
this.mProgressMsgColor = progressMsgColor;
}
private int getProgressMsgDimen() {
return mProgressMsgDimen;
}
private void setProgressMsgDimen(int progressMsgDimen) {
this.mProgressMsgDimen = progressMsgDimen;
}
/** Public Methods **/
private int[] getDegreesMatrixRow(int numRow){
if(mRotateDegrees != null && mRotateDegrees.length > numRow) {
return mRotateDegrees[numRow];
} else {
return new int[]{};
}
}
private int getShape(int position){
if(mShapes != null && mShapes.length > position){
return mShapes[position];
} else {
return -0x1;
}
}
private int getInnerRadius(int position){
if(mInnerRadius != null && mInnerRadius.length > position){
return mInnerRadius[position];
} else {
return -0x1;
}
}
private int getThickness(int position){
if(mThickness != null && mThickness.length > position){
return mThickness[position];
} else {
return -0x1;
}
}
private int[] getColorsMatrixRow(int numRow) {
if(mColors != null && mColors.length > numRow){
return mColors[numRow];
} else {
return new int[]{};
}
}
/** Private Static Class Parcelable Creator **/
private static class CreatorCustomLayoutBuilder implements Parcelable.Creator<CustomLayoutBuilder> {
#Override
public CustomLayoutBuilder createFromParcel(Parcel in) {
return new CustomLayoutBuilder(in);
}
#Override
public CustomLayoutBuilder[] newArray(int size) {
return new CustomLayoutBuilder[size];
}
}
}
}
The class has a progress bar with no dialog and custom text over it.
The text can have 2 animations:
Fade In -> Fade Out animation: digits will fade out like a sequentially from left to right. At end of text will restart from left.
Rotate Animation: same sequential behavior but the digits rotate on themself one by one instead of fading out.
Other way is a Progress Bar with a dialog (Title + Message)
Rest of code
Code of utils methods:
public static int[] resourcesIDsToColors(Context context, int[] resIDs){
int[] colors = new int[resIDs.length];
for(int i=0; i < resIDs.length; i++){
colors[i] = ActivityCompat.getColor(context, resIDs[i]);
}
return colors;
}
public static void setSubClassFieldIntValue(Object objField, Class<?> superClass, String subName, String fieldName, int fieldValue){
Class<?> subClass = getSubClass(superClass, subName);
if(subClass != null) {
Field field = getClassField(subClass, fieldName);
if (field != null) {
setFieldValue(objField, field, fieldValue);
}
}
}
public static Class<?> getSubClass(Class<?> superClass, String subName){
Class<?>[] classes = superClass.getDeclaredClasses();
if(classes != null && classes.length > 0){
for(Class<?> clss : classes){
if(clss.getSimpleName().equals(subName)){
return clss;
}
}
}
return null;
}
public static Field getClassField(Class<?> clss, String fieldName){
try {
Field field = clss.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException nsfE) {
Log.e(TAG, nsfE.getMessage());
} catch (SecurityException sE){
Log.e(TAG, sE.getMessage());
} catch (Exception e){
Log.e(TAG, e.getMessage());
}
return null;
}
public static int[][] arrayToMatrix(int[] array, int numColumns){
int numRows = array.length / numColumns;
int[][] matrix = new int[numRows][numColumns];
int nElemens = array.length;
for(int i=0; i < nElemens; i++){
matrix[i / numColumns][i % numColumns] = array[i];
}
return matrix;
}
public static int[] matrixToArray(int[][] matrix){
/** [+] Square matrix of order n -> A matrix with n rows and n columns, same number of rows and columns.
* [+] Matrix rows & columns number annotations:
* matrix[rows][columns] matrix (rows x columns) matrix rows, columns rows by columns matrix
* **/
int numRows = matrix.length;
int[] arr = new int[]{};
for(int i=0; i < numRows; i++){
int numColumns = matrix[i].length;
int[] row = new int[numColumns];
for(int j=0; j < numColumns; j++){
row[j] = matrix[i][j];
}
arr = ArrayUtils.addAll(arr, row);
}
return arr;
}
Code of default layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#color/transparent">
<LinearLayout
android:id="#+id/layout_progress_bar_only"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<android.support.constraint.ConstraintLayout
android:id="#+id/dpb_constraint_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="#+id/dpb_progress_bar"
android:layout_width="#dimen/pbd_progressbar_width_2"
android:layout_height="#dimen/pbd_progressbar_height_2"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"/>
<LinearLayout
android:id="#+id/dpb_text_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"/>
</android.support.constraint.ConstraintLayout>
</LinearLayout>
<LinearLayout
android:id="#+id/layout_progress_bar_and_msg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
style="#style/PBDTextualMainLayoutStyle">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardElevation="#dimen/pbd_textual_card_elevation">
<TextView
android:id="#+id/pbd_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/PBDTextualTitle"/>
</android.support.v7.widget.CardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="#dimen/pbd_textual_main_layout_height"
android:orientation="horizontal">
<android.support.v7.widget.CardView
android:layout_width="#dimen/pbd_textual_progressbar_width"
android:layout_height="#dimen/pbd_textual_progressbar_height">
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="#+id/dpb_progress_bar_and_msg"
android:layout_width="match_parent"
android:layout_height="match_parent"
style="#style/PBDProgressBarStyle"/>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="#dimen/pbd_textual_msg_container_height">
<TextView
android:id="#+id/dpb_progress_msg"
android:layout_width="match_parent"
android:layout_height="match_parent"
style="#style/PBDTextualProgressMsgStyle"/>
</android.support.v7.widget.CardView>
</LinearLayout>
</LinearLayout>
</LinearLayout>
Progress Bar Rings:
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<rotate
android:fromDegrees="300"
android:toDegrees="660">
<shape
android:shape="ring"
android:useLevel="false">
<gradient
android:type="sweep"/>
</shape>
</rotate>
</item>
<item>
<rotate
android:fromDegrees="210"
android:toDegrees="570">
<shape
android:shape="ring"
android:useLevel="false">
<gradient
android:type="sweep"/>
</shape>
</rotate>
</item>
<item>
<rotate
android:fromDegrees="120"
android:toDegrees="480">
<shape
android:shape="ring"
android:useLevel="false">
<gradient
android:type="sweep"
android:startColor="#00000000"
android:centerColor="#00000000"/>
</shape>
</rotate>
</item>
<item>
<rotate
android:fromDegrees="30"
android:toDegrees="390">
<shape
android:shape="ring"
android:useLevel="false">
<solid android:color="#000000"/>
<gradient
android:type="sweep"/>
</shape>
</rotate>
</item>
</layer-list>
Dimens Resources:
<!-- ProgressBarDialog Dimens (Normal & Textual Versions) -->
<dimen name="pbd_window_width">250dp</dimen>
<dimen name="pbd_window_height">250dp</dimen>
<dimen name="pbd_progressbar_width_1">250dp</dimen>
<dimen name="pbd_progressbar_height_1">250dp</dimen>
<dimen name="pbd_progressbar_width_2">400dp</dimen>
<dimen name="pbd_progressbar_height_2">400dp</dimen>
<dimen name="pbd_textual_window_height">170dp</dimen>
<dimen name="pbd_textual_main_layout_height">150dp</dimen>
<dimen name="pbd_textual_progressbar_width">150dp</dimen>
<dimen name="pbd_textual_progressbar_height">150dp</dimen>
<dimen name="pbd_textual_msg_container_height">150dp</dimen>
<dimen name="pbd_textual_main_layout_margin_horizontal">50dp</dimen>
<dimen name="pbd_textual_main_layout_padding_horizontal">5dp</dimen>
<dimen name="pbd_textual_main_layout_padding_bottom">15dp</dimen>
<dimen name="pbd_textual_title_padding">4dp</dimen>
<dimen name="pbd_textual_msg_container_margin">3dp</dimen>
<dimen name="pbd_textual_msg_container_padding">3dp</dimen>
<dimen name="pbd_textual_card_elevation">15dp</dimen>
<dimen name="pbd_textual_progressmsg_padding_start">10dp</dimen>
<dimen name="pbd_inner_radius_30dp">30dp</dimen>
<dimen name="pbd_inner_radius_60dp">60dp</dimen>
<dimen name="pbd_inner_radius_90dp">90dp</dimen>
<dimen name="pbd_inner_radius_120dp">120dp</dimen>
<dimen name="pbd_thickness_40dp">40dp</dimen>
<dimen name="pbd_thickness_30dp">30dp</dimen>
<dimen name="pbd_thickness_25dp">25dp</dimen>
<dimen name="pbd_thickness_20dp">20dp</dimen>
<dimen name="pbd_thickness_15dp">15dp</dimen>
<dimen name="pbd_thickness_10dp">10dp</dimen>
Styles Resources:
<!-- PROGRESS BAR DIALOG STYLES -->
<style name="PBDCenterTextStyleWhite">
<item name="android:textAlignment">center</item>
<item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
<item name="android:textStyle">bold|italic</item>
<item name="android:textColor">#color/material_white</item>
<item name="android:layout_gravity">center</item>
<item name="android:gravity">center</item>
</style>
<style name="PBDTextualTitle">
<item name="android:textAlignment">viewStart</item>
<item name="android:textAppearance">?android:attr/textAppearanceLargeInverse</item>
<item name="android:textStyle">bold|italic</item>
<item name="android:textColor">#color/colorAccent</item>
<item name="android:padding">#dimen/pbd_textual_title_padding</item>
<item name="android:layout_gravity">start</item>
<item name="android:gravity">center_vertical|start</item>
<item name="android:background">#color/colorPrimaryDark</item>
</style>
<style name="PBDTextualProgressMsgStyle">
<item name="android:textAlignment">viewStart</item>
<item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
<item name="android:textColor">#color/material_black</item>
<item name="android:textStyle">normal|italic</item>
<item name="android:paddingStart">#dimen/pbd_textual_progressmsg_padding_start</item>
<item name="android:layout_gravity">start</item>
<item name="android:gravity">center_vertical|start</item>
<item name="android:background">#color/material_yellow_A100</item>
</style>
<style name="PBDTextualMainLayoutStyle">
<item name="android:paddingLeft">#dimen/pbd_textual_main_layout_padding_horizontal</item>
<item name="android:paddingRight">#dimen/pbd_textual_main_layout_padding_horizontal</item>
<item name="android:paddingBottom">#dimen/pbd_textual_main_layout_padding_bottom</item>
<item name="android:background">#color/colorPrimaryDark</item>
</style>
<style name="PBDProgressBarStyle">
<item name="android:layout_gravity">center</item>
<item name="android:gravity">center</item>
</style>
I have made my app to change background color every few milliseconds. But it is not appealing to user. If you have seen Instagram's login screen, it changes color at a very soft rate and with blurry effect.
I just wanted that type of background for my app.
What should I have to do for that
Using following code might be good start for you:
public class BackgroundPainter {
private static final int MIN = 800;
private static final int MAX = 1500;
private final Random random;
public BackgroundPainter() {
random = new Random();
}
public void animate(#NonNull final View target, #ColorInt final int color1,
#ColorInt final int color2) {
final ValueAnimator valueAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), color1, color2);
valueAnimator.setDuration(randInt(MIN, MAX));
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override public void onAnimationUpdate(ValueAnimator animation) {
target.setBackgroundColor((int) animation.getAnimatedValue());
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
#Override public void onAnimationEnd(Animator animation) {
//reverse animation
animate(target, color2, color1);
}
});
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.start();
}
private int randInt(int min, int max) {
return random.nextInt((max - min) + 1) + min;
}
}
With usage:
final View targetView = findViewById(R.id.root_view);
BackgroundPainter backgroundPainter = new BackgroundPainter();
int color1 = ContextCompat.getColor(this, R.color.colorAccent);
int color2 = ContextCompat.getColor(this, R.color.colorPrimary);
backgroundPainter.animate(targetView, color1, color2);
Update
For changing background which consists of more than one color, generally speaking of drawables instead of ValueAnimator you can try using below solution.
I have tested this code on Device with API 19 and 23.
Define colors in your colors.xml:
<color name="color1">#9C27B0</color>
<color name="color2">#FF4081</color>
<color name="color3">#7B1FA2</color>
<color name="color4">#F8BBD0</color>
<color name="color5">#FF5252</color>
<color name="color6">#607D8B</color>
<color name="color7">#FF5722</color>
<color name="color8">#FFA000</color>
Define gradients in drawable:
gradient_1.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="45"
android:endColor="#color/color2"
android:startColor="#color/color1"
android:type="linear"/>
</shape>
gradient_2.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="-45"
android:endColor="#color/color5"
android:startColor="#color/color3"
android:type="linear"/>
</shape>
gradient_3.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="45"
android:endColor="#color/color8"
android:startColor="#color/color7"
android:type="linear"/>
</shape>
Create class GradientBackgroundPainter in your project:
public class GradientBackgroundPainter {
private static final int MIN = 4000;
private static final int MAX = 5000;
private final Random random;
private final Handler handler;
private final View target;
private final int[] drawables;
private final Context context;
public GradientBackgroundPainter(#NonNull View target, int[] drawables) {
this.target = target;
this.drawables = drawables;
random = new Random();
handler = new Handler();
context = target.getContext().getApplicationContext();
}
private void animate(final int firstDrawable, int secondDrawable, final int duration) {
if (secondDrawable >= drawables.length) {
secondDrawable = 0;
}
final Drawable first = ContextCompat.getDrawable(context, drawables[firstDrawable]);
final Drawable second = ContextCompat.getDrawable(context, drawables[secondDrawable]);
final TransitionDrawable transitionDrawable =
new TransitionDrawable(new Drawable[] { first, second });
target.setBackgroundDrawable(transitionDrawable);
transitionDrawable.setCrossFadeEnabled(false);
transitionDrawable.startTransition(duration);
final int localSecondDrawable = secondDrawable;
handler.postDelayed(new Runnable() {
#Override public void run() {
animate(localSecondDrawable, localSecondDrawable + 1, randInt(MIN, MAX));
}
}, duration);
}
public void start() {
final int duration = randInt(MIN, MAX);
animate(0, 1, duration);
}
public void stop() {
handler.removeCallbacksAndMessages(null);
}
private int randInt(int min, int max) {
return random.nextInt((max - min) + 1) + min;
}
}
And usage:
public class MainActivity extends AppCompatActivity {
private GradientBackgroundPainter gradientBackgroundPainter;
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View backgroundImage = findViewById(R.id.root_view);
final int[] drawables = new int[3];
drawables[0] = R.drawable.gradient_1;
drawables[1] = R.drawable.gradient_2;
drawables[2] = R.drawable.gradient_3;
gradientBackgroundPainter = new GradientBackgroundPainter(backgroundImage, drawables);
gradientBackgroundPainter.start();
}
#Override protected void onDestroy() {
super.onDestroy();
gradientBackgroundPainter.stop();
}
}
You can check the Property Animation Api
int colorFrom = getResources().getColor(R.color.red);
int colorTo = getResources().getColor(R.color.blue);
ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.setDuration(250); // milliseconds
colorAnimation.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animator) {
textView.setBackgroundColor((int) animator.getAnimatedValue());
}
});
colorAnimation.start();
Use a Relative layout,
give an id to your Relative layout,downcast it ,and put into condition:
if(something){
rlayout.setBackground(getResource().getDrawable().R.drawable.img);
}
or
use switch
Create a TransitionDrawable to change between two drawables that you use for the background.
<?xml version="1.0" encoding="UTF-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<!-- use whatever drawables you want, gradients, shapes etc-->
<item android:drawable="#drawable/start" />
<item android:drawable="#drawable/end" />
Transition Drawables in Android
TransitionDrawable trans = (TransitionDrawable) myLayout.getBackground();
trans.startTransition(2000);
I think this little code might help you.
i want to custom progress bar in circle shape like same as download blazer app in google play shop.i am expected like below screen shot:
any one help me how to do that?
The best two libraries I found on the net are on github:
https://github.com/Todd-Davies/ProgressWheel
https://github.com/f2prateek/progressbutton?source=c
Hope that will help you
I've encountered same problem and not found any appropriate solution for my case, so I decided to go another way. I've created custom drawable class. Within this class I've created 2 Paints for progress line and background line (with some bigger stroke). First of all set startAngle and sweepAngle in constructor:
mSweepAngle = 0;
mStartAngle = 270;
Here is onDraw method of this class:
#Override
public void draw(Canvas canvas) {
// draw background line
canvas.drawArc(mRectF, 0, 360, false, mPaintBackground);
// draw progress line
canvas.drawArc(mRectF, mStartAngle, mSweepAngle, false, mPaintProgress);
}
So now all you need to do is set this drawable as a backgorund of the view, in background thread change sweepAngle:
mSweepAngle += 360 / totalTimerTime // this is mStep
and directly call InvalidateSelf() with some interval (e.g every 1 second or more often if you want smooth progress changes) on the view that have this drawable as a background. Thats it!
P.S. I know, I know...of course you want some more code. So here it is all flow:
Create XML view :
<View
android:id="#+id/timer"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Create and configure Custom Drawable class (as I described above). Don't forget to setup Paints for lines. Here paint for progress line:
mPaintProgress = new Paint();
mPaintProgress.setAntiAlias(true);
mPaintProgress.setStyle(Paint.Style.STROKE);
mPaintProgress.setStrokeWidth(widthProgress);
mPaintProgress.setStrokeCap(Paint.Cap.ROUND);
mPaintProgress.setColor(colorThatYouWant);
Same for backgroung paint (set width little more if you want)
In drawable class create method for updating (Step calculation described above)
public void update() {
mSweepAngle += mStep;
invalidateSelf();
}
Set this drawable class to YourTimerView (I did it in runtime) - view with #+id/timer from xml above:
OurSuperDrawableClass superDrawable = new OurSuperDrawableClass();
YourTimerView.setBackgroundDrawable(superDrawable);
Create background thread with runnable and update view:
YourTimerView.post(new Runnable() {
#Override
public void run() {
// update progress view
superDrawable.update();
}
});
Thats it ! Enjoy your cool progress bar. Here screenshot of result if you're too bored of this amount of text.
for more information on How to create Circle Android Custom Progress Bar view this link
Step 01
You should create an xml file on drawable file for configure the appearance of progress bar . So Im creating my xml file as circular_progress_bar.xml.
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="120"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="140">
<item android:id="#android:id/background">
<shape
android:innerRadiusRatio="3"
android:shape="ring"
android:useLevel="false"
android:angle="0"
android:type="sweep"
android:thicknessRatio="50.0">
<solid android:color="#000000"/>
</shape>
</item>
<item android:id="#android:id/progress">
<rotate
android:fromDegrees="120"
android:toDegrees="120">
<shape
android:innerRadiusRatio="3"
android:shape="ring"
android:angle="0"
android:type="sweep"
android:thicknessRatio="50.0">
<solid android:color="#ffffff"/>
</shape>
</rotate>
</item>
</layer-list>
Step 02
Then create progress bar on your xml file
Then give the name of xml file on your drawable folder as the parth of android:progressDrawable
<ProgressBar
android:id="#+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_marginLeft="0dp"
android:layout_centerHorizontal="true"
android:indeterminate="false"
android:max="100"
android:progressDrawable="#drawable/circular_progress_bar" />
Step 03
Visual the progress bar using thread
package com.example.progress;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.view.Menu;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity {
private ProgressBar progBar;
private TextView text;
private Handler mHandler = new Handler();
private int mProgressStatus=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progBar= (ProgressBar)findViewById(R.id.progressBar);
text = (TextView)findViewById(R.id.textView1);
dosomething();
}
public void dosomething() {
new Thread(new Runnable() {
public void run() {
final int presentage=0;
while (mProgressStatus < 63) {
mProgressStatus += 1;
// Update the progress bar
mHandler.post(new Runnable() {
public void run() {
progBar.setProgress(mProgressStatus);
text.setText(""+mProgressStatus+"%");
}
});
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
A very useful lib for custom progress bar in android.
In your layout file
<com.lylc.widget.circularprogressbar.example.CircularProgressBar
android:id="#+id/mycustom_progressbar"
.
.
.
/>
and Java file
CircularProgressBar progressBar = (CircularProgressBar) findViewById(R.id.mycustom_progressbar);
progressBar.setTitle("Circular Progress Bar");
Try this piece of code to create circular progress bar(pie chart). pass it integer value to draw how many percent of filling area. :)
private void circularImageBar(ImageView iv2, int i) {
Bitmap b = Bitmap.createBitmap(300, 300,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(b);
Paint paint = new Paint();
paint.setColor(Color.parseColor("#c4c4c4"));
paint.setStrokeWidth(10);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(150, 150, 140, paint);
paint.setColor(Color.parseColor("#FFDB4C"));
paint.setStrokeWidth(10);
paint.setStyle(Paint.Style.FILL);
final RectF oval = new RectF();
paint.setStyle(Paint.Style.STROKE);
oval.set(10,10,290,290);
canvas.drawArc(oval, 270, ((i*360)/100), false, paint);
paint.setStrokeWidth(0);
paint.setTextAlign(Align.CENTER);
paint.setColor(Color.parseColor("#8E8E93"));
paint.setTextSize(140);
canvas.drawText(""+i, 150, 150+(paint.getTextSize()/3), paint);
iv2.setImageBitmap(b);
}
I have solved this cool custom progress bar by creating the custom view. I have overriden the onDraw() method to draw the circles, filled arc and text on the canvas.
following is the custom progress bar
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import com.investorfinder.utils.UiUtils;
public class CustomProgressBar extends View {
private int max = 100;
private int progress;
private Path path = new Path();
int color = 0xff44C8E5;
private Paint paint;
private Paint mPaintProgress;
private RectF mRectF;
private Paint textPaint;
private String text = "0%";
private final Rect textBounds = new Rect();
private int centerY;
private int centerX;
private int swipeAndgle = 0;
public CustomProgressBar(Context context) {
super(context);
initUI();
}
public CustomProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
initUI();
}
public CustomProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initUI();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initUI();
}
private void initUI() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(UiUtils.dpToPx(getContext(), 1));
paint.setStyle(Paint.Style.STROKE);
paint.setColor(color);
mPaintProgress = new Paint();
mPaintProgress.setAntiAlias(true);
mPaintProgress.setStyle(Paint.Style.STROKE);
mPaintProgress.setStrokeWidth(UiUtils.dpToPx(getContext(), 9));
mPaintProgress.setColor(color);
textPaint = new Paint();
textPaint.setAntiAlias(true);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setColor(color);
textPaint.setStrokeWidth(2);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int viewWidth = MeasureSpec.getSize(widthMeasureSpec);
int viewHeight = MeasureSpec.getSize(heightMeasureSpec);
int radius = (Math.min(viewWidth, viewHeight) - UiUtils.dpToPx(getContext(), 2)) / 2;
path.reset();
centerX = viewWidth / 2;
centerY = viewHeight / 2;
path.addCircle(centerX, centerY, radius, Path.Direction.CW);
int smallCirclRadius = radius - UiUtils.dpToPx(getContext(), 7);
path.addCircle(centerX, centerY, smallCirclRadius, Path.Direction.CW);
smallCirclRadius += UiUtils.dpToPx(getContext(), 4);
mRectF = new RectF(centerX - smallCirclRadius, centerY - smallCirclRadius, centerX + smallCirclRadius, centerY + smallCirclRadius);
textPaint.setTextSize(radius * 0.5f);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path, paint);
canvas.drawArc(mRectF, 270, swipeAndgle, false, mPaintProgress);
drawTextCentred(canvas);
}
public void drawTextCentred(Canvas canvas) {
textPaint.getTextBounds(text, 0, text.length(), textBounds);
canvas.drawText(text, centerX - textBounds.exactCenterX(), centerY - textBounds.exactCenterY(), textPaint);
}
public void setMax(int max) {
this.max = max;
}
public void setProgress(int progress) {
this.progress = progress;
int percentage = progress * 100 / max;
swipeAndgle = percentage * 360 / 100;
text = percentage + "%";
invalidate();
}
public void setColor(int color) {
this.color = color;
}
}
In layout XML
<com.your.package.name.CustomProgressBar
android:id="#+id/progress_bar"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_alignParentRight="true"
android:layout_below="#+id/txt_title"
android:layout_marginRight="15dp" />
in activity
CustomProgressBar progressBar = (CustomProgressBar)findViewById(R.id.progress_bar);
progressBar.setMax(9);
progressBar.setProgress(5);
I'd make a new view class and derive from the existing ProgressBar. Then override the onDraw function. You're going to need to make direct draw calls to the canvas for this, since its so custom- a combination of drawText, drawArc, and drawOval should do it- an oval for the outer ring and empty portions, and an arc for the colored in parts. You may end up needing to override onMeasure and onLayout as well. Then in your xml, reference this view by class name like this when you want to use it.
I did a simple class which u can use to make custom ProgressBar dialog.
Actually it has 2 default layouts:
- First dialog with no panel with progress bar and animated text centered over it
- Second normal dialog with panel, progress bar, title and msg
It is just a class, so not a customizable library which u can import in your project, so you need to copy it and change it how you want.
It is a DialogFragment class, but you can use it inside an activity as a normal fragment just like you do with classic fragment by using FragmentManager.
Code of the dialog class:
public class ProgressBarDialog extends DialogFragment {
private static final String TAG = ProgressBarDialog.class.getSimpleName();
private static final String KEY = TAG.concat(".key");
// Argument Keys
private static final String KEY_DIALOG_TYPE = KEY.concat(".dialogType");
private static final String KEY_TITLE = KEY.concat(".title");
private static final String KEY_PROGRESS_TEXT = KEY.concat(".progressText");
private static final String KEY_CUSTOM_LAYOUT_BUILDER = KEY.concat(".customLayoutBuilder");
// Class Names
private static final String CLASS_GRADIENT_STATE = "GradientState";
// Field Names
private static final String FIELD_THICKNESS = "mThickness";
private static final String FIELD_INNER_RADIUS = "mInnerRadius";
/** Dialog Types **/
private static final int TYPE_PROGRESS_BAR_ONLY_NO_ANIM = 0x0;
private static final int TYPE_PROGRESS_BAR_ONLY_ROTATE_ANIM = 0x1;
private static final int TYPE_PROGRESS_BAR_ONLY_FADE_ANIM = 0x2;
private static final int TYPE_PROGRESS_BAR_AND_MSG = 0xF;
/** Animations Values **/
private static final long CENTER_TEXT_VIEWS_ANIMATION_DURATION = 250L;
private static final long CENTER_TEXT_VIEWS_START_OFFSET_MULTIPLIER = 250L;
private MaterialProgressBar mProgressBar;
private LinearLayout mllTextContainer;
private TextView mtvTitle;
private TextView mtvProgressText;
private List<TextView> mCenterTextViews;
private int mDialogType;
private String mTitle;
private String mProgressText;
private CustomLayoutBuilder mCustomLayoutBuilder;
/** Public Static Factory Methods **/
public static ProgressBarDialog initLayoutProgressBarOnlyNoAnim(String text, CustomLayoutBuilder builder){
return initLayoutProgressBarOnly(TYPE_PROGRESS_BAR_ONLY_NO_ANIM, text, builder);
}
public static ProgressBarDialog initLayoutProgressBarOnlyRotateAnim(String text, CustomLayoutBuilder builder){
return initLayoutProgressBarOnly(TYPE_PROGRESS_BAR_ONLY_ROTATE_ANIM, text, builder);
}
public static ProgressBarDialog initLayoutProgressBarOnlyFadeAnim(String text, CustomLayoutBuilder builder){
return initLayoutProgressBarOnly(TYPE_PROGRESS_BAR_ONLY_FADE_ANIM, text, builder);
}
public static ProgressBarDialog initLayoutProgressBarAndMsg(String title, String text, CustomLayoutBuilder builder){
ProgressBarDialog mInstance = new ProgressBarDialog();
Bundle args = new Bundle();
args.putInt(KEY_DIALOG_TYPE, TYPE_PROGRESS_BAR_AND_MSG);
args.putString(KEY_TITLE, title);
args.putString(KEY_PROGRESS_TEXT, text);
args.putParcelable(KEY_CUSTOM_LAYOUT_BUILDER, builder);
mInstance.setArguments(args);
return mInstance;
}
/** Private Static Factory Methods **/
private static ProgressBarDialog initLayoutProgressBarOnly(int animation, String text, CustomLayoutBuilder builder){
ProgressBarDialog mInstance = new ProgressBarDialog();
Bundle args = new Bundle();
args.putInt(KEY_DIALOG_TYPE, animation);
args.putString(KEY_PROGRESS_TEXT, text);
args.putParcelable(KEY_CUSTOM_LAYOUT_BUILDER, builder);
mInstance.setArguments(args);
return mInstance;
}
/** Override Lifecycle Methods **/
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initData();
}
#Override #Nullable
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.dialog_progress_bar, container, false);
initViews(view);
if(getContext() != null && mCustomLayoutBuilder != null) {
mProgressBar.setIndeterminateDrawable(getResources().getDrawable(mCustomLayoutBuilder.getLayoutResID()));
initShapes();
}
return view;
}
private void initShapes(){
if(mProgressBar.getIndeterminateDrawable() instanceof LayerDrawable) {
LayerDrawable layerDrawable = (LayerDrawable) mProgressBar.getIndeterminateDrawable();
for (int i = 0; i < layerDrawable.getNumberOfLayers(); i++) {
if(layerDrawable.getDrawable(i) instanceof RotateDrawable) {
RotateDrawable rotateDrawable = (RotateDrawable) layerDrawable.getDrawable(i);
int[] fromToDeg = mCustomLayoutBuilder.getDegreesMatrixRow(i);
if(fromToDeg.length > 0){
rotateDrawable.setFromDegrees(fromToDeg[CustomLayoutBuilder.INDEX_FROM_DEGREES]);
rotateDrawable.setToDegrees(fromToDeg[CustomLayoutBuilder.INDEX_TO_DEGREES]);
}
if(rotateDrawable.getDrawable() instanceof GradientDrawable){
GradientDrawable gradientDrawable = (GradientDrawable) rotateDrawable.getDrawable();
int innerRadius = getResources().getDimensionPixelSize(mCustomLayoutBuilder.getInnerRadius(i));
if(mDialogType == TYPE_PROGRESS_BAR_AND_MSG){
innerRadius /= 3;
}
int thickness = getResources().getDimensionPixelSize(mCustomLayoutBuilder.getThickness(i));
int[] colors = mCustomLayoutBuilder.getColorsMatrixRow(i);
if(colors.length > 0x0){
gradientDrawable.setColors(DataUtils.resourcesIDsToColors(this.getContext(), colors));
}
if(innerRadius != -0x1){
DataUtils.setSubClassFieldIntValue(gradientDrawable.getConstantState(), gradientDrawable.getClass(), CLASS_GRADIENT_STATE, FIELD_INNER_RADIUS, innerRadius);
}
if(thickness != -0x1){
DataUtils.setSubClassFieldIntValue(gradientDrawable.getConstantState(), gradientDrawable.getClass(), CLASS_GRADIENT_STATE, FIELD_THICKNESS, thickness);
}
}
}
}
}
}
#Override
public void onStart() {
super.onStart();
setWindowSize();
startAnimation();
}
/** Public Methods **/
public void changeTextViews(String progressText){
mProgressText = progressText;
initTextViews();
startAnimation();
}
public String getProgressText(){
return mProgressText;
}
/** Private Methods **//** Init Methods **/
private void initData(){
if(getArguments() != null) {
if (getArguments().containsKey(KEY_DIALOG_TYPE)) {
mDialogType = getArguments().getInt(KEY_DIALOG_TYPE);
}
if(getArguments().containsKey(KEY_TITLE)){
mTitle = getArguments().getString(KEY_TITLE);
}
if (getArguments().containsKey(KEY_PROGRESS_TEXT)) {
mProgressText = getArguments().getString(KEY_PROGRESS_TEXT);
}
if (getArguments().containsKey(KEY_CUSTOM_LAYOUT_BUILDER)){
mCustomLayoutBuilder = getArguments().getParcelable(KEY_CUSTOM_LAYOUT_BUILDER);
}
}
mCenterTextViews = new ArrayList<>();
}
private void initViews(View layout){
if(layout != null){
switch(mDialogType){
case TYPE_PROGRESS_BAR_ONLY_NO_ANIM:
case TYPE_PROGRESS_BAR_ONLY_ROTATE_ANIM:
case TYPE_PROGRESS_BAR_ONLY_FADE_ANIM:
if(getDialog() != null && getDialog().getWindow() != null) {
getDialog().getWindow().setBackgroundDrawableResource(android.R.color.transparent);
}
LinearLayout mLayoutProgressBarOnly = layout.findViewById(R.id.layout_progress_bar_only);
mLayoutProgressBarOnly.setVisibility(LinearLayout.VISIBLE);
mProgressBar = layout.findViewById(R.id.dpb_progress_bar);
if(mCustomLayoutBuilder.getProgressBarWidthDimen() != -0x1){
ConstraintLayout.LayoutParams lp = (ConstraintLayout.LayoutParams) mProgressBar.getLayoutParams();
lp.width = getResources().getDimensionPixelSize(mCustomLayoutBuilder.getProgressBarWidthDimen());
lp.height = getResources().getDimensionPixelSize(mCustomLayoutBuilder.getProgressBarHeightDimen());
mProgressBar.setLayoutParams(lp);
}
mllTextContainer = layout.findViewById(R.id.dpb_text_container);
initTextViews();
break;
case TYPE_PROGRESS_BAR_AND_MSG:
LinearLayout mLayoutProgressBarAndMsg = layout.findViewById(R.id.layout_progress_bar_and_msg);
mLayoutProgressBarAndMsg.setVisibility(LinearLayout.VISIBLE);
mProgressBar = layout.findViewById(R.id.dpb_progress_bar_and_msg);
mtvTitle = layout.findViewById(R.id.pbd_title);
mtvProgressText = layout.findViewById(R.id.dpb_progress_msg);
initProgressMsg();
break;
}
}
}
private void initTextViews(){
if(!TextUtils.isEmpty(mProgressText)){
clearTextContainer();
for(char digit : mProgressText.toCharArray()){
TextView tv = new TextView(getContext(), null, 0x0, R.style.PBDCenterTextStyleWhite);
if(mCustomLayoutBuilder.getProgressMsgColor() != CustomLayoutBuilder.DEFAULT_COLOR && getContext() != null){
tv.setTextColor(ActivityCompat.getColor(getContext(), mCustomLayoutBuilder.getProgressMsgColor()));
}
if(mCustomLayoutBuilder.getProgressMsgDimen() != CustomLayoutBuilder.DEFAULT_DIMEN){
tv.setTextSize(getResources().getDimension(mCustomLayoutBuilder.getProgressMsgDimen()));
}
tv.setText(String.valueOf(digit));
mCenterTextViews.add(tv);
mllTextContainer.addView(tv);
}
}
}
private void initProgressMsg(){
mtvTitle.setText(mTitle);
mtvProgressText.setText(mProgressText);
if(mCustomLayoutBuilder.getProgressMsgColor() != CustomLayoutBuilder.DEFAULT_COLOR){
mtvProgressText.setTextColor(mCustomLayoutBuilder.getProgressMsgColor());
}
if(mCustomLayoutBuilder.getProgressMsgDimen() != CustomLayoutBuilder.DEFAULT_DIMEN){
mtvProgressText.setTextSize(getResources().getDimension(mCustomLayoutBuilder.getProgressMsgDimen()));
}
}
private void clearTextContainer(){
if(mllTextContainer.getChildCount() >= 0x0){
for(int i=0; i < mllTextContainer.getChildCount(); i++){
View v = mllTextContainer.getChildAt(i);
if(v instanceof TextView){
TextView tv = (TextView) v;
if(tv.getAnimation() != null){
tv.clearAnimation();
}
}
}
}
mllTextContainer.removeAllViews();
}
private void setWindowSize(){
Dialog dialog = getDialog();
if(dialog != null && dialog.getWindow() != null){
int width = 0x0, height = 0x0;
switch(mDialogType){
case TYPE_PROGRESS_BAR_ONLY_NO_ANIM:
case TYPE_PROGRESS_BAR_ONLY_ROTATE_ANIM:
case TYPE_PROGRESS_BAR_ONLY_FADE_ANIM:
width = ViewGroup.LayoutParams.WRAP_CONTENT; //getResources().getDimensionPixelSize(R.dimen.pbd_window_width);
height = ViewGroup.LayoutParams.WRAP_CONTENT; //getResources().getDimensionPixelSize(R.dimen.pbd_window_height);
break;
case TYPE_PROGRESS_BAR_AND_MSG:
width = ViewGroup.LayoutParams.MATCH_PARENT;
height = ViewGroup.LayoutParams.WRAP_CONTENT; //getResources().getDimensionPixelSize(R.dimen.pbd_textual_window_height);
break;
}
dialog.getWindow().setLayout(width, height);
}
}
/** Animation Methods **/
private void startAnimation(){
switch(mDialogType){
case TYPE_PROGRESS_BAR_ONLY_ROTATE_ANIM:
startRotateAnimations();
break;
case TYPE_PROGRESS_BAR_ONLY_FADE_ANIM:
startFadeInAnimations();
break;
}
}
private void startRotateAnimations(){
for(TextView tv : mCenterTextViews){
if(tv != null && tv.getText() != null && !TextUtils.isEmpty(tv.getText().toString().trim())) {
int i = mCenterTextViews.indexOf(tv);
RotateAnimation anim = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(CENTER_TEXT_VIEWS_ANIMATION_DURATION);
anim.setFillAfter(true);
anim.setStartOffset(CENTER_TEXT_VIEWS_START_OFFSET_MULTIPLIER * i);
if (i == (mCenterTextViews.size() - 0x1)) {
anim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
startRotateAnimations();
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
}
tv.startAnimation(anim);
}
}
}
private void startFadeInAnimations(){
for(TextView tv : mCenterTextViews){
if(tv != null && tv.getText() != null && !TextUtils.isEmpty(tv.getText().toString().trim())) {
int i = mCenterTextViews.indexOf(tv);
AlphaAnimation anim = new AlphaAnimation(0x1, 0x0);
anim.setDuration(CENTER_TEXT_VIEWS_ANIMATION_DURATION);
anim.setFillAfter(true);
anim.setStartOffset(CENTER_TEXT_VIEWS_START_OFFSET_MULTIPLIER * i);
if (i == (mCenterTextViews.size() - 0x1)) {
anim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
startFadeOutAnimations();
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
}
tv.startAnimation(anim);
}
}
}
private void startFadeOutAnimations(){
for(TextView tv : mCenterTextViews){
int i = mCenterTextViews.indexOf(tv);
AlphaAnimation anim = new AlphaAnimation(0x0, 0x1);
anim.setDuration(CENTER_TEXT_VIEWS_ANIMATION_DURATION);
anim.setFillAfter(true);
anim.setStartOffset(CENTER_TEXT_VIEWS_START_OFFSET_MULTIPLIER * i);
if(i == (mCenterTextViews.size() - 0x1)){
anim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
startFadeInAnimations();
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
}
tv.startAnimation(anim);
}
}
/** Progress Bar Custom Layout Builder Class **/
public static class CustomLayoutBuilder implements Parcelable {
/** Shapes **/
private static final int RING = GradientDrawable.RING;
/** Colors **/
private static final int[][] COLORS_MATRIX_RYGB = new int[][]{
new int[]{ R.color.transparent, R.color.transparent, R.color.material_red_A700 },
new int[]{ R.color.transparent, R.color.transparent, R.color.material_amber_A700 },
new int[]{ R.color.transparent, R.color.transparent, R.color.material_light_green_A700 },
new int[]{ R.color.transparent, R.color.transparent, R.color.material_blue_A700 }
};
private static final int DEFAULT_COLOR = -0x1;
/** Dimens **/
private static final int DEFAULT_DIMEN = -0x1;
private static final int[] DEFAULT_PROGRESS_BAR_DIMEN = new int[]{};
/** Indexes **/
private static final int INDEX_PROGRESS_BAR_WIDTH = 0x0;
private static final int INDEX_PROGRESS_BAR_HEIGHT = 0x1;
private static final int INDEX_FROM_DEGREES = 0x0;
private static final int INDEX_TO_DEGREES = 0x1;
/** Arrays Sizes **/
private static final int SIZE_PROGRESS_BAR_DIMENS_ARRAY = 0x2;
/** Matrix Columns Number **/
private static final int NUM_COLUMNS_DEGREES_MATRIX = 0x2; /* Degrees Matrix Index -> degrees[3] = { fromDegrees, toDegrees } */
private static final int NUM_COLUMNS_COLORS_MATRIX = 0x3; /* GradientDrawable Colors Matrix Index -> colors[3] = { startColor, centerColor, endColor } */
/** Drawables Layout Resource IDs **/
private static final int LAYOUT_RES_PROGRESS_BAR_RINGS = R.drawable.progress_bar_rings;
/** Layout Data: Four Rings Overlaid **/
private static final int RINGS_OVERLAID_LAYERS = 0x4;
private static final int[][] RINGS_OVERLAID_DEGREES = new int[][]{ new int[]{ 300, 660 }, new int[]{ 210, 570 }, new int[]{ 120, 480 }, new int[]{ 30, 390 } };
private static final int[] RINGS_OVERLAID_SHAPES = new int[]{ RING, RING, RING, RING };
private static final int[] RINGS_OVERLAID_INNER_RADIUS = new int[]{ R.dimen.pbd_inner_radius_60dp, R.dimen.pbd_inner_radius_60dp, R.dimen.pbd_inner_radius_60dp, R.dimen.pbd_inner_radius_60dp};
private static final int[] RINGS_OVERLAID_THICKNESS = new int[]{ R.dimen.pbd_thickness_40dp, R.dimen.pbd_thickness_30dp, R.dimen.pbd_thickness_20dp, R.dimen.pbd_thickness_10dp };
/** Layout Data: Four Rings Spaced **/
private static final int RINGS_SPACED_LAYERS = 0x4;
private static final int[][] RINGS_SPACED_DEGREES = new int[][]{ new int[]{ 180, 540 }, new int[]{ 0, 360 }, new int[]{ 90, 450 }, new int[]{ 270, 630 } };
private static final int[] RINGS_SPACED_SHAPES = new int[]{ RING, RING, RING, RING };
private static final int[] RINGS_SPACED_INNER_RADIUS = new int[]{ R.dimen.pbd_inner_radius_30dp, R.dimen.pbd_inner_radius_60dp, R.dimen.pbd_inner_radius_90dp, R.dimen.pbd_inner_radius_120dp };
private static final int[] RINGS_SPACED_THICKNESS = new int[]{ R.dimen.pbd_thickness_10dp, R.dimen.pbd_thickness_15dp, R.dimen.pbd_thickness_20dp, R.dimen.pbd_thickness_25dp };
private int mLayoutResID;
private int[] mProgressBarDimens;
private int mNumLayers;
private int[][] mRotateDegrees;
private int[] mShapes;
private int[] mInnerRadius;
private int[] mThickness;
private int[][] mColors;
private int mProgressMsgColor;
private int mProgressMsgDimen;
public static Parcelable.Creator CREATOR = new CreatorCustomLayoutBuilder();
/** Constructors **/
private CustomLayoutBuilder(int layoutResID, int[] progressBarDimens, int numLayers, int[][] degreesMatrix, int[] shapes, int[] innerRadius, int[] thickness,
int[][] colorsMatrix, int msgColor, int progressMsgDimen){
mLayoutResID = layoutResID;
mProgressBarDimens = progressBarDimens;
mNumLayers = numLayers;
mRotateDegrees = degreesMatrix;
mShapes = shapes;
mInnerRadius = innerRadius;
mThickness = thickness;
mColors = colorsMatrix;
mProgressMsgColor = msgColor;
mProgressMsgDimen = progressMsgDimen;
}
private CustomLayoutBuilder(Parcel in){
mLayoutResID = in.readInt();
mProgressBarDimens = new int[SIZE_PROGRESS_BAR_DIMENS_ARRAY];
in.readIntArray(mProgressBarDimens);
mNumLayers = in.readInt();
int[] tempArray = new int[NUM_COLUMNS_DEGREES_MATRIX * mNumLayers];
in.readIntArray(tempArray);
mRotateDegrees = DataUtils.arrayToMatrix(tempArray, NUM_COLUMNS_DEGREES_MATRIX);
mShapes = new int[mNumLayers];
in.readIntArray(mShapes);
mInnerRadius = new int[mNumLayers];
in.readIntArray(mInnerRadius);
mThickness = new int[mNumLayers];
in.readIntArray(mThickness);
tempArray = new int[NUM_COLUMNS_COLORS_MATRIX * mNumLayers];
in.readIntArray(tempArray);
mColors = DataUtils.arrayToMatrix(tempArray, NUM_COLUMNS_COLORS_MATRIX);
mProgressMsgColor = in.readInt();
mProgressMsgDimen = in.readInt();
}
/** Public Static Factory Methods **/
public static CustomLayoutBuilder initLayoutRingsOverlaid(){
return new CustomLayoutBuilder(LAYOUT_RES_PROGRESS_BAR_RINGS, DEFAULT_PROGRESS_BAR_DIMEN, RINGS_OVERLAID_LAYERS, RINGS_OVERLAID_DEGREES, RINGS_OVERLAID_SHAPES,
RINGS_OVERLAID_INNER_RADIUS, RINGS_OVERLAID_THICKNESS, COLORS_MATRIX_RYGB, R.color.material_white, DEFAULT_DIMEN);
}
public static CustomLayoutBuilder initLayoutRingsOverlaid(int[] resProgBarDimens, int resProgMsgColor, int resProgMsgDimen){
return new CustomLayoutBuilder(LAYOUT_RES_PROGRESS_BAR_RINGS, resProgBarDimens, RINGS_OVERLAID_LAYERS, RINGS_OVERLAID_DEGREES, RINGS_OVERLAID_SHAPES,
RINGS_OVERLAID_INNER_RADIUS, RINGS_OVERLAID_THICKNESS, COLORS_MATRIX_RYGB, resProgMsgColor, resProgMsgDimen);
}
public static CustomLayoutBuilder initLayoutRingsSpaced(){
return new CustomLayoutBuilder(LAYOUT_RES_PROGRESS_BAR_RINGS, DEFAULT_PROGRESS_BAR_DIMEN, RINGS_SPACED_LAYERS, RINGS_SPACED_DEGREES, RINGS_SPACED_SHAPES,
RINGS_SPACED_INNER_RADIUS, RINGS_SPACED_THICKNESS, COLORS_MATRIX_RYGB, DEFAULT_COLOR, DEFAULT_DIMEN);
}
/** Override Parcelable Methods **/
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mLayoutResID);
out.writeIntArray(mProgressBarDimens);
out.writeInt(mNumLayers);
out.writeIntArray(DataUtils.matrixToArray(mRotateDegrees));
out.writeIntArray(mShapes);
out.writeIntArray(mInnerRadius);
out.writeIntArray(mThickness);
out.writeIntArray(DataUtils.matrixToArray(mColors));
out.writeInt(mProgressMsgColor);
out.writeInt(mProgressMsgDimen);
}
/** Getter & Setter Methods **/
private int getLayoutResID() {
return mLayoutResID;
}
private void setLayoutResID(int layoutResID) {
this.mLayoutResID = layoutResID;
}
private int[] getProgressBarDimens() {
return mProgressBarDimens;
}
private void setProgressBarDimens(int[] progressBarDimens) {
this.mProgressBarDimens = progressBarDimens;
}
private int getProgressBarWidthDimen(){ // Used to check if 'mProgressBarDimens' array is set.
if(mProgressBarDimens != null && mProgressBarDimens.length == SIZE_PROGRESS_BAR_DIMENS_ARRAY){
return mProgressBarDimens[INDEX_PROGRESS_BAR_WIDTH];
} else {
return -0x1;
}
}
private int getProgressBarHeightDimen(){
return mProgressBarDimens[INDEX_PROGRESS_BAR_HEIGHT];
}
private int getNumLayers() {
return mNumLayers;
}
private void setNumLayers(int numLayers) {
this.mNumLayers = numLayers;
}
private int[][] getRotateDegrees() {
return mRotateDegrees;
}
private void setRotateDegrees(int[][] rotateDegrees) {
this.mRotateDegrees = rotateDegrees;
}
private int[] getShapes() {
return mShapes;
}
private void setShapes(int[] shapes) {
this.mShapes = shapes;
}
private int[] getInnerRadius() {
return mInnerRadius;
}
private void setInnerRadius(int[] innerRadius) {
this.mInnerRadius = innerRadius;
}
private int[] getThickness() {
return mThickness;
}
private void setThickness(int[] thickness) {
this.mThickness = thickness;
}
private int[][] getColorsMatrix() {
return mColors;
}
private void setColorsMatrix(int[][] colorsMatrix) {
this.mColors = colorsMatrix;
}
private int getProgressMsgColor() {
return mProgressMsgColor;
}
private void setProgressMsgColor(int progressMsgColor) {
this.mProgressMsgColor = progressMsgColor;
}
private int getProgressMsgDimen() {
return mProgressMsgDimen;
}
private void setProgressMsgDimen(int progressMsgDimen) {
this.mProgressMsgDimen = progressMsgDimen;
}
/** Public Methods **/
private int[] getDegreesMatrixRow(int numRow){
if(mRotateDegrees != null && mRotateDegrees.length > numRow) {
return mRotateDegrees[numRow];
} else {
return new int[]{};
}
}
private int getShape(int position){
if(mShapes != null && mShapes.length > position){
return mShapes[position];
} else {
return -0x1;
}
}
private int getInnerRadius(int position){
if(mInnerRadius != null && mInnerRadius.length > position){
return mInnerRadius[position];
} else {
return -0x1;
}
}
private int getThickness(int position){
if(mThickness != null && mThickness.length > position){
return mThickness[position];
} else {
return -0x1;
}
}
private int[] getColorsMatrixRow(int numRow) {
if(mColors != null && mColors.length > numRow){
return mColors[numRow];
} else {
return new int[]{};
}
}
/** Private Static Class Parcelable Creator **/
private static class CreatorCustomLayoutBuilder implements Parcelable.Creator<CustomLayoutBuilder> {
#Override
public CustomLayoutBuilder createFromParcel(Parcel in) {
return new CustomLayoutBuilder(in);
}
#Override
public CustomLayoutBuilder[] newArray(int size) {
return new CustomLayoutBuilder[size];
}
}
}
}
The class has a progress bar with no dialog and custom text over it.
The text can have 2 animations:
Fade In -> Fade Out animation: digits will fade out like a sequentially from left to right. At end of text will restart from left.
Rotate Animation: same sequential behavior but the digits rotate on themself one by one instead of fading out.
Other way is a Progress Bar with a dialog (Title + Message)
Rest of code
Code of utils methods:
public static int[] resourcesIDsToColors(Context context, int[] resIDs){
int[] colors = new int[resIDs.length];
for(int i=0; i < resIDs.length; i++){
colors[i] = ActivityCompat.getColor(context, resIDs[i]);
}
return colors;
}
public static void setSubClassFieldIntValue(Object objField, Class<?> superClass, String subName, String fieldName, int fieldValue){
Class<?> subClass = getSubClass(superClass, subName);
if(subClass != null) {
Field field = getClassField(subClass, fieldName);
if (field != null) {
setFieldValue(objField, field, fieldValue);
}
}
}
public static Class<?> getSubClass(Class<?> superClass, String subName){
Class<?>[] classes = superClass.getDeclaredClasses();
if(classes != null && classes.length > 0){
for(Class<?> clss : classes){
if(clss.getSimpleName().equals(subName)){
return clss;
}
}
}
return null;
}
public static Field getClassField(Class<?> clss, String fieldName){
try {
Field field = clss.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException nsfE) {
Log.e(TAG, nsfE.getMessage());
} catch (SecurityException sE){
Log.e(TAG, sE.getMessage());
} catch (Exception e){
Log.e(TAG, e.getMessage());
}
return null;
}
public static int[][] arrayToMatrix(int[] array, int numColumns){
int numRows = array.length / numColumns;
int[][] matrix = new int[numRows][numColumns];
int nElemens = array.length;
for(int i=0; i < nElemens; i++){
matrix[i / numColumns][i % numColumns] = array[i];
}
return matrix;
}
public static int[] matrixToArray(int[][] matrix){
/** [+] Square matrix of order n -> A matrix with n rows and n columns, same number of rows and columns.
* [+] Matrix rows & columns number annotations:
* matrix[rows][columns] matrix (rows x columns) matrix rows, columns rows by columns matrix
* **/
int numRows = matrix.length;
int[] arr = new int[]{};
for(int i=0; i < numRows; i++){
int numColumns = matrix[i].length;
int[] row = new int[numColumns];
for(int j=0; j < numColumns; j++){
row[j] = matrix[i][j];
}
arr = ArrayUtils.addAll(arr, row);
}
return arr;
}
Code of default layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#color/transparent">
<LinearLayout
android:id="#+id/layout_progress_bar_only"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<android.support.constraint.ConstraintLayout
android:id="#+id/dpb_constraint_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="#+id/dpb_progress_bar"
android:layout_width="#dimen/pbd_progressbar_width_2"
android:layout_height="#dimen/pbd_progressbar_height_2"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"/>
<LinearLayout
android:id="#+id/dpb_text_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"/>
</android.support.constraint.ConstraintLayout>
</LinearLayout>
<LinearLayout
android:id="#+id/layout_progress_bar_and_msg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
style="#style/PBDTextualMainLayoutStyle">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardElevation="#dimen/pbd_textual_card_elevation">
<TextView
android:id="#+id/pbd_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/PBDTextualTitle"/>
</android.support.v7.widget.CardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="#dimen/pbd_textual_main_layout_height"
android:orientation="horizontal">
<android.support.v7.widget.CardView
android:layout_width="#dimen/pbd_textual_progressbar_width"
android:layout_height="#dimen/pbd_textual_progressbar_height">
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="#+id/dpb_progress_bar_and_msg"
android:layout_width="match_parent"
android:layout_height="match_parent"
style="#style/PBDProgressBarStyle"/>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="#dimen/pbd_textual_msg_container_height">
<TextView
android:id="#+id/dpb_progress_msg"
android:layout_width="match_parent"
android:layout_height="match_parent"
style="#style/PBDTextualProgressMsgStyle"/>
</android.support.v7.widget.CardView>
</LinearLayout>
</LinearLayout>
</LinearLayout>
Progress Bar Rings:
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<rotate
android:fromDegrees="300"
android:toDegrees="660">
<shape
android:shape="ring"
android:useLevel="false">
<gradient
android:type="sweep"/>
</shape>
</rotate>
</item>
<item>
<rotate
android:fromDegrees="210"
android:toDegrees="570">
<shape
android:shape="ring"
android:useLevel="false">
<gradient
android:type="sweep"/>
</shape>
</rotate>
</item>
<item>
<rotate
android:fromDegrees="120"
android:toDegrees="480">
<shape
android:shape="ring"
android:useLevel="false">
<gradient
android:type="sweep"
android:startColor="#00000000"
android:centerColor="#00000000"/>
</shape>
</rotate>
</item>
<item>
<rotate
android:fromDegrees="30"
android:toDegrees="390">
<shape
android:shape="ring"
android:useLevel="false">
<solid android:color="#000000"/>
<gradient
android:type="sweep"/>
</shape>
</rotate>
</item>
</layer-list>
Dimens Resources:
<!-- ProgressBarDialog Dimens (Normal & Textual Versions) -->
<dimen name="pbd_window_width">250dp</dimen>
<dimen name="pbd_window_height">250dp</dimen>
<dimen name="pbd_progressbar_width_1">250dp</dimen>
<dimen name="pbd_progressbar_height_1">250dp</dimen>
<dimen name="pbd_progressbar_width_2">400dp</dimen>
<dimen name="pbd_progressbar_height_2">400dp</dimen>
<dimen name="pbd_textual_window_height">170dp</dimen>
<dimen name="pbd_textual_main_layout_height">150dp</dimen>
<dimen name="pbd_textual_progressbar_width">150dp</dimen>
<dimen name="pbd_textual_progressbar_height">150dp</dimen>
<dimen name="pbd_textual_msg_container_height">150dp</dimen>
<dimen name="pbd_textual_main_layout_margin_horizontal">50dp</dimen>
<dimen name="pbd_textual_main_layout_padding_horizontal">5dp</dimen>
<dimen name="pbd_textual_main_layout_padding_bottom">15dp</dimen>
<dimen name="pbd_textual_title_padding">4dp</dimen>
<dimen name="pbd_textual_msg_container_margin">3dp</dimen>
<dimen name="pbd_textual_msg_container_padding">3dp</dimen>
<dimen name="pbd_textual_card_elevation">15dp</dimen>
<dimen name="pbd_textual_progressmsg_padding_start">10dp</dimen>
<dimen name="pbd_inner_radius_30dp">30dp</dimen>
<dimen name="pbd_inner_radius_60dp">60dp</dimen>
<dimen name="pbd_inner_radius_90dp">90dp</dimen>
<dimen name="pbd_inner_radius_120dp">120dp</dimen>
<dimen name="pbd_thickness_40dp">40dp</dimen>
<dimen name="pbd_thickness_30dp">30dp</dimen>
<dimen name="pbd_thickness_25dp">25dp</dimen>
<dimen name="pbd_thickness_20dp">20dp</dimen>
<dimen name="pbd_thickness_15dp">15dp</dimen>
<dimen name="pbd_thickness_10dp">10dp</dimen>
Styles Resources:
<!-- PROGRESS BAR DIALOG STYLES -->
<style name="PBDCenterTextStyleWhite">
<item name="android:textAlignment">center</item>
<item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
<item name="android:textStyle">bold|italic</item>
<item name="android:textColor">#color/material_white</item>
<item name="android:layout_gravity">center</item>
<item name="android:gravity">center</item>
</style>
<style name="PBDTextualTitle">
<item name="android:textAlignment">viewStart</item>
<item name="android:textAppearance">?android:attr/textAppearanceLargeInverse</item>
<item name="android:textStyle">bold|italic</item>
<item name="android:textColor">#color/colorAccent</item>
<item name="android:padding">#dimen/pbd_textual_title_padding</item>
<item name="android:layout_gravity">start</item>
<item name="android:gravity">center_vertical|start</item>
<item name="android:background">#color/colorPrimaryDark</item>
</style>
<style name="PBDTextualProgressMsgStyle">
<item name="android:textAlignment">viewStart</item>
<item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
<item name="android:textColor">#color/material_black</item>
<item name="android:textStyle">normal|italic</item>
<item name="android:paddingStart">#dimen/pbd_textual_progressmsg_padding_start</item>
<item name="android:layout_gravity">start</item>
<item name="android:gravity">center_vertical|start</item>
<item name="android:background">#color/material_yellow_A100</item>
</style>
<style name="PBDTextualMainLayoutStyle">
<item name="android:paddingLeft">#dimen/pbd_textual_main_layout_padding_horizontal</item>
<item name="android:paddingRight">#dimen/pbd_textual_main_layout_padding_horizontal</item>
<item name="android:paddingBottom">#dimen/pbd_textual_main_layout_padding_bottom</item>
<item name="android:background">#color/colorPrimaryDark</item>
</style>
<style name="PBDProgressBarStyle">
<item name="android:layout_gravity">center</item>
<item name="android:gravity">center</item>
</style>
How could i highlight Gallery selected item without adding the gray border to the image,
without using this.
TypedArray typArray = obtainStyledAttributes(R.styleable.GalleryTheme);
GalItemBg = typArray.getResourceId(
R.styleable.GalleryTheme_android_galleryItemBackground, 3);
typArray.recycle();
and could i add a reflect to the images,
you can define your own view like this:
a custom background:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle" android:layout_width="wrap_content">
<stroke android:width="1dp" android:color="#FF000000" />
<solid android:color="#00000000" />
<padding android:left="1dp" android:top="1dp" android:right="1dp"
android:bottom="1dp" />
<corners android:radius="1dp" />
</shape>
</item>
<item android:top="1dp" android:bottom="1dp">
<shape android:shape="rectangle">
<gradient android:startColor="#252525" android:endColor="#252525"
android:angle="270" android:centerColor="#545454" />
<!-- border width and color -->
<stroke android:width="1dp" android:color="#FFDDDDDD" />
</shape>
</item>
</layer-list>
its adapter:
public class AdapterGalleryProducts extends ArrayAdapter<String> {
private int ITEM_WIDTH = 136;
private int ITEM_HEIGHT = 88;
private final int mGalleryItemBackground;
private final Context mContext;
private final float mDensity;
public AdapterGalleryProducts(Context context, int resource,
List<String> items) {
super(context, resource, items);
mContext = context;
TypedArray a = mContext
.obtainStyledAttributes(R.styleable.Gallery1);
mGalleryItemBackground = R.drawable.background02;
a.recycle();
mDensity = mContext.getResources().getDisplayMetrics().density;
boInvProducts = new BoInvProducts(mContext);
}
public void setImageSize(int width, int height) {
ITEM_WIDTH = width;
ITEM_HEIGHT = height;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
convertView = new ImageView(mContext);
imageView = (ImageView) convertView;
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.setLayoutParams(new Gallery.LayoutParams(
(int) (ITEM_WIDTH * mDensity + 0.5f),
(int) (ITEM_HEIGHT * mDensity + 0.5f)));
// The preferred Gallery item background
imageView.setBackgroundResource(mGalleryItemBackground);
imageView.setPadding(5, 5, 5, 5);
} else {
imageView = (ImageView) convertView;
}
Bitmap bitmap = null;
try {
bitmap = getBitmapByFilePath(getItem(position));
} catch (Exception e) {
e.printStackTrace();
}
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
imageView.setAdjustViewBounds(true);
}
return imageView;
}
}
and add animation effects to selected item:
gal.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
gal_onItemClick(parent, v, position, id);
}
});
protected void gal_onItemClick(AdapterView<?> parent, View v,
int position, long id) {
// animate selected image
Animation growAnimation = AnimationUtils.loadAnimation(this,
R.anim.grow_shrink_image);
v.startAnimation(growAnimation);
}
an example of animation (grow_shrink_image.xml):
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200" android:fromXScale="1.0" android:toXScale="1.20"
android:fromYScale="1.0" android:toYScale="1.20" android:pivotX="50%"
android:pivotY="50%" android:interpolator="#android:anim/accelerate_interpolator"
android:fillAfter="false" />
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:startOffset="200" android:duration="200" android:fromXScale="1.0"
android:toXScale="0.8333" android:fromYScale="1.0" android:toYScale="0.8333"
android:pivotX="50%" android:pivotY="50%"
android:interpolator="#android:anim/accelerate_interpolator"
android:fillAfter="false" />
</set>