Android -- [SelfView] 多动画效果图片播放器

news2024/10/23 9:30:58

Android – [SelfView] 多动画效果图片播放器

效果(录制的有点卡)

在这里插入图片描述
在这里插入图片描述

1. 引用:
    <com.nepalese.virgolib.widget.image.BaseImageView
        android:id="@+id/base_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
2. 接口:
private BaseImageView baseImageView;
//绑定
baseImageView = findViewById(R.id.base_image);
//设置动画类型
baseImageView.setAnimType(type);
//设置图片资源
baseImageView.setImageResource(path);
3. BaseImageView.java
package com.nepalese.virgolib.widget.image;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Movie;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;

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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Created by Administrator on 2024/10/10.
 * Usage:自带动画的图片播放器: 两张图之间
 */

public class BaseImageView extends View {
    private static final String TAG = "BaseImageView";

    private static final long COM_INTERVAL = 500L;//通用动画时长
    private static final int ANIM_NUM = 10;//动画效果数(除随机)
    private static final int LENTH = 40;//像素跨值|cube 边长//按最大1920*1080容器,45时,差不多拆分1000个
    private static final int MAX_CACHE = 10;//最大缓存数量
    private static final int MAX_ALPHA = 255;//最大透明度
    private static final int MAX_GRADE = 180;//最大旋转角度

    public static final int ANIM_NONE = 0;//无动画
    public static final int ANIM_FADE = 1;//淡入淡出 默认
    public static final int ANIM_RIGHT = 2;//右进左出
    public static final int ANIM_SCALE = 3;//中心缩放
    public static final int ANIM_SCALE2 = 4;//中心缩放, 上一张不变
    public static final int ANIM_JUMP = 5;//弹跳退出
    public static final int ANIM_UP = 6;//底部上推
    public static final int ANIM_BOTTOM_UP = 7;//底部上浮
    public static final int ANIN_ROTATE = 8;//顺时针旋转
    public static final int ANIM_ROLL = 9;//左下角顺时针旋转 + 右移

    public static final int ANIM_CRASH = 10;//破碎效果
    public static final int ANIM_RANDOM = 99;//随机

    private final Context context;
    private Drawable[] drawables;//操作的两张图
    private ValueAnimator animator;//动画
    private Paint paint;//画笔
    private Movie movie;//用于承载gif文件
    private Rect rectLast, rectCur, rectJump;//上一张、当前图片位置
    private List<CubeBean> pixelList;
    private HashMap<String, Drawable> cacheMap;//图片缓存

    private int width, height;//控件宽高
    private int animType;//动画类型
    private int curIndex;//0|1
    private int CV;//线性变化的基础值
    private int JUMP_THRESHOLD;//跳动偏移阈值 px
    private int rotate, cX, cY;//当前旋转角度
    private int alphaLast, alphaCur;//上一张、当前图片透明值[0-255]
    private int leftLast, leftCur;//上一张、当前图片左上点x[0-width]
    private int topLast, topCur;//上一张、当前图片左上点xy[0-height]
    private float time;//动画运行时间
    private float whRate;//宽高比
    private long INTERVAL_ANIMATION;//动画时长,按动画类型分配
    private long gifPlayTime;//当前gif已播放时长
    private boolean isSecond;//第二部分
    private boolean isOver;//动画结束
    private boolean isRandom;//随机动画效果
    private boolean isGif;//当前为gif 文件?
    private String lastPath;//记录上一张图片路径:相同的文件则不切换

    public BaseImageView(Context context) {
        this(context, null);
    }

