Constant ripple effect in android - android

What I'm trying to do is to have a constant ripple effect on the background of a LinearLayout. Why? Basically, this LinearLayout indicates live users watching this item. So I want the background to have a constant ripple animation similar to some apps that have a live indicator with a ripple effect on the background of that indicator. I hope my question was clear.
Example:
I want this effect to be happing constantly

Hi i tried to code something like this and below is what i come close to. You can always tweek numbers to slow down the animation and other things.
1) Create a ripple drawable background in your res/drawable named temp_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#color/colorPrimary"
>
<item android:id="#android:id/mask"
android:drawable="#android:color/holo_green_dark"
>
</item>
<item
android:drawable="#android:color/holo_orange_dark">
</item>
</ripple>
2) assign the background to possible view candidate like below, here AppCompatButton to android:background="#drawable/temp_ripple"
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatButton
android:id="#+id/btnLive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:background="#drawable/temp_ripple"
android:foreground="?selectableItemBackground"
android:text="12.5k Live"
android:textColor="#android:color/white" />
</RelativeLayout>
3) Get the ripple drawable from the view and create a runnable running after 2 sec to repeat the animation by setting the states of the ripple drawable in click listener of the button
package com.example.android.treasureHunt
import android.content.res.ColorStateList
import android.graphics.drawable.RippleDrawable
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.util.Log
import android.view.MotionEvent
import android.view.View
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.temp_activity.*
class TempActivity : AppCompatActivity(R.layout.temp_activity) {
val handler = Handler()
lateinit var runnable: Runnable
var count = 0
lateinit var rippleDrawable: RippleDrawable
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
rippleDrawable = btnLive.background as RippleDrawable
setLiveCountListener()
btnLive.setOnClickListener {
Log.d("TAG++", "button clicked")
rippleDrawable.state = intArrayOf(
android.R.attr.state_pressed,
android.R.attr.state_enabled
)
}
}
private fun setLiveCountListener() {
runnable = Runnable {
rippleDrawable.state = intArrayOf()
btnLive.performClick()
//to perform another runnable after some time creating a race condition
handler.postDelayed(runnable, 2000)
//condition to breakout from loop
if (count == 10) {
handler.removeCallbacks(runnable)
}
Log.d("TAG++", "Loop running")
}
//trigger the start of the ui thread
handler.postDelayed(runnable, 2000)
}
}

