超好看的下拉刷新动画Android代码实现_Android

最近看到了好多高端、大气、上档次的动画效果,如果给你的项目中加上这些动画,相信你的app一定很优秀,今天给大家分析一下来自Yalantis的一个超好看的下拉刷新动画。

首先我们看一下效果如何:

怎么样?是不是很高大上?接下来我们看一下代码:

一、首先我们需要自定义刷新的动态RefreshView(也就是下拉时候的头)
1.初始化头所占用的Dimens

private void initiateDimens() {
    mScreenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
    mJetTopOffset = mParent.getTotalDragDistance() * 0.5f;
    mTop = -mParent.getTotalDragDistance();
  }

2.为头填充图片,设置图片的大小
分别为左边的云彩,右边的云彩,中间的云彩还有中间的飞机,飞机是带有动画的,下面会介绍飞机的动画

private void createBitmaps() {
    mLeftClouds = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.clouds_left);
    mRightClouds = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.clouds_right);
    mFrontClouds = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.clouds_center);
    mJet = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.airplane); 

    mJetWidthCenter = mJet.getWidth() / 2;
    mJetHeightCenter = mJet.getHeight() / 2;
    mFrontCloudWidthCenter = mFrontClouds.getWidth() / 2;
    mFrontCloudHeightCenter = mFrontClouds.getHeight() / 2; 

    mRightCloudsWidthCenter = mRightClouds.getWidth() / 2;
    mRightCloudsHeightCenter = mRightClouds.getHeight() / 2;
    mLeftCloudsWidthCenter = mLeftClouds.getWidth() / 2;
    mLeftCloudsHeightCenter = mLeftClouds.getHeight() / 2;
  } 

3.然后我们来画这个头

public void draw(@NonNull Canvas canvas) {
    final int saveCount = canvas.save(); 

    // DRAW BACKGROUND.
    canvas.drawColor(mContext.getResources().getColor(R.color.sky_background)); 

    if (isRefreshing) {
      // Set up new set of wind
      while (mWinds.size() < WIND_SET_AMOUNT) {
        float y = (float) (mParent.getTotalDragDistance() / (Math.random() * RANDOM_Y_COEFFICIENT));
        float x = random(MIN_WIND_X_OFFSET, MAX_WIND_X_OFFSET); 

        // Magic with checking interval between winds
        if (mWinds.size() > 1) {
          y = 0;
          while (y == 0) {
            float tmp = (float) (mParent.getTotalDragDistance() / (Math.random() * RANDOM_Y_COEFFICIENT)); 

            for (Map.Entry<Float, Float> wind : mWinds.entrySet()) {
              // We want that interval will be greater than fifth part of draggable distance
              if (Math.abs(wind.getKey() - tmp) > mParent.getTotalDragDistance() / RANDOM_Y_COEFFICIENT) {
                y = tmp;
              } else {
                y = 0;
                break;
              }
            }
          }
        } 

        mWinds.put(y, x);
        drawWind(canvas, y, x);
      } 

      // Draw current set of wind
      if (mWinds.size() >= WIND_SET_AMOUNT) {
        for (Map.Entry<Float, Float> wind : mWinds.entrySet()) {
          drawWind(canvas, wind.getKey(), wind.getValue());
        }
      } 

      // We should to create new set of winds
      if (mInverseDirection && mNewWindSet) {
        mWinds.clear();
        mNewWindSet = false;
        mWindLineWidth = random(MIN_WIND_LINE_WIDTH, MAX_WIND_LINE_WIDTH);
      } 

      // needed for checking direction
      mLastAnimationTime = mLoadingAnimationTime;
    } 

    drawJet(canvas);
    drawSideClouds(canvas);
    drawCenterClouds(canvas); 

    canvas.restoreToCount(saveCount);
  }