    public BaseImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BaseImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        init();
    }

    private void init() {
        time = 0;
        curIndex = -1;
        gifPlayTime = 0;
        isSecond = false;
        isOver = false;
        isRandom = false;
        isGif = false;
        animType = ANIM_NONE;
    }

    /**
     * 设置|更改布局时调用
     *
     * @param width  容器宽
     * @param height 容器高
     */
    public void initLayout(int width, int height) {
        this.width = width;
        this.height = height;
        this.whRate = width * 1f / height;
        initAnimator();
    }

    private void initAnimator() {
        cancelAnim();
        resetAnimator();
    }

    private void resetAnimator() {
        switch (animType) {
            case ANIM_NONE:
                CV = 0;
                break;
            case ANIM_FADE:
                CV = MAX_ALPHA; //透明值
                INTERVAL_ANIMATION = COM_INTERVAL;
                break;
            case ANIN_ROTATE:
                CV = MAX_GRADE;
                cX = width / 2;//旋转中心
                cY = height / 2;
                INTERVAL_ANIMATION = COM_INTERVAL;
                break;
            case ANIM_ROLL:
                CV = MAX_GRADE;
                cX = 0;//旋转中心
                cY = height;
                INTERVAL_ANIMATION = COM_INTERVAL;
                break;
            case ANIM_RIGHT:
                CV = width;
                INTERVAL_ANIMATION = COM_INTERVAL;
                break;
            case ANIM_UP:
                CV = height;
                INTERVAL_ANIMATION = COM_INTERVAL;
                break;
            case ANIM_BOTTOM_UP:
                CV = height;
                INTERVAL_ANIMATION = COM_INTERVAL;
                break;
            case ANIM_SCALE:
                rectLast = new Rect();
                rectCur = new Rect();
                CV = width / 2;
                INTERVAL_ANIMATION = COM_INTERVAL;
                break;
            case ANIM_SCALE2:
                rectCur = new Rect();
                CV = width / 2;
                INTERVAL_ANIMATION = COM_INTERVAL;
                break;
            case ANIM_CRASH:
                if (paint == null) {
                    paint = new Paint();
                    paint.setAntiAlias(true);
                    paint.setStyle(Paint.Style.FILL);
                }
                if (pixelList == null) {
                    pixelList = new ArrayList<>();
                }
                CV = 128;
                INTERVAL_ANIMATION = 1280L;
                break;
            case ANIM_JUMP:
                rectJump = new Rect();
                JUMP_THRESHOLD = Math.max(width / 8, 30);
                CV = width + JUMP_THRESHOLD;
                INTERVAL_ANIMATION = 800L;
                break;
        }

        if (CV > 0) {
            animator = ValueAnimator.ofInt(0, CV);
            animator.setDuration(INTERVAL_ANIMATION);
            animator.setInterpolator(new LinearInterpolator());//插值器设为线性
        } else {
            animator = null;
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (w > 0 && h > 0) {
            initLayout(w, h);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (drawables == null) {
            return;
        }

        if (isGif && this.getVisibility() == VISIBLE) {
            drawGif(canvas);
            return;
        }

        switch (animType) {
            case ANIM_NONE:
                drawNone(canvas);
                break;
            case ANIM_FADE:
                drawFade(canvas);
                break;
            case ANIN_ROTATE:
                drawRotate(canvas);
                break;
            case ANIM_ROLL:
                drawRoll(canvas);
                break;
            case ANIM_RIGHT:
                drawRight(canvas);
                break;
            case ANIM_UP:
                drawUp(canvas);
                break;
            case ANIM_BOTTOM_UP:
                drawBottomUp(canvas);
                break;
            case ANIM_SCALE:
                drawScal(canvas);
                break;
            case ANIM_SCALE2:
                drawScal2(canvas);
                break;
            case ANIM_CRASH:
                drawCrash(canvas);
                break;
            case ANIM_JUMP:
                drawJump(canvas);
                break;
        }
    }

    /**
     * 播放gif: 直接用movie绘制闪退?
     * 使用读帧方式,绘制每帧
     */
    private void drawGif(Canvas canvas) {
        if (movie != null) {
            long now = System.currentTimeMillis();
            if (gifPlayTime == 0) {
                gifPlayTime = now;
            }

            int dur = movie.duration();
            if (dur <= 0) {
                gifPlayTime = 0;
                Log.w(TAG, "Gif 读取失败|文件有问题,仅画一次");
                drawNone(canvas);
                return;
            }

            int relTime = (int) ((now - gifPlayTime) % dur);
            Drawable drawable = getMovieFirstFrame(movie, relTime);
            if (drawable != null) {
                drawable.setBounds(0, 0, width, height);
                drawable.draw(canvas);
            } else {
                canvas.drawColor(Color.WHITE);
            }

            invalidate();
        }
    }

    //无动画
    private void drawNone(Canvas canvas) {
        if (curIndex < 0) {
            Log.e(TAG, "drawNone: error -- " + curIndex);
            return;
        }

        if (drawables[curIndex] != null) {
            drawables[curIndex].setBounds(0, 0, width, height);
            drawables[curIndex].draw(canvas);
        }
    }

    private void drawFade(Canvas canvas) {
        if (curIndex < 0) {
            Log.e(TAG, "drawFade: error -- " + curIndex);
            return;
        }
        //如果有上一张,先消失,再加载当前的
        if (curIndex == 0) {
            if (drawables[1] == null) {
                //第一张图
                drawables[curIndex].setBounds(0, 0, width, height);
                drawables[curIndex].setAlpha(alphaCur);
                drawables[curIndex].draw(canvas);
            } else {
                if (isSecond) {
                    if (drawables[curIndex] != null) {
                        drawables[curIndex].setBounds(0, 0, width, height);
                        drawables[curIndex].setAlpha(alphaCur);
                        drawables[curIndex].draw(canvas);
                    }
                } else {
                    //上一张,先消失
                    drawables[1].setBounds(0, 0, width, height);
                    drawables[1].setAlpha(alphaLast);
                    drawables[1].draw(canvas);
                }
            }
        } else {
            if (isSecond) {
                if (drawables[curIndex] != null) {
                    drawables[curIndex].setBounds(0, 0, width, height);
                    drawables[curIndex].setAlpha(alphaCur);
                    drawables[curIndex].draw(canvas);
                }
            } else {
                //上一张,先消失
                if (drawables[0] != null) {
                    drawables[0].setBounds(0, 0, width, height);
                    drawables[0].setAlpha(alphaLast);
                    drawables[0].draw(canvas);
                }
            }
        }
    }

    /**
     * 顺时针旋转 180°
     * 仅顶部,底下不变
     */
    private void drawRotate(Canvas canvas) {
        if (curIndex < 0) {
            Log.e(TAG, "drawFade: error -- " + curIndex);
            return;
        }

        if (isOver) {
            //动画结束
            if (drawables[curIndex] != null) {
                drawables[curIndex].setBounds(0, 0, width, height);
                drawables[curIndex].draw(canvas);
            }
        } else {
            if (curIndex == 0) {
                if (drawables[1] != null) {
                    //上一张
                    drawables[1].setBounds(0, 0, width, height);
                    drawables[1].draw(canvas);
                }
            } else {
                //上一张
                if (drawables[0] != null) {
                    drawables[0].setBounds(0, 0, width, height);
                    drawables[0].draw(canvas);
                }
            }

            if (drawables[curIndex] != null) {
                drawables[curIndex].setBounds(0, 0, width, height);
            }

            if (drawables[curIndex] != null) {
                canvas.save();
                canvas.rotate(rotate, cX, cY);
                drawables[curIndex].draw(canvas);
                canvas.restore();
            }
        }
    }

    /**
     * 顺时针旋转90°
     * 仅顶部,底下不变
     */
    private void drawRoll(Canvas canvas) {
        if (curIndex < 0) {
            Log.e(TAG, "drawFade: error -- " + curIndex);
            return;
        }

        if (isOver) {
            //动画结束
            if (drawables[curIndex] != null) {
                drawables[curIndex].setBounds(0, 0, width, height);
                drawables[curIndex].draw(canvas);
            }
        } else {
            if (curIndex == 0) {
                if (drawables[1] != null) {
                    //上一张
                    drawables[1].setBounds(0, 0, width, height);
                    drawables[1].draw(canvas);
                }
            } else {
                //上一张
                if (drawables[0] != null) {
                    drawables[0].setBounds(0, 0, width, height);
                    drawables[0].draw(canvas);
                }
            }

            if (drawables[curIndex] != null) {
                drawables[curIndex].setBounds(0, 0, width, height);
            }

            if (drawables[curIndex] != null) {
                canvas.save();
                canvas.rotate(rotate, cX, cY);
                drawables[curIndex].draw(canvas);
                canvas.restore();
            }
        }
    }

    /**
     * 右进左出:进入时由变不变(宽度慢慢变大),出去时保持宽度不变
     */
    private void drawRight(Canvas canvas) {
        if (curIndex < 0) {
            Log.e(TAG, "drawRight: error -- " + curIndex);
            return;
        }
        //如果有上一张,先消失,再加载当前的
        if (isOver) {
            //动画结束
            if (drawables[curIndex] != null) {
                drawables[curIndex].setBounds(0, 0, width, height);
            }
        } else {
            if (curIndex == 0) {
                if (drawables[1] != null) {
                    //上一张
                    drawables[1].setBounds(leftLast, 0, width + leftLast, height);
                    drawables[1].draw(canvas);
                }
            } else {
                //上一张
                if (drawables[0] != null) {
                    drawables[0].setBounds(leftLast, 0, width + leftLast, height);
                    drawables[0].draw(canvas);
                }
            }

            if (drawables[curIndex] != null) {
                drawables[curIndex].setBounds(leftCur, 0, width, height);
            }
        }

        if (drawables[curIndex] != null) {
            drawables[curIndex].draw(canvas);
        }
    }

    /**
     * 从下面上浮:进入时由变不变(高度慢慢变大),出去时保持高度不变
     */
    private void drawUp(Canvas canvas) {
        if (curIndex < 0) {
            Log.e(TAG, "drawRight: error -- " + curIndex);
            return;
        }
        //如果有上一张,先消失,再加载当前的
        if (isOver) {
            //动画结束
            if (drawables[curIndex] != null) {
                drawables[curIndex].setBounds(0, 0, width, height);
            }
        } else {
            if (curIndex == 0) {
                if (drawables[1] != null) {
                    //上一张
                    drawables[1].setBounds(0, topLast, width, height - topLast);
                    drawables[1].draw(canvas);
                }
            } else {
                //上一张
                if (drawables[0] != null) {
                    drawables[0].setBounds(0, topLast, width, height - topLast);
                    drawables[0].draw(canvas);
                }
            }

            if (drawables[curIndex] != null) {
                drawables[curIndex].setBounds(0, topCur, width, height);
            }
        }

        if (drawables[curIndex] != null) {
            drawables[curIndex].draw(canvas);
        }
    }

    /**
     * 底部往上走,上一张不动
     * 图片大小不变
     */
    private void drawBottomUp(Canvas canvas) {
        if (curIndex < 0) {
            Log.e(TAG, "drawRight: error -- " + curIndex);
            return;
        }
        //如果有上一张,先消失,再加载当前的
        if (isOver) {
            //动画结束
            if (drawables[curIndex] != null) {
                drawables[curIndex].setBounds(0, 0, width, height);
            }
        } else {
            if (curIndex == 0) {
                if (drawables[1] != null) {
                    //上一张
                    drawables[1].setBounds(0, 0, width, height);
                    drawables[1].draw(canvas);
                }
            } else {
                //上一张
                if (drawables[0] != null) {
                    drawables[0].setBounds(0, 0, width, height);
                    drawables[0].draw(canvas);
                }
            }

            if (drawables[curIndex] != null) {
                drawables[curIndex].setBounds(0, 0, width, height);
            }
        }

        if (drawables[curIndex] != null) {
            canvas.save();
            canvas.translate(0, topCur);
            drawables[curIndex].draw(canvas);
            canvas.restore();
        }
    }

    private void drawScal(Canvas canvas) {
        if (curIndex < 0) {
            Log.e(TAG, "drawScal: error -- " + curIndex);
            return;
        }
        if (isOver) {
            //动画结束
            if (drawables[curIndex] != null) {
                drawables[curIndex].setBounds(0, 0, width, height);
            }
        } else {
            if (curIndex == 0) {
                if (drawables[1] != null) {
                    //上一张
                    drawables[1].setBounds(rectLast);
                    drawables[1].draw(canvas);
                }
            } else {
                //上一张
                if (drawables[0] != null) {
                    drawables[0].setBounds(rectLast);
                    drawables[0].draw(canvas);
                }
            }

            if (drawables[curIndex] != null) {
                drawables[curIndex].setBounds(rectCur);
            }
        }

        if (drawables[curIndex] != null) {
            drawables[curIndex].draw(canvas);
        }
    }

    /**
     * 仅当前图片慢慢变大, 上一张不变
     */
    private void drawScal2(Canvas canvas) {
        if (curIndex < 0) {
            Log.e(TAG, "drawScal: error -- " + curIndex);
            return;
        }
        if (isOver) {
            //动画结束
            if (drawables[curIndex] != null) {
                drawables[curIndex].setBounds(0, 0, width, height);
            }
        } else {
            if (curIndex == 0) {
                if (drawables[1] != null) {
                    //上一张
                    drawables[1].setBounds(0, 0, width, height);
                    drawables[1].draw(canvas);
                }
            } else {
                //上一张
                if (drawables[0] != null) {
                    drawables[0].setBounds(0, 0, width, height);
                    drawables[0].draw(canvas);
                }
            }

            if (drawables[curIndex] != null) {
                drawables[curIndex].setBounds(rectCur);
            }
        }

        if (drawables[curIndex] != null) {
            drawables[curIndex].draw(canvas);
        }
    }

    private void drawCrash(Canvas canvas) {
        if (curIndex < 0) {
            Log.e(TAG, "drawCrash: error -- " + curIndex);
            return;
        }
        if (isOver) {
            //动画结束
            if (drawables[curIndex] != null) {
                drawables[curIndex].setBounds(0, 0, width, height);
                drawables[curIndex].draw(canvas);
            }
        } else {
            for (CubeBean item : pixelList) {
                if (item.sY > height) {
                    //超出容器不用画
                    continue;
                }
                paint.setColor(item.color);
                canvas.drawRect(item.sX, item.sY, item.sX + item.cL, item.sY + item.cL, paint);

                //变化 s = v0t + at^2
                item.sY += (float) (item.vY * time + item.aY * Math.pow(time, 2));
            }
        }
    }

    private void drawJump(Canvas canvas) {
        if (curIndex < 0) {
            Log.e(TAG, "drawJump: error -- " + curIndex);
            return;
        }
        //当前图片一直存在
        if (drawables[curIndex] != null) {
            drawables[curIndex].setBounds(0, 0, width, height);
            drawables[curIndex].draw(canvas);
        }

        if (isOver) {
            return;
        }

        //上一张图
        if (curIndex == 0) {
            if (drawables[1] != null) {
                //上一张
                drawables[1].setBounds(rectJump);
                drawables[1].draw(canvas);
            }
        } else {
            //上一张
            if (drawables[0] != null) {
                drawables[0].setBounds(rectJump);
                drawables[0].draw(canvas);
            }
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        releaseBase();
        super.onDetachedFromWindow();
    }

    @Override
    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);
        if (isGif) {
            if (visibility == VISIBLE) {
                invalidate();
            }
        }
    }
    

    /**
     * 获取Gif图片帧
     */
    private Drawable getMovieFirstFrame(Movie movie, int time) {
        if (movie == null) {
            return null;
        }
        Bitmap bitmap = Bitmap.createBitmap(movie.width(), movie.height(),
                Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        movie.setTime(time);
        movie.draw(canvas, 0, 0);
        canvas.save();
        return new BitmapDrawable(getResources(), bitmap);
    }

    /**
     * 大于控件分辨率的图片会自动压缩
     *
     * @param filePath 图片路径
     * @return Drawable
     */
    private Drawable getDrawableFromFile(String filePath) {
        //启用缓存
        if (cacheMap == null) {
            cacheMap = new HashMap<>();
        } else {
            if (cacheMap.containsKey(filePath)) {
                return cacheMap.get(filePath);
            }
        }

        Drawable drawable = null;

        try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            //设置为true,仅解析图片的原始宽高信息,并不会加载图片
            options.inJustDecodeBounds = true;
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            BitmapFactory.decodeFile(filePath, options);

            //按控件宽高压缩
            options.inSampleSize = calculateInSampleSize(options, width, height);
            options.inJustDecodeBounds = false;

            if (options.inSampleSize > 0) {
                drawable = new BitmapDrawable(getResources(), BitmapFactory.decodeFile(filePath, options));
                if (cacheMap.size() < MAX_CACHE) {
                    cacheMap.put(filePath, drawable);
                }//超出不处理
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return drawable;
    }

    /**
     * 计算出合适的图片倍率
     *
     * @param options:   图片bitmapFactory选项
     * @param reqWidth:  需要的图片宽
     * @param reqHeight: 需要的图片长
     * @return int 成功返回倍率, 异常-1
     */
    private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        int inSampleSize;
        try {
            // reqWidth/width, reqHeight/height两者中最大值作为压缩比
            inSampleSize = Math.max(options.outWidth / reqWidth, options.outHeight / reqHeight);
            inSampleSize = Math.max(inSampleSize, 1);//小图不变
        } catch (Exception ignored) {
            return -1;
        }
        return inSampleSize;
    }

    protected void cancelAnim() {
        if (animator != null) {
            animator.removeAllListeners();
            animator.end();
            animator = null;
        }
    }

    private void doAnimation() {
        if (animator != null) {
            animator.removeAllListeners();
        }

        if (isRandom) {
            animType = (int) (Math.random() * 1000) % ANIM_NUM;
            resetAnimator();
        }
        if (animType == ANIM_NONE || animator == null) {
            //无动画
            invalidate();
            return;
        } else if (animType == ANIM_CRASH) {
            pixelList.clear();
            //获取上一张图片的像素
            BitmapDrawable bitmapDrawable = null;
            if (curIndex == 0) {
                bitmapDrawable = (BitmapDrawable) drawables[1];
            } else if (curIndex == 1) {
                bitmapDrawable = (BitmapDrawable) drawables[0];
            }
            if (bitmapDrawable == null) {
                isOver = true;
                invalidate();
                return;
            }

            Bitmap bitmap = bitmapDrawable.getBitmap();
            if (bitmap != null) {
                //该参数控制原来每一个像素点在屏幕上的缩放比例,此时为放大两倍
                //基于控件宽高, 获取等比缩放下对应的像素点
                float rW = bitmap.getWidth() * 1f / width;
                float rH = bitmap.getHeight() * 1f / height;
                CubeBean item;
                for (int i = 0; i < width; i += LENTH) {//像素跨值
                    for (int j = 0; j < height; j += LENTH) {
                        item = new CubeBean();
                        item.color = bitmap.getPixel((int) (i * rW), (int) (j * rH));//取样点

                        item.sX = i;
                        item.sY = j;
                        item.cL = LENTH;

                        //初始速度
                        item.vY = getRandom(3, 20);
                        //加速度
                        item.aY = 15f;//9.8f;

                        pixelList.add(item);
                    }
                }
            }
        }

        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                time = 0;
                isSecond = false;
                isOver = false;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                time = 0;
                isOver = true;
                invalidate();
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                //
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
                //
            }
        });

        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int av = (int) animation.getAnimatedValue();
                switch (animType) {
                    case ANIM_FADE:
                        if (av > CV / 2) {//一半
                            isSecond = true;
                        }
                        alphaCur = av;
                        alphaLast = CV - av;
                        break;
                    case ANIN_ROTATE:
                        rotate = MAX_GRADE + av;
                        break;
                    case ANIM_ROLL:
                        rotate = MAX_GRADE + av;
                        break;
                    case ANIM_RIGHT:
                        leftLast = -av - 10;//增加两图之间间隔
                        leftCur = CV - av;
                        break;
                    case ANIM_UP:
                        topLast = -av - 10;//增加两图之间间隔
                        topCur = CV - av;
                        break;
                    case ANIM_BOTTOM_UP:
                        topCur = CV - av;
                        break;
                    case ANIM_SCALE:
                        if (av > CV / 2) {//一半
                            isSecond = true;
                        }

                        if (!isSecond) {
                            //后面不用变化上一张 变小
                            rectLast.left = av;
                            rectLast.top = (int) (av / whRate);
                            rectLast.right = width - av;
                            rectLast.bottom = height - rectLast.top;
                        }

                        //当前:变大
                        rectCur.left = CV - av;
                        rectCur.top = (int) (rectCur.left / whRate);
                        rectCur.right = CV + av;
                        rectCur.bottom = height - rectCur.top;
                        break;
                    case ANIM_SCALE2:
                        //当前:变大
                        rectCur.left = CV - av;
                        rectCur.top = (int) (rectCur.left / whRate);
                        rectCur.right = CV + av;
                        rectCur.bottom = height - rectCur.top;
                        break;
                    case ANIM_CRASH:
                        time = animation.getCurrentPlayTime() / 1000f;//ms
                        break;
                    case ANIM_JUMP:
                        if (curIndex == 0) {
                            //右出
                            if (av < JUMP_THRESHOLD) {
                                //先向左移动
                                rectJump.left = -av;
                            } else {
                                //向右跳出
                                rectJump.left = av - JUMP_THRESHOLD;
                            }
                        } else {
                            //左出
                            if (av < JUMP_THRESHOLD) {
                                //先向左移动
                                rectJump.left = av;
                            } else {
                                //向右跳出
                                rectJump.left = JUMP_THRESHOLD - av;
                            }
                        }

                        rectJump.right = width + rectJump.left;
                        rectJump.top = 0;
                        rectJump.bottom = height;
                        break;
                }

                invalidate();
            }
        });
        animator.start();
    }

    private float getRandom(int a, int b) {
        return (float) (Math.random() * (b - a) + a);
    }

    

    /**
     * 设置动画类型
     */
    public void setAnimType(int animType) {
        if (animType >= ANIM_NUM) {
            //随机
            this.isRandom = true;
            return;
        }
        this.animType = animType;
        resetAnimator();
    }

    /**
     * 设置图片路径,默认路径存在(外部校验)
     *
     * @param path 图片路径
     */
    public boolean setImageResource(String path) {
        if (TextUtils.isEmpty(path)) {
            return false;
        }

        if (path.equals(lastPath)) {
            Log.d(TAG, "相同图片, 不切换!");
            return true;
        } else {
            lastPath = path;
        }

        if (path.endsWith(".gif")) {
            isGif = true;
            movie = Movie.decodeFile(path);
            return setImageResource(getMovieFirstFrame(movie, 500), path);
        } else {
            isGif = false;
            return setImageResource(getDrawableFromFile(path), path);
        }
    }

    /**
     * 设置图片
     */
    public boolean setImageResource(@DrawableRes int resId) {
        Drawable d;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            d = context.getDrawable(resId);
        } else {
            d = context.getResources().getDrawable(resId);
        }

        return setImageResource(d, String.valueOf(resId));
    }

    /**
     * 设置图片
     *
     * @return 是否成功播放
     */
    public boolean setImageResource(Drawable drawable, String msg) {
        if (drawable == null) {
            Log.e(TAG, "图片资源为空!" + msg);
            return false;
        }

        //重置透明度
        drawable.setAlpha(MAX_ALPHA);
        if (drawables == null) {
            drawables = new Drawable[2];
        }

        curIndex++;//def -1

        if (curIndex > 1) {
            curIndex = 0;
        }

        if (drawables[curIndex] != null) {
            drawables[curIndex] = null;//回收
        }

        drawables[curIndex] = drawable;

        //Animators may only be run on Looper threads
        if (isGif) {
            //gif 文件直接播放,不用动画
            invalidate();
        } else {
            doAnimation();
        }
        return true;
    }

    public void releaseBase() {
        cancelAnim();
        isGif = false;
        movie = null;
        lastPath = null;
        drawables = null;
        curIndex = -1;
        gifPlayTime = 0;
        if (cacheMap != null) {
            cacheMap.clear();
            cacheMap = null;
        }
        if (pixelList != null) {
            pixelList.clear();
            pixelList = null;
        }
    }
}

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

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

