用SurfaceView实现落花动画效果

上篇文章 Android子线程真的不能刷新UI吗?(一)复现异常 中可以看出子线程更新main线程创建的View,会抛出异常。SurfaceView不依赖main线程,可以直接使用自己的线程控制绘制逻辑。具体代码怎么实现了?

这篇文章用SurfaceView实现落花动画效果。

效果展示

录像帧率低,实际效果比下面的流畅。

SurfaceView控制原理

背景图片通过:android:background="@drawable/bg"控制,Activity布局如下:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    tools:context=".ShowFlowerActivity">

    <com.exp.showwavedemo.FlowerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/bg" />
</LinearLayout>

小红花下落效果原理是:开启一个子线程,每80毫秒增加一朵小红花,并绘制一次。

完整代码

整个demo上传到gitee上了,要下载图片去gitee,地址:https://gitee.com/zhagnjinaaaa/show-flower-demo

FlowerView.java

java 复制代码
package com.exp.showflowerdemo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

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

public class FlowerView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    private SurfaceHolder mHolder;
    private boolean mFlag = true;//绘制小花线程的开关标志
    private ArrayList<PointF> mFlowerList;//小花点的坐标集合
    private Random mRandom;//负责随机数生成
    private Bitmap mBitmap;//小花的图案

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

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

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

    private void init() {
        mHolder = getHolder();
        mHolder.addCallback(this);
        //设置背景透明
        this.setZOrderOnTop(true);
        mHolder.setFormat(PixelFormat.TRANSLUCENT);

        mFlowerList = new ArrayList<>();
        mRandom = new Random();

        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_small_hua_15);

    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mFlag = true;
        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mFlowerList.clear();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mFlag = false;
    }

    @Override
    public void run() {
        while (mFlag) {
            try {
                Thread.sleep(80);
                Canvas canvas = mHolder.lockCanvas();
                PointF pointF = null;
                //清屏操作
                if (canvas != null) {
                    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                } else {
                    continue;
                }
                for (PointF point : mFlowerList) {
                    pointF = point;
                    canvas.drawBitmap(mBitmap, pointF.x, pointF.y, null);
                    int i = mRandom.nextInt(getHeight() / 50) + getHeight() / 50;//修改雨滴线的纵坐标,使其看起来在下雨
                    pointF.y = pointF.y + i;
                }
                mHolder.unlockCanvasAndPost(canvas);
                addFlower();
                if (mFlowerList.size() > 0 && pointF != null && pointF.y >= getHeight()) {
                    mFlowerList.remove(pointF);
                }

            } catch (Exception e) {
            }
        }
    }

    /**
     * 添加花朵
     */
    private void addFlower() {
        PointF point = new PointF();
        point.x = mRandom.nextInt(getWidth());
        point.y = -mBitmap.getHeight();
        mFlowerList.add(point);
    }
}
相关推荐
沐怡旸8 分钟前
【底层机制】【Android】Binder 驱动作用
android·面试
沐怡旸8 分钟前
【底层机制】【Socket】本地Socket VS 普通 Socket?Zygote为什么使用本地Socket?
android
沐怡旸8 分钟前
【底层机制】【Android】详解 Zygote
android·面试
灯火不休➴1 小时前
安卓 ContentProvider 详解:跨应用数据共享的核心方案
android
沐怡旸1 小时前
【底层机制】【Android】Android 系统的启动流程
android
limuyang21 小时前
【http3/quic】cronet 已经原生集成在Android内啦!还不快来开开眼!
android·http·google
乌萨奇也要立志学C++2 小时前
【Linux】Ext系列文件系统 从磁盘结构到文件存储的原理剖析
android·linux·缓存·1024程序员节
宋发元3 小时前
IPhone 17 Pro Max拍摄专业画质视频教程
android·gradle·iphone
出门吃三碗饭5 小时前
如何在LLM大语言模型上微调来优化数学推理能力?
android·人工智能·语言模型
shaominjin1236 小时前
Android访问OTG文件全解析:从连接到操作的完整指南Android系统访问U盘的实现机制与操作指南
android