Android开发之自定义加载动画详解

Android开发之自定义加载动画详解

一、前言

在移动APP的开发中,由于数据的加载速度不可控,我们通常会使用加载动画来占位,让用户知道数据正在努力获取中,以此来提升用户体验。在Android开发中,我们可以通过自定义View来创建各种各样的加载动画,本篇攻略将详细讲解如何自定义加载动画。

二、核心步骤

2.1 绘制动画

自定义加载动画的第一步是绘制动画。需要在View的onDraw()方法中完成绘制。这里我们以绘制小球旋转的动画为例。

public class BallRotationView extends View {

    private Paint mPaint;
    private int mWidth, mHeight;
    private float mRadius = 20; // 小球半径
    private float mDegrees; // 小球旋转角度
    private float[] mPositions = new float[8]; // 8个小球的旋转初始角度

    private AnimatorSet mAnimatorSet;

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

    public BallRotationView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
        initPositions();
        initAnimator();
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
    }

    private void initPositions() {
        for (int i = 0; i < 8; i++) {
            mPositions[i] = i * 45;
        }
    }

    private void initAnimator() {
        ObjectAnimator degreeAnimator = ObjectAnimator.ofFloat(this, "degrees", 0, 360);
        degreeAnimator.setDuration(1000);
        degreeAnimator.setInterpolator(new LinearInterpolator());
        degreeAnimator.setRepeatCount(ValueAnimator.INFINITE);

        mAnimatorSet = new AnimatorSet();
        mAnimatorSet.playTogether(degreeAnimator);
        mAnimatorSet.start();
    }

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

        canvas.save();
        canvas.translate(mWidth / 2, mHeight / 2);

        for (int i = 0; i < 8; i++) {
            canvas.rotate(mPositions[i] + mDegrees);
            canvas.drawCircle(0, mWidth / 2 - mRadius * 2, mRadius, mPaint);
        }

        canvas.restore();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        mHeight = MeasureSpec.getSize(heightMeasureSpec);
    }

    public void setDegrees(float degrees) {
        mDegrees = degrees;
        invalidate();
    }

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

}

2.2 循环动画

接下来,我们需要实现动画的循环播放。可以通过AnimatorSet来实现多个Animator的组合。

ObjectAnimator degreeAnimator = ObjectAnimator.ofFloat(this, "degrees", 0, 360);
degreeAnimator.setDuration(1000);
degreeAnimator.setInterpolator(new LinearInterpolator());
degreeAnimator.setRepeatCount(ValueAnimator.INFINITE);

mAnimatorSet = new AnimatorSet();
mAnimatorSet.playTogether(degreeAnimator);
mAnimatorSet.start();

2.3 动画控制

当View被移除屏幕时,停止动画,释放所有资源。可以在onDetachedFromWindow()方法中实现。

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

三、示例说明

3.1 示例一:小球旋转动画

我们可以使用自定义的BallRotationView实现小球旋转动画效果。代码如下:

<com.example.myapplication.BallRotationView
    android:layout_width="wrap_content"
    android:layout_height="50dp"
    android:layout_gravity="center_horizontal" />

3.2 示例二:微信小程序的加载动画

我们可以根据微信小程序的加载动画示意图来自定义仿微信小程序的加载动画,代码如下:

public class WxLoadingView extends View {

    private Paint mPaint;

    private int mWidth, mHeight;
    private float mRadius;
    private float mScale; // 尺寸变化比例

    private int mCount = 3; // 圆点个数
    private int mCurrentIndex = 0; // 当前动画圆点序号
    private int mDuration = 300; // 动画时长
    private boolean mIsAnimatorRunning = false;

    private ValueAnimator mAnimator;

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

