俗话说好记性不如烂笔头,决定以后将研究过的东西写到博客里,方便自己以后查找,也方便技术分享。第一篇从基础的自定义下拉刷新开始。这里说下,我是在大神的肩膀上进行自定义的,因为自己重写下拉刷新的话会有很多边界,状态和动画等问题要处理,以前写过一次,效果和功能可以实现,但是有不少bug,而且封装也不好。因此直接使用android-Ultra-Pull-To-Refresh

效果图

本文是基于github上的一个开源项目:android-Ultra-Pull-To-Refresh(下面简称UltraPtr) ,有兴趣的同学可以去看看。废话不多说,开车。

1.准备headview头部布局

<?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:layout_centerHorizontal="true"android:gravity="center_vertical"android:orientation="horizontal"android:paddingTop="0dp"><ImageViewandroid:id="@+id/iv_ptr"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="100dp"android:src="@drawable/ptr_dra" /><TextViewandroid:id="@+id/tv_ptr"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="下拉刷新..." />
</LinearLayout>

布局很简单,就是线性布局套一个imageview和textview。

2. 实现PtrUIHandler,处理下拉刷新回调

这一步是最重要的,几乎所有的刷新样式变化的逻辑都是在这里进行处理的,看代码。

public class RefreshHeadView extends FrameLayout implements PtrUIHandler {private Context context;private ImageView iv;private TextView tv;private AnimationDrawable animationDrawable;public RefreshHeadView(Context context) {super(context);this.context = context;initView();}private void initView() {View.inflate(context, R.layout.ptrheadview, this);iv = (ImageView) findViewById(R.id.iv_ptr);tv = (TextView) findViewById(R.id.tv_ptr);animationDrawable = (AnimationDrawable) iv.getDrawable();}public RefreshHeadView(Context context, AttributeSet attrs) {super(context, attrs);}public RefreshHeadView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overridepublic void onUIReset(PtrFrameLayout frame) {//onUIRefreshComplete之后调用,用于复位setImageAndText(R.mipmap.ptr_loading_9, "刷新完成...");}@Overridepublic void onUIRefreshPrepare(PtrFrameLayout frame) {//开始下拉的时候调用一次setImageAndText(R.mipmap.ptr_loading_1, "下拉刷新...");}@Overridepublic void onUIRefreshBegin(PtrFrameLayout frame) {//正在刷新的时候调用,开始帧动画iv.setImageDrawable(animationDrawable);animationDrawable.start();tv.setText("F5的能量女朋友出现了");}@Overridepublic void onUIRefreshComplete(PtrFrameLayout frame) {//刷新完成的时候调用animationDrawable.stop();setImageAndText(R.mipmap.ptr_loading_9, "刷新完成...");}@Overridepublic void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) {//触发刷新的高度  默认是头部高度的1.2倍final int offsetToRefresh = ptrIndicator.getOffsetToRefresh();//当前下拉的高度final int currentPos = ptrIndicator.getCurrentPosY();//计算下拉的百分比int persent = (int) (((double) currentPos / offsetToRefresh) * 100);//只有在prepare状态的时候才执行下面的替换图片if (status!=PtrFrameLayout.PTR_STATUS_PREPARE) {return;}//根据不同的比例替换不同的图片和文字if (persent < 50) {setImageAndText(R.mipmap.ptr_loading_1, "下拉刷新");} else if (persent < 60) {setImageAndText(R.mipmap.ptr_loading_2, "下拉刷新");} else if (persent < 70) {setImageAndText(R.mipmap.ptr_loading_3, "下拉刷新");} else if (persent < 80) {setImageAndText(R.mipmap.ptr_loading_4, "下拉刷新");} else if (persent < 100) {setImageAndText(R.mipmap.ptr_loading_5, "下拉刷新");} else {setImageAndText(R.mipmap.ptr_loading_6, "松开松开~");}}private void setImageAndText(int res, String text) {iv.setImageResource(res);tv.setText(text);}
}

代码里基本上注释都写很清楚了,只需要注意几个地方。

在构造方法中initView().将刚才定义的headview填充进来,并找到里面的imageview和textview

实现PtrUIHandler,需要重写几个方法。
onUIRefreshPrepare 下拉准备,刚开始下拉的时候就会调用一次。这里我也没什么好准备的,就设置了第一张图片。

onUIRefreshBegin 这是刷新的时候调用一次,按照acfun的下拉刷新,会显示一个萌萌的妹纸动画。所以对图片设置帧动画,并开启帧动画,这样刷新的时候就是小人动的效果

onUIRefreshComplete 刷新完成,停止帧动画

onUIRefreshReset 复位,最后调用,设置了一张图片。

onUIPositionChange 这是比较复杂一些的方法。这个回调会传给我们下拉的距离和手指是否在下拉等参数,用于下拉的时候变换图片。这里介绍一下参数,frame不用说了,父类。isUnderTouch,表示手指是否按在屏幕上。status:表示当前的刷新状态,有init  prepare loading comlete四种。最后ptrIndicator是手势类。首先获取到触发刷新的高度,这个是可以自己设置的,默认头部高度1.2倍。然后获取当前下拉的高度,根据两个高度计算下拉的百分比。最后根据百分比来替换不同的图片就可以了。其中有一点要注意,只有status在prepare的时候才执行替换图片,否则高度每次变化都会调用此方法不停替换图片。

