package in.srain.cube.views.ptr;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.*;
import android.widget.Scroller;
import android.widget.TextView;
import in.srain.cube.util.CLog;
import in.srain.cube.views.ptr.util.PtrLocalDisplay;
/**
* This layout view for "Pull to Refresh(Ptr)" support all of the view, you can contain everything you want.
* support: pull to refresh / release to refresh / auto refresh / keep header view while refreshing / hide header view while refreshing
* It defines {@link in.srain.cube.views.ptr.PtrUIHandler}, which allows you customize the UI easily.
*/
public class PtrFrameLayout extends ViewGroup {
// status enum
protected final static byte PTR_STATUS_INIT = 1;
protected final static byte PTR_STATUS_PREPARE = 2;
protected final static byte PTR_STATUS_LOADING = 3;
protected final static byte PTR_STATUS_COMPLETE = 4;
private final static int POS_START = 0;
public static boolean DEBUG = false;
private static int ID = 1;
// auto refresh status
private static byte STATUS_AUTO_SCROLL_AT_ONCE = 0x01;
private static byte STATUS_AUTO_SCROLL_LATER = 0x02;
protected final String LOG_TAG = "ptr-frame-" + ++ID;
protected View mContent;
protected int mOffsetToRefresh = 0;
// optional config for define header and content in xml file
private int mHeaderId = 0;
private int mContainerId = 0;
// config
private float mResistance = 1.7f;
private int mDurationToClose = 200;
private int mDurationToCloseHeader = 1000;
private float mRatioOfHeaderHeightToRefresh = 1.2f;
private boolean mKeepHeaderWhenRefresh = true;
private boolean mPullToRefresh = false;
private View mHeaderView;
private PtrUIHandlerHolder mPtrUIHandlerHolder = PtrUIHandlerHolder.create();
private PtrHandler mPtrHandler;
// working parameters
private ScrollChecker mScrollChecker;
private PointF mPtLastMove = new PointF();
private int mCurrentPos = 0;
private int mLastPos = 0;
private int mPagingTouchSlop;
private int mHeaderHeight;
private byte mStatus = PTR_STATUS_INIT;
private boolean mIsUnderTouch = false;
private boolean mDisableWhenHorizontalMove = false;
private int mAutoScrollRefresh = 0x00;
// disable when detect moving horizontally
private boolean mPreventForHorizontal = false;
// intercept child event while working
private boolean mInterceptEventWhileWorking = false;
private MotionEvent mDownEvent;
private MotionEvent mLastMoveEvent;
public PtrFrameLayout(Context context) {
this(context, null);
}
public PtrFrameLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PtrFrameLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
PtrLocalDisplay.init(getContext());
TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.PtrFrameLayout, 0, 0);
if (arr != null) {
mHeaderId = arr.getResourceId(R.styleable.PtrFrameLayout_ptr_header, mHeaderId);
mContainerId = arr.getResourceId(R.styleable.PtrFrameLayout_ptr_content, mContainerId);
mResistance = arr.getFloat(R.styleable.PtrFrameLayout_ptr_resistance, mResistance);
mDurationToClose = arr.getInt(R.styleable.PtrFrameLayout_ptr_duration_to_close, mDurationToClose);
mDurationToCloseHeader = arr.getInt(R.styleable.PtrFrameLayout_ptr_duration_to_close_header, mDurationToCloseHeader);
mRatioOfHeaderHeightToRefresh = arr.getFloat(R.styleable.PtrFrameLayout_ptr_ratio_of_header_height_to_refresh, mRatioOfHeaderHeightToRefresh);
mKeepHeaderWhenRefresh = arr.getBoolean(R.styleable.PtrFrameLayout_ptr_keep_header_when_refresh, mKeepHeaderWhenRefresh);
mPullToRefresh = arr.getBoolean(R.styleable.PtrFrameLayout_ptr_pull_to_fresh, mPullToRefresh);
arr.recycle();
}
mScrollChecker = new ScrollChecker();
final ViewConfiguration conf = ViewConfiguration.get(getContext());
mPagingTouchSlop = conf.getScaledTouchSlop() * 2;
}
@Override
protected void onFinishInflate() {
final int childCount = getChildCount();
if (childCount > 2) {
throw new IllegalStateException("PtrFrameLayout only can host 2 elements");
} else if (childCount == 2) {
if (mHeaderId != 0 && mHeaderView == null) {
mHeaderView = findViewById(mHeaderId);
}
if (mContainerId != 0 && mContent == null) {
mContent = findViewById(mContainerId);
}
// not specify header or content
if (mContent == null || mHeaderView == null) {
View child1 = getChildAt(0);
View child2 = getChildAt(1);
if (child1 instanceof PtrUIHandler) {
mHeaderView = child1;
mContent = child2;
} else if (child2 instanceof PtrUIHandler) {
mHeaderView = child2;
mContent = child1;
} else {
// both are not specified
if (mContent == null && mHeaderView == null) {
mHeaderView = child1;
mContent = child2;
}
// only one is specified
else {
if (mHeaderView == null) {
mHeaderView = mContent == child1 ? child2 : child1;
} else {
mContent = mHeaderView == child1 ? child2 : child1;
}
}
}
}
} else if (childCount == 1) {
mContent = getChildAt(0);
} else {
TextView errorView = new TextView(getContext());
errorView.setClickable(true);
errorView.setTextColor(0xffff6600);
errorView.setGravity(Gravity.CENTER);
errorView.setTextSize(20);
errorView.setText("The content view in PtrFrameLayout is empty. Do you forget to specify its id in xml layout file?");
mContent = errorView;
addView(mContent);
}
super.onFinishInflate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (DEBUG) {
CLog.d(LOG_TAG, "onMeasure frame: width: %s, height: %s, padding: %s %s %s %s",
getMeasuredHeight(), getMeasuredWidth(),
getPaddingLeft(), getPaddingRight(), getPaddingTop(), getPaddingBottom());
}
if (mHeaderView != null) {
measureChildWithMargins(mHeaderView, widthMeasureSpec, 0, heightMeasureSpec, 0);
MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams();
mHeaderHeight = mHeaderView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
mOffsetToRefresh = (int) (mHeaderHeight * mRatioOfHeaderHeightToRefresh);
if (DEBUG) {
CLog.d(LOG_TAG, "onMeasure header: height: %s, topMargin: %s, bottomMargin: %s, headerHeight: %s",
mHeaderView.getMeasuredHeight(), lp.leftMargin, lp.rightMargin, mHeaderHeight);
}
}
if (mContent != null) {
measureContentView(mContent, widthMeasureSpec, heightMeasureSpec);
if (DEBUG) {
ViewGroup.MarginLayoutParams lp = (MarginLayoutParams) mContent.getLayoutParams();
CLog.d(LOG_TAG, "onMeasure content, width: %s, height: %s, margin: %s %s %s %s",
getMeasuredWidth(), getMeasuredHeight(),
lp.leftMargin, lp.topMargin, lp.rightMargin, lp.bottomMargin);
CLog.d(LOG_TAG, "onMeasure, mCurrentPos: %s, mLastPos: %s, top: %s",
mCurrentPos, mLastPos, mContent.getTop());
}
}
}
private void measureContentView(View child,
int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
getPaddingTop() + getPaddingBottom() + lp.topMargin, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
@Override
protected void onLayout(boolean flag, int i, int j, int k, int l) {
layoutChildren();
}
private void layoutChildren() {
int offsetX = mCurrentPos;
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
if (mHeaderView != null) {
MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams();
final int left = paddingLeft + lp.leftMargin;
final int top = paddingTop + lp.topMargin + offsetX - mHeaderHeight;
final int right = left + mHeaderView.getMeasuredWidth();
final int bottom = top + mHeaderView.getMeasuredHeight();
mHeaderView.layout(left, top, right, bottom);
if (DEBUG) {
CLog.d(LOG_TAG, "onLayout header: %s %s %s %s", left, top, right, bottom);
}
}
if (mContent != null) {
MarginLayoutParams lp = (MarginLayoutParams) mContent.getLayoutParams();
final int left = paddingLeft + lp.leftMargin;
final int top = paddingTop + lp.topMargin + offsetX;
final int right = left + mContent.getMeasuredWidth();
final int bottom = top + mContent.getMeasuredHeight();
if (DEBUG) {
CLog.d(LOG_TAG, "onLayout content: %s %s %s %s", left, top, right, bottom);
}
mContent.layout(left, top, right, bottom);
}
}
public boolean dispatchTouchEventSupper(MotionEvent e) {
return super.dispatchTouchEvent(e);
}
@Override
public boolean dispatchTouchEvent(MotionEvent e) {
if (!isEnabled() || mContent == null || mHeaderView == null) {
return dispatchTouchEventSupper(e);
}
int action = e.getAction();
switch (action) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mIsUnderTouch = false;
if (mCurrentPos > POS_START) {
onRelease(false);
return true;
} else {
return dispatchTouchEventSupper(e);
}
case MotionEvent.ACTION_DOWN:
mDownEvent = e;
mPtLastMove.set(e.getX(), e.getY());
mIsUnderTouch = true;
mPreventForHorizontal = false;
if (mInterceptEventWhileWorking && mCurrentPos > POS_START) {
// do nothing, intercept child event
} else {
dispatchTouchEventSupper(e);
}
return true;
case MotionEvent.ACTION_MOVE:
mLastMoveEvent = e;
float offsetX = e.getX() - mPtLastMove.x;
float offsetY = (int) (e.getY() - mPtLastMove.y);
mPtLastMove.set(e.getX(), e.getY());
if (mDisableWhenHorizontalMove && !mPreventForHorizontal && (Math.abs(offsetX) > mPagingTouchSlop || Math.abs(offsetX) > 3 * Math.abs(offsetY))) {
if (frameIsNotMoved()) {
mPreventForHorizontal = true;
}
}
if (mPreventForHorizontal) {
return dispatchTouchEventSupper(e);
}
mScrollChecker.abortIfWorking();
boolean moveDown = offsetY > 0;
boolean moveUp = !moveDown;
boolean canMoveUp = mCurrentPos > POS_START;
if (DEBUG) {
boolean canMoveDown = mPtrHandler != null && mPtrHandler.checkCanDoRefresh(this, mContent, mHeaderView);
CLog.v(LOG_TAG, "ACTION_MOVE: offsetY:%s, mCurrentPos: %s, moveUp: %s, canMoveUp: %s, moveDown: %s: canMoveDown: %s", offsetY, mCurrentPos, moveUp, canMoveUp, moveDown, canMoveDown);
}
// disable move when header not reach top
if (moveDown && mPtrHandler != null && !mPtrHandler.checkCanDoRefresh(this, mContent, mHeaderView)) {
return dispatchTouchEventSupper(e);
}
if ((moveUp && canMoveUp) || moveDown) {
offsetY = (float) ((double) offsetY / mResistance);
movePos(offsetY);
return true;
}
}
return dispatchTouchEventSupper(e);
}
/**
* if deltaY > 0, move the content down
*
* @param deltaY
*/
private void movePos(float deltaY) {
// has reached the top
if ((deltaY < 0 && mCurrentPos == POS_START)) {
if (DEBUG) {
CLog.e(LOG_TAG, String.format("has reached the top"));
}
return;
}
int to = mCurrentPos + (int) deltaY;
// over top
if (to < POS_START) {
if (DEBUG) {
CLog.e(LOG_TAG, String.format("over top"));
}
to = POS_START;
}
mCurrentPos = to;
updatePos();
mLastPos = mCurrentPos;
}
private void updatePos() {
int change = mCurrentPos - mLastPos;
if (change == 0) {
return;
}
// leave initiated position
if (mLastPos == POS_START && mCurrentPos != POS_START && mPtrUIHandlerHolder.hasHandler()) {
if (mStatus == PTR_STATUS_INIT) {
mStatus = PTR_STATUS_PREPARE;
mPtrUIHandlerHolder.onUIRefreshPrepare(this);
if (DEBUG) {
CLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshPrepare, mAutoScrollRefresh %s", mAutoScrollRefresh);
}
}
// send cancel event to children
if (mIsUnderTouch && mInterceptEventWhileWorking) {
sendCancelEvent();
}
}
// back to initiated position
if (mLastPos != POS_START && mCurrentPos == POS_START) {
tryToNotifyReset();
// recover event to children
if (mIsUnderTouch && mInterceptEventWhileWorking) {
sendDownEvent();
}
}
// Pull to Refresh
if (mStatus == PTR_STATUS_PREPARE) {
// reach fresh height while moving from top to bottom
if (mIsUnderTouch && mAutoScrollRefresh == 0 && mPullToRefresh
&& mLastPos < mOffsetToRefresh && mCurrentPos >= mOffsetToRefresh) {
tryToPerformRefresh();
}
// reach header height while auto refresh
if (mAutoScrollRefresh == STATUS_AUTO_SCROLL_LATER && mLastPos < mHeaderHeight && mCurrentPos >= mHeaderHeight) {
tryToPerformRefresh();
}
}
if (DEBUG) {
CLog.v(LOG_TAG, "updatePos: change: %s, current: %s last: %s, top: %s, headerHeight: %s",
change, mCurrentPos, mLastPos, mContent.getTop(), mHeaderHeight);
}
mHeaderView.offsetTopAndBottom(change);
mContent.offsetTopAndBottom(change);
invalidate();
final float oldPercent = mHeaderHeight == 0 ? 0 : mLastPos * 1f / mHeaderHeight;
final float currentPercent = mHeaderHeight == 0 ? 0 : mCurrentPos * 1f / mHeaderHeight;
if (mPtrUIHandlerHolder.hasHandler()) {
mPtrUIHandlerHolder.onUIPositionChange(this, mIsUnderTouch, mStatus, mLastPos, mCurrentPos, oldPercent, currentPercent);
}
onPositionChange(mIsUnderTouch, mStatus, mLastPos, mCurrentPos, oldPercent, currentPercent);
}
protected void onPositionChange(boolean isInTouching, byte status, int lastPosition, int currentPosition, float oldPercent, float currentPercent) {
}
public int getHeaderHeight() {
return mHeaderHeight;
}
private void onRelease(boolean stayForLoading) {
if (DEBUG) {
CLog.i(LOG_TAG, "onRelease");
}
tryToPerformRefresh();
// keep header for fresh
if (mStatus == PTR_STATUS_LOADING) {
if (mKeepHeaderWhenRefresh) {
// scroll header back
if (mCurrentPos > mHeaderHeight && !stayForLoading) {
mScrollChecker.tryToScrollTo(mHeaderHeight, mDurationToClose);
} else {
// do nothing
}
} else {
mScrollChecker.tryToScrollTo(POS_START, mDurationToCloseHeader);
}
} else {
mScrollChecker.tryToScrollTo(POS_START, mDurationToCloseHeader);
}
}
private boolean tryToPerformRefresh() {
if (mStatus != PTR_STATUS_PREPARE) {
return false;
}
if ((mCurrentPos >= mHeaderHeight && mAutoScrollRefresh > 0) || mCurrentPos >= mOffsetToRefresh) {
mStatus = PTR_STATUS_LOADING;
performRefresh();
}
return false;
}
private void performRefresh() {
if (mPtrUIHandlerHolder.hasHandler()) {
mPtrUIHandlerHolder.onUIRefreshBegin(this);
if (DEBUG) {
CLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshBegin");
}
}
if (mPtrHandler != null) {
mPtrHandler.onRefreshBegin(this);
}
}
private void tryToNotifyReset() {
if ((mStatus == PTR_STATUS_COMPLETE || mStatus == PTR_STATUS_PREPARE) && mCurrentPos == POS_START) {
if (mPtrUIHandlerHolder.hasHandler()) {
mPtrUIHandlerHolder.onUIReset(this);
if (DEBUG) {
CLog.i(LOG_TAG, "PtrUIHandler: onUIReset");
}
}
mStatus = PTR_STATUS_INIT;
mAutoScrollRefresh = 0;
}
}
protected void onPtrScrollAbort() {
}
protected void onPtrScrollFinish() {
if (mCurrentPos > 0 && mAutoScrollRefresh > 0) {
onRelease(true);
}
}
private boolean frameIsNotMoved() {
return mCurrentPos == POS_START;
}
final public void refreshComplete() {
if (DEBUG) {
CLog.i(LOG_TAG, "refreshComplete");
}
mStatus = PTR_STATUS_COMPLETE;
tryToNotifyReset();
if (mPtrUIHandlerHolder.hasHandler()) {
mPtrUIHandlerHolder.onUIRefreshComplete(this);
if (DEBUG) {
CLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshComplete");
}
}
// back to top only while under touch or auto moving down when auto refresh
// not in scroll and also not under touch
if ((mScrollChecker.mIsRunning && mAutoScrollRefresh == STATUS_AUTO_SCROLL_LATER) || mIsUnderTouch) {
} else {
mScrollChecker.tryToScrollTo(POS_START, mDurationToCloseHeader);
}
}
public void autoRefresh() {
autoRefresh(true, mDurationToCloseHeader);
}
public void autoRefresh(boolean atOnce) {
autoRefresh(atOnce, mDurationToCloseHeader);
}
public void autoRefresh(boolean atOnce, int duration) {
if (mStatus != PTR_STATUS_INIT) {
return;
}
mAutoScrollRefresh = atOnce ? STATUS_AUTO_SCROLL_AT_ONCE : STATUS_AUTO_SCROLL_LATER;
mStatus = PTR_STATUS_PREPARE;
if (mPtrUIHandlerHolder.hasHandler()) {
mPtrUIHandlerHolder.onUIRefreshPrepare(this);
if (DEBUG) {
CLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshPrepare, mAutoScrollRefresh %s", mAutoScrollRefresh);
}
}
mScrollChecker.tryToScrollTo(mOffsetToRefresh, duration);
if (atOnce) {
mStatus = PTR_STATUS_LOADING;
performRefresh();
}
}
/**
* It's useful when working with viewpager.
*
* @param disable
*/
public void disableWhenHorizontalMove(boolean disable) {
mDisableWhenHorizontalMove = disable;
}
public void setInterceptEventWhileWorking(boolean yes) {
mInterceptEventWhileWorking = yes;
}
public View getContentView() {
return mContent;
}
public void setPtrHandler(PtrHandler ptrHandler) {
mPtrHandler = ptrHandler;
}
public void addPtrUIHandler(PtrUIHandler ptrUIHandler) {
PtrUIHandlerHolder.addHandler(mPtrUIHandlerHolder, ptrUIHandler);
}
public void removePtrUIHandler(PtrUIHandler ptrUIHandler) {
mPtrUIHandlerHolder = PtrUIHandlerHolder.removeHandler(mPtrUIHandlerHolder, ptrUIHandler);
}
public float getResistance() {
return mResistance;
}
public void setResistance(float resistance) {
mResistance = resistance;
}
public float getDurationToClose() {
return mDurationToClose;
}
public void setDurationToClose(int duration) {
mDurationToClose = duration;
}
public float getDurationToCloseHeader() {
return mDurationToCloseHeader;
}
public void setDurationToCloseHeader(int duration) {
mDurationToCloseHeader = duration;
}
public void setRatioOfHeaderHeightToRefresh(float ratio) {
mRatioOfHeaderHeightToRefresh = ratio;
mOffsetToRefresh = (int) (mHeaderHeight * mRatioOfHeaderHeightToRefresh);
}
public int getOffsetToRefresh() {
return mOffsetToRefresh;
}
public void setOffsetToRefresh(int offset) {
mOffsetToRefresh = offset;
}
public float getRatioOfHeaderToHeightRefresh() {
return mRatioOfHeaderHeightToRefresh;
}
public boolean isKeepHeaderWhenRefresh() {
return mKeepHeaderWhenRefresh;
}
public void setKeepHeaderWhenRefresh(boolean keepOrNot) {
mKeepHeaderWhenRefresh = keepOrNot;
}
public boolean isPullToRefresh() {
return mPullToRefresh;
}
public void setPullToRefresh(boolean pullToRefresh) {
mPullToRefresh = pullToRefresh;
}
public View getHeaderView() {
return mHeaderView;
}
public void setHeaderView(View header) {
if (mHeaderView != null && header != null && mHeaderView != header) {
removeView(mHeaderView);
}
ViewGroup.LayoutParams lp = header.getLayoutParams();
if (lp == null) {
lp = new LayoutParams(-1, -2);
header.setLayoutParams(lp);
}
mHeaderView = header;
addView(header);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
private void sendCancelEvent() {
if (DEBUG) {
CLog.d(LOG_TAG, "send cancel event");
}
MotionEvent e = MotionEvent.obtain(mDownEvent.getDownTime(), mDownEvent.getEventTime() + ViewConfiguration.getLongPressTimeout(), MotionEvent.ACTION_CANCEL, mDownEvent.getX(), mDownEvent.getY(), mDownEvent.getMetaState());
dispatchTouchEventSupper(e);
}
private void sendDownEvent() {
if (DEBUG) {
CLog.d(LOG_TAG, "send down event");
}
final MotionEvent last = mLastMoveEvent;
MotionEvent e = MotionEvent.obtain(last.getDownTime(), last.getEventTime(), MotionEvent.ACTION_DOWN, last.getX(), last.getY(), last.getMetaState());
dispatchTouchEventSupper(e);
}
public static class LayoutParams extends MarginLayoutParams {
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(MarginLayoutParams source) {
super(source);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
class ScrollChecker implements Runnable {
private int mLastFlingY;
private Scroller mScroller;
private boolean mIsRunning = false;
private int mStart;
private int mTo;
public ScrollChecker() {
mScroller = new Scroller(getContext());
}
public void run() {
boolean finish = !mScroller.computeScrollOffset() || mScroller.isFinished();
int curY = mScroller.getCurrY();
int deltaY = curY - mLastFlingY;
if (DEBUG) {
if (deltaY != 0) {
CLog.v(LOG_TAG,
"scroll: %s, start: %s, to: %s, mCurrentPos: %s, current :%s, last: %s, delta: %s",
finish, mStart, mTo, mCurrentPos, curY, mLastFlingY, deltaY);
}
}
if (!finish) {
mLastFlingY = curY;
movePos(deltaY);
post(this);
} else {
finish();
}
}
private void finish() {
if (DEBUG) {
CLog.v(LOG_TAG, "finish, mCurrentPos:%s", mCurrentPos);
}
reset();
onPtrScrollFinish();
}
private void reset() {
mIsRunning = false;
mLastFlingY = 0;
removeCallbacks(this);
}
public void abortIfWorking() {
if (mIsRunning) {
if (!mScroller.isFinished()) {
mScroller.forceFinished(true);
}
onPtrScrollAbort();
reset();
}
}
public void tryToScrollTo(int to, int duration) {
if (mCurrentPos == to) {
return;
}
mStart = mCurrentPos;
mTo = to;
int distance = to - mStart;
if (DEBUG) {
CLog.d(LOG_TAG, "tryToScrollTo: start: %s, distance:%s, to:%s", mStart, distance, to);
}
removeCallbacks(this);
mLastFlingY = 0;
mScroller = new Scroller(getContext());
mScroller.startScroll(0, 0, 0, distance, duration);
post(this);
mIsRunning = true;
}
}
}