android 有阻尼下拉刷新列表的实现方法

本文将会介绍有阻尼下拉刷新列表的实现,先来看看效果预览:

这是下拉状态:

这是下拉松开手指后listView回滚到刷新状态时的样子:

1. 如何调用

虽然效果图看起来样子不太好看,主要是因为那个蓝色的背景对不对,没关系,这只是一个背景而已,在了解了我们这个下拉刷新列表的实现之后,你就可以很轻松地修改这个背景,从而实现你想要的UI效果!话不多说,下面我们先来讲讲这个下拉刷新列表是如何使用的,这也是我们编写代码所要实现的目标。

    final PullToRefreshListView eListView = (PullToRefreshListView) rootView.findViewById(R.id.profile_listView);
    eListView.setOnLoadCallBack(new PullToRefreshListView.OnLoadCallBack() {
      @Override
      public int whereToLoad() {
        return PullToRefreshListView.DEFAULT_WHERE_TO_LOAD;
      }
      @Override
      public void onLoad() {
        eListView.postDelayed(new Runnable() {
          @Override
          public void run() {
            eListView.setLoadingFinish();
          }
        }, 5000);
      }
      @Override
      public void cancelLoad() {
      }
      @Override
      public Drawable refreshDrawable() {
        return new ColorDrawable(Color.CYAN);
      }
    });
    eListView.setAdapter(new BaseAdapter() {
      @Override
      public int getCount() {
        return 30;
      }
      @Override
      public Object getItem(int position) {
        return null;
      }
      @Override
      public long getItemId(int position) {
        return 0;
      }
      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
        TextView tv;
        if (convertView == null) {
          tv = new TextView(getActivity());
          tv.setGravity(Gravity.CENTER_VERTICAL);
          tv.setHeight(200);
          tv.setBackgroundColor(Color.WHITE);
        } else {
          tv = (TextView) convertView;
        }
        tv.setText(position+"");
        return tv;
      }
    });

在上述代码中,我们可以看到PullToRefreshListView的使用在adapter上跟ListView是一样的,这个当然,因为我们实现下拉刷新功能并不需要修改数据适配器。我们也看到,PullToRefreshListView的实例需要设置一个OnLoadCallBack回调,该回调需要实现4个方法,包括:

  /**
   * 下拉刷新的回调
   */
  public interface OnLoadCallBack {
    /**
     * 下拉结束后将listView定位到哪个位置等待刷新完成
     * @return listView的定位y坐标值,in dp
     */
    int whereToLoad();
    /**
     * 下拉结束后进行刷新的回调
     */
    void onLoad();
    /**
     * 取消刷新
     */
    void cancelLoad();
    /**
     * 下拉刷新的背景
     * @return 背景drawable
     */
    Drawable refreshDrawable();
  }

whereToLoad方法告知PullToRefreshListView对象下拉刷新时停留在哪个位置,具体点说,也就是上述第二章效果图中蓝色背景的高度。onLoad方法是下拉刷新的回调,调用者可以在这里实现刷新动作。cancelLoad方法是取消刷新动作的回调,调用者需要在这里将刷新动作取消。

根据上述方法,我们可以猜测,在onLoad方法中执行的应该是一个线程或者AsyncTask,而在cancelLoad方法中要做的就是将这个线程或者AsyncTask取消掉。最后还有一个refreshDrawable方法,这个方法是为修改listView的背景而提供给调用者的,调用者可以返回任意一个喜欢的背景Drawable。

知道如何调用以后,我们就要一步一步地实现这个PullToRefreshListView了。

2. 在dispatchDraw中重画子View实现下拉视觉

