58同城加载动画的实现( Android属性动画)

最近看了58同城新版 app ,里面还是做了很多动画特效,其中我看到加载数据时的一个加载动画比较好玩,就试着做了一下,先一起来看看效果

很多人看了这个效果图,第一个疑问就是底下的阴影部分是如何实现的?其实如果真要自己动手实现的话,这个问题反而不是问题,而真正有困难的是,如何控制这个图片上升的时候速度减慢,而下降的时候速度加快,当然这个问题只有在动手做的过程中才会发现。

这里还是按步骤来实现

1、实现整个 LoadingView的布局

public class LoadingLayout extends RelativeLayout {
    public LoadingLayout(Context context) {
        this(context, null);
    }

    public LoadingLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LoadingLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //添加所需要的效果图片,并根据需求排列
        initView(getContext());
    }
}
    private void initView(Context context) {
        /*固定这几个图片的大小为28个 dp 值*/
        int viewSize = (int) (28 * getResources().getDisplayMetrics().density + .5f);
        /*创建一个 显示圆形图片的View*/
        mCircleView = new View(context);
        /*设置参数*/
        RelativeLayout.LayoutParams circleParams = new LayoutParams(viewSize, viewSize);
        /*让他水平居中显示*/
        circleParams.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
        mCircleView.setLayoutParams(circleParams);
        /*设置背景图片*/
        mCircleView.setBackgroundResource(R.mipmap.loading_yuan);
        /*设置 id,这里的作用,是为了下面阴影的排列,需要用此View 作为参考对象*/
        mCircleView.setId(R.id.action_bar_root);

        /*创建一个显示正方形图片的View*/
        mRectView = new View(context);
        RelativeLayout.LayoutParams rectParams = new LayoutParams(viewSize, viewSize);
        rectParams.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
        mRectView.setLayoutParams(rectParams);
        mRectView.setBackgroundResource(R.mipmap.loading_fangxing);

        /*创建一个显示三角形图片的View*/
        mTriangleView = new View(context);
        RelativeLayout.LayoutParams triangleParams = new LayoutParams(viewSize, viewSize);
        triangleParams.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
        mTriangleView.setLayoutParams(triangleParams);
        mTriangleView.setBackgroundResource(R.mipmap.loading_sanjiao);

        /*创建一个显示底部阴影图片的ImageView*/
        mBottomView = new ImageView(context);
        RelativeLayout.LayoutParams bottomParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        /*设置水平居中*/
        bottomParams.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
        /*设置在圆形图片的下方*/
        bottomParams.addRule(RelativeLayout.BELOW, R.id.action_bar_root);
        mBottomView.setLayoutParams(bottomParams);
        mBottomView.setBackgroundResource(R.mipmap.loading_bottom);
        /*整个Layout 中的View 居中显示*/
        setGravity(Gravity.CENTER);
        /*添加View*/
        addView(mCircleView);
        addView(mRectView);
        addView(mTriangleView);
        addView(mBottomView);

        mRectView.setVisibility(INVISIBLE);
        mTriangleView.setVisibility(INVISIBLE);
    }

到这里,第一步算是完成了,能够正常显示我们想要的数据,接下来看第二步,

