Android 下的花瓣飘落视图

本文讲述了作者如何在项目中实现一个具有旋转和缩放功能的花瓣飘落动画,从网上找到的JavaScript代码转换为Java,并针对特定需求进行了修改,包括花瓣的起始位置和扇形区域,最终通过自定义Petal和PetalView类实现动画效果。
摘要由CSDN通过智能技术生成

由于项目需要,在项目中做一个飘浮在所有视图顶部的桃花花瓣飘落的动画效果,网上找了和多,基本都是仅飘落效果,很少有带花瓣旋转缩放的,看起来很死板,好不容易在网上看到一个用JS写的基于HTML的代码,将其转为JAVA,运行起来了,效果也不错,但是我的项目要求的是花瓣从右上角为圆心,宽度的1/4为半径,-45°到45°为扇形区域开始飘落的效果,JS的这个却是从左上角飘落,并且不是指定扇形区域,改别人的代码吧,很麻烦,想想还是自己写一个,于是参考JS代码后花了1天时间自己写了一个。
共两个类,源码如下:
Petal.java类,花瓣绘制过程类。

package com.racer.smart.pro.view.petal;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PointF;

import java.util.List;
import java.util.Random;
/**花瓣对象*/
public class Petal {
    /**花瓣图片*/
    private Bitmap bitmapPetal;
    /**花瓣宽高*/
    private int petalWidth, petalHeight;
    /**图片矩阵*/
    private Matrix matrix;
    /**花瓣位置*/
    private PointF point=new PointF();
    /**花瓣下降速度*/
    private PointF speed=new PointF();
    /**花瓣旋转角度*/
    private float rotationAngle;
    /** 花瓣X或Y轴缩放比例*/
    private float scale;
    /**缩小或放大*/
    private boolean isZoomingIn = true;
    /**屏幕或父视图尺寸*/
    private int screenWidth, screenHeight;
    /**随机生成器*/
    private Random random;
    /**活动上下文*/
    private Context ctx;
    /**图片资源列表*/
    private List<Integer> resIdList;

    /**
     * 实例化PetalObject
     * @param ctx 上下文
     * @param resIds 图片资源列表
     * @param canvasWidth 画布或屏幕宽度。如果为0,则取屏幕宽度
     * @param canvasHeight 画布或屏幕高度。如果为0,则取屏幕高度
     */
    public Petal(Context ctx, List<Integer> resIds,int canvasWidth, int canvasHeight) {
        this.ctx=ctx;
        resIdList=resIds;
        if(canvasWidth>0){
            screenWidth=canvasWidth;
        }else{
            screenWidth=ctx.getResources().getDisplayMetrics().widthPixels;
        }
        if(canvasHeight>0){
            screenHeight=canvasHeight;
        }else{
            screenHeight=ctx.getResources().getDisplayMetrics().heightPixels;
        }
        matrix = new Matrix();
        random = new Random();
        resetPetal();    //重置花瓣
    }
    /**
     * 绘制花瓣到画布
     * @param canvas
     */
    public void draw(Canvas canvas) {
        if(bitmapPetal!=null) {
            matrix.reset();  // 重置矩阵
            // 设置旋转和缩放
            matrix.postRotate(-rotationAngle, petalWidth / 2, petalHeight / 2);
            matrix.postScale(1, scale);   // 设置y轴缩放
            matrix.postTranslate(point.x, point.y);  //设置Bitmap的位置
            // 在画布上绘制Bitmap
            canvas.drawBitmap(bitmapPetal, matrix, null);
            // 如果 Bitmap 飘出屏幕,重置位置
            if (point.x + petalWidth < 0 || point.y - petalHeight > screenHeight) {
                resetPetal();
            }
            movePetal();
        }
    }
    /**移动花瓣位置*/
    private void movePetal(){
        if (isZoomingIn) {
            scale += 0.01f;
            if (scale >= 1.0f) {
                isZoomingIn = false;
            }
        } else {
            scale -= 0.01f;
            if (scale <= 0.0f) {
                isZoomingIn = true;
            }
        }
        // 更新 Bitmap 的位置
        point.x += speed.x;
        point.y += speed.y;
        // 更新旋转角度。在现有角度上增加一个6以内的随机值
        rotationAngle += random.nextInt(6);
        rotationAngle%=360f;  //将旋转角度限制在0-359之间
    }

