PullToRefresh 这个库用的是很至多。github  今天主要分析一下源代码实现.

我们通过ListView的下拉刷新进行分析。其他的类似。

整个下拉刷新  父View是LinearLayout, 在LinearLayout加入了Header View ,Footer View,和ListView

PullToRefreshBase 是父类 扩展了 LinearLayout水平布局   假设我们使用ListView 须要观看子类  PullToRefreshAdapterViewBase -> PullToRefreshListView

初始化代码在PullToRefreshBase init方法中

重点代码:

		// Refreshable View
// By passing the attrs, we can add ListView/GridView params via XML
mRefreshableView = createRefreshableView(context, attrs);//通过子类传入的View,ListView或者ScrollView等
addRefreshableView(context, mRefreshableView);//加入view到布局中 // We need to create now layouts now 创建Header和Footer视图,默认是INVISIBLE。要加入到父窗体
mHeaderLayout = createLoadingLayout(context, Mode.PULL_FROM_START, a);
mFooterLayout = createLoadingLayout(context, Mode.PULL_FROM_END, a); handleStyledAttributes(a);//加入loadingView效果。这里是把View加入到ListView HeaderView里面去
updateUIForMode(); //把布局加入到父View中
	protected void updateUIForMode() {
final LinearLayout.LayoutParams lp = getLoadingLayoutLayoutParams();
// Remove Header, and then add Header Loading View again if needed
if (this == mHeaderLayout.getParent()) {
removeView(mHeaderLayout);
}
if (mMode.showHeaderLoadingLayout()) {
addViewInternal(mHeaderLayout, 0, lp);//增加View到LinearLayout
} // Remove Footer, and then add Footer Loading View again if needed
if (this == mFooterLayout.getParent()) {
removeView(mFooterLayout);
}
if (mMode.showFooterLoadingLayout()) {
addViewInternal(mFooterLayout, lp);//增加View到LinearLayout
} // Hide Loading Views
refreshLoadingViewsSize();//把headerView隐藏起来,其有用的是padding的方式 设置为负值 就到屏幕顶部的外面了 // If we're not using Mode.BOTH, set mCurrentMode to mMode, otherwise
// set it to pull down
mCurrentMode = (mMode != Mode.BOTH) ? mMode : Mode.PULL_FROM_START;
}

//这里有2个LoadingView,一个是增加到LinearLayout中去了,另一个是增加到ListView本身的Header里面

看看handleStyledAttributes方法 定位到子类复写的地方



FrameLayout frame = new FrameLayout(getContext());

mHeaderLoadingView = createLoadingLayout(getContext(), Mode.PULL_FROM_START, a);

mHeaderLoadingView.setVisibility(View.GONE);

frame.addView(mHeaderLoadingView, lp);

mRefreshableView.addHeaderView(frame, null, false);//加入LoadingView到ListView Header上



        //headerView一共同拥有2个LoadingView,一个是被增加到LinearLayout一个是被增加到ListView的HeaderView

addViewInternal方法就是增加到LinearLayout父类中 





看看LoadingLayout 有2种  FlipLoadingLayout 和  RotateLoadingLayout 一般我们用旋转的载入动画

左边一个旋转图片,右边是文字和时间提示

    

第一个LoadingLayout主要显示 :下拉刷新,放开以刷新

第二个LoadingLayout显示松手后的文字:正在加载...

结构是这样

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYmlyZHNhY3Rpb24=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