相关文章

2024让我爱不释手的Mac清理神器CleanMyMac X4.15.8免费版

大家好&#xff0c;今天我要和大家分享一款让我爱不释手的Mac清理神器——CleanMyMac X。作为一个长期使用Mac的用户&#xff0c;我深知电脑在长时间使用后容易出现卡顿、存储空间不足等问题。而自从我遇到了CleanMyMac X&#xff0c;这些问题都迎刃而解啦&#xff01; #### 一…

实现一个进度条对话框

效果如下&#xff1a; 点击按钮后开启1个线程模拟加载什么东西&#xff0c;同时弹出1个进度条对话框&#xff0c;进度条达到最大值后&#xff0c;进度条对话框慢慢变透明然后消失 关键点是我们要在进度条类中添加1个槽函数&#xff0c;在这个槽函数中设置进度条的值 代码如下…

高校学科竞赛平台:SpringBoot实现的高效开发流程

3系统分析 3.1可行性分析 通过对本高校学科竞赛平台实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本高校学科竞赛平台采用SSM框架&#xff0c;JAVA作为开发语…

【Java】集合中单列集合详解(一):Collection与List

目录 引言 一、Collection接口 1.1 主要方法 1.1.1 添加元素 1.1.2 删除元素 1.1.3 清空元素 1.1.4 判断元素是否存在 1.1.5 判断是否为空 1.1.6 求取元素个数 1.2 遍历方法 1.2.1 迭代器遍历 1.2.2 增强for遍历 1.2.3 Lambda表达式遍历 1.2.4 应用场景 二、…

