How:(使用)

转自:http://blog.csdn.net/hantangsongming/article/details/42490277

PullToRefresh是一套实现非常好的下拉刷新库,它支持:
ListView
ExpandableListView
GridView
WebView
ScrollView
HorizontalScrollView
ViewPager
等多种常用的需要刷新的View类型,而且使用起来也十分方便。
(下载地址:https://github.com/chrisbanes/Android-PullToRefresh

PullToRefresh基本用法(步骤):

一、继承OnClickListener实现onRefresh()方法

1、在布局文件中添加PullToRefresh控件,比如PullToRefreshListView;

2、用PullToRefresh控件的setMode()方法设置PullToRefresh控件的Mode(包括 Mode.BOTH、Mode.PULL_FROM_START、Mode.PULL_FROM_END等);

3、在Activity中,设置监听器OnRefreshListener以响应用户下拉操作;
4、在监听器的onRefresh()方法中通过PullToRefresh控件的getCurrentMode()方法来获取当前“拉”操作的Mode,区分是“上拉”还是“下拉”,并各自执行自己的数据刷新操作,可以通过AsyncTask来实现;

  注:getMode()方法返回的是setMode()时的字符串值,getCurrentMode()返回的是实际操作时的Mode类型。各种Mode的值是字符串而不是数值,进行比较时必须注意。

5、在AsyncTask中获取到数据后,记得调用onRefreshComplete()方法通知PullToRefresh控件数据已获取完毕,可以结束刷新操作。

二、继承OnClickListener2实现onPullUpToRefresh()和onPullDownToRefresh()方法

1、同上;

2、用setMode(Mode.BOTH)方法设置PullToRefresh控件的Mode为“上拉”和“下拉”均可;

3、在Activity中,设置监听器OnRefreshListener以响应用户下拉操作;

4、在监听器的onPullUpToRefresh()和onPullDownToRefresh()方法中分别执行“上拉”和“下拉”各自的操作,可通过AsyncTask来实现。

5、同上。

附加:

ListView显示哪个Item通过如下方法

  1. chatRoomAdapter.notifyDataSetChanged();
  1. listView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL); //数据变动则自动回到底部
  1. listView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_DISABLED); //数据变动不回到底部
  1. listView.setSelection(group.size()); //显示第几个Item。注:数据变动必须不会自动回到底部才有效。
  2. 注意:setSelection()方法必须写在ListView设置适配器并提示数据改变后
  1. 注:ListView是从上到下从1开始排序的。而数据项插入ListView是从下往上顶的。

改变ListView中数据的整体位置是在顶部还是底部setStackFromBottom(Boolean)

改变ListView中的数据项的顺序只能改变适配器中添加数据的顺序或集合中数据的顺序。如:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
 
public class T {
  public static void main(String[] args) {
    ArrayList list = new ArrayList();
    list.add("92.8");
    list.add("68.9");
    list.add("168.61");
    list.add("242");
    list.add("317");
    list.add("105");
    // 字符串排序
    Collections.sort(list);
    System.out.println(list.toString()); // [105, 168.61, 242, 317, 68.9, 92.8]
    Collections.sort(list, new Comparator() {
      @Override
      public int compare(Object o1, Object o2) {
        return new Double((String) o1).compareTo(new Double((String) o2));
      }
    });
    System.out.println(list.toString()); // [68.9, 92.8, 105, 168.61, 242, 317]
  }
} 
  1. Why:(源码、类图)

http://www.2cto.com/kf/201504/387623.html

PullToRefresh 这个库用的是非常至多,github 今天主要分析一下源码实现.

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

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

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

初始化代码在PullToRefreshBase init方法中

重点代码:

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

//这里有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显示松手后的文字:正在载入...