    /**重置花瓣。初始化或下落超出屏幕后都需要重置*/
    private void resetPetal() {
        if(resIdList!=null && resIdList.size()>0) {
            int position = random.nextInt(resIdList.size());    //随机得到图片资源
            //限制资源ID不得大于资源列表长度,防止随机数超出范围
            if(position>=resIdList.size()) {
                position = resIdList.size() - 1;
            }
            int resId = resIdList.get(position);
            // 加载图片
            bitmapPetal = BitmapFactory.decodeResource(ctx.getResources(), resId);
            if (bitmapPetal != null) {
                petalWidth = bitmapPetal.getWidth();
                petalHeight = bitmapPetal.getHeight();
            }
            PointF center = new PointF(screenWidth, 0);   // 圆心坐标
            // 最小和最大半径
            float minRadius = screenWidth / 10f; // 屏幕宽度的1/10
            float maxRadius = screenWidth / 3f; // 屏幕宽度的1/3
            // 随机半径,限制在minRadius与maxRadius之间
            float radius = minRadius + (float) (Math.random() * (maxRadius - minRadius));
            // 随机角度。范围从-45度到45度
            double angle = Math.random() * Math.PI * 90 - Math.PI * 45;
            // 通过随机角度和随机半径计算点的坐标
            point.x = (float) (center.x + radius * Math.cos(angle));
            point.y = (float) (center.y + radius * Math.sin(angle));
            //初始化随机旋转角度
            rotationAngle = random.nextInt(360);
            //随机初始化花瓣缩放比例
            scale = random.nextFloat();
            // 随机初始化花瓣x方向速度
            speed.x = -(random.nextInt(4) + 0.5f);
            // 随机初始化花瓣y方向速度
            speed.y = random.nextInt(2) + 1;
        }
    }
}```
PetalView,java花瓣视图类,用于将多个Petal构建的花瓣实体绘制到视图中显示出来。

```java
package com.racer.smart.pro.view.petal;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 花瓣视图
 * 调用步骤:
 * <ul>
 *     <li>1、实例化视图对象</li>
 *     <li>2、使用{@link #addImage(int)}方法添加花瓣图像</li>
 *     <li>3、调用{@link #startThread()}方法启动绘制线程</li>
 *     <li>4、释放视图,停止绘制线程</li>
 * </ul>
 */
public class PetalView extends SurfaceView implements SurfaceHolder.Callback {
    /**SurfaceView的子类,用于绘制花瓣动画*/
    private SurfaceHolder surfaceHolder;
    /**绘图线程*/
    private DrawThread drawThread;
    /**花瓣对象列表*/
    private List<Petal> petalList;
    /**花瓣图像资源列表*/
    private List<Integer> petalImgList;
    /**画布或屏幕宽度*/
    private int canvasWidth;
    /**画布或屏幕高度*/
    private int canvasHeight;
    /**需要创建的花瓣数量*/
    private int petalCount=40;   //默认40个
    /**创建全部花瓣。true 创建全部,false 延时逐个创建*/
    private boolean isCreateAll=true;
    /**延时时长*/
    private long delay=0;
    /**随机生成器*/
    private Random random;
    private Handler handler;
    public PetalView(Context context) {
        this(context, null);
    }
    public PetalView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }
    public PetalView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr,0);
    }
    public PetalView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        drawThread.stopThread();
        //handler = null;
    }
    @Override
    public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
        canvasWidth = getWidth();
        canvasHeight = getHeight();
        handler=new Handler();
        createPetals();
        drawThread.start();  // 启动绘制线程
    }
    @Override
    public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {
    }
    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
        drawThread.stopThread();  // 当Surface销毁时被调用,停止绘制线程
    }
    /**
     * 启动绘制线程。
     * 这个方法必须在添加完花瓣图像,并且绘图表面创建完成后才能调用。
     */
    public void startThread() {
        surfaceHolder = getHolder();
        surfaceHolder.addCallback(this);
        drawThread = new DrawThread();
    }
    public void stopThread() {
        drawThread.stopThread();
    }

    /**
     * 添加花瓣图像资源ID,运行前调用添加
     * @param resId
     */
    public void addImage(int resId) {
        petalImgList.add(resId);
    }
    /**
     * 设置花瓣数量
     * @param petalCount
     * @param refresh 是否刷新,如果在运行过程中设置花瓣数量,需要刷新才会增加多出来的花瓣
     */
    public void setPetalCount(int petalCount,boolean refresh) {
        this.petalCount = petalCount;
        if(refresh){
            createPetals();   //创建多出来的花瓣
        }
    }

    /**
     * 运行开始即将所有花瓣创建完成
     * @param createAll
     */
    public void setCreateAll(boolean createAll) {
        isCreateAll = createAll;
    }
    /**初始化*/
    private void init() {
        petalList=new ArrayList<>();
        petalImgList=new ArrayList<>();
        setZOrderOnTop(true);        //放置在顶部
        getHolder().setFormat(PixelFormat.TRANSLUCENT);  //透明框架
    }
    /**创建花瓣列表*/
    private void createPetals() {
        if(isCreateAll) {
            //需要创建的花瓣数=花瓣总数-已经存在的花瓣数
            int createPetalCount = petalCount - petalList.size();
            for (int i = 0; i < createPetalCount; i++) {
                // 创建新的花瓣
                Petal petal = new Petal(getContext(), petalImgList, canvasWidth, canvasHeight);
                petalList.add(petal); // 添加到花瓣列表
            }
        }else{
            if(random==null)
                random=new Random();
            handler.post(runnable);
        }
    }
    /**运行单个花瓣创建*/
    private  Runnable runnable=new Runnable() {
        @Override
        public void run() {
            if(petalList.size()<petalCount-1) {
                // 创建新的花瓣
                Petal petal = new Petal(getContext(), petalImgList, canvasWidth, canvasHeight);
                petalList.add(petal); // 添加到花瓣列表
                if(petalList.size()<petalCount-1) {
                    delay = random.nextInt(400);
                    handler.postDelayed(this, delay);
                }
            }
        }
    };
    /**绘制线程*/
    private class DrawThread extends Thread {
        private boolean isRunning = true;
        @Override
        public void run() {
            while (isRunning) {
                Canvas canvas = surfaceHolder.lockCanvas(); // 锁定画布以进行绘制
                if (canvas != null) {
                    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);   // 用透明色清空画布
                    for (int i = 0; i < petalList.size(); i++) {  //循环绘制全部花瓣
                        Petal petal = petalList.get(i);
                        petal.draw(canvas);  // 绘制花瓣
                    }
                    surfaceHolder.unlockCanvasAndPost(canvas);  // 解锁画布并提交绘制结果
                }
            }
        }
        public void stopThread() {
            isRunning = false;
        }
    }
}

类中为了在频繁绘制过程中不影响主线程运行,重写了SurfaceView类来绘制花瓣。效果如下:

花瓣飘落

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值