自定义粒子破碎效果

自定义粒子破碎效果图:

本文内容仿至阿曌博客,并在此基础上稍作修改,纯粹学习目的,更多详情请查看原文

个人觉得粒子破碎的核心就是怎样把一张图片分解成粒子,并让其呈现破碎四散的效果,下面说说实现过程:

  1. 我们需要拿到图片对象,因为我们是要让整个view都呈现破碎的效果,那么就直接把view复制成一张图片
    /**
     * 把当前view复制成一张图片,并绘制出来
     * @param view
     * @return
     */
private Bitmap CopyView(View view) {
        Bitmap bitmap=Bitmap.createBitmap(view.getMeasuredWidth(),view.getMeasuredHeight(),Config.ARGB_8888);
        canvas.setBitmap(bitmap);
        view.draw(canvas);
        return bitmap;
    }

2 . 拿到图片后,把图片裁剪成若干个粒子,每个粒子都保存相应的信息(中心点,半径,颜色,透明度),并把粒子保存到一个二维数组里

    /**
     * 把当前复制过来的view图片裁剪成颗粒
     * @param copyView
     * @param rect  包裹view的矩形
     */
    private Granule[][] DivisionView(Bitmap copyView, Rect rect) {
        int color;
        Point point;
        //当前view宽高可以裁剪成多少粒子
        int countW=rect.width()/Granule.GranuleWH;
        int countH=rect.height()/Granule.GranuleWH;
        int bitmap_part_w = copyView.getWidth() / countW;
        int bitmap_part_h = copyView.getHeight() / countH;
        //二位数组包含该view图片所有的粒子信息
        Granule[][]granules=new Granule[countH][countW];//先行后列
        for(int row=0;row<countH;row++){//行
            for(int column=0;column<countW;column++){//列
                //获取每个粒子的颜色
                color=copyView.getPixel(column*bitmap_part_w,row*bitmap_part_h);//竖
                //记录每个点
                point=new Point(column,row);
                //记录粒子信息,并返回一个粒子对象
                granules[row][column]=Granule.SetGranuleAttribute(color, rect, point);
            }
        }
        return granules;
    }

3 . 上面我们已经把图片裁剪成了粒子,呈现破碎效果就只需要不断的更改每个粒子的信息(中心点,半径,颜色,透明度)就可以了,修改粒子信息需要一个动态的值,最合适的就是通过动画来实现,通过继承属性 ValueAnimator,设置setFloatValues(0f,1f);表示从0平滑过渡到1,然后我们通过getAnimatedValue()拿到实时的过渡值,通过这个值来修改信息,即可实现破碎的效果;

        public void ondraw(Canvas canvas){
            //判断动画是否开始
            if(!isStarted()){
                return;
            }
            for(Granule[]granule:granules){
                for(Granule g:granule){
                    //修改粒子信息
                    g.ChangeGranuleAttribute((float) getAnimatedValue());
                    //把粒子修改成圆形绘制出来
                    paint.setColor(g.color);
                    //                  paint.setAlpha((int) (255 * g.alpha)); //不会出现透明度的渐变效果
                    paint.setAlpha((int) (Color.alpha(g.color) * g.alpha)); 
                    canvas.drawCircle(g.centerX,g.centerY, g.radius,paint);
                }
            }
            view.invalidate();
        }


/**
     * 修改粒子属性
     * @param factor 
     */
    public void ChangeGranuleAttribute(float factor){
        //中心点在原view矩形内随机
        centerX=centerX+factor*random.nextInt(rect.width())* (random.nextFloat() - 0.5f);//nextFloat() 0-1内随机
        centerY=centerY+factor*random.nextInt(rect.height())* (random.nextFloat() - 0.5f);
        //半径慢慢变小
        radius=radius-factor*random.nextInt(2);
        //透明度随之变低
        alpha = (1f - factor) * (1 + random.nextFloat());
    }

最关键的部分上面已经介绍完了,下面说说我在实现效果过程中觉得有必要记录的知识点:

  1. 继承ViewGroup 的view通常情况下不会调用onDraw方法,原因, 解决办法:

    1,在构造函数里面,给其设置一个颜色,如#00000000。

    if(getBackground()==null){
    setBackgroundColor(Color.WHITE);
    }

    2,在构造函数里面,调用setWillNotDraw(false),去掉其WILL_NOT_DRAW flag。

  2. 属性动画ValueAnimator的使用


源代码:

GranuleExplodeView:

import java.util.ArrayList;
import java.util.List;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
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.graphics.Bitmap.Config;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.LinearLayout;
import android.view.View.OnClickListener;;

public class GranuleExplodeView extends LinearLayout implements OnClickListener, OnItemClickListener{
    private Canvas canvas;
    /** 包含了粒子信息的粒子集合*/
    private Granule [][]granules;
    /** 动画集合*/
    private List<GranuleAnimation> animations;

    public GranuleExplodeView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

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

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

    private void init() {
//      if(getBackground()==null){
//          setBackgroundColor(Color.WHITE);
//          }
        setWillNotDraw(false);
        setOrientation(VERTICAL);
        canvas=new Canvas();
        animations=new ArrayList<GranuleAnimation>();
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        MeasureChild(this);
    }
    private void MeasureChild(ViewGroup viewGroup) {
        int childcount=viewGroup.getChildCount();
        for(int i=0;i<childcount;i++){
            View view=viewGroup.getChildAt(i);
            if(view instanceof ViewGroup){
                if(view instanceof AbsListView){
                    ((AbsListView) view).setOnItemClickListener(this);
                }else {
                    MeasureChild(((ViewGroup) view));
                }
            }else {
                //给每个子view都添加监听
                view.setClickable(true);
                view.setOnClickListener(this);
            }

        }
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for(GranuleAnimation animation:animations){
            animation.ondraw(canvas);   
        }
    }
    @Override
    public void onClick(View v) {
        CopyDivisionView(v);
    }
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        CopyDivisionView(view);
    }
    /**
     * 把view复制并分割成若干颗粒
     * @param view 
     */
    private void CopyDivisionView(final View view) {
        Rect rect=new Rect();
        //http://www.cnblogs.com/ai-developers/p/4413585.html
        //getGlobalVisibleRect方法的作用是获取视图在屏幕坐标中的可视区域
        //getLocalVisibleRect的作用是获取视图本身可见的坐标区域,坐标以自己的左上角为原点(0,0)
        view.getGlobalVisibleRect(rect);
        //把view图片裁剪,记录每个粒子信息 并保存在一个二维数组里
        granules=DivisionView(CopyView(view),rect);
        //开启一个动画
        GranuleAnimation granuleAnimation=new GranuleAnimation(this, granules);
        animations.add(granuleAnimation);
        granuleAnimation.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                //让原有的view隐藏起来
                view.animate().alpha(0f).setDuration(5).start();
            }
            @Override
            public void onAnimationEnd(Animator animation) {
                //动画结束的时候可以去刷新,这里就直接还原了
                view.animate().alpha(1f).setDuration(150).start();
                //移除动画
                animations.remove(animation);
                animation=null;
            }
        });
        granuleAnimation.start();
    }

    /**
     * 把当前复制过来的view图片裁剪成颗粒
     * @param copyView
     * @param rect  包裹view的矩形
     */
    private Granule[][] DivisionView(Bitmap copyView, Rect rect) {
        int color;
        Point point;
        //当前view宽高可以裁剪成多少粒子
        int countW=rect.width()/Granule.GranuleWH;
        int countH=rect.height()/Granule.GranuleWH;
        int bitmap_part_w = copyView.getWidth() / countW;
        int bitmap_part_h = copyView.getHeight() / countH;
        //二位数组包含该view图片所有的粒子信息
        Granule[][]granules=new Granule[countH][countW];//先行后列
        for(int row=0;row<countH;row++){//行
            for(int column=0;column<countW;column++){//列
                //获取每个粒子的颜色
                color=copyView.getPixel(column*bitmap_part_w,row*bitmap_part_h);//竖
                //记录每个点
                point=new Point(column,row);
                //记录粒子信息,并返回一个粒子对象
                granules[row][column]=Granule.SetGranuleAttribute(color, rect, point);
            }
        }
        return granules;
    }

    /**
     * 把当前view复制成一张图片,并绘制出来
     * @param view
     * @return
     */
    private Bitmap CopyView(View view) {
        Bitmap bitmap=Bitmap.createBitmap(view.getMeasuredWidth(),view.getMeasuredHeight(),Config.ARGB_8888);
        canvas.setBitmap(bitmap);
        view.draw(canvas);
        return bitmap;
    }
    class GranuleAnimation extends ValueAnimator{
        private View view;
        private Granule[][]granules;
        private Paint paint;
        public GranuleAnimation(View view,Granule[][]granules) {
            paint=new Paint();
            this.view=view;
            this.granules=granules;
            setFloatValues(0f,1f);
            setDuration(1500);
        }
        @Override
        public void start() {
            super.start();
            //会触发ondraw
            view.invalidate();
        }
        public void ondraw(Canvas canvas){
            //判断动画是否开始
            if(!isStarted()){
                return;
            }
            for(Granule[]granule:granules){
                for(Granule g:granule){
                    //修改粒子信息
                    g.ChangeGranuleAttribute((float) getAnimatedValue());
                    //把粒子修改成圆形绘制出来
                    paint.setColor(g.color);
                    //                  paint.setAlpha((int) (255 * g.alpha)); //不会出现透明度的渐变效果
                    paint.setAlpha((int) (Color.alpha(g.color) * g.alpha)); 
                    canvas.drawCircle(g.centerX,g.centerY, g.radius,paint);
                }
            }
            view.invalidate();
        }

    }
}

