Android实现雪花特效自定义view

news2024/12/23 1:40:41

一、前言

这个冬天,老家一直没有下雨, 正好圣诞节,就想着制作一个下雪的特效。
圣诞祝福:平安夜,舞翩阡。雪花飘,飞满天。心与心,永相伴。
圣诞节是传统的宗教节日,对于基 督徒,那是庆祝耶稣的诞生,纪念耶稣和发扬基督精神。现在整个西方社会都在过圣诞节,像许多宗教节日一样,它已经越来越民俗化了。
尽管如此,圣诞节依然倍受尊重。人们在圣诞快乐中怀有对耶稣的敬仰,欢乐的节庆里含有庄严肃穆的神念。欢度圣诞佳节的人都不拒绝耶稣的教诲,要仁爱、善良、诚实、忍耐、感恩……在信神的国度,不是基 督徒的人们,也都知道人应该感恩,心存谢意。对需要帮助的人给予关爱;对他人的帮助给予感谢。这是西方社会价值观的一部份,而不是说圣诞夜就只是一家坐在壁炉前,共进有火鸡或烤鹅的圣诞大餐或是冬季里开的一个最热闹的大派对。

二、创意名

Android实现雪花特效自定义view

三、效果展示

在这里插入图片描述

四、实现步骤

1.创建一个view,里面加载雪花类的集合,有一个死循环线程,一直执行动画

 public class myRunnable implements Runnable {
        @Override
        public void run() {
            while (true){
                Canvas canvas =null;
                try {
                    synchronized (holder){
                        canvas = holder.lockCanvas();
                        //清除画布
                        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                        for (Snowflake snowflake :list){
                            snowflake.draw(canvas);
                            snowflake.update();
                        }
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    if (canvas!=null){
                        holder.unlockCanvasAndPost(canvas);
                    }
                }

            }
        }
    }

这样的话,可以让所有的雪花图片动起来

2.创建雪花类,其实就是一个bitmap,然后设置不同尺寸和动画

这步相对来说简单一些,其实就是将bitmap绘制到画布上面

public void reset(){
        size = randomizer.randomInt(sizeMinInPx, sizeMaxInPx, true);
        if (image!=null){
            if (bitmap==null){
                bitmap = Bitmap.createScaledBitmap(image, size, size, false);
            }
        }
        float speed =  (float)(size - sizeMinInPx) / (sizeMaxInPx - sizeMinInPx) * (speedMax - speedMin) + speedMin;
        double angle = Math.toRadians(randomizer.randomDouble(angleMax) * randomizer.randomSignum());
        speedX = speed* Math.sin(angle);
        speedY = speed* Math.cos(angle);

        alpha = randomizer.randomInt(alphaMin, alphaMax, false);
        paint.setAlpha(alpha);

        positionX = randomizer.randomDouble(parentWidth);

        this.positionY=randomizer.randomDouble(parentHeight);
        if (!alreadyFalling){
            this.positionY = this.positionY-parentHeight-size;
        }
    }

3.界面展示

实现manifest加载视图即可

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000"
    tools:context="com.marvin.snowfall_master.MainActivity">

    <com.itbird.SnowfallView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/sf_snow"
        app:snowflakesNum="200"
        app:snowflakeAlphaMin="150"
        app:snowflakeAlphaMax="255"
        app:snowflakeAngleMax="5"
        app:snowflakeSizeMin="2dp"
        app:snowflakeSizeMax="40dp"
        app:snowflakeSpeedMin="2"
        app:snowflakeSpeedMax="10"
        app:snowflakesFadingEnabled="true"
        app:snowflakesAlreadyFalling="false"
        app:snowflakeImage="@mipmap/snowflake"
        />

</android.support.constraint.ConstraintLayout>

五、编码实现

界面类SnowfallView

public class SnowfallView extends SurfaceView implements SurfaceHolder.Callback {

    private int DEFAULT_SNOWFLAKES_NUM = 200;
    private int DEFAULT_SNOWFLAKE_ALPHA_MIN = 150;
    private int DEFAULT_SNOWFLAKE_ALPHA_MAX = 250;
    private int DEFAULT_SNOWFLAKE_ANGLE_MAX = 10;
    private int DEFAULT_SNOWFLAKE_SIZE_MIN_IN_DP = 2;
    private int DEFAULT_SNOWFLAKE_SIZE_MAX_IN_DP = 8;
    private int DEFAULT_SNOWFLAKE_SPEED_MIN = 2;
    private int DEFAULT_SNOWFLAKE_SPEED_MAX = 8;
    private boolean DEFAULT_SNOWFLAKES_FADING_ENABLED = false;
    private boolean DEFAULT_SNOWFLAKES_ALREADY_FALLING = false;

    private int snowflakesNum;
    private Bitmap snowflakeImage;
    private int snowflakeAlphaMin;
    private int snowflakeAlphaMax;
    private int snowflakeAngleMax;
    private int snowflakeSizeMinInPx;
    private int snowflakeSizeMaxInPx;
    private int snowflakeSpeedMin;
    private int snowflakeSpeedMax;
    private boolean snowflakesFadingEnabled;
    private boolean snowflakesAlreadyFalling;

    //雪花类集合
    private ArrayList<Snowflake> list =new ArrayList<>();

    private SnowfallView.myRunnable myRunnable = new myRunnable();
    private Thread myThread;
    private SurfaceHolder holder;


    public SnowfallView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context,attrs);
    }

    private void init(Context context, AttributeSet attributeSet) {
        TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.SnowfallView);
        snowflakesNum = typedArray.getInt(R.styleable.SnowfallView_snowflakesNum, DEFAULT_SNOWFLAKES_NUM);
        snowflakeImage = drawable2Bitmap(typedArray.getDrawable(R.styleable.SnowfallView_snowflakeImage));
        snowflakeAlphaMin = typedArray.getInt(R.styleable.SnowfallView_snowflakeAlphaMin, DEFAULT_SNOWFLAKE_ALPHA_MIN);
        snowflakeAlphaMax = typedArray.getInt(R.styleable.SnowfallView_snowflakeAlphaMax, DEFAULT_SNOWFLAKE_ALPHA_MAX);
        snowflakeAngleMax = typedArray.getInt(R.styleable.SnowfallView_snowflakeAngleMax, DEFAULT_SNOWFLAKE_ANGLE_MAX);
        snowflakeSizeMinInPx = typedArray.getDimensionPixelSize(R.styleable.SnowfallView_snowflakeSizeMin, dp2Px(DEFAULT_SNOWFLAKE_SIZE_MIN_IN_DP));
        snowflakeSizeMaxInPx = typedArray.getDimensionPixelSize(R.styleable.SnowfallView_snowflakeSizeMax, dp2Px(DEFAULT_SNOWFLAKE_SIZE_MAX_IN_DP));
        snowflakeSpeedMin = typedArray.getInt(R.styleable.SnowfallView_snowflakeSpeedMin, DEFAULT_SNOWFLAKE_SPEED_MIN);
        snowflakeSpeedMax = typedArray.getInt(R.styleable.SnowfallView_snowflakeSpeedMax, DEFAULT_SNOWFLAKE_SPEED_MAX);
        snowflakesFadingEnabled = typedArray.getBoolean(R.styleable.SnowfallView_snowflakesFadingEnabled, DEFAULT_SNOWFLAKES_FADING_ENABLED);
        snowflakesAlreadyFalling = typedArray.getBoolean(R.styleable.SnowfallView_snowflakesAlreadyFalling, DEFAULT_SNOWFLAKES_ALREADY_FALLING);
        typedArray.recycle();

        holder = this.getHolder();
        holder.addCallback(this);

        //设置背景为透明
        setZOrderOnTop(true);
        holder.setFormat(PixelFormat.TRANSPARENT);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //获取雪花集合
        for (int i=0;i<snowflakesNum;i++){
            list.add(new Snowflake(w, h, snowflakeImage, snowflakeAlphaMin, snowflakeAlphaMax
                    , snowflakeAngleMax, snowflakeSizeMinInPx, snowflakeSizeMaxInPx, snowflakeSpeedMin, snowflakeSpeedMax
                    , snowflakesFadingEnabled, snowflakesAlreadyFalling));
        }
    }

    @Override
    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);
        if (changedView==this&&visibility==GONE){
            //初始化雪花类
            try {
                for (Snowflake snowflake :list){
                    snowflake.reset();
                }
            }catch (Exception e){
                e.printStackTrace();
            }

        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (isInEditMode()){
            return;
        }
    }

    /**
     * dp转px
     * @param dp
     * @return
     */
    private int dp2Px(int dp){
        return (int) (dp*getResources().getDisplayMetrics().density);
    }

    /**
     * drawble转Bitmap
     * @param drawable
     * @return
     */
    private Bitmap drawable2Bitmap(Drawable drawable){
        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0,0,drawable.getIntrinsicWidth(),drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        return bitmap;
    }

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        if (myThread==null){
            myThread = new Thread(myRunnable);
        }
        if(!myThread.isAlive()){
            myThread.start();
        }

    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        if (myThread!=null){
            myThread.interrupt();
        }
    }

    public class myRunnable implements Runnable {
        @Override
        public void run() {
            while (true){
                Canvas canvas =null;
                try {
                    synchronized (holder){
                        canvas = holder.lockCanvas();
                        //清除画布
                        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                        for (Snowflake snowflake :list){
                            snowflake.draw(canvas);
                            snowflake.update();
                        }
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    if (canvas!=null){
                        holder.unlockCanvasAndPost(canvas);
                    }
                }

            }
        }
    }
}

