Android下拉刷新ListView实现

自定义下拉刷新ListView

PullToRefreshListView

原理:
拉刷新ListView无非就是对普通的List View添加一个HeaderView,然后通过对ListView onTouchEvent来获取当前下拉刷新的状态。然后去改变HeaderView的状态。

  1. 自定义ListView,在构造方法中去添加HeaderView 通过ListView.addHeaderView()去添加HeaderView的时候,HeaderView会显示在屏幕的最初位置,我们需要它默认的时候是在屏幕的上方,这样默认时是不可见的,但是我们下拉ListView的时候,它就能够显示出来。这就要通过设置HeaderViewpadding来实现它的隐藏。注意:View的显示最初要经过Measure测量宽高,我们在构造方法去添加的时候,该View可能并没有被测量,所以在获取HeaderView高度的时候会为0,这时候我们要手动的去测量一下HeaderView

       
       
    1.  
    2. /** 
    3.   * 当前状态 
    4.   */ 
    5.  private State mState = State.ORIGNAL; 
    6.  
    7.  public PullToRefreshListView(Context context, AttributeSet attrs, 
    8.          int defStyle) { 
    9.      super(context, attrs, defStyle); 
    10.      initView(context); 
    11.  } 
    12.  
    13.  public PullToRefreshListView(Context context, AttributeSet attrs) { 
    14.      super(context, attrs); 
    15.      initView(context); 
    16.  } 
    17.  
    18.  public PullToRefreshListView(Context context) { 
    19.      super(context); 
    20.      initView(context); 
    21.  } 
    22.  
    23.  private void initView(Context context) { 
    24.      LayoutInflater inflater = (LayoutInflater) context 
    25.              .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    26.  
    27.      mHeader = inflater.inflate(R.layout.pull_to_refresh_header, null); 
    28.      iv_arrow = (ImageView) mHeader.findViewById(R.id.iv_arrow); 
    29.      pb_refresh = (ProgressBar) mHeader.findViewById(R.id.pb_refresh); 
    30.      tv_title = (TextView) mHeader.findViewById(R.id.tv_title); 
    31.      tv_time = (TextView) mHeader.findViewById(R.id.tv_time); 
    32.  
    33.      measureHeaderView(mHeader); 
    34.  
    35.      mHeaderHeight = mHeader.getMeasuredHeight(); 
    36.      // To make header view above the window, so use -mHeaderHeight. 
    37.      mHeader.setPadding(0, -mHeaderHeight, 00); 
    38.  
    39.      mHeader.invalidate(); 
    40.  
    41.      addHeaderView(mHeader); 
    42.  } 
    43.  
    44.  /** 
    45.   * 下拉刷新所有的状态 
    46.   */ 
    47.  public enum State { 
    48.      ORIGNAL, PULL_TO_REFRESH, REFRESHING, RELEASE_TO_REFRESH; 
    49.  }
  2. 重写onTouchEvent,来监听手指的下拉

       
       
    1.  public boolean onTouchEvent(MotionEvent ev) { 
    2.      int y = (int) ev.getRawY(); 
    3.      int action = ev.getAction(); 
    4.      switch (action) { 
    5.      case MotionEvent.ACTION_DOWN: 
    6.          //记录手指点下的位置 
    7.          downPositionY = y; 
    8.          break
    9.      case MotionEvent.ACTION_MOVE: 
    10.          //手指当前滑动的位置 
    11.          currentPositionY = y; 
    12.          //手指移动的距离,由于HeaderView高度固定,但是手指下拉的高度可以最大为屏幕的高度,如手指下拉屏幕高度时,HeaderView会很难看,所以我们让下拉的距离进行一个缩放。 
    13.          pullDistance = (currentPositionY - downPositionY) / RATIO; 
    14.  
    15.          if (mState == State.REFRESHING) { 
    16.              break
    17.          } else if (mState == State.ORIGNAL && pullDistance > 0) { 
    18.              //如果现在处理起始的状态,并且距离大于0,就说明是下拉了,这时候状态需要变为下拉刷新的状态 
    19.              mState = State.PULL_TO_REFRESH; 
    20.              changeState(); 
    21.          } else if (mState == State.PULL_TO_REFRESH 
    22.                  && pullDistance > mHeaderHeight) { 
    23.              //当时为下拉刷新的状态,但是下拉的距离大于HeaderView的高度。这时状态要变为松手即可刷新 
    24.              mState = State.RELEASE_TO_REFRESH; 
    25.              changeState(); 
    26.          } else if (mState == State.RELEASE_TO_REFRESH) { 
    27.          //释放刷新时有三种情况,一是我继续下啦,这时候不用管,因为继续下拉还是释放刷新。二是我手指往上移动,此时HeaderView不完全可见,这时候状态要改变为下拉刷新了。三是我手指上移的很厉害,导致HeaderView完全不可见了,这是状态要改变为起始状态。 
    28.              if (pullDistance < 0) { 
    29.                  // 如果当时状态为松手刷新,但是这时候我并没有松手,而是直接将手指往上移动,移动回手指最先的位置,这时候状态要变为起始状态。 
    30.                  mState = State.ORIGNAL; 
    31.                  changeState(); 
    32.              } else if (pullDistance < mHeaderHeight) { 
    33.                  //手指上移,但是并没有移动到HeaderView完全不可见,这时候要将状态改变为下拉刷新 
    34.                  mState = State.PULL_TO_REFRESH; 
    35.                  isBack = true
    36.                  changeState(); 
    37.              } 
    38.  
    39.          } 
    40.  
    41.          //在移动的过程中不断的去改版Padding的值,控制其显示的大小 
    42.          if (mState != State.REFRESHING) { 
    43.              mHeader.setPadding(0, (int) (pullDistance - mHeaderHeight), 0
    44.                      0); 
    45.          } 
    46.  
    47.          break
    48.      case MotionEvent.ACTION_UP: 
    49.          if (mState == State.REFRESHING) { 
    50.              //如果当前已经是正在刷新中了,再去下拉就不要处理了 
    51.              break
    52.          } else if (mState == State.PULL_TO_REFRESH) { 
    53.              //显现下拉刷新时,松手了,这时候要将其改为起始状态 
    54.              mState = State.ORIGNAL; 
    55.          } else if (mState == State.RELEASE_TO_REFRESH) { 
    56.              //松手刷新时松手了,这时候状态要变为正在刷新中。 
    57.              mState = State.REFRESHING; 
    58.          } else { 
    59.              break
    60.          } 
    61.          changeState(); 
    62.          break
    63.  
    64.      default
    65.          break
    66.      } 
    67.  
    68.      return super.onTouchEvent(ev); 
    69.  }
  3. 上面的写法仍有些问题。就是我们在onTouchEventMove里面对移动的距离进行了判断,但是ListView本身就是一个可以上下滑动的组件,如果我们直接这样判断,那ListView本上上下滑动的功能就被我们给抹去了。

       
       
    1.      @Override 
    2.  public boolean onTouchEvent(MotionEvent ev) { 
    3.      int y = (int) ev.getRawY(); 
    4.      int action = ev.getAction(); 
    5.      switch (action) { 
    6.      case MotionEvent.ACTION_DOWN: 
    7.          downPositionY = y; 
    8.          break
    9.      case MotionEvent.ACTION_MOVE: 
    10.          //一定要加上这句话,来判断当前是否可以下拉刷新 
    11.          if (!isCanPullToRefresh) { 
    12.              break
    13.          } 
    14.          ..... 
    15.          break;

    而对于isCanPullToRefresh的判断是通过ListView.setOnScrollListener去进行判断当前第一个可见条目是不是ListView的第一个条目,只有第一个条目在最顶端位置的时候才可以进行下拉刷新。

       
       
    1.  super.setOnScrollListener(new OnScrollListener() { 
    2.  
    3.      @Override 
    4.      public void onScrollStateChanged(AbsListView view, int scrollState) { 
    5.      } 
    6.  
    7.      @Override 
    8.      public void onScroll(AbsListView view, int firstVisibleItem, 
    9.              int visibleItemCount, int totalItemCount) { 
    10.  
    11.          if (firstVisibleItem == 0) { 
    12.              isCanPullToRefresh = true
    13.          } else { 
    14.              isCanPullToRefresh = false
    15.          } 
    16.      } 
    17.  });
  4. 提供刷新接口
       
       
    1.  public interface OnRefreshListener { 
    2.      abstract void onRefresh(); 
    3.  }
  5. ChangeState方法

       
       
    1.  /** 
    2.   * Change the state of header view when ListView in different state. 
    3.   */ 
    4.  private void changeState() { 
    5.      //在该方法中对mState进行判断,根据不同状态作出处理,并且去调用刷新方法 
    6.  } 
    7.  
    8.  /** 
    9.   *数据刷新完成后需要调用此方法去恢复装填 
    10.   */ 
    11.  @SuppressWarnings("deprecation"
    12.  public void onRefreshComplete() { 
    13.      mState = State.ORIGNAL; 
    14.      changeState(); 
    15.      tv_time.setText(getResources().getString(R.string.update_time) 
    16.              + new Date().toLocaleString()); 
    17.  }

LoadMoreListView

原理:
滑动到底部自动加载更多的ListView,无非就是通过对其滑动过程进行监听,一旦滑动到底部的时候我们就去加载新的数据。通过对ListView添加FooterView,然后在不同的状态控制它的显示与隐藏。

  1. 自定义ListView的子类,在构造方法中去添加FooterView.

       
       
    1. public class LoadMoreListView extends ListView { 
    2.  
    3. public LoadMoreListView(Context context, AttributeSet attrs, int defStyle) { 
    4.     super(context, attrs, defStyle); 
    5.     init(context); 
    6.  
    7. public LoadMoreListView(Context context, AttributeSet attrs) { 
    8.     super(context, attrs); 
    9.     init(context); 
    10.  
    11. public LoadMoreListView(Context context) { 
    12.     super(context); 
    13.     init(context); 
    14.  
    15. private void init(Context context) { 
    16.     mFooterView = View.inflate(context, R.layout.load_more_footer, null); 
    17.     addFooterView(mFooterView); 
    18.     hideFooterView(); 
    19. }
  2. 监听滑动状态,通过setOnScrollListener即可实现对状态的监听。

       
       
    1.  private void init(Context context) { 
    2.      mFooterView = View.inflate(context, R.layout.load_more_footer, null); 
    3.      addFooterView(mFooterView); 
    4.      hideFooterView(); 
    5.      //为了防止在使用时调用setOnScrollListener会覆盖此时设置的Listener,我们在此使用super.setOnScrollListenr() 
    6.      super.setOnScrollListener(superOnScrollListener); 
    7.  }
  3. ScrollListener中去判断当前滑动的状态。从而依据不同的状态去控制FooterView的显示与隐藏。

       
       
    1.  private OnScrollListener superOnScrollListener = new OnScrollListener() { 
    2.  
    3.      @Override 
    4.      public void onScrollStateChanged(AbsListView view, int scrollState) { 
    5.          mCurrentScrollState = scrollState; 
    6.      } 
    7.  
    8.      @Override 
    9.      public void onScroll(AbsListView view, int firstVisibleItem, 
    10.              int visibleItemCount, int totalItemCount) { 
    11.          if (visibleItemCount == totalItemCount) { 
    12.              //此时说明当前ListView所有的条目比较少,不足一屏 
    13.              hideFooterView(); 
    14.          } else if (!mIsLoading 
    15.                  && (firstVisibleItem + visibleItemCount >= totalItemCount) 
    16.                  && mCurrentScrollState != SCROLL_STATE_IDLE) { 
    17.              //当第一个可见的条目位置加上当前也所有可见的条目数 等于 ListView当前总的条目数时,就说明已经滑动到了底部,这时候就要去显示FooterView。 
    18.              showFooterView(); 
    19.              mIsLoading = true
    20.              if (mOnLoadMoreListener != null) { 
    21.                  mOnLoadMoreListener.onLoadMore(); 
    22.              } 
    23.          } 
    24.      } 
    25.  };
  4. 提供自动加载更多的接口。

       
       
    1.  public interface OnLoadMoreListener { 
    2.      /** 
    3.       * Load more data. 
    4.       */ 
    5.      void onLoadMore(); 
    6.  }

在使用的时候,与ListView使用方法相同,只需调用setOnLoadMoreListener对其设置OnLoadMoreListener即可,然后在数据加载完成后调用onLoadComplete方法去恢复状态。

PullAndLoadMoreListView

将下拉刷新和自动加载更多进行整合。就不多说了


写的不好,多见谅,写的不明白的地方就看源码吧。

源码:点击打开链接

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值