/**
   * Draw wind on loading animation
   *
   * @param canvas - area where we will draw
   * @param y    - y position fot one of lines
   * @param xOffset - x offset for on of lines
   */
  private void drawWind(Canvas canvas, float y, float xOffset) {
    /* We should multiply current animation time with this coefficient for taking all screen width in time
    Removing slowing of animation with dividing on {@LINK #SLOW_DOWN_ANIMATION_COEFFICIENT}
    And we should don't forget about distance that should "fly" line that depend on screen of device and x offset
    */
    float cof = (mScreenWidth + xOffset) / (LOADING_ANIMATION_COEFFICIENT / SLOW_DOWN_ANIMATION_COEFFICIENT);
    float time = mLoadingAnimationTime; 

    // HORRIBLE HACK FOR REVERS ANIMATION THAT SHOULD WORK LIKE RESTART ANIMATION
    if (mLastAnimationTime - mLoadingAnimationTime > 0) {
      mInverseDirection = true;
      // take time from 0 to end of animation time
      time = (LOADING_ANIMATION_COEFFICIENT / SLOW_DOWN_ANIMATION_COEFFICIENT) - mLoadingAnimationTime;
    } else {
      mNewWindSet = true;
      mInverseDirection = false;
    } 

    // Taking current x position of drawing wind
    // For fully disappearing of line we should subtract wind line width
    float x = (mScreenWidth - (time * cof)) + xOffset - mWindLineWidth;
    float xEnd = x + mWindLineWidth; 

    canvas.drawLine(x, y, xEnd, y, mWindPaint);
  } 

  private void drawSideClouds(Canvas canvas) {
    Matrix matrixLeftClouds = mMatrix;
    Matrix matrixRightClouds = mAdditionalMatrix;
    matrixLeftClouds.reset();
    matrixRightClouds.reset(); 

    // Drag percent will newer get more then 1 here
    float dragPercent = Math.min(1f, Math.abs(mPercent)); 

    boolean overdrag = false; 

    // But we check here for overdrag
    if (mPercent > 1.0f) {
      overdrag = true;
    } 

    float scale;
    float scalePercentDelta = dragPercent - SCALE_START_PERCENT;
    if (scalePercentDelta > 0) {
      float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT);
      scale = SIDE_CLOUDS_INITIAL_SCALE + (SIDE_CLOUDS_FINAL_SCALE - SIDE_CLOUDS_INITIAL_SCALE) * scalePercent;
    } else {
      scale = SIDE_CLOUDS_INITIAL_SCALE;
    } 

    // Current y position of clouds
    float dragYOffset = mParent.getTotalDragDistance() * (1.0f - dragPercent); 

    // Position where clouds fully visible on screen and we should drag them with content of listView
    int cloudsVisiblePosition = mParent.getTotalDragDistance() / 2 - mLeftCloudsHeightCenter; 

    boolean needMoveCloudsWithContent = false;
    if (dragYOffset < cloudsVisiblePosition) {
      needMoveCloudsWithContent = true;
    } 

    float offsetRightX = mScreenWidth - mRightClouds.getWidth();
    float offsetRightY = (needMoveCloudsWithContent
        ? mParent.getTotalDragDistance() * dragPercent - mLeftClouds.getHeight()
        : dragYOffset)
        + (overdrag ? mTop : 0); 

    float offsetLeftX = 0;
    float offsetLeftY = (needMoveCloudsWithContent
        ? mParent.getTotalDragDistance() * dragPercent - mLeftClouds.getHeight()
        : dragYOffset)
        + (overdrag ? mTop : 0); 

    // Magic with animation on loading process
    if (isRefreshing) {
      if (checkCurrentAnimationPart(AnimationPart.FIRST)) {
        offsetLeftY += getAnimationPartValue(AnimationPart.FIRST) / Y_SIDE_CLOUDS_SLOW_DOWN_COF;
        offsetRightX -= getAnimationPartValue(AnimationPart.FIRST) / X_SIDE_CLOUDS_SLOW_DOWN_COF;
      } else if (checkCurrentAnimationPart(AnimationPart.SECOND)) {
        offsetLeftY += getAnimationPartValue(AnimationPart.SECOND) / Y_SIDE_CLOUDS_SLOW_DOWN_COF;
        offsetRightX -= getAnimationPartValue(AnimationPart.SECOND) / X_SIDE_CLOUDS_SLOW_DOWN_COF;
      } else if (checkCurrentAnimationPart(AnimationPart.THIRD)) {
        offsetLeftY -= getAnimationPartValue(AnimationPart.THIRD) / Y_SIDE_CLOUDS_SLOW_DOWN_COF;
        offsetRightX += getAnimationPartValue(AnimationPart.THIRD) / X_SIDE_CLOUDS_SLOW_DOWN_COF;
      } else if (checkCurrentAnimationPart(AnimationPart.FOURTH)) {
        offsetLeftY -= getAnimationPartValue(AnimationPart.FOURTH) / X_SIDE_CLOUDS_SLOW_DOWN_COF;
        offsetRightX += getAnimationPartValue(AnimationPart.FOURTH) / Y_SIDE_CLOUDS_SLOW_DOWN_COF;
      }
    } 

    matrixRightClouds.postScale(scale, scale, mRightCloudsWidthCenter, mRightCloudsHeightCenter);
    matrixRightClouds.postTranslate(offsetRightX, offsetRightY); 

    matrixLeftClouds.postScale(scale, scale, mLeftCloudsWidthCenter, mLeftCloudsHeightCenter);
    matrixLeftClouds.postTranslate(offsetLeftX, offsetLeftY); 

    canvas.drawBitmap(mLeftClouds, matrixLeftClouds, null);
    canvas.drawBitmap(mRightClouds, matrixRightClouds, null);
  } 

  private void drawCenterClouds(Canvas canvas) {
    Matrix matrix = mMatrix;
    matrix.reset();
    float dragPercent = Math.min(1f, Math.abs(mPercent)); 

    float scale;
    float overdragPercent = 0;
    boolean overdrag = false; 

    if (mPercent > 1.0f) {
      overdrag = true;
      // Here we want know about how mach percent of over drag we done
      overdragPercent = Math.abs(1.0f - mPercent);
    } 

    float scalePercentDelta = dragPercent - SCALE_START_PERCENT;
    if (scalePercentDelta > 0) {
      float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT);
      scale = CENTER_CLOUDS_INITIAL_SCALE + (CENTER_CLOUDS_FINAL_SCALE - CENTER_CLOUDS_INITIAL_SCALE) * scalePercent;
    } else {
      scale = CENTER_CLOUDS_INITIAL_SCALE;
    } 

    float parallaxPercent = 0;
    boolean parallax = false;
    // Current y position of clouds
    float dragYOffset = mParent.getTotalDragDistance() * dragPercent;
    // Position when should start parallax scrolling
    int startParallaxHeight = mParent.getTotalDragDistance() - mFrontCloudHeightCenter; 

    if (dragYOffset > startParallaxHeight) {
      parallax = true;
      parallaxPercent = dragYOffset - startParallaxHeight;
    } 

    float offsetX = (mScreenWidth / 2) - mFrontCloudWidthCenter;
    float offsetY = dragYOffset
        - (parallax ? mFrontCloudHeightCenter + parallaxPercent : mFrontCloudHeightCenter)
        + (overdrag ? mTop : 0); 

    float sx = overdrag ? scale + overdragPercent / 4 : scale;
    float sy = overdrag ? scale + overdragPercent / 2 : scale; 

    if (isRefreshing && !overdrag) {
      if (checkCurrentAnimationPart(AnimationPart.FIRST)) {
        sx = scale - (getAnimationPartValue(AnimationPart.FIRST) / LOADING_ANIMATION_COEFFICIENT) / 8;
      } else if (checkCurrentAnimationPart(AnimationPart.SECOND)) {
        sx = scale - (getAnimationPartValue(AnimationPart.SECOND) / LOADING_ANIMATION_COEFFICIENT) / 8;
      } else if (checkCurrentAnimationPart(AnimationPart.THIRD)) {
        sx = scale + (getAnimationPartValue(AnimationPart.THIRD) / LOADING_ANIMATION_COEFFICIENT) / 6;
      } else if (checkCurrentAnimationPart(AnimationPart.FOURTH)) {
        sx = scale + (getAnimationPartValue(AnimationPart.FOURTH) / LOADING_ANIMATION_COEFFICIENT) / 6;
      }
      sy = sx;
    } 

    matrix.postScale(sx, sy, mFrontCloudWidthCenter, mFrontCloudHeightCenter);
    matrix.postTranslate(offsetX, offsetY); 

    canvas.drawBitmap(mFrontClouds, matrix, null);
  } 

  private void drawJet(Canvas canvas) {
    Matrix matrix = mMatrix;
    matrix.reset(); 

    float dragPercent = mPercent;
    float rotateAngle = 0; 

    // Check overdrag
    if (dragPercent > 1.0f && !mEndOfRefreshing) {
      rotateAngle = (dragPercent % 1) * 10;
      dragPercent = 1.0f;
    } 

    float offsetX = ((mScreenWidth * dragPercent) / 2) - mJetWidthCenter; 

    float offsetY = mJetTopOffset
        + (mParent.getTotalDragDistance() / 2)
        * (1.0f - dragPercent)
        - mJetHeightCenter; 

    if (isRefreshing) {
      if (checkCurrentAnimationPart(AnimationPart.FIRST)) {
        offsetY -= getAnimationPartValue(AnimationPart.FIRST);
      } else if (checkCurrentAnimationPart(AnimationPart.SECOND)) {
        offsetY -= getAnimationPartValue(AnimationPart.SECOND);
      } else if (checkCurrentAnimationPart(AnimationPart.THIRD)) {
        offsetY += getAnimationPartValue(AnimationPart.THIRD);
      } else if (checkCurrentAnimationPart(AnimationPart.FOURTH)) {
        offsetY += getAnimationPartValue(AnimationPart.FOURTH);
      }
    } 

    matrix.setTranslate(offsetX, offsetY); 

    if (dragPercent == 1.0f) {
      matrix.preRotate(rotateAngle, mJetWidthCenter, mJetHeightCenter);
    } 

    canvas.drawBitmap(mJet, matrix, null);
  } 