PullToRefreshListView实现的关键在于重画该listVIew的子View。重画ViewGroup的子View一般是在dispatchDraw方法中实现的。因此,我们的PullToRefreshListView继承自ListView类,重载其dispatchDraw方法。

  @Override
  protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    if (distanceY > 0) {
      if (refreshDrawable == null) {
        refreshDrawable = onLoadCallBack.refreshDrawable();
      }
      if (refreshDrawable == null) {
        canvas.drawColor(Color.GRAY);
      } else {
        int left = getPaddingLeft();
        int top = getPaddingTop();
        refreshDrawable.setBounds(left, top, getWidth()+left, getHeight()+top);
        refreshDrawable.draw(canvas);
      }
      canvas.save();
      canvas.translate(getPaddingLeft(), getPaddingTop() + distanceY);
      for (int i=0;i<getChildCount();i++) {
        View child = getChildAt(i);
        drawChild(canvas, child, getDrawingTime());
      }
      canvas.restore();
    }
  }

重画子View的关键在于这一句代码:

canvas.translate(getPaddingLeft(), getPaddingTop() + distanceY); 

在重画子View之前,我们需要先将canvas向上移动distanceY距离。这是为什么呢?我们先来看看在canvas画子View的方法

drawChild方法的文档是怎么说的。

protected boolean drawChild (Canvas canvas, View child, long drawingTime)

Added in API level 1 Draw one child of this View Group. This method is responsible for getting the canvas in the right state. This includes clipping, translating so that the child's scrolled origin is at 0, 0, and applying any animation transformations.

Parameters canvas The canvas on which to draw the child child Who to draw drawingTime The time at which draw is occurring Returns True if an invalidate() was issued

我来翻译一下,drawChild方法可以画出这个View Group的一个子View。该方法需要使canvas处于一个正确的状态,该状态就

是通过对canvas进行clip裁剪,translate评议操作等以使得该子View位于canvas的(0,0)位置。

什么意思呢?简单来说就是,drawChild方法会将child view画在canvas的(0,0)位置,因此为了使得该child view位于

canvas的正确位置,我们需要在重画之前对canvas进行裁剪平移等操作。举个例子,有一个canvas和一个child view,本来

child view要画在(0,0)位置上,于是呈现在我们眼前的child view就是位于canvas的顶部,但是如果在画之前我们将

canvas向上移动100个像素单位,然后再将child view画在(0,0)位置上,那么呈现在我们眼前的child view的位置将会是

位于canvas的(0,100)位置上。

根据以上分析,我们可以知道,重画子View的原理就是:

当PullToRefreshListView已经滚动到顶部的时候,通过监控滑动手势来计算distanceY,从而确定要将canvas向上移动多少再重画子View,就可以实现PullToRefreshListView跟随滑动手势进行下拉的功能了。

3. 计算下拉距离

实现了重画以后,我们需要做的就是如何计算distanceY。我们的初步想法是,根据滑动的距离来计算,考虑到我们要实现阻尼效果,即随着滑动距离的变长,PullToRefreshListView的下拉距离会越来越短。在PullToRefreshListView实现中,我使用指数函数来实现这一阻尼效果,具体计算如下:

distanceY = ev.getY() - pullStartY;
distanceY = (float) (Math.exp(-ev.getY() / pullStartY / 40) * distanceY); 

我们知道负指数是加速度随距离变小的单调递增函数,我使用手指滑动距离计算负指数作为PullToRefreshListView的滑动距离的参考标准,便可以实现有阻尼下拉效果。

4. 监控手势判断ListView是否进入下拉状态并更新distanceY

更进一步,我们要实现的就是对手势的监控,在PullToRefreshListView中,我们在onTouchEvent方法中进行处理。