当UI初始化好,以下看看onTouch 下拉捕获事件

	public final boolean onTouchEvent(MotionEvent event) {
if (!isPullToRefreshEnabled()) {
return false;
}
// If we're refreshing, and the flag is set. Eat the event
if (!mScrollingWhileRefreshingEnabled && isRefreshing()) {
return true;
}
if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE: {
if (mIsBeingDragged) {
mLastMotionY = event.getY();
mLastMotionX = event.getX();
pullEvent();//開始下拉,移动
return true;
}
break;
} case MotionEvent.ACTION_DOWN: {
if (isReadyForPull()) {//按下 開始下拉
mLastMotionY = mInitialMotionY = event.getY();
mLastMotionX = mInitialMotionX = event.getX();
return true;
}
break;
} case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: { //停止下拉的时候
if (mIsBeingDragged) {
mIsBeingDragged = false;
if (mState == State.RELEASE_TO_REFRESH
&& (null != mOnRefreshListener || null != mOnRefreshListener2)) {
setState(State.REFRESHING, true);//放下手指開始回调,运行我们的回调任务
return true;
} // If we're already refreshing, just scroll back to the top
if (isRefreshing()) {
smoothScrollTo(0);
return true;
} // If we haven't returned by here, then we're not in a state
// to pull, so just reset
setState(State.RESET); //恢复到原来的UI状态 return true;
}
break;
}
} return false;
}

看看pullEvent方法
private void pullEvent() {
final int newScrollValue;
final int itemDimension;
final float initialMotionValue, lastMotionValue; switch (getPullToRefreshScrollDirection()) {
case HORIZONTAL:
initialMotionValue = mInitialMotionX;
lastMotionValue = mLastMotionX;
break;
case VERTICAL:
default:
initialMotionValue = mInitialMotionY;
lastMotionValue = mLastMotionY;
break;
}
//计算下拉移动了多少
switch (mCurrentMode) {
case PULL_FROM_END://上拉
newScrollValue = Math.round(Math.max(initialMotionValue - lastMotionValue, 0) / FRICTION);
itemDimension = getFooterSize();
break;
case PULL_FROM_START://下拉
default:
newScrollValue = Math.round(Math.min(initialMotionValue - lastMotionValue, 0) / FRICTION);
itemDimension = getHeaderSize();
break;
} //显示HeaderView 得到移动的值。能够让LoadingView显示出来
setHeaderScroll(newScrollValue); if (newScrollValue != 0 && !isRefreshing()) {
float scale = Math.abs(newScrollValue) / (float) itemDimension;
switch (mCurrentMode) {
case PULL_FROM_END:
mFooterLayout.onPull(scale);
break;
case PULL_FROM_START:
default:
mHeaderLayout.onPull(scale);//旋转左边的载入图片,显示文字和图片 这个地方终于会运行LoadingLayout中的 onPullImpl方法
break;
}
//更新状态 包含2中 释放按下触摸,还有就是 没释放手的触摸
if (mState != State.PULL_TO_REFRESH && itemDimension >= Math.abs(newScrollValue)) {
setState(State.PULL_TO_REFRESH);
} else if (mState == State.PULL_TO_REFRESH && itemDimension < Math.abs(newScrollValue)) {
setState(State.RELEASE_TO_REFRESH);//下拉松手 能够松手了
}
}
}

再看看setHeaderScroll方法代码
protected final void setHeaderScroll(int value) {
if (DEBUG) {
Log.d(LOG_TAG, "setHeaderScroll: " + value);
} if (DEBUG) {
Log.d(LOG_TAG, "setHeaderScroll:" + value );
} // Clamp value to with pull scroll range
final int maximumPullScroll = getMaximumPullScroll();
value = Math.min(maximumPullScroll, Math.max(-maximumPullScroll, value)); if (mLayoutVisibilityChangesEnabled) {
if (value < 0) { //有位移才显示
mHeaderLayout.setVisibility(View.VISIBLE);
} else if (value > 0) { <span style="font-family: Arial, Helvetica, sans-serif;">//有位移才显示</span>
mFooterLayout.setVisibility(View.VISIBLE);
} else {
mHeaderLayout.setVisibility(View.INVISIBLE);
mFooterLayout.setVisibility(View.INVISIBLE);
}
} if (USE_HW_LAYERS) {
/**
* Use a Hardware Layer on the Refreshable View if we've scrolled at
* all. We don't use them on the Header/Footer Views as they change
* often, which would negate any HW layer performance boost.
*/
ViewCompat.setLayerType(mRefreshableViewWrapper, value != 0 ? View.LAYER_TYPE_HARDWARE
: View.LAYER_TYPE_NONE);
} //回到最原始的scrollTo 最经常使用的 移动布局
switch (getPullToRefreshScrollDirection()) {
case VERTICAL:
scrollTo(0, value);
break;
case HORIZONTAL:
scrollTo(value, 0);
break;
}
}