结构是这样    (注:下图似乎有点问题

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

  1. public final boolean onTouchEvent(MotionEvent event) {
  2. if (!isPullToRefreshEnabled()) {
  3. return false;
  4. }
  5. // If we're refreshing, and the flag is set. Eat the event
  6. if (!mScrollingWhileRefreshingEnabled && isRefreshing()) {
  7. return true;
  8. }
  9. if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0) {
  10. return false;
  11. }
  12. switch (event.getAction()) {
  13. case MotionEvent.ACTION_MOVE: {
  14. if (mIsBeingDragged) {
  15. mLastMotionY = event.getY();
  16. mLastMotionX = event.getX();
  17. pullEvent();//开始下拉,移动
  18. return true;
  19. }
  20. break;
  21. }
  22.  
  23. case MotionEvent.ACTION_DOWN: {
  24. if (isReadyForPull()) {//按下 开始下拉
  25. mLastMotionY = mInitialMotionY = event.getY();
  26. mLastMotionX = mInitialMotionX = event.getX();
  27. return true;
  28. }
  29. break;
  30. }
  31.  
  32. case MotionEvent.ACTION_CANCEL:
  33. case MotionEvent.ACTION_UP: { //停止下拉的时候
  34. if (mIsBeingDragged) {
  35. mIsBeingDragged = false;
  36. if (mState == State.RELEASE_TO_REFRESH
  37. && (null != mOnRefreshListener || null != mOnRefreshListener2)) {
  38. setState(State.REFRESHING, true);//放下手指开始回调,执行我们的回调任务
  39. return true;
  40. }
  41.  
  42. // If we're already refreshing, just scroll back to the top
  43. if (isRefreshing()) {
  44. smoothScrollTo(0);
  45. return true;
  46. }
  47.  
  48. // If we haven't returned by here, then we're not in a state
  49. // to pull, so just reset
  50. setState(State.RESET); //恢复到原来的UI状态
  51.  
  52. return true;
  53. }
  54. break;
  55. }
  56. }
  57.  
  58. return false;
  59. }
  1. 看看pullEvent方法
  2. private void pullEvent() {
  3. final int newScrollValue;
  4. final int itemDimension;
  5. final float initialMotionValue, lastMotionValue;
  6.  
  7. switch (getPullToRefreshScrollDirection()) {
  8. case HORIZONTAL:
  9. initialMotionValue = mInitialMotionX;
  10. lastMotionValue = mLastMotionX;
  11. break;
  12. case VERTICAL:
  13. default:
  14. initialMotionValue = mInitialMotionY;
  15. lastMotionValue = mLastMotionY;
  16. break;
  17. }
  18. //计算下拉移动了多少
  19. switch (mCurrentMode) {
  20. case PULL_FROM_END://上拉
  21. newScrollValue = Math.round(Math.max(initialMotionValue - lastMotionValue, 0) / FRICTION);
  22. itemDimension = getFooterSize();
  23. break;
  24. case PULL_FROM_START://下拉
  25. default:
  26. newScrollValue = Math.round(Math.min(initialMotionValue - lastMotionValue, 0) / FRICTION);
  27. itemDimension = getHeaderSize();
  28. break;
  29. }
  30.  
  31. //显示HeaderView 得到移动的值,可以让LoadingView显示出来
  32. setHeaderScroll(newScrollValue);
  33.  
  34. if (newScrollValue != 0 && !isRefreshing()) {
  35. float scale = Math.abs(newScrollValue) / (float) itemDimension;
  36. switch (mCurrentMode) {
  37. case PULL_FROM_END:
  38. mFooterLayout.onPull(scale);
  39. break;
  40. case PULL_FROM_START:
  41. default:
  42. mHeaderLayout.onPull(scale);//旋转左边的加载图片,显示文字和图片 这个地方最终会执行LoadingLayout中的 onPullImpl方法
  43. break;
  44. }
  45. //更新状态 包括2种 下拉刷新:拉伸距离不足需要继续拉伸的状态;松手刷新:拉伸距离已经足够可以释放的状态。
  46. if (mState != State.PULL_TO_REFRESH && itemDimension >= Math.abs(newScrollValue)) {
  47. setState(State.PULL_TO_REFRESH);
  48. } else if (mState == State.PULL_TO_REFRESH && itemDimension < Math.abs(newScrollValue)) {
  49. setState(State.RELEASE_TO_REFRESH);//下拉松手 可以松手了
  50. }
  51. }
  52. }
  1. 再看看setHeaderScroll方法代码
  2. protected final void setHeaderScroll(int value) {
  3. if (DEBUG) {
  4. Log.d(LOG_TAG, "setHeaderScroll: " + value);
  5. }
  6.  
  7. if (DEBUG) {
  8. Log.d(LOG_TAG, "setHeaderScroll:" + value );
  9. }
  10.  
  11. // Clamp value to with pull scroll range
  12. final int maximumPullScroll = getMaximumPullScroll();
  13. value = Math.min(maximumPullScroll, Math.max(-maximumPullScroll, value));
  14.  
  15. if (mLayoutVisibilityChangesEnabled) {
  16. if (value < 0) { //有位移才显示
  17. mHeaderLayout.setVisibility(View.VISIBLE);
  18. } else if (value > 0) { //有位移才显示
  19. mFooterLayout.setVisibility(View.VISIBLE);
  20. } else {
  21. mHeaderLayout.setVisibility(View.INVISIBLE);
  22. mFooterLayout.setVisibility(View.INVISIBLE);
  23. }
  24. }
  25.  
  26. if (USE_HW_LAYERS) {
  27. /**
  28. * Use a Hardware Layer on the Refreshable View if we've scrolled at
  29. * all. We don't use them on the Header/Footer Views as they change
  30. * often, which would negate any HW layer performance boost.
  31. */
  32. ViewCompat.setLayerType(mRefreshableViewWrapper, value != 0 ? View.LAYER_TYPE_HARDWARE
  33. : View.LAYER_TYPE_NONE);
  34. }
  35.  
  36. //回到最原始的scrollTo 最常用的 移动布局
  37. switch (getPullToRefreshScrollDirection()) {
  38. case VERTICAL:
  39. scrollTo(0, value);
  40. break;
  41. case HORIZONTAL:
  42. scrollTo(value, 0);
  43. break;
  44. }
  45. }

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

  1. protected void onRefreshing(final boolean doScroll) {
  2. if (mMode.showHeaderLoadingLayout()) {
  3. mHeaderLayout.refreshing();
  4. }
  5. if (mMode.showFooterLoadingLayout()) {
  6. mFooterLayout.refreshing();
  7. }
  8.  
  9. if (doScroll) {
  10. if (mShowViewWhileRefreshing) {
  11.  
  12. // Call Refresh Listener when the Scroll has finished
  13. OnSmoothScrollFinishedListener listener = new OnSmoothScrollFinishedListener() {
  14. @Override
  15. public void onSmoothScrollFinished() {
  16. callRefreshListener();//回调接口执行
  17. }
  18. };
  19.  
  20. switch (mCurrentMode) {
  21. case MANUAL_REFRESH_ONLY:
  22. case PULL_FROM_END:
  23. smoothScrollTo(getFooterSize(), listener);
  24. break;
  25. default:
  26. case PULL_FROM_START:
  27. smoothScrollTo(-getHeaderSize(), listener);
  28. break;
  29. }
  30. } else {
  31. smoothScrollTo(0);//回到原来的位置
  32. }
  33. } else {
  34. // We're not scrolling, so just call Refresh Listener now
  35. callRefreshListener();//回调接口执行
  36. }
  37. }
  1. private void callRefreshListener() {
  2. if (null != mOnRefreshListener) {
  3. mOnRefreshListener.onRefresh(this);//回调
  4. } else if (null != mOnRefreshListener2) { //这个是上拉,下拉都可以的情况,使用 onRefreshListener2
  5. if (mCurrentMode == Mode.PULL_FROM_START) {
  6. mOnRefreshListener2.onPullDownToRefresh(this);
  7. } else if (mCurrentMode == Mode.PULL_FROM_END) {
  8. mOnRefreshListener2.onPullUpToRefresh(this);
  9. }
  10. }
  11. }

