Android粒子系统

一、粒子属性
粒子图片,粒子大小,粒子缩放比例,粒子位置(X,Y),粒子移动方向与速度(X,Y坐标与时间t的关系)
计算粒子所处位置,绘制渲染

粒子系统,是大量基础图元构成的具有一定规律的不规律渲染图像。即要够成粒子系统,需要有基本图元,保持基本图元的一定规律性,并细化不规律性。
一个基本图元即是粒子系统中展示的一个粒子,有以下基本属性:粒子纹理【图片】,粒子的大小,粒子缩放,粒子的位置【平面坐标系中只有X,Y】,粒子移动方向与速度(X,Y坐标与时间T的关系)。
粒子属性基本信息保持一致,在各个变量细化时加入随机变量震动,从而实现统一大规律下不统一。
准备好所有粒子信息后,先检查粒子当前所处的位置,超出范围部分强制修改,然后绘制渲染粒子。

二、控件实现
构造方法,初始化基本数据:
    public ParticleView(Context context) {
        super(context);
    }

    public ParticleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initData(context);
        initBitmapInfo();
        initPaint();
    }

    public ParticleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

 /**
     * 初始化绘制笔
     */
    private void initPaint() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        // 防抖动
        paint.setDither(true);
        // 开启图像过滤
        paint.setFilterBitmap(true);
    }

    /**
     * 初始化粒子纹理
     */
    private void initBitmapInfo() {
        mStarOne = ((BitmapDrawable) mResources.getDrawable(R.drawable.star2)).getBitmap();
//        mStarOne = ((BitmapDrawable) mResources.getDrawable(R.drawable.snow1)).getBitmap();
        mStarOneWidth = mStarOne.getWidth();
        mStarOneHeight = mStarOne.getHeight();

//        mStarTwo = ((BitmapDrawable) mResources.getDrawable(R.drawable.star1)).getBitmap();
        mStarTwo = ((BitmapDrawable) mResources.getDrawable(R.drawable.snow2)).getBitmap();
        mStarTwoWidth = mStarTwo.getWidth();
        mStarTwoHeight = mStarTwo.getHeight();

//        mStarThree = ((BitmapDrawable) mResources.getDrawable(R.drawable.star3)).getBitmap();
        mStarThree = ((BitmapDrawable) mResources.getDrawable(R.drawable.snow3)).getBitmap();
        mStarThreeWidth = mStarThree.getWidth();
        mStarThreeHeight = mStarThree.getHeight();
    }

    /**
     * 初始化数据
     *
     * @param context
     */
    private void initData(Context context) {
        mResources = getResources();
        DisplayMetrics dm = context.getApplicationContext().getResources().getDisplayMetrics();

        mTotalWidth = dm.widthPixels;
        mTotalHeight = dm.heightPixels;
        Log.i(TAG, "mTotalWidth=" + mTotalWidth + "--1--mTotalHeight=" + mTotalHeight);
        //设置三个不同大小的速度值
        mFloatTransLowSpeed = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.5f,
                mResources.getDisplayMetrics());
        mFloatTransMidSpeed = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.75f,
                mResources.getDisplayMetrics());
        mFloatTransFastSpeed = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f,
                mResources.getDisplayMetrics());
    }