动画效果已经画好了,下面我们来看看怎么结合下拉刷新来调用吧?
二、我们还需要自定义一个PullToRefreshView(下拉刷新)
1.我们的PullToRefreshView这里需要继承ViewGroup
我们先把刚才定义的刷新时的动画加进来

private RefreshView mRefreshView;
<pre name="code" class="java">private ImageView mRefreshImageView;
<pre name="code" class="java">mRefreshImageView = new ImageView(context);
    mRefreshView = new RefreshView(getContext(), this);
    mRefreshImageView.setImageDrawable(mRefreshView); 

    addView(mRefreshImageView);

2.然后我们设置OntouchEvent()事件

@Override
  public boolean onTouchEvent(@NonNull MotionEvent ev) { 

    if (!mIsBeingDragged) {
      return super.onTouchEvent(ev);
    } 

    final int action = MotionEventCompat.getActionMasked(ev); 

    switch (action) {
      case MotionEvent.ACTION_MOVE: {
        final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
        if (pointerIndex < 0) {
          return false;
        } 

        final float y = MotionEventCompat.getY(ev, pointerIndex);
        final float yDiff = y - mInitialMotionY;
        final float scrollTop = yDiff * DRAG_RATE;
        mCurrentDragPercent = scrollTop / mTotalDragDistance;
        if (mCurrentDragPercent < 0) {
          return false;
        }
        float boundedDragPercent = Math.min(1f, Math.abs(mCurrentDragPercent));
        float extraOS = Math.abs(scrollTop) - mTotalDragDistance;
        float slingshotDist = mTotalDragDistance;
        float tensionSlingshotPercent = Math.max(0,
            Math.min(extraOS, slingshotDist * 2) / slingshotDist);
        float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow(
            (tensionSlingshotPercent / 4), 2)) * 2f;
        float extraMove = (slingshotDist) * tensionPercent / 2;
        int targetY = (int) ((slingshotDist * boundedDragPercent) + extraMove); 

        mRefreshView.setPercent(mCurrentDragPercent);
        setTargetOffsetTop(targetY - mCurrentOffsetTop, true);
        break;
      }
      case MotionEventCompat.ACTION_POINTER_DOWN:
        final int index = MotionEventCompat.getActionIndex(ev);
        mActivePointerId = MotionEventCompat.getPointerId(ev, index);
        break;
      case MotionEventCompat.ACTION_POINTER_UP:
        onSecondaryPointerUp(ev);
        break;
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL: {
        if (mActivePointerId == INVALID_POINTER) {
          return false;
        }
        final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
        final float y = MotionEventCompat.getY(ev, pointerIndex);
        final float overScrollTop = (y - mInitialMotionY) * DRAG_RATE;
        mIsBeingDragged = false;
        if (overScrollTop > mTotalDragDistance) {
          setRefreshing(true, true);
        } else {
          mRefreshing = false;
          animateOffsetToPosition(mAnimateToStartPosition);
        }
        mActivePointerId = INVALID_POINTER;
        return false;
      }
    } 

    return true;
  }

