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


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

截图

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

一.自定义圆形头像——

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

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

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="roundedimageview">

        <attr name="border_thickness" format="dimension" />

        <attr name="border_inside_color" format="color" />

        <attr name="border_outside_color" format="color"></attr>

    </declare-styleable>
</resources>

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
  <com.lgl.animview.RoundImageView
        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" />
好的,让我们运行下吧

二.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<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;
    }
}

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中我们什么也不用做,好的,让我们来运行一下

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

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

Android特效专辑(五)——自定义圆形头像和仿MIUI卸载动画—粒子爆炸的更多相关文章

  1. Android特效专辑(三)——自定义不一样的Toast

    Android特效专辑(三)--自定义不一样的Toast 大家都知道,Android的控件有时候很难满足我们的需求,所以我们需要自定义View.自定义的方式很多,有继承原生控件也有直接自定义View的 ...

  2. 【Android开源项目分析】自定义圆形头像CircleImageView的使用和源码分析

    原文地址: http://blog.csdn.net/zhoubin1992/article/details/47258639 效果

  3. Android特效专辑(十二)——仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View

    Android特效专辑(十二)--仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View 先来看看这个效果 这是我的在Only上添加的效果,说实话,Only现在都还只是半成品,台面都上不了,怪自己技术 ...

  4. Android特效专辑(九)——仿微信雷达搜索好友特效,逻辑清晰实现简单

    Android特效专辑(九)--仿微信雷达搜索好友特效,逻辑清晰实现简单 不知不觉这个春节也已经过完了,遗憾家里没网,没能及时给大家送上祝福,今天回到深圳,明天就要上班了,小伙伴们是不是和我一样呢?今 ...

  5. Android特效专辑(六)——仿QQ聊天撒花特效,无形装逼,最为致命

    Android特效专辑(六)--仿QQ聊天撒花特效,无形装逼,最为致命 我的关于特效的专辑已经在CSDN上申请了一个专栏--http://blog.csdn.net/column/details/li ...

  6. Android特效专辑(十一)——仿水波纹流量球进度条控制器,实现高端大气的主流特效

    Android特效专辑(十一)--仿水波纹流球进度条控制器,实现高端大气的主流特效 今天看到一个效果挺不错的,就模仿了下来,加上了一些自己想要的效果,感觉还不错的样子,所以就分享出来了,话不多说,上图 ...

  7. Android特效专辑(十)——点击水波纹效果实现,逻辑清晰实现简单

    Android特效专辑(十)--点击水波纹效果实现,逻辑清晰实现简单 这次做的东西呢,和上篇有点类似,就是用比较简单的逻辑思路去实现一些比较好玩的特效,最近也是比较忙,所以博客更新的速度还得看时间去推 ...

  8. Android特效专辑(八)——实现心型起泡飞舞的特效,让你的APP瞬间暖心

    Android特效专辑(八)--实现心型起泡飞舞的特效,让你的APP瞬间暖心 马上也要放年假了,家里估计会没网,更完这篇的话,可能要到年后了,不过在此期间会把更新内容都保存在本地,这样有网就可以发表了 ...

  9. Android特效专辑(七)——飞机升空特效,一键清理缓存,灵活运用动画会有不一样的感受

    Android特效专辑(七)--飞机升空特效,一键清理缓存,灵活运用属性动画 最近的几篇博文反响还不错,也会继续的写下去的,关于这些特效的专辑,大多数也是借鉴大神的,最近由于工作的关系,会深入的了解一 ...

随机推荐

  1. Android的log日志知识点剖析

    log类的继承结构 Log public final class Log extends Object java.lang.Object ↳ android.util.Log log日志的常用方法 分 ...

  2. cocos2dx 3.2之Lua打飞机项目

    1          创建lua打飞机项目 cocos new T32Lua -dE:\Installed\cocos2d-x-3.2\cocos2d-x-3.2\projects -l lua 2 ...

  3. Maven 介绍、安装使用

    简介         Maven是一个强大的构建工具,能够帮我们自动化构建过程,从清理.编译.测试到生成报告,再到打包和部署.只要使用Maven配置好项目,然后执行命令(如mvn clean inst ...

  4. Android中FrameAnimation动画的使用

    Frame Animation 表示帧动画,是顺序播放事先做好的图像,跟电影类似,Android SDK提供了另外一个类AnimationDrawable来定义使用Frame Animation. 下 ...

  5. 【java虚拟机序列】java中的垃圾回收与内存分配策略

    在[java虚拟机系列]java虚拟机系列之JVM总述中我们已经详细讲解过java中的内存模型,了解了关于JVM中内存管理的基本知识,接下来本博客将带领大家了解java中的垃圾回收与内存分配策略. 垃 ...

  6. Sky(dart)语言介绍-android学习之旅(十)

    认识dart语言 google于2011年10月10日发布了"dart"语言的"早起预览版",google希望利用这款语言,帮助开发者克服javaScript的 ...

  7. 带吸附效果的ViewPager(二)

    上篇实现了一个简单的吸附效果,那么这篇我们来实现上篇中所示的360软件详情页(带viewpager)的效果!先来参观下本篇所实现的效果图: 了解了上一篇的实现过程,那么本篇的效果无非是修改一下布局,将 ...

  8. .so的封装调用

    .so的创建和调用有一个特点,我们要知道.so的调用并不一定必须在Activity中进行,那么制作时也并不一定要在Activity中,但是,一旦.so制作成功,那么再调用时,调用的java类就必须跟制 ...

  9. Docker教程:docker machine的配置和命令

    http://blog.csdn.net/pipisorry/article/details/50921335 安装virtualbox 如果要使用virtualbox,首先要安装virtualbox ...

  10. 怎么对MySQL数据库操作大数据?这里有思路

    最近学到一招关于使用java代码操作MySQL数据库,对大文本数据(LOB)进行CRUD的技巧,虽然向数据库很少向中存入大文本文件(一般都是存储在硬盘上),但是还是很有必要知道这一技巧的.下面我就来说 ...