private void onRelease(boolean stayForLoading) { tryToPerformRefresh(); if (mStatus == PTR_STATUS_LOADING) { // keep header for fresh if (mKeepHeaderWhenRefresh) { // scroll header back if (mPtrIndicator.isOverOffsetToKeepHeaderWhileLoading() && !stayForLoading) { mScrollChecker.tryToScrollTo( mPtrIndicator.getOffsetToKeepHeaderWhileLoading(), mDurationToClose); } else { // do nothing } } else { tryScrollBackToTopWhileLoading(); } } else { if (mStatus == PTR_STATUS_COMPLETE) { notifyUIRefreshComplete(false); } else { tryScrollBackToTopAbortRefresh(); } } }
@Override public void onUIPositionChange( PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) { mLoadingView.setPaintAlpha( ptrIndicator.getCurrentPercent() >= 1 ? 0xFF : (int) (ptrIndicator.getCurrentPercent() * 0xFF)); }
@Override public void onUIPositionChange( PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) { float percent = Math.min(1f, ptrIndicator.getCurrentPercent()); if ((status == PtrFrameLayout.PTR_STATUS_PREPARE) && (ptrIndicator.getCurrentPercent() <= 1.0f)) { mRoundProgressBar.setProgress((int) (ptrIndicator.getCurrentPercent() * 100.0f) - 8, true); } }
private boolean tryToPerformRefresh() { if (mStatus != PTR_STATUS_PREPARE) { return false; } // if ((mPtrIndicator.isOverOffsetToKeepHeaderWhileLoading() && isAutoRefresh()) || mPtrIndicator.isOverOffsetToRefresh()) { mStatus = PTR_STATUS_LOADING; performRefresh(); } return false; }
private void layoutChildren() { int offsetX = mPtrIndicator.getCurrentPosY(); 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 && DEBUG_LAYOUT) { PtrCLog.d(LOG_TAG, "onLayout header: %s %s %s %s", left, top, right, bottom); } } if (mContent != null) { if (isPinContent()) { offsetX = 0; } 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 && DEBUG_LAYOUT) { PtrCLog.d(LOG_TAG, "onLayout content: %s %s %s %s", left, top, right, bottom); } mContent.layout(left, top, right, bottom); } }
@Override public void onUIPositionChange( PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) { int mOffsetToRefresh = frame.getOffsetToRefresh(); int currentPos = ptrIndicator.getCurrentPosY(); int lastPos = ptrIndicator.getLastPosY(); if (currentPos < mOffsetToRefresh && lastPos >= mOffsetToRefresh) { if (isUnderTouch && status == 2) { this.crossRotateLineFromBottomUnderTouch(frame); } } else if (currentPos > mOffsetToRefresh && lastPos <= mOffsetToRefresh && isUnderTouch && status == 2) { this.crossRotateLineFromTopUnderTouch(frame); } }
private boolean tryToPerformRefresh() { if (mStatus != PTR_STATUS_PREPARE) { return false; } // 网络不可用时,不能下拉刷新 if (!NetWorkUtil.isNetworkAvailable(getContext())) { return false; } // if ((mPtrIndicator.isOverOffsetToKeepHeaderWhileLoading() && isAutoRefresh()) || mPtrIndicator.isOverOffsetToRefresh()) { mStatus = PTR_STATUS_LOADING; performRefresh(); } return false; }
protected void onPtrScrollFinish() { if (mPtrIndicator.hasLeftStartPosition() && isAutoRefresh()) { if (DEBUG) { PtrCLog.d(LOG_TAG, "call onRelease after scroll finish"); } onRelease(true); } }
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (DEBUG && DEBUG_LAYOUT) { PtrCLog.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; mPtrIndicator.setHeaderHeight(mHeaderHeight); } if (mContent != null) { measureContentView(mContent, widthMeasureSpec, heightMeasureSpec); if (DEBUG && DEBUG_LAYOUT) { ViewGroup.MarginLayoutParams lp = (MarginLayoutParams) mContent.getLayoutParams(); PtrCLog.d( LOG_TAG, "onMeasure content, width: %s, height: %s, margin: %s %s %s %s", getMeasuredWidth(), getMeasuredHeight(), lp.leftMargin, lp.topMargin, lp.rightMargin, lp.bottomMargin); PtrCLog.d( LOG_TAG, "onMeasure, currentPos: %s, lastPos: %s, top: %s", mPtrIndicator.getCurrentPosY(), mPtrIndicator.getLastPosY(), mContent.getTop()); } } }
@Override public void onUIPositionChange( PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) { float percent = ptrIndicator.getCurrentPercent(); if (percent <= 1) { this.ptrPullDownPercent = percent; postInvalidate(); } }
/** * if deltaY > 0, move the content down * * @param deltaY */ private void movePos(float deltaY) { // has reached the top if ((deltaY < 0 && mPtrIndicator.isInStartPosition())) { if (DEBUG) { PtrCLog.e(LOG_TAG, String.format("has reached the top")); } return; } int to = mPtrIndicator.getCurrentPosY() + (int) deltaY; // over top if (mPtrIndicator.willOverTop(to)) { if (DEBUG) { PtrCLog.e(LOG_TAG, String.format("over top")); } to = PtrIndicator.POS_START; } mPtrIndicator.setCurrentPos(to); int change = to - mPtrIndicator.getLastPosY(); updatePos(change); }
/** * Do real refresh work. If there is a hook, execute the hook first. * * @param ignoreHook */ private void notifyUIRefreshComplete(boolean ignoreHook) { /** * After hook operation is done, {@link #notifyUIRefreshComplete} will be call in resume action * to ignore hook. */ if (mPtrIndicator.hasLeftStartPosition() && !ignoreHook && mRefreshCompleteHook != null) { if (DEBUG) { PtrCLog.d(LOG_TAG, "notifyUIRefreshComplete mRefreshCompleteHook run."); } mRefreshCompleteHook.takeOver(); return; } if (mPtrUIHandlerHolder.hasHandler()) { if (DEBUG) { PtrCLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshComplete"); } mPtrUIHandlerHolder.onUIRefreshComplete(this); } mPtrIndicator.onUIRefreshComplete(); tryScrollBackToTopAfterComplete(); tryToNotifyReset(); }
public PtrFrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mPtrIndicator = new PtrIndicator(); 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); mPtrIndicator.setResistance( arr.getFloat(R.styleable.PtrFrameLayout_ptr_resistance, mPtrIndicator.getResistance())); mDurationToClose = arr.getInt(R.styleable.PtrFrameLayout_ptr_duration_to_close, mDurationToClose); mDurationToCloseHeader = arr.getInt( R.styleable.PtrFrameLayout_ptr_duration_to_close_header, mDurationToCloseHeader); float ratio = mPtrIndicator.getRatioOfHeaderToHeightRefresh(); ratio = arr.getFloat(R.styleable.PtrFrameLayout_ptr_ratio_of_header_height_to_refresh, ratio); mPtrIndicator.setRatioOfHeaderHeightToRefresh(ratio); 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; }
/** If at the top and not in loading, reset */ private boolean tryToNotifyReset() { if ((mStatus == PTR_STATUS_COMPLETE || mStatus == PTR_STATUS_PREPARE) && mPtrIndicator.isInStartPosition()) { if (mPtrUIHandlerHolder.hasHandler()) { mPtrUIHandlerHolder.onUIReset(this); if (DEBUG) { PtrCLog.i(LOG_TAG, "PtrUIHandler: onUIReset"); } } mStatus = PTR_STATUS_INIT; clearFlag(); return true; } return false; }
@Override public void onUIPositionChange( PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) { float percent = Math.min(1f, ptrIndicator.getCurrentPercent()); if (status == PtrFrameLayout.PTR_STATUS_PREPARE) { mDrawable.setAlpha((int) (255 * percent)); mDrawable.showArrow(true); float strokeStart = ((percent) * .8f); mDrawable.setStartEndTrim(0f, Math.min(0.8f, strokeStart)); mDrawable.setArrowScale(Math.min(1f, percent)); // magic float rotation = (-0.25f + .4f * percent + percent * 2) * .5f; mDrawable.setProgressRotation(rotation); invalidate(); } }
public void autoRefresh(boolean atOnce, int duration) { if (mStatus != PTR_STATUS_INIT) { return; } mFlag |= atOnce ? FLAG_AUTO_REFRESH_AT_ONCE : FLAG_AUTO_REFRESH_BUT_LATER; mStatus = PTR_STATUS_PREPARE; if (mPtrUIHandlerHolder.hasHandler()) { mPtrUIHandlerHolder.onUIRefreshPrepare(this); if (DEBUG) { PtrCLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshPrepare, mFlag %s", mFlag); } } mScrollChecker.tryToScrollTo(mPtrIndicator.getOffsetToRefresh(), duration); if (atOnce) { mStatus = PTR_STATUS_LOADING; performRefresh(); } }
@SuppressWarnings({"unused"}) public int getOffsetToKeepHeaderWhileLoading() { return mPtrIndicator.getOffsetToKeepHeaderWhileLoading(); }
@SuppressWarnings({"unused"}) public void setOffsetToKeepHeaderWhileLoading(int offset) { mPtrIndicator.setOffsetToKeepHeaderWhileLoading(offset); }
@SuppressWarnings({"unused"}) public float getRatioOfHeaderToHeightRefresh() { return mPtrIndicator.getRatioOfHeaderToHeightRefresh(); }
@SuppressWarnings({"unused"}) public void setOffsetToRefresh(int offset) { mPtrIndicator.setOffsetToRefresh(offset); }
public int getOffsetToRefresh() { return mPtrIndicator.getOffsetToRefresh(); }
public void setRatioOfHeaderHeightToRefresh(float ratio) { mPtrIndicator.setRatioOfHeaderHeightToRefresh(ratio); }
public void setResistance(float resistance) { mPtrIndicator.setResistance(resistance); }
@SuppressWarnings({"unused"}) public float getResistance() { return mPtrIndicator.getResistance(); }
private void updatePos(int change) { if (change == 0) { return; } boolean isUnderTouch = mPtrIndicator.isUnderTouch(); // once moved, cancel event will be sent to child if (isUnderTouch && !mHasSendCancelEvent && mPtrIndicator.hasMovedAfterPressedDown()) { mHasSendCancelEvent = true; sendCancelEvent(); } // leave initiated position or just refresh complete if ((mPtrIndicator.hasJustLeftStartPosition() && mStatus == PTR_STATUS_INIT) || (mPtrIndicator.goDownCrossFinishPosition() && mStatus == PTR_STATUS_COMPLETE && isEnabledNextPtrAtOnce())) { mStatus = PTR_STATUS_PREPARE; mPtrUIHandlerHolder.onUIRefreshPrepare(this); if (DEBUG) { PtrCLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshPrepare, mFlag %s", mFlag); } } // back to initiated position if (mPtrIndicator.hasJustBackToStartPosition()) { tryToNotifyReset(); // recover event to children if (isUnderTouch) { sendDownEvent(); } } // Pull to Refresh if (mStatus == PTR_STATUS_PREPARE) { // reach fresh height while moving from top to bottom if (isUnderTouch && !isAutoRefresh() && mPullToRefresh && mPtrIndicator.crossRefreshLineFromTopToBottom()) { tryToPerformRefresh(); } // reach header height while auto refresh if (performAutoRefreshButLater() && mPtrIndicator.hasJustReachedHeaderHeightFromTopToBottom()) { tryToPerformRefresh(); } } if (DEBUG) { PtrCLog.v( LOG_TAG, "updatePos: change: %s, current: %s last: %s, top: %s, headerHeight: %s", change, mPtrIndicator.getCurrentPosY(), mPtrIndicator.getLastPosY(), mContent.getTop(), mHeaderHeight); } mHeaderView.offsetTopAndBottom(change); if (!isPinContent()) { mContent.offsetTopAndBottom(change); } invalidate(); if (mPtrUIHandlerHolder.hasHandler()) { mPtrUIHandlerHolder.onUIPositionChange(this, isUnderTouch, mStatus, mPtrIndicator); } onPositionChange(isUnderTouch, mStatus, mPtrIndicator); }
/** Scroll back to to if is not under touch */ private void tryScrollBackToTop() { if (!mPtrIndicator.isUnderTouch()) { mScrollChecker.tryToScrollTo(PtrIndicator.POS_START, mDurationToCloseHeader); } }
public void setPtrIndicator(PtrIndicator slider) { if (mPtrIndicator != null && mPtrIndicator != slider) { slider.convertFrom(mPtrIndicator); } mPtrIndicator = slider; }
@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: mPtrIndicator.onRelease(); if (mPtrIndicator.hasLeftStartPosition()) { if (DEBUG) { PtrCLog.d(LOG_TAG, "call onRelease when user release"); } onRelease(false); if (mPtrIndicator.hasMovedAfterPressedDown()) { sendCancelEvent(); return true; } return dispatchTouchEventSupper(e); } else { return dispatchTouchEventSupper(e); } case MotionEvent.ACTION_DOWN: mHasSendCancelEvent = false; mPtrIndicator.onPressDown(e.getX(), e.getY()); mScrollChecker.abortIfWorking(); mPreventForHorizontal = false; // The cancel event will be sent once the position is moved. // So let the event pass to children. // fix #93, #102 dispatchTouchEventSupper(e); return true; case MotionEvent.ACTION_MOVE: mLastMoveEvent = e; mPtrIndicator.onMove(e.getX(), e.getY()); float offsetX = mPtrIndicator.getOffsetX(); float offsetY = mPtrIndicator.getOffsetY(); if (mDisableWhenHorizontalMove && !mPreventForHorizontal && (Math.abs(offsetX) > mPagingTouchSlop && Math.abs(offsetX) > Math.abs(offsetY))) { if (mPtrIndicator.isInStartPosition()) { mPreventForHorizontal = true; } } if (mPreventForHorizontal) { return dispatchTouchEventSupper(e); } boolean moveDown = offsetY > 0; boolean moveUp = !moveDown; boolean canMoveUp = mPtrIndicator.hasLeftStartPosition(); if (DEBUG) { boolean canMoveDown = mPtrHandler != null && mPtrHandler.checkCanDoRefresh(this, mContent, mHeaderView); PtrCLog.v( LOG_TAG, "ACTION_MOVE: offsetY:%s, currentPos: %s, moveUp: %s, canMoveUp: %s, moveDown: %s: canMoveDown: %s", offsetY, mPtrIndicator.getCurrentPosY(), 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) { movePos(offsetY); return true; } } return dispatchTouchEventSupper(e); }