After taking many hours I have created custom class for infinite ripple view as per you want using this lib with customisation.
InfiniteRippleLayout
public class InfiniteRippleLayout extends FrameLayout {
/**
* Author:Hardik Talaviya
* Date: 2020.02.15 1:30 PM
* Describe:
*/
private static final int DEFAULT_DURATION = 350;
private static final int DEFAULT_FADE_DURATION = 75;
private static final float DEFAULT_ALPHA = 0.2f;
private static final int DEFAULT_COLOR = Color.BLACK;
private static final int DEFAULT_BACKGROUND = Color.TRANSPARENT;
private static final boolean DEFAULT_DELAY_CLICK = true;
private static final boolean DEFAULT_PERSISTENT = false;
private static final boolean DEFAULT_SEARCH_ADAPTER = false;
private static final boolean DEFAULT_RIPPLE_OVERLAY = false;
private static final int DEFAULT_ROUNDED_CORNERS = 0;
private static final int FADE_EXTRA_DELAY = 50;
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Rect bounds = new Rect();
private int rippleColor;
private boolean rippleOverlay;
private int rippleDuration;
private int rippleAlpha;
private boolean rippleDelayClick;
private int rippleFadeDuration;
private boolean ripplePersistent;
private Drawable rippleBackground;
private boolean rippleInAdapter;
private float rippleRoundedCorners;
private float radius;
private AdapterView parentAdapter;
private View childView;
private AnimatorSet rippleAnimator;
private Point currentCoords = new Point();
private int layerType;
private int positionInAdapter;
/*
* Animations
*/
private Property<InfiniteRippleLayout, Float> radiusProperty
= new Property<InfiniteRippleLayout, Float>(Float.class, "radius") {
#Override
public Float get(InfiniteRippleLayout object) {
return object.getRadius();
}
#Override
public void set(InfiniteRippleLayout object, Float value) {
object.setRadius(value);
}
};
private Property<InfiniteRippleLayout, Integer> circleAlphaProperty
= new Property<InfiniteRippleLayout, Integer>(Integer.class, "rippleAlpha") {
#Override
public Integer get(InfiniteRippleLayout object) {
return object.getRippleAlpha();
}
#Override
public void set(InfiniteRippleLayout object, Integer value) {
object.setRippleAlpha(value);
}
};
public InfiniteRippleLayout(Context context) {
this(context, null, 0);
}
public InfiniteRippleLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public InfiniteRippleLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setWillNotDraw(false);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.InfiniteRippleLayout);
rippleColor = a.getColor(R.styleable.InfiniteRippleLayout_mrl_rippleColor, DEFAULT_COLOR);
rippleOverlay = a.getBoolean(R.styleable.InfiniteRippleLayout_mrl_rippleOverlay, DEFAULT_RIPPLE_OVERLAY);
rippleDuration = a.getInt(R.styleable.InfiniteRippleLayout_mrl_rippleDuration, DEFAULT_DURATION);
rippleAlpha = (int) (255 * a.getFloat(R.styleable.InfiniteRippleLayout_mrl_rippleAlpha, DEFAULT_ALPHA));
rippleDelayClick = a.getBoolean(R.styleable.InfiniteRippleLayout_mrl_rippleDelayClick, DEFAULT_DELAY_CLICK);
rippleFadeDuration = a.getInteger(R.styleable.InfiniteRippleLayout_mrl_rippleFadeDuration, DEFAULT_FADE_DURATION);
rippleBackground = new ColorDrawable(a.getColor(R.styleable.InfiniteRippleLayout_mrl_rippleBackground, DEFAULT_BACKGROUND));
ripplePersistent = a.getBoolean(R.styleable.InfiniteRippleLayout_mrl_ripplePersistent, DEFAULT_PERSISTENT);
rippleInAdapter = a.getBoolean(R.styleable.InfiniteRippleLayout_mrl_rippleInAdapter, DEFAULT_SEARCH_ADAPTER);
rippleRoundedCorners = a.getDimensionPixelSize(R.styleable.InfiniteRippleLayout_mrl_rippleRoundedCorners, DEFAULT_ROUNDED_CORNERS);
a.recycle();
paint.setColor(rippleColor);
paint.setAlpha(rippleAlpha);
enableClipPathSupportIfNecessary();
startRipple();
}
#Override
public final void addView(View child, int index, ViewGroup.LayoutParams params) {
if (getChildCount() > 0) {
throw new IllegalStateException("MaterialRippleLayout can host only one child");
}
//noinspection unchecked
childView = child;
super.addView(child, index, params);
}
#Override
public void setOnClickListener(OnClickListener onClickListener) {
if (childView == null) {
throw new IllegalStateException("MaterialRippleLayout must have a child view to handle clicks");
}
childView.setOnClickListener(onClickListener);
}
#Override
public void setOnLongClickListener(OnLongClickListener onClickListener) {
if (childView == null) {
throw new IllegalStateException("MaterialRippleLayout must have a child view to handle clicks");
}
childView.setOnLongClickListener(onClickListener);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return !findClickableViewInChild(childView, (int) event.getX(), (int) event.getY());
}
private void startRipple() {
float endRadius = getEndRadius();
cancelAnimations();
rippleAnimator = new AnimatorSet();
rippleAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
if (!ripplePersistent) {
setRadius(0);
setRippleAlpha(rippleAlpha);
}
if (rippleDelayClick) {
startRipple();
}
childView.setPressed(false);
}
});
ObjectAnimator ripple = ObjectAnimator.ofFloat(this, radiusProperty, radius, endRadius);
ripple.setDuration(rippleDuration);
ripple.setInterpolator(new DecelerateInterpolator());
ObjectAnimator fade = ObjectAnimator.ofInt(this, circleAlphaProperty, rippleAlpha, 0);
fade.setDuration(rippleFadeDuration);
fade.setInterpolator(new AccelerateInterpolator());
fade.setStartDelay(rippleDuration - rippleFadeDuration - FADE_EXTRA_DELAY);
if (ripplePersistent) {
rippleAnimator.play(ripple);
} else if (getRadius() > endRadius) {
fade.setStartDelay(0);
rippleAnimator.play(fade);
} else {
rippleAnimator.playTogether(ripple, fade);
}
rippleAnimator.start();
}
private void cancelAnimations() {
if (rippleAnimator != null) {
rippleAnimator.cancel();
rippleAnimator.removeAllListeners();
}
}
private float getEndRadius() {
final int width = getWidth();
final int height = getHeight();
final int halfWidth = width / 2;
final int halfHeight = height / 2;
final float radiusX = halfWidth > currentCoords.x ? width - currentCoords.x : currentCoords.x;
final float radiusY = halfHeight > currentCoords.y ? height - currentCoords.y : currentCoords.y;
return (float) Math.sqrt(Math.pow(radiusX, 2) + Math.pow(radiusY, 2)) * 1.2f;
}
private AdapterView findParentAdapterView() {
if (parentAdapter != null) {
return parentAdapter;
}
ViewParent current = getParent();
while (true) {
if (current instanceof AdapterView) {
parentAdapter = (AdapterView) current;
return parentAdapter;
} else {
try {
current = current.getParent();
} catch (NullPointerException npe) {
throw new RuntimeException("Could not find a parent AdapterView");
}
}
}
}
private boolean adapterPositionChanged() {
if (rippleInAdapter) {
int newPosition = findParentAdapterView().getPositionForView(InfiniteRippleLayout.this);
final boolean changed = newPosition != positionInAdapter;
positionInAdapter = newPosition;
if (changed) {
cancelAnimations();
childView.setPressed(false);
setRadius(0);
}
return changed;
}
return false;
}
private boolean findClickableViewInChild(View view, int x, int y) {
if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View child = viewGroup.getChildAt(i);
final Rect rect = new Rect();
child.getHitRect(rect);
final boolean contains = rect.contains(x, y);
if (contains) {
return findClickableViewInChild(child, x - rect.left, y - rect.top);
}
}
} else if (view != childView) {
return (view.isEnabled() && (view.isClickable() || view.isLongClickable() || view.isFocusableInTouchMode()));
}
return view.isFocusableInTouchMode();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
bounds.set(0, 0, w, h);
rippleBackground.setBounds(bounds);
}
#Override
public boolean isInEditMode() {
return true;
}
/*
* Drawing
*/
#Override
public void draw(Canvas canvas) {
final boolean positionChanged = adapterPositionChanged();
currentCoords = new Point(getWidth() / 2, getHeight() / 2);
if (rippleOverlay) {
if (!positionChanged) {
rippleBackground.draw(canvas);
}
super.draw(canvas);
if (!positionChanged) {
if (rippleRoundedCorners != 0) {
Path clipPath = new Path();
RectF rect = new RectF(0, 0, canvas.getWidth(), canvas.getHeight());
clipPath.addRoundRect(rect, rippleRoundedCorners, rippleRoundedCorners, Path.Direction.CW);
canvas.clipPath(clipPath);
}
canvas.drawCircle(currentCoords.x, currentCoords.y, radius, paint);
}
} else {
if (!positionChanged) {
rippleBackground.draw(canvas);
canvas.drawCircle(currentCoords.x, currentCoords.y, radius, paint);
}
super.draw(canvas);
}
}
private float getRadius() {
return radius;
}
public void setRadius(float radius) {
this.radius = radius;
invalidate();
}
public int getRippleAlpha() {
return paint.getAlpha();
}
public void setRippleAlpha(Integer rippleAlpha) {
paint.setAlpha(rippleAlpha);
invalidate();
}
/**
* {#link Canvas#clipPath(Path)} is not supported in hardware accelerated layers
* before API 18. Use software layer instead
* <p/>
* https://developer.android.com/guide/topics/graphics/hardware-accel.html#unsupported
*/
private void enableClipPathSupportIfNecessary() {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR1) {
if (rippleRoundedCorners != 0) {
layerType = getLayerType();
setLayerType(LAYER_TYPE_SOFTWARE, null);
} else {
setLayerType(layerType, null);
}
}
}
}
attributes.xml
Add this attributes in your res->values->attributes.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="InfiniteRippleLayout">
<attr name="mrl_rippleColor" format="color" localization="suggested" />
<attr name="mrl_rippleOverlay" format="boolean" localization="suggested" />
<attr name="mrl_rippleAlpha" format="float" localization="suggested" />
<attr name="mrl_rippleDuration" format="integer" localization="suggested" />
<attr name="mrl_rippleFadeDuration" format="integer" localization="suggested" />
<attr name="mrl_rippleBackground" format="color" localization="suggested" />
<attr name="mrl_rippleDelayClick" format="boolean" localization="suggested" />
<attr name="mrl_ripplePersistent" format="boolean" localization="suggested" />
<attr name="mrl_rippleInAdapter" format="boolean" localization="suggested" />
<attr name="mrl_rippleRoundedCorners" format="dimension" localization="suggested" />
</declare-styleable>
</resources>
Add effect using below xml code
<com.broooapps.curvegraphview.InfiniteRippleLayout
android:layout_width="match_parent"
android:layout_height="150dp"
app:mrl_rippleAlpha="0.2"
app:mrl_rippleColor="#585858"
app:mrl_rippleDelayClick="true"
app:mrl_rippleDuration="1100"
app:mrl_rippleOverlay="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="HARDIK TALAVIYA"
android:textColor="#000"
android:textSize="20sp" />
</com.broooapps.curvegraphview.InfiniteRippleLayout>
Result
I hope this can help you!

