android下拉刷新上拉加载原理研究

整体布局结构

视图实现原理

该组件整体以竖直方向的LinearLayout为根视图,分别是Header、ContentView、Foooter, 从上到下依次排列下来,其中ContentView的宽高都为match_parent,footer和header的宽、高分别为match_parent、wrap_content,在Header、Foooter初始时都会通过设置padding隐藏掉,只有 ContentView区域显示出来。当用户下拉到顶端,并且继续下拉时触发下拉刷新操作;当用户上拉到底部, 并且继续上拉时触发加载更多的操作。

下拉刷新基本原理

基本原理就是在用户滑动屏幕上的组件时,在 onInterceptTouchEvent方法中判断是否到了ContentView (这里我们以ListView为例来说明)的最顶端,如果到了最顶端且用户还继续向下滑,那么拦截触摸事件,即在 onInterceptTouchEvent中返回true ( 不太清楚的可以 参考资料如下 :  Android Touch事件分发过程 。  ),这样就将触摸事件分发到了onTouchEvent函数中,我们对于用户触摸事件的处理逻辑主要都在这个函数中。

/*
     * 在适当的时候拦截触摸事件,这里指的适当的时候是当mContentView滑动到顶部,并且是下拉时拦截触摸事件,否则不拦截,交给其child
     * view 来处理。
     * @see
     * android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

  /*
   * This method JUST determines whether we want to intercept the motion.
   * If we return true, onTouchEvent will be called and we do the actual
   * scrolling there.
   */
  final int action = MotionEventCompat.getActionMasked(ev);
  // Always handle the case of the touch gesture being complete.
  if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
      // Do not intercept touch event, let the child handle it
      return false;
  }

  switch (action) {

      case MotionEvent.ACTION_DOWN:
    mYDown = (int) ev.getRawY();
    break;

      case MotionEvent.ACTION_MOVE:
    // int yDistance = (int) ev.getRawY() - mYDown;
    mYDistance = (int) ev.getRawY() - mYDown;
    showStatus(mCurrentStatus);
    Log.d(VIEW_LOG_TAG, "%%% isBottom : " + isBottom() + ", isTop : " + isTop()
      + ", mYDistance : " + mYDistance);
    // 如果拉到了顶部, 并且是下拉,则拦截触摸事件,从而转到onTouchEvent来处理下拉刷新事件
    if ((isTop() && mYDistance > 0)
      || (mYDistance > 0 && mCurrentStatus == STATUS_REFRESHING)) {
        return true;
    }
    break;

  }

  // Do not intercept touch event, let the child handle it
  return false;
    }

首先我们在ACTION_DOWN事件中记录用户按下的触摸点的Y轴坐标mYDown,然后在ACTION_MOVE中再次获取Y轴的坐标,计算出两者之间的差值。如果滑动的差值大于mTouchSlop则继续进行处理,mTouchSlop为判断一个触摸滑动事件是否有效的的最小阀值,如果小于这个阀值我们认为这个触摸滑动事件无效,例如手抖了一下,距离很短,因此我们忽略类似的事件。

在有效的滑动距离之内,我们判断当前组件的状态,如果不是正在刷新的状态,那么我们根据当前ListView的paddingTop的高度来设置不同的值, paddingTop 如果高度大于ListView高度的70%,那么我们将当前状态设置为“释放可刷新”状态,即STATUS_RELEASE_TO_REFRESH状态;反之,我们设置状态为“继续下拉”状态,即“ STATUS_PULL_TO_REFRESH ”。通过这个 paddingTop 高度我们来规定当用户下拉距离到一定的距离后才出发刷新操作,否则视为无效下拉。然而不管这个时候是什么状态,我们都会修改Header的padding Top属性,从而达到拉伸header的效果。

当状态为“ 释放可刷新”时,我们抬起手指,会出发ACTION_UP事件,此时我们在该事件中进行下拉刷新操作。onTouchEvent代码如下 :

/*
   * 在这里处理触摸事件以达到下拉刷新或者上拉自动加载的问题
   * @see android.view.View#onTouchEvent(android.view.MotionEvent)
   */
  @Override
  public boolean onTouchEvent(MotionEvent event) {

    Log.d(VIEW_LOG_TAG, "@@@ onTouchEvent : action = " + event.getAction());
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        mYDown = (int) event.getRawY();
        Log.d(VIEW_LOG_TAG, "#### ACTION_DOWN");
        break;

      case MotionEvent.ACTION_MOVE:
        Log.d(VIEW_LOG_TAG, "#### ACTION_MOVE");
        int currentY = (int) event.getRawY();
        mYDistance = currentY - mYDown;
        // 高度大于header view的高度才可以刷新
        if (mYDistance >= mTouchSlop) {
          if (mCurrentStatus != STATUS_REFRESHING) {
            //
            if (mHeaderView.getPaddingTop() > mHeaderViewHeight * 0.7f) {
              mCurrentStatus = STATUS_RELEASE_TO_REFRESH;
              mTipsTextView.setText(R.string.pull_to_refresh_release_label);
            } else {
              mCurrentStatus = STATUS_PULL_TO_REFRESH;
              mTipsTextView.setText(R.string.pull_to_refresh_pull_label);
            }
          }

          rotateHeaderArrow();
          int scaleHeight = (int) (mYDistance * 0.8f);// 去了滑动距离的80%,减小灵敏度而已
          // Y轴的滑动距离小于屏幕高度4分之一时才会拉伸header,反之保持不变
          if (scaleHeight <= mScrHeight / 4) {
            adjustHeaderPadding(scaleHeight);
          }
        }

        break;

      case MotionEvent.ACTION_UP:
        // 下拉刷新的具体操作
        doRefresh();
        break;
      default:
        break;

    }
    return true;
  }

