安卓动画爱心雨和大风车动画+擦除动画

该博客主要介绍了Android开发中的动画实现。包括使用第三方fallingview实现爱心雨动画,涵盖导入依赖、布局文件和主类编写;还介绍了大风车动画,通过自定义View类、布局文件和线程操作实现;另外展示了自定义擦除动画的代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

爱心雨动画 使用第三方fallingview实现

先看效果:

在这里插入图片描述

第一步:导入依赖

implementation 'com.dingmouren.fallingview:fallingview:1.0.2'

第二步:布局文件

在这里插入图片描述

主类:

   fallingView=findViewById(R.id.fall_view);
     fallingView.setImageResource(R.drawable.a_3);//设置碎片的图片,默认的图片是雪花
     fallingView.setDensity(80);//设置密度,数值越大,碎片越密集,默认值是 80
     fallingView.setScale(100);//设置碎片的大小,数值越大,碎片越小,默认值是 3
     fallingView.setDelay(10);//设置碎片飘落的速度,数值越大,飘落的越慢,默认值是 10

这样爱心雨就出来啦!

2.大风车动画自定义View

看一下效果:

在这里插入图片描述

自定义View类

package com.example.study_03_view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

        public class WindView extends View implements Runnable {

            Context context;
            Paint paint;
            Paint paint2;
            Paint paint3;
            Paint paintLine;


            int height; //当前的高度
            int width;//当前的宽度

            int sector1;
            int sector2;
            int sector3;

            public WindView(Context context, AttributeSet attrs) {
                super(context, attrs);

                //初始化画笔
                paint=new Paint();
                paint.setAntiAlias(true);
                paint.setColor(Color.RED);
                paint.setStrokeWidth(3);
                paint.setStyle(Paint.Style.FILL);

                paint2=new Paint();
                paint2.setAntiAlias(true);
                paint2.setColor(Color.GREEN);
                paint2.setStrokeWidth(3);
                paint2.setStyle(Paint.Style.FILL);

                paint3=new Paint();
                paint3.setAntiAlias(true);
                paint3.setColor(Color.BLUE);
                paint3.setStrokeWidth(3);
                paint3.setStyle(Paint.Style.FILL);

                paintLine=new Paint();
                paintLine.setAntiAlias(true);
                paintLine.setColor(Color.BLACK);
                paintLine.setStrokeWidth(3);
                paintLine.setStyle(Paint.Style.FILL);


            }

            @Override
            protected void onSizeChanged(int w, int h, int oldw, int oldh) {
                super.onSizeChanged(w, h, oldw, oldh);
                //获取当前正在改变的宽高
                width=w;
                height=h;
            }

            @Override
            protected void onDraw(Canvas canvas) {
                super.onDraw(canvas);

                //画个线
        canvas.drawLine(width/2,height/2,width/2,height/2-100,paintLine);

        //画三个扇形
        RectF rectF = new RectF(-(width/4), -(width/4), (width/4), (width/4));
        canvas.translate(width/2,height/2);
        canvas.drawArc(rectF,sector1,30,true,paint);
        canvas.drawArc(rectF,sector2,30,true,paint2);
        canvas.drawArc(rectF,sector3,30,true,paint3);

    }


    @Override
    public void run() {
        //重绘   invalidate();
        //异步重绘   postInvalidate();

        while (true) {
            sector1 +=20;
            sector2 = sector1 + 120;
            sector3 = sector2 + 120;
            postInvalidate();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

布局文件:这是我们自定义View的名字

在这里插入图片描述

测试类: 因为风车是动的吗所以我们使用线程操作他

在这里插入图片描述

自定义擦除动画

package com.example.study_06_view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;

public class WipeView extends View {

private Context context;
private Paint paint;
private Path path;
private Canvas mycanvas;
private int gao;
private int width;
private Bitmap bfBit;
private Bitmap afBit;

public WipeView(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.context=context;
    init();
}

private void init() {
    //获取窗口的管理者的宽和高
    WindowManager windowManager =(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    DisplayMetrics displayMetrics = new DisplayMetrics();
    windowManager.getDefaultDisplay().getMetrics(displayMetrics);
    gao = displayMetrics.heightPixels;
    width = displayMetrics.widthPixels;

    //初始化画笔
    paint=new Paint();
    //防锯齿
    paint.setAntiAlias(true);
    //防抖动
    paint.setDither(true);
    //样式
    paint.setStyle(Paint.Style.STROKE);
    //透明度
    paint.setARGB(128,255,0,0);
    //设置路径结合处样式
    paint.setStrokeJoin(Paint.Join.ROUND);
    //设置混合模式  DSI_IH
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    paint.setStrokeWidth(40);
    //设置笔触类型
    paint.setStrokeCap(Paint.Cap.ROUND);




    //生成前景的bitmap 设置图片质量参数
    bfBit = Bitmap.createBitmap(width,gao,Bitmap.Config.ARGB_8888);
    //将其注入画布
    mycanvas=new Canvas(bfBit);
    //灰色
    mycanvas.drawColor(Color.GRAY);
   //背景图
    afBit= BitmapFactory.decodeResource(context.getResources(), R.drawable.hai1);
    afBit = Bitmap.createScaledBitmap(afBit,width, gao, true);
    path=new Path();

}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //设置背景
    canvas.drawBitmap(afBit,0,0,null);
    //设置前景
    canvas.drawBitmap(bfBit,0,0,null);
    mycanvas.drawPath(path,paint);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    float x = event.getX();
    float y = event.getY();
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
           path.moveTo(x,y);
            break;
        case MotionEvent.ACTION_MOVE:
            path.lineTo(x,y);
            break;
    }
    invalidate();
    return true;
}

}

效果:

在这里插入图片描述

效果开始前先做个热身( ˘•灬•˘ )自己实现比较容易,但是到了要出博客整理思路,总结要点的时候就挠头,不知云所以,所以最简单的还是 Read the fucking source code如果对安卓UI有兴趣的朋友可以加我好友互相探讨,这里有很多自定义view可以参考思路思路比较简单,整个view无非两样东西云滴这里又包含两部分动画,一部分是云的左右移动动画,一部分是滴移动动画 那我们这里可以自定义一些属性,如果对自定义属性还不太了解的同学,搜下百度哈<resources>     <declare-styleable name="RainyView">         <!--滴的颜色-->         <attr name="raindrop_color" format="color"></attr>         <!--左边云的颜色-->         <attr name="left_cloud_color" format="color"></attr>         <!--右边云的颜色-->         <attr name="right_cloud_color" format="color"></attr>         <!-可同时存在的滴的最大数量-->         <attr name="raindrop_max_number" format="integer"></attr>         <!--每个滴之间创建的时间间隔-->         <attr name="raindrop_creation_interval" format="integer"></attr>         <!--每个滴的最小长度-->         <attr name="raindrop_min_length" format="integer"></attr>         <!--每个滴的最大长度-->         <attr name="raindrop_max_length" format="integer"></attr>         <!--滴的大小-->         <attr name="raindrop_size" format="integer"></attr>         <!--滴的最小移动速度-->         <attr name="raindrop_min_speed" format="float"></attr>         <!--滴的最大移动速度-->         <attr name="raindrop_max_speed" format="float"></attr>         <!--滴的斜率-->         <attr name="raindrop_slope" format="float"></attr>     </declare-styleable> </resources>画云云怎么画?云的形状不可胜举,我这里只实现了一种简单的形状:那我们如何通过画笔将其画出来:1.首先,我们先画底部,底部是一个圆角的矩形,通过下面方法绘制添加圆角矩形path.addRoundRect(RectF rect, float rx, float ry, Direction dir) 2.在该圆角的矩形的基础上,再画两个圆,左边的为小圆,右边的为大圆,这样就产生了一个最简单的云的图形, 在设置了以下代码之后paint.setStyle(Paint.Style.FILL);云的效果如下:我们把这个云作为左边的云,那么右边的云怎么画?很简单,因为我们这里用path来装载了这个云的路径,通过以下方法,mComputeMatrix.preTranslate(rightCloudTranslateX, -calculateRect.height() * (1 - CLOUD_SCALE_RATIO) / 2); mComputeMatrix.postScale(CLOUD_SCALE_RATIO, CLOUD_SCALE_RATIO, rightCloudCenterX, leftCloudEndY); mLeftCloudPath.transform(mComputeMatrix, mRightCloudPath);将这个云的path移动,缩小,并将其路径转换到mRightCloudPath即可在onDraw()的时候,调用以下方法就可以描绘路径了canvas.drawPath()接下来我们来实现云的动画,我们由上面已经了解到:/**  * Transform the points in this path by matrix, and write the answer  * into dst. If dst is null, then the the original path is modified.  *  * @param matrix The matrix to apply to the path  * @param dst    The transformed path is written here. If dst is null,  *               then the the original path is modified  */ public void transform(Matrix matrix, Path dst) {     long dstNative = 0;     if (dst != null) {         dst.isSimplePath = false;         dstNative = dst.mNativePath;     }     nTransform(mNativePath, matrix.native_instance, dstNative); }该方法可以将一个path进行matrix转换,即矩阵转换,因此我们可以通过方法matrix.postTranslate来实现平移动画,即创建一个循环动画,通过postTranslate来设置动画值就可以了,这里左边的云在右边的云之上,因此先画右边的云。mComputeMatrix.reset(); mComputeMatrix.postTranslate((mMaxTranslationX / 2) * mRightCloudAnimatorValue, 0); mRightCloudPath.transform(mComputeMatrix, mComputePath); canvas.drawPath(mComputePath, mRightCloudPaint); mComputeMatrix.reset(); mComputeMatrix.postTranslate(mMaxTranslationX * mLeftCloudAnimatorValue, 0); mLeftCloudPath.transform(mComputeMatrix, mComputePath); canvas.drawPath(mComputePath, mLeftCloudPaint);画滴首先我们要知道一点是,所有的滴都是随机产生的,而产生的值,可以根据上面的自定义属性指定,也可以使用自定义的值,我们先定义一个滴类private class RainDrop{     float speedX;  //滴x轴移动速度     float speedY;   //滴y轴移动速度     float xLength; //滴的x轴长度     float yLength; //滴的y轴长度     float x;        //滴的x轴坐标     float y;        //滴的y轴坐标     float slope; //滴的斜率 }关于上面参数,这里画张图来示例:关于斜率 我这里开放了一个设置斜率的接口,代表滴的一个倾斜度,可以看到下图的滴都是倾斜的,就是通过斜率来设置这个倾斜度 斜率:表示一条直线(或曲线的切线)关于(横)坐标轴倾斜程度的量。它通常用直线(或曲线的切线)与(横)坐标轴夹角的正切,或两点的纵坐标之差与横坐标之差的比来表示。该直线的斜率为k=(y1-y2)/(x1-x2)我这里使用了固定的斜率,使所有的滴方向一致,如果想将其改为随机值的同学,可以下载源码自行修改。在创建滴对象的时候,以下步骤使我们需要做的:斜率赋值(我这里是指定的,因此不用计算随机斜率)计算x轴、y轴移动速度随机值计算滴长度随机值(同时计算x轴,y轴长度值)计算x,y坐标随机值(为了营造滴更好的出场效果,这里设置了y轴的起点坐标为y-滴y轴长度)创建滴对象后,我们有了想要的参数,我们可以canvas.drawLine来画滴canvas.drawLine(rainDrop.x, rainDrop.y,             rainDrop.slope > 0 ? rainDrop.x   rainDrop.xLength : rainDrop.x - rainDrop.xLength,             rainDrop.y   rainDrop.yLength,             mRainPaint);这里需要注意以下,为什么canvas.drawLine中的stopX参数要设置为rainDrop.slope > 0 ? rainDrop.x   rainDrop.xLength : rainDrop.x - rainDrop.xLength这是因为,我们的滴是一直往下移动即y是增加的,我们上面知道斜率公式为:k=(y1-y2)/(x1-x2)即y1-y2肯定是大于0的,因此当斜率小于0的时候,滴是这样的,即x1-x2 < 0 当斜率大于0的时候,滴是这样的,即x1-x2 > 0 动画,由于每一个滴对象参数已经定义,在进行动画的时候,只需要根据速度,设置x、y轴的下一个点的坐标就行了if (rainDrop.slope >= 0) {         rainDrop.x  = rainDrop.speedX;     }else{         rainDrop.x -= rainDrop.speedX;     } rainDrop.y  = rainDrop.speedY;优化我们知道,在频繁的创建滴的时候,如果每次都创建新对象的话, 可能会增加不必要的内存使用,而且很容易引起频繁的gc,甚至是内存抖动。因此这里我增加了一个回收功能//首先判断栈中是否存在回收的对象,若存在,则直接复用,若不存在,则创建一个新的对象 private RainDrop obtainRainDrop(){      if (mRecycler.isEmpty()){          return new RainDrop();      }      return mRecycler.pop();  } //回收到一个栈里面,若这个栈数量超过最大可显示数量,则pop private void recycle(RainDrop rainDrop){     if (rainDrop == null){         return;     }     if (mRecycler.size() >= mRainDropMaxNumber){         mRecycler.pop();     }     mRecycler.push(rainDrop); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值