总结:状态包括下拉刷新松手刷新正在刷新Loading隐藏。移动UI还是用的scrollTo最基本的代码. 动画部分可以看LoadingLayout的2个子类

主要的就这些,还有很多细节没有分析。若有问题请指出谢谢。

Android 下拉刷新框架实现 (自定义的下拉刷新)   !!!未看!!!

转自:http://blog.csdn.net/leehong2005/article/details/12567757

前段时间项目中用到了下拉刷新功能,之前在网上也找到过类似的demo,但这些demo的质量参差不齐,用户体验也不好,接口设计也不行。最张没办法,终于忍不了了,自己就写了一个下拉刷新的框架,这个框架是一个通用的框架,效果和设计感觉都还不错,现在分享给各位看官。

致谢:

1. 感谢lk6233160同学提出的问题,旋转View时调用setRotation方法只能是在API Level11(3.0)以上才能用,这个问题的解决办法是给ImageView设置一个Matrix,把Matrix上面作用一个旋转矩阵,但是如果不是ImageView的话,可能实现起来比较麻烦,再次谢谢lk6233160同学。

2. 谢谢如毛毛风提出的问题,向下滑动后,再向上滑动到头,只能再松手后才能再次下拉。这个问题的回复请参考评论。

技术交流群:

QQ:197990971(人员已满)

