Java tutorial
package org.alice.jack_blog.widget.refreshContainer; import android.content.Context; import android.content.res.TypedArray; import android.support.annotation.FloatRange; import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.support.design.widget.AppBarLayout; import android.support.design.widget.CoordinatorLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.NestedScrollingChild; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.AbsListView; import android.widget.FrameLayout; import android.widget.Scroller; import org.alice.jack_blog.R; import java.util.ArrayList; import java.util.List; /** * Copyright 2016 canyinghao * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ public class CanRefreshLayout extends FrameLayout { // private static final int DEFAULT_DURATION = 300; // private static final int DEFAULT_AUTO_DURATION = 100; // private static final float DEFAULT_FRICTION = 0.5f; // ???? CLASSIC private static final int DEFAULT_SMOOTH_LENGTH = 50; // ??? CLASSIC private static final int DEFAULT_SMOOTH_DURATION = 3; // ? private static byte NO_SCROLL = 0; private static byte NO_SCROLL_UP = 1; private static byte NO_SCROLL_DOWN = 2; // public static final byte CLASSIC = 0; public static final byte UPPER = 1; public static final byte LOWER = 2; public static final byte MID = 3; // protected View mHeaderView; // protected View mFooterView; // protected View mContentView; // mIsCoo=true protected View mScrollView; protected ViewPager mViewPager; // 0 mScrollViewNestedScrollingChild 1 viewpager protected boolean mIsViewPager; protected AppBarLayout mAppBar; // 0?? protected int mMaxHeaderHeight; // 0?? protected int mMaxFooterHeight; // protected OnStartUpListener onStartUpListener; // protected OnStartDownListener onStartDownListener; // protected int mHeaderHeight; // protected int mFooterHeight; private boolean isSetHeaderHeight; private boolean isSetFooterHeight; // ? private boolean isHeaderRefreshing; private boolean isFooterRefreshing; // private float mFriction = DEFAULT_FRICTION; // ?? private boolean mRefreshEnabled = true; // ?? private boolean mLoadMoreEnabled = true; // ? protected OnRefreshListener mOnRefreshListener; // ? protected OnLoadMoreListener mOnLoadMoreListener; // private int mHeadStyle = CLASSIC; // private int mFootStyle = CLASSIC; // private int mDuration = DEFAULT_DURATION; // ??? CLASSIC private int mSmoothLength = DEFAULT_SMOOTH_LENGTH; // ?? CLASSIC private int mSmoothDuration = DEFAULT_SMOOTH_DURATION; // ??view? private int isUpOrDown = NO_SCROLL; // y? float directionX; // x? float directionY; // ?? private int mHeadOffY; // ?? private int mFootOffY; // ?? private int mContentOffY; // ?? private float lastY; // ?? private int currentOffSetY; // ? private int offsetSum; // ? private int scrollSum; // private int tempY; // private int mRefreshBackgroundResource; // private int mLoadMoreBackgroundResource; // ?CoordinatorLayout private boolean mIsCoo; // ? mIsCoo=true private boolean isDependentOpen = true; private Scroller mScroller = new Scroller(getContext()); public CanRefreshLayout(Context context) { this(context, null); } public CanRefreshLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CanRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CanRefreshLayout, defStyleAttr, 0); try { final int N = a.getIndexCount(); for (int i = 0; i < N; i++) { int attr = a.getIndex(i); if (attr == R.styleable.CanRefreshLayout_can_enabled_up) { setRefreshEnabled(a.getBoolean(attr, true)); } else if (attr == R.styleable.CanRefreshLayout_can_enabled_down) { setLoadMoreEnabled(a.getBoolean(attr, true)); } else if (attr == R.styleable.CanRefreshLayout_can_style_up) { mHeadStyle = a.getInt(attr, CLASSIC); } else if (attr == R.styleable.CanRefreshLayout_can_style_down) { mFootStyle = a.getInt(attr, CLASSIC); } else if (attr == R.styleable.CanRefreshLayout_can_friction) { setFriction(a.getFloat(attr, DEFAULT_FRICTION)); } else if (attr == R.styleable.CanRefreshLayout_can_duration) { mDuration = a.getInt(attr, DEFAULT_DURATION); } else if (attr == R.styleable.CanRefreshLayout_can_smooth_duration) { mSmoothDuration = a.getInt(attr, DEFAULT_SMOOTH_DURATION); } else if (attr == R.styleable.CanRefreshLayout_can_smooth_length) { mSmoothLength = a.getInt(attr, DEFAULT_SMOOTH_LENGTH); } else if (attr == R.styleable.CanRefreshLayout_can_bg_up) { mRefreshBackgroundResource = a.getResourceId(attr, android.R.color.transparent); } else if (attr == R.styleable.CanRefreshLayout_can_bg_down) { mLoadMoreBackgroundResource = a.getResourceId(attr, android.R.color.transparent); } else if (attr == R.styleable.CanRefreshLayout_can_is_coo) { mIsCoo = a.getBoolean(attr, false); } } } finally { a.recycle(); } } private void setAppBarListener() { if (mAppBar != null) { mAppBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() { @Override public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) { int miniH = mAppBar.getMeasuredHeight() / 2; if (verticalOffset == 0) { isDependentOpen = true; } else if (Math.abs(verticalOffset) >= miniH) { isDependentOpen = false; } } }); } } /** * * * @param mRefreshBackgroundResource int */ public void setRefreshBackgroundResource(int mRefreshBackgroundResource) { this.mRefreshBackgroundResource = mRefreshBackgroundResource; } /** * * * @param mLoadMoreBackgroundResource int */ public void setLoadMoreBackgroundResource(int mLoadMoreBackgroundResource) { this.mLoadMoreBackgroundResource = mLoadMoreBackgroundResource; } /** * ?? * * @param mHeaderHeight int */ public void setHeaderHeight(int mHeaderHeight) { this.mHeaderHeight = mHeaderHeight; isSetHeaderHeight = true; } /** * ?? * * @param mFooterHeight int */ public void setFooterHeight(int mFooterHeight) { this.mFooterHeight = mFooterHeight; isSetFooterHeight = true; } /** * ?? * * @param enable boolean */ public void setRefreshEnabled(boolean enable) { this.mRefreshEnabled = enable; } /** * ?? * * @param enable boolean */ public void setLoadMoreEnabled(boolean enable) { this.mLoadMoreEnabled = enable; } /** * ? * * @param mOnRefreshListener OnRefreshListener */ public void setOnRefreshListener(@NonNull OnRefreshListener mOnRefreshListener) { this.mOnRefreshListener = mOnRefreshListener; } /** * ? * * @param mOnLoadMoreListener OnLoadMoreListener */ public void setOnLoadMoreListener(@NonNull OnLoadMoreListener mOnLoadMoreListener) { this.mOnLoadMoreListener = mOnLoadMoreListener; } /** * * * @param mMaxHeaderHeight int */ public void setMaxHeaderHeight(int mMaxHeaderHeight) { this.mMaxHeaderHeight = mMaxHeaderHeight; } /** * * * @param mMaxFooterHeight int */ public void setMaxFooterHeight(int mMaxFooterHeight) { this.mMaxFooterHeight = mMaxFooterHeight; } /** * ? * * @param onStartUpListener OnStartUpListener */ public void setOnStartUpListener(OnStartUpListener onStartUpListener) { this.onStartUpListener = onStartUpListener; } /** * ? * * @param onStartDownListener OnStartDownListener */ public void setOnStartDownListener(OnStartDownListener onStartDownListener) { this.onStartDownListener = onStartDownListener; } /** * * * @param mFriction FloatRange */ public void setFriction(@FloatRange(from = 0.0, to = 1.0) float mFriction) { this.mFriction = mFriction; } /** * * * @param mDuration int */ public void setDuration(int mDuration) { this.mDuration = mDuration; } /** * ??? * * @param mSmoothLength int */ public void setSmoothLength(int mSmoothLength) { this.mSmoothLength = mSmoothLength; } /** * ?? * * @param mSmoothDuration int */ public void setSmoothDuration(int mSmoothDuration) { this.mSmoothDuration = mSmoothDuration; } /** * * * @param headStyle IntRange * @param footStyle IntRange */ public void setStyle(@IntRange(from = 0, to = 3) int headStyle, @IntRange(from = 0, to = 3) int footStyle) { this.mHeadStyle = headStyle; this.mFootStyle = footStyle; if (mHeadStyle == LOWER || mHeadStyle == MID) { bringChildToFront(mContentView); } if (mFootStyle == LOWER || mFootStyle == MID) { bringChildToFront(mContentView); } if (mHeaderView != null && (mHeadStyle == CLASSIC || mHeadStyle == UPPER)) { bringChildToFront(mHeaderView); } if (mFooterView != null && (mFootStyle == CLASSIC || mFootStyle == UPPER)) { bringChildToFront(mFooterView); } int count = getChildCount(); List<View> list = new ArrayList<>(); for (int i = 0; i < count; i++) { View v = getChildAt(i); if (v != mHeaderView && v != mFooterView && v != mContentView) { list.add(v); } } for (View v : list) { bringChildToFront(v); } } /** * idview */ @Override protected void onFinishInflate() { final int childCount = getChildCount(); if (childCount > 0) { mHeaderView = findViewById(R.id.can_refresh_header); mContentView = findViewById(R.id.can_content_view); mFooterView = findViewById(R.id.can_refresh_footer); mScrollView = findViewById(R.id.can_scroll_view); } if (mContentView == null) { throw new IllegalStateException("mContentView is null"); } if (mIsCoo) { if (mContentView instanceof CoordinatorLayout) { CoordinatorLayout coo = (CoordinatorLayout) mContentView; mAppBar = (AppBarLayout) coo.getChildAt(0); setAppBarListener(); } else { throw new IllegalStateException("mContentView is not CoordinatorLayout"); } if (mScrollView == null) { throw new IllegalStateException("mScrollView is null"); } if (mScrollView instanceof ViewPager) { mViewPager = (ViewPager) mScrollView; mIsViewPager = true; } else if (mScrollView instanceof NestedScrollingChild) { mIsViewPager = false; } else { throw new IllegalStateException("mScrollView is not NestedScrollingChild or ViewPager"); } } if (mHeaderView != null && !(mHeaderView instanceof CanRefresh)) { throw new IllegalStateException("mHeaderView error"); } if (mFooterView != null && !(mFooterView instanceof CanRefresh)) { throw new IllegalStateException("mFooterView error"); } if (mHeaderView != null) { getHeaderInterface().setIsHeaderOrFooter(true); } if (mFooterView != null) { getFooterInterface().setIsHeaderOrFooter(false); } super.onFinishInflate(); setStyle(mHeadStyle, mFootStyle); } @Override protected void onLayout(boolean b, int i, int i1, int i2, int i3) { childLayout(); } /** * view? */ private void childLayout() { 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 - mHeaderHeight + mHeadOffY; final int right = left + mHeaderView.getMeasuredWidth(); final int bottom = top + mHeaderView.getMeasuredHeight(); mHeaderView.layout(left, top, right, bottom); } if (mFooterView != null) { MarginLayoutParams lp = (MarginLayoutParams) mFooterView.getLayoutParams(); final int left = paddingLeft + lp.leftMargin; final int top = getMeasuredHeight() + paddingTop + lp.topMargin - mFootOffY; final int right = left + mFooterView.getMeasuredWidth(); final int bottom = top + mFooterView.getMeasuredHeight(); mFooterView.layout(left, top, right, bottom); } if (mContentView != null) { MarginLayoutParams lp = (MarginLayoutParams) mContentView.getLayoutParams(); final int left = paddingLeft + lp.leftMargin; final int top = paddingTop + lp.topMargin + mContentOffY; final int right = left + mContentView.getMeasuredWidth(); final int bottom = top + mContentView.getMeasuredHeight(); mContentView.layout(left, top, right, bottom); } int count = getChildCount(); for (int i = 0; i < count; i++) { View v = getChildAt(i); if (v != mHeaderView && v != mFooterView && v != mContentView) { MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams(); int left = paddingLeft + lp.leftMargin; int top = paddingTop + lp.topMargin + mContentOffY; int right = left + v.getMeasuredWidth(); int bottom = top + v.getMeasuredHeight(); v.layout(left, top, right, bottom); System.out.println("==========top" + top); } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mHeaderView != null) { measureChildWithMargins(mHeaderView, widthMeasureSpec, 0, heightMeasureSpec, 0); MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams(); if (!isSetHeaderHeight) { mHeaderHeight = mHeaderView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; } } if (mFooterView != null) { measureChildWithMargins(mFooterView, widthMeasureSpec, 0, heightMeasureSpec, 0); MarginLayoutParams lp = (MarginLayoutParams) mFooterView.getLayoutParams(); if (!isSetFooterHeight) { mFooterHeight = mFooterView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; } } if (mContentView != null) { measureChildWithMargins(mContentView, widthMeasureSpec, 0, heightMeasureSpec, 0); } int count = getChildCount(); for (int i = 0; i < count; i++) { View v = getChildAt(i); if (v != mHeaderView && v != mFooterView && v != mContentView) { measureChildWithMargins(v, widthMeasureSpec, 0, heightMeasureSpec, 0); } } } /** * ? * * @return boolean */ private boolean canRefresh() { return !isHeaderRefreshing && mRefreshEnabled && mHeaderView != null && !canChildScrollUp(); } /** * ? * * @return boolean */ private boolean canLoadMore() { return !isFooterRefreshing && mLoadMoreEnabled && mFooterView != null && !canChildScrollDown(); } @Override public boolean dispatchTouchEvent(MotionEvent e) { return super.dispatchTouchEvent(e); } @Override public boolean onInterceptTouchEvent(MotionEvent e) { switch (e.getAction()) { case MotionEvent.ACTION_DOWN: directionY = e.getY(); directionX = e.getX(); break; case MotionEvent.ACTION_MOVE: if (directionY <= 0 || directionX <= 0) { break; } float eventY = e.getY(); float eventX = e.getX(); float offY = eventY - directionY; float offX = eventX - directionX; directionY = eventY; directionX = eventX; boolean moved = Math.abs(offY) > Math.abs(offX); if (offY > 0 && moved && canRefresh()) { isUpOrDown = NO_SCROLL_UP; } else if (offY < 0 && moved && canLoadMore()) { isUpOrDown = NO_SCROLL_DOWN; } else { isUpOrDown = NO_SCROLL; } if (isUpOrDown == NO_SCROLL_DOWN || isUpOrDown == NO_SCROLL_UP) { return true; } break; } return super.onInterceptTouchEvent(e); } @Override public boolean onTouchEvent(MotionEvent e) { // ??view if (!canChildScrollDown() && !canChildScrollUp()) { if (isUpOrDown == NO_SCROLL_UP) { if (canRefresh()) { return touch(e, true); } } else if (isUpOrDown == NO_SCROLL_DOWN) { if (canLoadMore()) { return touch(e, false); } } else { switch (e.getAction()) { case MotionEvent.ACTION_DOWN: directionY = e.getY(); directionX = e.getX(); break; case MotionEvent.ACTION_MOVE: if (directionY <= 0 || directionX <= 0) { break; } float eventY = e.getY(); float eventX = e.getX(); float offY = eventY - directionY; float offX = eventX - directionX; directionY = eventY; directionX = eventX; boolean moved = Math.abs(offY) > Math.abs(offX); if (offY > 0 && moved && canRefresh()) { isUpOrDown = NO_SCROLL_UP; } else if (offY < 0 && moved && canLoadMore()) { isUpOrDown = NO_SCROLL_DOWN; } else { isUpOrDown = NO_SCROLL; } break; } return true; } } else { if (canRefresh()) { return touch(e, true); } else if (canLoadMore()) { return touch(e, false); } } return super.onTouchEvent(e); } /** * ? * * @param e MotionEvent * @param isHead boolean * @return boolean */ private boolean touch(MotionEvent e, boolean isHead) { switch (e.getAction()) { case MotionEvent.ACTION_DOWN: lastY = e.getY(); return true; case MotionEvent.ACTION_MOVE: if (lastY > 0) { currentOffSetY = (int) (e.getY() - lastY); offsetSum += currentOffSetY; } lastY = e.getY(); boolean isCanMove; if (isHead) { isCanMove = offsetSum > 0; } else { isCanMove = offsetSum < 0; } if (isCanMove) { float ratio = getRatio(); if (ratio < 0) { ratio = 0; } int scrollNum = -((int) (currentOffSetY * ratio)); scrollSum += scrollNum; if (isHead) { if (mMaxHeaderHeight > 0 && Math.abs(scrollSum) > mMaxHeaderHeight) { scrollSum = scrollSum > 0 ? mMaxHeaderHeight : -mMaxHeaderHeight; scrollNum = 0; } } else { if (mMaxFooterHeight > 0 && Math.abs(scrollSum) > mMaxFooterHeight) { scrollSum = scrollSum > 0 ? mMaxFooterHeight : -mMaxFooterHeight; scrollNum = 0; } } if (isHead) { setBackgroundResource(mRefreshBackgroundResource); if (onStartUpListener != null && Math.abs(scrollSum) > 0) { onStartUpListener.onUp(); } smoothMove(true, true, scrollNum, scrollSum); if (Math.abs(scrollSum) > mHeaderHeight) { getHeaderInterface().onPrepare(); } getHeaderInterface().onPositionChange(Math.abs(scrollSum) / (float) mHeaderHeight); } else { setBackgroundResource(mLoadMoreBackgroundResource); if (onStartDownListener != null && Math.abs(scrollSum) > 0) { onStartDownListener.onDown(); } smoothMove(false, true, scrollNum, scrollSum); if (Math.abs(scrollSum) > mFooterHeight) { getFooterInterface().onPrepare(); } getFooterInterface().onPositionChange(Math.abs(scrollSum) / (float) mFooterHeight); } } return true; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (isHead) { if (Math.abs(scrollSum) > mHeaderHeight) { smoothMove(true, false, mHeadStyle == CLASSIC ? -mHeaderHeight : mHeaderHeight, mHeaderHeight); getHeaderInterface().onRelease(); refreshing(); } else { smoothMove(true, false, 0, 0); if (onStartUpListener != null) { onStartUpListener.onReset(); } } } else { if (Math.abs(scrollSum) > mFooterHeight) { smoothMove(false, false, mFootStyle == CLASSIC ? mContentView.getMeasuredHeight() - getMeasuredHeight() + mFooterHeight : mFooterHeight, mFooterHeight); getFooterInterface().onRelease(); loadingMore(); } else { smoothMove(false, false, mFootStyle == CLASSIC ? mContentView.getMeasuredHeight() - getMeasuredHeight() : 0, 0); if (onStartDownListener != null) { onStartDownListener.onReset(); } } } resetParameter(); break; } return super.onTouchEvent(e); } /** * ?? * * @return float */ private float getRatio() { return 1 - (Math.abs(offsetSum) / (float) getMeasuredHeight()) - 0.3f * mFriction; } /** * ?? */ private void resetParameter() { directionX = 0; directionY = 0; isUpOrDown = NO_SCROLL; lastY = 0; offsetSum = 0; scrollSum = 0; } /** * * * * @param isHeader boolean * @param isMove boolean ? * @param moveScrollY int * @param moveY int */ private void smoothMove(boolean isHeader, boolean isMove, int moveScrollY, int moveY) { moveY = Math.abs(moveY); if (isHeader) { if (mHeadStyle == CLASSIC) { if (isMove) { smoothScrollBy(0, moveScrollY); } else { smoothScrollTo(0, moveScrollY); } } else { layoutMove(true, isMove, moveScrollY, moveY); } } else { if (mFootStyle == CLASSIC) { if (isMove) { smoothScrollBy(0, moveScrollY); } else { smoothScrollTo(0, moveScrollY); } } else { layoutMove(false, isMove, moveScrollY, moveY); } } } /** * ? * * @param fx int * @param fy int */ public void smoothScrollTo(int fx, int fy) { int dx = fx - mScroller.getFinalX(); int dy = fy - mScroller.getFinalY(); smoothScrollBy(dx, dy); } /** * ?? * * @param dx int * @param dy int */ public void smoothScrollBy(int dx, int dy) { mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy); invalidate(); } /** * ??? * * @param isHeader ? * @param moveScrollY ? * @param isMove ? * @param moveY ? */ private void layoutMove(boolean isHeader, boolean isMove, int moveScrollY, int moveY) { if (isMove) { if (isHeader) { if (mHeadStyle == UPPER) { mHeadOffY = moveY; } else if (mHeadStyle == LOWER) { mHeadOffY = mHeaderHeight; mContentOffY = moveY; } else if (mHeadStyle == MID) { mHeadOffY = moveY / 2 + mHeaderHeight / 2; mContentOffY = moveY; } } else { if (mFootStyle == UPPER) { mFootOffY = moveY; } else if (mFootStyle == LOWER) { mFootOffY = mFooterHeight; mContentOffY = -moveY; } else if (mFootStyle == MID) { mFootOffY = moveY / 2 + mFooterHeight / 2; mContentOffY = -moveY; } } } else { layoutMoveSmooth(isHeader, moveScrollY, moveY); } requestLayout(); } private void layoutMoveSmooth(boolean isHeader, int moveScrollY, int moveY) { if (moveScrollY > 0) { tempY = moveScrollY; layoutSmoothMove(isHeader, moveScrollY, moveY); } else { tempY = Math.abs(scrollSum); layoutSmoothMove(isHeader, moveScrollY, moveY); } } private void layoutSmoothMove(final boolean isHeader, final int moveScrollY, final int moveY) { tempY -= mSmoothLength; if (tempY <= moveY) { layoutMove(isHeader, true, moveScrollY, moveY); return; } layoutMove(isHeader, true, moveScrollY, tempY); postDelayed(new Runnable() { @Override public void run() { layoutSmoothMove(isHeader, moveScrollY, moveY); } }, mSmoothDuration); } /** * ? */ public void refreshComplete() { if (!isHeaderRefreshing) { return; } postDelayed(new Runnable() { @Override public void run() { smoothMove(true, false, mHeadStyle == CLASSIC ? 0 : mHeaderHeight, 0); isHeaderRefreshing = false; getHeaderInterface().onComplete(); getHeaderInterface().onReset(); if (onStartUpListener != null) { onStartUpListener.onReset(); } } }, mDuration); } /** * ? */ public void loadMoreComplete() { if (!isFooterRefreshing) { return; } postDelayed(new Runnable() { @Override public void run() { smoothMove(false, false, mFootStyle == CLASSIC ? mContentView.getMeasuredHeight() - getMeasuredHeight() : mFooterHeight, 0); isFooterRefreshing = false; getFooterInterface().onComplete(); getFooterInterface().onReset(); if (onStartDownListener != null) { onStartDownListener.onReset(); } } }, mDuration); } /** * */ public void autoRefresh() { if (mHeaderView != null) { postDelayed(new Runnable() { @Override public void run() { setBackgroundResource(mRefreshBackgroundResource); smoothMove(true, false, -mHeaderHeight, -mHeaderHeight); getHeaderInterface().onRelease(); refreshing(); } }, DEFAULT_AUTO_DURATION); } } private void refreshing() { isHeaderRefreshing = true; if (mOnRefreshListener != null) { mOnRefreshListener.onRefresh(); } } private void loadingMore() { isFooterRefreshing = true; if (mOnLoadMoreListener != null) { mOnLoadMoreListener.onLoadMore(); } } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } super.computeScroll(); } private CanRefresh getHeaderInterface() { return (CanRefresh) mHeaderView; } private CanRefresh getFooterInterface() { return (CanRefresh) mFooterView; } /** * ? * * @return boolean */ protected boolean canChildScrollUp() { if (mIsCoo) { if (mIsViewPager) { int current = mViewPager.getCurrentItem(); if (current < mViewPager.getChildCount()) { PagerAdapter adapter = mViewPager.getAdapter(); if (adapter instanceof FragmentPagerAdapter) { FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) adapter; Fragment fragment = fragmentPagerAdapter.getItem(current); if (fragment != null) { mScrollView = fragment.getView(); } } else { mScrollView = mViewPager.getChildAt(current); } } } if (mScrollView == null) { return false; } return !isDependentOpen || canScrollUp(mScrollView); } return canScrollUp(mContentView); } private boolean canScrollUp(View view) { if (android.os.Build.VERSION.SDK_INT < 14) { if (view instanceof AbsListView) { final AbsListView absListView = (AbsListView) view; return absListView.getChildCount() > 0 && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0).getTop() < absListView.getPaddingTop()); } else { return ViewCompat.canScrollVertically(view, -1) || view.getScrollY() > 0; } } else { return ViewCompat.canScrollVertically(view, -1); } } /** * ? * * @return boolean */ protected boolean canChildScrollDown() { if (mIsCoo) { if (mIsViewPager) { int current = mViewPager.getCurrentItem(); if (current < mViewPager.getChildCount()) { PagerAdapter adapter = mViewPager.getAdapter(); if (adapter instanceof FragmentPagerAdapter) { FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) adapter; Fragment fragment = fragmentPagerAdapter.getItem(current); if (fragment != null) { mScrollView = fragment.getView(); } } else { mScrollView = mViewPager.getChildAt(current); } } } if (mScrollView == null) { return false; } return isDependentOpen || canScrollDown(mScrollView); } return canScrollDown(mContentView); } private boolean canScrollDown(View view) { if (android.os.Build.VERSION.SDK_INT < 14) { if (view instanceof AbsListView) { final AbsListView absListView = (AbsListView) view; return absListView.getChildCount() > 0 && (absListView.getLastVisiblePosition() < absListView.getChildCount() - 1 || absListView.getChildAt(absListView.getChildCount() - 1).getBottom() > absListView .getPaddingBottom()); } else { return ViewCompat.canScrollVertically(view, 1) || view.getScrollY() < 0; } } else { return ViewCompat.canScrollVertically(view, 1); } } /** * ? */ public interface OnLoadMoreListener { void onLoadMore(); } /** * ? */ public interface OnRefreshListener { void onRefresh(); } /** * ? */ public interface OnStartUpListener { void onUp(); void onReset(); } /** * ? */ public interface OnStartDownListener { void onDown(); void onReset(); } }