/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Kurt Mbanje
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package mbanje.kurt.fabbutton;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.TransitionDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;
import com.fastbootmobile.encore.app.R;
public class CircleImageView extends ImageView {
public interface OnFabViewListener {
public void onProgressVisibilityChanged(boolean visible);
public void onProgressCompleted();
}
private static final int animationDuration = 200;
private int centerY;
private int centerX;
private int viewRadius;
private boolean progressVisible;
private int circleRadius;
private Paint circlePaint;
private OnFabViewListener fabViewListener;
private final int ringAlpha = 75;
private int ringRadius;
private Paint ringPaint;
private float currentRingWidth;
private float ringWidthRatio = 0.14f; //of a possible 1f;
private BitmapDrawable drawables[] = new BitmapDrawable[2];
private TransitionDrawable crossfader;
private int ringWidth;
private ObjectAnimator ringAnimator;
float radiusY = 3.5f;
float radiusX = 0f;
float shadowRadius = 10f;
int shadowTransparency = 100;
private boolean showEndBitmap;
public CircleImageView(Context context) {
super(context);
init(context, null);
}
public CircleImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
public void setFabViewListener(OnFabViewListener fabViewListener) {
this.fabViewListener = fabViewListener;
}
private void init(Context context, AttributeSet attrs) {
this.setFocusable(false);
this.setScaleType(ScaleType.CENTER_INSIDE);
setClickable(true);
circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint.setStyle(Paint.Style.FILL);
circlePaint.setShadowLayer(shadowRadius, radiusX, radiusY, Color.argb(shadowTransparency, 0, 0, 0));
ringPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
ringPaint.setStyle(Paint.Style.STROKE);
setWillNotDraw(false);
//setLayerType(View.LAYER_TYPE_SOFTWARE, null);
int color = Color.BLACK;
if (attrs != null) {
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView);
color = a.getColor(R.styleable.CircleImageView_android_color, Color.BLACK);
ringWidthRatio = a.getFloat(R.styleable.CircleImageView_fbb_progressWidthRatio, ringWidthRatio);
a.recycle();
}
setColor(color);
final int pressedAnimationTime = animationDuration;
ringAnimator = ObjectAnimator.ofFloat(this, "currentRingWidth", 0f, 0f);
ringAnimator.setDuration(pressedAnimationTime);
ringAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (fabViewListener != null) {
fabViewListener.onProgressVisibilityChanged(progressVisible);
}
}
});
}
public void setShowEndBitmap(boolean showEndBitmap) {
this.showEndBitmap = showEndBitmap;
}
/**
* sets the icon that will be shown on the fab icon
* @param resource the resource id of the icon
*/
public void setIcon(int resource,int endBitmapResource){
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(),resource);
if(showEndBitmap){
Bitmap endBitmap = BitmapFactory.decodeResource(getResources(),endBitmapResource);
drawables[0] = new BitmapDrawable(getResources(),srcBitmap);
drawables[1] = new BitmapDrawable(getResources(),endBitmap);
crossfader = new TransitionDrawable(drawables);
crossfader.setCrossFadeEnabled(true);
setImageDrawable(crossfader);
}else{
setImageBitmap(srcBitmap);
}
}
public void resetIcon(){
crossfader.resetTransition();
}
/**
* this sets the thickness of the ring as a fraction of the radius of the circle.
* @param ringWidthRatio the ratio 0-1
*/
public void setRingWidthRatio(float ringWidthRatio) {
this.ringWidthRatio = ringWidthRatio;
}
@Override
protected void onDraw(Canvas canvas) {
final float ringR = ringRadius + currentRingWidth;
canvas.drawCircle(centerX, centerY, ringR, ringPaint); // the outer ring
canvas.drawCircle(centerX, centerY, circleRadius, circlePaint); //the actual circle
super.onDraw(canvas);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
centerX = w / 2;
centerY = h / 2;
viewRadius = Math.min(w,h) / 2;
ringWidth = Math.round((float) viewRadius * ringWidthRatio);
circleRadius = viewRadius - ringWidth;
ringPaint.setStrokeWidth(ringWidth);
ringPaint.setAlpha(ringAlpha);
ringRadius = circleRadius - ringWidth/2;
}
public float getCurrentRingWidth() {
return currentRingWidth;
}
public void setCurrentRingWidth(float currentRingWidth) {
this.currentRingWidth = currentRingWidth;
this.invalidate();
}
/**
* sets the color of the circle
* @param color the actual color to set to
*/
public void setColor(int color) {
circlePaint.setColor(color);
ringPaint.setColor(color);
ringPaint.setAlpha(ringAlpha);
this.invalidate();
}
/**
* whether to show the ring or not
* @param show set flag
*/
public void showRing(boolean show){
if (progressVisible == show) {
return;
}
progressVisible = show;
if(show){
ringAnimator.setFloatValues(currentRingWidth, ringWidth);
}else{
ringAnimator.setFloatValues(ringWidth, 0f);
}
ringAnimator.start();
}
/**
* this animates between the icon set in the imageview and the completed icon. does as crossfade animation
* @param show set flag
* @param hideOnComplete if true animate outside ring out after progress complete
*/
public void showCompleted(boolean show, boolean hideOnComplete){
if(show){
crossfader.startTransition(500);
}
if (hideOnComplete) {
ObjectAnimator hideAnimator = ObjectAnimator.ofFloat(this, "currentRingWidth", 0f, 0f);
hideAnimator.setFloatValues(1);
hideAnimator.setDuration(animationDuration);
hideAnimator.start();
}
}
}