控件在展示中,生命周期变化,影响控件展示效果,重新计算控件的基本属性。
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mCenterX = mTotalWidth / 2;
        mCenterY = mTotalHeight / 2;
        mSrcRect = new Rect();
        mDestRect = new Rect(0, 0, mTotalWidth, mTotalHeight);

        mStarOneSrcRect = new Rect(0, 0, mStarOneWidth, mStarOneHeight);
        mStarTwoSrcRect = new Rect(0, 0, mStarTwoWidth, mStarTwoHeight);
        mStarThreeSrcRect = new Rect(0, 0, mStarThreeWidth, mStarThreeHeight);
        Log.i(TAG, "mTotalWidth=" + mTotalWidth + "---2-mTotalHeight=" + mTotalHeight);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        initParticleInfo();
    }

  /**
     * 初始化所有粒子数据
     */
    private void initParticleInfo() {
        Particle starInfo = null;
        Random random = new Random();
        for (int i = 0; i < mFloatCount; i++) {
            // 获取星星大小比例
            float starSize = initParticleSize(0.4f, 0.8f);
            //小球的坐标
            float[] starLocation = STAR_LOCATION[i];
            starInfo = new Particle();
            // 初始化星星大小
            starInfo.sizePercent = starSize;
            // 初始化漂浮速度
            int randomSpeed = random.nextInt(3);
            switch (randomSpeed) {
                case 0:
                    starInfo.speed = mFloatTransLowSpeed;
                    break;
                case 1:
                    starInfo.speed = mFloatTransMidSpeed;
                    break;
                case 2:
                    starInfo.speed = mFloatTransFastSpeed;
                    break;
                default:
                    starInfo.speed = mFloatTransMidSpeed;
                    break;
            }
            // 初始化星星透明度
            starInfo.alpha = initParticleSize(0.3f, 0.8f);
            // 初始化星星位置
            starInfo.xLocation = (int) (starLocation[0] * mTotalWidth);
            starInfo.yLocation = (int) (starLocation[1] * mTotalHeight);
            Log.i(TAG, "xLocation = " + starInfo.xLocation + "--yLocation = "
                    + starInfo.yLocation);
            Log.i(TAG, "stoneSize = " + starSize + "---stoneAlpha = "
                    + starInfo.alpha);
            // 初始化星星位置
            starInfo.direction = getParticleDirection();
            mStarInfos.add(starInfo);
        }
    }

  /**
     * 获取粒子当前运动方向
     *
     * @return
     */
    private int getParticleDirection() {
        int randomInt;
        Random random = new Random();
        if (floatTyep == 100) {
            randomInt = random.nextInt(3);
        } else {
            randomInt = floatTyep;
        }
        int direction = 0;
        switch (randomInt) {
            case 0:
                direction = LEFT;
                break;
            case 1:
                direction = RIGHT;
                break;
            case 2:
                direction = TOP;
                break;
            case 3:
                direction = BOTTOM;
                break;
            case 4:
                direction = FREE_POINT;
                break;
            default:
                break;
        }
        return direction;
    }

    /**
     * 对粒子进行缩放
     *
     * @param start 缩放最小值
     * @param end   缩放最大值
     * @return
     */
    private float initParticleSize(float start, float end) {
        float nextFloat = (float) Math.random();
        if (start < nextFloat && nextFloat < end) {
            return nextFloat;
        } else {
            // 如果不处于想要的数据段,则再随机一次,因为不断递归有风险
            return (float) Math.random();
        }
    }

