好久没写博客了,小编之前一段时间一直在找工作,从天津来到了我们的大帝都,感觉还不错。好了废话不多说了,开始我们今天的主题吧。现如今的APP各式各样,同样也带来了各种需求,一个下拉刷新都能玩出花样了,前两天订饭的时候不经意间看到了“百度外卖”的下拉刷新,今天的主题就是它–自定义下拉刷新动画

看一下实现效果吧:

动画

我们先来看看Android中的动画吧:
Android中的动画分为三种:

  • Tween动画,这一类的动画提供了旋转、平移、缩放等效果。

    • Alpha – 淡入淡出
    • Scale – 缩放效果
    • Roate – 旋转效果
    • Translate – 平移效果
  • Frame动画(帧动画),这一类动画可以创建一个Drawable序列,按照指定时间间歇一个一个显示出来。
  • Property动画(属性动画),Android3.0之后引入出来的属性动画,它更改的是对象的实际属性。

分析

我们可以看到百度外卖的下拉刷新的头是一个骑车的快递员在路上疾行,分析一下我们得到下面的动画:

  1. 背景图片的平移动画
  2. 太阳的自旋转动画
  3. 两个小轮子的自旋转动画

这就很简单了,接下来我们去百度外面的图片资源文件里找到这几张图片:(下载百度外卖的apk直接解压即可)

定义下拉刷新头文件:headview.xml

这里注意一下:我们定义了两张背景图片的ImageView是为了可以实现背景的平移动画效果。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"><ImageView
        android:id="@+id/iv_back1"android:src="@drawable/pull_back"android:layout_width="match_parent"android:layout_height="100dp" /><ImageView
        android:id="@+id/iv_back2"android:src="@drawable/pull_back"android:layout_width="match_parent"android:layout_height="100dp" /><RelativeLayout
        android:id="@+id/main"android:layout_centerHorizontal="true"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageView
            android:layout_marginTop="45dp"android:id="@+id/iv_rider"android:background="@drawable/pull_rider"android:layout_width="50dp"android:layout_height="50dp" /><ImageView
            android:id="@+id/wheel1"android:layout_marginLeft="10dp"android:layout_marginTop="90dp"android:background="@drawable/pull_wheel"android:layout_width="15dp"android:layout_height="15dp" /><ImageView
            android:id="@+id/wheel2"android:layout_marginLeft="40dp"android:layout_marginTop="90dp"android:background="@drawable/pull_wheel"android:layout_width="15dp"android:layout_height="15dp" /></RelativeLayout><ImageView
        android:id="@+id/ivsun"android:layout_marginTop="20dp"android:layout_toRightOf="@+id/main"android:background="@drawable/pull_sun"android:layout_width="30dp"android:layout_height="30dp" /></RelativeLayout>

接下来我们定义动画效果:

背景图片的平移效果:
实现两个animation xml文件,一个起始位置在100%,结束位置在0%,设置repeat属性为循环往复。

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator"><translate android:fromXDelta="100%p" android:toXDelta="0%p"android:repeatMode="restart"android:interpolator="@android:anim/linear_interpolator"android:repeatCount="infinite"android:duration="5000" />
</set>

另一个起始位置在0%,结束位置在-100%

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator"><translate android:fromXDelta="0%p" android:toXDelta="-100%p"android:repeatMode="restart"android:interpolator="@android:anim/linear_interpolator"android:repeatCount="infinite"android:duration="5000" />
</set>

太阳围绕中心旋转动画:
从0-360度开始循环旋转,旋转所用时间为1s,旋转中心距离view的左定点上边缘为50%的距离,也就是正中心。

下面是具体属性:

android:fromDegrees 起始的角度度数

android:toDegrees 结束的角度度数,负数表示逆时针,正数表示顺时针。如10圈则比android:fromDegrees大3600即可

android:pivotX 旋转中心的X坐标

浮点数或是百分比。浮点数表示相对于Object的左边缘,如5; 百分比表示相对于Object的左边缘,如5%; 另一种百分比表示相对于父容器的左边缘,如5%p; 一般设置为50%表示在Object中心