setState(State.REFRESHING, true);//拉倒最顶部 松手,会运行onRefreshing方法。回调我们实现的任务接口 也就是OnRefreshListener

protected void onRefreshing(final boolean doScroll) {
if (mMode.showHeaderLoadingLayout()) {
mHeaderLayout.refreshing();
}
if (mMode.showFooterLoadingLayout()) {
mFooterLayout.refreshing();
} if (doScroll) {
if (mShowViewWhileRefreshing) { // Call Refresh Listener when the Scroll has finished
OnSmoothScrollFinishedListener listener = new OnSmoothScrollFinishedListener() {
@Override
public void onSmoothScrollFinished() {
callRefreshListener();//回调接口运行
}
}; switch (mCurrentMode) {
case MANUAL_REFRESH_ONLY:
case PULL_FROM_END:
smoothScrollTo(getFooterSize(), listener);
break;
default:
case PULL_FROM_START:
smoothScrollTo(-getHeaderSize(), listener);
break;
}
} else {
smoothScrollTo(0);//回到原来的位置
}
} else {
// We're not scrolling, so just call Refresh Listener now
callRefreshListener();//回调接口运行
}
}

	private void callRefreshListener() {
if (null != mOnRefreshListener) {
mOnRefreshListener.onRefresh(this);//回调
} else if (null != mOnRefreshListener2) { //这个是上拉,下拉都能够的情况,使用 onRefreshListener2
if (mCurrentMode == Mode.PULL_FROM_START) {
mOnRefreshListener2.onPullDownToRefresh(this);
} else if (mCurrentMode == Mode.PULL_FROM_END) {
mOnRefreshListener2.onPullUpToRefresh(this);
}
}
}

总结:状态包含下拉刷新。松手刷新,正在刷新,Loading隐藏。移动UI还是用的scrollTo最主要的代码. 动画部分能够看LoadingLayout的2个子类

基本的就这些。还有非常多细节没有分析。若有问题请指出谢谢。