@Override
public boolean onTouchEvent(MotionEvent ev) {
  if (lastAction == -1 && ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
    // 按下的时候
    lastAction = MotionEvent.ACTION_DOWN;
    cancelAnimating();
    L.d(TAG, "touch down");
  } else if (lastAction == MotionEvent.ACTION_MOVE && ev.getActionMasked() == MotionEvent.ACTION_UP) {
    // 放开手指,开始回滚
    isPulling = false;
    lastAction = -1;
    startAnimating();
    L.d(TAG, "touch up");
  } else if (lastAction == MotionEvent.ACTION_DOWN) {
    if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
      // 在按下手指的基础上,开始滑动
      if (isTop && !isPulling) {
        // listView在顶部而且不处于下拉刷新状态,开始下拉
        pullStartY = ev.getY();
        lastAction = MotionEvent.ACTION_MOVE;
        isPulling = true;
      }
    }
  } else if (lastAction == MotionEvent.ACTION_MOVE) {
    if (isTop) {
      // 下拉
      distanceY = ev.getY() - pullStartY;
      L.d(TAG, distanceY + "");
      if (distanceY > 0) {
        distanceY = (float) (Math.exp(-ev.getY() / pullStartY / 40) * distanceY);
        // 在下拉状态时取消系统对move动作的响应,完全由本类响应
        ev.setAction(MotionEvent.ACTION_DOWN);
      } else {
        distanceY = 0;
        // 在下拉过程中往上拉动该listView使得其回到顶部位置,则将该move动作交由系统进行响应
        ev.setAction(MotionEvent.ACTION_MOVE);
      }
    } else {
      // 在下拉过程中往上拉动listView使listView往下滚动到其没有滚动到顶部,则取消其下拉状态,回到手指按下的初始状态
      lastAction = MotionEvent.ACTION_DOWN;
      isPulling = false;
      distanceY = 0;
    }
  }
  return super.onTouchEvent(ev);
} 

这一段代码相对有一点复杂,我们慢慢解析。首先,我们有一个lastAction变量来记录上一个手势是什么,有一个isPulling变量来记录当前PullToRefreshListView是否处于下拉状态,有一个isTop变量记录当前PullToRefreshListView是否已经滚动到顶部。

在onTouchEvent方法的重载实现中,一开始PullToRefreshListView没有接受任何手势,然后当用户按下手指出发ACTION_DOWN事件时,我记录下这个动作,然后当用户进行滑动时,如果此时PullToRefreshListView没有“滚动到顶部”,则不做任何处理,反之则将lastAction更新为ACTION_MOVE状态,更新isPulling变量,记录当前手指的位置作为计算下拉距离的起始位置,开始下拉刷新,然后在下拉的过程中计算PullToRefreshListView下拉的距离以重画子View。

在这个手势处理的实现中,当用户在下拉过程中突然将PullToRefreshListView往上拉,如果将PullToRefreshListView 拉到不处于“滚动到顶部的状态”时,则重置下拉状态,使得:

lastAction = MotionEvent.ACTION_DOWN; 

于是PullToRefreshListView接下来的下滑手势响应权被交还给系统,知道用户又将PullToRefreshListView下拉到“滚动到顶部”状态,则又重新执行上述操作,使PullToRefreshListView进入下拉状态。

5. 如何判断ListView是否已经滚动到顶部

下一步,我们如何判断ListView是否处于“滚动到顶部”状态呢?这一问题我PullToRefreshListView的onScroll中解决。

    setOnScrollListener(new OnScrollListener() {
      @Override
      public void onScrollStateChanged(AbsListView view, int scrollState) {
      }
      @Override
      public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        // 没有子view的时候(没有数据,或者被拉到看不到子view),意味着该listView滚动到顶部
        if (getChildCount() == 0) {
          isTop = true;
          return;
        }
        if (firstVisibleItem == 0) {
          View firstView = getChildAt(0);
          if (firstView.getTop() + distanceY >= 0) {
            // 第一个view可见且其相对parent(该listView)的顶部距离大于等于0,意味着该listView也是滚动到顶部
            isTop = true;
            return;
          }
        }
        isTop = false;
      }
    });

为PullToRefreshListView设置一个OnScrollListener回调,并在其onScroll方法中监控其滚动位置,具体看注释也已经一目了然,我就不多解释了。

6. 下拉后的回滚动画

最后,当下拉结束松开手指时,我们需要为PullToRefreshListView执行一个回滚的动画,我们在onTouchEvent方法中看到:

    // ......
    else if (lastAction == MotionEvent.ACTION_MOVE && ev.getActionMasked() == MotionEvent.ACTION_UP) {
      // 放开手指,开始回滚
      isPulling = false;
      lastAction = -1;
      startAnimating();
      L.d(TAG, "touch up");
    }
    // ......

