Все началось с того, что необходимо было стилизовать ProgressBar в одном из приложений. Как известно в андройде для этого используются Drawable объекты, описанные либо в виде XML, либо унаследованные от абстрактного класса Drawable, который входит в состав AndroidSDK. Про первый способ информации довольно много, а вот о втором — пойдет речь в данном посте.
Для начала создадим два класса CustomProgressDrawable и CustomProgressIndeterminateDrawable, для determinate и indeterminate состояний нашего прогресс бара соотвественно.
Начнем с CustomProgressDrawable:
В конструкторе созданим объект класса Paint, для того, чтобы в дальнейшем мы могли с помощью него рисовать на Canvas.
public CustomProgressDrawable(int color) {
super();
mPaint = new Paint();
mPaint.setColor(color);
}
Далее переопределяем метод onLevelChange, именно этот метод вызывется, когда мы вызываем метод setProgress у нашего объекта ProgressBar. В конце метода вызываем метод invalidateSelf, который заставит наш Drawable отрисоваться.
@Override
protected boolean onLevelChange(int level) {
final float drawTo = START_ANGLE + HALF_CIRCLE_DEGREES * level / (float)MainActivity.PROGRESS_MAX_VALUE;
boolean update = drawTo != mDrawTo;
mDrawTo = drawTo;
invalidateSelf();
return update;
}
Так как класс Drawable является абстрактным, нам также будет необходимо реализовать все абстрактные методы данного класса, но их реализация довольно простая и ее в статье я опустил. Ниже есть ссылка на полный код примера.
Логику отрисовки реализуем в методе draw:
@Override
public void draw(Canvas canvas) {
canvas.rotate(CANVAS_1ST_ARC_ROTATE_DEGREES, getBounds().centerX(), getBounds().centerY());
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawOval(mBoundsF, mPaint);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawArc(mInnerBoundsF, START_ANGLE, mDrawTo, true, mPaint);
canvas.rotate(CANVAS_2ND_ARC_ROTATE_DEGREES, getBounds().centerX(), getBounds().centerY());
canvas.drawArc(mInnerBoundsF, START_ANGLE, mDrawTo, true, mPaint);
}
Переходим к CustomProgressIndeterminateDrawable:
В целом логика реализации аналогична предыдущему классу, за исключением того, что вместо переопределения метода onLevelChange, нам необходимо имплементировать два интерфейса Animatable и Runnable.
@Override
public void run() {
invalidateSelf();
scheduleSelf(this, AnimationUtils.currentAnimationTimeMillis());
}
@Override
public boolean isRunning() {
return mIsRunning;
}
@Override
public void start() {
if (!isRunning()) {
mIsRunning = true;
mStartTicks = AnimationUtils.currentAnimationTimeMillis();
run();
}
}
@Override
public void stop() {
if (isRunning()) {
unscheduleSelf(this);
mIsRunning = false;
}
}
Сама анимация происходит в методе run, сначала мы перерисовываем наш Drawable, вызывая метод invalidateSelf. Затем, используя метод scheduleSelf класса Drawable, вновь вызываем метод run.
Теперь, там где нам необходимо, инициализируем наши Drawable и устанавливаем их обычному элементу ProgressBar, и далее работаем с ним через методы setProgress и setIndeterminate.
final int color = getResources().getColor(R.color.green);
// initialize drawables
CustomProgressDrawable progressDrawable = new
CustomProgressDrawable(color);
CustomProgressIndeterminateDrawable barIndeterminateDrawable = new
CustomProgressIndeterminateDrawable(color);
// set drawables and max value
mCustomProgressBar = (ProgressBar)
findViewById(R.id.custom_progress_bar);
mCustomProgressBar.setIndeterminateDrawable(barIndeterminateDrawable);
mCustomProgressBar.setProgressDrawable(progressDrawable);
mCustomProgressBar.setMax(PROGRESS_MAX_VALUE);
И вот, что в итоге у нас получилось:
Весь код на github тыц.