1. 关于下拉刷新

下拉刷新这种用户交互最早由twitter创始人洛伦•布里切特(Loren Brichter)发明,有理论认为,下拉刷新是一种适用于按照从新到旧的时间顺序排列feeds的应用,在这种应用场景中看完旧的内容时,用户会很自然地下拉查找更新的内容,因此下拉刷新就显得非常合理。大家可以参考这篇文章:有趣的下拉刷新,下面我贴出一个有趣的下拉刷新的案例。
图一、有趣的下拉刷新案例(一)
 
图一、有趣的下拉刷新案例(二)
 
 

2. 实现原理

上面这些例子,外观做得再好看,他的本质上都一样,那就是一个下拉刷新控件通常由以下几部分组成:
【1】Header
Header通常有下拉箭头,文字,进度条等元素,根据下拉的距离来改变它的状态,从而显示不同的样式
【2】Content
这部分是内容区域,网上有很多例子都是直接在ListView里面添加Header,但这就有局限性,因为好多情况下并不一定是用ListView来显示数据。我们把要显示内容的View放置在我们的一个容器中,如果你想实现一个用ListView显示数据的下拉刷新,你需要创建一个ListView旋转到我的容器中。我们处理这个容器的事件(down, move, up),如果向下拉,则把整个布局向下滑动,从而把header显示出来。
【3】Footer
Footer可以用来显示向上拉的箭头,自动加载更多的进度条等。
 
以上三部分总结的说来,就是如下图所示的这种布局结构:
图三,下拉刷新的布局结构
 
关于上图,需要说明几点:
1、这个布局扩展于LinearLayout,垂直排列
2、从上到下的顺序是:Header, Content, Footer
3、Content填充满父控件,通过设置top, bottom的padding来使Header和Footer不可见,也就是让它超出屏幕外
4、下拉时,调用scrollTo方法来将整个布局向下滑动,从而把Header显示出来,上拉正好与下拉相反。
5、派生类需要实现的是:将Content View填充到父容器中,比如,如果你要使用的话,那么你需要把ListView, ScrollView, WebView等添加到容器中。
6、上图中的红色区域就是屏的大小(严格来说,这里说屏幕大小并不准确,应该说成内容区域更加准确)
 

3. 具体实现

明白了实现原理与过程,我们尝试来具体实现,首先,为了以后更好地扩展,设计更加合理,我们把下拉刷新的功能抽象成一个接口:

1、IPullToRefresh<T extends View>

它具体的定义方法如下:
 
  1. public interface IPullToRefresh<T extends View> {
  2. public void setPullRefreshEnabled(boolean pullRefreshEnabled);
  3. public void setPullLoadEnabled(boolean pullLoadEnabled);
  4. public void setScrollLoadEnabled(boolean scrollLoadEnabled);
  5. public boolean isPullRefreshEnabled();
  6. public boolean isPullLoadEnabled();
  7. public boolean isScrollLoadEnabled();
  8. public void setOnRefreshListener(OnRefreshListener<T> refreshListener);
  9. public void onPullDownRefreshComplete();
  10. public void onPullUpRefreshComplete();
  11. public T getRefreshableView();
  12. public LoadingLayout getHeaderLoadingLayout();
  13. public LoadingLayout getFooterLoadingLayout();
  14. public void setLastUpdatedLabel(CharSequence label);
  15. }