android:pivotY 旋转中心的Y坐标

浮点数或是百分比。浮点数表示相对于Object的上边缘,如5; 百分比表示相对于Object的上边缘,如5%; 另一种百分比表示相对于父容器的上边缘,如5%p; 一般设置为50%表示在Object中心

android:duration 表示从android:fromDegrees转动到android:toDegrees所花费的时间,单位为毫秒。可以用来计算速度。

android:interpolator表示变化率,但不是运行速度。一个插补属性,可以将动画效果设置为加速,减速,反复,反弹等。默认为开始和结束慢中间快,

android:startOffset 在调用start函数之后等待开始运行的时间,单位为毫秒,若为10,表示10ms后开始运行

android:repeatCount 重复的次数,默认为0,必须是int,可以为-1表示不停止

android:repeatMode 重复的模式,默认为restart,即重头开始重新运行,可以为reverse即从结束开始向前重新运行。在android:repeatCount大于0或为infinite时生效

android:detachWallpaper 表示是否在壁纸上运行

android:zAdjustment 表示被animated的内容在运行时在z轴上的位置,默认为normal。

normal保持内容当前的z轴顺序

top运行时在最顶层显示

bottom运行时在最底层显示

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"><rotate
        android:fromDegrees="0"android:toDegrees="360"android:duration="1000"android:repeatCount="-1"android:pivotX="50%"android:pivotY="50%" />
</set>

同理轮子的动画也一样,不占代码了。

动画定义完了我们开始定义下拉刷新列表,下拉刷新网上有很多,不详细的说了,简单的改造一下,根据刷新状态开启关闭动画即可。
注释写的很详细,看一下代码吧:

package com.hankkin.baidugoingrefreshlayout;import android.widget.AbsListView;
import android.widget.ListView;import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.RelativeLayout;/*** Created by Hankkin on 16/4/10.*/
public class BaiDuRefreshListView extends ListView implements AbsListView.OnScrollListener{private static final int DONE = 0;      //刷新完毕状态private static final int PULL_TO_REFRESH = 1;   //下拉刷新状态private static final int RELEASE_TO_REFRESH = 2;    //释放状态private static final int REFRESHING = 3;    //正在刷新状态private static final int RATIO = 3;private RelativeLayout headView;    //下拉刷新头private int headViewHeight; //头高度private float startY;   //开始Y坐标private float offsetY;  //Y轴偏移量private OnBaiduRefreshListener mOnRefreshListener;  //刷新接口private int state;  //状态值private int mFirstVisibleItem;  //第一项可见item索引private boolean isRecord;   //是否记录private boolean isEnd;  //是否结束private boolean isRefreable;    //是否刷新private ImageView ivWheel1,ivWheel2;    //轮组图片组件private ImageView ivRider;  //骑手图片组件private ImageView ivSun,ivBack1,ivBack2;    //太阳、背景图片1、背景图片2private Animation wheelAnimation,sunAnimation;  //轮子、太阳动画private Animation backAnimation1,backAnimation2;    //两张背景图动画public BaiDuRefreshListView(Context context) {super(context);init(context);}public BaiDuRefreshListView(Context context, AttributeSet attrs) {super(context, attrs);init(context);}public BaiDuRefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context);}public interface OnBaiduRefreshListener{void onRefresh();}/*** 回调接口,想实现下拉刷新的listview实现此接口* @param onRefreshListener*/public void setOnBaiduRefreshListener(OnBaiduRefreshListener onRefreshListener){mOnRefreshListener = onRefreshListener;isRefreable = true;}/*** 刷新完毕,从主线程发送过来,并且改变headerView的状态和文字动画信息*/public void setOnRefreshComplete(){//一定要将isEnd设置为true,以便于下次的下拉刷新isEnd = true;state = DONE;changeHeaderByState(state);}private void init(Context context) {//关闭view的OverScrollsetOverScrollMode(OVER_SCROLL_NEVER);setOnScrollListener(this);//加载头布局headView = (RelativeLayout) LayoutInflater.from(context).inflate(R.layout.headview,this,false);//测量头布局measureView(headView);//给ListView添加头布局addHeaderView(headView);//设置头文件隐藏在ListView的第一项headViewHeight = headView.getMeasuredHeight();headView.setPadding(0, -headViewHeight, 0, 0);//获取头布局图片组件ivRider = (ImageView) headView.findViewById(R.id.iv_rider);ivSun = (ImageView) headView.findViewById(R.id.ivsun);ivWheel1 = (ImageView) headView.findViewById(R.id.wheel1);ivWheel2 = (ImageView) headView.findViewById(R.id.wheel2);ivBack1 = (ImageView) headView.findViewById(R.id.iv_back1);ivBack2 = (ImageView) headView.findViewById(R.id.iv_back2);//获取动画wheelAnimation = AnimationUtils.loadAnimation(context, R.anim.tip);sunAnimation = AnimationUtils.loadAnimation(context, R.anim.tip1);backAnimation1 = AnimationUtils.loadAnimation(context, R.anim.a);backAnimation2 = AnimationUtils.loadAnimation(context, R.anim.b);state = DONE;isEnd = true;isRefreable = false;}@Overridepublic void onScrollStateChanged(AbsListView absListView, int i) {}@Overridepublic void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {mFirstVisibleItem = firstVisibleItem;}@Overridepublic boolean onTouchEvent(MotionEvent ev) {if (isEnd) {//如果现在时结束的状态,即刷新完毕了,可以再次刷新了,在onRefreshComplete中设置if (isRefreable) {//如果现在是可刷新状态   在setOnMeiTuanListener中设置为trueswitch (ev.getAction()){//用户按下case MotionEvent.ACTION_DOWN://如果当前是在listview顶部并且没有记录y坐标if (mFirstVisibleItem == 0 && !isRecord) {//将isRecord置为true,说明现在已记录y坐标isRecord = true;//将当前y坐标赋值给startY起始y坐标startY = ev.getY();}break;//用户滑动case MotionEvent.ACTION_MOVE://再次得到y坐标,用来和startY相减来计算offsetY位移值float tempY = ev.getY();//再起判断一下是否为listview顶部并且没有记录y坐标if (mFirstVisibleItem == 0 && !isRecord) {isRecord = true;startY = tempY;}//如果当前状态不是正在刷新的状态,并且已经记录了y坐标if (state!=REFRESHING && isRecord ) {//计算y的偏移量offsetY = tempY - startY;//计算当前滑动的高度float currentHeight = (-headViewHeight+offsetY/3);//用当前滑动的高度和头部headerView的总高度进行比 计算出当前滑动的百分比 0到1float currentProgress = 1+currentHeight/headViewHeight;//如果当前百分比大于1了,将其设置为1,目的是让第一个状态的椭圆不再继续变大if (currentProgress>=1) {currentProgress = 1;}//如果当前的状态是放开刷新,并且已经记录y坐标if (state == RELEASE_TO_REFRESH && isRecord) {setSelection(0);//如果当前滑动的距离小于headerView的总高度if (-headViewHeight+offsetY/RATIO<0) {//将状态置为下拉刷新状态state = PULL_TO_REFRESH;//根据状态改变headerView,主要是更新动画和文字等信息changeHeaderByState(state);//如果当前y的位移值小于0,即为headerView隐藏了}else if (offsetY<=0) {//将状态变为donestate = DONE;stopAnim();//根据状态改变headerView,主要是更新动画和文字等信息changeHeaderByState(state);}}//如果当前状态为下拉刷新并且已经记录y坐标if (state == PULL_TO_REFRESH && isRecord) {setSelection(0);//如果下拉距离大于等于headerView的总高度if (-headViewHeight+offsetY/RATIO>=0) {//将状态变为放开刷新state = RELEASE_TO_REFRESH;//根据状态改变headerView,主要是更新动画和文字等信息changeHeaderByState(state);//如果当前y的位移值小于0,即为headerView隐藏了}else if (offsetY<=0) {//将状态变为donestate = DONE;//根据状态改变headerView,主要是更新动画和文字等信息changeHeaderByState(state);}}//如果当前状态为done并且已经记录y坐标if (state == DONE && isRecord) {//如果位移值大于0if (offsetY>=0) {//将状态改为下拉刷新状态state = PULL_TO_REFRESH;changeHeaderByState(state);}}//如果为下拉刷新状态if (state == PULL_TO_REFRESH) {//则改变headerView的padding来实现下拉的效果headView.setPadding(0,(int)(-headViewHeight+offsetY/RATIO) ,0,0);}//如果为放开刷新状态if (state == RELEASE_TO_REFRESH) {//改变headerView的padding值headView.setPadding(0,(int)(-headViewHeight+offsetY/RATIO) ,0, 0);}}break;//当用户手指抬起时case MotionEvent.ACTION_UP://如果当前状态为下拉刷新状态if (state == PULL_TO_REFRESH) {//平滑的隐藏headerViewthis.smoothScrollBy((int)(-headViewHeight+offsetY/RATIO)+headViewHeight, 500);//根据状态改变headerViewchangeHeaderByState(state);}//如果当前状态为放开刷新if (state == RELEASE_TO_REFRESH) {//平滑的滑到正好显示headerViewthis.smoothScrollBy((int)(-headViewHeight+offsetY/RATIO), 500);//将当前状态设置为正在刷新state = REFRESHING;//回调接口的onRefresh方法mOnRefreshListener.onRefresh();//根据状态改变headerViewchangeHeaderByState(state);}//这一套手势执行完,一定别忘了将记录y坐标的isRecord改为false,以便于下一次手势的执行isRecord = false;break;}}}return super.onTouchEvent(ev);}/*** 根据状态改变headerView的动画和文字显示* @param state*/private void changeHeaderByState(int state){switch (state) {case DONE://如果的隐藏的状态//设置headerView的padding为隐藏headView.setPadding(0, -headViewHeight, 0, 0);startAnim();break;case RELEASE_TO_REFRESH://当前状态为放开刷新break;case PULL_TO_REFRESH://当前状态为下拉刷新startAnim();break;case REFRESHING://当前状态为正在刷新break;default:break;}}/*** 测量View* @param child*/private void measureView(View child) {ViewGroup.LayoutParams p = child.getLayoutParams();if (p == null) {p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);}int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);int lpHeight = p.height;int childHeightSpec;if (lpHeight > 0) {childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,MeasureSpec.EXACTLY);} else {childHeightSpec = MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED);}child.measure(childWidthSpec, childHeightSpec);}/*** 开启动画*/public void startAnim(){ivBack1.startAnimation(backAnimation1);ivBack2.startAnimation(backAnimation2);ivSun.startAnimation(sunAnimation);ivWheel1.startAnimation(wheelAnimation);ivWheel2.startAnimation(wheelAnimation);}/*** 关闭动画*/public void stopAnim(){ivBack1.clearAnimation();ivBack2.clearAnimation();ivSun.clearAnimation();ivWheel1.clearAnimation();ivWheel2.clearAnimation();}
}