startAnimating方法的实现如下:

  /**
   * 下拉结束时进行回滚动画并执行刷新动作
   */
  private void startAnimating() {
    int whereToLoad = dp2px(onLoadCallBack.whereToLoad());
    final boolean toLoad;
    if (distanceY <= whereToLoad) {
      pullCancelAnimator = ValueAnimator.ofFloat(distanceY, 0);
      toLoad = false;
    } else {
      pullCancelAnimator = ValueAnimator.ofFloat(distanceY, whereToLoad);
      toLoad = true;
    }
    pullCancelAnimator.setDuration((long) (DEFAULT_BASE_ANIMATING_TIME_PER_100DP*px2dp(distanceY)/100));
    pullCancelAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
    pullCancelAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        distanceY = (float) animation.getAnimatedValue();
        ViewCompat.postInvalidateOnAnimation(PullToRefreshListView.this);
      }
    });
    pullCancelAnimator.addListener(new Animator.AnimatorListener() {
      @Override
      public void onAnimationStart(Animator animation) {
      }
      @Override
      public void onAnimationEnd(Animator animation) {
        post(new Runnable() {
          @Override
          public void run() {
            pullCancelAnimator = null;
            if (toLoad) {
              onLoadCallBack.onLoad();
            }
          }
        });
      }
      @Override
      public void onAnimationCancel(Animator animation) {
        post(new Runnable() {
          @Override
          public void run() {
            pullCancelAnimator = null;
            if (toLoad) {
              onLoadCallBack.cancelLoad();
            }
          }
        });
      }
      @Override
      public void onAnimationRepeat(Animator animation) {
      }
    });
    pullCancelAnimator.start();
  }

我使用ValueAnimator来实现这一回滚动画,其中为ValueAnimator设置的回调中,在动画更新和动画结束以及动画取消中分别调用了OnLoadCallBack的3歌回调方法,从而实现PullToRefreshListView的下拉刷新动作。我们可以看到,onLoad方法是在UI线程执行的,因此如果在onLoad方法中执行耗时操作的话,需要在后台线程中操作,这与我们前面的解析是对应的。

7. 改进和问题

(1) 我们可以将onLoad回调修改成一个返回一个异步任务对象的方法,然后PullToRefreshListView在下拉结束后执行这个异步任务,因此我们就可以不需要cancelLoading回调了,直接就可以在PullToRefreshListView内部进行取消操作,这样做可以增强封装性,但相对目前的做法自由度就没有那么高了。

(2) 回滚动画应该也可以进行优化,具体怎么优化我也不清楚。。。各位朋友有好的想法可以在评论区提议一下,谢谢~

(3) 下拉的时候对多点触碰的响应并不完美,虽然也可以接受,但是做不到像qq客户端的聊天列表那样。

8. 源码

至此,我已经解析了如何实现一个下拉刷新列表,PullToRefreshListView的源码如下。

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.ListView;
import com.ivan.healthcare.healthcare_android.log.L;
/**
 * 支持下拉刷新的的listView
 * Created by Ivan on 16/2/14.
 */