这个接口是一个泛型的,它接受View的派生类,因为要放到我们的容器中的不就是一个View吗?

2、PullToRefreshBase<T extends View>
这个类实现了IPullToRefresh接口,它是从LinearLayout继承过来,作为下拉刷新的一个抽象基类,如果你想实现ListView的下拉刷新,只需要扩展这个类,实现一些必要的方法就可以了。这个类的职责主要有以下几点:
  • 处理onInterceptTouchEvent()和onTouchEvent()中的事件:当内容的View(比如ListView)正如处于最顶部,此时再向下拉,我们必须截断事件,然后move事件就会把后续的事件传递到onTouchEvent()方法中,然后再在这个方法中,我们根据move的距离再进行scroll整个View。
  • 负责创建Header、Footer和Content View:在构造方法中调用方法去创建这三个部分的View,派生类可以重写这些方法,以提供不同式样的Header和Footer,它会调用createHeaderLoadingLayout和createFooterLoadingLayout方法来创建Header和Footer创建Content View的方法是一个抽象方法,必须让派生类来实现,返回一个非null的View,然后容器再把这个View添加到自己里面。
  • 设置各种状态:这里面有很多状态,如下拉、上拉、刷新、加载中、释放等,它会根据用户拉动的距离来更改状态,状态的改变,它也会把Header和Footer的状态改变,然后Header和Footer会根据状态去显示相应的界面式样。
3、PullToRefreshBase<T extends View>继承关系
这里我实现了三个下拉刷新的派生类,分别是ListView、ScrollView、WebView三个,它们的继承关系如下:
图四、PullToRefreshBase类的继承关系
 
关于PullToRefreshBase类及其派和类,有几点需要说明:
  • 对于ListView,ScrollView,WebView这三种情况,他们是否滑动到最顶部或是最底部的实现是不一样的,所以,在PullToRefreshBase类中需要调用两个抽象方法来判断当前的位置是否在顶部或底部,而其派生类必须要实现这两个方法。比如对于ListView,它滑动到最顶部的条件就是第一个child完全可见并且first postion是0。这两个抽象方法是:

  1. /**
  2. * 判断刷新的View是否滑动到顶部
  3. *
  4. * @return true表示已经滑动到顶部,否则false
  5. */
  6. protected abstract boolean isReadyForPullDown();
  7. /**
  8. * 判断刷新的View是否滑动到底
  9. *
  10. * @return true表示已经滑动到底部,否则false
  11. */
  12. protected abstract boolean isReadyForPullUp();
  • 创建可下拉刷新的View(也就是content view)的抽象方法是
  1. /**
  2. * 创建可以刷新的View
  3. *
  4. * @param context context
  5. * @param attrs 属性
  6. * @return View
  7. */
  8. protected abstract T createRefreshableView(Context context, AttributeSet attrs);
4、LoadingLayout
LoadingLayout是刷新Layout的一个抽象,它是一个抽象基类。Header和Footer都扩展于这个类。这类抽象类,提供了两个抽象方法:
  • getContentSize

这个方法返回当前这个刷新Layout的大小,通常返回的是布局的高度,为了以后可以扩展为水平拉动,所以方法名字没有取成getLayoutHeight()之类的,这个返回值,将会作为松手后是否可以刷新的临界值,如果下拉的偏移值大于这个值,就认为可以刷新,否则不刷新,这个方法必须由派生类来实现。

  • setState
这个方法用来设置当前刷新Layout的状态,PullToRefreshBase类会调用这个方法,当进入下拉,松手等动作时,都会调用这个方法,派生类里面只需要根据这些状态实现不同的界面显示,如下拉状态时,就显示出箭头,刷新状态时,就显示loading的图标。
可能的状态值有:RESET, PULL_TO_REFRESH, RELEASE_TO_REFRESH, REFRESHING, NO_MORE_DATA
LoadingLayout及其派生类的继承关系如下图所示:
图五、LoadingLayout及其派生类的类图
 
