Ripple系列的使用

首先写出新建一个OnRippleCompleteListener接口类

package com.example.ripplebutton.weight;
import android.view.View;
/**
 * Defines a callback called at the end of the Ripple effect
 */
public interface OnRippleCompleteListener {
void onComplete(View v);
}

然后写出剩下的几个类

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Robin Chutaux
 *
 * 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 com.example.ripplebutton.weight;
import com.example.ripplebuttondemo1.R;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.Handler;
import android.support.annotation.ColorRes;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.widget.AdapterView;
import android.widget.LinearLayout;
/**
 * RippleView custom layout
 *
 * Custom Layout that allows to use Ripple UI pattern above API 21
 *
 * @author Chutaux Robin
 * @version 2015.0512
 */
public class LinearRippleView extends LinearLayout {
private static final int TOUCH = 1;
private static final int INTERCEPT_TOUCH = 2;
private int WIDTH;
private int HEIGHT;
private int frameRate = 10;
private int rippleDuration = 100;
private int rippleAlpha = 90;
private Handler canvasHandler;
private float radiusMax = 0;
private boolean animationRunning = false;
private int timer = 0;
private int timerEmpty = 0;
private int durationEmpty = -1;
private float x = -1;
private float y = -1;
private int zoomDuration;
private float zoomScale;
private ScaleAnimation scaleAnimation;
private Boolean hasToZoom;
private Boolean isCentered;
private Integer rippleType;
private Paint paint;
private Bitmap originBitmap;
private int rippleColor;
private int ripplePadding;
private GestureDetector gestureDetector;
private final Runnable runnable = new Runnable() {
@Override
public void run() {
invalidate();
}
};
private OnRippleCompleteListener onCompletionListener;
private int touchFlag;
private boolean interceptTouchEnable = true;
public LinearRippleView(Context context) {
super(context);
initWithoutAttrib(context);
}
public LinearRippleView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
@SuppressLint("NewApi")
public LinearRippleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void initWithoutAttrib(Context context) {
//这就是修改点击效果的的颜色的地方只需去修改R.color.rippleColor就能修改点击效果的颜色
rippleColor = getResources().getColor(R.color.rippelColor);
rippleType = 0;
hasToZoom = false;
isCentered = false;
ripplePadding = 0;
canvasHandler = new Handler();
zoomScale = 1.03f;
zoomDuration = 200;
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(rippleColor);
paint.setAlpha(rippleAlpha);
this.setWillNotDraw(false);
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent event) {
super.onLongPress(event);
animateRipple(event);
sendClickEvent(true);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
this.setDrawingCacheEnabled(true);
this.setClickable(true);
}
/**
 * Method that initializes all fields and sets listeners
 *
 * @param context
 *            Context used to create this view
 * @param attrs
 *            Attribute used to initialize fields
 */
private void init(final Context context, final AttributeSet attrs) {
if (isInEditMode())
return;
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RippleView);
rippleColor = typedArray.getColor(R.styleable.RippleView_rv_color,
getResources().getColor(R.color.rippelColor));
rippleType = typedArray.getInt(R.styleable.RippleView_rv_type, 0);
hasToZoom = typedArray.getBoolean(R.styleable.RippleView_rv_zoom, false);
isCentered = typedArray.getBoolean(R.styleable.RippleView_rv_centered, false);
rippleDuration = typedArray.getInteger(R.styleable.RippleView_rv_rippleDuration, rippleDuration);
frameRate = typedArray.getInteger(R.styleable.RippleView_rv_framerate, frameRate);
rippleAlpha = typedArray.getInteger(R.styleable.RippleView_rv_alpha, rippleAlpha);
ripplePadding = typedArray.getDimensionPixelSize(R.styleable.RippleView_rv_ripplePadding, 0);
canvasHandler = new Handler();
zoomScale = typedArray.getFloat(R.styleable.RippleView_rv_zoomScale, 1.03f);
zoomDuration = typedArray.getInt(R.styleable.RippleView_rv_zoomDuration, 200);
typedArray.recycle();
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(rippleColor);
paint.setAlpha(rippleAlpha);
this.setWillNotDraw(false);
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent event) {
super.onLongPress(event);
animateRipple(event);
sendClickEvent(true);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
this.setDrawingCacheEnabled(true);
this.setClickable(true);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
try {
if (animationRunning && isEnabled()) {
if (rippleDuration <= timer * frameRate) {
animationRunning = false;
timer = 0;
durationEmpty = -1;
timerEmpty = 0;
canvas.restore();
invalidate();
if (onCompletionListener != null && isEnabled() && touchEnable()) {
onCompletionListener.onComplete(this);
}
return;
} else
canvasHandler.postDelayed(runnable, frameRate);
if (timer == 0)
canvas.save();
canvas.drawCircle(x, y, (radiusMax * (((float) timer * frameRate) / rippleDuration)), paint);
// paint.setColor(Color.parseColor("#ffff4444"));
if (rippleType == 1 && originBitmap != null && (((float) timer * frameRate) / rippleDuration) > 0.4f) {
if (durationEmpty == -1)
durationEmpty = rippleDuration - timer * frameRate;
timerEmpty++;
final Bitmap tmpBitmap = getCircleBitmap(
(int) ((radiusMax) * (((float) timerEmpty * frameRate) / (durationEmpty))));
canvas.drawBitmap(tmpBitmap, 0, 0, paint);
tmpBitmap.recycle();
}
paint.setColor(rippleColor);
if (rippleType == 1) {
if ((((float) timer * frameRate) / rippleDuration) > 0.6f)
paint.setAlpha((int) (rippleAlpha
- ((rippleAlpha) * (((float) timerEmpty * frameRate) / (durationEmpty)))));
else
paint.setAlpha(rippleAlpha);
} else
paint.setAlpha(
(int) (rippleAlpha - ((rippleAlpha) * (((float) timer * frameRate) / rippleDuration))));
timer++;
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void setInterceptTouchEnable(boolean interceptTouchEnable) {
this.interceptTouchEnable = interceptTouchEnable;
}
private boolean touchEnable() {
return touchFlag == TOUCH || interceptTouchEnable;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
WIDTH = w;
HEIGHT = h;
scaleAnimation = new ScaleAnimation(1.0f, zoomScale, 1.0f, zoomScale, w / 2, h / 2);
scaleAnimation.setDuration(zoomDuration);
scaleAnimation.setRepeatMode(Animation.REVERSE);
scaleAnimation.setRepeatCount(1);
}
/**
 * Launch Ripple animation for the current view with a MotionEvent
 *
 * @param event
 *            MotionEvent registered by the Ripple gesture listener
 */
public void animateRipple(MotionEvent event) {
createAnimation(event.getX(), event.getY());
}
/**
 * Launch Ripple animation for the current view centered at x and y position
 *
 * @param x
 *            Horizontal position of the ripple center
 * @param y
 *            Vertical position of the ripple center
 */
public void animateRipple(final float x, final float y) {
createAnimation(x, y);
}
/**
 * Create Ripple animation centered at x, y
 *
 * @param x
 *            Horizontal position of the ripple center
 * @param y
 *            Vertical position of the ripple center
 */
private void createAnimation(final float x, final float y) {
if (this.isEnabled() && !animationRunning) {
if (hasToZoom)
this.startAnimation(scaleAnimation);
radiusMax = Math.max(WIDTH, HEIGHT);
if (rippleType != 2)
radiusMax /= 2;
radiusMax -= ripplePadding;
if (isCentered || rippleType == 1) {
this.x = getMeasuredWidth() / 2;
this.y = getMeasuredHeight() / 2;
} else {
this.x = x;
this.y = y;
}
animationRunning = true;
if (rippleType == 1 && originBitmap == null)
originBitmap = getDrawingCache(true);
invalidate();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
touchFlag = TOUCH;
animateRipple(event);
sendClickEvent(false);
}
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (interceptTouchEnable) {
onTouchEvent(event);
touchFlag = INTERCEPT_TOUCH;
}
return super.onInterceptTouchEvent(event);
}
/**
 * Send a click event if parent view is a Listview instance
 *
 * @param isLongClick
 *            Is the event a long click ?
 */
private void sendClickEvent(final Boolean isLongClick) {
if (getParent() instanceof AdapterView) {
final AdapterView adapterView = (AdapterView) getParent();
final int position = adapterView.getPositionForView(this);
final long id = adapterView.getItemIdAtPosition(position);
if (isLongClick) {
if (adapterView.getOnItemLongClickListener() != null)
adapterView.getOnItemLongClickListener().onItemLongClick(adapterView, this, position, id);
} else {
if (adapterView.getOnItemClickListener() != null)
adapterView.getOnItemClickListener().onItemClick(adapterView, this, position, id);
}
}
}
private Bitmap getCircleBitmap(final int radius) {
if (originBitmap.isRecycled()) {
originBitmap = getDrawingCache();
}
final Bitmap output = Bitmap.createBitmap(originBitmap.getWidth(), originBitmap.getHeight(),
Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final Rect rect = new Rect((int) (x - radius), (int) (y - radius), (int) (x + radius), (int) (y + radius));
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawCircle(x, y, radius, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(originBitmap, rect, rect, paint);
return output;
}
/**
 * Set Ripple color, default is #FFFFFF
 *
 * @param rippleColor
 *            New color resource
 */
@ColorRes
public void setRippleColor(int rippleColor) {
this.rippleColor = getResources().getColor(rippleColor);
}
public int getRippleColor() {
return rippleColor;
}
public RippleType getRippleType() {
return RippleType.values()[rippleType];
}
/**
 * Set Ripple type, default is RippleType.SIMPLE
 *
 * @param rippleType
 *            New Ripple type for next animation
 */
public void setRippleType(final RippleType rippleType) {
this.rippleType = rippleType.ordinal();
}
public Boolean isCentered() {
return isCentered;
}
/**
 * Set if ripple animation has to be centered in its parent view or not,
 * default is False
 *
 * @param isCentered
 */
public void setCentered(final Boolean isCentered) {
this.isCentered = isCentered;
}
public int getRipplePadding() {
return ripplePadding;
}
/**
 * Set Ripple padding if you want to avoid some graphic glitch
 *
 * @param ripplePadding
 *            New Ripple padding in pixel, default is 0px
 */
public void setRipplePadding(int ripplePadding) {
this.ripplePadding = ripplePadding;
}
public Boolean isZooming() {
return hasToZoom;
}
/**
 * At the end of Ripple effect, the child views has to zoom
 *
 * @param hasToZoom
 *            Do the child views have to zoom ? default is False
 */
public void setZooming(Boolean hasToZoom) {
this.hasToZoom = hasToZoom;
}
public float getZoomScale() {
return zoomScale;
}
/**
 * Scale of the end animation
 *
 * @param zoomScale
 *            Value of scale animation, default is 1.03f
 */
public void setZoomScale(float zoomScale) {
this.zoomScale = zoomScale;
}
public int getZoomDuration() {
return zoomDuration;
}
/**
 * Duration of the ending animation in ms
 *
 * @param zoomDuration
 *            Duration, default is 200ms
 */
public void setZoomDuration(int zoomDuration) {
this.zoomDuration = zoomDuration;
}
public int getRippleDuration() {
return rippleDuration;
}
/**
 * Duration of the Ripple animation in ms
 *
 * @param rippleDuration
 *            Duration, default is 400ms
 */
public void setRippleDuration(int rippleDuration) {
this.rippleDuration = rippleDuration;
}
public int getFrameRate() {
return frameRate;
}
/**
 * Set framerate for Ripple animation
 *
 * @param frameRate
 *            New framerate value, default is 10
 */
public void setFrameRate(int frameRate) {
this.frameRate = frameRate;
}
public int getRippleAlpha() {
return rippleAlpha;
}
/**
 * Set alpha for ripple effect color
 *
 * @param rippleAlpha
 *            Alpha value between 0 and 255, default is 90
 */
public void setRippleAlpha(int rippleAlpha) {
this.rippleAlpha = rippleAlpha;
}
public void setOnRippleCompleteListener(OnRippleCompleteListener listener) {
this.onCompletionListener = listener;
}
public enum RippleType {
SIMPLE(0), DOUBLE(1), RECTANGLE(2);
int type;
RippleType(int type) {
this.type = type;
}
}
}
package com.example.ripplebutton.weight;
import com.example.ripplebuttondemo1.R;
import com.nineoldandroids.view.ViewHelper;
import com.nineoldandroids.view.ViewPropertyAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.support.annotation.ColorRes;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
import android.view.animation.ScaleAnimation;
import android.widget.AdapterView;
import android.widget.Button;
public class RippleButton extends Button {
private int WIDTH;
private int HEIGHT;
private int frameRate = 10;
private int rippleDuration = 100;
private int rippleAlpha = 90;
private Handler canvasHandler;
private float radiusMax = 0;
private boolean animationRunning = false;
private int timer = 0;
private int timerEmpty = 0;
private int durationEmpty = -1;
private float x = -1;
private float y = -1;
private int zoomDuration;
private float zoomScale;
private ScaleAnimation scaleAnimation;
private Boolean hasToZoom;
private Boolean isCentered;
private Integer rippleType;
private Paint paint;
private Bitmap originBitmap;
private int rippleColor;
private int ripplePadding;
private GestureDetector gestureDetector;
private final Runnable runnable = new Runnable() {
@Override
public void run() {
invalidate();
}
};
private OnRippleCompleteListener onCompletionListener;
public RippleButton(Context context) {
super(context);
initWithoutAttrib(context);
}
public RippleButton(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public RippleButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void initWithoutAttrib(Context context) {
rippleColor = getResources().getColor(R.color.rippelColor);
rippleType = 0;
hasToZoom = false;
isCentered = false;
ripplePadding = 0;
canvasHandler = new Handler();
zoomScale = 1.03f;
zoomDuration = 200;
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(rippleColor);
paint.setAlpha(rippleAlpha);
this.setWillNotDraw(false);
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent event) {
super.onLongPress(event);
animateRipple(event);
sendClickEvent(true);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
this.setDrawingCacheEnabled(true);
this.setClickable(true);
}
/**
 * Method that initializes all fields and sets listeners
 *
 * @param context
 *            Context used to create this view
 * @param attrs
 *            Attribute used to initialize fields
 */
private void init(final Context context, final AttributeSet attrs) {
if (isInEditMode())
return;
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RippleView);
rippleColor = typedArray.getColor(R.styleable.RippleView_rv_color,
getResources().getColor(R.color.rippelColor));
rippleType = typedArray.getInt(R.styleable.RippleView_rv_type, 0);
hasToZoom = typedArray.getBoolean(R.styleable.RippleView_rv_zoom, false);
isCentered = typedArray.getBoolean(R.styleable.RippleView_rv_centered, false);
rippleDuration = typedArray.getInteger(R.styleable.RippleView_rv_rippleDuration, rippleDuration);
frameRate = typedArray.getInteger(R.styleable.RippleView_rv_framerate, frameRate);
rippleAlpha = typedArray.getInteger(R.styleable.RippleView_rv_alpha, rippleAlpha);
ripplePadding = typedArray.getDimensionPixelSize(R.styleable.RippleView_rv_ripplePadding, 0);
canvasHandler = new Handler();
zoomScale = typedArray.getFloat(R.styleable.RippleView_rv_zoomScale, 1.03f);
zoomDuration = typedArray.getInt(R.styleable.RippleView_rv_zoomDuration, 200);
typedArray.recycle();
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(rippleColor);
paint.setAlpha(rippleAlpha);
this.setWillNotDraw(false);
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent event) {
super.onLongPress(event);
animateRipple(event);
sendClickEvent(true);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
this.setDrawingCacheEnabled(true);
this.setClickable(true);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
try {
if (animationRunning && isEnabled()) {
if (rippleDuration <= timer * frameRate) {
animationRunning = false;
timer = 0;
durationEmpty = -1;
timerEmpty = 0;
canvas.restore();
invalidate();
if (onCompletionListener != null && isEnabled())
onCompletionListener.onComplete(this);
return;
} else
canvasHandler.postDelayed(runnable, frameRate);
if (timer == 0)
canvas.save();
canvas.drawCircle(x, y, (radiusMax * (((float) timer * frameRate) / rippleDuration)), paint);
// paint.setColor(Color.parseColor("#ffff4444"));
if (rippleType == 1 && originBitmap != null && (((float) timer * frameRate) / rippleDuration) > 0.4f) {
if (durationEmpty == -1)
durationEmpty = rippleDuration - timer * frameRate;
timerEmpty++;
final Bitmap tmpBitmap = getCircleBitmap(
(int) ((radiusMax) * (((float) timerEmpty * frameRate) / (durationEmpty))));
canvas.drawBitmap(tmpBitmap, 0, 0, paint);
tmpBitmap.recycle();
}
paint.setColor(rippleColor);
if (rippleType == 1) {
if ((((float) timer * frameRate) / rippleDuration) > 0.6f)
paint.setAlpha((int) (rippleAlpha
- ((rippleAlpha) * (((float) timerEmpty * frameRate) / (durationEmpty)))));
else
paint.setAlpha(rippleAlpha);
} else
paint.setAlpha(
(int) (rippleAlpha - ((rippleAlpha) * (((float) timer * frameRate) / rippleDuration))));
timer++;
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
WIDTH = w;
HEIGHT = h;
scaleAnimation = new ScaleAnimation(1.0f, zoomScale, 1.0f, zoomScale, w / 2, h / 2);
scaleAnimation.setDuration(zoomDuration);
scaleAnimation.setRepeatMode(Animation.REVERSE);
scaleAnimation.setRepeatCount(1);
}
/**
 * Launch Ripple animation for the current view with a MotionEvent
 *
 * @param event
 *            MotionEvent registered by the Ripple gesture listener
 */
public void animateRipple(MotionEvent event) {
createAnimation(event.getX(), event.getY());
}
/**
 * Launch Ripple animation for the current view centered at x and y position
 *
 * @param x
 *            Horizontal position of the ripple center
 * @param y
 *            Vertical position of the ripple center
 */
public void animateRipple(final float x, final float y) {
createAnimation(x, y);
}
/**
 * Create Ripple animation centered at x, y
 *
 * @param x
 *            Horizontal position of the ripple center
 * @param y
 *            Vertical position of the ripple center
 */
private void createAnimation(final float x, final float y) {
if (this.isEnabled() && !animationRunning) {
if (hasToZoom)
this.startAnimation(scaleAnimation);
radiusMax = Math.max(WIDTH, HEIGHT);
if (rippleType != 2)
radiusMax /= 2;
radiusMax -= ripplePadding;
if (isCentered || rippleType == 1) {
this.x = getMeasuredWidth() / 2;
this.y = getMeasuredHeight() / 2;
} else {
this.x = x;
this.y = y;
}
animationRunning = true;
if (rippleType == 1 && originBitmap == null)
originBitmap = getDrawingCache(true);
invalidate();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
animateRipple(event);
sendClickEvent(false);
}
return super.onTouchEvent(event);
}
// @Override
// public boolean onInterceptTouchEvent(MotionEvent event) {
// this.onTouchEvent(event);
// return super.onInterceptTouchEvent(event);
// }
/**
 * Send a click event if parent view is a Listview instance
 *
 * @param isLongClick
 *            Is the event a long click ?
 */
private void sendClickEvent(final Boolean isLongClick) {
if (getParent() instanceof AdapterView) {
final AdapterView adapterView = (AdapterView) getParent();
final int position = adapterView.getPositionForView(this);
final long id = adapterView.getItemIdAtPosition(position);
if (isLongClick) {
if (adapterView.getOnItemLongClickListener() != null)
adapterView.getOnItemLongClickListener().onItemLongClick(adapterView, this, position, id);
} else {
if (adapterView.getOnItemClickListener() != null)
adapterView.getOnItemClickListener().onItemClick(adapterView, this, position, id);
}
}
}
private Bitmap getCircleBitmap(final int radius) {
if (originBitmap.isRecycled()) {
originBitmap = getDrawingCache();
}
final Bitmap output = Bitmap.createBitmap(originBitmap.getWidth(), originBitmap.getHeight(),
Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final Rect rect = new Rect((int) (x - radius), (int) (y - radius), (int) (x + radius), (int) (y + radius));
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawCircle(x, y, radius, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(originBitmap, rect, rect, paint);
return output;
}
/**
 * Set Ripple color, default is #FFFFFF
 *
 * @param rippleColor
 *            New color resource
 */
@ColorRes
public void setRippleColor(int rippleColor) {
this.rippleColor = getResources().getColor(rippleColor);
}
public int getRippleColor() {
return rippleColor;
}
public RippleType getRippleType() {
return RippleType.values()[rippleType];
}
/**
 * Set Ripple type, default is RippleType.SIMPLE
 *
 * @param rippleType
 *            New Ripple type for next animation
 */
public void setRippleType(final RippleType rippleType) {
this.rippleType = rippleType.ordinal();
}
public Boolean isCentered() {
return isCentered;
}
/**
 * Set if ripple animation has to be centered in its parent view or not,
 * default is False
 *
 * @param isCentered
 */
public void setCentered(final Boolean isCentered) {
this.isCentered = isCentered;
}
public int getRipplePadding() {
return ripplePadding;
}
/**
 * Set Ripple padding if you want to avoid some graphic glitch
 *
 * @param ripplePadding
 *            New Ripple padding in pixel, default is 0px
 */
public void setRipplePadding(int ripplePadding) {
this.ripplePadding = ripplePadding;
}
public Boolean isZooming() {
return hasToZoom;
}
/**
 * At the end of Ripple effect, the child views has to zoom
 *
 * @param hasToZoom
 *            Do the child views have to zoom ? default is False
 */
public void setZooming(Boolean hasToZoom) {
this.hasToZoom = hasToZoom;
}
public float getZoomScale() {
return zoomScale;
}
/**
 * Scale of the end animation
 *
 * @param zoomScale
 *            Value of scale animation, default is 1.03f
 */
public void setZoomScale(float zoomScale) {
this.zoomScale = zoomScale;
}
public int getZoomDuration() {
return zoomDuration;
}
/**
 * Duration of the ending animation in ms
 *
 * @param zoomDuration
 *            Duration, default is 200ms
 */
public void setZoomDuration(int zoomDuration) {
this.zoomDuration = zoomDuration;
}
public int getRippleDuration() {
return rippleDuration;
}
/**
 * Duration of the Ripple animation in ms
 *
 * @param rippleDuration
 *            Duration, default is 400ms
 */
public void setRippleDuration(int rippleDuration) {
this.rippleDuration = rippleDuration;
}
public int getFrameRate() {
return frameRate;
}
/**
 * Set framerate for Ripple animation
 *
 * @param frameRate
 *            New framerate value, default is 10
 */
public void setFrameRate(int frameRate) {
this.frameRate = frameRate;
}
public int getRippleAlpha() {
return rippleAlpha;
}
/**
 * Set alpha for ripple effect color
 *
 * @param rippleAlpha
 *            Alpha value between 0 and 255, default is 90
 */
public void setRippleAlpha(int rippleAlpha) {
this.rippleAlpha = rippleAlpha;
}
public void setOnRippleCompleteListener(OnRippleCompleteListener listener) {
this.onCompletionListener = listener;
}
public enum RippleType {
SIMPLE(0), DOUBLE(1), RECTANGLE(2);
int type;
RippleType(int type) {
this.type = type;
}
}
// -------------------------闅愯棌or鏄剧ず鍔ㄧ敾鐩稿叧浠g爜--------------------------
private boolean mVisible = true;
private static final int TRANSLATE_DURATION_MILLIS = 200;
private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
public void show(boolean animate) {
toggle(true, animate, false);
}
public void hide(boolean animate) {
toggle(false, animate, false);
}
private void toggle(final boolean visible, final boolean animate, boolean force) {
if (mVisible != visible || force) {
mVisible = visible;
int height = getHeight();
if (height == 0 && !force) {
ViewTreeObserver vto = getViewTreeObserver();
if (vto.isAlive()) {
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
ViewTreeObserver currentVto = getViewTreeObserver();
if (currentVto.isAlive()) {
currentVto.removeOnPreDrawListener(this);
}
toggle(visible, animate, true);
return true;
}
});
return;
}
}
int translationY = visible ? 0 : height + getMarginBottom();
if (animate) {
ViewPropertyAnimator.animate(this).setInterpolator(mInterpolator).setDuration(TRANSLATE_DURATION_MILLIS)
.translationY(translationY);
} else {
ViewHelper.setTranslationY(this, translationY);
}
// On pre-Honeycomb a translated view is still clickable, so we need
// to disable clicks manually
if (!hasHoneycombApi()) {
setClickable(visible);
}
}
}
private int getMarginBottom() {
int marginBottom = 0;
final ViewGroup.LayoutParams layoutParams = getLayoutParams();
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
marginBottom = ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin;
}
return marginBottom;
}
@SuppressLint("NewApi")
private boolean hasHoneycombApi() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
}
}
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Robin Chutaux
 *
 * 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 com.example.ripplebutton.weight;
import com.example.ripplebuttondemo1.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.Handler;
import android.support.annotation.ColorRes;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
/**
 * RippleView custom layout
 *
 * Custom Layout that allows to use Ripple UI pattern above API 21
 *
 * @author Chutaux Robin
 * @version 2015.0512
 */
public class RippleFrameLayout extends FrameLayout {
private int WIDTH;
private int HEIGHT;
private int frameRate = 10;
private int rippleDuration = 100;
private int rippleAlpha = 90;
private Handler canvasHandler;
private float radiusMax = 0;
private boolean animationRunning = false;
private int timer = 0;
private int timerEmpty = 0;
private int durationEmpty = -1;
private float x = -1;
private float y = -1;
private int zoomDuration;
private float zoomScale;
private ScaleAnimation scaleAnimation;
private Boolean hasToZoom;
private Boolean isCentered;
private Integer rippleType;
private Paint paint;
private Bitmap originBitmap;
private int rippleColor;
private int ripplePadding;
private GestureDetector gestureDetector;
private final Runnable runnable = new Runnable() {
@Override
public void run() {
invalidate();
}
};
private OnRippleCompleteListener onCompletionListener;
public RippleFrameLayout(Context context) {
super(context);
initWithoutAttrib(context);
}
public RippleFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public RippleFrameLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void initWithoutAttrib(Context context) {
rippleColor = getResources().getColor(R.color.rippelColor);
rippleType = 0;
hasToZoom = false;
isCentered = false;
ripplePadding = 0;
canvasHandler = new Handler();
zoomScale = 1.03f;
zoomDuration = 200;
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(rippleColor);
paint.setAlpha(rippleAlpha);
this.setWillNotDraw(false);
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent event) {
super.onLongPress(event);
animateRipple(event);
sendClickEvent(true);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
this.setDrawingCacheEnabled(true);
this.setClickable(true);
}
/**
 * Method that initializes all fields and sets listeners
 *
 * @param context
 *            Context used to create this view
 * @param attrs
 *            Attribute used to initialize fields
 */
private void init(final Context context, final AttributeSet attrs) {
if (isInEditMode())
return;
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RippleView);
rippleColor = typedArray.getColor(R.styleable.RippleView_rv_color,
getResources().getColor(R.color.rippelColor));
rippleType = typedArray.getInt(R.styleable.RippleView_rv_type, 0);
hasToZoom = typedArray.getBoolean(R.styleable.RippleView_rv_zoom, false);
isCentered = typedArray.getBoolean(R.styleable.RippleView_rv_centered, false);
rippleDuration = typedArray.getInteger(R.styleable.RippleView_rv_rippleDuration, rippleDuration);
frameRate = typedArray.getInteger(R.styleable.RippleView_rv_framerate, frameRate);
rippleAlpha = typedArray.getInteger(R.styleable.RippleView_rv_alpha, rippleAlpha);
ripplePadding = typedArray.getDimensionPixelSize(R.styleable.RippleView_rv_ripplePadding, 0);
canvasHandler = new Handler();
zoomScale = typedArray.getFloat(R.styleable.RippleView_rv_zoomScale, 1.03f);
zoomDuration = typedArray.getInt(R.styleable.RippleView_rv_zoomDuration, 200);
typedArray.recycle();
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(rippleColor);
paint.setAlpha(rippleAlpha);
this.setWillNotDraw(false);
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent event) {
super.onLongPress(event);
animateRipple(event);
sendClickEvent(true);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
this.setDrawingCacheEnabled(true);
this.setClickable(true);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
try {
if (animationRunning) {
if (rippleDuration <= timer * frameRate) {
animationRunning = false;
timer = 0;
durationEmpty = -1;
timerEmpty = 0;
canvas.restore();
invalidate();
if (onCompletionListener != null)
onCompletionListener.onComplete(this);
return;
} else
canvasHandler.postDelayed(runnable, frameRate);
if (timer == 0)
canvas.save();
canvas.drawCircle(x, y, (radiusMax * (((float) timer * frameRate) / rippleDuration)), paint);
// paint.setColor(Color.parseColor("#ffff4444"));
if (rippleType == 1 && originBitmap != null && (((float) timer * frameRate) / rippleDuration) > 0.4f) {
if (durationEmpty == -1)
durationEmpty = rippleDuration - timer * frameRate;
timerEmpty++;
final Bitmap tmpBitmap = getCircleBitmap(
(int) ((radiusMax) * (((float) timerEmpty * frameRate) / (durationEmpty))));
canvas.drawBitmap(tmpBitmap, 0, 0, paint);
tmpBitmap.recycle();
}
paint.setColor(rippleColor);
if (rippleType == 1) {
if ((((float) timer * frameRate) / rippleDuration) > 0.6f)
paint.setAlpha((int) (rippleAlpha
- ((rippleAlpha) * (((float) timerEmpty * frameRate) / (durationEmpty)))));
else
paint.setAlpha(rippleAlpha);
} else
paint.setAlpha(
(int) (rippleAlpha - ((rippleAlpha) * (((float) timer * frameRate) / rippleDuration))));
timer++;
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
WIDTH = w;
HEIGHT = h;
scaleAnimation = new ScaleAnimation(1.0f, zoomScale, 1.0f, zoomScale, w / 2, h / 2);
scaleAnimation.setDuration(zoomDuration);
scaleAnimation.setRepeatMode(Animation.REVERSE);
scaleAnimation.setRepeatCount(1);
}
/**
 * Launch Ripple animation for the current view with a MotionEvent
 *
 * @param event
 *            MotionEvent registered by the Ripple gesture listener
 */
public void animateRipple(MotionEvent event) {
createAnimation(event.getX(), event.getY());
}
/**
 * Launch Ripple animation for the current view centered at x and y position
 *
 * @param x
 *            Horizontal position of the ripple center
 * @param y
 *            Vertical position of the ripple center
 */
public void animateRipple(final float x, final float y) {
createAnimation(x, y);
}
/**
 * Create Ripple animation centered at x, y
 *
 * @param x
 *            Horizontal position of the ripple center
 * @param y
 *            Vertical position of the ripple center
 */
private void createAnimation(final float x, final float y) {
if (this.isEnabled() && !animationRunning) {
if (hasToZoom)
this.startAnimation(scaleAnimation);
radiusMax = Math.max(WIDTH, HEIGHT);
if (rippleType != 2)
radiusMax /= 2;
radiusMax -= ripplePadding;
if (isCentered || rippleType == 1) {
this.x = getMeasuredWidth() / 2;
this.y = getMeasuredHeight() / 2;
} else {
this.x = x;
this.y = y;
}
animationRunning = true;
if (rippleType == 1 && originBitmap == null)
originBitmap = getDrawingCache(true);
invalidate();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
animateRipple(event);
sendClickEvent(false);
}
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
this.onTouchEvent(event);
return super.onInterceptTouchEvent(event);
}
/**
 * Send a click event if parent view is a Listview instance
 *
 * @param isLongClick
 *            Is the event a long click ?
 */
private void sendClickEvent(final Boolean isLongClick) {
if (getParent() instanceof AdapterView) {
final AdapterView adapterView = (AdapterView) getParent();
final int position = adapterView.getPositionForView(this);
final long id = adapterView.getItemIdAtPosition(position);
if (isLongClick) {
if (adapterView.getOnItemLongClickListener() != null)
adapterView.getOnItemLongClickListener().onItemLongClick(adapterView, this, position, id);
} else {
if (adapterView.getOnItemClickListener() != null)
adapterView.getOnItemClickListener().onItemClick(adapterView, this, position, id);
}
}
}
private Bitmap getCircleBitmap(final int radius) {
if (originBitmap.isRecycled()) {
originBitmap = getDrawingCache();
}
final Bitmap output = Bitmap.createBitmap(originBitmap.getWidth(), originBitmap.getHeight(),
Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final Rect rect = new Rect((int) (x - radius), (int) (y - radius), (int) (x + radius), (int) (y + radius));
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawCircle(x, y, radius, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(originBitmap, rect, rect, paint);
return output;
}
/**
 * Set Ripple color, default is #FFFFFF
 *
 * @param rippleColor
 *            New color resource
 */
@ColorRes
public void setRippleColor(int rippleColor) {
this.rippleColor = getResources().getColor(rippleColor);
}
public int getRippleColor() {
return rippleColor;
}
public RippleType getRippleType() {
return RippleType.values()[rippleType];
}
/**
 * Set Ripple type, default is RippleType.SIMPLE
 *
 * @param rippleType
 *            New Ripple type for next animation
 */
public void setRippleType(final RippleType rippleType) {
this.rippleType = rippleType.ordinal();
}
public Boolean isCentered() {
return isCentered;
}
/**
 * Set if ripple animation has to be centered in its parent view or not,
 * default is False
 *
 * @param isCentered
 */
public void setCentered(final Boolean isCentered) {
this.isCentered = isCentered;
}
public int getRipplePadding() {
return ripplePadding;
}
/**
 * Set Ripple padding if you want to avoid some graphic glitch
 *
 * @param ripplePadding
 *            New Ripple padding in pixel, default is 0px
 */
public void setRipplePadding(int ripplePadding) {
this.ripplePadding = ripplePadding;
}
public Boolean isZooming() {
return hasToZoom;
}
/**
 * At the end of Ripple effect, the child views has to zoom
 *
 * @param hasToZoom
 *            Do the child views have to zoom ? default is False
 */
public void setZooming(Boolean hasToZoom) {
this.hasToZoom = hasToZoom;
}
public float getZoomScale() {
return zoomScale;
}
/**
 * Scale of the end animation
 *
 * @param zoomScale
 *            Value of scale animation, default is 1.03f
 */
public void setZoomScale(float zoomScale) {
this.zoomScale = zoomScale;
}
public int getZoomDuration() {
return zoomDuration;
}
/**
 * Duration of the ending animation in ms
 *
 * @param zoomDuration
 *            Duration, default is 200ms
 */
public void setZoomDuration(int zoomDuration) {
this.zoomDuration = zoomDuration;
}
public int getRippleDuration() {
return rippleDuration;
}
/**
 * Duration of the Ripple animation in ms
 *
 * @param rippleDuration
 *            Duration, default is 400ms
 */
public void setRippleDuration(int rippleDuration) {
this.rippleDuration = rippleDuration;
}
public int getFrameRate() {
return frameRate;
}
/**
 * Set framerate for Ripple animation
 *
 * @param frameRate
 *            New framerate value, default is 10
 */
public void setFrameRate(int frameRate) {
this.frameRate = frameRate;
}
public int getRippleAlpha() {
return rippleAlpha;
}
/**
 * Set alpha for ripple effect color
 *
 * @param rippleAlpha
 *            Alpha value between 0 and 255, default is 90
 */
public void setRippleAlpha(int rippleAlpha) {
this.rippleAlpha = rippleAlpha;
}
public void setOnRippleCompleteListener(OnRippleCompleteListener listener) {
this.onCompletionListener = listener;
}
public enum RippleType {
SIMPLE(0), DOUBLE(1), RECTANGLE(2);
int type;
RippleType(int type) {
this.type = type;
}
}
}
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Robin Chutaux
 *
 * 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 com.example.ripplebutton.weight;
import com.example.ripplebuttondemo1.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.Handler;
import android.support.annotation.ColorRes;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.widget.AdapterView;
import android.widget.RelativeLayout;
/**
 * RippleView custom layout
 *
 * Custom Layout that allows to use Ripple UI pattern above API 21
 *
 * @author Chutaux Robin
 * @version 2015.0512
 */
public class RippleView extends RelativeLayout {
private int WIDTH;
private int HEIGHT;
private int frameRate = 10;
private int rippleDuration = 100;
private int rippleAlpha = 90;
private Handler canvasHandler;
private float radiusMax = 0;
private boolean animationRunning = false;
private int timer = 0;
private int timerEmpty = 0;
private int durationEmpty = -1;
private float x = -1;
private float y = -1;
private int zoomDuration;
private float zoomScale;
private ScaleAnimation scaleAnimation;
private Boolean hasToZoom;
private Boolean isCentered;
private Integer rippleType;
private Paint paint;
private Bitmap originBitmap;
private int rippleColor;
private int ripplePadding;
private GestureDetector gestureDetector;
private final Runnable runnable = new Runnable() {
@Override
public void run() {
invalidate();
}
};
private OnRippleCompleteListener onCompletionListener;
public RippleView(Context context) {
super(context);
initWithoutAttrib(context);
}
public RippleView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public RippleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void initWithoutAttrib(Context context) {
rippleColor = getResources().getColor(R.color.rippelColor);
rippleType = 0;
hasToZoom = false;
isCentered = false;
ripplePadding = 0;
canvasHandler = new Handler();
zoomScale = 1.03f;
zoomDuration = 200;
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(rippleColor);
paint.setAlpha(rippleAlpha);
this.setWillNotDraw(false);
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent event) {
super.onLongPress(event);
animateRipple(event);
sendClickEvent(true);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
this.setDrawingCacheEnabled(true);
this.setClickable(true);
}
/**
 * Method that initializes all fields and sets listeners
 *
 * @param context
 *            Context used to create this view
 * @param attrs
 *            Attribute used to initialize fields
 */
private void init(final Context context, final AttributeSet attrs) {
if (isInEditMode())
return;
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RippleView);
rippleColor = typedArray.getColor(R.styleable.RippleView_rv_color,
getResources().getColor(R.color.rippelColor));
rippleType = typedArray.getInt(R.styleable.RippleView_rv_type, 0);
hasToZoom = typedArray.getBoolean(R.styleable.RippleView_rv_zoom, false);
isCentered = typedArray.getBoolean(R.styleable.RippleView_rv_centered, false);
rippleDuration = typedArray.getInteger(R.styleable.RippleView_rv_rippleDuration, rippleDuration);
frameRate = typedArray.getInteger(R.styleable.RippleView_rv_framerate, frameRate);
rippleAlpha = typedArray.getInteger(R.styleable.RippleView_rv_alpha, rippleAlpha);
ripplePadding = typedArray.getDimensionPixelSize(R.styleable.RippleView_rv_ripplePadding, 0);
canvasHandler = new Handler();
zoomScale = typedArray.getFloat(R.styleable.RippleView_rv_zoomScale, 1.03f);
zoomDuration = typedArray.getInt(R.styleable.RippleView_rv_zoomDuration, 200);
typedArray.recycle();
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(rippleColor);
paint.setAlpha(rippleAlpha);
this.setWillNotDraw(false);
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent event) {
super.onLongPress(event);
animateRipple(event);
sendClickEvent(true);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
this.setDrawingCacheEnabled(true);
this.setClickable(true);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
try {
if (animationRunning && isEnabled()) {
if (rippleDuration <= timer * frameRate) {
animationRunning = false;
timer = 0;
durationEmpty = -1;
timerEmpty = 0;
canvas.restore();
invalidate();
if (onCompletionListener != null && isEnabled())
onCompletionListener.onComplete(this);
return;
} else
canvasHandler.postDelayed(runnable, frameRate);
if (timer == 0)
canvas.save();
canvas.drawCircle(x, y, (radiusMax * (((float) timer * frameRate) / rippleDuration)), paint);
// paint.setColor(Color.parseColor("#ffff4444"));
if (rippleType == 1 && originBitmap != null && (((float) timer * frameRate) / rippleDuration) > 0.4f) {
if (durationEmpty == -1)
durationEmpty = rippleDuration - timer * frameRate;
timerEmpty++;
final Bitmap tmpBitmap = getCircleBitmap(
(int) ((radiusMax) * (((float) timerEmpty * frameRate) / (durationEmpty))));
canvas.drawBitmap(tmpBitmap, 0, 0, paint);
tmpBitmap.recycle();
}
paint.setColor(rippleColor);
if (rippleType == 1) {
if ((((float) timer * frameRate) / rippleDuration) > 0.6f)
paint.setAlpha((int) (rippleAlpha
- ((rippleAlpha) * (((float) timerEmpty * frameRate) / (durationEmpty)))));
else
paint.setAlpha(rippleAlpha);
} else
paint.setAlpha(
(int) (rippleAlpha - ((rippleAlpha) * (((float) timer * frameRate) / rippleDuration))));
timer++;
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
WIDTH = w;
HEIGHT = h;
scaleAnimation = new ScaleAnimation(1.0f, zoomScale, 1.0f, zoomScale, w / 2, h / 2);
scaleAnimation.setDuration(zoomDuration);
scaleAnimation.setRepeatMode(Animation.REVERSE);
scaleAnimation.setRepeatCount(1);
}
/**
 * Launch Ripple animation for the current view with a MotionEvent
 *
 * @param event
 *            MotionEvent registered by the Ripple gesture listener
 */
public void animateRipple(MotionEvent event) {
createAnimation(event.getX(), event.getY());
}
/**
 * Launch Ripple animation for the current view centered at x and y position
 *
 * @param x
 *            Horizontal position of the ripple center
 * @param y
 *            Vertical position of the ripple center
 */
public void animateRipple(final float x, final float y) {
createAnimation(x, y);
}
/**
 * Create Ripple animation centered at x, y
 *
 * @param x
 *            Horizontal position of the ripple center
 * @param y
 *            Vertical position of the ripple center
 */
private void createAnimation(final float x, final float y) {
if (this.isEnabled() && !animationRunning) {
if (hasToZoom)
this.startAnimation(scaleAnimation);
radiusMax = Math.max(WIDTH, HEIGHT);
if (rippleType != 2)
radiusMax /= 2;
radiusMax -= ripplePadding;
if (isCentered || rippleType == 1) {
this.x = getMeasuredWidth() / 2;
this.y = getMeasuredHeight() / 2;
} else {
this.x = x;
this.y = y;
}
animationRunning = true;
if (rippleType == 1 && originBitmap == null)
originBitmap = getDrawingCache(true);
invalidate();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
animateRipple(event);
sendClickEvent(false);
}
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
this.onTouchEvent(event);
return super.onInterceptTouchEvent(event);
}
/**
 * Send a click event if parent view is a Listview instance
 *
 * @param isLongClick
 *            Is the event a long click ?
 */
private void sendClickEvent(final Boolean isLongClick) {
if (getParent() instanceof AdapterView) {
final AdapterView adapterView = (AdapterView) getParent();
final int position = adapterView.getPositionForView(this);
final long id = adapterView.getItemIdAtPosition(position);
if (isLongClick) {
if (adapterView.getOnItemLongClickListener() != null)
adapterView.getOnItemLongClickListener().onItemLongClick(adapterView, this, position, id);
} else {
if (adapterView.getOnItemClickListener() != null)
adapterView.getOnItemClickListener().onItemClick(adapterView, this, position, id);
}
}
}
private Bitmap getCircleBitmap(final int radius) {
if (originBitmap.isRecycled()) {
originBitmap = getDrawingCache();
}
final Bitmap output = Bitmap.createBitmap(originBitmap.getWidth(), originBitmap.getHeight(),
Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final Rect rect = new Rect((int) (x - radius), (int) (y - radius), (int) (x + radius), (int) (y + radius));
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawCircle(x, y, radius, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(originBitmap, rect, rect, paint);
return output;
}
/**
 * Set Ripple color, default is #FFFFFF
 *
 * @param rippleColor
 *            New color resource
 */
@ColorRes
public void setRippleColor(int rippleColor) {
this.rippleColor = getResources().getColor(rippleColor);
}
public int getRippleColor() {
return rippleColor;
}
public RippleType getRippleType() {
return RippleType.values()[rippleType];
}
/**
 * Set Ripple type, default is RippleType.SIMPLE
 *
 * @param rippleType
 *            New Ripple type for next animation
 */
public void setRippleType(final RippleType rippleType) {
this.rippleType = rippleType.ordinal();
}
public Boolean isCentered() {
return isCentered;
}
/**
 * Set if ripple animation has to be centered in its parent view or not,
 * default is False
 *
 * @param isCentered
 */
public void setCentered(final Boolean isCentered) {
this.isCentered = isCentered;
}
public int getRipplePadding() {
return ripplePadding;
}
/**
 * Set Ripple padding if you want to avoid some graphic glitch
 *
 * @param ripplePadding
 *            New Ripple padding in pixel, default is 0px
 */
public void setRipplePadding(int ripplePadding) {
this.ripplePadding = ripplePadding;
}
public Boolean isZooming() {
return hasToZoom;
}
/**
 * At the end of Ripple effect, the child views has to zoom
 *
 * @param hasToZoom
 *            Do the child views have to zoom ? default is False
 */
public void setZooming(Boolean hasToZoom) {
this.hasToZoom = hasToZoom;
}
public float getZoomScale() {
return zoomScale;
}
/**
 * Scale of the end animation
 *
 * @param zoomScale
 *            Value of scale animation, default is 1.03f
 */
public void setZoomScale(float zoomScale) {
this.zoomScale = zoomScale;
}
public int getZoomDuration() {
return zoomDuration;
}
/**
 * Duration of the ending animation in ms
 *
 * @param zoomDuration
 *            Duration, default is 200ms
 */
public void setZoomDuration(int zoomDuration) {
this.zoomDuration = zoomDuration;
}
public int getRippleDuration() {
return rippleDuration;
}
/**
 * Duration of the Ripple animation in ms
 *
 * @param rippleDuration
 *            Duration, default is 400ms
 */
public void setRippleDuration(int rippleDuration) {
this.rippleDuration = rippleDuration;
}
public int getFrameRate() {
return frameRate;
}
/**
 * Set framerate for Ripple animation
 *
 * @param frameRate
 *            New framerate value, default is 10
 */
public void setFrameRate(int frameRate) {
this.frameRate = frameRate;
}
public int getRippleAlpha() {
return rippleAlpha;
}
/**
 * Set alpha for ripple effect color
 *
 * @param rippleAlpha
 *            Alpha value between 0 and 255, default is 90
 */
public void setRippleAlpha(int rippleAlpha) {
this.rippleAlpha = rippleAlpha;
}
public void setOnRippleCompleteListener(OnRippleCompleteListener listener) {
this.onCompletionListener = listener;
}
public enum RippleType {
SIMPLE(0), DOUBLE(1), RECTANGLE(2);
int type;
RippleType(int type) {
this.type = type;
}
}
}

然后使用的时候就很简单了

只需要在values中加入ripple_attrs.xml和ripple_colors.xml

<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ The MIT License (MIT)
  ~
  ~ Copyright (c) 2014 Robin Chutaux
  ~
  ~ 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.
  -->
<resources>
    <declare-styleable name="RippleView">
        <attr name="rv_alpha" format="integer" />
        <attr name="rv_framerate" format="integer"/>
        <attr name="rv_rippleDuration" format="integer"/>
        <attr name="rv_zoomDuration" format="integer" />
        <attr name="rv_color" format="color" />
        <attr name="rv_centered" format="boolean" />
        <attr name="rv_type" format="enum">
            <enum name="simpleRipple" value="0"/>
            <enum name="doubleRipple" value="1"/>
            <enum name="rectangle" value="2" />
        </attr>
        <attr name="rv_ripplePadding" format="dimension" />
        <attr name="rv_zoom" format="boolean" />
        <attr name="rv_zoomScale" format="float" />
    </declare-styleable>
</resources>

  

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="rippelColor">#9AC0CD</color>
</resources>
<com.example.ripplebutton.weight.LinearRippleView
        android:id="@+id/zhifubao_layout"
        android:layout_width="fill_parent"
        android:layout_height="88dp"
        android:background="#FF7256"
        android:gravity="center_vertical"
        android:paddingLeft="30dp"
        android:paddingRight="30dp" >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:text="adsfasd" />
    </com.example.ripplebutton.weight.LinearRippleView>

或者

   

<com.example.ripplebutton.weight.RippleButton
        android:id="@+id/evaluate_btn"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginRight="@dimen/w29"
        android:background="@drawable/ripple_button_background"
        android:paddingBottom="@dimen/h10"
        android:paddingLeft="@dimen/w30"
        android:paddingRight="@dimen/w30"
        android:paddingTop="@dimen/h10"
        android:text="评价"
        android:textColor="@color/blue_75caff"
        android:textSize="@dimen/w26" />

上面两个就是直接使用了

你可能感兴趣的:(Ripple系列的使用)