public class PullToRefreshListView extends ListView {
  private final String TAG = "PullToRefreshListView";
  private final int DEFAULT_BASE_ANIMATING_TIME_PER_100DP = 150;
  public static final int DEFAULT_WHERE_TO_LOAD = 80;
  private int lastAction = -1;
  private float pullStartY = -1;
  private boolean isTop = true;
  private float distanceY = 0;
  private boolean isPulling = false;
  private ValueAnimator pullCancelAnimator;
  private Context context;
  private Drawable refreshDrawable;
  private OnLoadCallBack onLoadCallBack = new OnLoadCallBack() {
    @Override
    public int whereToLoad() {
      return DEFAULT_WHERE_TO_LOAD;
    }
    @Override
    public void onLoad() {
    }
    @Override
    public void cancelLoad() {
    }
    @Override
    public Drawable refreshDrawable() {
      return null;
    }
  };
  public PullToRefreshListView(Context context) {
    super(context);
    initView(context);
  }
  public PullToRefreshListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView(context);
  }
  private void initView(Context context) {
    this.context = context;
    setOnScrollListener(new OnScrollListener() {
      @Override
      public void onScrollStateChanged(AbsListView view, int scrollState) {
      }
      @Override
      public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        // 没有子view的时候(没有数据,或者被拉到看不到子view),意味着该listView滚动到顶部
        if (getChildCount() == 0) {
          isTop = true;
          return;
        }
        if (firstVisibleItem == 0) {
          View firstView = getChildAt(0);
          if (firstView.getTop() + distanceY >= 0) {
            // 第一个view可见且其相对parent(该listView)的顶部距离大于等于0,意味着该listView也是滚动到顶部
            isTop = true;
            return;
          }
        }
        isTop = false;
      }
    });
  }
  @Override
  public boolean onTouchEvent(MotionEvent ev) {
    if (lastAction == -1 && ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
      // 按下的时候
      lastAction = MotionEvent.ACTION_DOWN;
      cancelAnimating();
      L.d(TAG, "touch down");
    } else if (lastAction == MotionEvent.ACTION_MOVE && ev.getActionMasked() == MotionEvent.ACTION_UP) {
      // 放开手指,开始回滚
      isPulling = false;
      lastAction = -1;
      startAnimating();
      L.d(TAG, "touch up");
    } else if (lastAction == MotionEvent.ACTION_DOWN) {
      if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
        // 在按下手指的基础上,开始滑动
        if (isTop && !isPulling) {
          // listView在顶部而且不处于下拉刷新状态,开始下拉
          pullStartY = ev.getY();
          lastAction = MotionEvent.ACTION_MOVE;
          isPulling = true;
        }
      }
    } else if (lastAction == MotionEvent.ACTION_MOVE) {
      if (isTop) {
        // 下拉
        distanceY = ev.getY() - pullStartY;
        L.d(TAG, distanceY + "");
        if (distanceY > 0) {
          distanceY = (float) (Math.exp(-ev.getY() / pullStartY / 40) * distanceY);
          // 在下拉状态时取消系统对move动作的响应,完全由本类响应
          ev.setAction(MotionEvent.ACTION_DOWN);
        } else {
          distanceY = 0;
          // 在下拉过程中往上拉动该listView使得其回到顶部位置,则将该move动作交由系统进行响应
          ev.setAction(MotionEvent.ACTION_MOVE);
        }
      } else {
        // 在下拉过程中往上拉动listView使listView往下滚动到其没有滚动到顶部,则取消其下拉状态,回到手指按下的初始状态
        lastAction = MotionEvent.ACTION_DOWN;
        isPulling = false;
        distanceY = 0;
      }
    }
    return super.onTouchEvent(ev);
  }
  @Override
  protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    if (distanceY > 0) {
      if (refreshDrawable == null) {
        refreshDrawable = onLoadCallBack.refreshDrawable();
      }
      if (refreshDrawable == null) {
        canvas.drawColor(Color.GRAY);
      } else {
        int left = getPaddingLeft();
        int top = getPaddingTop();
        refreshDrawable.setBounds(left, top, getWidth()+left, getHeight()+top);
        refreshDrawable.draw(canvas);
      }
      canvas.save();
      canvas.translate(getPaddingLeft(), getPaddingTop() + distanceY);
      for (int i=0;i<getChildCount();i++) {
        View child = getChildAt(i);
        drawChild(canvas, child, getDrawingTime());
      }
      canvas.restore();
    }
  }
  /**
   * 下拉结束时进行回滚动画并执行刷新动作
   */
  private void startAnimating() {
    int whereToLoad = dp2px(onLoadCallBack.whereToLoad());
    final boolean toLoad;
    if (distanceY <= whereToLoad) {
      pullCancelAnimator = ValueAnimator.ofFloat(distanceY, 0);
      toLoad = false;
    } else {
      pullCancelAnimator = ValueAnimator.ofFloat(distanceY, whereToLoad);
      toLoad = true;
    }
    pullCancelAnimator.setDuration((long) (DEFAULT_BASE_ANIMATING_TIME_PER_100DP*px2dp(distanceY)/100));
    pullCancelAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
    pullCancelAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        distanceY = (float) animation.getAnimatedValue();
        ViewCompat.postInvalidateOnAnimation(PullToRefreshListView.this);
      }
    });
    pullCancelAnimator.addListener(new Animator.AnimatorListener() {
      @Override
      public void onAnimationStart(Animator animation) {
      }
      @Override
      public void onAnimationEnd(Animator animation) {
        post(new Runnable() {
          @Override
          public void run() {
            pullCancelAnimator = null;
            if (toLoad) {
              onLoadCallBack.onLoad();
            }
          }
        });
      }
      @Override
      public void onAnimationCancel(Animator animation) {
        post(new Runnable() {
          @Override
          public void run() {
            pullCancelAnimator = null;
            if (toLoad) {
              onLoadCallBack.cancelLoad();
            }
          }
        });
      }
      @Override
      public void onAnimationRepeat(Animator animation) {
      }
    });
    pullCancelAnimator.start();
  }
  private void cancelAnimating() {
    if (pullCancelAnimator != null) {
      pullCancelAnimator.cancel();
    }
  }
  private float px2dp(float pxvalue) {
    return (pxvalue - 0.5f) /context.getResources().getDisplayMetrics().density;
  }
  private int dp2px(float dpvalue) {
    return (int) (dpvalue * context.getResources().getDisplayMetrics().density + 0.5f);
  }
  /**
   * 下拉刷新的回调
   */
  public interface OnLoadCallBack {
    /**
     * 下拉结束后将listView定位到哪个位置等待刷新完成
     * @return listView的定位y坐标值,in dp
     */
    int whereToLoad();
    /**
     * 下拉结束后进行刷新的回调
     */
    void onLoad();
    /**
     * 取消刷新
     */
    void cancelLoad();
    /**
     * 下拉刷新的背景
     * @return 背景drawable
     */
    Drawable refreshDrawable();
  }
  /**
   * 设置下拉刷新回调
   * @param cb 回调
   */
  public void setOnLoadCallBack(OnLoadCallBack cb) {
    this.onLoadCallBack = cb;
  }
  /**
   * 刷新动作结束后调用该方法结束刷新,使得listView回滚到顶部
   */
  public void setLoadingFinish() {
    startAnimating();
  }
}

