【Android】使用自定义view实现一个简单下雪的效果

直接看代码吧!!!
我这里用的是kotlin语言(不过应该都差不多,大概

1.创建一个自定义View(SnowView)

class SnowView : View, Runnable {

    lateinit var paint: Paint

    // 圆的半径
    private var circleRadius = 0f

    // 圆的中心X坐标
    private var centerX = 1f

    // 圆的中心Y坐标
    private var centerY = 1f

    //最大速度
    private var maxSpeed = 15

    //最小速度
    private var minSpeed = 5

    //记录时间,每两秒改变一次X轴的方向
    var time = 0L

    //当前view所占的宽度(像素
    private var viewWidth = 0
    //当前view所占的高度(像素
    private var viewHeight = 0

    //线程
    private lateinit var thread: Thread
    //每个图形的X值
    private var listX = mutableListOf<Float>()
    //每个图形的Y值
    private var listY = mutableListOf<Float>()
    //每个图形的透明度
    private var listAlpha = mutableListOf<Int>()
    //每个图形的速度
    private var listSpeed = mutableListOf<Float>()
    //每个图形的X轴初始移动方向
    private var listLeftOrRight = mutableListOf<Boolean>()

    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )


    private fun init() {
        paint = Paint(Paint.ANTI_ALIAS_FLAG)
        paint.color = Color.parseColor("#FFFFFF") // 设置圆的颜色
        paint.style = Paint.Style.FILL // 设置填充样式

        // 初始化圆心位置和半径,这里可以根据需要设置默认值
        circleRadius = 10f // 假设初始半径为10像素

        Log.i("TAG", "init: width::${width}\nheight:::::${height}")

        //开启子线程
        thread = Thread(this@SnowView)
        thread.start()
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas!!.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)

        // 在画布上绘制两百个圆
        for (i in 0..200) {

            //设置随机值
            val random = Random()
            //随机X值
            var randomx = random.nextInt(viewWidth).toFloat()
            //随机Y值
            var randomy = random.nextInt(viewHeight).toFloat()
            //随机透明度
            var randomAlpha = random.nextInt(255)
            //随机速度
            var randomSpeed = minSpeed + random.nextInt(maxSpeed).toFloat()

            //通过角度->转为弧度的值->正弦/余弦的值(抄的
            val angleMax = 10
            var leftOrRight = random.nextBoolean()
            val angle = random.nextDouble() + angleMax

            //将每个图形的信息保存起来(应该弄一个实体类存起来会好一些,这里偷个懒,也更直观一些
            if (listX.size <= 200) {
                //每个图形的X初始值
                listX.add(randomx)
                //每个图形的Y初始值
                listY.add(randomy)
                //每个图形的透明度
                listAlpha.add(randomAlpha)
                //每个图形的速度
                listSpeed.add(randomSpeed)
                //每个图形的X轴初始移动方向
                listLeftOrRight.add(leftOrRight)
            }

            //记录时间,每两秒改变一次X轴的方向
            if (listLeftOrRight.size > 200 && System.currentTimeMillis() - time > 2000) {
                listLeftOrRight[i] = !listLeftOrRight[i]
                time = System.currentTimeMillis()
            }

            //当图形的X轴超出设置View的宽度大小时,重新设置值
            if (listX[i] > viewWidth) {
                listX[i] = randomx
//                Log.e("TAG", "onDraw: x轴超出")
            }

            //当图形的Y轴超出设置View的高度大小时,设置为0f
            if (listY[i] > viewHeight) {
                listY[i] = 0f
//                Log.e("TAG", "onDraw: y轴超出")
            }

            //将正余弦设置上去
            val radians = if (listLeftOrRight[i]) {
                Math.toRadians(-angle)
            } else {
                Math.toRadians(angle)
            }
            listX[i] += listSpeed[i] * sin(radians).toFloat()
            listY[i] += listSpeed[i] * cos(radians).toFloat()

//            listY[i] += listSpeed[i]

            // 设置画笔透明度
            paint.alpha = listAlpha[i]

            //开始绘画
            canvas?.drawCircle(listX[i], listY[i], circleRadius, paint)
        }
    }


    override fun run() {
//        if (centerX > viewWidth) {
//            centerX = 1f
//        }
//
//        if (centerY > viewHeight) {
//            centerY = 1f
//        }
//
//        centerX += 2;
//        centerY += 2;

        //刷新绘画
        invalidate()
//        thread.interrupt()
        postDelayed(this, 50)
    }


    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        thread.interrupt()
        // sleep
//        SystemClock.sleep(1)
    }

    /**
     * 设置View的宽度和高度
     */
    fun start(width: Int, height: Int) {
        this.viewWidth = width
        this.viewHeight = height

        init()
    }


}

2.设置你的MainActivity

class MainActivity : AppCompatActivity() {

    lateinit var binding: ActivityMainBinding

    val TAG:String = "MainActivity"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        initView()
    }

    private fun initView() {

        var metrics = resources.displayMetrics
        var widthPixels = metrics.widthPixels
        var heightPixels = metrics.heightPixels

        Log.e(TAG, "initView: width::::$widthPixels:::::::$heightPixels")

        binding.svSnow.start(widthPixels,heightPixels)
    }
}

3.页面布局XML(activity_main.xml)

<androidx.constraintlayout.widget.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"
    tools:context=".MainActivity">

    <com.nnxy.snow.view.SnowView
        android:id="@+id/sv_snow"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#03A9F4"/>

</androidx.constraintlayout.widget.ConstraintLayout>

4.运行效果

snow

5.注意的地方

  1. 对性能的影响,这个我还没考虑到,应该是一个可以优化的地方
  2. MainActivity用了ViewBinding
  3. 雪的话我用了圆来实现,要做得更好看当然是要换成图片的形式,这也是可以优化的地方

6.参考链接

Android实现自定义飘雪效果
Android实现天气下雨效果

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 中的自定义 View 是开发中非常重要的一部分,可以帮助开发者创建出各种各样的交互效果和用户界面。本文将介绍如何使用 Android Studio 实现一个简单自定义 View。 1. 创建一个新项目并新建一个自定义 View 类 在 Android Studio 中创建一个新的项目,并在其中创建一个新的类,命名为 CustomView。CustomView 继承自 View,因此需要在该类中添加构造函数和必要的方法。 ```java public class CustomView extends View { public CustomView(Context context) { super(context); } public CustomView(Context context, AttributeSet attrs) { super(context, attrs); } public CustomView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 绘制代码 } } ``` 2. 在 CustomView 中添加绘制代码 在 CustomView 的 onDraw 方法中添加绘制代码,绘制一个简单的圆形,在这里我们使用 Canvas 和 Paint 类来绘制图形。 ```java @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.FILL); canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, paint); } ``` 3. 在布局文件中添加 CustomView 在布局文件中添加 CustomView,设置宽度和高度为 200dp,并设置背景色为白色。 ```xml <com.example.customview.CustomView android:layout_width="200dp" android:layout_height="200dp" android:background="@android:color/white" /> ``` 4. 运行程序 完成以上步骤后,即可运行程序,在屏幕上看到一个红色的圆形。 完整代码如下: ```java public class CustomView extends View { public CustomView(Context context) { super(context); } public CustomView(Context context, AttributeSet attrs) { super(context, attrs); } public CustomView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.FILL); canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, paint); } } ``` ```xml <com.example.customview.CustomView android:layout_width="200dp" android:layout_height="200dp" android:background="@android:color/white" /> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值