Autosar Dcm配置-App到Boot的跳转及1002回复配置及实现-基于ETAS软件

文章目录 前言App软复位的实现Dcm配置BswM配置BswMModeRequestPortBswMModeConditionBswMLogicalExpressionBswMActionListApp回复1002的实现Dcm配置代码实现App回NRC78的实现Dcm配置代码实现总结前言 在软件刷写流程中,上位机(诊断仪)发送1002后,APP检查允许跳转boot后,在…

python脚本处理--批量压缩解压文件(zip、rar) / 读取txt文件并在txt每行文件后面增加内容

一、批量压缩、解压文件 os库是为了监测生成的文件夹是否已存在。主要的库是zipfile&#xff0c;它提供了有关windows下的文件/文件夹的压缩、解压的函数。 压缩、解压函数及整体代码如下&#xff1a; import os import zipfiledef Compress_path_zip(path_all):path_all_list…

接口测试-day3-jmeter-3http请求默认值

postman只需要写上请求方式和url即可&#xff0c;但是在jmeter中则是分开写的。 对于同一个项目的接口而言&#xff1a;他们的协议、域名、端口号、内容编码都是一样的。这样就相当于做了重复的工作。 不一样的地方只是在路径。不同的页面的路径是不同的。 如果我们设置了相…