三、最后我们看怎样在Activity中使用这个下拉刷新控件
1.先看一下布局文件
这里是我们的下拉刷新空间嵌套着我们的ListView,然后我们再给ListView填充数据即可

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".PullToRefreshActivity"> 

  <com.hankkin.AnimationPullToRefreshDemo.PullToRefreshView
    android:id="@+id/pull_to_refresh"
    android:layout_width="match_parent"
    android:layout_height="match_parent"> 

    <ListView
      android:id="@+id/list_view"
      android:divider="@null"
      android:dividerHeight="0dp"
      android:fadingEdge="none"
      android:layout_width="match_parent"
      android:layout_height="match_parent" /> 

  </com.hankkin.AnimationPullToRefreshDemo.PullToRefreshView> 

</RelativeLayout>

2.为ListView填充数据
为了我们的效果比较好看,这里我们给ListView的每一个item填充不同的颜色,看起来会比较高大上。

Map<String, Integer> map;
    List<Map<String, Integer>> sampleList = new ArrayList<Map<String, Integer>>(); 

    int[] colors = {
        R.color.saffron,
        R.color.eggplant,
        R.color.sienna}; 

    int[] tripNames = {
        R.string.trip_to_india,
        R.string.trip_to_italy,
        R.string.trip_to_indonesia}; 

    for (int i = 0; i < tripNames.length; i++) {
      map = new HashMap<String, Integer>();
      map.put(SampleAdapter.KEY_NAME, tripNames[i]);
      map.put(SampleAdapter.KEY_COLOR, colors[i]);
      sampleList.add(map);
    } 

    ListView listView = (ListView) findViewById(R.id.list_view);
    listView.setAdapter(new SampleAdapter(this, R.layout.list_item, sampleList));