我们可以随意地制定自己的Header和Footer,我们也可以实现如图一和图二中显示的各种下拉刷新案例中的Header和Footer,只要重写上述两个方法getContentSize()和setState()就行了。HeaderLoadingLayout,它默认是显示箭头式样的布局,而RotateLoadingLayout则是显示一个旋转图标的式样。
 
5、事件处理
我们必须重写PullToRefreshBase类的两个事件相关的方法onInterceptTouchEvent()和onTouchEvent()方法。由于ListView,ScrollView,WebView它们是放到PullToRefreshBase内部的,所在事件先是传递到PullToRefreshBase#onInterceptTouchEvent()方法中,所以我们应该在这个方法中去处理ACTION_MOVE事件,判断如果当前ListView,ScrollView,WebView是否在最顶部或最底部,如果是,则开始截断事件,一旦事件被截断,后续的事件就会传递到PullToRefreshBase#onInterceptTouchEvent()方法中,我们再在ACTION_MOVE事件中去移动整个布局,从而实现下拉或上拉动作。
 
6、滚动布局(scrollTo)
如图三的布局结构可知,默认情况下Header和Footer是放置在Content View的最上面和最下面,通过设置padding来让他跑到屏幕外面去了,如果我们将整个布局向下滚动(scrollTo)一定距离,那么Header就会被显示出来,基于这种情况,所以在我的实现中,最终我是调用scrollTo来实现下拉动作的。
 
总的说来,实现的重要的点就这些,具体的一些细节在实现在会碰到很多,可以参考代码。
 

4. 如何使用

使用下拉刷新的代码如下

  1. @Override
  2. public void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. mPullListView = new PullToRefreshListView(this);
  5. setContentView(mPullListView);
  6. // 上拉加载不可用
  7. mPullListView.setPullLoadEnabled(false);
  8. // 滚动到底自动加载可用
  9. mPullListView.setScrollLoadEnabled(true);
  10. mCurIndex = mLoadDataCount;
  11. mListItems = new LinkedList<String>();
  12. mListItems.addAll(Arrays.asList(mStrings).subList(0, mCurIndex));
  13. mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mListItems);
  14. // 得到实际的ListView
  15. mListView = mPullListView.getRefreshableView();
  16. // 绑定数据
  17. mListView.setAdapter(mAdapter);
  18. // 设置下拉刷新的listener
  19. mPullListView.setOnRefreshListener(new OnRefreshListener<ListView>() {
  20. @Override
  21. public void onPullDownToRefresh(PullToRefreshBase<ListView> refreshView) {
  22. mIsStart = true;
  23. new GetDataTask().execute();
  24. }
  25. @Override
  26. public void onPullUpToRefresh(PullToRefreshBase<ListView> refreshView) {
  27. mIsStart = false;
  28. new GetDataTask().execute();
  29. }
  30. });
  31. setLastUpdateTime();
  32. // 自动刷新
  33. mPullListView.doPullRefreshing(true, 500);
  34. }
这是初始化一个下拉刷新的布局,并且调用setContentView来设置到Activity中。
在下拉刷新完成后,我们可以调用onPullDownRefreshComplete()和onPullUpRefreshComplete()方法来停止刷新和加载
 

5. 运行效果

这里列出了demo的运行效果图。
图六、ListView下拉刷新,注意Header和Footer的样式
 
图七、WebView和ScrollView的下拉刷新效果图
 
 

6. 源码下载

实现这个下拉刷新的框架,并不是我的原创,我也是参考了很多开源的,把我认为比较好的东西借鉴过来,从而形成我的东西,我主要是参考了下面这个demo:
https://github.com/chrisbanes/Android-PullToRefresh 这个demo写得不错,不过他这个太复杂了,我们都知道,一旦复杂了,万一我们要添加一些需要,自然也要费劲一些,我其实就是把他的简化再简化,以满足我们自己的需要。
 
 
转载请说明出处
谢谢!!!
 
 

7. Bug修复

 
已知bug修复情况如下,发现了代码bug的看官也可以给我反馈,谢谢~~~
 
