0
点赞
收藏
分享

微信扫一扫

Android特效专辑(五)——自定义圆形头像和仿MIUI卸载动画—粒子爆炸

Android特效专辑(五)——自定义圆形头像和仿MIUI卸载动画—粒子爆炸

好的,各位亲爱的朋友,今天讲的特效还是比较炫的,首先,我们会讲一个自定义圆形的imageView,接着,我们会来实现粒子爆炸的特效,按照国际惯例,无图无真相的没这个效果也是模仿大神的,现在应用在了我的《Only》上

截图


好的,我们新建一个工程——AnimView,我们要用到的图片

Android特效专辑(五)——自定义圆形头像和仿MIUI卸载动画—粒子爆炸_动画

一.自定义圆形头像——

直接开写了,要实现的东西都在注释上了

1.编写自定义属性attr.xml













2.自定义View

紧接着我们就可以编写这个类了

package com.lgl.animview;

/**
* 圆形头像
* Created by LGL on 2016/1/12.
*/

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;

/**
* 圆形ImageView,可设置最多两个宽度不同且颜色不同的圆形边框。 设置颜色在xml布局文件中由自定义属性配置参数指定
*/

public class RoundImageView extends ImageView {

private int mBorderThickness = 0;

private Context mContext;

private int defaultColor = 0xFFFFFFFF;

// 如果只有其中一个有值,则只画一个圆形边框

private int mBorderOutsideColor = 0;

private int mBorderInsideColor = 0;

// 控件默认长、宽

private int defaultWidth = 0;

private int defaultHeight = 0;

public RoundImageView(Context context) {

super(context);

mContext = context;

}

public RoundImageView(Context context, AttributeSet attrs) {

super(context, attrs);

mContext = context;

setCustomAttributes(attrs);

}

public RoundImageView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

mContext = context;

setCustomAttributes(attrs);

}

private void setCustomAttributes(AttributeSet attrs) {

// 获取自定义的属性
TypedArray a = mContext.obtainStyledAttributes(attrs,
R.styleable.roundedimageview);

mBorderThickness = a.getDimensionPixelSize(
R.styleable.roundedimageview_border_thickness, 0);

mBorderOutsideColor = a
.getColor(R.styleable.roundedimageview_border_outside_color,
defaultColor);

mBorderInsideColor = a.getColor(
R.styleable.roundedimageview_border_inside_color, defaultColor);

}

@Override
protected void onDraw(Canvas canvas) {

Drawable drawable = getDrawable();

if (drawable == null) {

return;

}

if (getWidth() == 0 || getHeight() == 0) {

return;

}

this.measure(0, 0);

if (drawable.getClass() == NinePatchDrawable.class)

return;

Bitmap b = ((BitmapDrawable) drawable).getBitmap();

Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);

if (defaultWidth == 0) {

defaultWidth = getWidth();

}

if (defaultHeight == 0) {

defaultHeight = getHeight();

}

int radius = 0;

if (mBorderInsideColor != defaultColor
&& mBorderOutsideColor != defaultColor) {// 定义画两个边框,分别为外圆边框和内圆边框

radius = (defaultWidth < defaultHeight ? defaultWidth
: defaultHeight) / 2 - 2 * mBorderThickness;

// 画内圆

drawCircleBorder(canvas, radius + mBorderThickness / 2,
mBorderInsideColor);

// 画外圆

drawCircleBorder(canvas, radius + mBorderThickness
+ mBorderThickness / 2, mBorderOutsideColor);

} else if (mBorderInsideColor != defaultColor
&& mBorderOutsideColor == defaultColor) {// 定义画一个边框

radius = (defaultWidth < defaultHeight ? defaultWidth
: defaultHeight) / 2 - mBorderThickness;

drawCircleBorder(canvas, radius + mBorderThickness / 2,
mBorderInsideColor);

} else if (mBorderInsideColor == defaultColor
&& mBorderOutsideColor != defaultColor) {// 定义画一个边框

radius = (defaultWidth < defaultHeight ? defaultWidth
: defaultHeight) / 2 - mBorderThickness;

drawCircleBorder(canvas, radius + mBorderThickness / 2,
mBorderOutsideColor);

} else {// 没有边框

radius = (defaultWidth < defaultHeight ? defaultWidth
: defaultHeight) / 2;

}