好了,自定义下拉刷新动画我们就实现了,其实很简单,所有的下拉刷新动画都类似这样实现的。源码我已经上传到Github上了:
https://github.com/Hankkin/BaiduGoingRefreshLayout
求star啊。有不合理的地方还希望大家多多指正,共同进步哈。

Android自定义下拉刷新动画--仿百度外卖下拉刷新相关推荐

  1. php 点击下拉显示内容,php+ajax实现仿百度查询下拉内容功能示例

    本文实例讲述了php+ajax实现仿百度查询下拉内容功能.分享给大家供大家参考,具体如下: 运行效果如下: html代码: Document body{ margin:0; padding: 0; } ...

  2. ajax下拉列表模糊,JS仿百度自动下拉框模糊匹配提示

    实际项目中,我们可以把数据获取改成ajax动态获取,在 getContent()中 js/jQuery实现类似百度搜索功能 #container { position: absolute; left: ...

  3. android仿百度外卖波浪_头像随波浪漂浮效果—仿Android百度外卖

    前几天看到有iOS仿百度外卖的个人页,所以就随手撸了个Android.效果图如下: demo.gif 源码 改造轮子 轮子就不重复造了,github上已经有实现波浪的效果WaveView,WaveVi ...

  4. android view 渐变动画,Android自定义view渐变圆形动画

    本文实例为大家分享了Android自定义view渐变圆形动画的具体代码,供大家参考,具体内容如下 直接上效果图 自定义属性 attrs.xml文件 创建一个类 ProgressRing继承自 view ...

  5. Android自定义view之围棋动画

    Android自定义view之围棋动画 好久不见,最近公众号内粉丝要求上新一篇有点难度的自定义view文章,所以它来了!! 干货文,建议收藏 文章目录 Android自定义view之围棋动画 前言 完 ...

  6. android 海浪动画,android自定义波浪加载动画的实现代码

    本文实例为大家分享了android自定义波浪加载动画的具体代码,供大家参考,具体内容如下 效果图 1.自定义控件 WaveView package com.example.wh.myapplicati ...

  7. android 自定义加载动画效果,Android自定义View实现loading动画加载效果

    项目开发中对Loading的处理是比较常见的,安卓系统提供的不太美观,引入第三发又太麻烦,这时候自己定义View来实现这个效果,并且进行封装抽取给项目提供统一的loading样式是最好的解决方式了. ...

  8. android 仿饿了么地图,iOS Andriod百度地图仿百度外卖 饿了么 选择我的地址 POI检索...

    title: iOS Andriod百度地图仿百度外卖 饿了么 选择我的地址 POI检索 date: 2015-09-19 21:06:26 tags: 百度外卖选择送货地址:饿了么选择送货地址: 第 ...

  9. iOS 仿百度外卖,饿了么-点餐效果(加入购物车效果)

    今天带来的是仿百度外卖,饿了么-点餐效果既物品加入购物车时的动画效果,这里用的Masonry布局,下滑的效果主要是通过UIBezierPath CAKeyframeAnimation CABasicA ...