2、为这几个 View 设置动画,让他们都动起来,这里说一下底部阴影的实现原理,其实没什么特别的,跟其他三个图片一样,通过属性动画改变其 X的放缩。

    private void startAnim() {
        Log.v("zgy","=========startAnim========") ;
        isAnim = true ;
        if (mCircleView.getVisibility() != VISIBLE){
            mCircleView.setVisibility(VISIBLE);
            mRectView.setVisibility(INVISIBLE);
            mTriangleView.setVisibility(INVISIBLE);
        }
        /*圆形图片的动画集合*/
        mCircleAnim = new AnimatorSet();
        /*设置执行时长800ms*/
        mCircleAnim.setDuration(800L);
        /*这里设置播放动画的个数,移动动画和底部阴影放缩动画*/
        mCircleAnim.playTogether(translationAnim(mCircleView),bottomAnim());
        /*开始动画*/
        mCircleAnim.start();
        /*设置动画监听事件*/
        mCircleAnim.addListener(mCircleListener);

        mRectAnim = new AnimatorSet();
        mRectAnim.setStartDelay(800L);
        mRectAnim.setDuration(800L);
        mRectAnim.playTogether(translationAnim(mRectView),bottomAnim(),rotationAnim(mRectView));
        mRectAnim.start();
        mRectAnim.addListener(mRectListener);

        mTriangleAnim = new AnimatorSet();
        mTriangleAnim.setStartDelay(1600L);
        mTriangleAnim.setDuration(800L);
        mTriangleAnim.playTogether(translationAnim(mTriangleView),bottomAnim(),rotationAnim(mTriangleView));
        mTriangleAnim.start();
        mTriangleAnim.addListener(mTriangleListener);

到这里,动画效果就已经实现了,来看看

但是总感觉效果不对劲,一般东西往下掉,给人的感觉是,也往下速度越快,而这里只是做匀速变化,那好,我们来改变插值器,因为这里动画是从下往上再往下算是一个完整的动画,其值为:

        mAnimTransValueRec = new float[7];
        mAnimTransValueRec[0] = 0f;
        mAnimTransValueRec[1] = -50f;
        mAnimTransValueRec[2] = -100f;
        mAnimTransValueRec[3] = -150f;
        mAnimTransValueRec[4] = -100f;
        mAnimTransValueRec[5] = -50f;
        mAnimTransValueRec[6] = 0f;

所以我们设插值器的时候,希望设置先减速再加速的插值器,可是,不巧的是,加速插值器有,减速插值器有,先加速再减速插值器也有,就是没有提供先减速再加速的插值器,不过没关系,我们自己实现一个;

要实现插值器,其实就是把

public interface TimeInterpolator {

    /**
     * Maps a value representing the elapsed fraction of an animation to a value that represents
     * the interpolated fraction. This interpolated value is then multiplied by the change in
     * value of an animation to derive the animated value at the current elapsed animation time.
     *
     * @param input A value between 0 and 1.0 indicating our current point
     *        in the animation where 0 represents the start and 1.0 represents
     *        the end
     * @return The interpolation value. This value can be more than 1.0 for
     *         interpolators which overshoot their targets, or less than 0 for
     *         interpolators that undershoot their targets.
     */
    float getInterpolation(float input);
}

中的 input 值转化成另一个 float,

如果什么都不做处理,就是线性变化,也就是匀速变化

/**
 * An interpolator where the rate of change is constant
 *
 */
public class LinearInterpolator implements Interpolator {

    public LinearInterpolator() {
    }

    public LinearInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        return input;
    }
}

再来看看先加速后减速的插值器

/**
 * An interpolator where the rate of change starts and ends slowly but
 * accelerates through the middle.
 *
 */
public class AccelerateDecelerateInterpolator implements Interpolator {
    public AccelerateDecelerateInterpolator() {
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }
}

那怎么来实现先减速后加速的插值器呢,根据上面的图形我们可以知道,要实现我们想要的效果,则在0~0.5之间的曲线应该是凸起来的,而在0.5~1之间应该是凹下去的,跟上图刚好要相反,这样的曲线怎么实现呢,来看一张图

以上图片是由两个抛物线组成,如果在0~0.5范围内取抛物线 b,在0.5~1.0取抛物线 a,刚好就是我们想要的曲线。而根据高中所学的数学知识,二次曲线求导可知,我们所截取的曲线其速度就是先减速再加速。

所以实现先减速再加速的插值器代码如下:

public final class DecelerateAccelerateInterpolator implements Interpolator {

    public final float getInterpolation(float input) {

        if (input < 0.5) {
            return - (input * (input/2.0f) -  input/2.0f);
        }else {
            return 1.0F + (input * (2.0F * input) - 2.0F * input) ;
        }
    }
}

ok,记得为动画设置插值器

animator.setInterpolator(new DecelerateAccelerateInterpolator());

最后测试效果就是第一副动态图的效果,明显比第二张动态图跟符合视觉逻辑;

点击源码下载

时间: 2024-12-14 10:31:10

58同城加载动画的实现( Android属性动画)的相关文章

gitHub-高仿58同城加载动画

