内容简介:这是【从零撸美团】系列文章第二篇。 项目地址:今天写了下拉刷新,框架用的是先上图:
这是【从零撸美团】系列文章第二篇。 项目地址: github.com/cachecats/L…
今天写了下拉刷新,框架用的是 SmartRefreshLayout ,不为啥,因为 Github 上它有 9.5k
个 star
,中文支持好节省时间。
先上图:
一、分析
美团的下拉加载动画初看挺简单的,就一个卖萌的小人。细看的话还稍微有点复杂,一共有三个状态。
- 刚开始下拉的时候,小脑袋从小变大的过程。
- 下拉到一定程度但还没松手,小人翻了个跟头直到完全出现。再往下拉保持最后完全出现的状态。
- 松开后左右摇头卖萌直至加载结束回弹回去。
这是三个动画啊!真佩服这些大厂,简单的加载动画都搞这么复杂。。
分析完过程该想怎么实现了。
二、反编译app看实现原理
最简单直白的方法就是反编译美团app,虽然看不到代码但资源文件能还原出来,图片和 xml 文件完美还原。
反编译 工具 是apktool,使用方法官网上都有就不啰嗦了。
大部分图片都放在 res/drawable-xhdpi-v4
和 res/drawable-xxhdpi-v4
两个文件夹内,仔细找下能看到多张连续的 loading 图片。这里给美团程序猿点个赞,文件命名都很规范,很好找~
看到图片后知道原来它用的是最普通的帧动画啊,也不是太复杂。 拿到资源图片,知道实现原理,就开工吧!
三、实现动画效果
首先自定义View CustomRefreshHeader
继承自 LinearLayout
,并实现 SmartRefreshLayout
的 RefreshHeader
接口。 然后主要就是重写 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…
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Swift iOS : 上拉刷新或者下拉刷新
- Swift iOS : 上拉刷新或者下拉刷新
- 上拉加载 下拉刷新
- 炫酷的列表下拉刷新效果
- Android中ListView下拉刷新的实现
- iOS 下拉刷新组件原理及简单实现
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
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》 这本书的介绍吧!