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

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

截图

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

一.自定义圆形头像——

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

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

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <declare-styleable name="roundedimageview">
  4. <attr name="border_thickness" format="dimension" />
  5. <attr name="border_inside_color" format="color" />
  6. <attr name="border_outside_color" format="color"></attr>
  7. </declare-styleable>
  8. </resources>

2.自定义View

  1. 紧接着我们就可以编写这个类了
  1. package com.lgl.animview;
  2. /**
  3. * 圆形头像
  4. * Created by LGL on 2016/1/12.
  5. */
  6. import android.content.Context;
  7. import android.content.res.TypedArray;
  8. import android.graphics.Bitmap;
  9. import android.graphics.Canvas;
  10. import android.graphics.Paint;
  11. import android.graphics.PorterDuff;
  12. import android.graphics.PorterDuffXfermode;
  13. import android.graphics.Rect;
  14. import android.graphics.drawable.BitmapDrawable;
  15. import android.graphics.drawable.Drawable;
  16. import android.graphics.drawable.NinePatchDrawable;
  17. import android.util.AttributeSet;
  18. import android.widget.ImageView;
  19. /**
  20. * 圆形ImageView,可设置最多两个宽度不同且颜色不同的圆形边框。 设置颜色在xml布局文件中由自定义属性配置参数指定
  21. */
  22. public class RoundImageView extends ImageView {
  23. private int mBorderThickness = 0;
  24. private Context mContext;
  25. private int defaultColor = 0xFFFFFFFF;
  26. // 如果只有其中一个有值,则只画一个圆形边框
  27. private int mBorderOutsideColor = 0;
  28. private int mBorderInsideColor = 0;
  29. // 控件默认长、宽
  30. private int defaultWidth = 0;
  31. private int defaultHeight = 0;
  32. public RoundImageView(Context context) {
  33. super(context);
  34. mContext = context;
  35. }
  36. public RoundImageView(Context context, AttributeSet attrs) {
  37. super(context, attrs);
  38. mContext = context;
  39. setCustomAttributes(attrs);
  40. }
  41. public RoundImageView(Context context, AttributeSet attrs, int defStyle) {
  42. super(context, attrs, defStyle);
  43. mContext = context;
  44. setCustomAttributes(attrs);
  45. }
  46. private void setCustomAttributes(AttributeSet attrs) {
  47. // 获取自定义的属性
  48. TypedArray a = mContext.obtainStyledAttributes(attrs,
  49. R.styleable.roundedimageview);
  50. mBorderThickness = a.getDimensionPixelSize(
  51. R.styleable.roundedimageview_border_thickness, 0);
  52. mBorderOutsideColor = a
  53. .getColor(R.styleable.roundedimageview_border_outside_color,
  54. defaultColor);
  55. mBorderInsideColor = a.getColor(
  56. R.styleable.roundedimageview_border_inside_color, defaultColor);
  57. }
  58. @Override
  59. protected void onDraw(Canvas canvas) {
  60. Drawable drawable = getDrawable();
  61. if (drawable == null) {
  62. return;
  63. }
  64. if (getWidth() == 0 || getHeight() == 0) {
  65. return;
  66. }
  67. this.measure(0, 0);
  68. if (drawable.getClass() == NinePatchDrawable.class)
  69. return;
  70. Bitmap b = ((BitmapDrawable) drawable).getBitmap();
  71. Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);
  72. if (defaultWidth == 0) {
  73. defaultWidth = getWidth();
  74. }
  75. if (defaultHeight == 0) {
  76. defaultHeight = getHeight();
  77. }
  78. int radius = 0;
  79. if (mBorderInsideColor != defaultColor
  80. && mBorderOutsideColor != defaultColor) {// 定义画两个边框,分别为外圆边框和内圆边框
  81. radius = (defaultWidth < defaultHeight ? defaultWidth
  82. : defaultHeight) / 2 - 2 * mBorderThickness;
  83. // 画内圆
  84. drawCircleBorder(canvas, radius + mBorderThickness / 2,
  85. mBorderInsideColor);
  86. // 画外圆
  87. drawCircleBorder(canvas, radius + mBorderThickness
  88. + mBorderThickness / 2, mBorderOutsideColor);
  89. } else if (mBorderInsideColor != defaultColor
  90. && mBorderOutsideColor == defaultColor) {// 定义画一个边框
  91. radius = (defaultWidth < defaultHeight ? defaultWidth
  92. : defaultHeight) / 2 - mBorderThickness;
  93. drawCircleBorder(canvas, radius + mBorderThickness / 2,
  94. mBorderInsideColor);
  95. } else if (mBorderInsideColor == defaultColor
  96. && mBorderOutsideColor != defaultColor) {// 定义画一个边框
  97. radius = (defaultWidth < defaultHeight ? defaultWidth
  98. : defaultHeight) / 2 - mBorderThickness;
  99. drawCircleBorder(canvas, radius + mBorderThickness / 2,
  100. mBorderOutsideColor);
  101. } else {// 没有边框
  102. radius = (defaultWidth < defaultHeight ? defaultWidth
  103. : defaultHeight) / 2;
  104. }
  105. Bitmap roundBitmap = getCroppedRoundBitmap(bitmap, radius);
  106. canvas.drawBitmap(roundBitmap, defaultWidth / 2 - radius, defaultHeight
  107. / 2 - radius, null);
  108. }
  109. /**
  110. * 获取裁剪后的圆形图片
  111. */
  112. public Bitmap getCroppedRoundBitmap(Bitmap bmp, int radius) {
  113. Bitmap scaledSrcBmp;
  114. int diameter = radius * 2;
  115. // 为了防止宽高不相等,造成圆形图片变形,因此截取长方形中处于中间位置最大的正方形图片
  116. int bmpWidth = bmp.getWidth();
  117. int bmpHeight = bmp.getHeight();
  118. int squareWidth = 0, squareHeight = 0;
  119. int x = 0, y = 0;
  120. Bitmap squareBitmap;
  121. if (bmpHeight > bmpWidth) {// 高大于宽
  122. squareWidth = squareHeight = bmpWidth;
  123. x = 0;
  124. y = (bmpHeight - bmpWidth) / 2;
  125. // 截取正方形图片
  126. squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth,
  127. squareHeight);
  128. } else if (bmpHeight < bmpWidth) {// 宽大于高
  129. squareWidth = squareHeight = bmpHeight;
  130. x = (bmpWidth - bmpHeight) / 2;
  131. y = 0;
  132. squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth,
  133. squareHeight);
  134. } else {
  135. squareBitmap = bmp;
  136. }
  137. if (squareBitmap.getWidth() != diameter
  138. || squareBitmap.getHeight() != diameter) {
  139. scaledSrcBmp = Bitmap.createScaledBitmap(squareBitmap, diameter,
  140. diameter, true);
  141. } else {
  142. scaledSrcBmp = squareBitmap;
  143. }
  144. Bitmap output = Bitmap.createBitmap(scaledSrcBmp.getWidth(),
  145. scaledSrcBmp.getHeight(),
  146. Bitmap.Config.ARGB_8888);
  147. Canvas canvas = new Canvas(output);
  148. Paint paint = new Paint();
  149. Rect rect = new Rect(0, 0, scaledSrcBmp.getWidth(),
  150. scaledSrcBmp.getHeight());
  151. paint.setAntiAlias(true);
  152. paint.setFilterBitmap(true);
  153. paint.setDither(true);
  154. canvas.drawARGB(0, 0, 0, 0);
  155. canvas.drawCircle(scaledSrcBmp.getWidth() / 2,
  156. scaledSrcBmp.getHeight() / 2,
  157. scaledSrcBmp.getWidth() / 2,
  158. paint);
  159. paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
  160. canvas.drawBitmap(scaledSrcBmp, rect, rect, paint);
  161. bmp = null;
  162. squareBitmap = null;
  163. scaledSrcBmp = null;
  164. return output;
  165. }
  166. /**
  167. * 边缘画圆
  168. */
  169. private void drawCircleBorder(Canvas canvas, int radius, int color) {
  170. Paint paint = new Paint();
  171. /* 去锯齿 */
  172. paint.setAntiAlias(true);
  173. paint.setFilterBitmap(true);
  174. paint.setDither(true);
  175. paint.setColor(color);
  176. /* 设置paint的 style 为STROKE:空心 */
  177. paint.setStyle(Paint.Style.STROKE);
  178. /* 设置paint的外框宽度 */
  179. paint.setStrokeWidth(mBorderThickness);
  180. canvas.drawCircle(defaultWidth / 2, defaultHeight / 2, radius, paint);
  181. }
  182. }