Bitmap roundBitmap = getCroppedRoundBitmap(bitmap, radius);

canvas.drawBitmap(roundBitmap, defaultWidth / 2 - radius, defaultHeight
/ 2 - radius, null);

}

/**
* 获取裁剪后的圆形图片
*/

public Bitmap getCroppedRoundBitmap(Bitmap bmp, int radius) {

Bitmap scaledSrcBmp;

int diameter = radius * 2;

// 为了防止宽高不相等,造成圆形图片变形,因此截取长方形中处于中间位置最大的正方形图片

int bmpWidth = bmp.getWidth();

int bmpHeight = bmp.getHeight();

int squareWidth = 0, squareHeight = 0;

int x = 0, y = 0;

Bitmap squareBitmap;

if (bmpHeight > bmpWidth) {// 高大于宽

squareWidth = squareHeight = bmpWidth;

x = 0;

y = (bmpHeight - bmpWidth) / 2;

// 截取正方形图片

squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth,
squareHeight);

} else if (bmpHeight < bmpWidth) {// 宽大于高

squareWidth = squareHeight = bmpHeight;

x = (bmpWidth - bmpHeight) / 2;

y = 0;

squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth,
squareHeight);

} else {

squareBitmap = bmp;

}

if (squareBitmap.getWidth() != diameter
|| squareBitmap.getHeight() != diameter) {

scaledSrcBmp = Bitmap.createScaledBitmap(squareBitmap, diameter,
diameter, true);

} else {

scaledSrcBmp = squareBitmap;

}

Bitmap output = Bitmap.createBitmap(scaledSrcBmp.getWidth(),

scaledSrcBmp.getHeight(),

Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(output);

Paint paint = new Paint();

Rect rect = new Rect(0, 0, scaledSrcBmp.getWidth(),
scaledSrcBmp.getHeight());

paint.setAntiAlias(true);

paint.setFilterBitmap(true);

paint.setDither(true);

canvas.drawARGB(0, 0, 0, 0);

canvas.drawCircle(scaledSrcBmp.getWidth() / 2,

scaledSrcBmp.getHeight() / 2,

scaledSrcBmp.getWidth() / 2,

paint);

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

canvas.drawBitmap(scaledSrcBmp, rect, rect, paint);

bmp = null;

squareBitmap = null;

scaledSrcBmp = null;

return output;

}

/**
* 边缘画圆
*/

private void drawCircleBorder(Canvas canvas, int radius, int color) {

Paint paint = new Paint();

/* 去锯齿 */

paint.setAntiAlias(true);

paint.setFilterBitmap(true);

paint.setDither(true);

paint.setColor(color);

/* 设置paint的 style 为STROKE:空心 */

paint.setStyle(Paint.Style.STROKE);

/* 设置paint的外框宽度 */

paint.setStrokeWidth(mBorderThickness);

canvas.drawCircle(defaultWidth / 2, defaultHeight / 2, radius, paint);

}

}

3.引用

引用起来就比较简单了,我们首先来引入他的命名空间

 xmlns:imagecontrol="http://schemas.android.com/apk/res-auto"

然后我们直接写xml

          android:id="@+id/iv_round"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/photo"
imagecontrol:border_inside_color="#bc0978"
imagecontrol:border_outside_color="#ba3456"
imagecontrol:border_thickness="1dp" />

好的,让我们运行下吧

Android特效专辑(五)——自定义圆形头像和仿MIUI卸载动画—粒子爆炸_图片_02

