I have searched for the dotted circular progress bar but i din get any perfect examples , There are circular progressbar but not with the dotted one So I have implemented hoping that it will help someone, and you can keep this as reference and customize further, for beginners it will help a lot to understand the code as its commented on each line what it is doing.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<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="com.example.naveenbm.customcircleprogressbar.MainActivity"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<com.example.custom.customcircleprogressbar.CricleProgressBarCustom
android:layout_width="wrap_content"
android:layout_height="wrap_content"
></com.example.custom.customcircleprogressbar.CricleProgressBarCustom>
</RelativeLayout>
</RelativeLayout>
CircleProgressBarCustom.java
public class CricleProgressBarCustom extends View {
//Normal dot radius
private int dotRadius = 10;
//Expanded Dot Radius
private int bounceDotRadius = 13;
//to get identified in which position dot has to expand its radius
private int dotPosition = 1;
//specify how many dots you need in a progressbar
private int dotAmount = 10;
//specify the circle radius
private int circleRadius = 50;
public CricleProgressBarCustom(Context context) {
super(context);
}
public CricleProgressBarCustom(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CricleProgressBarCustom(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
//Animation called when attaching to the window, i.e to your screen
startAnimation();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//take the point to the center of the screen
canvas.translate(this.getWidth()/2,this.getHeight()/2);
Paint progressPaint = new Paint();
progressPaint.setColor(Color.parseColor("#ff014e"));
//call create dot method
createDotInCircle(canvas,progressPaint);
}
private void createDotInCircle(Canvas canvas,Paint progressPaint) {
//angle for each dot angle = (360/number of dots) i.e (360/10)
int angle = 36;
for(int i = 1; i <= dotAmount; i++){
if(i == dotPosition){
// angle should be in radians i.e formula (angle *(Math.PI / 180))
float x = (float) (circleRadius * (Math.cos((angle * i) * (Math.PI / 180))));
float y = (float) (circleRadius * (Math.sin((angle * i) * (Math.PI / 180))));
canvas.drawCircle(x,y, bounceDotRadius, progressPaint);
}else{
// angle should be in radians i.e formula (angle *(Math.PI / 180))
float x = (float) (circleRadius * (Math.cos((angle * i) * (Math.PI / 180))));
float y = (float) (circleRadius * (Math.sin((angle * i) * (Math.PI / 180))));
canvas.drawCircle(x,y, dotRadius, progressPaint);
}
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = 0;
int height = 0;
//Dynamically setting width and height to progressbar 100 is circle radius, dotRadius * 3 to cover the width and height of Progressbar
width = 100 + (dotRadius*3);
height = 100 + (dotRadius*3);
//MUST CALL THIS
setMeasuredDimension(width, height);
}
private void startAnimation() {
BounceAnimation bounceAnimation = new BounceAnimation();
bounceAnimation.setDuration(150);
bounceAnimation.setRepeatCount(Animation.INFINITE);
bounceAnimation.setInterpolator(new LinearInterpolator());
bounceAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
dotPosition++;
//when dotPosition == dotAmount , then start again applying animation from 0th positon , i.e dotPosition = 0;
if (dotPosition > dotAmount) {
dotPosition = 1;
}
}
});
startAnimation(bounceAnimation);
}
private class BounceAnimation extends Animation {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
//call invalidate to redraw your view againg.
invalidate();
}
}
}
Snap Shot:
One example I can suggest you for this various Progress Bar with Smooth Working
https://github.com/ybq/Android-SpinKit
You put the class..
if You want Circle progress bar than Work it Like..
Take a TextView Where You Want...
And First Take..On your Class where You Use.(Declaration)
private Circle mCircleDrawable = new Circle();
Than in your Oncreate take this..
mCircleDrawable.setBounds(0, 0, 100, 100);
mCircleDrawable.setColor(R.color.colorPrimary);// What u want of color progress bar dots...
TextView progressBar_text=(TextView)findViewById(R.id.progress);// Your Textview..
progressBar_text.setCompoundDrawables(null, null, mCircleDrawable, null);// Set Progress bar on Textview..
You Need to Start the
mCircleDrawable.start();
You also Stop the Circle when You exit. On Detroyed Or on PostMethod..If use(AYSNC)
mCircleDrawable.stop();
The Classes Used For this...
You can Use all the Library for this and take any progress bar you need using Upper Link Example
Circle.java
import android.animation.ValueAnimator;
import com.github.ybq.android.spinkit.animation.SpriteAnimatorBuilder;
import com.github.ybq.android.spinkit.sprite.CircleSprite;
import com.github.ybq.android.spinkit.sprite.CircleSpriteGroup;
import com.github.ybq.android.spinkit.sprite.Sprite;
public class Circle extends CircleSpriteGroup {
#Override
public Sprite[] onCreateChild() {
Dot[] dots = new Dot[12];
for (int i = 0; i < dots.length; i++) {
dots[i] = new Dot();
dots[i].setAnimationDelay(1200 / 12 * i + -1200);
}
return dots;
}
class Dot extends CircleSprite {
public Dot() {
setScale(0f);
}
#Override
public ValueAnimator getAnimation() {
float fractions[] = new float[]{0f, 0.5f, 1f};
return new SpriteAnimatorBuilder(this).
scale(fractions, 0f, 1f, 0f).
duration(1200).
easeInOut(fractions)
.build();
}
}
}
SpriteAnimatorBuilder.Java
import android.animation.Keyframe;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.util.Property;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
import com.github.ybq.android.spinkit.animation.interpolator.KeyFrameInterpolator;
import com.github.ybq.android.spinkit.sprite.Sprite;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class SpriteAnimatorBuilder {
private Sprite sprite;
private List<PropertyValuesHolder> propertyValuesHolders = new ArrayList<> ();
private Interpolator interpolator;
private int repeatCount = Animation.INFINITE;
private long duration = 2000;
public SpriteAnimatorBuilder(Sprite sprite) {
this.sprite = sprite;
}
public SpriteAnimatorBuilder scale(float fractions[], float... scale) {
holder(fractions, Sprite.SCALE, scale);
return this;
}
public SpriteAnimatorBuilder alpha(float fractions[], int... alpha) {
holder(fractions, Sprite.ALPHA, alpha);
return this;
}
#SuppressWarnings("unused")
public SpriteAnimatorBuilder scaleX(float fractions[], float... scaleX) {
holder(fractions, Sprite.SCALE, scaleX);
return this;
}
public SpriteAnimatorBuilder scaleY(float fractions[], float... scaleY) {
holder(fractions, Sprite.SCALE_Y, scaleY);
return this;
}
public SpriteAnimatorBuilder rotateX(float fractions[], int... rotateX) {
holder(fractions, Sprite.ROTATE_X, rotateX);
return this;
}
public SpriteAnimatorBuilder rotateY(float fractions[], int... rotateY) {
holder(fractions, Sprite.ROTATE_Y, rotateY);
return this;
}
#SuppressWarnings("unused")
public SpriteAnimatorBuilder translateX(float fractions[], int... translateX) {
holder(fractions, Sprite.TRANSLATE_X, translateX);
return this;
}
#SuppressWarnings("unused")
public SpriteAnimatorBuilder translateY(float fractions[], int... translateY) {
holder(fractions, Sprite.TRANSLATE_Y, translateY);
return this;
}
public SpriteAnimatorBuilder rotate(float fractions[], int... rotate) {
holder(fractions, Sprite.ROTATE, rotate);
return this;
}
public SpriteAnimatorBuilder translateXPercentage(float fractions[], float... translateXPercentage) {
holder(fractions, Sprite.TRANSLATE_X_PERCENTAGE, translateXPercentage);
return this;
}
public SpriteAnimatorBuilder translateYPercentage(float[] fractions, float... translateYPercentage) {
holder(fractions, Sprite.TRANSLATE_Y_PERCENTAGE, translateYPercentage);
return this;
}
public PropertyValuesHolder holder(float[] fractions, Property property, float[] values) {
ensurePair(fractions.length, values.length);
Keyframe[] keyframes = new Keyframe[fractions.length];
for (int i = 0; i < values.length; i++) {
keyframes[i] = Keyframe.ofFloat(fractions[i], values[i]);
}
PropertyValuesHolder valuesHolder = PropertyValuesHolder.
ofKeyframe(property
, keyframes
);
propertyValuesHolders.add(valuesHolder);
return valuesHolder;
}
public PropertyValuesHolder holder(float[] fractions, Property property, int[] values) {
ensurePair(fractions.length, values.length);
Keyframe[] keyframes = new Keyframe[fractions.length];
for (int i = 0; i < values.length; i++) {
keyframes[i] = Keyframe.ofInt(fractions[i], values[i]);
}
PropertyValuesHolder valuesHolder = PropertyValuesHolder.
ofKeyframe(property
, keyframes
);
propertyValuesHolders.add(valuesHolder);
return valuesHolder;
}
private void ensurePair(int fractionsLength, int valuesLength) {
if (fractionsLength != valuesLength) {
throw new IllegalStateException(String.format(
Locale.getDefault(),
"The fractions.length must equal values.length, " +
"fraction.length[%d], values.length[%d]",
fractionsLength,
valuesLength));
}
}
public SpriteAnimatorBuilder interpolator(Interpolator interpolator) {
this.interpolator = interpolator;
return this;
}
public SpriteAnimatorBuilder easeInOut(float... fractions) {
interpolator(KeyFrameInterpolator.easeInOut(
fractions
));
return this;
}
public SpriteAnimatorBuilder duration(long duration) {
this.duration = duration;
return this;
}
#SuppressWarnings("unused")
public SpriteAnimatorBuilder repeatCount(int repeatCount) {
this.repeatCount = repeatCount;
return this;
}
public ObjectAnimator build() {
PropertyValuesHolder[] holders = new PropertyValuesHolder[propertyValuesHolders.size()];
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(sprite,
propertyValuesHolders.toArray(holders));
animator.setDuration(duration);
animator.setRepeatCount(repeatCount);
animator.setInterpolator(interpolator);
return animator;
}
}
CircleSprite.java
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
public class CircleSprite extends ShapeSprite {
#Override
public ValueAnimator getAnimation() {
return null;
}
#Override
public void drawShape(Canvas canvas, Paint paint) {
if (getDrawBounds() != null) {
int radius = Math.min(getDrawBounds().width(), getDrawBounds().height()) / 2;
canvas.drawCircle(getDrawBounds().centerX(),
getDrawBounds().centerY(),
radius, paint);
}
}
}
ShapeSprite .java
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
public abstract class ShapeSprite extends Sprite {
private Paint mPaint;
private int mUseColor;
private int mBaseColor;
public ShapeSprite() {
setColor(Color.WHITE);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(mUseColor);
}
#Override
public void setColor(int color) {
mBaseColor = color;
updateUseColor();
}
#Override
public int getColor() {
return mBaseColor;
}
#SuppressWarnings("unused")
public int getUseColor() {
return mUseColor;
}
#Override
public void setAlpha(int alpha) {
super.setAlpha(alpha);
updateUseColor();
}
private void updateUseColor() {
int alpha = getAlpha();
alpha += alpha >> 7;
final int baseAlpha = mBaseColor >>> 24;
final int useAlpha = baseAlpha * alpha >> 8;
mUseColor = (mBaseColor << 8 >>> 8) | (useAlpha << 24);
}
#Override
public void setColorFilter(ColorFilter colorFilter) {
mPaint.setColorFilter(colorFilter);
}
#Override
protected final void drawSelf(Canvas canvas) {
mPaint.setColor(mUseColor);
drawShape(canvas, mPaint);
}
public abstract void drawShape(Canvas canvas, Paint paint);
}
CircleSpriteGroup .java
import android.graphics.Canvas;
import android.graphics.Rect;
public abstract class CircleSpriteGroup extends SpriteGroup {
#Override
public void drawChild(Canvas canvas) {
for (int i = 0; i < getChildCount(); i++) {
Sprite sprite = getChildAt(i);
int count = canvas.save();
canvas.rotate(i * 360 / getChildCount(),
getBounds().centerX(),
getBounds().centerY());
sprite.draw(canvas);
canvas.restoreToCount(count);
}
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
bounds = clipSquare(bounds);
int radius = (int) (bounds.width() * Math.PI / 3.6f / getChildCount());
int left = bounds.centerX() - radius;
int right = bounds.centerX() + radius;
for (int i = 0; i < getChildCount(); i++) {
Sprite sprite = getChildAt(i);
sprite.setDrawBounds(left, bounds.top, right, bounds.top + radius * 2);
}
}
}
Sprite.java
import android.animation.ValueAnimator;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.util.Property;
import com.github.ybq.android.spinkit.animation.AnimationUtils;
import com.github.ybq.android.spinkit.animation.FloatProperty;
import com.github.ybq.android.spinkit.animation.IntProperty;
public abstract class Sprite extends Drawable implements
ValueAnimator.AnimatorUpdateListener
, Animatable
, Drawable.Callback {
private float scale = 1;
private float scaleX = 1;
private float scaleY = 1;
private float pivotX;
private float pivotY;
private int animationDelay;
private int rotateX;
private int rotateY;
private int translateX;
private int translateY;
private int rotate;
private float translateXPercentage;
private float translateYPercentage;
private ValueAnimator animator;
private int alpha = 255;
private static final Rect ZERO_BOUNDS_RECT = new Rect();
protected Rect drawBounds = ZERO_BOUNDS_RECT;
private Camera mCamera;
private Matrix mMatrix;
public Sprite() {
mCamera = new Camera();
mMatrix = new Matrix();
}
public abstract int getColor();
public abstract void setColor(int color);
#Override
public void setAlpha(int alpha) {
this.alpha = alpha;
}
#Override
public int getAlpha() {
return alpha;
}
#Override
public int getOpacity() {
return PixelFormat.RGBA_8888;
}
public float getTranslateXPercentage() {
return translateXPercentage;
}
public void setTranslateXPercentage(float translateXPercentage) {
this.translateXPercentage = translateXPercentage;
}
public float getTranslateYPercentage() {
return translateYPercentage;
}
public void setTranslateYPercentage(float translateYPercentage) {
this.translateYPercentage = translateYPercentage;
}
public int getTranslateX() {
return translateX;
}
public void setTranslateX(int translateX) {
this.translateX = translateX;
}
public int getTranslateY() {
return translateY;
}
public void setTranslateY(int translateY) {
this.translateY = translateY;
}
public int getRotate() {
return rotate;
}
public void setRotate(int rotate) {
this.rotate = rotate;
}
public float getScale() {
return scale;
}
public void setScale(float scale) {
this.scale = scale;
setScaleX(scale);
setScaleY(scale);
}
public float getScaleX() {
return scaleX;
}
public void setScaleX(float scaleX) {
this.scaleX = scaleX;
}
public float getScaleY() {
return scaleY;
}
public void setScaleY(float scaleY) {
this.scaleY = scaleY;
}
public int getRotateX() {
return rotateX;
}
public void setRotateX(int rotateX) {
this.rotateX = rotateX;
}
public int getRotateY() {
return rotateY;
}
public void setRotateY(int rotateY) {
this.rotateY = rotateY;
}
public float getPivotX() {
return pivotX;
}
public void setPivotX(float pivotX) {
this.pivotX = pivotX;
}
public float getPivotY() {
return pivotY;
}
public void setPivotY(float pivotY) {
this.pivotY = pivotY;
}
#SuppressWarnings("unused")
public int getAnimationDelay() {
return animationDelay;
}
public Sprite setAnimationDelay(int animationDelay) {
this.animationDelay = animationDelay;
return this;
}
#Override
public void setColorFilter(ColorFilter colorFilter) {
}
public abstract ValueAnimator getAnimation();
#Override
public void start() {
if (AnimationUtils.isStarted(animator)) {
return;
}
animator = obtainAnimation();
if (animator == null) {
return;
}
AnimationUtils.start(animator);
invalidateSelf();
}
public ValueAnimator obtainAnimation() {
if (animator == null) {
animator = getAnimation();
}
if (animator != null) {
animator.addUpdateListener(this);
animator.setStartDelay(animationDelay);
}
return animator;
}
#Override
public void stop() {
if (AnimationUtils.isStarted(animator)) {
animator.removeAllUpdateListeners();
animator.end();
reset();
}
}
protected abstract void drawSelf(Canvas canvas);
public void reset() {
scale = 1;
rotateX = 0;
rotateY = 0;
translateX = 0;
translateY = 0;
rotate = 0;
translateXPercentage = 0f;
translateYPercentage = 0f;
}
#Override
public boolean isRunning() {
return AnimationUtils.isRunning(animator);
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
setDrawBounds(bounds);
}
public void setDrawBounds(Rect drawBounds) {
setDrawBounds(drawBounds.left, drawBounds.top, drawBounds.right, drawBounds.bottom);
}
public void setDrawBounds(int left, int top, int right, int bottom) {
this.drawBounds = new Rect(left, top, right, bottom);
setPivotX(getDrawBounds().centerX());
setPivotY(getDrawBounds().centerY());
}
#Override
public void invalidateDrawable(Drawable who) {
invalidateSelf();
}
#Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
}
#Override
public void unscheduleDrawable(Drawable who, Runnable what) {
}
#Override
public void onAnimationUpdate(ValueAnimator animation) {
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
}
}
public Rect getDrawBounds() {
return drawBounds;
}
#Override
public void draw(Canvas canvas) {
int tx = getTranslateX();
tx = tx == 0 ? (int) (getBounds().width() * getTranslateXPercentage()) : tx;
int ty = getTranslateY();
ty = ty == 0 ? (int) (getBounds().height() * getTranslateYPercentage()) : ty;
canvas.translate(tx, ty);
canvas.scale(getScaleX(), getScaleY(), getPivotX(), getPivotY());
canvas.rotate(getRotate(), getPivotX(), getPivotY());
if (getRotateX() != 0 || getRotateY() != 0) {
mCamera.save();
mCamera.rotateX(getRotateX());
mCamera.rotateY(getRotateY());
mCamera.getMatrix(mMatrix);
mMatrix.preTranslate(-getPivotX(), -getPivotY());
mMatrix.postTranslate(getPivotX(), getPivotY());
mCamera.restore();
canvas.concat(mMatrix);
}
drawSelf(canvas);
}
public Rect clipSquare(Rect rect) {
int w = rect.width();
int h = rect.height();
int min = Math.min(w, h);
int cx = rect.centerX();
int cy = rect.centerY();
int r = min / 2;
return new Rect(
cx - r,
cy - r,
cx + r,
cy + r
);
}
public static final Property<Sprite, Integer> ROTATE_X = new IntProperty<Sprite>("rotateX") {
#Override
public void setValue(Sprite object, int value) {
object.setRotateX(value);
}
#Override
public Integer get(Sprite object) {
return object.getRotateX();
}
};
public static final Property<Sprite, Integer> ROTATE = new IntProperty<Sprite>("rotate") {
#Override
public void setValue(Sprite object, int value) {
object.setRotate(value);
}
#Override
public Integer get(Sprite object) {
return object.getRotate();
}
};
public static final Property<Sprite, Integer> ROTATE_Y = new IntProperty<Sprite>("rotateY") {
#Override
public void setValue(Sprite object, int value) {
object.setRotateY(value);
}
#Override
public Integer get(Sprite object) {
return object.getRotateY();
}
};
#SuppressWarnings("unused")
public static final Property<Sprite, Integer> TRANSLATE_X = new IntProperty<Sprite>("translateX") {
#Override
public void setValue(Sprite object, int value) {
object.setTranslateX(value);
}
#Override
public Integer get(Sprite object) {
return object.getTranslateX();
}
};
#SuppressWarnings("unused")
public static final Property<Sprite, Integer> TRANSLATE_Y = new IntProperty<Sprite>("translateY") {
#Override
public void setValue(Sprite object, int value) {
object.setTranslateY(value);
}
#Override
public Integer get(Sprite object) {
return object.getTranslateY();
}
};
public static final Property<Sprite, Float> TRANSLATE_X_PERCENTAGE = new FloatProperty<Sprite>("translateXPercentage") {
#Override
public void setValue(Sprite object, float value) {
object.setTranslateXPercentage(value);
}
#Override
public Float get(Sprite object) {
return object.getTranslateXPercentage();
}
};
public static final Property<Sprite, Float> TRANSLATE_Y_PERCENTAGE = new FloatProperty<Sprite>("translateYPercentage") {
#Override
public void setValue(Sprite object, float value) {
object.setTranslateYPercentage(value);
}
#Override
public Float get(Sprite object) {
return object.getTranslateYPercentage();
}
};
#SuppressWarnings("unused")
public static final Property<Sprite, Float> SCALE_X = new FloatProperty<Sprite>("scaleX") {
#Override
public void setValue(Sprite object, float value) {
object.setScaleX(value);
}
#Override
public Float get(Sprite object) {
return object.getScaleX();
}
};
public static final Property<Sprite, Float> SCALE_Y = new FloatProperty<Sprite>("scaleY") {
#Override
public void setValue(Sprite object, float value) {
object.setScaleY(value);
}
#Override
public Float get(Sprite object) {
return object.getScaleY();
}
};
public static final Property<Sprite, Float> SCALE = new FloatProperty<Sprite>("scale") {
#Override
public void setValue(Sprite object, float value) {
object.setScale(value);
}
#Override
public Float get(Sprite object) {
return object.getScale();
}
};
public static final Property<Sprite, Integer> ALPHA = new IntProperty<Sprite>("alpha") {
#Override
public void setValue(Sprite object, int value) {
object.setAlpha(value);
}
#Override
public Integer get(Sprite object) {
return object.getAlpha();
}
};
}
KeyFrameInterpolator.java
import android.animation.TimeInterpolator;
import android.view.animation.Interpolator;
public class KeyFrameInterpolator implements Interpolator {
private TimeInterpolator interpolator;
private float[] fractions;
public static KeyFrameInterpolator easeInOut(float... fractions) {
KeyFrameInterpolator interpolator = new KeyFrameInterpolator(Ease.inOut());
interpolator.setFractions(fractions);
return interpolator;
}
public KeyFrameInterpolator(TimeInterpolator interpolator) {
this.interpolator = interpolator;
}
public void setFractions(float... fractions) {
this.fractions = fractions;
}
#Override
public synchronized float getInterpolation(float input) {
if (fractions.length > 1) {
for (int i = 0; i < fractions.length - 1; i++) {
float start = fractions[i];
float end = fractions[i + 1];
float duration = end - start;
if (input >= start && input <= end) {
input = (input - start) / duration;
return start + (interpolator.getInterpolation(input)
* duration);
}
}
}
return interpolator.getInterpolation(input);
}
}
AnimationUtils.java
import android.animation.Animator;
import android.animation.ValueAnimator;
import com.github.ybq.android.spinkit.sprite.Sprite;
public class AnimationUtils {
public static void start(Animator animator) {
if (animator != null && !animator.isStarted()) {
animator.start();
}
}
public static void stop(Animator animator) {
if (animator != null && !animator.isRunning()) {
animator.end();
}
}
public static void start(Sprite... sprites) {
for (Sprite sprite : sprites) {
sprite.start();
}
}
public static void stop(Sprite... sprites) {
for (Sprite sprite : sprites) {
sprite.stop();
}
}
public static boolean isRunning(Sprite... sprites) {
for (Sprite sprite : sprites) {
if (sprite.isRunning()) {
return true;
}
}
return false;
}
public static boolean isRunning(ValueAnimator animator) {
return animator != null && animator.isRunning();
}
public static boolean isStarted(ValueAnimator animator) {
return animator != null && animator.isStarted();
}
}
FloatProperty.java
import android.util.Property;
public abstract class FloatProperty<T> extends Property<T, Float> {
public FloatProperty(String name) {
super(Float.class, name);
}
/**
* A type-specific override of the {#link #set(Object, Float)} that is faster when dealing
* with fields of type <code>float</code>.
*/
public abstract void setValue(T object, float value);
#Override
final public void set(T object, Float value) {
setValue(object, value);
}
}
IntProperty.java
import android.util.Property;
public abstract class IntProperty<T> extends Property<T, Integer> {
public IntProperty(String name) {
super(Integer.class, name);
}
public abstract void setValue(T object, int value);
#Override
final public void set(T object, Integer value) {
setValue(object, value);
}
}
Kotlin way of the great answer from #Naveen Shriyan:
class CustomDottedProgressBar : View {
constructor(context: Context) : super(context)
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr)
//Normal dot radius
private val dotRadius = 10F
//Expanded Dot Radius
private val bounceDotRadius = 15F
//to get identified in which position dot has to expand its radius
private var dotPosition = 0
//specify the circle radius
private val circleRadius = 50
// specify the sizes of dots
private val dotRadiusList = arrayListOf(2F, 3F, 4F, 5F, 6F, 7F, 8F, 9F, 10F)
//specify how many dots you need in a progressbar
private val dotAmount = dotRadiusList.size
private val progressPaint = Paint()
override fun onAttachedToWindow() {
super.onAttachedToWindow()
//Animation called when attaching to the window, i.e to your screen
startAnimation()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//take the point to the center of the screen
canvas.translate((this.width / 2).toFloat(), (this.height / 2).toFloat())
progressPaint.color = context.getColor(R.color.orange)
//call create dot method
createDotInCircle(canvas, progressPaint)
}
private fun createDotInCircle(canvas: Canvas, progressPaint: Paint) {
//angle for each dot angle = (360/number of dots) i.e (360/10)
val angle = 360 / dotAmount
for (i in 0 until dotAmount) {
if (i == dotPosition) {
// angle should be in radians i.e formula (angle *(Math.PI / 180))
val x = (circleRadius * cos(angle * i * (Math.PI / 180))).toFloat()
val y = (circleRadius * sin(angle * i * (Math.PI / 180))).toFloat()
canvas.drawCircle(x, y, bounceDotRadius, progressPaint)
} else {
// angle should be in radians i.e formula (angle *(Math.PI / 180))
val x = (circleRadius * cos(angle * i * (Math.PI / 180))).toFloat()
val y = (circleRadius * sin(angle * i * (Math.PI / 180))).toFloat()
canvas.drawCircle(x, y, dotRadiusList[i], progressPaint)
}
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
//Dynamically setting width and height to progressbar 100 is circle radius, dotRadius * 3 to cover the width and height of Progressbar
val width = 100 + dotRadius * 3
val height = 100 + dotRadius * 3
//MUST CALL THIS
setMeasuredDimension(width.roundToInt(), height.roundToInt())
}
private fun startAnimation() {
val bounceAnimation = BounceAnimation()
bounceAnimation.duration = 75
bounceAnimation.repeatCount = Animation.INFINITE
bounceAnimation.interpolator = LinearInterpolator()
bounceAnimation.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {}
override fun onAnimationEnd(animation: Animation) {}
override fun onAnimationRepeat(animation: Animation) {
dotPosition++
//when dotPosition == dotAmount , then start again applying animation from 0th position , i.e dotPosition = 0;
if (dotPosition > dotAmount) {
dotPosition = 0
}
}
})
startAnimation(bounceAnimation)
}
private inner class BounceAnimation : Animation() {
override fun applyTransformation(interpolatedTime: Float, t: Transformation?) {
super.applyTransformation(interpolatedTime, t)
//call invalidate to redraw your view again.
invalidate()
}
}}
Related
hey guys I am working on a launcher that's based off the rootless pixel launcher and am trying to change the folder icon to a squircle shape rather than a oval/circle shape. It looks like the launcher is making the folder icon using canvas draw method but I have little experience with this approach usually i'd make a xml shape in reference the shape that way, but when doing research on this it looks like you can not make a squircle xml shape so with that being said here is the class that makes the folder icon:
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.folder;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RadialGradient;
import android.graphics.Region;
import android.graphics.Shader;
import android.support.v4.graphics.ColorUtils;
import android.util.Property;
import android.view.View;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.util.Themes;
/**
* This object represents a FolderIcon preview background. It stores drawing / measurement
* information, handles drawing, and animation (accept state <--> rest state).
*/
public class PreviewBackground {
private static final int CONSUMPTION_ANIMATION_DURATION = 100;
private final PorterDuffXfermode mClipPorterDuffXfermode
= new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
// Create a RadialGradient such that it draws a black circle and then extends with
// transparent. To achieve this, we keep the gradient to black for the range [0, 1) and
// just at the edge quickly change it to transparent.
private final RadialGradient mClipShader = new RadialGradient(0, 0, 1,
new int[] {Color.BLACK, Color.BLACK, Color.TRANSPARENT },
new float[] {0, 0.999f, 1},
Shader.TileMode.CLAMP);
private final PorterDuffXfermode mShadowPorterDuffXfermode
= new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
private RadialGradient mShadowShader = null;
private final Matrix mShaderMatrix = new Matrix();
private final Path mPath = new Path();
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
float mScale = 1f;
private float mColorMultiplier = 1f;
private int mBgColor;
private float mStrokeWidth;
private int mStrokeAlpha = MAX_BG_OPACITY;
private int mShadowAlpha = 255;
private View mInvalidateDelegate;
int previewSize;
int basePreviewOffsetX;
int basePreviewOffsetY;
private CellLayout mDrawingDelegate;
public int delegateCellX;
public int delegateCellY;
// When the PreviewBackground is drawn under an icon (for creating a folder) the border
// should not occlude the icon
public boolean isClipping = true;
// Drawing / animation configurations
private static final float ACCEPT_SCALE_FACTOR = 1.20f;
private static final float ACCEPT_COLOR_MULTIPLIER = 1.5f;
// Expressed on a scale from 0 to 255.
private static final int BG_OPACITY = 160;
private static final int MAX_BG_OPACITY = 225;
private static final int SHADOW_OPACITY = 40;
private ValueAnimator mScaleAnimator;
private ObjectAnimator mStrokeAlphaAnimator;
private ObjectAnimator mShadowAnimator;
private static final Property<PreviewBackground, Integer> STROKE_ALPHA =
new Property<PreviewBackground, Integer>(Integer.class, "strokeAlpha") {
#Override
public Integer get(PreviewBackground previewBackground) {
return previewBackground.mStrokeAlpha;
}
#Override
public void set(PreviewBackground previewBackground, Integer alpha) {
previewBackground.mStrokeAlpha = alpha;
previewBackground.invalidate();
}
};
private static final Property<PreviewBackground, Integer> SHADOW_ALPHA =
new Property<PreviewBackground, Integer>(Integer.class, "shadowAlpha") {
#Override
public Integer get(PreviewBackground previewBackground) {
return previewBackground.mShadowAlpha;
}
#Override
public void set(PreviewBackground previewBackground, Integer alpha) {
previewBackground.mShadowAlpha = alpha;
previewBackground.invalidate();
}
};
public void setup(Launcher launcher, View invalidateDelegate,
int availableSpace, int topPadding) {
mInvalidateDelegate = invalidateDelegate;
mBgColor = Themes.getAttrColor(launcher, android.R.attr.colorPrimary);
DeviceProfile grid = launcher.getDeviceProfile();
final int previewSize = grid.folderIconSizePx;
final int previewPadding = grid.folderIconPreviewPadding;
this.previewSize = (previewSize - 2 * previewPadding);
basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
// Stroke width is 1dp
mStrokeWidth = launcher.getResources().getDisplayMetrics().density;
float radius = getScaledRadius();
float shadowRadius = radius + mStrokeWidth;
int shadowColor = Color.argb(SHADOW_OPACITY, 0, 0, 0);
mShadowShader = new RadialGradient(0, 0, 1,
new int[] {shadowColor, Color.TRANSPARENT},
new float[] {radius / shadowRadius, 1},
Shader.TileMode.CLAMP);
invalidate();
}
int getRadius() {
return previewSize / 2;
}
int getScaledRadius() {
return (int) (mScale * getRadius());
}
int getOffsetX() {
return basePreviewOffsetX - (getScaledRadius() - getRadius());
}
int getOffsetY() {
return basePreviewOffsetY - (getScaledRadius() - getRadius());
}
/**
* Returns the progress of the scale animation, where 0 means the scale is at 1f
* and 1 means the scale is at ACCEPT_SCALE_FACTOR.
*/
float getScaleProgress() {
return (mScale - 1f) / (ACCEPT_SCALE_FACTOR - 1f);
}
void invalidate() {
if (mInvalidateDelegate != null) {
mInvalidateDelegate.invalidate();
}
if (mDrawingDelegate != null) {
mDrawingDelegate.invalidate();
}
}
void setInvalidateDelegate(View invalidateDelegate) {
mInvalidateDelegate = invalidateDelegate;
invalidate();
}
public int getBgColor() {
int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
return ColorUtils.setAlphaComponent(mBgColor, alpha);
}
public void drawBackground(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(getBgColor());
//drawCircle(canvas, 0 /* deltaRadius */);
drawShadow(canvas);
}
public void drawShadow(Canvas canvas) {
if (mShadowShader == null) {
return;
}
float radius = getScaledRadius();
float shadowRadius = radius + mStrokeWidth;
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.BLACK);
int offsetX = getOffsetX();
int offsetY = getOffsetY();
final int saveCount;
if (canvas.isHardwareAccelerated()) {
saveCount = canvas.saveLayer(offsetX - mStrokeWidth, offsetY,
offsetX + radius + shadowRadius, offsetY + shadowRadius + shadowRadius,
null, Canvas.CLIP_TO_LAYER_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
} else {
saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipPath(getClipPath(), Region.Op.DIFFERENCE);
}
mShaderMatrix.setScale(shadowRadius, shadowRadius);
mShaderMatrix.postTranslate(radius + offsetX, shadowRadius + offsetY);
mShadowShader.setLocalMatrix(mShaderMatrix);
mPaint.setAlpha(mShadowAlpha);
mPaint.setShader(mShadowShader);
canvas.drawPaint(mPaint);
mPaint.setAlpha(255);
mPaint.setShader(null);
if (canvas.isHardwareAccelerated()) {
mPaint.setXfermode(mShadowPorterDuffXfermode);
canvas.drawCircle(radius + offsetX, radius + offsetY, radius, mPaint);
mPaint.setXfermode(null);
}
canvas.restoreToCount(saveCount);
}
public void fadeInBackgroundShadow() {
if (mShadowAnimator != null) {
mShadowAnimator.cancel();
}
mShadowAnimator = ObjectAnimator
.ofInt(this, SHADOW_ALPHA, 0, 255)
.setDuration(100);
mShadowAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mShadowAnimator = null;
}
});
mShadowAnimator.start();
}
public void animateBackgroundStroke() {
if (mStrokeAlphaAnimator != null) {
mStrokeAlphaAnimator.cancel();
}
mStrokeAlphaAnimator = ObjectAnimator
.ofInt(this, STROKE_ALPHA, MAX_BG_OPACITY / 2, MAX_BG_OPACITY)
.setDuration(100);
mStrokeAlphaAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mStrokeAlphaAnimator = null;
}
});
mStrokeAlphaAnimator.start();
}
public void drawBackgroundStroke(Canvas canvas) {
mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, mStrokeAlpha));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mStrokeWidth);
drawCircle(canvas, 1 /* deltaRadius */);
}
public void drawLeaveBehind(Canvas canvas) {
float originalScale = mScale;
mScale = 0.5f;
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.argb(160, 245, 245, 245));
drawCircle(canvas, 0 /* deltaRadius */);
mScale = originalScale;
}
private void drawCircle(Canvas canvas,float deltaRadius) {
float radius = getScaledRadius();
canvas.drawCircle(radius + getOffsetX(), radius + getOffsetY(),
radius - deltaRadius, mPaint);
}
public Path getClipPath() {
mPath.reset();
float r = getScaledRadius();
mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW);
return mPath;
}
// It is the callers responsibility to save and restore the canvas layers.
void clipCanvasHardware(Canvas canvas) {
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setXfermode(mClipPorterDuffXfermode);
float radius = getScaledRadius();
mShaderMatrix.setScale(radius, radius);
mShaderMatrix.postTranslate(radius + getOffsetX(), radius + getOffsetY());
mClipShader.setLocalMatrix(mShaderMatrix);
mPaint.setShader(mClipShader);
canvas.drawPaint(mPaint);
mPaint.setXfermode(null);
mPaint.setShader(null);
}
private void delegateDrawing(CellLayout delegate, int cellX, int cellY) {
if (mDrawingDelegate != delegate) {
delegate.addFolderBackground(this);
}
mDrawingDelegate = delegate;
delegateCellX = cellX;
delegateCellY = cellY;
invalidate();
}
private void clearDrawingDelegate() {
if (mDrawingDelegate != null) {
mDrawingDelegate.removeFolderBackground(this);
}
mDrawingDelegate = null;
isClipping = true;
invalidate();
}
boolean drawingDelegated() {
return mDrawingDelegate != null;
}
private void animateScale(float finalScale, float finalMultiplier,
final Runnable onStart, final Runnable onEnd) {
final float scale0 = mScale;
final float scale1 = finalScale;
final float bgMultiplier0 = mColorMultiplier;
final float bgMultiplier1 = finalMultiplier;
if (mScaleAnimator != null) {
mScaleAnimator.cancel();
}
mScaleAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
mScaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float prog = animation.getAnimatedFraction();
mScale = prog * scale1 + (1 - prog) * scale0;
mColorMultiplier = prog * bgMultiplier1 + (1 - prog) * bgMultiplier0;
invalidate();
}
});
mScaleAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
if (onStart != null) {
onStart.run();
}
}
#Override
public void onAnimationEnd(Animator animation) {
if (onEnd != null) {
onEnd.run();
}
mScaleAnimator = null;
}
});
mScaleAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
mScaleAnimator.start();
}
public void animateToAccept(final CellLayout cl, final int cellX, final int cellY) {
Runnable onStart = new Runnable() {
#Override
public void run() {
delegateDrawing(cl, cellX, cellY);
}
};
animateScale(ACCEPT_SCALE_FACTOR, ACCEPT_COLOR_MULTIPLIER, onStart, null);
}
public void animateToRest() {
// This can be called multiple times -- we need to make sure the drawing delegate
// is saved and restored at the beginning of the animation, since cancelling the
// existing animation can clear the delgate.
final CellLayout cl = mDrawingDelegate;
final int cellX = delegateCellX;
final int cellY = delegateCellY;
Runnable onStart = new Runnable() {
#Override
public void run() {
delegateDrawing(cl, cellX, cellY);
}
};
Runnable onEnd = new Runnable() {
#Override
public void run() {
clearDrawingDelegate();
}
};
animateScale(1f, 1f, onStart, onEnd);
}
public int getBackgroundAlpha() {
return (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
}
public float getStrokeWidth() {
return mStrokeWidth;
}
}
Any help would be amazing
Thanks in advance :)
I'm using this library from GitHub: https://github.com/lzyzsd/CircleProgress
I put ArcProgress into my MainLayout. I'm trying to change values on the progress bar but I cannot not see on screen. If I check with mArcProgress.getProgress(); this get me the right value, but the bar is empty in screen and doesn't show any progress.
I was trying using:
mArcProgress.invalidate();
but doesnt work.
The only way I found was with:
finish();
startActivity(getIntent());
but works not as I expected, because the full screen start and seems like a blink.
Anyone know how from another activity, changes this View and make possible to see the changes when setting mArcProgress.setProgress(aNumber); ?
this is ArcProgressFuel
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
public class ArcProgressFuel extends View {
private Paint paint;
private RectF rectF = new RectF();
private float strokeWidth;
public int progress;
private int max;
private int finishedStrokeColor;
private int unfinishedStrokeColor;
private float arcAngle;
Path path;
MainPageFragment mMainPageFragment;
public static int fuel_to_show;
private float arcBottomHeight;
private final int default_finished_color = Color.WHITE;
private final int default_unfinished_color = Color.rgb(72, 106, 176);
private final float default_suffix_padding;
private final float default_stroke_width;
private final int default_max = 100;
private final float default_arc_angle = 360 * 0.5f; //0.8 for speed, 0.5 for fuel
private float default_text_size;
private final int min_size;
private static final String INSTANCE_STATE = "saved_instance";
private static final String INSTANCE_STROKE_WIDTH = "stroke_width";
public static String instance_progress = "progress";
private static final String INSTANCE_MAX = "max";
private static final String INSTANCE_FINISHED_STROKE_COLOR = "finished_stroke_color";
private static final String INSTANCE_UNFINISHED_STROKE_COLOR = "unfinished_stroke_color";
private static final String INSTANCE_ARC_ANGLE = "arc_angle";
private static final String INSTANCE_SUFFIX = "suffix";
public ArcProgressFuel(Context context) {
this(context, null);
}
public ArcProgressFuel(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ArcProgressFuel(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
min_size = (int) Utils.dp2px(getResources(), 100);
default_suffix_padding = Utils.dp2px(getResources(), 4);
default_stroke_width = Utils.dp2px(getResources(), 4);
TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ArcProgress, defStyleAttr, 0);
initByAttributes(attributes);
attributes.recycle();
initPainters();
}
protected void initByAttributes(TypedArray attributes) {
finishedStrokeColor = attributes.getColor(R.styleable.ArcProgress_arc_finished_color, default_finished_color);
unfinishedStrokeColor = attributes.getColor(R.styleable.ArcProgress_arc_unfinished_color, default_unfinished_color);
arcAngle = attributes.getFloat(R.styleable.ArcProgress_arc_angle, default_arc_angle);
setMax(attributes.getInt(R.styleable.ArcProgress_arc_max, default_max));
setProgress(attributes.getInt(R.styleable.ArcProgress_arc_progress, 0));
strokeWidth = attributes.getDimension(R.styleable.ArcProgress_arc_stroke_width, default_stroke_width);
fuel_to_show=mMainPageFragment.carFuel;
setProgress(fuel_to_show); //Aca puedo modificar el valor
}
protected void initPainters() {
paint = new Paint();
paint.setColor(default_unfinished_color);
paint.setAntiAlias(true);
paint.setStrokeWidth(strokeWidth);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
}
/* #Override
public void invalidate() {
initPainters();
super.invalidate();
postInvalidate();
}*/
public float getStrokeWidth() {
return strokeWidth;
}
public void setStrokeWidth(float strokeWidth) {
this.strokeWidth = strokeWidth;
this.invalidate();
}
public int getProgress() {
return progress;
}
public void setProgress(int progress) {
this.progress = progress;
if (this.progress > getMax()) {
this.progress %= getMax();
}
invalidate();
}
public int getMax() {
return max;
}
public void setMax(int max) {
if (max > 0) {
this.max = max;
invalidate();
}
}
public int getFinishedStrokeColor() {
return finishedStrokeColor;
}
public void setFinishedStrokeColor(int finishedStrokeColor) {
this.finishedStrokeColor = finishedStrokeColor;
this.invalidate();
}
public int getUnfinishedStrokeColor() {
return unfinishedStrokeColor;
}
public void setUnfinishedStrokeColor(int unfinishedStrokeColor) {
this.unfinishedStrokeColor = unfinishedStrokeColor;
this.invalidate();
}
public float getArcAngle() {
return arcAngle;
}
public void setArcAngle(float arcAngle) {
this.arcAngle = arcAngle;
this.invalidate();
}
#Override
protected int getSuggestedMinimumHeight() {
return min_size;
}
#Override
protected int getSuggestedMinimumWidth() {
return min_size;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
rectF.set(strokeWidth / 2f, strokeWidth / 2f, width - strokeWidth / 2f, MeasureSpec.getSize(heightMeasureSpec) - strokeWidth / 2f);
float radius = width / 2f;
float angle = (360 - arcAngle) / 2f;
arcBottomHeight = radius * (float) (1 - Math.cos(angle / 180 * Math.PI));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float startAngle = 90 - arcAngle / 2f; //270 for speed, 90 for fuel
float finishedSweepAngle = progress / (float) getMax() * arcAngle;
float finishedStartAngle = startAngle;
if(progress == 0) finishedStartAngle = 0.01f;
paint.setColor(unfinishedStrokeColor);
canvas.drawArc(rectF, startAngle, arcAngle, false, paint);
paint.setColor(finishedStrokeColor);
canvas.drawArc(rectF, finishedStartAngle, finishedSweepAngle, false, paint);
if(arcBottomHeight == 0) {
float radius = getWidth() / 2f;
float angle = (360 - arcAngle) / 2f;
arcBottomHeight = radius * (float) (1 - Math.cos(angle / 180 * Math.PI));
}
}
#Override
protected Parcelable onSaveInstanceState() {
final Bundle bundle = new Bundle();
bundle.putParcelable(INSTANCE_STATE, super.onSaveInstanceState());
bundle.putFloat(INSTANCE_STROKE_WIDTH, getStrokeWidth());
bundle.putInt(instance_progress, getProgress());
bundle.putInt(INSTANCE_MAX, getMax());
bundle.putInt(INSTANCE_FINISHED_STROKE_COLOR, getFinishedStrokeColor());
bundle.putInt(INSTANCE_UNFINISHED_STROKE_COLOR, getUnfinishedStrokeColor());
bundle.putFloat(INSTANCE_ARC_ANGLE, getArcAngle());
return bundle;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
if(state instanceof Bundle) {
final Bundle bundle = (Bundle) state;
strokeWidth = bundle.getFloat(INSTANCE_STROKE_WIDTH);
setMax(bundle.getInt(INSTANCE_MAX));
setProgress(bundle.getInt(instance_progress));
finishedStrokeColor = bundle.getInt(INSTANCE_FINISHED_STROKE_COLOR);
unfinishedStrokeColor = bundle.getInt(INSTANCE_UNFINISHED_STROKE_COLOR);
initPainters();
super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATE));
return;
}
super.onRestoreInstanceState(state);
}
}
this is inside my MainLayout, this part I want to update:
<RelativeLayout
android:id="#+id/scale_gaz_inmain"
android:layout_width="#dimen/_57sdp"
android:layout_centerHorizontal="true"
android:layout_below="#+id/icon_gazinmain"
android:layout_height="#dimen/_60sdp"
>
<com.itelma.tele2.ArcProgressFuel
android:id="#+id/arc_progress2"
android:background="#android:color/transparent"
android:layout_width="match_parent"
android:scaleX="-1"
android:layout_height="match_parent"
custom:arc_progress="50"
custom:arc_unfinished_color="#color/mDark_gray"
custom:arc_finished_color="#color/colorApp"
custom:arc_max="100"/>
</RelativeLayout>
so in my main java file I have:
ArcProgressFuel mArcProgressFuel;
mArcProgressFuel=new ArcProgressFuel(this);
mArcProgressFuel.invalidate();
ok, I could resove it, with the following lines:
mArcProgressFuel=new ArcProgressFuel(this);
RelativeLayout relativeLayout2=(RelativeLayout)findViewById(R.id.speed_indicator);
View child = getLayoutInflater().inflate(R.layout.fragment_speed_indicator, null);
relativeLayout2.addView(child);
I would like to know if there is any simple solution to creating an overlay where an element would get highlighted.
So the final result would look something like this:
I would like to avoid using ShowcaseViewLibrary from variety of reason (it doesn't have the look I need, it's no longer supported etc.).
I thought about using FrameLayout but I am not sure how to achieve the highlighted existing element. Also putting the arrows or bubbles to the elements so they connect precisely.
A quick and easy way would be to make a copy of the Activity you want to demonstrate with overlays added and just show that. It's what I do and it works fine.
/**
* Created by Nikola D. on 10/1/2015.
*/
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class ShowCaseLayout extends ScrimInsetsFrameLayout {
private static final long DEFAULT_DURATION = 1000;
private static final int DEFAULT_RADIUS = 100;
private Paint mEmptyPaint;
private AbstractQueue<Pair<String, View>> mTargetQueue;
private int mLastCenterX = 600;
private int mLastCenterY = 100;
private ValueAnimator.AnimatorUpdateListener mAnimatorListenerX = new ValueAnimator.AnimatorUpdateListener() {
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mLastCenterX = (int) animation.getAnimatedValue();
setWillNotDraw(false);
postInvalidate();
}
};
private ValueAnimator.AnimatorUpdateListener mAnimatorListenerY = new ValueAnimator.AnimatorUpdateListener() {
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mLastCenterY = (int) animation.getAnimatedValue();
setWillNotDraw(false);
postInvalidate();
}
};
private ValueAnimator mCenterAnimatorX;
private ValueAnimator mCenterAnimatorY;
private boolean canRender = false;
private OnAttachStateChangeListener mAttachListener = new OnAttachStateChangeListener() {
#Override
public void onViewAttachedToWindow(View v) {
canRender = true;
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
#Override
public void onViewDetachedFromWindow(View v) {
canRender = false;
removeOnAttachStateChangeListener(this);
}
};
private long mDuration = DEFAULT_DURATION;
private int mRadius = (int) DEFAULT_RADIUS;
private Interpolator mInterpolator = new LinearOutSlowInInterpolator();
private ValueAnimator mRadiusAnimator;
private ValueAnimator.AnimatorUpdateListener mRadiusAnimatorListener = new ValueAnimator.AnimatorUpdateListener() {
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mRadius = (int) animation.getAnimatedValue();
}
};
private TextView mDescriptionText;
private Button mGotItButton;
private OnClickListener mExternalGotItButtonlistener;
private OnClickListener mGotItButtonClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
setNextTarget();
if (mExternalGotItButtonlistener != null) {
mExternalGotItButtonlistener.onClick(v);
}
}
};
private Animator.AnimatorListener mAnimatorSetListener = new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
setNextTarget();
invalidate();
//mDescriptionText.layout(mTempRect.left, mTempRect.bottom + mTempRect.bottom, mDescriptionText. );
}
};
private Rect mTempRect;
private Paint mBackgroundPaint;
private Bitmap bitmap;
private Canvas temp;
private int mStatusBarHeight = 0;
public ShowCaseLayout(Context context) {
super(context);
setupLayout();
}
public ShowCaseLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setupLayout();
}
public ShowCaseLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setupLayout();
}
public void setTarget(View target, String hint) {
mTargetQueue.add(new Pair<>(hint, target));
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void setupLayout() {
mTargetQueue = new LinkedBlockingQueue<>();
setWillNotDraw(false);
mBackgroundPaint = new Paint();
int c = Color.argb(127, Color.red(Color.RED), Color.blue(Color.RED), Color.green(Color.RED));
mBackgroundPaint.setColor(c);
mEmptyPaint = new Paint();
mEmptyPaint.setColor(Color.TRANSPARENT);
mEmptyPaint.setStyle(Paint.Style.FILL);
mEmptyPaint.setAntiAlias(true);
mEmptyPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
if (!ViewCompat.isLaidOut(this))
addOnAttachStateChangeListener(mAttachListener);
else canRender = true;
mDescriptionText = new TextView(getContext());
mGotItButton = new Button(getContext());
mGotItButton.setText("GOT IT");
mGotItButton.setOnClickListener(mGotItButtonClickListener);
addView(mGotItButton, generateDefaultLayoutParams());
//ViewCompat.setAlpha(this, 0.5f);
}
#Override
protected LayoutParams generateDefaultLayoutParams() {
return new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!canRender) return;
temp.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mBackgroundPaint);
temp.drawCircle(mLastCenterX, mLastCenterY, mRadius, mEmptyPaint);
canvas.drawBitmap(bitmap, 0, 0, null);
}
#TargetApi(Build.VERSION_CODES.M)
private void animateCenterToNextTarget(View target) {
int[] locations = new int[2];
target.getLocationInWindow(locations);
int x = locations[0];
int y = locations[1];
mTempRect = new Rect(x, y, x + target.getWidth(), y + target.getHeight());
int centerX = mTempRect.centerX();
int centerY = mTempRect.centerY();
int targetRadius = Math.abs(mTempRect.right - mTempRect.left) / 2;
targetRadius += targetRadius * 0.05;
mCenterAnimatorX = ValueAnimator.ofInt(mLastCenterX, centerX).setDuration(mDuration);
mCenterAnimatorX.addUpdateListener(mAnimatorListenerX);
mCenterAnimatorY = ValueAnimator.ofInt(mLastCenterY, centerY).setDuration(mDuration);
mCenterAnimatorY.addUpdateListener(mAnimatorListenerY);
mRadiusAnimator = ValueAnimator.ofInt(mRadius, targetRadius);
mRadiusAnimator.addUpdateListener(mRadiusAnimatorListener);
playTogether(mCenterAnimatorY, mCenterAnimatorX, mRadiusAnimator);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
bitmap.eraseColor(Color.TRANSPARENT);
temp = new Canvas(bitmap);
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void playTogether(ValueAnimator... animators) {
AnimatorSet set = new AnimatorSet();
set.setInterpolator(mInterpolator);
set.setDuration(mDuration);
set.playTogether(animators);
set.addListener(mAnimatorSetListener);
set.start();
}
public void start(Activity activity) {
if (getParent() == null) {
attachLayoutToWindow(activity);
}
setNextTarget();
}
private void setNextTarget() {
Pair<String, View> pair = mTargetQueue.poll();
if (pair != null) {
if (pair.second != null)
animateCenterToNextTarget(pair.second);
mDescriptionText.setText(pair.first);
}
}
private void attachLayoutToWindow(Activity activity) {
FrameLayout rootLayout = (FrameLayout) activity.findViewById(android.R.id.content);
rootLayout.addView(this);
}
public void hideShowcaseLayout() {
}
public void setGotItButtonClickistener(OnClickListener mExternalGotItButtonlistener) {
this.mExternalGotItButtonlistener = mExternalGotItButtonlistener;
}
public TextView getDescriptionTextView() {
return mDescriptionText;
}
public void setDescriptionTextView(TextView textView) {
mDescriptionText = textView;
}
}
Please note that this code is incomplete and is under development, you should tweak it according your needs.
This layout will draw a circle around the View over its Rect.
Instead of drawing the circle you could drawRect to the Rect bounds of the target view or drawRoundRect if the View's Rect and background drawable Rect are complementary.
Drawing the line (drawLine()) should be from the target view:
startX = (rect.right - rect.left)/2;
startY = rect.bottom;
endX = startX;
endY = startY + arbitraryLineHeight;
if the endY is larger than the layout height you should be drawing it upwards rect.top - arbitraryLineHeight, otherwise you draw it as it is.
arbitraryLineHeight could be descriptionViewRect.top which makes it more dynamic, instead of using a constant value.
I am using Dave Morrissey's sub sampling scale Image View to display a high resolution map image. I want to add location markers at predefined coordinates on the map such that even when the image is zoomed or panned around, the markers stay put at the specified coordinates. How can I do this?
Extend the SubsamplingScaleImageView and override its onDraw() method
public class MapView extends SubsamplingScaleImageView {
private PointF sPin;
ArrayList<MapPin> mapPins;
ArrayList<DrawnPin> drawnPins;
Context context;
String tag = getClass().getSimpleName();
public MapView(Context context) {
this(context, null);
this.context = context;
}
public MapView(Context context, AttributeSet attr) {
super(context, attr);
this.context = context;
initialise();
}
public void setPins(ArrayList<MapPin> mapPins)
{
this.mapPins = mapPins;
initialise();
invalidate();
}
public PointF getPin() {
return sPin;
}
private void initialise() {
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Don't draw pin before image is ready so it doesn't move around during setup.
if (!isReady()) {
return;
}
drawnPins = new ArrayList<>();
Paint paint = new Paint();
paint.setAntiAlias(true);
float density = getResources().getDisplayMetrics().densityDpi;
for(int i=0;i<mapPins.size();i++)
{
MapPin mPin = mapPins.get(i);
Bitmap bmpPin = Utils.getBitmapFromAsset(context, mPin.getPinImgSrc());
float w = (density/420f) * bmpPin.getWidth();
float h = (density/420f) * bmpPin.getHeight();
bmpPin = Bitmap.createScaledBitmap(bmpPin, (int)w, (int)h, true);
PointF vPin = sourceToViewCoord(mPin.getPoint());
//in my case value of point are at center point of pin image, so we need to adjust it here
float vX = vPin.x - (bmpPin.getWidth()/2);
float vY = vPin.y - (bmpPin.getHeight()/2);
canvas.drawBitmap(bmpPin, vX, vY, paint);
//add added pin to an Array list to get touched pin
DrawnPin dPin = new DrawnPin();
dPin.setStartX(mPin.getX()-w/2);
dPin.setEndX(mPin.getX()+w/2);
dPin.setStartY(mPin.getY()-h/2);
dPin.setEndY(mPin.getY()+h/2);
dPin.setId(mPin.getId());
drawnPins.add(dPin);
}
}
public int getPinIdByPoint(PointF point)
{
for(int i=drawnPins.size()-1;i>=0;i--)
{
DrawnPin dPin = drawnPins.get(i);
if(point.x >= dPin.getStartX() && point.x<=dPin.getEndX())
{
if(point.y >= dPin.getStartY() && point.y<=dPin.getEndY())
{
return dPin.getId();
}
}
}
return -1; //negative no means no pin selected
}
class DrawnPin
{
float startX,startY,endX,endY;
int id;
public DrawnPin(float startX, float startY, float endX, float endY, int id)
{
this.startX = startX;
this.startY = startY;
this.endX = endX;
this.endY = endY;
this.id = id;
}
public DrawnPin()
{
//empty
}
public float getStartX()
{
return startX;
}
public void setStartX(float startX)
{
this.startX = startX;
}
public float getStartY()
{
return startY;
}
public void setStartY(float startY)
{
this.startY = startY;
}
public float getEndX()
{
return endX;
}
public void setEndX(float endX)
{
this.endX = endX;
}
public float getEndY()
{
return endY;
}
public void setEndY(float endY)
{
this.endY = endY;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
}
In the main activity I have a handler to exchange messages with the bluetooth service. This works fine.
Now, I want launch second activity (SurfaceViewAnimation). I do this with:
startActivity (new Intent (this, SurfaceViewAnimation.class));
but I want change some attributes of SufaceViewAnimation class from main activity when it receives a command for bluetooth.
How I can do this?
The code of SufaceViewAnimation class is:
class BouncingBallView extends SurfaceView implements SurfaceHolder.Callback {
private BouncingBallAnimationThread bbThread = null;
private int xPosition = getWidth()/2;
private int yPosition = getHeight()/2;
private int xDirection = 20;
private int yDirection = 40;
private static int radius = 20;
private static int ballColor = Color.RED;
public BouncingBallView(Context ctx, AttributeSet attrs, int defStyle) {
super(ctx, attrs, defStyle);
getHolder().addCallback(this);
}
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.BLACK);
canvas.drawRect(0,0,getWidth(),getHeight(), paint);
paint.setColor(ballColor);
canvas.drawCircle(xPosition, yPosition, radius, paint);
}
public void surfaceCreated(SurfaceHolder holder) {
if (bbThread!=null) return;
bbThread = new BouncingBallAnimationThread(getHolder());
bbThread.start();
}
public void surfaceChanged(SurfaceHolder holder,int format, int width, int height) { }
public void surfaceDestroyed(SurfaceHolder holder) {
bbThread.stop = true;
}
private class BouncingBallAnimationThread extends Thread {
public boolean stop = false;
private SurfaceHolder surfaceHolder;
public BouncingBallAnimationThread(SurfaceHolder surfaceHolder) {
this.surfaceHolder = surfaceHolder;
}
public void run() {
while (!stop) {
xPosition += xDirection;
yPosition += yDirection;
if (xPosition<0) {
xDirection = -xDirection;
xPosition = radius; }
if (xPosition>getWidth()-radius) {
xDirection = -xDirection;
xPosition = getWidth()-radius; }
if (yPosition<0) {
yDirection = -yDirection;
yPosition = radius; }
if (yPosition>getHeight()-radius) {
yDirection = -yDirection;
yPosition = getHeight()-radius-1; }
Canvas c = null;
try {
c = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
onDraw(c);
}
} finally {
if (c != null) surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() != MotionEvent.ACTION_DOWN) return false;
if (xDirection!=0 || yDirection!=0)
xDirection = yDirection = 0;
else {
xDirection = (int) event.getX() - xPosition;
yDirection = (int) event.getY() - yPosition;
}
if(ballColor==Color.RED)
ballColor=Color.GREEN;
else
ballColor=Color.RED;
return true;
}
public void setxDirection(int xDirection) {
this.xDirection = xDirection;
}
public void setyDirection(int yDirection) {
this.yDirection = yDirection;
}
public void setballColor(int ballColor) {
this.ballColor = ballColor;
}
public void setradius(int radius) {
this.radius = radius;
}
}
public class SurfaceViewAnimation extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new BouncingBallView(this,null,0));
}
}
If you want to change the data in SurfaceViewAnimation while SurfaceViewAnimation is running, you're best off with creating an own handler.
Besides that you can fall back to basic Inter(Process/Activity)Communication using Handlers, static variables or the Observer pattern.