1,对于ListView的下拉刷新,当启用滚动到底自动加载时,如果footer由隐藏变为显示时,出现显示异常的情况
这个问题已经修复了,修正的代码如下:
  • PullToRefreshListView#setScrollLoadEnabled方法,修正后的代码如下:

  1. @Override
  2. public void setScrollLoadEnabled(boolean scrollLoadEnabled) {
  3. if (isScrollLoadEnabled() == scrollLoadEnabled) {
  4. return;
  5. }
  6. super.setScrollLoadEnabled(scrollLoadEnabled);
  7. if (scrollLoadEnabled) {
  8. // 设置Footer
  9. if (null == mLoadMoreFooterLayout) {
  10. mLoadMoreFooterLayout = new FooterLoadingLayout(getContext());
  11. mListView.addFooterView(mLoadMoreFooterLayout, null, false);
  12. }
  13. mLoadMoreFooterLayout.show(true);
  14. } else {
  15. if (null != mLoadMoreFooterLayout) {
  16. mLoadMoreFooterLayout.show(false);
  17. }
  18. }
  19. }

LoadingLayout#show方法,修正后的代码如下:

  1. /**
  2. * 显示或隐藏这个布局
  3. *
  4. * @param show flag
  5. */
  6. public void show(boolean show) {
  7. // If is showing, do nothing.
  8. if (show == (View.VISIBLE == getVisibility())) {
  9. return;
  10. }
  11. ViewGroup.LayoutParams params = mContainer.getLayoutParams();
  12. if (null != params) {
  13. if (show) {
  14. params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
  15. } else {
  16. params.height = 0;
  17. }
  18. requestLayout();
  19. setVisibility(show ? View.VISIBLE : View.INVISIBLE);
  20. }
  21. }

在更改LayoutParameter后,调用requestLayout()方法。

  • 图片旋转兼容2.x系统
我之前想的是这个只需要兼容3.x以上的系统,但发现有很多网友在使用过程中遇到过兼容性问题,这次抽空将这个兼容性一并实现了。
       onPull的修改如下:

  1. @Override
  2. public void onPull(float scale) {
  3. if (null == mRotationHelper) {
  4. mRotationHelper = new ImageViewRotationHelper(mArrowImageView);
  5. }
  6. float angle = scale * 180f; // SUPPRESS CHECKSTYLE
  7. mRotationHelper.setRotation(angle);
  8. }

ImageViewRotationHelper主要的作用就是实现了ImageView的旋转功能,内部作了版本的区分,实现代码如下:

  1. /**
  2. * The image view rotation helper
  3. *
  4. * @author lihong06
  5. * @since 2014-5-2
  6. */
  7. static class ImageViewRotationHelper {
  8. /** The imageview */
  9. private final ImageView mImageView;
  10. /** The matrix */
  11. private Matrix mMatrix;
  12. /** Pivot X */
  13. private float mRotationPivotX;
  14. /** Pivot Y */
  15. private float mRotationPivotY;
  16. /**
  17. * The constructor method.
  18. *
  19. * @param imageView the image view
  20. */
  21. public ImageViewRotationHelper(ImageView imageView) {
  22. mImageView = imageView;
  23. }
  24. /**
  25. * Sets the degrees that the view is rotated around the pivot point. Increasing values
  26. * result in clockwise rotation.
  27. *
  28. * @param rotation The degrees of rotation.
  29. *
  30. * @see #getRotation()
  31. * @see #getPivotX()
  32. * @see #getPivotY()
  33. * @see #setRotationX(float)
  34. * @see #setRotationY(float)
  35. *
  36. * @attr ref android.R.styleable#View_rotation
  37. */
  38. public void setRotation(float rotation) {
  39. if (APIUtils.hasHoneycomb()) {
  40. mImageView.setRotation(rotation);
  41. } else {
  42. if (null == mMatrix) {
  43. mMatrix = new Matrix();
  44. // 计算旋转的中心点
  45. Drawable imageDrawable = mImageView.getDrawable();
  46. if (null != imageDrawable) {
  47. mRotationPivotX = Math.round(imageDrawable.getIntrinsicWidth() / 2f);
  48. mRotationPivotY = Math.round(imageDrawable.getIntrinsicHeight() / 2f);
  49. }
  50. }
  51. mMatrix.setRotate(rotation, mRotationPivotX, mRotationPivotY);
  52. mImageView.setImageMatrix(mMatrix);
  53. }
  54. }
  55. }