 //只有在prepare状态的时候才执行下面的替换图片if (status!=PtrFrameLayout.PTR_STATUS_PREPARE) {return;}

至此,整个headview和uihandler都处理完成。

3,自定义PullToRefreshLayout

public class PullToRefreshLayout extends PtrFrameLayout {private Context context;private float startY;private float startX;// 记录viewPager是否拖拽的标记private boolean mIsHorizontalMove;// 记录事件是否已被分发private boolean isDeal;// viewpager触发滑动的距离private int mTouchSlop;private ArrayList<ViewPager> mViewPagers = new ArrayList<>();public PullToRefreshLayout(Context context) {super(context,null);}public PullToRefreshLayout(Context context, AttributeSet attrs) {super(context, attrs);this.context=context;initView();}private void initView() {RefreshHeadView headview = new RefreshHeadView(context);//设置headviewsetHeaderView(headview);//设置uihandler回调  因为headview实现了PtrUIHandler接口,所以这里还是headviewaddPtrUIHandler(headview);//阻尼系数 默认1.7f 越大下拉越吃力setResistance(1.7f);//触发刷新时移动的位置比例 默认,1.2f,移动达到头部高度1.2倍时可触发刷新操作。setRatioOfHeaderHeightToRefresh(1.2f);//回弹延时  默认 200ms,回弹到刷新高度所用时间setDurationToClose(500);//头部回弹时间 默认1000mssetDurationToCloseHeader(1500);//下拉刷新还是释放刷新  默认下拉刷新default is falsesetPullToRefresh(false);// default is true 刷新时是否保持头部setKeepHeaderWhenRefresh(true);//viewpager处理final ViewConfiguration configuration = ViewConfiguration.get(getContext());mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);}//处理下拉刷新和viewpager滑动冲突@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {if (mViewPagers.size() ==0) {return super.dispatchTouchEvent(ev);}int action = ev.getAction();switch (action) {case MotionEvent.ACTION_DOWN:// 记录手指按下的位置startY = ev.getY();startX = ev.getX();// 初始化标记mIsHorizontalMove = false;isDeal = false;break;case MotionEvent.ACTION_MOVE:// 如果已经判断出是否由横向还是纵向处理,则跳出if (isDeal) {break;}/**拦截禁止交给Ptr的 dispatchTouchEvent处理**/mIsHorizontalMove = true;// 获取当前手指位置float endY = ev.getY();float endX = ev.getX();float distanceX = Math.abs(endX - startX);float distanceY = Math.abs(endY - startY);if (distanceX != distanceY) {// 如果X轴位移大于Y轴位移,那么将事件交给viewPager处理。//横向滑动的距离大于触发viewpager滑动事件的距离并且 横向大于竖向if (distanceX > mTouchSlop && distanceX > distanceY) {mIsHorizontalMove = true;isDeal = true;} else if (distanceY > mTouchSlop) {mIsHorizontalMove = false;isDeal = true;}}break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL://下拉刷新状态时如果滚动了viewpager 此时mIsHorizontalMove为true 会导致PtrFrameLayout无法恢复原位// 初始化标记,mIsHorizontalMove = false;isDeal = false;break;}if (mIsHorizontalMove) {//相当于拦截事件,不交给ptr处理,直接向下分发return dispatchTouchEventSupper(ev);}//交给ptr,不做拦截,正常下拉return super.dispatchTouchEvent(ev);}@Overrideprotected void onLayout(boolean flag, int i, int j, int k, int l) {super.onLayout(flag, i, j, k, l);if (flag) {getAlLViewPager(mViewPagers, this);}}/*** 获取SwipeBackLayout里面的ViewPager的集合*/private void getAlLViewPager(List<ViewPager> mViewPagers, ViewGroup parent) {int childCount = parent.getChildCount();for (int i = 0; i < childCount; i++) {View child = parent.getChildAt(i);if (child instanceof ViewPager) {mViewPagers.add((ViewPager) child);} else if (child instanceof ViewGroup) {getAlLViewPager(mViewPagers, (ViewGroup) child);}}}
}

这个类就是我们最终要使用到布局里面的类。比较简单,分为两个部分

1.initPara()  用于设置各种参数,具体看代码都有注释

2,处理下拉刷新和viewpager的滑动冲突。虽然UIPtr的作者也更新了处理冲突的方法,但是处理的并不太好,在viewpager上的滑动还是太敏感。所以这里重写dispatchTouchEvent方法,判断子view里是否有viewpager,如果没有,直接交给父类处理。如果有,判断滑动方向和距离。如果是横向滑动,那么就不要交给父类处理,直接跳过父类,交给父类的父类处理。否则就是正常下拉刷新,还是交给父类。

至此,整个自定义下拉刷新完成。

源码地址 https://github.com/itwangyu/MyPullRefreshDemo

自定义下拉刷新之仿AcFun下拉刷新相关推荐