    public WxLoadingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
        startAnimation();
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setColor(Color.parseColor("#1296db"));
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
    }

    private void startAnimation() {
        mAnimator = ValueAnimator.ofFloat(1, 0.2f, 1);
        mAnimator.setDuration(mDuration);
        mAnimator.setRepeatCount(ValueAnimator.INFINITE);
        mAnimator.setRepeatMode(ValueAnimator.RESTART);
        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mScale = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        mIsAnimatorRunning = true;
        mAnimator.start();
    }

    private void stopAnimation() {
        if (mAnimator != null && mAnimator.isRunning()) {
            mAnimator.cancel();
            mIsAnimatorRunning = false;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float x = (float) (mWidth / 2);
        float y = (float) (mHeight / 2);
        float space = mRadius / 2;

        for (int i = 0; i < mCount; i++) {
            canvas.save();
            float translateX = x + (i - (mCount - 1) / 2f) * space * mScale;
            float translateY = y;
            canvas.translate(translateX, translateY);
            float scale = mCurrentIndex == i ? 1 : 0.5f;
            canvas.scale(scale, scale);
            canvas.drawCircle(0, 0, mRadius * mScale, mPaint);
            canvas.restore();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        mHeight = MeasureSpec.getSize(heightMeasureSpec);
        mRadius = mHeight / 5;
    }

    public boolean isAnimatorRunning() {
        return mIsAnimatorRunning;
    }

    public void show() {
        if (!isAnimatorRunning()) {
            startAnimation();
        }
        setVisibility(VISIBLE);
    }

    public void hide() {
        stopAnimation();
        setVisibility(GONE);
    }

    public void setCurrentIndex(int currentIndex) {
        this.mCurrentIndex = currentIndex;
        invalidate();
    }

}

我们可以使用自定义的WxLoadingView实现仿微信小程序的加载动画效果。代码如下:

<com.example.myapplication.WxLoadingView
    android:id="@+id/loading_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_marginTop="24dp" />

四、总结

通过自定义View的方式,我们可以轻松实现各种各样的加载动画效果。不仅能提升用户的体验,也能让APP的界面更具有个性化。希望本篇攻略对您有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Android开发之自定义加载动画详解 - Python技术站

(0)
上一篇 2023年6月25日
下一篇 2023年6月25日

相关文章

  • python使用epoll实现服务端的方法

    下面是Python使用epoll实现服务端的方法的完整攻略。 什么是epoll epoll是Linux内核提供的一种高性能、高扩展性的I/O多路复用机制。使用epoll可以监听多个socket上的I/O事件,并在有事件发生时立刻通知应用程序。与传统的select/poll相比,epoll能够处理上千个并发连接的情况,并且 epoll在注册和注销文件描述符时都…

    other 2023年6月27日
    00
  • oracle中闪回错误的dml操作原理

    Oracle中闪回错误的DML操作原理 在Oracle数据库中,我们经常需要对数据进行增删改查的操作。但是,有时候我们会不小心执行了一些错误的数据操作,例如误删了重要数据,或者错误地修改了数据。这时候,如果没有备份,我们可能会面临灾难性的后果。而这时候,Oracle中的“闪回”功能就能派上用场。本文将介绍Oracle中闪回功能的原理,以及如何通过闪回功能来纠…

    其他 2023年3月28日
    00
  • yum安装指定版本的软件包的方法

    yum安装指定版本的软件包的方法 在使用Linux系统时,我们通常会借助软件包管理工具,如yum来进行软件包的安装、升级等操作。然而,有时候我们希望安装或降级软件包的指定版本,而不是最新的版本。本文将介绍yum安装指定版本的软件包的方法。 1. 查看可用版本 首先,我们需要查看当前可用的软件包版本。可以通过以下命令来查看: yum –showduplica…

    其他 2023年3月29日
    00
  • 内存参数优化宝典

    内存参数优化宝典攻略 1. 理解内存参数优化的重要性 内存参数优化是提高系统性能和稳定性的关键步骤之一。通过合理配置内存参数,可以有效减少内存使用量,提高系统的响应速度和资源利用率。 2. 分析系统内存使用情况 在进行内存参数优化之前,首先需要了解系统的内存使用情况。可以使用工具如top、htop或free来查看系统的内存占用情况。 示例: $ free -…

    other 2023年8月1日
    00
  • C语言:min和max头文件

    以下是详细的“C语言:min和max头文件的完整攻略,过程中至少包含两条示例说明”。 问题描述 C语言中,我们经常需要比较两个数的大小,以进行相应的操作。min和max头文件提供了方便的方法来比较两个数的大小。本文将介绍如何使用min和max头文件,包括两个示例说明。 解决方法 在C语言中,我们可以使用以下步骤来使用min和max头文件比较两个数的大小: 在…

    other 2023年5月7日
    00
  • 详解关于html,css,js三者的加载顺序问题

    当网页被访问时,浏览器加载HTML、CSS和JavaScript的顺序非常重要。正确的加载顺序可以确保网站在用户端正确渲染,错序的加载则可能导致页面无法正常显示或者工作不正常。 以下是一个关于HTML、CSS、JS加载顺序问题的详细攻略。 HTML、CSS、JS的加载顺序 当用户访问一个网站时,浏览器按照以下顺序加载页面上的HTML、CSS和JavaScri…

    other 2023年6月25日
    00
  • iphone x怎么查看储存空间?苹果iphone x查看手机内存教程

    iPhone X查看储存空间攻略 苹果iPhone X提供了简便的方式来查看手机的储存空间。您可以按照以下步骤进行操作: 打开“设置”应用程序:在主屏幕上找到并点击“设置”图标,它通常显示为一个齿轮状的图标。 进入“通用”设置:在“设置”界面中,向下滚动并点击“通用”选项。它通常显示为一个蓝色的图标,上面有一个白色的地球。 进入“iPhone存储空间”:在“…

    other 2023年7月31日
    00
  • Android样式和主题之选择器的实例讲解

    Android样式和主题之选择器的实例讲解 在Android开发中,样式和主题是非常重要的概念,它们可以用来定义应用程序的外观和行为。其中,选择器是一种特殊的样式,它可以根据不同的状态来改变控件的外观。本文将详细讲解如何使用选择器来定义控件的样式。 选择器的基本语法 选择器是一个XML文件,它定义了一组状态和对应的样式。以下是选择器的基本语法: <se…

    other 2023年8月20日
    00
合作推广
合作推广
分享本页
返回顶部