Granule:

import java.util.Random;
import android.graphics.Point;
import android.graphics.Rect;

/**
 * 粒子颗粒(圆形)
 */
public class Granule {
    /** 默认粒子的宽高*/
    static final int GranuleWH=8;
    /** 每粒颗粒的中心点X*/
    float centerX;
    /** 每粒颗粒的中心点Y*/
    float centerY;
    /** 每粒颗粒的透明度,默认为1f,会随时间变为0f , 0为完全透明,255为全显示*/
    float alpha=1f;
    /** 每粒颗粒的圆半径*/
    float radius;
    /** 每粒颗粒的包裹矩形*/
    Rect rect;
    /** 每粒颗粒的颜色*/
    int color;
    /** 每粒颗粒扩散的随机值*/
    Random random=new Random();

    /**
     * 设置每个粒子的属性并返回该粒子对象
     * @param color
     * @param rect 
     * @param point
     */
    public static Granule SetGranuleAttribute(int color,Rect rect,Point point){
        Granule granule=new Granule();
        granule.centerX=rect.left+GranuleWH*point.x;
        granule.centerY=rect.top+GranuleWH*point.y;
        granule.alpha=1f;
        granule.color=color;
        granule.radius=GranuleWH;
        granule.rect=rect;
        return granule;
    }
    /**
     * 修改粒子属性
     * @param factor 
     */
    public void ChangeGranuleAttribute(float factor){
        //中心点在原view矩形内随机
        centerX=centerX+factor*random.nextInt(rect.width())* (random.nextFloat() - 0.5f);//nextFloat() 0-1内随机
        centerY=centerY+factor*random.nextInt(rect.height())* (random.nextFloat() - 0.5f);
        //半径慢慢变小
        radius=radius-factor*random.nextInt(2);
        //透明度随之变低
        alpha = (1f - factor) * (1 + random.nextFloat());
    }
}

GranuleExplodeActivity:

import java.util.ArrayList;
import java.util.HashMap;
import com.example.mytoolutils.R;
import android.app.Activity;
import android.os.Bundle;
import android.widget.GridView;
import android.widget.SimpleAdapter;

public class GranuleExplodeActivity extends Activity  {
    //生成动态数组,并且转入数据  
    private  ArrayList<HashMap<String, Object>> lstImageItem = new ArrayList<HashMap<String, Object>>();  
    private SimpleAdapter adapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_granule_explode);
        SetData();
        GridView gridView=(GridView) findViewById(R.id.gridview);
        adapter=new SimpleAdapter(this,lstImageItem, R.layout.activity_gridview_item,
                new String[]{"ItemImage","ItemText"},
                new int[] {R.id.imagviewgridview,R.id.textviewgridview});
        gridView.setAdapter(adapter);
    }
    private void SetData() {
        for(int i=0;i<20;i++)  
        {  
            HashMap<String, Object> map = new HashMap<String, Object>();  
            map.put("ItemImage", R.drawable.yueyunp);//添加图像资源的ID  
            map.put("ItemText", "NO."+String.valueOf(i));//按序号做ItemText  
            lstImageItem.add(map);  
        }  
    }
}

R.layout.activity_granule_explode:

<com.example.mytoolutils.explode.GranuleExplodeView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.mytoolutils.explode.GranuleExplodeActivity" >

    <GridView
        android:id="@+id/gridview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:horizontalSpacing="10dp"
        android:listSelector="@color/transparent"
        android:numColumns="4"
        android:padding="10dp"
        android:stretchMode="columnWidth"
        android:verticalSpacing="10dp" >
    </GridView>

</com.example.mytoolutils.explode.GranuleExplodeView>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值