291 lines
7.8 KiB
Java
291 lines
7.8 KiB
Java
/*
|
|
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
|
*
|
|
* This file is part of RoboSwag library.
|
|
*
|
|
* 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 ru.touchin.roboswag.components.views;
|
|
|
|
import android.content.Context;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Color;
|
|
import android.graphics.ColorFilter;
|
|
import android.graphics.Paint;
|
|
import android.graphics.PixelFormat;
|
|
import android.graphics.Rect;
|
|
import android.graphics.RectF;
|
|
import android.graphics.drawable.Animatable;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.SystemClock;
|
|
import android.support.annotation.ColorInt;
|
|
import android.support.annotation.NonNull;
|
|
|
|
import ru.touchin.roboswag.components.utils.UiUtils;
|
|
|
|
/**
|
|
* Created by Gavriil Sitnikov on 01/03/16.
|
|
* Simple realization of endless progress bar which is looking material-like.
|
|
*/
|
|
public class MaterialProgressDrawable extends Drawable implements Runnable, Animatable {
|
|
|
|
private static final int UPDATE_INTERVAL = 1000 / 60;
|
|
|
|
private static final float DEFAULT_STROKE_WIDTH_DP = 4.5f;
|
|
private static final Parameters DEFAULT_PARAMETERS = new Parameters(20, 270, 4, 12, 4, 8);
|
|
|
|
private final int size;
|
|
@NonNull
|
|
private final Paint paint;
|
|
@NonNull
|
|
private Parameters parameters = DEFAULT_PARAMETERS;
|
|
@NonNull
|
|
private final RectF arcBounds = new RectF();
|
|
|
|
private float rotationAngle;
|
|
private float arcSize;
|
|
private boolean running;
|
|
|
|
public MaterialProgressDrawable(@NonNull final Context context) {
|
|
this(context, -1);
|
|
}
|
|
|
|
public MaterialProgressDrawable(@NonNull final Context context, final int size) {
|
|
super();
|
|
|
|
this.size = size;
|
|
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
paint.setStyle(Paint.Style.STROKE);
|
|
paint.setStrokeWidth(UiUtils.OfMetrics.dpToPixels(context, DEFAULT_STROKE_WIDTH_DP));
|
|
paint.setColor(Color.BLACK);
|
|
}
|
|
|
|
@Override
|
|
public int getIntrinsicWidth() {
|
|
return size;
|
|
}
|
|
|
|
@Override
|
|
public int getIntrinsicHeight() {
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* Returns width of arc.
|
|
*
|
|
* @return Width.
|
|
*/
|
|
public float getStrokeWidth() {
|
|
return paint.getStrokeWidth();
|
|
}
|
|
|
|
/**
|
|
* Sets width of arc.
|
|
*
|
|
* @param strokeWidth Width.
|
|
*/
|
|
public void setStrokeWidth(final float strokeWidth) {
|
|
paint.setStrokeWidth(strokeWidth);
|
|
updateArcBounds();
|
|
invalidateSelf();
|
|
}
|
|
|
|
/**
|
|
* Sets color of arc.
|
|
*
|
|
* @param color Color.
|
|
*/
|
|
public void setColor(@ColorInt final int color) {
|
|
paint.setColor(color);
|
|
invalidateSelf();
|
|
}
|
|
|
|
/**
|
|
* Returns magic parameters of spinning.
|
|
*
|
|
* @return Parameters of spinning.
|
|
*/
|
|
@NonNull
|
|
public Parameters getParameters() {
|
|
return parameters;
|
|
}
|
|
|
|
/**
|
|
* Sets magic parameters of spinning.
|
|
*
|
|
* @param parameters Parameters of spinning.
|
|
*/
|
|
public void setParameters(@NonNull final Parameters parameters) {
|
|
this.parameters = parameters;
|
|
invalidateSelf();
|
|
}
|
|
|
|
@Override
|
|
protected void onBoundsChange(final Rect bounds) {
|
|
super.onBoundsChange(bounds);
|
|
updateArcBounds();
|
|
}
|
|
|
|
private void updateArcBounds() {
|
|
arcBounds.set(getBounds());
|
|
//HACK: + 1 as anti-aliasing drawing bug workaround
|
|
final int inset = (int) (paint.getStrokeWidth() / 2) + 1;
|
|
arcBounds.inset(inset, inset);
|
|
}
|
|
|
|
@SuppressWarnings("PMD.NPathComplexity")
|
|
@Override
|
|
public void draw(@NonNull final Canvas canvas) {
|
|
final boolean isGrowingCycle = (((int) (arcSize / parameters.maxAngle)) % 2) == 0;
|
|
final float angle = arcSize % parameters.maxAngle;
|
|
final float shift = (angle / parameters.maxAngle) * parameters.gapAngle;
|
|
canvas.drawArc(arcBounds, isGrowingCycle ? rotationAngle + shift : rotationAngle + parameters.gapAngle - shift,
|
|
isGrowingCycle ? angle + parameters.gapAngle : parameters.maxAngle - angle + parameters.gapAngle, false, paint);
|
|
//TODO: compute based on animation start time
|
|
rotationAngle += isGrowingCycle ? parameters.rotationMagicNumber1 : parameters.rotationMagicNumber2;
|
|
arcSize += isGrowingCycle ? parameters.arcMagicNumber1 : parameters.arcMagicNumber2;
|
|
if (arcSize < 0) {
|
|
arcSize = 0;
|
|
}
|
|
if (isRunning()) {
|
|
scheduleSelf(this, SystemClock.uptimeMillis() + UPDATE_INTERVAL);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setAlpha(final int alpha) {
|
|
paint.setAlpha(alpha);
|
|
invalidateSelf();
|
|
}
|
|
|
|
@Override
|
|
public void setColorFilter(final ColorFilter colorFilter) {
|
|
paint.setColorFilter(colorFilter);
|
|
invalidateSelf();
|
|
}
|
|
|
|
@Override
|
|
public int getOpacity() {
|
|
return PixelFormat.TRANSLUCENT;
|
|
}
|
|
|
|
@Override
|
|
public void start() {
|
|
if (!running) {
|
|
running = true;
|
|
run();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void stop() {
|
|
if (running) {
|
|
unscheduleSelf(this);
|
|
running = false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isRunning() {
|
|
return running;
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
if (running) {
|
|
invalidateSelf();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Some parameters which are using to spin progress bar.
|
|
*/
|
|
public static class Parameters {
|
|
|
|
private final float gapAngle;
|
|
private final float maxAngle;
|
|
private final float rotationMagicNumber1;
|
|
private final float rotationMagicNumber2;
|
|
private final float arcMagicNumber1;
|
|
private final float arcMagicNumber2;
|
|
|
|
public Parameters(final float gapAngle, final float maxAngle,
|
|
final float rotationMagicNumber1, final float rotationMagicNumber2,
|
|
final float arcMagicNumber1, final float arcMagicNumber2) {
|
|
this.gapAngle = gapAngle;
|
|
this.maxAngle = maxAngle;
|
|
this.rotationMagicNumber1 = rotationMagicNumber1;
|
|
this.rotationMagicNumber2 = rotationMagicNumber2;
|
|
this.arcMagicNumber1 = arcMagicNumber1;
|
|
this.arcMagicNumber2 = arcMagicNumber2;
|
|
}
|
|
|
|
/**
|
|
* Returns angle of gap of arc.
|
|
*
|
|
* @return Angle of gap.
|
|
*/
|
|
public float getGapAngle() {
|
|
return gapAngle;
|
|
}
|
|
|
|
/**
|
|
* Returns maximum angle of arc.
|
|
*
|
|
* @return Maximum angle of arc.
|
|
*/
|
|
public float getMaxAngle() {
|
|
return maxAngle;
|
|
}
|
|
|
|
/**
|
|
* Magic parameter 1.
|
|
*
|
|
* @return Magic.
|
|
*/
|
|
public float getRotationMagicNumber1() {
|
|
return rotationMagicNumber1;
|
|
}
|
|
|
|
/**
|
|
* Magic parameter 2.
|
|
*
|
|
* @return Magic.
|
|
*/
|
|
public float getRotationMagicNumber2() {
|
|
return rotationMagicNumber2;
|
|
}
|
|
|
|
/**
|
|
* Magic parameter 3.
|
|
*
|
|
* @return Magic.
|
|
*/
|
|
public float getArcMagicNumber1() {
|
|
return arcMagicNumber1;
|
|
}
|
|
|
|
/**
|
|
* Magic parameter 4.
|
|
*
|
|
* @return Magic.
|
|
*/
|
|
public float getArcMagicNumber2() {
|
|
return arcMagicNumber2;
|
|
}
|
|
|
|
}
|
|
|
|
} |