开源Android-PullToRefresh下拉刷新源代码分析的更多相关文章

  1. Android PullToRefresh 下拉刷新,上拉很多其它,支持ScrollView,ListView,可方便拓展GridView,WebView等

    在写着东西之前.从网上找到非常多这方面的源代码,可是基本没有找到惬意的.包含在GitHub上的比較有名的Android-PullToRefresh-master.思来想去还是自己写吧.当然当中借鉴了一 ...

  2. Android PullToRefresh下拉刷新控件的简单使用

    PullToRefresh这个开源库早就听说了,不过一直没用过.作为一个经典的的开源库,我觉得还是有必要认识一下. 打开github上的网址:https://github.com/chrisbanes ...

  3. Xamarin. Android实现下拉刷新功能

    PS:发现文章被其他网站或者博客抓取后发表为原创了,给图片加了个水印 下拉刷新功能在安卓和iOS中非常常见,一般实现这样的功能都是直接使用第三方的库,网上能找到很多这样的开源库.然而在Xamarin. ...

  4. Android智能下拉刷新加载框架—看这些就够了

    一些值得学习的几个下拉刷新上拉加载开源库 Android智能下拉刷新框架-SmartRefreshLayout 支持所有的 View(AbsListView.RecyclerView.WebView. ...

  5. .Net 转战 Android 4.4 日常笔记(10)--PullToRefresh下拉刷新使用

    下拉刷新很多地方都用到了,新浪微博,微信,百度新闻 这里我们使用一个开源的库叫:PullToRefresh 开源地址:https://github.com/chenyoca/pull-to-refre ...

  6. android——官方下拉刷新组件SwipeRefreshLayout(转)

    一.问题描述 在android开发中,使用最多的数据刷新方式就是下拉刷新了,而完成此功能我们使用最多的就是第三方的开源库PullToRefresh.现如今,google也忍不住推出了自己的下拉组件Sw ...

  7. android官方下拉刷新控件SwipeRefreshLayout的使用

    可能开发安卓的人大多数都用过很多下拉刷新的开源组件,但是今天用了官方v4支持包的SwipeRefreshLayout觉得效果也蛮不错的,特拿出来分享. 简介:SwipeRefreshLayout组件只 ...

  8. pullToRefresh下拉刷新上拉加载

    PullToRefresh 是一个第三方的工程. 之前的自定义下拉刷新控件貌似不太好用,于是网上找了这个. 参考:http://www.cnblogs.com/summers/p/4343964.ht ...

  9. 第三方开源框架的下拉刷新列表(QQ比较常用的)。

    PullToRefreshListView是第三方开源框架下拉刷新列表,比较流行的QQ 微信等上面都在用. 下载地址(此开源框架于2013年后不再更新) 点此下载 package com.lixu.k ...

随机推荐

  1. gulp一般使用

    gulp的基本使用总结了一下几点: 1.gulp-ejs的使用 [ file include,html文件合并 ]: let ejs = require("gulp-ejs"); ...

  2. 基于python3.x,使用Tornado中的torndb模块操作数据库

    目前Tornado中的torndb模块是不支持python3.x,所以需要修改部分torndb源码即可正常使用 1.开发环境介绍 操作系统:win8(64位),python版本:python3.6(3 ...

  3. .Net Core2.0秒杀CMS部署到Centos7.3遇到的坑,酸爽呀

    一.Centos7.3的安装 打开VirtualBox,点击新建,如下图所示: 点击“下一步”,弹出下面的对话框,调整内存大小,建议设置为2G,这样操作更流畅点 设置好,点击“OK”,再点击“启动”, ...

  4. Velocity(7)——velocity进阶用法

    1.截取部分字段substring 1 原始字符串:$!ag.tagValue,也许很长,前端页面展示时需要截取字符串. 2 #set($str=$!ag.tagValue) 3 4 #if($str ...

  5. C# 4.0 新特性dynamic、可选参数、命名参数等

    1.dynamic ExpandoObject熟悉js的朋友都知道js可以这么写 :   1 var t = new Object(); 2 t.Abc = ‘something’; 3 t.Valu ...

  6. ZZCMS8.1|代码审计

    这周的审计任务 ZZCMS8.1是站长招商网内容管理系统.审计这个CMS的原因很多,这里就不详说了(其实是漏洞类型多点)                                         ...

  7. web离线应用--dom storage

    web离线应用--dom storage dom storage是html5添加的新功能,其实也不是什么新的应用,只不过是cookie的放大版本,由于cookie的大小只有4kb,而且在每次请求一个新 ...

  8. 深入了解Android蓝牙Bluetooth——《进阶篇》

    在 [深入了解Android蓝牙Bluetooth--<基础篇>](http://blog.csdn.net/androidstarjack/article/details/6046846 ...

  9. ABAP中的AMDP(ABAP-Managed Database Procedures )

    ABAP管理下的数据库存储过程(ABAP-Managed Database Procedure,以下简称AMDP)是在APAP on SAP HANA开发中的一种优化模式.AMDP使用数据库语言书写, ...

  10. ios多线程开发总结

    1>无论使用哪种方法进行多线程开发,每个线程启动后并不一定立即执行相应的操作,具体什么时候由系统调度(CPU空闲时就会执行). 2>更新UI应该在主线程(UI线程)中进行,并且推荐使用同步 ...