3.引用

  1. 引用起来就比较简单了,我们首先来引入他的命名空间
  1. xmlns:imagecontrol="http://schemas.android.com/apk/res-auto"
  1. 然后我们直接写xml
  1. <com.lgl.animview.RoundImageView
  2. android:id="@+id/iv_round"
  3. android:layout_width="wrap_content"
  4. android:layout_height="wrap_content"
  5. android:src="@drawable/photo"
  6. imagecontrol:border_inside_color="#bc0978"
  7. imagecontrol:border_outside_color="#ba3456"
  8. imagecontrol:border_thickness="1dp" />
  1. 好的,让我们运行下吧

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

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

1.ParticleUtils

  1. 用于粒子动画的单位转换
  1. package com.lgl.animview;
  2. import android.content.res.Resources;
  3. /**
  4. * 粒子动画
  5. */
  6. public class ParticleUtils {
  7. /**
  8. * 密度
  9. */
  10. public static final float DENSITY = Resources.getSystem().getDisplayMetrics().density;
  11. public static int dp2px(int dp) {
  12. return Math.round(dp * DENSITY);
  13. }
  14. }

2.Particle

  1. 用于爆破效果的分子计算
  1. package com.lgl.animview;
  2. import java.util.Random;
  3. import android.graphics.Point;
  4. import android.graphics.Rect;
  5. /**
  6. * Created by lgl on 16/01/14. 爆破粒子
  7. */
  8. public class Particle {
  9. public static final int PART_WH = 8; // 默认小球宽高
  10. // 原本的值(不可变)
  11. // float originCX;
  12. // float originCY;
  13. // float originRadius;
  14. // 实际的值(可变)
  15. float cx; // center x of circle
  16. float cy; // center y of circle
  17. float radius;
  18. int color;
  19. float alpha;
  20. static Random random = new Random();
  21. Rect mBound;
  22. public static Particle generateParticle(int color, Rect bound, Point point) {
  23. int row = point.y; // 行是高
  24. int column = point.x; // 列是宽
  25. Particle particle = new Particle();
  26. particle.mBound = bound;
  27. particle.color = color;
  28. particle.alpha = 1f;
  29. particle.radius = PART_WH;
  30. particle.cx = bound.left + PART_WH * column;
  31. particle.cy = bound.top + PART_WH * row;
  32. return particle;
  33. }
  34. public void advance(float factor) {
  35. cx = cx + factor * random.nextInt(mBound.width())
  36. * (random.nextFloat() - 0.5f);
  37. cy = cy + factor * random.nextInt(mBound.height() / 2);
  38. radius = radius - factor * random.nextInt(2);
  39. alpha = (1f - factor) * (1 + random.nextFloat());
  40. }
  41. }