文心智能体:我的旅游小助手

文章目录 一、全球旅游推荐官&#xff08;旅游小帮手介绍&#xff09;二、为什么会创建全球旅游推荐官呢&#xff1f;1.创意灵感2.实现思路 三、开发步骤和方法四、调试方法和总结五、探索AI未来&#xff0c;开启无限可能&#xff1a;文心智能体平台&#xff0c;智能创新的领航…

6.Pytest快速上手

1.安装pytest pip install pytest 2.编写测试用例 1.test_开头的文件 2.test_开头的函数 3.Test_开头的类 4.用例中应该有断言 def test_cofool():assert "浩宇" "真帅" 3.执行测试用例 1.用命令行启动 pytest 2.用代码启动 import pytest#对用例进…

hadoop全分布式搭建(三台虚拟机,一个主节点,两个从节点)

根据尚硅谷哔哩哔哩视频搭建&#xff1a;bilibili.com/video/BV1Qp4y1n7EN/ 安装虚拟机教程可参考&#xff1a;VMware虚拟机 安装 Centos7(linux)&#xff08;新手超详细教程&#xff09;_vmware安装centos7教程-CSDN博客 集群配置如下&#xff1a; 一、先配置一台虚拟机hadoo…

【详细版教程】vue-cli 卸载(卸载不了)vue2.x.x版本卸载不了,无法更新版本的解决方案