二.MUI卸载动画——粒子爆炸

关于这个粒子特效,在开篇的时候已经展示了效果,那么我们接下来,要怎么做尼?

1.ParticleUtils

用于粒子动画的单位转换

package com.lgl.animview;

import android.content.res.Resources;

/**
* 粒子动画
*/
public class ParticleUtils {
/**
* 密度
*/
public static final float DENSITY = Resources.getSystem().getDisplayMetrics().density;

public static int dp2px(int dp) {
return Math.round(dp * DENSITY);
}
}

2.Particle

用于爆破效果的分子计算

package com.lgl.animview;

import java.util.Random;

import android.graphics.Point;
import android.graphics.Rect;

/**
* Created by lgl on 16/01/14. 爆破粒子
*/
public class Particle {

public static final int PART_WH = 8; // 默认小球宽高

// 原本的值(不可变)
// float originCX;
// float originCY;
// float originRadius;

// 实际的值(可变)
float cx; // center x of circle
float cy; // center y of circle
float radius;

int color;
float alpha;

static Random random = new Random();

Rect mBound;

public static Particle generateParticle(int color, Rect bound, Point point) {
int row = point.y; // 行是高
int column = point.x; // 列是宽

Particle particle = new Particle();
particle.mBound = bound;
particle.color = color;
particle.alpha = 1f;

particle.radius = PART_WH;
particle.cx = bound.left + PART_WH * column;
particle.cy = bound.top + PART_WH * row;

return particle;
}

public void advance(float factor) {
cx = cx + factor * random.nextInt(mBound.width())
* (random.nextFloat() - 0.5f);
cy = cy + factor * random.nextInt(mBound.height() / 2);

radius = radius - factor * random.nextInt(2);

alpha = (1f - factor) * (1 + random.nextFloat());
}
}

3.ExplosionAnimator

属性动画,用于动画展示

package com.lgl.animview;

import android.animation.ValueAnimator;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.View;

/**
* Created by lgl on 16/01/14.
*/
public class ExplosionAnimator extends ValueAnimator {
public static final int DEFAULT_DURATION = 1500;
private Particle[][] mParticles;
private Paint mPaint;
private View mContainer;

public ExplosionAnimator(View view, Bitmap bitmap, Rect bound) {

mPaint = new Paint();
mContainer = view;

setFloatValues(0.0f, 1.0f);
setDuration(DEFAULT_DURATION);

mParticles = generateParticles(bitmap, bound);
}

private Particle[][] generateParticles(Bitmap bitmap, Rect bound) {
int w = bound.width();
int h = bound.height();

int partW_Count = w / Particle.PART_WH; // 横向个数
int partH_Count = h / Particle.PART_WH; // 竖向个数

int bitmap_part_w = bitmap.getWidth() / partW_Count;
int bitmap_part_h = bitmap.getHeight() / partH_Count;

Particle[][] particles = new Particle[partH_Count][partW_Count];
Point point = null;
for (int row = 0; row < partH_Count; row++) { // 行
for (int column = 0; column < partW_Count; column++) { // 列
// 取得当前粒子所在位置的颜色
int color = bitmap.getPixel(column * bitmap_part_w, row
* bitmap_part_h);

point = new Point(column, row); // x是列,y是行

particles[row][column] = Particle.generateParticle(color,
bound, point);
}
}

return particles;
}

public void draw(Canvas canvas) {
if (!isStarted()) { // 动画结束时停止
return;
}
for (Particle[] particle : mParticles) {
for (Particle p : particle) {
p.advance((Float) getAnimatedValue());
mPaint.setColor(p.color);
// mPaint.setAlpha((int) (255 * p.alpha)); //只是这样设置,透明色会显示为黑色
mPaint.setAlpha((int) (Color.alpha(p.color) * p.alpha)); // 这样透明颜色就不是黑色了
canvas.drawCircle(p.cx, p.cy, p.radius, mPaint);
}
}

mContainer.invalidate();
}

@Override
public void start() {
super.start();
mContainer.invalidate();
}
}