3.最后,我们再设置一下下拉刷新的监听事件就OK了

mPullToRefreshView = (PullToRefreshView) findViewById(R.id.pull_to_refresh);
    mPullToRefreshView.setOnRefreshListener(new PullToRefreshView.OnRefreshListener() {
      @Override
      public void onRefresh() {
        mPullToRefreshView.postDelayed(new Runnable() {
          @Override
          public void run() {
            mPullToRefreshView.setRefreshing(false);
          }
        }, REFRESH_DELAY);
      }
    });

怎么样?有没有很高大上啊?

大家可以动手实践一下,希望对大家的学习有所帮助。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索Android下拉刷新
Android刷新
android 下拉刷新动画、ios 下拉刷新动画、下拉刷新动画、ios下拉刷新动画效果、ios 下拉刷新动画图片,以便于您获取更多的相关知识。

时间: 2024-09-11 12:10:26

超好看的下拉刷新动画Android代码实现_Android的相关文章

超好看的下拉刷新动画Android代码实现

最近看到了好多高端.大气.上档次的动画效果,如果给你的项目中加上这些动画,相信你的app一定很优秀,今天给大家分析一下来自Yalantis的一个超好看的下拉刷新动画. 首先我们看一下效果如何: 怎么样?是不是很高大上?接下来我们看一下代码: 一.首先我们需要自定义刷新的动态RefreshView(也就是下拉时候的头) 1.初始化头所占用的Dimens private void initiateDimens() { mScreenWidth = mContext.getResources().ge

Android下拉刷新框架实现代码实例_Android

前段时间项目中用到了下拉刷新功能,之前在网上也找到过类似的demo,但这些demo的质量参差不齐,用户体验也不好,接口设计也不行.最张没办法,终于忍不了了,自己就写了一个下拉刷新的框架,这个框架是一个通用的框架,效果和设计感觉都还不错,现在分享给各位看官. 一. 关于下拉刷新 下拉刷新这种用户交互最早由twitter创始人洛伦•布里切特(Loren Brichter)发明,有理论认为,下拉刷新是一种适用于按照从新到旧的时间顺序排列feeds的应用,在这种应用场景中看完旧的内容时,用户会很自然地下

Android使用PullToRefresh实现上拉加载和下拉刷新效果的代码_Android

在没给大家介绍正文之前,先给大家介绍展示下运行图,如果大家感觉还不错,请继续往下阅读: 相关阅读:分享Android中pullToRefresh的使用心得 项目已同步至:https://github.com/nanchen2251/pullToRefreshDemo 简单使用详情: 1)studio可以直接在app的module设置中直接进行搜索,但是有-的必须添上,而不能用空格代替,为了更加了解这个东西,我还是推荐大家去这里看看,奉上网址: https://github.com/chrisba

Android使用PullToRefresh实现上拉加载和下拉刷新效果的代码

在没给大家介绍正文之前,先给大家介绍展示下运行图,如果大家感觉还不错,请继续往下阅读: 相关阅读:分享Android中pullToRefresh的使用心得 项目已同步至:https://github.com/nanchen2251/pullToRefreshDemo 简单使用详情: 1)studio可以直接在app的module设置中直接进行搜索,但是有-的必须添上,而不能用空格代替,为了更加了解这个东西,我还是推荐大家去这里看看,奉上网址: https://github.com/chrisba

Android自定义控件案例汇总2(自定义开关、下拉刷新、侧滑菜单)_Android

案例四 自定义开关: 功能介绍:本案例实现的功能是创建一个自定义的开关,可以自行决定开关的背景.当滑动开关时,开关的滑块可跟随手指移动.当手指松开后,滑块根据开关的状态,滑到最右边或者滑到最左边,同时保存开关的状态,将开关的状态回调给调用者.当然,上述功能系统给定的switch控件也可以实现. 实现步骤:         1. 写一个类继承view,重写两个参数的构造方法.在构造方法中指定工作空间,通过attrs.getAttributeResourceValue方法将java代码中的属性值和x

Android中ListView下拉刷新的实现代码

Android中ListView下拉刷新 实现效果图: ListView中的下拉刷新是非常常见的,也是经常使用的,看到有很多同学想要,那我就整理一下,供大家参考.那我就不解释,直接上代码了. 这里需要自己重写一下ListView,重写代码如下: package net.loonggg.listview; import java.util.Date; import android.content.Context; import android.util.AttributeSet; import a

Android自定义渐变式炫酷ListView下拉刷新动画

本文实例为大家分享了自定义渐变式炫酷动画的ListView下拉刷新,供大家参考,具体内容如下 主要要点 listview刷新过程中主要有三个步骤当前:状态为下拉刷新,当前状态为下拉刷新,当前状态为放开刷新,当前状态为正在刷新:主要思路为三个步骤分别对应三个自定义的view:即ibuRefreshFirstStepView,ibuRefreshSecondStepView,ibuRefreshThirdStepView. 效果图 ibuRefreshFirstStepView代码,例如: priv

Android下拉刷新框架实现代码实例

前段时间项目中用到了下拉刷新功能,之前在网上也找到过类似的demo,但这些demo的质量参差不齐,用户体验也不好,接口设计也不行.最张没办法,终于忍不了了,自己就写了一个下拉刷新的框架,这个框架是一个通用的框架,效果和设计感觉都还不错,现在分享给各位看官. 一. 关于下拉刷新 下拉刷新这种用户交互最早由twitter创始人洛伦•布里切特(Loren Brichter)发明,有理论认为,下拉刷新是一种适用于按照从新到旧的时间顺序排列feeds的应用,在这种应用场景中看完旧的内容时,用户会很自然地下

Android PullToRefreshLayout下拉刷新控件的终结者_Android

       说到下拉刷新控件,网上版本有很多,很多软件也都有下拉刷新功能.有一个叫XListView的,我看别人用过,没看过是咋实现的,看这名字估计是继承自ListView修改的,不过效果看起来挺丑的,也没什么扩展性,太单调了.看了QQ2014的列表下拉刷新,发现挺好看的,我喜欢,贴一下图看一下qq的下拉刷新效果:                                                不错吧?嗯,是的.一看就知道实现方式不一样.咱们今天就来实现一个下拉刷新控件.由于有时