3.ExplosionAnimator

  1. 属性动画,用于动画展示
  1. package com.lgl.animview;
  2. import android.animation.ValueAnimator;
  3. import android.graphics.Bitmap;
  4. import android.graphics.Canvas;
  5. import android.graphics.Color;
  6. import android.graphics.Paint;
  7. import android.graphics.Point;
  8. import android.graphics.Rect;
  9. import android.view.View;
  10. /**
  11. * Created by lgl on 16/01/14.
  12. */
  13. public class ExplosionAnimator extends ValueAnimator {
  14. public static final int DEFAULT_DURATION = 1500;
  15. private Particle[][] mParticles;
  16. private Paint mPaint;
  17. private View mContainer;
  18. public ExplosionAnimator(View view, Bitmap bitmap, Rect bound) {
  19. mPaint = new Paint();
  20. mContainer = view;
  21. setFloatValues(0.0f, 1.0f);
  22. setDuration(DEFAULT_DURATION);
  23. mParticles = generateParticles(bitmap, bound);
  24. }
  25. private Particle[][] generateParticles(Bitmap bitmap, Rect bound) {
  26. int w = bound.width();
  27. int h = bound.height();
  28. int partW_Count = w / Particle.PART_WH; // 横向个数
  29. int partH_Count = h / Particle.PART_WH; // 竖向个数
  30. int bitmap_part_w = bitmap.getWidth() / partW_Count;
  31. int bitmap_part_h = bitmap.getHeight() / partH_Count;
  32. Particle[][] particles = new Particle[partH_Count][partW_Count];
  33. Point point = null;
  34. for (int row = 0; row < partH_Count; row++) { // 行
  35. for (int column = 0; column < partW_Count; column++) { // 列
  36. // 取得当前粒子所在位置的颜色
  37. int color = bitmap.getPixel(column * bitmap_part_w, row
  38. * bitmap_part_h);
  39. point = new Point(column, row); // x是列,y是行
  40. particles[row][column] = Particle.generateParticle(color,
  41. bound, point);
  42. }
  43. }
  44. return particles;
  45. }
  46. public void draw(Canvas canvas) {
  47. if (!isStarted()) { // 动画结束时停止
  48. return;
  49. }
  50. for (Particle[] particle : mParticles) {
  51. for (Particle p : particle) {
  52. p.advance((Float) getAnimatedValue());
  53. mPaint.setColor(p.color);
  54. // mPaint.setAlpha((int) (255 * p.alpha)); //只是这样设置,透明色会显示为黑色
  55. mPaint.setAlpha((int) (Color.alpha(p.color) * p.alpha)); // 这样透明颜色就不是黑色了
  56. canvas.drawCircle(p.cx, p.cy, p.radius, mPaint);
  57. }
  58. }
  59. mContainer.invalidate();
  60. }
  61. @Override
  62. public void start() {
  63. super.start();
  64. mContainer.invalidate();
  65. }
  66. }