导入方式: /build.gradle repositories { maven { url "https://jitpack.io" } } /app/build.gradle dependencies { compile 'com.github.zzz40500:android-shapeLoadingView:1.0.3.2' } 使用方式: 直接在xml布局文件上引入即可,动画会自动加载 <RelativeLayout xmlns:android="http:/

Android ListView分页加载(服务端+android端)Demo

Android ListView分页加载功能 在实际开发中经常用到,是每个开发者必须掌握的内容,本Demo给出了服务端+Android端的两者的代码,并成功通过了测试. 服务端使用MyEclipse,Android端使用Eclipse. 实现效果图: 服务端一共100条数据,共分四页,每页有25条数据. 源代码: 服务端: 需要导入图中这几个jar包. 在运行Android端代码前,需要开启服务端: 下面先给出服务端的代码: 类EmpDataSource: package com.android

【转载】一行代码加载网络图片到ImageView——Android Picasso

原文链接:一句代码加载网络图片到ImageView——Android Picasso 在这里介绍一个Android框架:Picasso. picasso是Square公司开源的一个Android图形缓存库,地址http://square.github.io/picasso/,可以实现图片下载和缓存功能.仅仅只需要一行代码就能完全实现图片的异步加载.代码如下: public class MainActivity extends Activity { private ImageView imageV

绘图篇——android属性动画

本文讲介绍android在3.0之后推出的一种新的动画机制,属性动画,对动画不了解的同学,可以先去看看绘图篇--android动画基础这篇文章.好了,现在我们进入正题. 基本概念 android传统动画Animation与属性动画Animator的工作原理 Animator出现的原因 Animation的局限性 属性动画ObjectAnimator translationX translationY X和Y rotation 组合动画 动画事件监听 小栗子 基本概念 [android传统动画An

Android 属性动画(Property Animation) 完全解析 (上)

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38067475 1.概述 Android提供了几种动画类型:View Animation .Drawable Animation .Property Animation .View Animation相当简单,不过只能支持简单的缩放.平移.旋转.透明度基本的动画,且有一定的局限性.比如:你希望View有一个颜色的切换动画:你希望可以使用3D旋转动画:你希望当动画停止时,View的

Android 属性动画(Property Animation) 完全解析 (下)

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38092093 上一篇Android 属性动画(Property Animation) 完全解析 (上)已经基本展示了属性动画的核心用法: ObjectAnimator实现动画,ValueAnimator实现动画,AnimatorSet的使用等~ 当然了属性动画还有一部分的知识点,也能做出很不错的效果,将在本篇博客为您展示~ 1.如何使用xml文件来创建属性动画 大家肯定都清楚,

Android属性动画完全解析(上)

Android属性动画完全解析(上) 转载:http://blog.csdn.net/guolin_blog/article/details/43536355 在手机上去实现一些动画效果算是件比较炫酷的事情,因此Android系统在一开始的时候就给我们提供了两种实现动画效果的方式,逐帧动画(frame-by-frame animation)和补间动画(tweened animation).逐帧动画的工作原理很简单,其实就是将一个完整的动画拆分成一张张单独的图片,然后再将它们连贯起来进行播放,类似

Android属性动画Property Animation系列三之LayoutTransition(布局容器动画)

在上一篇中我们学习了属性动画的ObjectAnimator使用,不了解的可以看看 Android属性动画Property Animation系列一之ObjectAnimator.这一篇我们来学点新的东西.做项目的时候应该碰到这种问题:根据不同条件显示或者隐藏一个控件或者布局,我们能想到的第一个方法就是 调用View.setVisibility()方法.虽然实现了显示隐藏效果,但是总感觉这样的显示隐藏过程很僵硬,让人不是很舒服,那么有没有办法能让这种显示隐藏有个过渡的动画效果呢?答案是肯定的,不言

通过AnimationSet 同步或一部播放多个动画 Android 属性动画(Property Animation) 完全解析 (下)

AnimationSet提供了一个把多个动画组合成一个组合的机制,并可设置组中动画的时序关系,如同时播放,顺序播放等. 以下例子同时应用5个动画: 播放anim1: 同时播放anim2,anim3,anim4: 播放anim5. AnimatorSet bouncer = new AnimatorSet(); bouncer.play(anim1).before(anim2); bouncer.play(anim2).with(anim3); bouncer.play(anim2).with(a