4.ExplosionField

开始执行这个实例的动画了

package com.lgl.animview;

import java.util.ArrayList;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;

/**
* Created by lgl on 16/01/14.
*/
public class ExplosionField extends View {
private static final String TAG = "ExplosionField";
private static final Canvas mCanvas = new Canvas();
private ArrayList explosionAnimators;
private OnClickListener onClickListener;

public ExplosionField(Context context) {
super(context);
init();
}

public ExplosionField(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

private void init() {
explosionAnimators = new ArrayList();

attach2Activity((Activity) getContext());
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (ExplosionAnimator animator : explosionAnimators) {
animator.draw(canvas);
}
}

/**
* 爆破
*
* @param view
* 使得该view爆破
*/
public void explode(final View view) {
Rect rect = new Rect();
view.getGlobalVisibleRect(rect); // 得到view相对于整个屏幕的坐标
rect.offset(0, -ParticleUtils.dp2px(25)); // 去掉状态栏高度

final ExplosionAnimator animator = new ExplosionAnimator(this,
createBitmapFromView(view), rect);
explosionAnimators.add(animator);

animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
view.animate().alpha(0f).setDuration(150).start();
}

@Override
public void onAnimationEnd(Animator animation) {
view.animate().alpha(1f).setDuration(150).start();

// 动画结束时从动画集中移除
explosionAnimators.remove(animation);
animation = null;
}
});
animator.start();
}

private Bitmap createBitmapFromView(View view) {
/*
* 为什么屏蔽以下代码段? 如果ImageView直接得到位图,那么当它设置背景(backgroud)时,不会读取到背景颜色
*/
// if (view instanceof ImageView) {
// Drawable drawable = ((ImageView)view).getDrawable();
// if (drawable != null && drawable instanceof BitmapDrawable) {
// return ((BitmapDrawable) drawable).getBitmap();
// }
// }

// view.clearFocus(); //不同焦点状态显示的可能不同——(azz:不同就不同有什么关系?)

Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
Bitmap.Config.ARGB_8888);

if (bitmap != null) {
synchronized (mCanvas) {
mCanvas.setBitmap(bitmap);
view.draw(mCanvas);
mCanvas.setBitmap(null); // 清除引用
}
}
return bitmap;
}

/**
* 给Activity加上全屏覆盖的ExplosionField
*/
private void attach2Activity(Activity activity) {
ViewGroup rootView = (ViewGroup) activity
.findViewById(Window.ID_ANDROID_CONTENT);

ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
rootView.addView(this, lp);
}

/**
* 希望谁有破碎效果,就给谁加Listener
*
* @param view
* 可以是ViewGroup
*/
public void addListener(View view) {
if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
int count = viewGroup.getChildCount();
for (int i = 0; i < count; i++) {
addListener(viewGroup.getChildAt(i));
}
} else {
view.setClickable(true);
view.setOnClickListener(getOnClickListener());
}
}

private OnClickListener getOnClickListener() {
if (null == onClickListener) {

onClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
ExplosionField.this.explode(v);

// view.setOnClickListener(null); // 用过一次就不需要了
}
};
}

return onClickListener;
}
}

5.MainActivity

好的,一切准备好了之后我们就可以使用了

package com.lgl.animview;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

// 实例化粒子动画
private ExplosionField explosionField;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

explosionField = new ExplosionField(this);
// 绑定哪个控件哪个控件就有效果,如果需要整个layout,只要绑定根布局的id即可
explosionField.addListener(findViewById(R.id.iv_round));
}
}

在xml中我们什么也不用做,好的,让我们来运行一下

Android特效专辑(五)——自定义圆形头像和仿MIUI卸载动画—粒子爆炸_imageview_03



举报

相关推荐

0 条评论