zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

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

Android动画 自定义 卸载 特效 粒子 头像 圆形
2023-09-14 08:59:39 时间
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); }
imagecontrol:border_inside_color="#bc0978" imagecontrol:border_outside_color="#ba3456" imagecontrol:border_thickness="1dp" /
好的,让我们运行下吧

这里写图片描述


public static final float DENSITY = Resources.getSystem().getDisplayMetrics().density; public static int dp2px(int dp) { return Math.round(dp * DENSITY);
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());
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();
public class ExplosionField extends View { private static final String TAG = "ExplosionField"; private static final Canvas mCanvas = new Canvas(); private ArrayList ExplosionAnimator 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 ExplosionAnimator 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;
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中我们什么也不用做,好的,让我们来运行一下

这里写图片描述

好的,本篇博客也到此结束了,喜欢的点个赞


在eclipse的安装文件夹下Features文件夹中删除以com.android开头的文件夹 同时也要删除plugin目录下面的有关com.android的jar包等相关内容。删除之前请做好备份! 最简单的,就是把已安装的整个ecl