效果图
思路
显示图片,所以继承ImageView。
图片有两种状态:一种是正常状态,一种是有较暗状态。较暗状态是将按一定的透明度比例绘制正常的图片即可。
代码
public class DakaImageView extends ImageView {
/**
* 从上往下刷新
*/
private static final int ANIM_UP_DOWN = 1;
/**
* 波纹扩散
*/
private static final int ANIM_SPREAD = 2;
/**
* 没有动画执行
*/
private static final int ANIM_NO = 3;
private static final String TAG = DakaImageView.class.getSimpleName();
private int mCurrAnim = ANIM_NO;
private ValueAnimator mUpdownAnim, mSpreadAnim;
private Paint mUpdownPaint;
private float mMaskOffsetY = 0, mMaskoffsetRadius = 0;
private Bitmap mUpdownMaskBitmap, mSpreadMaskBitmap, mUnmaskBitmap;
private static final float ALPHA = 0.3f;
private Path path;
public DakaImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
setLayerType(LAYER_TYPE_SOFTWARE, null);
mUpdownPaint = new Paint();
mUpdownPaint.setAntiAlias(true);
mUpdownPaint.setStyle(Paint.Style.FILL);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //设置死宽高
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.makeMeasureSpec(getDrawable().getIntrinsicWidth(),MeasureSpec.EXACTLY);
int height = MeasureSpec.makeMeasureSpec(getDrawable().getIntrinsicHeight(),MeasureSpec.EXACTLY);
setMeasuredDimension(width,height);
}
@Override
protected void onDetachedFromWindow() { //回收相应的资源文件
super.onDetachedFromWindow();
stopAndRecyclerAnim(mUpdownAnim);
stopAndRecyclerAnim(mSpreadAnim);
recyclerBitmap(mUpdownMaskBitmap);
recyclerBitmap(mUnmaskBitmap);
recyclerBitmap(mSpreadMaskBitmap);
mUpdownPaint = null;
}
private void recyclerBitmap(Bitmap bitmap) {
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
bitmap = null;
}
private void stopAndRecyclerAnim(ValueAnimator animator) {
if (animator != null && animator.isRunning()) {
animator.cancel();
}
animator = null;
if(mSpreadAnim == null && mUpdownAnim == null){//两个动画都停止后,恢复成正常状态
mCurrAnim = ANIM_NO;
invalidate();
}
}
@Override
protected void onDraw(Canvas canvas) {
switch (mCurrAnim) {
case ANIM_NO:
drawUnmask(canvas);
break;
case ANIM_UP_DOWN:
drawUpDown(canvas);
break;
case ANIM_SPREAD:
drawSpread(canvas);
break;
}
}
private void drawUnmask(Canvas canvas) {
Bitmap bitmap = getUnmaskBitmap();
canvas.drawBitmap(bitmap, 0, 0, null);
recyclerBitmap(mUpdownMaskBitmap);
recyclerBitmap(mSpreadMaskBitmap);
}
private void drawUpDown(Canvas canvas) {
//绘制底图
Bitmap unmaskBitmap = getUnmaskBitmap();
canvas.drawBitmap(unmaskBitmap, 0, 0, null);
//绘制阴影部分
Bitmap maskBitmap = getUpdownMaskBitmap();
mUpdownPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
int top = mMaskOffsetY - getHeight() < 0 ? 0 : (int) mMaskOffsetY - getHeight();
canvas.drawBitmap(maskBitmap, new Rect(0, top, unmaskBitmap.getWidth(), (int) mMaskOffsetY), new Rect(0, top, getWidth(), (int) mMaskOffsetY), mUpdownPaint);
mUpdownPaint.setXfermode(null);
}
/**
* 绘制水波纹
*/
private void drawSpread(Canvas canvas) {
//绘制水波纹
Bitmap maskBitmap = getSpreadMaskBitmap();
canvas.drawBitmap(maskBitmap, 0, 0, null);
}
private Bitmap getUnmaskBitmap() { //获取正常情况下的bitmap
if (mUnmaskBitmap != null)
return mUnmaskBitmap;
int width = getWidth();
int height = getHeight();
mUnmaskBitmap = createBitmapAndGcIfNecessary(width, height);
Canvas canvas = new Canvas(mUnmaskBitmap);
Drawable drawable = getDrawable();
drawable.setAlpha(255); //正常情况下,不透明。用drawable绘制到自己指定的canvas上,就会将drawable转换成bitmap
drawable.draw(canvas);
return mUnmaskBitmap;
}
private Bitmap getUpdownMaskBitmap() {
int width = getWidth();
int height = getHeight();
if (mUpdownMaskBitmap == null)
mUpdownMaskBitmap = createBitmapAndGcIfNecessary(width, height);
Canvas canvas = new Canvas(mUpdownMaskBitmap);
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
canvas.clipRect(0, 0, width, mMaskOffsetY); //截取一部分区域,这样在绘制半透明的区域时就会只绘制截取到的部分
Drawable drawable = getDrawable();
drawable.setAlpha((int) (255 * ALPHA));
drawable.draw(canvas);
return mUpdownMaskBitmap;
}
private Bitmap getSpreadMaskBitmap() {
int width = getWidth();
int height = getHeight();
if (mSpreadMaskBitmap == null) {
mSpreadMaskBitmap = createBitmapAndGcIfNecessary(width, height);
}
Canvas canvas = new Canvas(mSpreadMaskBitmap);
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
//画原来未变暗的图
Drawable drawable = getDrawable();
drawable.setAlpha(255);
drawable.draw(canvas);
int max = Math.max(getWidth(), getHeight()) / 2;
if(path == null) {
path = new Path();
}else{
path.reset();
}
if (mMaskoffsetRadius < max) { //从内往外扩散的是半透明的
path.addCircle(getWidth() / 2, getHeight() / 2, mMaskoffsetRadius, Path.Direction.CCW);
canvas.clipPath(path); //截取一个圆环,然后用drawable绘制,这样绘制的drawable就是一个半透明的的圆环了。
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
drawable.setAlpha((int) (255 * ALPHA));
drawable.draw(canvas);
} else {//从内往外扩散的是不透明的
//外圈阴影 。首先绘制外圈半透明的
path.reset();
path.addCircle(getWidth() / 2, getHeight() / 2, max, Path.Direction.CCW);
canvas.clipPath(path);
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
drawable.setAlpha((int) (255 * ALPHA));
drawable.draw(canvas);
//内圈正常
path.reset();
path.addCircle(getWidth() / 2, getHeight() / 2, mMaskoffsetRadius - max, Path.Direction.CCW);
canvas.clipPath(path);
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
drawable.setAlpha(255);
drawable.draw(canvas);
}
return mSpreadMaskBitmap;
}
private Bitmap createBitmapAndGcIfNecessary(int width, int height) {
try {
return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
} catch (OutOfMemoryError e) {
System.gc();
return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
}
}
//----------------------------------------提供出去的方法---------------------------------
public void startUpDown() {
mCurrAnim = ANIM_UP_DOWN;
stopAndRecyclerAnim(mSpreadAnim);
mUpdownAnim = ValueAnimator.ofInt(0, getHeight() * 2);
mUpdownAnim.setDuration(2000);
mUpdownAnim.setRepeatCount(ValueAnimator.INFINITE);
mUpdownAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mMaskOffsetY = (int) valueAnimator.getAnimatedValue();
invalidate();
}
});
mUpdownAnim.start();
}
public void stopUpDown() {
if (null != mUpdownAnim)
stopAndRecyclerAnim(mUpdownAnim);
}
public void startSpread() {
stopAndRecyclerAnim(mUpdownAnim);
mCurrAnim = ANIM_SPREAD;
int radius = Math.max(getWidth(), getHeight()) / 2;
mSpreadAnim = ValueAnimator.ofInt(0, radius * 2);
mSpreadAnim.setDuration(1000);
mSpreadAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mMaskoffsetRadius = (int) valueAnimator.getAnimatedValue();
invalidate();
}
});
mSpreadAnim.setRepeatCount(2);
mSpreadAnim.start();
}
public boolean isAnimming() {
return mCurrAnim == ANIM_SPREAD || mCurrAnim == ANIM_UP_DOWN;
}
}