以上这篇android 有阻尼下拉刷新列表的实现方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

您可能感兴趣的文章:

  • Android实现简单的下拉阻尼效应示例代码
  • Android开发中下拉刷新如何实现
  • Android下拉刷新框架实现代码实例
(0)

相关推荐

  • Android开发中下拉刷新如何实现

    因为最近的开发涉及到了网络读取数据,那么自然少不了的就是下拉刷新的功能,搜索的方法一般是自己去自定义ListView或者RecyclerView来重写OnTouch或者OnScroll方法来实现手势的监听然后播放动画最后刷新界面 今天说的是一个Google官方提供的下拉刷新布局,名字叫做SwipeRefreshLayout,找到这个布局的时候真的是喜出望外啊,下面来记录一下它怎么用. 这里放一下效果图先,就是下面这个小圈圈啦 首先是需要把这个布局套在我们需要刷新的控件之外,这里是Recycler

  • Android实现简单的下拉阻尼效应示例代码

    OS的下拉上拉都会出现一个很玄的动态效果.在Android中,虽然可以实现类似的效果,但有点不同的是,如果调用overScrollBy来实现类似的阻尼效应的话,最顶部会出现一片亮的区域,让人感觉不是很爽.所以决定不采用该方法来实现而是改用自定义的方式来实现. 下面是自定义控件的代码部分: public class MyView extends ScrollView { //记录下最开始点击的位置 int initY; //移动的位置 int deltaY; int touchY; //记录第一个

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

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

  • android 有阻尼下拉刷新列表的实现方法

    本文将会介绍有阻尼下拉刷新列表的实现,先来看看效果预览: 这是下拉状态: 这是下拉松开手指后listView回滚到刷新状态时的样子: 1. 如何调用 虽然效果图看起来样子不太好看,主要是因为那个蓝色的背景对不对,没关系,这只是一个背景而已,在了解了我们这个下拉刷新列表的实现之后,你就可以很轻松地修改这个背景,从而实现你想要的UI效果!话不多说,下面我们先来讲讲这个下拉刷新列表是如何使用的,这也是我们编写代码所要实现的目标. final PullToRefreshListView eListVie

  • Android UI设计系列之自定义ListView仿QQ空间阻尼下拉刷新和渐变菜单栏效果(8)

    好久没有写有关UI的博客了,刚刚翻了一下之前的博客,最近一篇有关UI的博客:Android UI设计系列之自定义Dialog实现各种风格的对话框效果(7) ,实现各种风格效果的对话框,在那篇博客写完后由于公司封闭开发封网以及其它原因致使博客中断至今,中断这么久很是惭愧,后续我会尽量把该写的都补充出来.近来项目有个需求,要做个和QQ空间类似的菜单栏透明度渐变和下拉刷新带有阻尼回弹的效果.于是花点时间动手试了试,基本上达到了QQ空间的效果,截图如下: 通过观察QQ空间的运行效果,发现当往上滚动时菜单

  • Android中ListView下拉刷新的实现方法实例分析

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

  • 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 实现的下拉刷新效果

    下面是自己实现的效果: 1.分析 可以将动画分解成: 睁眼毛驴绕着中心地球旋转,并且在到达地球中心时,切换为闭眼毛驴,最后发射出去 地球自我旋转,随着下拉而缓缓上升,达到半径距离后停止上升 一颗上下来回移动的卫星 2.实现 (1)下载赶集app,然后将其后缀名改为zip解压获取我们需要的资源图片: (2) 我们先实现卫星的上下移动 核心代码: @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Matrix

  • Android RecyclerView设置下拉刷新的实现方法

    Android RecyclerView设置下拉刷新的实现方法 1 集成 SwipeRefreshLayout 1.1 xml布局文件中使用 <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/refresh" android:layout_width = "match_parent" android:layout_height = "match_parent" &g

  • Android实现RecyclerView下拉刷新效果

    本文为大家分享了Android实现RecyclerView下拉刷新效果的具体代码,供大家参考,具体内容如下 思路 RealPullRefreshView继承了一个LinearLayout 里面放置了一个刷新头布局,将其margin_top设置为负的刷新头的高度的 再添加一个RecyclerView 触摸事件分发机制,当在特定条件下让RealPullRefreshView拦截触摸事件,否则的话,不拦截,让RecyclerView自己去处理触摸事件 在手指下拉时,定义好不同的状态STATE,在不同状

  • Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法

    RecyclerView 已经出来很久了,但是在项目中之前都使用的是ListView,最近新的项目上了都大量的使用了RecycleView.尤其是瀑布流的下拉刷新,网上吧啦吧啦没有合适的自己总结了一哈. 先贴图上来看看: 使用RecyclerView实现上拉加载更多和下拉刷新的功能我自己有两种方式: 1.使用系统自带的Android.support.v4.widget.SwipeRefreshLayout这个控价来实现. 2.自定义的里面带有RecyleView的控件. 使用RecycleVie

  • Android中Listview下拉刷新和上拉加载更多的多种实现方案

    listview经常结合下来刷新和上拉加载更多使用,本文总结了三种常用到的方案分别作出说明. 方案一:添加头布局和脚布局 android系统为listview提供了addfootview和addheadview两个API.这样可以直接自定义一个View,以添加视图的形式实现下来刷新和上拉加载. 实现步骤    1.创建一个类继承ListView:class PullToRefreshListView extends ListView: 2.在构造方法中添加HeadView:addHeaderVi

  • Android Scroller及下拉刷新组件原理解析

    Android事件拦截机制 Android中事件的传递和拦截和View树结构是相关联的,在View树中,分为叶子节点和普通节点,普通节点有子节点只能是ViewGroup,叶子节点可以是View或者ViewGroup.Android和事件分发拦截相关的方法有 dispatchTouchEvent(MotionEvent ev) 事件分发相关的方法,沿着View树将一个用户的触摸事件向下分发. onInterceptTouchEvent(MotionEvent ev) 在dispatchTouchE

随机推荐