抬起手指时出发的刷新操作,代码如下:

/**
   * 执行刷新操作
   */
  private final void doRefresh() {
    if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
      // 设置状态为正在刷新状态
      mCurrentStatus = STATUS_REFRESHING;
      mArrowImageView.clearAnimation();
      // 隐藏header中的箭头图标
      mArrowImageView.setVisibility(View.GONE);
      // 设置header中的进度条可见
      mHeaderProgressBar.setVisibility(View.VISIBLE);
      // 设置一些文本
      mTimeTextView.setText(R.string.pull_to_refresh_update_time_label);
      SimpleDateFormat sdf = new SimpleDateFormat();
      mTimeTextView.append(sdf.format(new Date()));
      //
      mTipsTextView.setText(R.string.pull_to_refresh_refreshing_label);

      // 执行回调
      if (mPullRefreshListener != null) {
        mPullRefreshListener.onRefresh();
      }
      // 使headview 正常显示, 直到调用了refreshComplete后再隐藏
      new HeaderViewHideTask().execute(0);

    } else {
      // 隐藏header view
      adjustHeaderPadding(-mHeaderViewHeight);
    }
  }

在刷新状态时,header正常显示,即此时的padding top需要设置为0,我们使用一个异步任务来逐步修改padding top的值,使得header从拉伸效果逐步、平滑的恢复原始的大小。用户调用refreshComplete()函数后,即刷新完成后,再逐步调整listview的padding top将其隐藏。至此,整个下拉刷新过程结束。

滑动到底部自动加载

滑动到底部自动加载相对来说要简单得多,我们也是以ContentView是ListView的情况来说明。原理就是监听ListView ( 即 ContentView )的

的滚动事件,因此如果ContentView的类型不支持滚动事件,则不能实现该功能。listview符合要求,因此其能实现自动加载。我们在onScroll函数中判断listview是否到了最后一项,如果到了最后一项,那么显示出footer,并且开始加载。当用户调用loadMoreComplete函数时代表加载结束。此时隐藏footer,整个过程结束。

/*
   * 滚动事件,实现滑动到底部时上拉加载更多
   * @see android.widget.AbsListView.OnScrollListener#onScroll(android.widget.
   * AbsListView, int, int, int)
   */
  @Override
  public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
      int totalItemCount) {

    Log.d(VIEW_LOG_TAG, "&&& mYDistance = " + mYDistance);
    if (mFooterView == null || mYDistance >= 0 || mCurrentStatus == STATUS_LOADING
        || mCurrentStatus == STATUS_REFRESHING) {
      return;
    }

    loadmore();
  }

  /**
   * 下拉到底部时加载更多
   */
  private void loadmore() {
    if (isShowFooterView() && mLoadMoreListener != null) {
      mFooterTextView.setText(R.string.pull_to_refresh_refreshing_label);
      mFooterProgressBar.setVisibility(View.VISIBLE);
      adjustFooterPadding(0);
      mCurrentStatus = STATUS_LOADING;
      mLoadMoreListener.onLoadMore();
    }
  }

其中loadmore函数中调用的isShowFooterView函数就是用来判断是否到了最底部的,代码如下 :

/*
  * 下拉到listview 最后一项时则返回true, 将出发自动加载
  * @see com.uit.pullrefresh.base.PullRefreshBase#isShowFooterView()
  */
    @Override
    protected boolean isShowFooterView() {
     if (mContentView == null || mContentView.getAdapter() == null) {
      return false;
     }

     return mContentView.getLastVisiblePosition() == mContentView.getAdapter().getCount() - 1;
    }

OK,至此整个核心的过程介绍完毕了。

效果图

ListView

TextView下拉刷新

github地址

猛击这里!

时间: 2024-10-14 11:44:41

android下拉刷新上拉加载原理研究的相关文章

最新Android ListView 下拉刷新 上滑加载

开发项目过程中基本都会用到listView的下拉刷新和上滑加载更多,之前大家最常用的应该是pull to refresh或它的变种版吧,google官方在最新的android.support.v4包中增加了一个新类SwipeRefreshLayout,地址 这个类的作用就是提供官方的下拉刷新,并且效果相当不错,而上拉加载更多则用我们自定义的listview,也是相当简单. 下拉刷新 简单的介绍下: 首先它是一个viewgroup,但是它只允许有一个子控件,子控件能是任何view,使用的时候,所在