  1. Android自定义下拉刷新动画--仿百度外卖下拉刷新

    好久没写博客了,小编之前一段时间一直在找工作,从天津来到了我们的大帝都,感觉还不错.好了废话不多说了,开始我们今天的主题吧.现如今的APP各式各样,同样也带来了各种需求,一个下拉刷新都能玩出花样了,前 ...

  2. 仿美团下拉刷新控件(二)

    如果想学习更多进阶知识,可以关注我的微信公众号:Android小菜. 也可以直接扫描二维码关注: 转载本专栏文章,请注明出处,尊重原创 .文章博客地址:道龙的博客 本篇是实现仿美团下拉刷新控件的第二篇 ...

  3. 仿美团下拉刷新控件(一)

    如果想学习更多进阶知识,可以关注我的微信公众号:Android小菜. 也可以直接扫描二维码关注: 转载本专栏文章,请注明出处,尊重原创 .文章博客地址:道龙的博客 很有幸能进入美团.本文就仿写一下美团 ...

  4. Android自定义控件之仿美团下拉刷新

    android美团下拉刷新控件自定义控件 目录(?)[+]   美团的下拉刷新分为三个状态:  第一个状态为下拉刷新状态(pull to refresh),在这个状态下是一个绿色的椭圆随着下拉的距离动 ...

  5. Android自定义控件实战——实现仿IOS下拉刷新上拉加载 PullToRefreshLayout

    下拉刷新控件,网上有很多版本,有自定义Layout布局的,也有封装控件的,各种实现方式的都有.但是很少有人告诉你具体如何实现的,今天我们就来一步步实现自己封装的 PullToRefreshLayout ...

  6. android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能,高仿京东下拉刷新,轻松上手!...

    直接进入主题,先来看一下京东的实现效果: jd.gif 以及我自己的实现效果: myjd.gif 实现过程 1.下拉原理 layout.png 整个布局为继承自LinearLayout的Viewgro ...

  7. Java txt 下拉刷新_手写上拉加载,下拉刷新(小demo)

    背景 使用过很多下拉刷新,上拉加载的插件,虽然也知道一点原理,但似乎一直不太完全能理解它,闲来无事,手写一个,感受下,借鉴了better-scroll的源码,功能当然相差甚远,也只是个简易版的实现,大 ...

  8. android 下拉刷新listview,实现Android下拉刷新的ListView

    ListView的下拉刷新及上拉加载更多数据是我们开发中通常要实现的功能,开源项目中有很多的上下拉加载刷新的ListView可直接拿来使用,这几天刚好学习了下刷新的实现方式,把学习的资料做个记录: 实 ...

  9. Android使用RecyclerView实现上拉加载更多,下拉刷新,分组显示

    项目地址:点击打开链接(https://github.com/MrGaoGang/luckly_recyclerview) 使用RecyclerView封装headerview,footerView, ...

最新文章

  1. 《移动App测试实战》——2.2 App UI层面的自动化
  2. 深入浅析Python 函数注解与匿名函数
  3. Thinkphp5 分页带参数(亲测)
  4. win_redis【win下安装使用redis】
  5. [Java基础]增强for循环
  6. 布朗的计算机排名,布朗大学计算机工程硕士排名第26(2020年TFE Times排名)
  7. js课程 2-6 js如何进行类型转换及js运算符有哪些
  8. windows7文本文档换成c语言,win10电脑新建文本文档默认编码是UTF-8怎么修改成ANSI编码...
  9. (1.5万字图文)解读华为集成产品开发IPD之市场管理流程(MM流程)
  10. 制作简易的幸运转盘抽奖
  11. 交换机、路由器、网关
  12. 2018-2019 起风了,唯有努力生存
  13. MATLAB获取屏幕大小
  14. 成都聚华祥科技:标题的关键词组合技巧
  15. Linux 命令一览表,持续更新中
  16. Nokia 5530XM
  17. 光荣与梦想:Uniswap的2020回顾和2021展望
  18. 一文彻底掌握时间复杂度和大O表示法
  19. vb中线性拟合_VB做曲线拟合
  20. QA11 QA32增强

热门文章

  1. Android中Hilt的简单使用
  2. 2018ICPC焦作站赛后总结
  3. 使用GDB和GEF进行调试
  4. 使用certbot获取 Let‘s Encrypt CA证书
  5. iOS-xcode模拟器录屏
  6. python函数装饰器一篇入魂
  7. (18)UVM sequencer和sequence
  8. Hololens动态显示图片
  9. Ant Design Pro -- 02项目结构@20210331
  10. 学Python爬虫,就得从爬高清美图开始!