今天新建vue的项目时&#xff0c;遇到以下问题 vue create is a Vue CLI 3 only command and you are using Vue CLI 2.9.6. You may want to run the following to upgrade to Vue CLI 3: 官网给出的解决方案&#xff1a; npm uninstall -g vue-cli npm install -g vue/…

springcloud之服务提供与负载均衡调用 Eureka

前言 提供一个基于Eurka的服务注册中心&#xff0c;两个服务提供者之后分别使用Ribbon、Fegin方式进行调用&#xff0c;测试负载均衡。 服务提供者Service Provider 本质上是一个 Eureka Client&#xff0c;它在服务启动时&#xff0c;会调用服务注册方法&#xff0c;向 Eurek…

Vue:若依部门数据权限设置

目录 一、修改菜单树二、后台修改三、新建用户配置数据权限 一、修改菜单树 修改菜单树&#xff0c;增加权限字符system:user:list&#xff0c;权限字符根据自己后台数查询的权限判断 二、后台修改 在Mapper层增加DataScope(deptAlias "sys_dept")注解&#xff0c…

Unity中搜索不到XR Interaction Toolkit包解决方法

问题&#xff1a; 针对Unity版本2020.3在中PackageManager可能搜素不到XR Interaction Toolkit包 在Package Manager中未显示XR Interaction Toolkit包 解决方法&#xff1a; Package manager左上角&#xff0c;点加号&#xff0c;选择 Add package from git URL..&#xff0c;…