绘制粒子系统:【先检查粒子位置,再渲染】
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < mStarInfos.size(); i++) {
            Particle starInfo = mStarInfos.get(i);
            drawParticleDynamic(i, starInfo, canvas, paint);
        }
    }

  private void drawParticleDynamic(int count, Particle starInfo,
                                     Canvas canvas, Paint paint) {
        //绘制前重置区域
        resetParticleFloat(starInfo);
        float starAlpha = starInfo.alpha;
        int xLocation = starInfo.xLocation;
        int yLocation = starInfo.yLocation;
        float sizePercent = starInfo.sizePercent;

        xLocation = (int) (xLocation / sizePercent);
        yLocation = (int) (yLocation / sizePercent);

        Bitmap bitmap = null;
        Rect srcRect = null;
        Rect destRect = new Rect();

        mStarOneSrcRect = new Rect(0, 0, mStarOneWidth, mStarOneHeight);
        if (count % 3 == 0) {

            bitmap = mStarOne;
            srcRect = mStarOneSrcRect;
            destRect.set(xLocation, yLocation,
                    xLocation + mStarOneWidth, yLocation
                            + mStarOneHeight);
        } else if (count % 2 == 0) {
            bitmap = mStarThree;
            srcRect = mStarThreeSrcRect;
            destRect.set(xLocation, yLocation, xLocation
                    + mStarThreeWidth, yLocation + mStarThreeHeight);
        } else {
            bitmap = mStarTwo;
            srcRect = mStarTwoSrcRect;
            destRect.set(xLocation, yLocation, xLocation
                    + mStarTwoWidth, yLocation + mStarTwoHeight);
        }
        paint.setAlpha((int) (starAlpha * 255));

        canvas.save();
        canvas.scale(sizePercent, sizePercent);
        canvas.drawBitmap(bitmap, srcRect, destRect, paint);
        canvas.restore();
    }

    /**
     * 区域外检测
     *
     * @param starInfo
     */
    private void resetParticleFloat(Particle starInfo) {
        switch (starInfo.direction) {
            case LEFT:
                if (starInfo.xLocation < -20) {
                    starInfo.xLocation = mTotalWidth;
                } else {
                    starInfo.xLocation -= starInfo.speed;
                }
                break;
            case RIGHT:
                if (starInfo.xLocation > mTotalWidth + 20) {
                    starInfo.xLocation = 0;
                } else {
                    starInfo.xLocation += starInfo.speed;
                }

                break;
            case TOP:
                if (starInfo.yLocation < -20) {
                    starInfo.yLocation = mTotalHeight;
                } else {
                    starInfo.yLocation -= starInfo.speed;
                }
                break;
            case BOTTOM:
                if (starInfo.yLocation > mTotalHeight + 30) {
                    starInfo.yLocation = 0;
                } else {
                    starInfo.yLocation += starInfo.speed;
                }
                break;
            case FREE_POINT://斜线运动,可扩展成为曲线运动
                if (starInfo.yLocation > mTotalHeight + 30) {
                    starInfo.yLocation = 0;
                } else {
                    starInfo.yLocation += starInfo.speed;
                }

                if (starInfo.xLocation < -20) {
                    starInfo.xLocation = mTotalWidth;
                } else {
                    starInfo.xLocation -= starInfo.speed;
                }
                break;
            default:
                break;
        }
    }

管理控制动画的开始结束:
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (isRuning) {
                postInvalidate();
                handler.sendMessageDelayed(Message.obtain(), 50);
            }
        }
    };

    public void startAnimationFloat() {
        isRuning = true;
        handler.sendMessage(Message.obtain());
    }

    public void stopAnimationFloat() {
        isRuning = false;
    }

    public void restartAnimationFloat() {
        startAnimationFloat();
    }

    public void setFloatType(int floatType) {
        this.floatTyep = floatType;
    }

三、展示效果
叠加效果【扩展背景】
使用:


    


public class ShowActivity extends Activity {
    /**
     * 粒子系统
     */
    private ParticleView particleView;
    /**
     * 粒子系统运动类型
     */
    private int type = 100;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_show);
        particleView = (ParticleView) findViewById(R.id.particle_view);
        try {//防止无传参跳入
            type = getIntent().getIntExtra("type", 100);
        } catch (Exception e) {
            e.printStackTrace();
        }
        particleView.setFloatType(ParticleView.DEFAULT_TYPE);
        switch (type) {
            case 1:
                particleView.setFloatType(ParticleView.DEFAULT_TYPE);
                break;
            case 2:
                particleView.setFloatType(ParticleView.FREE_POINT);
                break;
            case 3:
                particleView.setFloatType(ParticleView.TOP);
                break;
            case 4:
                particleView.setFloatType(ParticleView.BOTTOM);
                break;
            case 5:
                particleView.setFloatType(ParticleView.LEFT);
                break;
            case 6:
                particleView.setFloatType(ParticleView.RIGHT);
                break;
        }
        particleView.startAnimationFloat();
    }
}


源码


百花开时我不开,我若开时百花杀,若与西风战一场,满城尽带黄金甲! 

你可能感兴趣的:(Android进阶等级二)