package com.cjj; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.os.Build; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPropertyAnimatorCompat; import android.support.v4.view.ViewPropertyAnimatorUpdateListener; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; import android.widget.AbsListView; import android.widget.FrameLayout; public class MaterialRefreshLayout extends FrameLayout { public static final String Tag = MaterialRefreshLayout.class.getSimpleName(); private final static int DEFAULT_WAVE_HEIGHT = 140; private final static int HIGHER_WAVE_HEIGHT = 180; private final static int DEFAULT_HEAD_HEIGHT = 70; private final static int hIGHER_HEAD_HEIGHT = 100; private final static int DEFAULT_PROGRESS_SIZE = 40; private final static int BIG_PROGRESS_SIZE = 50; private final static int PROGRESS_STOKE_WIDTH = 3; private MaterialHeaderView mMaterialHeaderView; private MaterialFooterView mMaterialFooterView; private SunLayout mSunLayout; private boolean isOverlay; private int waveType; private int waveColor; protected float mWaveHeight; protected float mHeadHeight; private View mChildView; protected boolean isRefreshing; private float mTouchY; private float mCurrentY; private DecelerateInterpolator decelerateInterpolator; private float headHeight; private float waveHeight; private int[] colorSchemeColors; private int colorsId; private int progressTextColor; private int progressValue, progressMax; private boolean showArrow = true; private int textType; private MaterialRefreshListener refreshListener; private boolean showProgressBg; private int progressBg; private boolean isShowWave; private int progressSizeType; private int progressSize = 0; private boolean isLoadMoreing; private boolean isLoadMore; private boolean isSunStyle = false; public MaterialRefreshLayout(Context context) { this(context, null, 0); } public MaterialRefreshLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MaterialRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr); } private void init(Context context, AttributeSet attrs, int defstyleAttr) { if (isInEditMode()) { return; } if (getChildCount() > 1) { throw new RuntimeException("can only have one child widget"); } decelerateInterpolator = new DecelerateInterpolator(10); TypedArray t = context.obtainStyledAttributes(attrs, R.styleable.MaterialRefreshLayout, defstyleAttr, 0); isOverlay = t.getBoolean(R.styleable.MaterialRefreshLayout_overlay, false); /**attrs for materialWaveView*/ waveType = t.getInt(R.styleable.MaterialRefreshLayout_wave_height_type, 0); if (waveType == 0) { headHeight = DEFAULT_HEAD_HEIGHT; waveHeight = DEFAULT_WAVE_HEIGHT; MaterialWaveView.DefaulHeadHeight = DEFAULT_HEAD_HEIGHT; MaterialWaveView.DefaulWaveHeight = DEFAULT_WAVE_HEIGHT; } else { headHeight = hIGHER_HEAD_HEIGHT; waveHeight = HIGHER_WAVE_HEIGHT; MaterialWaveView.DefaulHeadHeight = hIGHER_HEAD_HEIGHT; MaterialWaveView.DefaulWaveHeight = HIGHER_WAVE_HEIGHT; } waveColor = t.getColor(R.styleable.MaterialRefreshLayout_wave_color, Color.WHITE); isShowWave = t.getBoolean(R.styleable.MaterialRefreshLayout_wave_show, true); /**attrs for circleprogressbar*/ colorsId = t.getResourceId(R.styleable.MaterialRefreshLayout_progress_colors, R.array.material_colors); colorSchemeColors = context.getResources().getIntArray(colorsId); showArrow = t.getBoolean(R.styleable.MaterialRefreshLayout_progress_show_arrow, true); textType = t.getInt(R.styleable.MaterialRefreshLayout_progress_text_visibility, 1); progressTextColor = t.getColor(R.styleable.MaterialRefreshLayout_progress_text_color, Color.BLACK); progressValue = t.getInteger(R.styleable.MaterialRefreshLayout_progress_value, 0); progressMax = t.getInteger(R.styleable.MaterialRefreshLayout_progress_max_value, 100); showProgressBg = t.getBoolean(R.styleable.MaterialRefreshLayout_progress_show_circle_backgroud, true); progressBg = t.getColor(R.styleable.MaterialRefreshLayout_progress_backgroud_color, CircleProgressBar.DEFAULT_CIRCLE_BG_LIGHT); progressSizeType = t.getInt(R.styleable.MaterialRefreshLayout_progress_size_type, 0); if (progressSizeType == 0) { progressSize = DEFAULT_PROGRESS_SIZE; } else { progressSize = BIG_PROGRESS_SIZE; } isLoadMore = t.getBoolean(R.styleable.MaterialRefreshLayout_isLoadMore, false); t.recycle(); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); Log.i(Tag, "onAttachedToWindow"); Context context = getContext(); mChildView = getChildAt(0); if (mChildView == null) { return; } setWaveHeight(Util.dip2px(context, waveHeight)); setHeaderHeight(Util.dip2px(context, headHeight)); if (isSunStyle) { mSunLayout = new SunLayout(context); LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Util.dip2px(context, hIGHER_HEAD_HEIGHT)); layoutParams.gravity = Gravity.TOP; mSunLayout.setVisibility(View.GONE); setHeaderView(mSunLayout); } else { mMaterialHeaderView = new MaterialHeaderView(context); LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Util.dip2px(context, hIGHER_HEAD_HEIGHT)); layoutParams.gravity = Gravity.TOP; mMaterialHeaderView.setLayoutParams(layoutParams); mMaterialHeaderView.setWaveColor(isShowWave ? waveColor : Color.TRANSPARENT); mMaterialHeaderView.showProgressArrow(showArrow); mMaterialHeaderView.setProgressSize(progressSize); mMaterialHeaderView.setProgressColors(colorSchemeColors); mMaterialHeaderView.setProgressStokeWidth(PROGRESS_STOKE_WIDTH); mMaterialHeaderView.setTextType(textType); mMaterialHeaderView.setProgressTextColor(progressTextColor); mMaterialHeaderView.setProgressValue(progressValue); mMaterialHeaderView.setProgressValueMax(progressMax); mMaterialHeaderView.setIsProgressBg(showProgressBg); mMaterialHeaderView.setProgressBg(progressBg); mMaterialHeaderView.setVisibility(View.GONE); setHeaderView(mMaterialHeaderView); } mMaterialFooterView = new MaterialFooterView(context); LayoutParams layoutParams2 = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Util.dip2px(context, hIGHER_HEAD_HEIGHT)); layoutParams2.gravity = Gravity.BOTTOM; mMaterialFooterView.setLayoutParams(layoutParams2); mMaterialFooterView.showProgressArrow(showArrow); mMaterialFooterView.setProgressSize(progressSize); mMaterialFooterView.setProgressColors(colorSchemeColors); mMaterialFooterView.setProgressStokeWidth(PROGRESS_STOKE_WIDTH); mMaterialFooterView.setTextType(textType); mMaterialFooterView.setProgressValue(progressValue); mMaterialFooterView.setProgressValueMax(progressMax); mMaterialFooterView.setIsProgressBg(showProgressBg); mMaterialFooterView.setProgressBg(progressBg); mMaterialFooterView.setVisibility(View.GONE); setFooderView(mMaterialFooterView); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (isRefreshing) return true; switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mTouchY = ev.getY(); mCurrentY = mTouchY; break; case MotionEvent.ACTION_MOVE: float currentY = ev.getY(); float dy = currentY - mTouchY; if (dy > 0 && !canChildScrollUp()) { if (mMaterialHeaderView != null) { mMaterialHeaderView.setVisibility(View.VISIBLE); mMaterialHeaderView.onBegin(this); } else if (mSunLayout != null) { mSunLayout.setVisibility(View.VISIBLE); mSunLayout.onBegin(this); } return true; } else if (dy < 0 && !canChildScrollDown() && isLoadMore) { if (mMaterialFooterView != null && !isLoadMoreing) { soveLoadMoreLogic(); } return super.onInterceptTouchEvent(ev); } break; } return super.onInterceptTouchEvent(ev); } private void soveLoadMoreLogic() { isLoadMoreing = true; mMaterialFooterView.setVisibility(View.VISIBLE); mMaterialFooterView.onBegin(this); mMaterialFooterView.onRefreshing(this); if (refreshListener != null) { refreshListener.onRefreshLoadMore(MaterialRefreshLayout.this); } } @Override public boolean onTouchEvent(MotionEvent e) { if (isRefreshing) { return super.onTouchEvent(e); } if(!isEnabled()) return false; switch (e.getAction()) { case MotionEvent.ACTION_MOVE: mCurrentY = e.getY(); float dy = mCurrentY - mTouchY; dy = Math.min(mWaveHeight * 2, dy); dy = Math.max(0, dy); if (mChildView != null) { float offsetY = decelerateInterpolator.getInterpolation(dy / mWaveHeight / 2) * dy / 2; float fraction = offsetY / mHeadHeight; if (mMaterialHeaderView != null) { mMaterialHeaderView.getLayoutParams().height = (int) offsetY; mMaterialHeaderView.requestLayout(); mMaterialHeaderView.onPull(this, fraction); } else if (mSunLayout != null) { mSunLayout.getLayoutParams().height = (int) offsetY; mSunLayout.requestLayout(); mSunLayout.onPull(this, fraction); } if (!isOverlay) ViewCompat.setTranslationY(mChildView, offsetY); } return true; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (mChildView != null) { if (mMaterialHeaderView != null) { if (isOverlay) { if (mMaterialHeaderView.getLayoutParams().height > mHeadHeight) { updateListener(); mMaterialHeaderView.getLayoutParams().height = (int) mHeadHeight; mMaterialHeaderView.requestLayout(); } else { mMaterialHeaderView.getLayoutParams().height = 0; mMaterialHeaderView.requestLayout(); } } else { if (ViewCompat.getTranslationY(mChildView) >= mHeadHeight) { createAnimatorTranslationY(mChildView, mHeadHeight, mMaterialHeaderView); updateListener(); } else { createAnimatorTranslationY(mChildView, 0, mMaterialHeaderView); } } } else if (mSunLayout != null) { if (isOverlay) { if (mSunLayout.getLayoutParams().height > mHeadHeight) { updateListener(); mSunLayout.getLayoutParams().height = (int) mHeadHeight; mSunLayout.requestLayout(); } else { mSunLayout.getLayoutParams().height = 0; mSunLayout.requestLayout(); } } else { if (ViewCompat.getTranslationY(mChildView) >= mHeadHeight) { createAnimatorTranslationY(mChildView, mHeadHeight, mSunLayout); updateListener(); } else { createAnimatorTranslationY(mChildView, 0, mSunLayout); } } } } return true; } return super.onTouchEvent(e); } public void setSunStyle(boolean isSunStyle) { this.isSunStyle = isSunStyle; } public void autoRefresh() { this.postDelayed(new Runnable() { @Override public void run() { if (!isRefreshing) { if (mMaterialHeaderView != null) { mMaterialHeaderView.setVisibility(View.VISIBLE); if (isOverlay) { mMaterialHeaderView.getLayoutParams().height = (int) mHeadHeight; mMaterialHeaderView.requestLayout(); } else { createAnimatorTranslationY(mChildView, mHeadHeight, mMaterialHeaderView); } } else if (mSunLayout != null) { mSunLayout.setVisibility(View.VISIBLE); if (isOverlay) { mSunLayout.getLayoutParams().height = (int) mHeadHeight; mSunLayout.requestLayout(); } else { createAnimatorTranslationY(mChildView, mHeadHeight, mSunLayout); } } updateListener(); } } }, 50); } public void autoRefreshLoadMore() { this.post(new Runnable() { @Override public void run() { if (isLoadMore) { soveLoadMoreLogic(); } else { throw new RuntimeException("you must setLoadMore ture"); } } }); } public void updateListener() { isRefreshing = true; if (mMaterialHeaderView != null) { mMaterialHeaderView.onRefreshing(MaterialRefreshLayout.this); } else if (mSunLayout != null) { mSunLayout.onRefreshing(MaterialRefreshLayout.this); } if (refreshListener != null) { refreshListener.onRefresh(MaterialRefreshLayout.this); } } public void setLoadMore(boolean isLoadMore) { this.isLoadMore = isLoadMore; } public void setProgressColors(int[] colors) { this.colorSchemeColors = colors; } public void setShowArrow(boolean showArrow) { this.showArrow = showArrow; } public void setShowProgressBg(boolean showProgressBg) { this.showProgressBg = showProgressBg; } public void setWaveColor(int waveColor) { this.waveColor = waveColor; } public void setWaveShow(boolean isShowWave) { this.isShowWave = isShowWave; } public void setIsOverLay(boolean isOverLay) { this.isOverlay = isOverLay; } // public void setProgressValue(int progressValue) { // this.progressValue = progressValue; // mMaterialHeaderView.setProgressValue(progressValue); // } public void createAnimatorTranslationY(final View v, final float h, final FrameLayout fl) { ViewPropertyAnimatorCompat viewPropertyAnimatorCompat = ViewCompat.animate(v); viewPropertyAnimatorCompat.setDuration(250); viewPropertyAnimatorCompat.setInterpolator(new DecelerateInterpolator()); viewPropertyAnimatorCompat.translationY(h); viewPropertyAnimatorCompat.start(); viewPropertyAnimatorCompat.setUpdateListener(new ViewPropertyAnimatorUpdateListener() { @Override public void onAnimationUpdate(View view) { float height = ViewCompat.getTranslationY(v); fl.getLayoutParams().height = (int) height; fl.requestLayout(); } }); } /** * @return Whether it is possible for the child view of this layout to * scroll up. Override this if the child view is a custom view. */ public boolean canChildScrollUp() { if (mChildView == null) { return false; } if (Build.VERSION.SDK_INT < 14) { if (mChildView instanceof AbsListView) { final AbsListView absListView = (AbsListView) mChildView; return absListView.getChildCount() > 0 && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) .getTop() < absListView.getPaddingTop()); } else { return ViewCompat.canScrollVertically(mChildView, -1) || mChildView.getScrollY() > 0; } } else { return ViewCompat.canScrollVertically(mChildView, -1); } } public boolean canChildScrollDown() { if (mChildView == null) { return false; } if (android.os.Build.VERSION.SDK_INT < 14) { if (mChildView instanceof AbsListView) { final AbsListView absListView = (AbsListView) mChildView; if (absListView.getChildCount() > 0) { int lastChildBottom = absListView.getChildAt(absListView.getChildCount() - 1).getBottom(); return absListView.getLastVisiblePosition() == absListView.getAdapter().getCount() - 1 && lastChildBottom <= absListView.getMeasuredHeight(); } else { return false; } } else { return ViewCompat.canScrollVertically(mChildView, 1) || mChildView.getScrollY() > 0; } } else { return ViewCompat.canScrollVertically(mChildView, 1); } } public void setWaveHigher() { headHeight = hIGHER_HEAD_HEIGHT; waveHeight = HIGHER_WAVE_HEIGHT; MaterialWaveView.DefaulHeadHeight = hIGHER_HEAD_HEIGHT; MaterialWaveView.DefaulWaveHeight = HIGHER_WAVE_HEIGHT; } public void finishRefreshing() { if (mChildView != null) { ViewPropertyAnimatorCompat viewPropertyAnimatorCompat = ViewCompat.animate(mChildView); viewPropertyAnimatorCompat.setDuration(200); viewPropertyAnimatorCompat.y(ViewCompat.getTranslationY(mChildView)); viewPropertyAnimatorCompat.translationY(0); viewPropertyAnimatorCompat.setInterpolator(new DecelerateInterpolator()); viewPropertyAnimatorCompat.start(); if (mMaterialHeaderView != null) { mMaterialHeaderView.onComplete(MaterialRefreshLayout.this); } else if (mSunLayout != null) { mSunLayout.onComplete(MaterialRefreshLayout.this); } if (refreshListener != null) { refreshListener.onfinish(); } } isRefreshing = false; progressValue = 0; } public void finishRefresh() { this.post(new Runnable() { @Override public void run() { finishRefreshing(); } }); } public void finishRefreshLoadMore() { this.post(new Runnable() { @Override public void run() { if (mMaterialFooterView != null && isLoadMoreing) { isLoadMoreing = false; mMaterialFooterView.onComplete(MaterialRefreshLayout.this); } } }); } private void setHeaderView(final View headerView) { addView(headerView); } public void setHeader(final View headerView) { setHeaderView(headerView); } public void setFooderView(final View fooderView) { this.addView(fooderView); } public void setWaveHeight(float waveHeight) { this.mWaveHeight = waveHeight; } public void setHeaderHeight(float headHeight) { this.mHeadHeight = headHeight; } public void setMaterialRefreshListener(MaterialRefreshListener refreshListener) { this.refreshListener = refreshListener; } }