Android从零撸美团(二) - 仿美团下拉刷新自定义动画

栏目: IOS · Android · 发布时间: 6年前

内容简介:这是【从零撸美团】系列文章第二篇。 项目地址:今天写了下拉刷新,框架用的是先上图:

这是【从零撸美团】系列文章第二篇。 项目地址: github.com/cachecats/L…

今天写了下拉刷新,框架用的是 SmartRefreshLayout ,不为啥,因为 Github 上它有 9.5kstar ,中文支持好节省时间。

先上图:

Android从零撸美团(二) - 仿美团下拉刷新自定义动画

一、分析

美团的下拉加载动画初看挺简单的,就一个卖萌的小人。细看的话还稍微有点复杂,一共有三个状态。

  1. 刚开始下拉的时候,小脑袋从小变大的过程。
  2. 下拉到一定程度但还没松手,小人翻了个跟头直到完全出现。再往下拉保持最后完全出现的状态。
  3. 松开后左右摇头卖萌直至加载结束回弹回去。

这是三个动画啊!真佩服这些大厂,简单的加载动画都搞这么复杂。。

分析完过程该想怎么实现了。

二、反编译app看实现原理

最简单直白的方法就是反编译美团app,虽然看不到代码但资源文件能还原出来,图片和 xml 文件完美还原。

Android从零撸美团(二) - 仿美团下拉刷新自定义动画

反编译 工具 是apktool,使用方法官网上都有就不啰嗦了。

大部分图片都放在 res/drawable-xhdpi-v4res/drawable-xxhdpi-v4 两个文件夹内,仔细找下能看到多张连续的 loading 图片。这里给美团程序猿点个赞,文件命名都很规范,很好找~

Android从零撸美团(二) - 仿美团下拉刷新自定义动画
Android从零撸美团(二) - 仿美团下拉刷新自定义动画

看到图片后知道原来它用的是最普通的帧动画啊,也不是太复杂。 拿到资源图片,知道实现原理,就开工吧!

三、实现动画效果

首先自定义View CustomRefreshHeader 继承自 LinearLayout ,并实现 SmartRefreshLayoutRefreshHeader 接口。 然后主要就是重写 RefreshHeader 接口中的方法,里面提供了下拉刷新时不同阶段的回调,找到对应的方法码代码就好。

public class CustomRefreshHeader extends LinearLayout implements RefreshHeader {

    private ImageView mImage;
    private AnimationDrawable pullDownAnim;
    private AnimationDrawable refreshingAnim;

    private boolean hasSetPullDownAnim = false;

    public CustomRefreshHeader(Context context) {
        this(context, null, 0);
    }

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

    public CustomRefreshHeader(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        View view = View.inflate(context, R.layout.widget_custom_refresh_header, this);
        mImage = (ImageView) view.findViewById(R.id.iv_refresh_header);
    }

    @NonNull
    @Override
    public View getView() {
        return this;
    }

    @Override
    public SpinnerStyle getSpinnerStyle() {
        return SpinnerStyle.Translate;
    }

    @Override
    public void onStartAnimator(RefreshLayout layout, int height, int extendHeight) {

    }

    /**
     * 状态改变时调用。在这里切换第三阶段的动画卖萌小人
     * @param refreshLayout
     * @param oldState
     * @param newState
     */
    @Override
    public void onStateChanged(RefreshLayout refreshLayout, RefreshState oldState, RefreshState newState) {
        switch (newState) {
            case PullDownToRefresh: //下拉刷新开始。正在下拉还没松手时调用
                //每次重新下拉时,将图片资源重置为小人的大脑袋
                mImage.setImageResource(R.drawable.commonui_pull_image);
                break;
            case Refreshing: //正在刷新。只调用一次
                //状态切换为正在刷新状态时,设置图片资源为小人卖萌的动画并开始执行
                mImage.setImageResource(R.drawable.anim_pull_refreshing);
                refreshingAnim = (AnimationDrawable) mImage.getDrawable();
                refreshingAnim.start();
                break;
            case ReleaseToRefresh:

                break;
        }
    }

    /**
     * 下拉过程中不断调用此方法。第一阶段从小变大的小人头动画,和第二阶段翻跟头动画都在这里设置
     */
    @Override
    public void onPullingDown(float percent, int offset, int headerHeight, int extendHeight) {
        Logger.d("percent: " + percent);

        // 下拉的百分比小于100%时,不断调用 setScale 方法改变图片大小
        if (percent < 1) {
            mImage.setScaleX(percent);
            mImage.setScaleY(percent);

            //是否执行过翻跟头动画的标记
            if (hasSetPullDownAnim) {
                hasSetPullDownAnim = false;
            }
        }

        //当下拉的高度达到Header高度100%时,开始加载正在下拉的初始动画,即翻跟头
        if (percent >= 1.0) {
            //因为这个方法是不停调用的,防止重复
            if (!hasSetPullDownAnim) {
                mImage.setImageResource(R.drawable.anim_pull_end);
                pullDownAnim = (AnimationDrawable) mImage.getDrawable();
                pullDownAnim.start();

                hasSetPullDownAnim = true;
            }
        }
    }

    /**
     * 动画结束后调用
     */
    @Override
    public int onFinish(RefreshLayout layout, boolean success) {
        // 结束动画
        if (pullDownAnim != null && pullDownAnim.isRunning()) {
            pullDownAnim.stop();
        }
        if (refreshingAnim != null && refreshingAnim.isRunning()) {
            refreshingAnim.stop();
        }
        //重置状态
        hasSetPullDownAnim = false;
        return 0;
    }

    @Override
    public void onReleasing(float percent, int offset, int headerHeight, int extendHeight) {

    }

    @Override
    public void onRefreshReleased(RefreshLayout layout, int headerHeight, int extendHeight) {

    }

    @Override
    public void setPrimaryColors(int... colors) {

    }

    @Override
    public void onInitialized(RefreshKernel kernel, int height, int extendHeight) {

    }

    @Override
    public void onHorizontalDrag(float percentX, int offsetX, int offsetMax) {

    }

    @Override
    public boolean isSupportHorizontalDrag() {
        return false;
    }
}
复制代码

逻辑主要在 onStateChanged()onPullingDown() 方法里,代码中注释写的很详细。 切换状态原理是每次都给 ImageView 设置对应的资源图片或动画文件,然后得到 AnimationDrawable 开启动画,如下:

mImage.setImageResource(R.drawable.anim_pull_end);
pullDownAnim = (AnimationDrawable) mImage.getDrawable();
pullDownAnim.start();
复制代码

代码中调用:

smartRefreshLayout.setRefreshHeader(new CustomRefreshHeader(getActivity()));
        smartRefreshLayout.setOnRefreshLoadmoreListener(new OnRefreshLoadmoreListener() {
            @Override
            public void onLoadmore(RefreshLayout refreshlayout) {
                Logger.d("onLoadmore");
                smartRefreshLayout.finishLoadmore(2000, true);
            }

            @Override
            public void onRefresh(RefreshLayout refreshlayout) {
                Logger.d("onRefresh");
                smartRefreshLayout.finishRefresh(2000, true);
            }
        });
复制代码

贴出资源布局文件: widget_custom_refresh_header.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/white"
    android:gravity="center"
    android:padding="5dp">

    <ImageView
        android:id="@+id/iv_refresh_header"
        android:layout_width="41dp"
        android:layout_height="54dp"
        android:scaleX="0"
        android:scaleY="0"
        android:translationY="0dp" />

</LinearLayout>
复制代码

anim_pull_end.xml

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">

    <item
        android:drawable="@drawable/commonui_pull_end_image_frame_01"
        android:duration="100" />

    <item
        android:drawable="@drawable/commonui_pull_end_image_frame_02"
        android:duration="100" />

    <item
        android:drawable="@drawable/commonui_pull_end_image_frame_03"
        android:duration="100" />

    <item
        android:drawable="@drawable/commonui_pull_end_image_frame_04"
        android:duration="100" />

    <item
        android:drawable="@drawable/commonui_pull_end_image_frame_05"
        android:duration="100" />

</animation-list>
复制代码

anim_pull_refreshing.xml

<?xml version="1.0" encoding="utf-8"?>
<animation-list android:oneshot="false"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:duration="50" android:drawable="@drawable/commonui_refreshing_image_frame_01" />
    <item android:duration="50" android:drawable="@drawable/commonui_refreshing_image_frame_02" />
    <item android:duration="50" android:drawable="@drawable/commonui_refreshing_image_frame_03" />
    <item android:duration="50" android:drawable="@drawable/commonui_refreshing_image_frame_02" />
    <item android:duration="50" android:drawable="@drawable/commonui_refreshing_image_frame_05" />
    <item android:duration="50" android:drawable="@drawable/commonui_refreshing_image_frame_06" />
    <item android:duration="50" android:drawable="@drawable/commonui_refreshing_image_frame_07" />
    <item android:duration="50" android:drawable="@drawable/commonui_refreshing_image_frame_06" />
</animation-list>
复制代码

好啦,以上就是仿美团下拉刷新自定义动画的实现过程。 源码地址: github.com/cachecats/L…


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Introduction to Computation and Programming Using Python

Introduction to Computation and Programming Using Python

John V. Guttag / The MIT Press / 2013-7 / USD 25.00

This book introduces students with little or no prior programming experience to the art of computational problem solving using Python and various Python libraries, including PyLab. It provides student......一起来看看 《Introduction to Computation and Programming Using Python》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具