/* * Copyright (C) 2014 AChep@xda <artemchep@gmail.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ package com.achep.base.ui.drawables; import android.animation.Animator; import android.animation.ObjectAnimator; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Property; import com.achep.base.utils.FloatProperty; /** * A class for creating simple transformation buttons. It is very simple to * use and perfectly fits simple Material icons' transformation. * * @author Artem Chepurnoy * @see com.achep.base.ui.drawables.PlayPauseDrawable */ public abstract class TransformationDrawable extends Drawable { private final Path mPath; private final Paint mPaint; private final float[][][] mVertex; private final float[][] mVertexFrom; private int mSize = Integer.MAX_VALUE; private float mProgress; private int mToShape; private final Animator mAnimator = ObjectAnimator.ofFloat(this, TRANSFORM, 0f, 1f); private final static Property<TransformationDrawable, Float> TRANSFORM = new FloatProperty<TransformationDrawable>("setTransformation") { @Override public void setValue(TransformationDrawable object, float value) { object.setTransformation(value); } @Override public Float get(TransformationDrawable object) { return object.getTransformation(); } }; protected TransformationDrawable(@NonNull float[][]... vertex) { mVertexFrom = new float[2][vertex[0][0].length]; mVertex = vertex; mProgress = 1f; mPath = new Path(); mPath.setFillType(Path.FillType.WINDING); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setColor(Color.WHITE); mPaint.setStyle(Paint.Style.FILL); } public void transformToShape(int i) { transformToShape(i, true); } public void transformToShape(int i, boolean animate) { if (setTargetShape(i) && animate && !mAnimator.isRunning()) { // If the target shape is new, then we probably should animate the // change. mAnimator.start(); } } /** * Sets the size of canvas rectangle. * * @param size size in pixels. */ public void setSize(int size) { mSize = size; } public boolean setTargetShape(int i) { if (mToShape == i) return false; updateVertexFrom(); mToShape = i; return true; } public void setTransformation(float progress) { mProgress = progress; Rect rect = getBounds(); final float size = Math.min(Math.min( rect.right - rect.left, rect.bottom - rect.top), mSize); final float left = rect.left + (rect.right - rect.left - size) / 2; final float top = rect.top + (rect.bottom - rect.top - size) / 2; mPath.reset(); mPath.moveTo( left + calcTransformation(0, 0, progress, size), top + calcTransformation(1, 0, progress, size)); for (int i = 1; i < mVertex[0][0].length; i++) { mPath.lineTo( left + calcTransformation(0, i, progress, size), top + calcTransformation(1, i, progress, size)); } mPath.close(); invalidateSelf(); } public float getTransformation() { return mProgress; } private float calcTransformation(int type, int i, float progress, float size) { float v0 = mVertexFrom[type][i] * (1f - progress); float v1 = mVertex[mToShape][type][i] * progress; return (v0 + v1) * size; } /** * Updates the current `init` state of the icon. While animating, the icon will go * from this state to one of the defined {@link #mVertex by vertexes}. */ private void updateVertexFrom() { int length = mVertexFrom[0].length; for (int i = 0; i < length; i++) { mVertexFrom[0][i] = calcTransformation(0, i, mProgress, 1f); mVertexFrom[1][i] = calcTransformation(1, i, mProgress, 1f); } } /** * {@inheritDoc} */ @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); setTransformation(mProgress); } /** * {@inheritDoc} */ @Override public void draw(Canvas canvas) { canvas.drawPath(mPath, mPaint); } /** * {@inheritDoc} */ @Override public void setAlpha(int alpha) { mPaint.setAlpha(alpha); invalidateSelf(); } /** * {@inheritDoc} */ @Override public void setColorFilter(@Nullable ColorFilter cf) { mPaint.setColorFilter(cf); invalidateSelf(); } /** * Specify an optional color filter for the drawable. Note that the color * is an int containing alpha as well as r,g,b. This 32bit value is not * pre-multiplied, meaning that its alpha can be any value, regardless * of the values of r,g,b. See the {@link Color Color class} for more details. * * @param color the color to be set * @see #setColorFilter(ColorFilter) */ public void setColor(int color) { mPaint.setColor(color); invalidateSelf(); } /** * {@inheritDoc} */ @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } }