内容简介:这是【从零撸美团】系列文章第二篇。 项目地址:今天写了下拉刷新,框架用的是先上图:
这是【从零撸美团】系列文章第二篇。 项目地址: 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 下拉刷新组件原理及简单实现
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
翻转课堂的可汗学院
萨尔曼·可汗(Salman Khan) / 刘婧 / 浙江人民出版社 / 2014-4-1 / 49.00元
MIT和哈佛毕业的高材生缘何放弃金融分析师工作投身教育事业?YouTube上的“可汗学院频道”至今共吸引了163.3万订阅者,观看次数超过3.55亿次,它为什么如此大受欢迎?创始人萨尔曼·可汗阐述属于未来的教育理念——让地球上的任何人都能随时随地享受世界一流的免费教育! 现行教育模式已有200余年历史,可汗认为,在互联网蓬勃发展、社交网络盛况空前的时代,免费、灵活、适合个体、全球共享的教育才......一起来看看 《翻转课堂的可汗学院》 这本书的介绍吧!