十分钟实现ListView下拉刷新上滑加载更多

说到ListView下拉刷新几乎每个APP都会用到,所以ListView下拉刷新是很重要的,就像ListView优化一样是你必会的东西. ListView实现下拉刷新如果我们开发人员自己编写相对来说比较费事的,当我们使用第三方库之后我们再来开发这个功能就会省事很多.相比与自己实现可以少编写不少代码,Android-PullToRefresh库可以轻松实现ListView的下拉刷新功能. 要使用Android—PullToRefesh库对ListView实现下拉刷新要经过以下几个步骤: 1.下载A

Android 下拉刷新上啦加载SmartRefreshLayout + RecyclerView

在弄android刷新的时候,可算是耗费了一番功夫,最后发觉有现成的控件,并且非常好用,这里记录一下. 原文是 https://blog.csdn.net/huangxin112/article/details/78781682 ,这里是看了之后,结合自己实际遇到的问题写的. 首先引入包. //下拉框 implementation 'com.android.support:recyclerview-v7:28.0.0-beta01' implementation 'com.scwang.smar

android 安卓 listview 支持下拉刷新 上拉加载更多

[1]重写listView import java.text.SimpleDateFormat; import java.util.Date; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGrou

Android 下拉刷新上拉加载 多种应用场景 超级大放送(上)

转载请标明原文地址:http://blog.csdn.net/yalinfendou/article/details/47707017 关于Android下拉刷新上拉加载,网上的Demo太多太多了,这里不是介绍怎么去实现下拉刷新上拉加载,而是针对下拉刷新上拉加载常用的一些应用场景就行了一些总结,包含了下拉刷新上拉加载过程中遇到的一些手势冲突问题的解决方法(只能算是抛砖引玉). 去年9月的时候,那时自己正在独立做Android项目.记得刚刚写完那个ListView列表页面(木有下拉刷新,上拉加载)

android 下拉刷新上拉加载更多,高仿ios左滑动删除item,解决了众多手势问题

一.前言 老规矩,别的不说,这demo是找了很相关知识集合而成的,可以说对我这种小白来说是绞尽脑汁!程序员讲的是无图无真相!现在大家一睹为快! 二.比较关键的还是scroller这个类的 package com.icq.slideview.view; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; i

Android 下拉刷新上拉加载效果功能,使用开源项目android-pulltorefresh实现

应用场景: 在App开发中,对于信息的获取与演示,不可能全部将其获取与演示,为了在用户使用中,给予用户以友好.方便的用户体验,以滑动.下拉的效果动态加载数据的要求就会出现.为此,该效果功能就需要应用到所需要的展示页面中. 知识点介绍: 本文主要根据开源项目android-pulltorefresh展开介绍. android-pulltorefresh [一个强大的拉动刷新开源项目,支持各种控件下拉刷新 ListView.ViewPager.WevView.ExpandableListView.G

android Fragment 实例 Listfragment listiew 下拉刷新 上拉加载 横向滑动事件监听

Android Fragment开发实例及他色功能说明 代码太长,请网盘下载附件源代码: http://yunpan.cn/cs24a2Z7C5kRk (提取码:a0cf)  有疑问的欢迎联系QQ972910164 一. CustomListView说明:可下拉刷新,上拉加载,横向滑动接口回调, /** * 功能类似ListView,天机以下特性: * 1. 可下拉刷新,上拉加载,实现CustomListView.OnPullListener接口 * 3. Slide切换,实现CustomLis

Android 下拉刷新上拉加载效果功能

应用场景: 在App开发中,对于信息的获取与演示,不可能全部将其获取与演示,为了在用户使用中,给予用户以友好.方便的用户体验,以滑动.下拉的效果动态加载数据的要求就会出现.为此,该效果功能就需要应用到所需要的展示页面中. 知识点介绍: 本文主要根据开源项目android-pulltorefresh展开介绍. android-pulltorefresh [一个强大的拉动刷新开源项目,支持各种控件下拉刷新 ListView.ViewPager.WevView.ExpandableListView.G

自个儿写Android的下拉刷新/上拉加载控件 (续)

本文算是对之前的一篇博文<自个儿写Android的下拉刷新/上拉加载控件>的续章,如果有兴趣了解更多的朋友可以先看一看之前的这篇博客. 事实上之所以会有之前的那篇博文的出现,是起因于前段时间自己在写一个练手的App时很快就遇到这种需求.其实我们可以发现类似这样下拉刷新.上拉加载的功能正在变得越来越普遍,可以说如今基本上绝大多数的应用里面都会使用到.当然,随着Android的发展,已经有不少现成的可以实现这种需求的"轮子"供我们使用了. 但转过头想一下想,既然本来就是自己练手