最核心的就是,如果在2.x的版本上,旋转ImageView使用Matrix。
 
  • PullToRefreshBase构造方法兼容2.x
在三个参数的构造方法声明如下标注:
    @SuppressLint("NewApi")
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)

Android-PullToRefresh下拉刷新库基本用法的更多相关文章

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

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

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

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

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

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

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

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

  5. android一个下拉放大库bug的解决过程及思考

    android一个下拉放大库bug的解决过程及思考 起因 项目中要做一个下拉缩放图片的效果,搜索了下github上面,找到了两个方案. https://github.com/Frank-Zhu/Pul ...

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

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

  7. Android几种强大的下拉刷新库

    BeautifulRefreshLayout 众多优秀的下拉刷新(除了我写的之外T_T) 说起下拉刷新,好像经历一段历史的洗礼... (1)在我刚学android的时候,用的是XListView,在g ...

  8. Android学习之两款下拉刷新库分享

    昨天没有写博客.心里非常罪过呀,今天给大家写两种比較常见的下拉刷新的用法.一款是SwipeRefreshLayout,一款是CircleRefreshLayout. SwipeRefreshLayou ...

  9. Android下拉刷新SwipeRefreshLayout简单用法

    之前一直都想用下拉刷新,感觉上是庞大的工程,所以搁置了.现在学习了一下其实真的超级简单. 看了<第一行代码>以及 https://www.jianshu.com/p/3c402a9e4b7 ...

随机推荐

  1. QT---线程间通信(要先编译libqt-mt.so?)

    在 Qt 系统中,运行着一个GUI 主事件线程,这个主线程从窗口系统中获取事件,并将它们分发到各个组件去处理.在 QThread 类中有一种从非主事件线程中将事件提交给一个对象的方法,也就是 QThr ...

  2. MVC之ActionResult

    一.所有的Controller都继承自System.Web.Mvc.Controller 目前ASP.NET MVC3默认提供了多种ActionResult的实现,在System.Web.Mvc命名空 ...

  3. vmware 网络连接

    解决VMware nat service等服务不能启动 虚拟机如何设置网络连接来上网?

  4. C语言随笔_printf输出多行

    想在printf中,输出多行数据,如果写成下面这样: printf("line 1\n line 2\n line 3\n");编译器会报错“error C2001: newlin ...

  5. location.href IE6 下不起作用的罪魁祸首

    解决问题 在btn_publish函数内逻辑最后面加:return false;

  6. TRIZ系列-创新原理-32-改变颜色原理

    改变颜色原理的详细描写叙述例如以下:1)改变物体或其环境的颜色:2)改变物体或其环境的透明度:3)对于难以看到的物体或过程.使用颜色加入剂来观測.4)假设已经使用了这样的加入剂,那么使用发光跟踪或原子 ...

  7. 【Samza系列】实时计算Samza中文教程(二)——概念

    希望上一篇背景篇让大家对流式计算有了宏观的认识,本篇依据官网是介绍概念,先让我们看看有哪些东西呢?     概念一:Streams     Samza是处理流的.流则是由一系列不可变的一种相似类型的消 ...

  8. 安装sql server 2008 management studio时,提示升级VS2008 到 SP1

    安装sql server 2008 management studio时,提示错误:此计算机上安装了 Microsoft Visual Studio 2008 的早期版本.请在安装 SQL Serve ...

  9. log4.net 日志工具使用

    1. 在应用程序的相同目录下建立: winform :    程序名.exe.config          .(log4net程序,就log4net.exe.config) web:         ...

  10. UMl概述(转)

    1. UML的组成 UML由视图(View).图(Diagram).模型元素(Model Element)和通用机制(General Mechanism)等几个部分组成. a) 视图(View): 是 ...