Related

Ripple effect over shape background on Button

[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);
}
}

How to create custom progress bar? [duplicate]

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>

Unable to set visibility of any relative layout in a View Group in Android

I am using a floating action button in my activity. I have created a related layout with a black opacity background. I want it to appear when the floating action button is clicked. The relative layout will appear behind the action button to make it look prominent.
Here is my xml layout
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:fab="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.paaltao.activity.HomeActivity">
<include
android:id="#+id/app_bar"
layout="#layout/app_bar" />
<it.neokree.materialtabs.MaterialTabHost
android:id="#+id/materialTabHost"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_below="#+id/app_bar"
app:accentColor="#color/greenMaterial"
app:hasIcons="true"
app:primaryColor="#color/primaryColor"
app:textColor="#color/white" />
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/materialTabHost" />
<RelativeLayout
android:id="#+id/white_opacity"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/materialTabHost"
android:background="#color/black80"
android:visibility="gone" />
<com.paaltao.classes.FloatingActionsMenu
android:id="#+id/multiple_actions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_gravity="end"
fab:fab_addButtonColorPressed="#color/white_pressed"
fab:fab_addButtonPlusIconColor="#color/white"
fab:fab_labelStyle="#style/menu_labels_style"
app:fab_addButtonColorNormal="#color/primaryColor">
<com.paaltao.classes.FloatingActionButton
android:id="#+id/action_b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_colorNormal="#color/white"
fab:fab_colorPressed="#color/white_pressed"
fab:fab_title="Action B" />
</com.paaltao.classes.FloatingActionsMenu>
</RelativeLayout>
I am basically trying to set visibility of the relative layout to View.VISIBLE in the floating action menu class which extends a view group. Now I am getting null pointer exception when I am setting the visibility as View.VISIBLE in the button's onClick event.
Here is my Java Code :
package com.paaltao.classes;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.ColorRes;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.paaltao.R;
import java.util.zip.Inflater;
public class FloatingActionsMenu extends ViewGroup {
public static final int EXPAND_UP = 0;
public static final int EXPAND_DOWN = 1;
public static final int EXPAND_LEFT = 2;
public static final int EXPAND_RIGHT = 3;
private static final int ANIMATION_DURATION = 300;
private static final float COLLAPSED_PLUS_ROTATION = 0f;
private static final float EXPANDED_PLUS_ROTATION = 90f + 45f;
private int mAddButtonPlusColor;
private int mAddButtonColorNormal;
private int mAddButtonColorPressed;
private int mAddButtonSize;
private boolean mAddButtonStrokeVisible;
private int mExpandDirection;
private int mButtonSpacing;
private int mLabelsMargin;
private int mLabelsVerticalOffset;
private boolean mExpanded;
private AnimatorSet mExpandAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION);
private AnimatorSet mCollapseAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION);
private AddFloatingActionButton mAddButton;
private RotatingDrawable mRotatingDrawable;
private int mMaxButtonWidth;
private int mMaxButtonHeight;
private int mLabelsStyle;
private int mButtonsCount;
private OnFloatingActionsMenuUpdateListener mListener;
private RelativeLayout whiteOverlay;
public interface OnFloatingActionsMenuUpdateListener {
void onMenuExpanded();
void onMenuCollapsed();
}
public FloatingActionsMenu(Context context) {
this(context, null);
}
public FloatingActionsMenu(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public FloatingActionsMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attributeSet) {
mButtonSpacing = (int) (getResources().getDimension(R.dimen.fab_actions_spacing) - getResources().getDimension(R.dimen.fab_shadow_radius) - getResources().getDimension(R.dimen.fab_shadow_offset));
mLabelsMargin = getResources().getDimensionPixelSize(R.dimen.fab_labels_margin);
mLabelsVerticalOffset = getResources().getDimensionPixelSize(R.dimen.fab_shadow_offset);
TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.FloatingActionsMenu, 0, 0);
mAddButtonPlusColor = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonPlusIconColor, getColor(android.R.color.white));
mAddButtonColorNormal = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonColorNormal, getColor(android.R.color.holo_blue_dark));
mAddButtonColorPressed = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonColorPressed, getColor(android.R.color.holo_blue_light));
mAddButtonSize = attr.getInt(R.styleable.FloatingActionsMenu_fab_addButtonSize, FloatingActionButton.SIZE_NORMAL);
mAddButtonStrokeVisible = attr.getBoolean(R.styleable.FloatingActionsMenu_fab_addButtonStrokeVisible, true);
mExpandDirection = attr.getInt(R.styleable.FloatingActionsMenu_fab_expandDirection, EXPAND_UP);
mLabelsStyle = attr.getResourceId(R.styleable.FloatingActionsMenu_fab_labelStyle, 0);
attr.recycle();
if (mLabelsStyle != 0 && expandsHorizontally()) {
throw new IllegalStateException("Action labels in horizontal expand orientation is not supported.");
}
createAddButton(context);
}
public void setOnFloatingActionsMenuUpdateListener(OnFloatingActionsMenuUpdateListener listener) {
mListener = listener;
}
private boolean expandsHorizontally() {
return mExpandDirection == EXPAND_LEFT || mExpandDirection == EXPAND_RIGHT;
}
private static class RotatingDrawable extends LayerDrawable {
public RotatingDrawable(Drawable drawable) {
super(new Drawable[] { drawable });
}
private float mRotation;
#SuppressWarnings("UnusedDeclaration")
public float getRotation() {
return mRotation;
}
#SuppressWarnings("UnusedDeclaration")
public void setRotation(float rotation) {
mRotation = rotation;
invalidateSelf();
}
#Override
public void draw(Canvas canvas) {
canvas.save();
canvas.rotate(mRotation, getBounds().centerX(), getBounds().centerY());
super.draw(canvas);
canvas.restore();
}
}
private void createAddButton(Context context) {
mAddButton = new AddFloatingActionButton(context) {
#Override
void updateBackground() {
mPlusColor = mAddButtonPlusColor;
mColorNormal = mAddButtonColorNormal;
mColorPressed = mAddButtonColorPressed;
mStrokeVisible = mAddButtonStrokeVisible;
super.updateBackground();
}
#Override
Drawable getIconDrawable() {
final RotatingDrawable rotatingDrawable = new RotatingDrawable(super.getIconDrawable());
mRotatingDrawable = rotatingDrawable;
final OvershootInterpolator interpolator = new OvershootInterpolator();
final ObjectAnimator collapseAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", EXPANDED_PLUS_ROTATION, COLLAPSED_PLUS_ROTATION);
final ObjectAnimator expandAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", COLLAPSED_PLUS_ROTATION, EXPANDED_PLUS_ROTATION);
collapseAnimator.setInterpolator(interpolator);
expandAnimator.setInterpolator(interpolator);
mExpandAnimation.play(expandAnimator);
mCollapseAnimation.play(collapseAnimator);
return rotatingDrawable;
}
};
mAddButton.setId(R.id.fab_expand_menu_button);
mAddButton.setSize(mAddButtonSize);
mAddButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
toggle();
// updateBackground();
}
});
addView(mAddButton, super.generateDefaultLayoutParams());
}
// public void updateBackground(){
// whiteOverlay = (RelativeLayout)findViewById(R.id.white_opacity);
// whiteOverlay.setVisibility(View.VISIBLE);
// }
public void addButton(FloatingActionButton button) {
addView(button, mButtonsCount - 1);
mButtonsCount++;
if (mLabelsStyle != 0) {
createLabels();
}
}
public void removeButton(FloatingActionButton button) {
removeView(button.getLabelView());
removeView(button);
mButtonsCount--;
}
private int getColor(#ColorRes int id) {
return getResources().getColor(id);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
int width = 0;
int height = 0;
mMaxButtonWidth = 0;
mMaxButtonHeight = 0;
int maxLabelWidth = 0;
for (int i = 0; i < mButtonsCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
switch (mExpandDirection) {
case EXPAND_UP:
case EXPAND_DOWN:
mMaxButtonWidth = Math.max(mMaxButtonWidth, child.getMeasuredWidth());
height += child.getMeasuredHeight();
break;
case EXPAND_LEFT:
case EXPAND_RIGHT:
width += child.getMeasuredWidth();
mMaxButtonHeight = Math.max(mMaxButtonHeight, child.getMeasuredHeight());
break;
}
if (!expandsHorizontally()) {
TextView label = (TextView) child.getTag(R.id.fab_label);
if (label != null) {
maxLabelWidth = Math.max(maxLabelWidth, label.getMeasuredWidth());
}
}
}
if (!expandsHorizontally()) {
width = mMaxButtonWidth + (maxLabelWidth > 0 ? maxLabelWidth + mLabelsMargin : 0);
} else {
height = mMaxButtonHeight;
}
switch (mExpandDirection) {
case EXPAND_UP:
case EXPAND_DOWN:
height += mButtonSpacing * (getChildCount() - 1);
height = adjustForOvershoot(height);
break;
case EXPAND_LEFT:
case EXPAND_RIGHT:
width += mButtonSpacing * (getChildCount() - 1);
width = adjustForOvershoot(width);
break;
}
setMeasuredDimension(width, height);
}
private int adjustForOvershoot(int dimension) {
return dimension * 12 / 10;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
switch (mExpandDirection) {
case EXPAND_UP:
case EXPAND_DOWN:
boolean expandUp = mExpandDirection == EXPAND_UP;
int addButtonY = expandUp ? b - t - mAddButton.getMeasuredHeight() : 0;
// Ensure mAddButton is centered on the line where the buttons should be
int addButtonLeft = r - l - mMaxButtonWidth + (mMaxButtonWidth - mAddButton.getMeasuredWidth()) / 2;
mAddButton.layout(addButtonLeft, addButtonY, addButtonLeft + mAddButton.getMeasuredWidth(), addButtonY + mAddButton.getMeasuredHeight());
int labelsRight = r - l - mMaxButtonWidth - mLabelsMargin;
int nextY = expandUp ?
addButtonY - mButtonSpacing :
addButtonY + mAddButton.getMeasuredHeight() + mButtonSpacing;
for (int i = mButtonsCount - 1; i >= 0; i--) {
final View child = getChildAt(i);
if (child == mAddButton || child.getVisibility() == GONE) continue;
int childX = addButtonLeft + (mAddButton.getMeasuredWidth() - child.getMeasuredWidth()) / 2;
int childY = expandUp ? nextY - child.getMeasuredHeight() : nextY;
child.layout(childX, childY, childX + child.getMeasuredWidth(), childY + child.getMeasuredHeight());
float collapsedTranslation = addButtonY - childY;
float expandedTranslation = 0f;
child.setTranslationY(mExpanded ? expandedTranslation : collapsedTranslation);
child.setAlpha(mExpanded ? 1f : 0f);
LayoutParams params = (LayoutParams) child.getLayoutParams();
params.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation);
params.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation);
params.setAnimationsTarget(child);
View label = (View) child.getTag(R.id.fab_label);
if (label != null) {
int labelLeft = labelsRight - label.getMeasuredWidth();
int labelTop = childY - mLabelsVerticalOffset + (child.getMeasuredHeight() - label.getMeasuredHeight()) / 2;
label.layout(labelLeft, labelTop, labelsRight, labelTop + label.getMeasuredHeight());
label.setTranslationY(mExpanded ? expandedTranslation : collapsedTranslation);
label.setAlpha(mExpanded ? 1f : 0f);
LayoutParams labelParams = (LayoutParams) label.getLayoutParams();
labelParams.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation);
labelParams.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation);
labelParams.setAnimationsTarget(label);
}
nextY = expandUp ?
childY - mButtonSpacing :
childY + child.getMeasuredHeight() + mButtonSpacing;
}
break;
case EXPAND_LEFT:
case EXPAND_RIGHT:
boolean expandLeft = mExpandDirection == EXPAND_LEFT;
int addButtonX = expandLeft ? r - l - mAddButton.getMeasuredWidth() : 0;
// Ensure mAddButton is centered on the line where the buttons should be
int addButtonTop = b - t - mMaxButtonHeight + (mMaxButtonHeight - mAddButton.getMeasuredHeight()) / 2;
mAddButton.layout(addButtonX, addButtonTop, addButtonX + mAddButton.getMeasuredWidth(), addButtonTop + mAddButton.getMeasuredHeight());
int nextX = expandLeft ?
addButtonX - mButtonSpacing :
addButtonX + mAddButton.getMeasuredWidth() + mButtonSpacing;
for (int i = mButtonsCount - 1; i >= 0; i--) {
final View child = getChildAt(i);
if (child == mAddButton || child.getVisibility() == GONE) continue;
int childX = expandLeft ? nextX - child.getMeasuredWidth() : nextX;
int childY = addButtonTop + (mAddButton.getMeasuredHeight() - child.getMeasuredHeight()) / 2;
child.layout(childX, childY, childX + child.getMeasuredWidth(), childY + child.getMeasuredHeight());
float collapsedTranslation = addButtonX - childX;
float expandedTranslation = 0f;
child.setTranslationX(mExpanded ? expandedTranslation : collapsedTranslation);
child.setAlpha(mExpanded ? 1f : 0f);
LayoutParams params = (LayoutParams) child.getLayoutParams();
params.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation);
params.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation);
params.setAnimationsTarget(child);
nextX = expandLeft ?
childX - mButtonSpacing :
childX + child.getMeasuredWidth() + mButtonSpacing;
}
break;
}
}
#Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(super.generateDefaultLayoutParams());
}
#Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(super.generateLayoutParams(attrs));
}
#Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(super.generateLayoutParams(p));
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return super.checkLayoutParams(p);
}
private static Interpolator sExpandInterpolator = new OvershootInterpolator();
private static Interpolator sCollapseInterpolator = new DecelerateInterpolator(3f);
private static Interpolator sAlphaExpandInterpolator = new DecelerateInterpolator();
private class LayoutParams extends ViewGroup.LayoutParams {
private ObjectAnimator mExpandDir = new ObjectAnimator();
private ObjectAnimator mExpandAlpha = new ObjectAnimator();
private ObjectAnimator mCollapseDir = new ObjectAnimator();
private ObjectAnimator mCollapseAlpha = new ObjectAnimator();
private boolean animationsSetToPlay;
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
mExpandDir.setInterpolator(sExpandInterpolator);
mExpandAlpha.setInterpolator(sAlphaExpandInterpolator);
mCollapseDir.setInterpolator(sCollapseInterpolator);
mCollapseAlpha.setInterpolator(sCollapseInterpolator);
mCollapseAlpha.setProperty(View.ALPHA);
mCollapseAlpha.setFloatValues(1f, 0f);
mExpandAlpha.setProperty(View.ALPHA);
mExpandAlpha.setFloatValues(0f, 1f);
switch (mExpandDirection) {
case EXPAND_UP:
case EXPAND_DOWN:
mCollapseDir.setProperty(View.TRANSLATION_Y);
mExpandDir.setProperty(View.TRANSLATION_Y);
break;
case EXPAND_LEFT:
case EXPAND_RIGHT:
mCollapseDir.setProperty(View.TRANSLATION_X);
mExpandDir.setProperty(View.TRANSLATION_X);
break;
}
}
public void setAnimationsTarget(View view) {
mCollapseAlpha.setTarget(view);
mCollapseDir.setTarget(view);
mExpandAlpha.setTarget(view);
mExpandDir.setTarget(view);
// Now that the animations have targets, set them to be played
if (!animationsSetToPlay) {
mCollapseAnimation.play(mCollapseAlpha);
mCollapseAnimation.play(mCollapseDir);
mExpandAnimation.play(mExpandAlpha);
mExpandAnimation.play(mExpandDir);
animationsSetToPlay = true;
}
}
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
bringChildToFront(mAddButton);
mButtonsCount = getChildCount();
if (mLabelsStyle != 0) {
createLabels();
}
}
private void createLabels() {
Context context = new ContextThemeWrapper(getContext(), mLabelsStyle);
for (int i = 0; i < mButtonsCount; i++) {
FloatingActionButton button = (FloatingActionButton) getChildAt(i);
String title = button.getTitle();
if (button == mAddButton || title == null ||
button.getTag(R.id.fab_label) != null) continue;
TextView label = new TextView(context);
label.setText(button.getTitle());
addView(label);
button.setTag(R.id.fab_label, label);
}
}
public void collapse() {
if (mExpanded) {
mExpanded = false;
mCollapseAnimation.start();
mExpandAnimation.cancel();
if (mListener != null) {
mListener.onMenuCollapsed();
}
}
}
public void toggle() {
if (mExpanded) {
collapse();
} else {
expand();
}
}
public void expand() {
if (!mExpanded) {
mExpanded = true;
mCollapseAnimation.cancel();
mExpandAnimation.start();
if (mListener != null) {
mListener.onMenuExpanded();
}
}
}
public boolean isExpanded() {
return mExpanded;
}
#Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState savedState = new SavedState(superState);
savedState.mExpanded = mExpanded;
return savedState;
}
#Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof SavedState) {
SavedState savedState = (SavedState) state;
mExpanded = savedState.mExpanded;
if (mRotatingDrawable != null) {
mRotatingDrawable.setRotation(mExpanded ? EXPANDED_PLUS_ROTATION : COLLAPSED_PLUS_ROTATION);
}
super.onRestoreInstanceState(savedState.getSuperState());
} else {
super.onRestoreInstanceState(state);
}
}
public static class SavedState extends BaseSavedState {
public boolean mExpanded;
public SavedState(Parcelable parcel) {
super(parcel);
}
private SavedState(Parcel in) {
super(in);
mExpanded = in.readInt() == 1;
}
#Override
public void writeToParcel(#NonNull Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(mExpanded ? 1 : 0);
}
public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
#Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
#Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
Please Help.
If I understand correctly, the code causing the problem is the one commented, otherwise this line :
whiteOverlay.setVisibility(View.VISIBLE);
You are trying to access your RelativeLayout (R.id.white_opacity) through your FloatingActionsMenu view :
whiteOverlay = (RelativeLayout)findViewById(R.id.white_opacity);
But R.id.white_opacity is a member of your HomeActivity view.
So it's normal that this line returns a null pointer since it doesn't have any member named like this.
Try do find a way to access your HomeActivity view and then call "findViewById()" on it.
If "context" is indeed this HomeActivity, then pass the argument to the updateBackground() method (or set context as a class member) and try somthing like that :
whiteOverlay = ((Activity)context).findViewById(R.id.white_opacity);
Regards.

Encounter Range-seek-bar runtime error in android

I have used this tutorial https://code.google.com/p/range-seek-bar/#Example_usage_as_Integer_range?.
Encountered runtime error of my activity stopped. Would like to seek help from you. I attempted to correct the following compile errors as listed below.
RangeSeekBar<Integer> seekBar = new RangeSeekBar<Integer>(20, 75, context);
Context cannot be resolved to a variable
I tried adding changing context to this.
Log.i(TAG, "User selected new range values: MIN=" + minValue + ", MAX=" + maxValue);
TAG cannot be resolved to a variable.
I tried adding "protected static final String TAG = null;" to main activity.
ViewGroup layout = (ViewGroup) findViewById(<your-layout-id>)
Does the layout id refer to my main_activity.xml in my layout?
Really grateful for your feedback.
MainActivity.Java
package com.example.rangeseekbargooglecode;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.ViewGroup;
import com.example.rangeseekbargooglecode.RangeSeekBar.OnRangeSeekBarChangeListener;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final String TAG = null;
// create RangeSeekBar as Integer range between 20 and 75
RangeSeekBar<Integer> seekBar = new RangeSeekBar<Integer>(20, 75, this);
seekBar.setOnRangeSeekBarChangeListener(new OnRangeSeekBarChangeListener<Integer>() {
#Override
public void onRangeSeekBarValuesChanged(RangeSeekBar<?> bar, Integer minValue, Integer maxValue) {
// handle changed range values
Log.i(TAG, "User selected new range values: MIN=" + minValue + ", MAX=" + maxValue);
}
});
// add RangeSeekBar to pre-defined layout
ViewGroup layout = (ViewGroup) findViewById(R.layout.activity_main);
layout.addView(seekBar);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<SeekBar
android:id="#+id/seekBar1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginTop="109dp" />
</RelativeLayout>
Edited Code to allow TextView to display range.
public class MainActivity extends Activity {
private TextView textview;
protected static final String TAG = "com.example.gto_doubleseekbar";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textview = (TextView) findViewById(R.id.textView1);
// create RangeSeekBar as Integer range between 20 and 75
RangeSeekBar<Integer> seekBar = new RangeSeekBar<Integer>(20, 75, this);
seekBar.setOnRangeSeekBarChangeListener(new OnRangeSeekBarChangeListener<Integer>() {
#Override
public void onRangeSeekBarValuesChanged(RangeSeekBar<?> bar, Integer minValue, Integer maxValue) {
// handle changed range values
String powerranger = "User selected new range values: MIN=" + minValue + ", MAX=" + maxValue;
Log.i(TAG, powerranger);
textview.setText(powerranger);
}
});
// add RangeSeekBar to pre-defined layout
LayoutInflater inflater = (LayoutInflater)getApplicationContext().getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.activity_main,null);
layout.addView(seekBar);
setContentView(layout);
}
RangeSeekBar seekBar = new RangeSeekBar(20, 75, context); 1. Context cannot be resolved to a variable
this must work:
RangeSeekBar seekBar = new RangeSeekBar(20, 75, this);
Log.i(TAG, "User selected new range values: MIN=" + minValue + ", MAX=" + maxValue); 2. TAG cannot be resolved to a variable. I tried adding "protected static final String TAG = null;" to main activity.
Don't set it to null. Usually you use the app or component name, e.g.
protected static final String TAG = "MyApp";
ViewGroup layout = (ViewGroup) findViewById() 3. Does the layout id refer to my main_activity.xml in my layout?
Use this:
LayoutInflater inflater = (LayoutInflater)getApplicationContext().getSystemService (Context.LAYOUT_INFLATER_SERVICE);
ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.activity_main,null);
layout.addView(seekBar);
setContentView(layout);
Try to implement custom RangeSeekBar.
xml file:
<com.doondoz.utility.common_function.RangeSeekBar
android:id="#+id/seekBarPrice"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:left_index="0"
android:layout_weight="0.90" />
java file:
RangeSeekBar.java
public class RangeSeekBar extends View {
private static final int DEFAULT_HEIGHT = 70;
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_TICK_COUNT = 100;
private Thumb leftThumb, rightThumb;
private Thumb pressedThumb = null;
private SeekBar seekBar;
private Paint thumbPaint;
private OnRangeSeekBarChangerListener mListener;
private int mTickCount;
private int mLeftIndex = 0;
private int mRightIndex;
private int mThumbColor;
private int mThumbNormalRadius;
private int mThumbPressedRadius;
public RangeSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
public RangeSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public RangeSeekBar(Context context) {
super(context);
}
#SuppressWarnings("deprecation")
private void init(Context context, AttributeSet attrs) {
Resources resources = getResources();
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RangeSeekBar);
try {
mTickCount = typedArray.getInteger(R.styleable.RangeSeekBar_tick_count, DEFAULT_TICK_COUNT);
mRightIndex = mTickCount - 1;
mThumbColor = typedArray.getColor(R.styleable.RangeSeekBar_thumb_color, getResources().getColor(R.color.thumb_default));
mThumbNormalRadius = typedArray.getDimensionPixelSize(R.styleable.RangeSeekBar_thumb_normal_radius, 12);
mThumbPressedRadius = typedArray.getDimensionPixelSize(R.styleable.RangeSeekBar_thumb_pressed_radius, 16);
mLeftIndex = typedArray.getInteger(R.styleable.RangeSeekBar_left_index, 0);
mRightIndex = typedArray.getInteger(R.styleable.RangeSeekBar_right_index, mRightIndex);
if (mLeftIndex < 0)
throw new IllegalArgumentException("Left index must be >= 0");
if (mRightIndex > mTickCount)
throw new IllegalArgumentException("Right index must be <= tick count");
} finally {
typedArray.recycle();
}
setUp();
}
private void setUp() {
thumbPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
thumbPaint.setStyle(Paint.Style.FILL);
thumbPaint.setColor(mThumbColor);
leftThumb = new Thumb(0, 0, thumbPaint, mThumbNormalRadius, mThumbPressedRadius);
rightThumb = new Thumb(0, 0, thumbPaint, mThumbNormalRadius, mThumbPressedRadius);
seekBar = new SeekBar(0, 0, 0, Color.BLACK, 1, mThumbColor, 3);
seekBar.setTickNumb(mTickCount);
}
public void setOnRangeBarChangeListener(OnRangeSeekBarChangerListener onRangeBarChangeListener) {
mListener = onRangeBarChangeListener;
}
/**
* Set number of ticks
*
* #param tickCount Default is 100
*/
public void setTickCount(int tickCount) {
mTickCount = tickCount;
seekBar.setTickNumb(mTickCount);
invalidate();
}
/**
* Set thumb's color
*
* #param thumbColor Default is orange
*/
public void setThumbColor(int thumbColor) {
mThumbColor = thumbColor;
thumbPaint.setColor(mThumbColor);
invalidate();
}
/**
* Set thumb's normal radius
*
* #param thumbRadius Default is 6dp
*/
public void setThumbNormalRadius(float thumbRadius) {
mThumbNormalRadius = (int) (thumbRadius*getResources().getDisplayMetrics().density);
leftThumb.radius = mThumbNormalRadius;
rightThumb.radius = mThumbNormalRadius;
invalidate();
}
/**
* Set thumb's pressed radius
*
* #param thumbPressedRadius Default is 8dp
*/
public void setThumbPressedRadius(float thumbPressedRadius) {
mThumbPressedRadius = (int) (thumbPressedRadius*getResources().getDisplayMetrics().density);
leftThumb.pressedRadius = mThumbPressedRadius;
rightThumb.pressedRadius = mThumbPressedRadius;
invalidate();
}
/**
* Set index for the Left Thumb
*
* #param leftIndex Default is 0
*/
public void setLeftIndex(int leftIndex) {
if (leftIndex < 0) {
throw new IllegalArgumentException("Left index must be >= 0");
}
mLeftIndex = leftIndex;
leftThumb.setIndex(seekBar, mLeftIndex);
invalidate();
}
/**
* Set index for the Right thumb
*
* #param rightIndex Default is 99
*/
public void setRightIndex(int rightIndex) {
if (rightIndex > mTickCount) {
throw new IllegalArgumentException("Left index must be <= tick count");
}
mRightIndex = rightIndex;
leftThumb.setIndex(seekBar, mRightIndex);
invalidate();
}
/**
* Get left index
*
* #return int
*/
public int getLeftIndex() {
return mLeftIndex;
}
/**
* Get right index
*
* #return int
*/
public int getRightIndex() {
return mRightIndex;
}
/**
* Get number of tick
*
* #return int
*/
public int getTickCount() {
return mTickCount;
}
#Override
protected synchronized void onDraw(Canvas canvas) {
seekBar.draw(canvas, leftThumb, rightThumb);
leftThumb.draw(canvas);
rightThumb.draw(canvas);
if (pressedThumb != null && pressedThumb.isAnimating) {
invalidate();
}
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height, width;
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(heightSize, DEFAULT_HEIGHT);
} else {
height = DEFAULT_HEIGHT;
}
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
width = Math.min(widthSize, DEFAULT_WIDTH);
} else {
width = DEFAULT_WIDTH;
}
setMeasuredDimension(width, height);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
pressedThumb = checkThumbPressed(eventX, eventY);
if (pressedThumb == null) {
return super.onTouchEvent(event);
}
pressedThumb.setPressed(true);
invalidate();
setPressed(true);
return true;
case MotionEvent.ACTION_MOVE:
onActionMove(eventX);
return true;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (pressedThumb == null) {
return super.onTouchEvent(event);
}
onActionUp();
break;
}
return super.onTouchEvent(event);
}
private void onActionUp() {
pressedThumb.onActionUp(seekBar);
invalidate();
}
private void onActionMove(float eventX) {
if (eventX >= seekBar.leftX && eventX <= seekBar.rightX) {
pressedThumb.x = eventX;
invalidate();
if (leftThumb.x > rightThumb.x) {
final Thumb temp = leftThumb;
leftThumb = rightThumb;
rightThumb = temp;
}
int leftIndex = seekBar.getNearestTick(leftThumb);
int rightIndex = seekBar.getNearestTick(rightThumb);
if (mLeftIndex != leftIndex || mRightIndex != rightIndex) {
mLeftIndex = leftIndex;
mRightIndex = rightIndex;
if (mListener != null) {
mListener.onIndexChange(this, mLeftIndex, mRightIndex);
}
}
}
}
private Thumb checkThumbPressed(float eventX, float eventY) {
Thumb result = null;
boolean isLeftThumbPressed = leftThumb.isInTargetZone(eventX, eventY);
boolean isRightThumbPressed = rightThumb.isInTargetZone(eventX, eventY);
if (isLeftThumbPressed && isRightThumbPressed) {
result = (eventX / getWidth() >= 0.5f) ? leftThumb : rightThumb;
} else if (isLeftThumbPressed) {
result = leftThumb;
} else if (isRightThumbPressed) {
result = rightThumb;
}
return result;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
leftThumb.x = (getPaddingLeft() + 20 + leftThumb.normalRadius / 2);
leftThumb.y = (h + getPaddingTop() + getPaddingBottom()) / 2;
rightThumb.y = leftThumb.y;
rightThumb.x = (w - getPaddingRight() - 20 - rightThumb.normalRadius / 2);
seekBar.leftX = leftThumb.x;
seekBar.rightX = rightThumb.x;
seekBar.y = leftThumb.y;
leftThumb.setIndex(seekBar, mLeftIndex);
rightThumb.setIndex(seekBar, mRightIndex);
}
#Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
SavedState state = new SavedState(super.onSaveInstanceState());
state.tickCount = mTickCount;
state.leftIndex = mLeftIndex;
state.rightIndex = mRightIndex;
state.thumbColor = mThumbColor;
state.thumbNormalRadius = mThumbNormalRadius;
state.thumbPressedRadius = mThumbPressedRadius;
bundle.putParcelable(SavedState.STATE,state);
return bundle;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
SavedState savedState = bundle.getParcelable(SavedState.STATE);
mTickCount = savedState.tickCount;
mThumbColor = savedState.thumbColor;
mThumbNormalRadius = savedState.thumbNormalRadius;
mThumbPressedRadius = savedState.thumbPressedRadius;
mLeftIndex = savedState.leftIndex;
mRightIndex = savedState.rightIndex;
super.onRestoreInstanceState(savedState.getSuperState());
return;
}
super.onRestoreInstanceState(SavedState.EMPTY_STATE);
}
public interface OnRangeSeekBarChangerListener {
void onIndexChange(RangeSeekBar rangeBar, int leftIndex, int rightIndex);
}
static class SavedState extends BaseSavedState {
static final String STATE = "RangeSeekBar.STATE";
int tickCount;
int leftIndex;
int rightIndex;
int thumbColor;
int thumbNormalRadius;
int thumbPressedRadius;
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
}
#Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
}
public static final Creator<SavedState> CREATOR =
new Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}

How To Make Circle Custom Progress Bar in Android

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>

Categories

Resources