动力电池SOC估算方法

1. SOC介绍 电池的荷电状态SOC反映电池的剩余容量状况&#xff0c;即在一定的放电倍率下&#xff0c;当前电池的剩余容量与总容量的比值。 为了充分发挥电池性能和提高安全性&#xff0c;需要准确估算电池SOC。动力电池在使用过程中表现的高度非线性提高了SOC估算的难度&#…

【linux】信号(下)

8. 阻塞信号 (一)信号其他相关常见概念 实际执行信号的处理动作称为信号递达(Delivery)信号从产生到递达之间的状态,称为信号未决(Pending)进程可以选择阻塞 (Block )某个信号被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作(即被阻塞的信…

class 034 链表高频题目和必备技巧

这篇文章是看了“左程云”老师在b站上的讲解之后写的, 自己感觉已经能理解了, 所以就将整个过程写下来了。 这个是“左程云”老师个人空间的b站的链接, 数据结构与算法讲的很好很好, 希望大家可以多多支持左程云老师, 真心推荐. 左程云的个人空间-左程云个人主页-哔哩哔哩视频…

Redis 典型应用之缓存

目录 1. 缓存的基本概念 2. 使用 Redis 作为缓存 3. 缓存的更新策略 3.1 定期生成 3.2 实时生成 3.2.1 内存淘汰策略 1. FIFO (First In First Out) 先进先出 2. LRU (Least Recently Used) 淘汰最久未使使用的 3. LFU (Least Frequently Used) 淘汰访问次数最少的 4…

用C++编写信息管理系统(歌单信息管理)

C语言是面向过程的编程语言&#xff0c;而C是面向对象的编程语言&#xff0c;在书写代码时风格有所不同&#xff08;也存在很多共性&#xff09;。 程序说明 本次系统程序使用的是C语言进行编写&#xff0c;主要考虑怎么实现面向对象的问题。 因为本次程序属于小型系统程序&…

【C++学习】核心编程之类和对象(上)黑马学习笔记—超详细

目录 &#xff08;一&#xff09;封装 1.1 封装的意义&#xff1a; 意义一&#xff1a;在设计类的时候&#xff0c;属性和行为写在一起&#xff0c;表现事物 意义二&#xff1a;类在设计时&#xff0c;可以把属性和行为放在不同的权限下&#xff0c;加以控制 1.2 struct和…