4.ExplosionField

  1. 开始执行这个实例的动画了
  1. package com.lgl.animview;
  2. import java.util.ArrayList;
  3. import android.animation.Animator;
  4. import android.animation.AnimatorListenerAdapter;
  5. import android.app.Activity;
  6. import android.content.Context;
  7. import android.graphics.Bitmap;
  8. import android.graphics.Canvas;
  9. import android.graphics.Rect;
  10. import android.util.AttributeSet;
  11. import android.view.View;
  12. import android.view.ViewGroup;
  13. import android.view.Window;
  14. /**
  15. * Created by lgl on 16/01/14.
  16. */
  17. public class ExplosionField extends View {
  18. private static final String TAG = "ExplosionField";
  19. private static final Canvas mCanvas = new Canvas();
  20. private ArrayList<ExplosionAnimator> explosionAnimators;
  21. private OnClickListener onClickListener;
  22. public ExplosionField(Context context) {
  23. super(context);
  24. init();
  25. }
  26. public ExplosionField(Context context, AttributeSet attrs) {
  27. super(context, attrs);
  28. init();
  29. }
  30. private void init() {
  31. explosionAnimators = new ArrayList<ExplosionAnimator>();
  32. attach2Activity((Activity) getContext());
  33. }
  34. @Override
  35. protected void onDraw(Canvas canvas) {
  36. super.onDraw(canvas);
  37. for (ExplosionAnimator animator : explosionAnimators) {
  38. animator.draw(canvas);
  39. }
  40. }
  41. /**
  42. * 爆破
  43. *
  44. * @param view
  45. * 使得该view爆破
  46. */
  47. public void explode(final View view) {
  48. Rect rect = new Rect();
  49. view.getGlobalVisibleRect(rect); // 得到view相对于整个屏幕的坐标
  50. rect.offset(0, -ParticleUtils.dp2px(25)); // 去掉状态栏高度
  51. final ExplosionAnimator animator = new ExplosionAnimator(this,
  52. createBitmapFromView(view), rect);
  53. explosionAnimators.add(animator);
  54. animator.addListener(new AnimatorListenerAdapter() {
  55. @Override
  56. public void onAnimationStart(Animator animation) {
  57. view.animate().alpha(0f).setDuration(150).start();
  58. }
  59. @Override
  60. public void onAnimationEnd(Animator animation) {
  61. view.animate().alpha(1f).setDuration(150).start();
  62. // 动画结束时从动画集中移除
  63. explosionAnimators.remove(animation);
  64. animation = null;
  65. }
  66. });
  67. animator.start();
  68. }
  69. private Bitmap createBitmapFromView(View view) {
  70. /*
  71. * 为什么屏蔽以下代码段? 如果ImageView直接得到位图,那么当它设置背景(backgroud)时,不会读取到背景颜色
  72. */
  73. // if (view instanceof ImageView) {
  74. // Drawable drawable = ((ImageView)view).getDrawable();
  75. // if (drawable != null && drawable instanceof BitmapDrawable) {
  76. // return ((BitmapDrawable) drawable).getBitmap();
  77. // }
  78. // }
  79. // view.clearFocus(); //不同焦点状态显示的可能不同——(azz:不同就不同有什么关系?)
  80. Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
  81. Bitmap.Config.ARGB_8888);
  82. if (bitmap != null) {
  83. synchronized (mCanvas) {
  84. mCanvas.setBitmap(bitmap);
  85. view.draw(mCanvas);
  86. mCanvas.setBitmap(null); // 清除引用
  87. }
  88. }
  89. return bitmap;
  90. }
  91. /**
  92. * 给Activity加上全屏覆盖的ExplosionField
  93. */
  94. private void attach2Activity(Activity activity) {
  95. ViewGroup rootView = (ViewGroup) activity
  96. .findViewById(Window.ID_ANDROID_CONTENT);
  97. ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
  98. ViewGroup.LayoutParams.MATCH_PARENT,
  99. ViewGroup.LayoutParams.MATCH_PARENT);
  100. rootView.addView(this, lp);
  101. }
  102. /**
  103. * 希望谁有破碎效果,就给谁加Listener
  104. *
  105. * @param view
  106. * 可以是ViewGroup
  107. */
  108. public void addListener(View view) {
  109. if (view instanceof ViewGroup) {
  110. ViewGroup viewGroup = (ViewGroup) view;
  111. int count = viewGroup.getChildCount();
  112. for (int i = 0; i < count; i++) {
  113. addListener(viewGroup.getChildAt(i));
  114. }
  115. } else {
  116. view.setClickable(true);
  117. view.setOnClickListener(getOnClickListener());
  118. }
  119. }
  120. private OnClickListener getOnClickListener() {
  121. if (null == onClickListener) {
  122. onClickListener = new OnClickListener() {
  123. @Override
  124. public void onClick(View v) {
  125. ExplosionField.this.explode(v);
  126. // view.setOnClickListener(null); // 用过一次就不需要了
  127. }
  128. };
  129. }
  130. return onClickListener;
  131. }
  132. }

5.MainActivity

  1. 好的,一切准备好了之后我们就可以使用了
  1. package com.lgl.animview;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. public class MainActivity extends Activity {
  5. // 实例化粒子动画
  6. private ExplosionField explosionField;
  7. @Override
  8. protected void onCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.activity_main);
  11. explosionField = new ExplosionField(this);
  12. // 绑定哪个控件哪个控件就有效果,如果需要整个layout,只要绑定根布局的id即可
  13. explosionField.addListener(findViewById(R.id.iv_round));
  14. }
  15. }
  1. xml中我们什么也不用做,好的,让我们来运行一下
  1. 好的,本篇博客也到此结束了,喜欢的点个赞

Demo下载:http://download.csdn.net/detail/qq_26787115/9409139