最新文章

  1. 技术图文:Matlab VS. Numpy 常见矩阵
  2. “无效数字” ;java.lang.Integer cannot be cast to java.lang.String
  3. java从键盘输入数据斐波那契数_从键盘输入一个正整数n,打印出斐波那契数列的前n个元素...
  4. git reset revert 回退回滚取消提交返回上一版本
  5. android - FlutterActivity MethodChannel和FlutterView
  6. python中的 if __name__ == '__main_'的作用和原理
  7. MCU为什么内部不集成晶振
  8. matlab save txt 乱码,matlab代码或中文复制到word就变成乱码怎么办?
  9. @kafkalistener中id的作用_SSM框架(十一):Spring框架中的IoC(1)
  10. 圆通速递:2022年1月快递产品收入36.19亿元 同比增长20.09%
  11. 【Elasticsearch】es FST (Finite State Tranduer) 有限状态转换器
  12. [转 js] 分析JsUnit
  13. mysql汽车品牌系列_爬取汽车之家汽车品牌型号系列数据
  14. 不允许有匹配 [xX][mM][lL] 的处理指令目标
  15. opencv实战从0到N (14)- 凸包算法 convexity defects
  16. qq屏蔽怎么知道对方信息(qq屏蔽怎么让对方知道)
  17. 火狐浏览器插件开发小试
  18. 《Windows 8 权威指南》——2.5 Windows 8 Metro应用内存回收机制
  19. nb_samples(采样数)转换
  20. HTTP之Chunk

热门文章

  1. Excel VBA读取其它excel单元格内容详细案例
  2. 洋垃圾如何安装windows11学习心得
  3. MFC 渐变色背景以及控件透明处理
  4. P3801 红色的幻想乡(树状数组)
  5. IT面试技巧之亲徒弟的面经分享
  6. 最新爆笑段子120个,看了心境很舒服的噢(转吧转吧!)
  7. flops什么意思中文_FLOPS的含义及其计算方式
  8. Linux常用端口总结
  9. 常见TCP/UDP端口号大全
  10. Awstats安装配置