@Override public void computeScroll() { super.computeScroll(); if (mScroller.computeScrollOffset()) { int lastScrollY = mHolder.mOffsetY; int currentY = mScroller.getCurrY(); int offsetY = currentY - lastScrollY; lastScrollY = currentY; moveView(offsetY); LogUtils.d("currentY=" + currentY + ";mHolder.mOffsetY=" + mHolder.mOffsetY); } else { LogUtils.d("scroll end mOffsetY=" + mHolder.mOffsetY); if (mHolder.mOffsetY == 0) mHasScrollBack = false; } }
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); LogUtils.d("onLayout mHolder.mOffsetY=" + mHolder.mOffsetY); mFootHeight = ((IFooterCallBack) mFooterView).getFooterHeight(); int childCount = getChildCount(); int top = getPaddingTop() + mHolder.mOffsetY; int adHeight = 0; for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (child.getVisibility() == View.VISIBLE) { if (i == 0) { adHeight = child.getMeasuredHeight() - mHeaderViewHeight; // 通过把headerview向上移动一个headerview高度的距离来达到隐藏headerview的效果 child.layout(0, top - mHeaderViewHeight, child.getMeasuredWidth(), top + adHeight); top += adHeight; } else if (i == 1) { int childHeight = child.getMeasuredHeight() - adHeight; child.layout(0, top, child.getMeasuredWidth(), childHeight + top); top += childHeight; } else { child.layout(0, top, child.getMeasuredWidth(), child.getMeasuredHeight() + top); top += child.getMeasuredHeight(); } } } }
/** * 设置自定义headerView * * @param headerView headerView必须要实现 IHeaderCallBack接口 */ public void setCustomHeaderView(View headerView) { LogUtils.i("setCustomHeaderView"); if (headerView instanceof IHeaderCallBack) { mHeaderView = headerView; } else { throw new RuntimeException("headerView must be implementes IHeaderCallBack!"); } }
/** reset header view's height. */ private void resetHeaderHeight() { float height = mHolder.mOffsetY; if (height == 0) // not visible. return; // refreshing and header isn't shown fully. do nothing. if (mPullRefreshing && height <= mHeaderViewHeight) { return; } int offsetY = 0; if (mPullRefreshing) { offsetY = mHeaderViewHeight - mHolder.mOffsetY; startScroll(offsetY, SCROLL_DURATION); } else { offsetY = 0 - mHolder.mOffsetY; startScroll(offsetY, SCROLL_DURATION); } LogUtils.d("resetHeaderHeight offsetY=" + offsetY); }
private void sendCancelEvent() { LogUtils.d("sendCancelEvent"); if (!mHasSendCancelEvent) { setRefreshTime(); mHasSendCancelEvent = true; mHasSendDownEvent = false; MotionEvent last = mLastMoveEvent; MotionEvent e = MotionEvent.obtain( last.getDownTime(), last.getEventTime() + ViewConfiguration.getLongPressTimeout(), MotionEvent.ACTION_CANCEL, last.getX(), last.getY(), last.getMetaState()); dispatchTouchEventSupper(e); } }
private void addFooterView(OnGlobalLayoutListener listener) { mHeaderViewHeight = ((IHeaderCallBack) mHeaderView).getHeaderHeight(); LogUtils.d("onGlobalLayout mHeaderViewHeight=" + mHeaderViewHeight); mContentView.setHolder(mHolder); mContentView.setScrollListener(); if (mEnablePullLoad && needAddFooterView()) { Log.i("CustomView", "add footView" + ";mHeaderViewHeight=" + mHeaderViewHeight); addView(mFooterView); } // 移除视图树监听器 removeViewTreeObserver(listener); if (autoRefresh) { startRefresh(); } if (mHeadMoveDistence == 0) { mHeadMoveDistence = getHeight() / 3; } }
/** stop refresh, reset header view. */ public void stopRefresh() { LogUtils.i("stopRefresh mPullRefreshing=" + mPullRefreshing); if (mPullRefreshing == true) { mPullRefreshing = false; mHeaderCallBack.onStateFinish(); mState = XRefreshViewState.STATE_COMPLETE; mHasScrollBack = true; mHandler.postDelayed( new Runnable() { @Override public void run() { resetHeaderHeight(); lastRefreshTime = Calendar.getInstance().getTimeInMillis(); } }, mPinnedTime); } }
private void sendDownEvent() { if (!mHasSendDownEvent) { LogUtils.d("sendDownEvent"); mHasSendCancelEvent = false; mHasSendDownEvent = true; isIntercepted = false; final MotionEvent last = mLastMoveEvent; if (last == null) return; MotionEvent e = MotionEvent.obtain( last.getDownTime(), last.getEventTime(), MotionEvent.ACTION_DOWN, last.getX(), last.getY(), last.getMetaState()); dispatchTouchEventSupper(e); } }
@Override public void computeScroll() { super.computeScroll(); if (mScroller.computeScrollOffset()) { int lastScrollY = mHolder.mOffsetY; int currentY = mScroller.getCurrY(); int offsetY = currentY - lastScrollY; lastScrollY = currentY; moveView(offsetY); LogUtils.d("currentY=" + currentY + ";mHolder.mOffsetY=" + mHolder.mOffsetY); } else { int currentY = mScroller.getCurrY(); if (mHolder.mOffsetY == 0) { mStopingRefresh = false; } else { // 有时scroller已经停止了,但是却没有回到应该在的位置,执行下面的方法恢复 if (mStopingRefresh && !mPullLoading && !mPullRefreshing) { startScroll(-currentY, SCROLL_DURATION); } } } }
@Override protected void onLayout(boolean changed, int l, int t2, int r, int b) { super.onLayout(changed, l, t2, r, b); // if(mHolder.mOffsetY!=0)return; LogUtils.d("onLayout mHolder.mOffsetY=" + mHolder.mOffsetY); mFootHeight = ((IFooterCallBack) mFooterView).getFooterHeight(); int childCount = getChildCount(); int top = getPaddingTop() + mHolder.mOffsetY; int adHeight = 0; for (int i = 0; i < childCount; i++) { View child = getChildAt(i); LayoutParams margins = (LayoutParams) child.getLayoutParams(); int topMargin = margins.topMargin; int bottomMargin = margins.bottomMargin; int leftMargin = margins.leftMargin; int rightMargin = margins.rightMargin; l = leftMargin; top += topMargin; r -= rightMargin; if (child.getVisibility() != View.GONE) { if (i == 0) { adHeight = child.getMeasuredHeight() - mHeaderViewHeight; // 通过把headerview向上移动一个headerview高度的距离来达到隐藏headerview的效果 child.layout(l, top - mHeaderViewHeight, r, top + adHeight); top += adHeight; } else if (i == 1) { int childHeight = child.getMeasuredHeight() - adHeight; child.layout(l, top, r, childHeight + top); top += childHeight; } else { child.layout(l, top, r, child.getMeasuredHeight() + top); top += child.getMeasuredHeight(); } } } }
/** * enable or disable pull up load more feature. * * @param enable */ public void setPullLoadEnable(boolean enable) { LogUtils.d("setPullLoadEnable"); mEnablePullLoad = enable; }
@Override public boolean dispatchTouchEvent(MotionEvent ev) { final int action = ev.getAction(); int deltaY = 0; int deltaX = 0; switch (action) { case MotionEvent.ACTION_DOWN: mHasSendCancelEvent = false; mHasSendDownEvent = false; mLastY = (int) ev.getRawY(); mLastX = (int) ev.getRawX(); mInitialMotionY = mLastY; // if (!mScroller.isFinished() && !mPullRefreshing && !mPullLoading) // { // mScroller.forceFinished(true); // } break; case MotionEvent.ACTION_MOVE: mLastMoveEvent = ev; if (mPullLoading || mPullRefreshing || !isEnabled() || mIsIntercept || mHasScrollBack || mContentView.isLoading()) { if (mIsPinnedContentWhenRefreshing) { return super.dispatchTouchEvent(ev); } else { sendCancelEvent(); return true; } } int currentY = (int) ev.getRawY(); int currentX = (int) ev.getRawX(); deltaY = currentY - mLastY; deltaX = currentX - mLastX; mLastY = currentY; mLastX = currentX; // intercept the MotionEvent only when user is not scrolling if (!isIntercepted && (Math.abs(deltaY) < mTouchSlop)) { isIntercepted = true; return super.dispatchTouchEvent(ev); } if (isForHorizontalMove && !mMoveForHorizontal && Math.abs(deltaX) > mTouchSlop && Math.abs(deltaX) > Math.abs(deltaY)) { if (mHolder.mOffsetY == 0) { mMoveForHorizontal = true; } } if (mMoveForHorizontal) { return super.dispatchTouchEvent(ev); } LogUtils.d("isTop=" + mContentView.isTop() + ";isBottom=" + mContentView.isBottom()); if (deltaY > 0 && mHolder.mOffsetY <= mHeadMoveDistence || deltaY < 0) { deltaY = (int) (deltaY / OFFSET_RADIO); } else { deltaY = 0; } if (mContentView.isTop() && (deltaY > 0 || (deltaY < 0 && mHolder.hasHeaderPullDown()))) { sendCancelEvent(); updateHeaderHeight(currentY, deltaY); } else if (needAddFooterView() && mContentView.isBottom() && (deltaY < 0 || deltaY > 0 && mHolder.hasFooterPullUp())) { sendCancelEvent(); updateFooterHeight(deltaY); } else if (mContentView.isTop() && !mHolder.hasHeaderPullDown() || mContentView.isBottom() && !mHolder.hasFooterPullUp()) { if (Math.abs(deltaY) > 0) sendDownEvent(); } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: // if (mHolder.mOffsetY != 0 && mRefreshViewListener != null // && !mPullRefreshing && !mPullLoading) { // mRefreshViewListener.onRelease(mHolder.mOffsetY); // } if (mHolder.hasHeaderPullDown() && !mHasScrollBack) { // invoke refresh if (mEnablePullRefresh && mHolder.mOffsetY > mHeaderViewHeight) { mPullRefreshing = true; mHeaderCallBack.onStateRefreshing(); mState = XRefreshViewState.STATE_REFRESHING; if (mRefreshViewListener != null) { mRefreshViewListener.onRefresh(); } } resetHeaderHeight(); } else if (mHolder.hasFooterPullUp()) { if (mEnablePullLoad && needAddFooterView() && !mHasLoadComplete) { invoketLoadMore(); } else { int offset = 0 - mHolder.mOffsetY; startScroll(offset, SCROLL_DURATION); } } mLastY = -1; // reset mInitialMotionY = 0; isIntercepted = true; mMoveForHorizontal = false; mIsIntercept = false; break; } return super.dispatchTouchEvent(ev); }