package cn.coderss.pulltorefresh;
import cn.coderss.pulltorefresh.ILoadingLayout.State;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
/**
* 这个实现了下拉刷新和上拉加载更多的功能
*
* @author Li Hong
* @since 2013-7-29
* @param <T>
*/
@SuppressLint("NewApi") public abstract class PullToRefreshBase<T extends View> extends LinearLayout implements IPullToRefresh<T> {
/**
* 定义了下拉刷新和上拉加载更多的接口。
*
* @author Li Hong
* @since 2013-7-29
*/
public interface OnRefreshListener<V extends View> {
/**
* 下拉松手后会被调用
*
* @param refreshView 刷新的View
*/
void onPullDownToRefresh(final PullToRefreshBase<V> refreshView);
/**
* 加载更多时会被调用或上拉时调用
*
* @param refreshView 刷新的View
*/
void onPullUpToRefresh(final PullToRefreshBase<V> refreshView);
}
/**回滚的时间*/
private static final int SCROLL_DURATION = 150;
/**阻尼系数*/
private static final float OFFSET_RADIO = 2.5f;
/**上一次移动的点 */
private float mLastMotionY = -1;
/**下拉刷新和加载更多的监听器 */
private OnRefreshListener<T> mRefreshListener;
/**下拉刷新的布局 */
private LoadingLayout mHeaderLayout;
/**上拉加载更多的布局*/
private LoadingLayout mFooterLayout;
/**HeaderView的高度*/
private int mHeaderHeight;
/**FooterView的高度*/
private int mFooterHeight;
/**下拉刷新是否可用*/
private boolean mPullRefreshEnabled = true;
/**上拉加载是否可用*/
private boolean mPullLoadEnabled = false;
/**判断滑动到底部加载是否可用*/
private boolean mScrollLoadEnabled = false;
/**是否截断touch事件*/
private boolean mInterceptEventEnable = true;
/**表示是否消费了touch事件,如果是,则不调用父类的onTouchEvent方法*/
private boolean mIsHandledTouchEvent = false;
/**移动点的保护范围值*/
private int mTouchSlop;
/**下拉的状态*/
private State mPullDownState = State.NONE;
/**上拉的状态*/
private State mPullUpState = State.NONE;
/**可以下拉刷新的View*/
T mRefreshableView;
/**平滑滚动的Runnable*/
private SmoothScrollRunnable mSmoothScrollRunnable;
/**可刷新View的包装布局*/
private FrameLayout mRefreshableViewWrapper;
/**
* 构造方法
*
* @param context context
*/
public PullToRefreshBase(Context context) {
super(context);
init(context, null);
}
/**
* 构造方法
*
* @param context context
* @param attrs attrs
*/
public PullToRefreshBase(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
/**
* 构造方法
*
* @param context context
* @param attrs attrs
* @param defStyle defStyle
*/
public PullToRefreshBase(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
/**
* 初始化
*
* @param context context
*/
private void init(Context context, AttributeSet attrs) {
setOrientation(LinearLayout.VERTICAL);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mHeaderLayout = createHeaderLoadingLayout(context, attrs);
mFooterLayout = createFooterLoadingLayout(context, attrs);
mRefreshableView = createRefreshableView(context, attrs);
if (null == mRefreshableView) {
throw new NullPointerException("Refreshable view can not be null.");
}
addRefreshableView(context, mRefreshableView);
addHeaderAndFooter(context);
// 得到Header的高度,这个高度需要用这种方式得到,在onLayout方法里面得到的高度始终是0
getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
refreshLoadingViewsSize();
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
}
/**
* 初始化padding,我们根据header和footer的高度来设置top padding和bottom padding
*/
private void refreshLoadingViewsSize() {
// 得到header和footer的内容高度,它将会作为拖动刷新的一个临界值,如果拖动距离大于这个高度
// 然后再松开手,就会触发刷新操作
int headerHeight = (null != mHeaderLayout) ? mHeaderLayout.getContentSize() : 0;
int footerHeight = (null != mFooterLayout) ? mFooterLayout.getContentSize() : 0;
if (headerHeight < 0) {
headerHeight = 0;
}
if (footerHeight < 0) {
footerHeight = 0;
}
mHeaderHeight = headerHeight;
mFooterHeight = footerHeight;
// 这里得到Header和Footer的高度,设置的padding的top和bottom就应该是header和footer的高度
// 因为header和footer是完全看不见的
headerHeight = (null != mHeaderLayout) ? mHeaderLayout.getMeasuredHeight() : 0;
footerHeight = (null != mFooterLayout) ? mFooterLayout.getMeasuredHeight() : 0;
if (0 == footerHeight) {
footerHeight = mFooterHeight;
}
int pLeft = getPaddingLeft();
int pTop = getPaddingTop();
int pRight = getPaddingRight();
int pBottom = getPaddingBottom();
pTop = -headerHeight;
pBottom = -footerHeight;
setPadding(pLeft, pTop, pRight, pBottom);
}
@Override
protected final void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// We need to update the header/footer when our size changes
refreshLoadingViewsSize();
// 设置刷新View的大小
refreshRefreshableViewSize(w, h);
/**
* As we're currently in a Layout Pass, we need to schedule another one
* to layout any changes we've made here
*/
post(new Runnable() {
@Override
public void run() {
requestLayout();
}
});
}
@Override
public void setOrientation(int orientation) {
if (LinearLayout.VERTICAL != orientation) {
throw new IllegalArgumentException("This class only supports VERTICAL orientation.");
}
// Only support vertical orientation
super.setOrientation(orientation);
}
@Override
public final boolean onInterceptTouchEvent(MotionEvent event) {
if (!isInterceptTouchEventEnabled()) {
return false;
}
if (!isPullLoadEnabled() && !isPullRefreshEnabled()) {
return false;
}
final int action = event.getAction();
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mIsHandledTouchEvent = false;
return false;
}
if (action != MotionEvent.ACTION_DOWN && mIsHandledTouchEvent) {
return true;
}
switch (action) {
case MotionEvent.ACTION_DOWN:
mLastMotionY = event.getY();
mIsHandledTouchEvent = false;
break;
case MotionEvent.ACTION_MOVE:
final float deltaY = event.getY() - mLastMotionY;
final float absDiff = Math.abs(deltaY);
// 这里有三个条件:
// 1,位移差大于mTouchSlop,这是为了防止快速拖动引发刷新
// 2,isPullRefreshing(),如果当前正在下拉刷新的话,是允许向上滑动,并把刷新的HeaderView挤上去
// 3,isPullLoading(),理由与第2条相同
if (absDiff > mTouchSlop || isPullRefreshing() || isPullLoading()) {
mLastMotionY = event.getY();
// 第一个显示出来,Header已经显示或拉下
if (isPullRefreshEnabled() && isReadyForPullDown()) {
// 1,Math.abs(getScrollY()) > 0:表示当前滑动的偏移量的绝对值大于0,表示当前HeaderView滑出来了或完全
// 不可见,存在这样一种case,当正在刷新时并且RefreshableView已经滑到顶部,向上滑动,那么我们期望的结果是
// 依然能向上滑动,直到HeaderView完全不可见
// 2,deltaY > 0.5f:表示下拉的值大于0.5f
mIsHandledTouchEvent = (Math.abs(getScrollYValue()) > 0 || deltaY > 0.5f);
// 如果截断事件,我们则仍然把这个事件交给刷新View去处理,典型的情况是让ListView/GridView将按下
// Child的Selector隐藏
if (mIsHandledTouchEvent) {
mRefreshableView.onTouchEvent(event);
}
} else if (isPullLoadEnabled() && isReadyForPullUp()) {
// 原理如上
mIsHandledTouchEvent = (Math.abs(getScrollYValue()) > 0 || deltaY < -0.5f);
}
}
break;
default:
break;
}
return mIsHandledTouchEvent;
}
@Override
public final boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastMotionY = ev.getY();
mIsHandledTouchEvent = false;
break;
case MotionEvent.ACTION_MOVE:
final float deltaY = ev.getY() - mLastMotionY;
mLastMotionY = ev.getY();
if (isPullRefreshEnabled() && isReadyForPullDown()) {
pullHeaderLayout(deltaY / OFFSET_RADIO);
handled = true;
} else if (isPullLoadEnabled() && isReadyForPullUp()) {
pullFooterLayout(deltaY / OFFSET_RADIO);
handled = true;
} else {
mIsHandledTouchEvent = false;
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (mIsHandledTouchEvent) {
mIsHandledTouchEvent = false;
// 当第一个显示出来时
if (isReadyForPullDown()) {
// 调用刷新
if (mPullRefreshEnabled && (mPullDownState == State.RELEASE_TO_REFRESH)) {
startRefreshing();
handled = true;
}
resetHeaderLayout();
} else if (isReadyForPullUp()) {
// 加载更多
if (isPullLoadEnabled() && (mPullUpState == State.RELEASE_TO_REFRESH)) {
startLoading();
handled = true;
}
resetFooterLayout();
}
}
break;
default:
break;
}
return handled;
}
@Override
public void setPullRefreshEnabled(boolean pullRefreshEnabled) {
mPullRefreshEnabled = pullRefreshEnabled;
}
@Override
public void setPullLoadEnabled(boolean pullLoadEnabled) {
mPullLoadEnabled = pullLoadEnabled;
}
@Override
public void setScrollLoadEnabled(boolean scrollLoadEnabled) {
mScrollLoadEnabled = scrollLoadEnabled;
}
@Override
public boolean isPullRefreshEnabled() {
return mPullRefreshEnabled && (null != mHeaderLayout);
}
@Override
public boolean isPullLoadEnabled() {
return mPullLoadEnabled && (null != mFooterLayout);
}
@Override
public boolean isScrollLoadEnabled() {
return mScrollLoadEnabled;
}
@Override
public void setOnRefreshListener(OnRefreshListener<T> refreshListener) {
mRefreshListener = refreshListener;
}
@Override
public void onPullDownRefreshComplete() {
if (isPullRefreshing()) {
mPullDownState = State.RESET;
onStateChanged(State.RESET, true);
// 回滚动有一个时间,我们在回滚完成后再设置状态为normal
// 在将LoadingLayout的状态设置为normal之前,我们应该禁止
// 截断Touch事件,因为设里有一个post状态,如果有post的Runnable
// 未被执行时,用户再一次发起下拉刷新,如果正在刷新时,这个Runnable
// 再次被执行到,那么就会把正在刷新的状态改为正常状态,这就不符合期望
postDelayed(new Runnable() {
@Override
public void run() {
setInterceptTouchEventEnabled(true);
mHeaderLayout.setState(State.RESET);
}
}, getSmoothScrollDuration());
resetHeaderLayout();
setInterceptTouchEventEnabled(false);
}
}
@Override
public void onPullUpRefreshComplete() {
if (isPullLoading()) {
mPullUpState = State.RESET;
onStateChanged(State.RESET, false);
postDelayed(new Runnable() {
@Override
public void run() {
setInterceptTouchEventEnabled(true);
mFooterLayout.setState(State.RESET);
}
}, getSmoothScrollDuration());
resetFooterLayout();
setInterceptTouchEventEnabled(false);
}
}
@Override
public T getRefreshableView() {
return mRefreshableView;
}
@Override
public LoadingLayout getHeaderLoadingLayout() {
return mHeaderLayout;
}
@Override
public LoadingLayout getFooterLoadingLayout() {
return mFooterLayout;
}
@Override
public void setLastUpdatedLabel(CharSequence label){
if (null != mHeaderLayout) {
mHeaderLayout.setLastUpdatedLabel(label);
}
if (null != mFooterLayout) {
mFooterLayout.setLastUpdatedLabel(label);
}
}
/**
* 开始刷新,通常用于调用者主动刷新,典型的情况是进入界面,开始主动刷新,这个刷新并不是由用户拉动引起的
*
* @param smoothScroll 表示是否有平滑滚动,true表示平滑滚动,false表示无平滑滚动
* @param delayMillis 延迟时间
*/
public void doPullRefreshing(final boolean smoothScroll, final long delayMillis) {
postDelayed(new Runnable() {
@Override
public void run() {
int newScrollValue = -mHeaderHeight;
int duration = smoothScroll ? SCROLL_DURATION : 0;
startRefreshing();
smoothScrollTo(newScrollValue, duration, 0);
}
}, delayMillis);
}
/**
* 创建可以刷新的View
*
* @param context context
* @param attrs 属性
* @return View
*/
protected abstract T createRefreshableView(Context context, AttributeSet attrs);
/**
* 判断刷新的View是否滑动到顶部
*
* @return true表示已经滑动到顶部,否则false
*/
protected abstract boolean isReadyForPullDown();
/**
* 判断刷新的View是否滑动到底
*
* @return true表示已经滑动到底部,否则false
*/
protected abstract boolean isReadyForPullUp();
/**
* 创建Header的布局
*
* @param context context
* @param attrs 属性
* @return LoadingLayout对象
*/
protected LoadingLayout createHeaderLoadingLayout(Context context, AttributeSet attrs) {
return new HeaderLoadingLayout(context);
}
/**
* 创建Footer的布局
*
* @param context context
* @param attrs 属性
* @return LoadingLayout对象
*/
protected LoadingLayout createFooterLoadingLayout(Context context, AttributeSet attrs) {
return new FooterLoadingLayout(context);
}
/**
* 得到平滑滚动的时间,派生类可以重写这个方法来控件滚动时间
*
* @return 返回值时间为毫秒
*/
protected long getSmoothScrollDuration() {
return SCROLL_DURATION;
}
/**
* 计算刷新View的大小
*
* @param width 当前容器的宽度
* @param height 当前容器的宽度
*/
protected void refreshRefreshableViewSize(int width, int height) {
if (null != mRefreshableViewWrapper) {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mRefreshableViewWrapper.getLayoutParams();
if (lp.height != height) {
lp.height = height;
mRefreshableViewWrapper.requestLayout();
}
}
}
/**
* 将刷新View添加到当前容器中
*
* @param context context
* @param refreshableView 可以刷新的View
*/
protected void addRefreshableView(Context context, T refreshableView) {
int width = ViewGroup.LayoutParams.MATCH_PARENT;
int height = ViewGroup.LayoutParams.MATCH_PARENT;
// 创建一个包装容器
mRefreshableViewWrapper = new FrameLayout(context);
mRefreshableViewWrapper.addView(refreshableView, width, height);
// 这里把Refresh view的高度设置为一个很小的值,它的高度最终会在onSizeChanged()方法中设置为MATCH_PARENT
// 这样做的原因是,如果此是它的height是MATCH_PARENT,那么footer得到的高度就是0,所以,我们先设置高度很小
// 我们就可以得到header和footer的正常高度,当onSizeChanged后,Refresh view的高度又会变为正常。
height = 10;
addView(mRefreshableViewWrapper, new LinearLayout.LayoutParams(width, height));
}
/**
* 添加Header和Footer
*
* @param context context
*/
protected void addHeaderAndFooter(Context context) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
final LoadingLayout headerLayout = mHeaderLayout;
final LoadingLayout footerLayout = mFooterLayout;
if (null != headerLayout) {
if (this == headerLayout.getParent()) {
removeView(headerLayout);
}
addView(headerLayout, 0, params);
}
if (null != footerLayout) {
if (this == footerLayout.getParent()) {
removeView(footerLayout);
}
addView(footerLayout, -1, params);
}
}
/**
* 拉动Header Layout时调用
*
* @param delta 移动的距离
*/
protected void pullHeaderLayout(float delta) {
// 向上滑动,并且当前scrollY为0时,不滑动
int oldScrollY = getScrollYValue();
if (delta < 0 && (oldScrollY - delta) >= 0) {
setScrollTo(0, 0);
return;
}
// 向下滑动布局
setScrollBy(0, -(int)delta);
if (null != mHeaderLayout && 0 != mHeaderHeight) {
float scale = Math.abs(getScrollYValue()) / (float) mHeaderHeight;
mHeaderLayout.onPull(scale);
}
// 未处于刷新状态,更新箭头
int scrollY = Math.abs(getScrollYValue());
if (isPullRefreshEnabled() && !isPullRefreshing()) {
if (scrollY > mHeaderHeight) {
mPullDownState = State.RELEASE_TO_REFRESH;
} else {
mPullDownState = State.PULL_TO_REFRESH;
}
mHeaderLayout.setState(mPullDownState);
onStateChanged(mPullDownState, true);
}
}
/**
* 拉Footer时调用
*
* @param delta 移动的距离
*/
protected void pullFooterLayout(float delta) {
int oldScrollY = getScrollYValue();
if (delta > 0 && (oldScrollY - delta) <= 0) {
setScrollTo(0, 0);
return;
}
setScrollBy(0, -(int)delta);
if (null != mFooterLayout && 0 != mFooterHeight) {
float scale = Math.abs(getScrollYValue()) / (float) mFooterHeight;
mFooterLayout.onPull(scale);
}
int scrollY = Math.abs(getScrollYValue());
if (isPullLoadEnabled() && !isPullLoading()) {
if (scrollY > mFooterHeight) {
mPullUpState = State.RELEASE_TO_REFRESH;
} else {
mPullUpState = State.PULL_TO_REFRESH;
}
mFooterLayout.setState(mPullUpState);
onStateChanged(mPullUpState, false);
}
}
/**
* 得置header
*/
protected void resetHeaderLayout() {
final int scrollY = Math.abs(getScrollYValue());
final boolean refreshing = isPullRefreshing();
if (refreshing && scrollY <= mHeaderHeight) {
smoothScrollTo(0);
return;
}
if (refreshing) {
smoothScrollTo(-mHeaderHeight);
} else {
smoothScrollTo(0);
}
}
/**
* 重置footer
*/
protected void resetFooterLayout() {
int scrollY = Math.abs(getScrollYValue());
boolean isPullLoading = isPullLoading();
if (isPullLoading && scrollY <= mFooterHeight) {
smoothScrollTo(0);
return;
}
if (isPullLoading) {
smoothScrollTo(mFooterHeight);
} else {
smoothScrollTo(0);
}
}
/**
* 判断是否正在下拉刷新
*
* @return true正在刷新,否则false
*/
protected boolean isPullRefreshing() {
return (mPullDownState == State.REFRESHING);
}
/**
* 是否正的上拉加载更多
*
* @return true正在加载更多,否则false
*/
protected boolean isPullLoading() {
return (mPullUpState == State.REFRESHING);
}
/**
* 开始刷新,当下拉松开后被调用
*/
protected void startRefreshing() {
// 如果正在刷新
if (isPullRefreshing()) {
return;
}
mPullDownState = State.REFRESHING;
onStateChanged(State.REFRESHING, true);
if (null != mHeaderLayout) {
mHeaderLayout.setState(State.REFRESHING);
}
if (null != mRefreshListener) {
// 因为滚动回原始位置的时间是200,我们需要等回滚完后才执行刷新回调
postDelayed(new Runnable() {
@Override
public void run() {
mRefreshListener.onPullDownToRefresh(PullToRefreshBase.this);
}
}, getSmoothScrollDuration());
}
}
/**
* 开始加载更多,上拉松开后调用
*/
protected void startLoading() {
// 如果正在加载
if (isPullLoading()) {
return;
}
mPullUpState = State.REFRESHING;
onStateChanged(State.REFRESHING, false);
if (null != mFooterLayout) {
mFooterLayout.setState(State.REFRESHING);
}
if (null != mRefreshListener) {
// 因为滚动回原始位置的时间是200,我们需要等回滚完后才执行加载回调
postDelayed(new Runnable() {
@Override
public void run() {
mRefreshListener.onPullUpToRefresh(PullToRefreshBase.this);
}
}, getSmoothScrollDuration());
}
}
/**
* 当状态发生变化时调用
*
* @param state 状态
* @param isPullDown 是否向下
*/
protected void onStateChanged(State state, boolean isPullDown) {
}
/**
* 设置滚动位置
*
* @param x 滚动到的x位置
* @param y 滚动到的y位置
*/
private void setScrollTo(int x, int y) {
scrollTo(x, y);
}
/**
* 设置滚动的偏移
*
* @param x 滚动x位置
* @param y 滚动y位置
*/
private void setScrollBy(int x, int y) {
scrollBy(x, y);
}
/**
* 得到当前Y的滚动值
*
* @return 滚动值
*/
private int getScrollYValue() {
return getScrollY();
}
/**
* 平滑滚动
*
* @param newScrollValue 滚动的值
*/
private void smoothScrollTo(int newScrollValue) {
smoothScrollTo(newScrollValue, getSmoothScrollDuration(), 0);
}
/**
* 平滑滚动
*
* @param newScrollValue 滚动的值
* @param duration 滚动时候
* @param delayMillis 延迟时间,0代表不延迟
*/
private void smoothScrollTo(int newScrollValue, long duration, long delayMillis) {
if (null != mSmoothScrollRunnable) {
mSmoothScrollRunnable.stop();
}
int oldScrollValue = this.getScrollYValue();
boolean post = (oldScrollValue != newScrollValue);
if (post) {
mSmoothScrollRunnable = new SmoothScrollRunnable(oldScrollValue, newScrollValue, duration);
}
if (post) {
if (delayMillis > 0) {
postDelayed(mSmoothScrollRunnable, delayMillis);
} else {
post(mSmoothScrollRunnable);
}
}
}
/**
* 设置是否截断touch事件
*
* @param enabled true截断,false不截断
*/
private void setInterceptTouchEventEnabled(boolean enabled) {
mInterceptEventEnable = enabled;
}
/**
* 标志是否截断touch事件
*
* @return true截断,false不截断
*/
private boolean isInterceptTouchEventEnabled() {
return mInterceptEventEnable;
}
/**
* 实现了平滑滚动的Runnable
*
* @author Li Hong
* @since 2013-8-22
*/
final class SmoothScrollRunnable implements Runnable {
/**动画效果*/
private final Interpolator mInterpolator;
/**结束Y*/
private final int mScrollToY;
/**开始Y*/
private final int mScrollFromY;
/**滑动时间*/
private final long mDuration;
/**是否继续运行*/
private boolean mContinueRunning = true;
/**开始时刻*/
private long mStartTime = -1;
/**当前Y*/
private int mCurrentY = -1;
/**
* 构造方法
*
* @param fromY 开始Y
* @param toY 结束Y
* @param duration 动画时间
*/
public SmoothScrollRunnable(int fromY, int toY, long duration) {
mScrollFromY = fromY;
mScrollToY = toY;
mDuration = duration;
mInterpolator = new DecelerateInterpolator();
}
@Override
public void run() {
/**
* If the duration is 0, we scroll the view to target y directly.
*/
if (mDuration <= 0) {
setScrollTo(0, mScrollToY);
return;
}
/**
* Only set mStartTime if this is the first time we're starting,
* else actually calculate the Y delta
*/
if (mStartTime == -1) {
mStartTime = System.currentTimeMillis();
} else {
/**
* We do do all calculations in long to reduce software float
* calculations. We use 1000 as it gives us good accuracy and
* small rounding errors
*/
final long oneSecond = 1000; // SUPPRESS CHECKSTYLE
long normalizedTime = (oneSecond * (System.currentTimeMillis() - mStartTime)) / mDuration;
normalizedTime = Math.max(Math.min(normalizedTime, oneSecond), 0);
final int deltaY = Math.round((mScrollFromY - mScrollToY)
* mInterpolator.getInterpolation(normalizedTime / (float) oneSecond));
mCurrentY = mScrollFromY - deltaY;
setScrollTo(0, mCurrentY);
}
// If we're not at the target Y, keep going...
if (mContinueRunning && mScrollToY != mCurrentY) {
PullToRefreshBase.this.postDelayed(this, 16);// SUPPRESS CHECKSTYLE
}
}
/**
* 停止滑动
*/
public void stop() {
mContinueRunning = false;
removeCallbacks(this);
}
}
}