package com.cheikh.lazywaimai.widget.refresh; import android.content.Context; import android.os.Build; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPropertyAnimatorCompat; import android.support.v4.view.ViewPropertyAnimatorUpdateListener; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; import android.widget.AbsListView; import android.widget.FrameLayout; import android.widget.ScrollView; public class RefreshLayout extends FrameLayout { private static final float OFFSET_RADIO = 1.2f; private View mChildView; private View mScrollableView; private BaseRefreshHeaderView mHeaderView; private float mHeaderHeight; private float mTouchY; private int mTouchSlop; private boolean mIsOverlay; private int mMaxOffset; private boolean mIsRefreshing; private OnRefreshListener mOnRefreshListener; public RefreshLayout(Context context) { this(context, null, 0); } public RefreshLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public RefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mHeaderView = new DefaultRefreshHeaderView(getContext()); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (getChildCount() > 1) { throw new RuntimeException("can only have one child view"); } mChildView = getChildAt(0); if (mChildView == null) { throw new RuntimeException("must be have one child view"); } if (isScrollableView(mChildView)) { mScrollableView = mChildView; } else { mScrollableView = findScrollableChildView(mChildView); } if (mScrollableView == null) { mScrollableView = mChildView; } initHeaderView(); } /** * 初始化刷新的顶部视图 */ private void initHeaderView() { // 初始化配置 HeaderConfig headerConfig = mHeaderView.getConfig(); mIsOverlay = headerConfig.isOverlay; mMaxOffset = headerConfig.maxOffset; // 提前测量刷新视图的宽高 measureView(mHeaderView); int height = mHeaderView.getMeasuredHeight(); if (height > 0) { mHeaderHeight = height; } else { throw new RuntimeException("the height of the header view is 0!"); } // 设置layout params LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, (int) mHeaderHeight); layoutParams.gravity = Gravity.TOP; mHeaderView.setLayoutParams(layoutParams); ViewCompat.setTranslationY(mHeaderView, -mHeaderHeight); addView(mHeaderView); } /** * 测量视图的宽高 * @param view */ private void measureView(View view) { int w = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); int h = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); view.measure(w, h); } /** * 是否是可滚动的View * @return */ private boolean isScrollableView(View view) { return view instanceof AbsListView || view instanceof ScrollView || view instanceof RecyclerView; } /** * 查找可滚动的子View * @param root * @return */ private View findScrollableChildView(View root) { if (root == null) { return null; } if (root instanceof ViewGroup) { ViewGroup group = (ViewGroup) root; View view = null; for (int i = 0; i < group.getChildCount(); i++) { view = group.getChildAt(i); if (isScrollableView(view)) { return view; } else { view = findScrollableChildView(view); if (view != null) { return view; } } } } return null; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mTouchY = ev.getY(); break; case MotionEvent.ACTION_MOVE: float dy = ev.getY() - mTouchY; if (!mIsRefreshing && dy > mTouchSlop && !canChildScrollUp()) { boolean enablePull = false; if (mOnRefreshListener != null) { enablePull = mOnRefreshListener.enableRefresh(); } if (enablePull) { mHeaderView.onBegin(); } return enablePull; } break; default: // nothing to do break; } return super.onInterceptTouchEvent(ev); } /** * 能否继续往上滑动(是否到达顶部) */ public boolean canChildScrollUp() { if (Build.VERSION.SDK_INT < 14) { if (mScrollableView instanceof AbsListView) { final AbsListView absListView = (AbsListView) mScrollableView; return absListView.getChildCount() > 0 && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) .getTop() < absListView.getPaddingTop()); } else { return ViewCompat.canScrollVertically(mScrollableView, -1) || mScrollableView.getScrollY() > 0; } } else { return ViewCompat.canScrollVertically(mScrollableView, -1); } } @Override public boolean onTouchEvent(MotionEvent e) { switch (e.getAction()) { case MotionEvent.ACTION_MOVE: float dy = e.getY() - mTouchY; dy = Math.max(0, dy); // 按照比例缩减滑动的有效距离 float offsetY = dy / OFFSET_RADIO; offsetY = Math.min(offsetY, mMaxOffset); // 下拉刷新视图的可见度 float fraction = offsetY / mHeaderHeight; fraction = fraction < 1 ? fraction : 1; // 逐渐显示下拉刷新的视图 ViewCompat.setTranslationY(mHeaderView, (int) (offsetY - mHeaderHeight)); // 如果不是覆盖模式,则将列表视图向下平移以留出空位来显示下拉刷新的视图 if (!mIsOverlay) { LayoutParams lp = (LayoutParams) mChildView.getLayoutParams(); lp.topMargin = (int) offsetY; mChildView.requestLayout(); } if (offsetY >= mHeaderHeight) { mHeaderView.onRelease(); } else { mHeaderView.onPull(fraction); } return true; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (mIsOverlay) { if (ViewCompat.getTranslationY(mHeaderView) >= 0) { viewAnimateTranslationY(mHeaderView, 0); startRefresh(); } else { viewAnimateTranslationY(mHeaderView, -mHeaderHeight); } } else { if (ViewCompat.getTranslationY(mHeaderView) >= 0) { viewAnimateTranslationY(mHeaderView, 0, mChildView); startRefresh(); } else { viewAnimateTranslationY(mHeaderView, -mHeaderHeight, mChildView); } } return true; default: // nothing to do break; } return super.onTouchEvent(e); } /** * 使用动画的方式将视图在 Y 轴上平移 * @param v * @param y */ private void viewAnimateTranslationY(final View v, final float y) { ViewPropertyAnimatorCompat viewPropertyAnimatorCompat = ViewCompat.animate(v); viewPropertyAnimatorCompat.setDuration(250); viewPropertyAnimatorCompat.setInterpolator(new DecelerateInterpolator()); viewPropertyAnimatorCompat.translationY(y); viewPropertyAnimatorCompat.start(); } /** * 使用动画的方式将视图在 Y 轴上平移 * @param v * @param y */ private void viewAnimateTranslationY(final View v, final float y, final View childView) { ViewPropertyAnimatorCompat viewPropertyAnimatorCompat = ViewCompat.animate(v); viewPropertyAnimatorCompat.setDuration(250); viewPropertyAnimatorCompat.setInterpolator(new DecelerateInterpolator()); viewPropertyAnimatorCompat.translationY(y); viewPropertyAnimatorCompat.start(); viewPropertyAnimatorCompat.setUpdateListener(new ViewPropertyAnimatorUpdateListener() { @Override public void onAnimationUpdate(View view) { float translationY = ViewCompat.getTranslationY(v); LayoutParams lp = (LayoutParams) childView.getLayoutParams(); lp.topMargin = (int) (mHeaderHeight + translationY); childView.requestLayout(); } }); } /** * 开始刷新 */ private void startRefresh() { mIsRefreshing = true; mHeaderView.onRefreshing(); if (mOnRefreshListener != null) { mOnRefreshListener.onRefresh(); } } /** * 是否正在刷新 * @return */ public boolean isRefreshing() { return mIsRefreshing; } /** * 设置是否是刷新状态 * @param refreshing */ public void setRefreshing(boolean refreshing) { if (mIsRefreshing == refreshing) { return; } mIsRefreshing = refreshing; if (refreshing) { if (mIsOverlay) { viewAnimateTranslationY(mHeaderView, 0); } else { viewAnimateTranslationY(mHeaderView, 0, mChildView); } mHeaderView.onRefreshing(); } else { if (mIsOverlay) { viewAnimateTranslationY(mHeaderView, -mHeaderHeight); } else { viewAnimateTranslationY(mHeaderView, -mHeaderHeight, mChildView); } mHeaderView.onComplete(); } } /** * 自动刷新 */ public void autoRefresh() { if (mIsRefreshing) { return; } if (mIsOverlay) { viewAnimateTranslationY(mHeaderView, 0); } else { viewAnimateTranslationY(mHeaderView, 0, mChildView); } startRefresh(); } /** * 设置自定义的下拉刷新视图 * @param headerView */ public void setRefreshHeaderView(BaseRefreshHeaderView headerView) { mHeaderView = headerView; } /** * 获取下拉刷新视图 */ public BaseRefreshHeaderView getRefreshHeaderView() { return mHeaderView; } /** * 设置下拉刷新视图是否为覆盖模式 * @param isOverlay */ public void setIsOverlay(boolean isOverlay) { mIsOverlay = isOverlay; } /** * 设置刷新的监听器 * @param onRefreshListener */ public void setOnRefreshListener(OnRefreshListener onRefreshListener) { mOnRefreshListener = onRefreshListener; } }