雪花类Snowflake


public class Snowflake {

    private int parentWidth;
    private int parentHeight;
    private int alphaMin;
    private int alphaMax;
    private int angleMax;
    private int sizeMinInPx;
    private int sizeMaxInPx;
    private int speedMin;
    private int speedMax;
    private Bitmap image;
    private boolean fadingEnabled;
    private boolean alreadyFalling;

    private int size = 0 ;
    private int alpha = 255;
    private Bitmap bitmap = null;
    private double speedX= 0.0;
    private double speedY = 0.0;
    private double positionX = 0.0;
    private double positionY = 0.0;
    private final Randomizer randomizer;

    private Paint paint;

    public Snowflake(int parentWidth, int parentHeight, Bitmap image
            ,int alphaMin,int alphaMax,int angleMax,int sizeMinInPx,int sizeMaxInPx,
                    int speedMin,int speedMax,boolean fadingEnabled,boolean alreadyFalling ){
        this.parentWidth = parentWidth;
        this.parentHeight = parentHeight;
        this.alphaMin = alphaMin;
        this.alphaMax = alphaMax;
        this.angleMax = angleMax;
        this.sizeMinInPx = sizeMinInPx;
        this.sizeMaxInPx = sizeMaxInPx;
        this.speedMin = speedMin;
        this.speedMax = speedMax;
        this.image = image;
        this.fadingEnabled=fadingEnabled;
        this.alreadyFalling=alreadyFalling;

        randomizer = new Randomizer();
        initPaint();
        reset();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.rgb(255,255,255));
        paint.setStyle(Paint.Style.FILL);
    }

    public void reset(double positionY){
        size = randomizer.randomInt(sizeMinInPx, sizeMaxInPx, true);
        if (image!=null){
            if (bitmap==null){
                bitmap = Bitmap.createScaledBitmap(image, size, size, false);
            }
        }
        float speed = (float)(size - sizeMinInPx) / (sizeMaxInPx - sizeMinInPx) * (speedMax - speedMin) + speedMin;

        double angle = Math.toRadians(randomizer.randomDouble(alphaMax) * randomizer.randomSignum());
        if (angle<-1||angle>1){
            angle = 0;
        }
        speedX = speed* Math.sin(angle);
        speedY = speed* Math.cos(angle);
        alpha = randomizer.randomInt(alphaMin, alphaMax, false);
        paint.setAlpha(alpha);

        positionX = randomizer.randomDouble(parentWidth);

        this.positionY = positionY;
    }

    public void reset(){
        size = randomizer.randomInt(sizeMinInPx, sizeMaxInPx, true);
        if (image!=null){
            if (bitmap==null){
                bitmap = Bitmap.createScaledBitmap(image, size, size, false);
            }
        }
        float speed =  (float)(size - sizeMinInPx) / (sizeMaxInPx - sizeMinInPx) * (speedMax - speedMin) + speedMin;
        double angle = Math.toRadians(randomizer.randomDouble(angleMax) * randomizer.randomSignum());
        speedX = speed* Math.sin(angle);
        speedY = speed* Math.cos(angle);

        alpha = randomizer.randomInt(alphaMin, alphaMax, false);
        paint.setAlpha(alpha);

        positionX = randomizer.randomDouble(parentWidth);

        this.positionY=randomizer.randomDouble(parentHeight);
        if (!alreadyFalling){
            this.positionY = this.positionY-parentHeight-size;
        }
    }

    public void update(){
        positionX = positionX+speedX;
        positionY = positionY+speedY;
        if (positionY>parentHeight){
            positionY = -(double)size;
            reset(positionY);
        }
        if (fadingEnabled){
            paint.setAlpha((int) (alpha * ((float) (parentHeight - positionY) / parentHeight)));
        }
    }

    public void draw(Canvas canvas){
        if (bitmap!=null){
            canvas.drawBitmap(bitmap,(float)positionX,(float)positionY,paint);
        }else {
            canvas.drawCircle((float)positionX,(float)positionY,(float)size,paint);
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/115898.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端自学你还在浪费时间吗?

其实最主要不是学的过程&#xff0c;而是学完后&#xff0c;你有没有把今天的练习题自己在重新敲个2&#xff0c;3遍&#xff0c;这样印象就会更加深刻&#xff0c;以后自己写代码的时候也会更加的得心应手。 手抄笔记让我打好了HTML基础和良好的CSS能力&#xff0c;当然这不一…

Cesium打包入门(gulp与esbuild)

本文针对Cesium源码包的打包工具gulp和esbuild进行了初步探讨&#xff0c;属于入门篇。 首先简要介绍采用gulpesbuild如何为多个源代码文件打包成一个单独文件&#xff0c;然后介绍了下Cesium中的源码包的结构&#xff0c;并简要分析了其打包的相关函数。 本文编译环境IDE使用…

【并发编程学习】一、线程的基本认识

一、线程的基本认识 1.1线程的基本介绍 线程是什么&#xff1f; 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中&#xff0c;是进程中的实际运行单位。 为什么会有多线程&#xff1f; ①因为其是CPU的最小调度单位&#xff0c;所以在多核CPU中&#xff0c…

圣诞节,深圳街头有点冷清了~

正文大家好&#xff0c;我是bug菌~今天是圣诞节&#xff0c;这也是我新冠康复的第二周了吧&#xff0c;还有点小咳嗽&#xff0c;伴随有点鼻炎&#xff0c;不过这周已经上了三天班了&#xff0c;整体感觉还算好吧&#xff0c;毕竟我嘴巴不硬&#xff0c;也比较低调不嚣张&#…

底层硬件创新夯实算力、应用创新贴近业务:英特尔至强助力下的VR医疗培训系统

早在1935年&#xff0c;科幻小说家斯坦利温鲍姆的小说《皮格马利翁的眼镜》中&#xff0c;就构想了一款实现虚拟现实&#xff08;VR&#xff09;的眼镜。近年来&#xff0c;除游戏、娱乐等大众熟知的应用场景外&#xff0c;VR逐渐涉足医疗、教育、生产制造等各种领域。 以医疗…

LeetCode-1759-统计同构子字符串的数目

1、数学 我们可以使用数学进行分析&#xff1a;每当出现连续的nnn个字符时&#xff0c;我们最终将其合在一起进行计算个数。显然我们可以获得的同构子字符串的个数应为n(n1)2\frac{n \times (n1)}{2}2n(n1)​。因此我们只需要遍历整个字符串&#xff0c;分别统计连续出现的字符…

57岛屿数量-61全排列 最长递增路径

57岛屿数量 矩阵中多处聚集着1&#xff0c;要想统计1聚集的堆数而不重复统计&#xff0c;那我们可以考虑每次找到一堆相邻的1&#xff0c;就将其全部改成0&#xff0c;而将所有相邻的1改成0的步骤又可以使用深度优先搜索&#xff08;dfs&#xff09;&#xff1a;当我们遇到矩阵…

AtCoder Beginner Contest 283 (A~F)

比赛名称&#xff1a;UNIQUE VISION Programming Contest 2022 Winter(AtCoder Beginner Contest 283) 比赛链接&#xff1a;AtCoder Beginner Contest 283 A - Power 题意&#xff1a; 求A^B(1<A,B<9) 要注意这个int强制转换&#xff0c;不然9^9输出结果时387420489&…

python根据json数据画疫情分布地图

目录 一.基础地图使用 1.掌握使用pyecharts构建基础的全国地图可视化图表 二.疫情地图——国内疫情地图 1.案例效果 代码 三.疫情地图——省级疫情地图 四.数据集 注&#xff1a;数据集在文章最后 一.基础地图使用 1.掌握使用pyecharts构建基础的全国地图可视化图表 演…

Learning to Segment Every Thing

摘要 现有的目标实例分割方法要求所有训练样本都具有分割mask标注。然而&#xff0c;标注新的类别是非常费劲的&#xff0c;因此这将实例分割模型的应用范围限制在100个左右的有标注的类。本文的目的是提出一种新的部分监督的训练模型&#xff0c;以及一种新的权重传递函数&am…

洛谷【算法1-7】搜索刷题——优化、错题

文章目录[USACO1.5]八皇后 Checker Challenge题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示思路搜索框架代码位运算优化lowbit运算思路kkksc03考前临时抱佛脚题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示思路代码吃奶酪题目描述输入格式输…

C++ STL 之可变长数组 Vector 详解

文章目录Part.I AttentionChap.I 注意事项Chap.II 操作技巧Part.II FunctionChap.I 构造函数Chap.II 增加函数Chap.III 删除函数Chap.IV 遍历函数Chap.V 判断/大小/其他函数Part.III CodePart.I Attention Chap.I 注意事项 使用vector需要注意的地方&#xff1a; 加引用#inc…

分布式与微服务系列 - SpringBoot + Zookeeper集群 + Dubbo分布式托管(提供者、消费者)+ Nginx反向代理

一、前言 本内容仅用于个人学习笔记&#xff0c;如有侵扰&#xff0c;联系删除 再搭建集群项目前&#xff0c;请先学习相关技术的知识&#xff1a; 分布式与微服务系列 - Dubbo分布式与微服务系列 - Zookeeper上篇&#xff1a;入门到精通参考文档&#xff1a;分布式与微服务…

状态机——奇葩的状态表示

状态机——奇葩的状态表示 简述 在动态规划中&#xff0c;遇到有一个点有多个状态&#xff0c;混在一起无法表示&#xff0c;那么就可以把状态分开&#xff0c;并且构造出不同状态之间的转移关系&#xff0c;然后再求出状态转移方程&#xff0c;之后就OK了。 题目 1049. 大…

GNN algorithms(3): Tri-party Deep Network Representation

目录 Tri-party Deep Network Representation Essence Thinking Abstract Introduction Problem Definition Tri-DNR pipelines Model Architecture Tri-party Deep Network Representation Essence 1) Deepwalk提取graph structure信息&#xff0c;即structural nod…

MyBatis 一文基础总结

MyBatis 一文基础总结老师的随堂上课笔记&#xff0c; 供参考~1.框架概述1.1 软件开发常用结构1.1.1三层架构三层的处理请求的交互&#xff1a;用户---> 界面层--->业务逻辑层--->数据访问层--->DB 数据库1.1.2 常用框架常见的 J2EE 中开发框架&#xff1a;MyBatis…

adworld-web-inget-20221226

inget 题目来源: 宜兴网信办 题目描述: 无 题目场景: http://61.147.171.105:51222 http://61.147.171.105:51222/?id1%27%20%20or%2011%20– Please enter ID,and Try to bypass nice : congratulations Flag Is : cyberpeace{3df1eecfb5f794d6a94eba429f7e2846} ┌…

【NI Multisim 14.0编辑环境——菜单栏】

目录 序言 一、菜单栏 &#x1f349;1.文件 &#x1f349;2.编辑 &#x1f349;3.视图 &#x1f349; 4.绘制 &#x1f349;5.MCU&#xff08;微控制器&#xff09;菜单 &#x1f349; 6.仿真 &#x1f349; 7.转移 &#x1f349; 8.工具 &#x1f349; 9.报告 &…

录屏有声音吗?电脑如何录屏有声音?图文教程来了!

无论是学习、办公还是娱乐&#xff0c;都需要屏幕录制。许多人在录制视频后发现视频没有发出声音。录屏有声音吗&#xff1f;电脑如何录屏有声音&#xff1f;一个相对简单的方法是在使用电脑的屏幕录制功能时检查是否打开录制屏幕的声音。此外&#xff0c;如果您担心每次都需要…

这应该是全网最全的MySQL数据库的常用命令了吧

目录 前言 数据库的创建 数据表的操作 表数据的增删查改 分组与函数查询 分组与函数查询 运算符&#xff1a;数学运算符 连接查询 前言 今天给大家点来MySQL数据库的常用命令总结&#xff0c;这应该是全网最详细&#xff0c;最实用的数据库命令总结了&#xff0c;因为命…