package com.example.administrator.cookman.ui.component.twinklingrefreshlayout;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import com.example.administrator.cookman.R;
import com.example.administrator.cookman.ui.component.twinklingrefreshlayout.Footer.BottomProgressView;
import com.example.administrator.cookman.ui.component.twinklingrefreshlayout.header.GoogleDotView;
import com.example.administrator.cookman.ui.component.twinklingrefreshlayout.utils.DensityUtil;
import com.example.administrator.cookman.ui.component.twinklingrefreshlayout.utils.ScrollingUtil;
/**
* Created by lcodecore on 16/3/2.
*/
public class TwinklingRefreshLayout extends RelativeLayout {
//波浪的高度,最大扩展高度
protected float mWaveHeight;
//头部的高度
protected float mHeadHeight;
//允许的越界回弹的高度
protected float mOverScrollHeight;
//子控件
private View mChildView;
//头部layout
protected FrameLayout mHeadLayout;
//整个头部
private FrameLayout mExtraHeadLayout;
//附加顶部高度
private int mExHeadHeight = 0;
private IHeaderView mHeadView;
private IBottomView mBottomView;
//底部高度
private float mBottomHeight;
//底部layout
private FrameLayout mBottomLayout;
//是否刷新视图可见
protected boolean isRefreshVisible = false;
//是否加载更多视图可见
protected boolean isLoadingVisible = false;
//是否需要加载更多,默认需要
protected boolean enableLoadmore = true;
//是否需要下拉刷新,默认需要
protected boolean enableRefresh = true;
//是否在越界回弹的时候显示下拉图标
protected boolean isOverScrollTopShow = true;
//是否在越界回弹的时候显示上拉图标
protected boolean isOverScrollBottomShow = true;
//是否隐藏刷新控件,开启越界回弹模式(开启之后刷新控件将隐藏)
protected boolean isPureScrollModeOn = false;
//是否自动加载更多
protected boolean autoLoadMore = false;
//是否开启悬浮刷新模式
protected boolean floatRefresh = false;
//是否允许进入越界回弹模式
protected boolean enableOverScroll = true;
private CoProcessor cp;
public TwinklingRefreshLayout(Context context) {
this(context, null, 0);
}
public TwinklingRefreshLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TwinklingRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TwinklingRefreshLayout, defStyleAttr, 0);
mWaveHeight = a.getDimensionPixelSize(R.styleable.TwinklingRefreshLayout_tr_wave_height, (int) DensityUtil.dp2px(context, 120));
mHeadHeight = a.getDimensionPixelSize(R.styleable.TwinklingRefreshLayout_tr_head_height, (int) DensityUtil.dp2px(context, 80));
mBottomHeight = a.getDimensionPixelSize(R.styleable.TwinklingRefreshLayout_tr_bottom_height, (int) DensityUtil.dp2px(context, 60));
mOverScrollHeight = a.getDimensionPixelSize(R.styleable.TwinklingRefreshLayout_tr_overscroll_height, (int) mHeadHeight);
enableLoadmore = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_enable_loadmore, true);
isPureScrollModeOn = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_pureScrollMode_on, false);
isOverScrollTopShow = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_overscroll_top_show, true);
isOverScrollBottomShow = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_overscroll_bottom_show, true);
enableOverScroll = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_enable_overscroll, true);
a.recycle();
cp = new CoProcessor();
}
private void init() {
//使用isInEditMode解决可视化编辑器无法识别自定义控件的问题
if (isInEditMode()) return;
setPullListener(new TwinklingPullListener());
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
//添加头部
if (mHeadLayout == null) {
FrameLayout headViewLayout = new FrameLayout(getContext());
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, 0);
layoutParams.addRule(ALIGN_PARENT_TOP);
layoutParams.addRule(CENTER_VERTICAL);
FrameLayout extraHeadLayout = new FrameLayout(getContext());
extraHeadLayout.setId(R.id.ex_header);
LayoutParams layoutParams2 = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
this.addView(extraHeadLayout, layoutParams2);
this.addView(headViewLayout, layoutParams);
mExtraHeadLayout = extraHeadLayout;
mHeadLayout = headViewLayout;
if (mHeadView == null) setHeaderView(new GoogleDotView(getContext()));
}
//添加底部
if (mBottomLayout == null) {
FrameLayout bottomViewLayout = new FrameLayout(getContext());
LayoutParams layoutParams2 = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
layoutParams2.addRule(ALIGN_PARENT_BOTTOM);
layoutParams2.addRule(CENTER_VERTICAL);
bottomViewLayout.setLayoutParams(layoutParams2);
mBottomLayout = bottomViewLayout;
this.addView(mBottomLayout);
if (mBottomView == null) {
BottomProgressView mProgressView = new BottomProgressView(getContext());
setBottomView(mProgressView);
}
}
//获得子控件
mChildView = getChildAt(0);
cp.init();
}
/************************************* 触摸事件处理 *****************************************/
/**
* 拦截事件
*
* @return return true时,ViewGroup的事件有效,执行onTouchEvent事件
* return false时,事件向下传递,onTouchEvent无效
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercept = cp.interceptTouchEvent(ev);
return intercept || super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent e) {
boolean consume = cp.consumeTouchEvent(e);
return consume || super.onTouchEvent(e);
}
/*************************************
* 开放api区
*****************************************/
//主动刷新
public void startRefresh() {
cp.startRefresh();
}
//主动加载跟多
public void startLoadMore() {
cp.startLoadMore();
}
/**
* 刷新结束
*/
public void finishRefreshing() {
cp.finishRefreshing();
}
/**
* 加载更多结束
*/
public void finishLoadmore() {
cp.finishLoadmore();
}
/**
* 设置头部View
*/
public void setHeaderView(final IHeaderView headerView) {
if (headerView != null) {
post(new Runnable() {
@Override
public void run() {
mHeadLayout.removeAllViewsInLayout();
mHeadLayout.addView(headerView.getView());
}
});
mHeadView = headerView;
}
}
/**
* 设置固定在顶部的header
*/
public void addFixedExHeader(final View view) {
post(new Runnable() {
@Override
public void run() {
if (view != null && mExtraHeadLayout != null) {
mExtraHeadLayout.addView(view);
cp.onAddExHead();
cp.setExHeadFixed();
}
}
});
}
/**TODO 适配可以随界面滚动的Header
public void addNormalExHeader(View view) {
if (view != null && mExtraHeadLayout != null) {
mExtraHeadLayout.addView(view);
cp.onAddExHead();
cp.setExHeadNormal();
}
}
**/
/**
* 获取额外附加的头部
*/
public View getExtraHeaderView() {
return mExtraHeadLayout;
}
/**
* 设置底部View
*/
public void setBottomView(final IBottomView bottomView) {
if (bottomView != null) {
post(new Runnable() {
@Override
public void run() {
mBottomLayout.removeAllViewsInLayout();
mBottomLayout.addView(bottomView.getView());
}
});
mBottomView = bottomView;
}
}
public void setFloatRefresh(boolean ifOpenFloatRefreshMode) {
floatRefresh = ifOpenFloatRefreshMode;
}
/**
* 设置wave的下拉高度
*
* @param waveHeightDp
*/
public void setWaveHeight(float waveHeightDp) {
this.mWaveHeight = DensityUtil.dp2px(getContext(), waveHeightDp);
}
/**
* 设置下拉头的高度
*/
public void setHeaderHeight(float headHeightDp) {
this.mHeadHeight = DensityUtil.dp2px(getContext(), headHeightDp);
}
/**
* 设置底部高度
*/
public void setBottomHeight(float bottomHeightDp) {
this.mBottomHeight = DensityUtil.dp2px(getContext(), bottomHeightDp);
}
/**
* 是否允许加载更多
*/
public void setEnableLoadmore(boolean enableLoadmore1) {
enableLoadmore = enableLoadmore1;
if (mBottomView != null) {
if (enableLoadmore) mBottomView.getView().setVisibility(VISIBLE);
else mBottomView.getView().setVisibility(GONE);
}
}
/**
* 是否允许下拉刷新
*/
public void setEnableRefresh(boolean enableRefresh1) {
this.enableRefresh = enableRefresh1;
}
/**
* 是否允许越界时显示刷新控件
*/
public void setOverScrollTopShow(boolean isOverScrollTopShow) {
this.isOverScrollTopShow = isOverScrollTopShow;
}
public void setOverScrollBottomShow(boolean isOverScrollBottomShow) {
this.isOverScrollBottomShow = isOverScrollBottomShow;
}
public void setOverScrollRefreshShow(boolean isOverScrollRefreshShow) {
this.isOverScrollTopShow = isOverScrollRefreshShow;
this.isOverScrollBottomShow = isOverScrollRefreshShow;
}
/**
* 是否允许开启越界回弹模式
*/
public void setEnableOverScroll(boolean enableOverScroll1) {
this.enableOverScroll = enableOverScroll1;
}
/**
* 是否开启纯净的越界回弹模式,开启时刷新和加载更多控件不显示
*/
public void setPureScrollModeOn(boolean pureScrollModeOn) {
isPureScrollModeOn = pureScrollModeOn;
if (pureScrollModeOn) {
isOverScrollTopShow = false;
isOverScrollBottomShow = false;
setWaveHeight(mOverScrollHeight);
setHeaderHeight(mOverScrollHeight);
setBottomHeight(mOverScrollHeight);
}
}
/**
* 设置越界高度
*/
public void setOverScrollHeight(float overScrollHeightDp) {
this.mOverScrollHeight = DensityUtil.dp2px(getContext(), overScrollHeightDp);
}
/**
* 设置OverScroll时自动加载更多
*
* @param ifAutoLoadMore 为true表示底部越界时主动进入加载跟多模式,否则直接回弹
*/
public void setAutoLoadMore(boolean ifAutoLoadMore) {
autoLoadMore = ifAutoLoadMore;
}
/**
* 设置刷新控件监听器
*/
private RefreshListenerAdapter refreshListener;
public void setOnRefreshListener(RefreshListenerAdapter refreshListener) {
if (refreshListener != null) {
this.refreshListener = refreshListener;
}
}
//设置拖动屏幕的监听器
private PullListener pullListener;
private void setPullListener(PullListener pullListener) {
this.pullListener = pullListener;
}
private class TwinklingPullListener implements PullListener {
@Override
public void onPullingDown(TwinklingRefreshLayout refreshLayout, float fraction) {
mHeadView.onPullingDown(fraction, mWaveHeight, mHeadHeight);
if (refreshListener != null) refreshListener.onPullingDown(refreshLayout, fraction);
}
@Override
public void onPullingUp(TwinklingRefreshLayout refreshLayout, float fraction) {
mBottomView.onPullingUp(fraction, mWaveHeight, mHeadHeight);
if (refreshListener != null) refreshListener.onPullingUp(refreshLayout, fraction);
}
@Override
public void onPullDownReleasing(TwinklingRefreshLayout refreshLayout, float fraction) {
mHeadView.onPullReleasing(fraction, mWaveHeight, mHeadHeight);
if (refreshListener != null)
refreshListener.onPullDownReleasing(refreshLayout, fraction);
}
@Override
public void onPullUpReleasing(TwinklingRefreshLayout refreshLayout, float fraction) {
mBottomView.onPullReleasing(fraction, mWaveHeight, mHeadHeight);
if (refreshListener != null) refreshListener.onPullUpReleasing(refreshLayout, fraction);
}
@Override
public void onRefresh(TwinklingRefreshLayout refreshLayout) {
mHeadView.startAnim(mWaveHeight, mHeadHeight);
if (refreshListener != null) refreshListener.onRefresh(refreshLayout);
}
@Override
public void onLoadMore(TwinklingRefreshLayout refreshLayout) {
mBottomView.startAnim(mWaveHeight, mHeadHeight);
if (refreshListener != null) refreshListener.onLoadMore(refreshLayout);
}
@Override
public void onFinishRefresh() {
if (!isRefreshVisible) return;
mHeadView.onFinish(new OnAnimEndListener() {
@Override
public void onAnimEnd() {
cp.finishRefreshAfterAnim();
}
});
}
@Override
public void onFinishLoadMore() {
if (!isLoadingVisible) return;
mBottomView.onFinish();
}
@Override
public void onRefreshCanceled() {
if (refreshListener != null) refreshListener.onRefreshCanceled();
}
@Override
public void onLoadmoreCanceled() {
if (refreshListener != null) refreshListener.onLoadmoreCanceled();
}
}
public class CoProcessor {
private RefreshProcessor refreshProcessor;
private OverScrollProcessor overScrollProcessor;
private AnimProcessor animProcessor;
private final static int PULLING_TOP_DOWN = 0;
private final static int PULLING_BOTTOM_UP = 1;
private int state = PULLING_TOP_DOWN;
private static final int EX_MODE_NORMAL = 0;
private static final int EX_MODE_FIXED = 1;
private int exHeadMode = EX_MODE_NORMAL;
public CoProcessor() {
animProcessor = new AnimProcessor(this);
overScrollProcessor = new OverScrollProcessor(this);
refreshProcessor = new RefreshProcessor(this);
}
public void init() {
if (isPureScrollModeOn) {
setOverScrollTopShow(false);
setOverScrollBottomShow(false);
if (mHeadLayout != null) mHeadLayout.setVisibility(GONE);
if (mBottomLayout != null) mBottomLayout.setVisibility(GONE);
}
overScrollProcessor.init();
animProcessor.init();
}
public AnimProcessor getAnimProcessor() {
return animProcessor;
}
public float getMaxHeadHeight() {
return mWaveHeight;
}
public int getHeadHeight() {
return (int) mHeadHeight;
}
public int getExtraHeadHeight() {
return mExtraHeadLayout.getHeight();
}
public int getBottomHeight() {
return (int) mBottomHeight;
}
public int getOsHeight() {
return (int) mOverScrollHeight;
}
public View getScrollableView() {
return mChildView;
}
public View getContent() {
return mChildView;
}
public View getHeader() {
return mHeadLayout;
}
public View getFooter() {
return mBottomLayout;
}
public Context getContext() {
return TwinklingRefreshLayout.this.getContext();
}
public int getTouchSlop() {
return ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
public boolean interceptTouchEvent(MotionEvent ev) {
return refreshProcessor.interceptTouchEvent(ev);
}
public boolean consumeTouchEvent(MotionEvent ev) {
return refreshProcessor.consumeTouchEvent(ev);
}
/**
* 在越界时阻止再次进入这个状态而导致动画闪烁。 Prevent entering the overscroll-mode again on animating.
*/
private boolean isOverScrollTopLocked = false;
public void lockOsTop() {
isOverScrollTopLocked = true;
}
public void releaseOsTopLock() {
isOverScrollTopLocked = false;
}
public boolean isOsTopLocked() {
return isOverScrollTopLocked;
}
private boolean isOverScrollBottomLocked = false;
public void lockOsBottom() {
isOverScrollBottomLocked = true;
}
public void releaseOsBottomLock() {
isOverScrollBottomLocked = false;
}
public boolean isOsBottomLocked() {
return isOverScrollBottomLocked;
}
public void resetHeaderView() {
if (mHeadView != null) mHeadView.reset();
}
public void resetBottomView() {
if (mBottomView != null) mBottomView.reset();
}
/**
* 在添加附加Header前锁住,阻止一些额外的位移动画
*/
private boolean isExHeadLocked = true;
public boolean isExHeadLocked() {
return isExHeadLocked;
}
private void unlockExHead() {
isExHeadLocked = false;
}
public View getExHead() {
return mExtraHeadLayout;
}
public void setExHeadNormal() {
exHeadMode = EX_MODE_NORMAL;
}
public void setExHeadFixed() {
exHeadMode = EX_MODE_FIXED;
}
public boolean isExHeadNormal() {
return exHeadMode == EX_MODE_NORMAL;
}
public boolean isExHeadFixed() {
return exHeadMode == EX_MODE_FIXED;
}
//添加了额外头部时触发
public void onAddExHead() {
unlockExHead();
LayoutParams params = (LayoutParams) mChildView.getLayoutParams();
params.addRule(BELOW, mExtraHeadLayout.getId());
mChildView.setLayoutParams(params);
requestLayout();
}
/**
* 主动刷新、加载更多、结束
*/
public void startRefresh() {
post(new Runnable() {
@Override
public void run() {
setStatePTD();
if (!isPureScrollModeOn && mChildView != null) {
setRefreshing(true);
animProcessor.animHeadToRefresh();
}
}
});
}
public void startLoadMore() {
post(new Runnable() {
@Override
public void run() {
setStatePBU();
if (!isPureScrollModeOn && mChildView != null) {
setLoadingMore(true);
animProcessor.animBottomToLoad();
}
}
});
}
public void finishRefreshing() {
onFinishRefresh();
}
public void finishRefreshAfterAnim() {
if (isRefreshVisible() && mChildView != null) {
setRefreshing(false);
animProcessor.animHeadBack();
}
}
public void finishLoadmore() {
onFinishLoadMore();
if (isLoadingVisible() && mChildView != null) {
ScrollingUtil.scrollAViewBy(mChildView, (int) mBottomHeight);
setLoadingMore(false);
animProcessor.animBottomBack();
}
}
//TODO 支持分别设置头部或者顶部允许越界
private boolean enableOverScrollTop = false, enableOverScrollBottom = false;
public boolean enableOverScroll() {
return enableOverScroll;
}
public boolean allowPullDown() {
return enableRefresh || enableOverScrollTop;
}
public boolean allowPullUp() {
return enableLoadmore || enableOverScrollBottom;
}
public boolean allowOverScroll() {
return (!isRefreshVisible && !isLoadingVisible);
}
public boolean isRefreshVisible() {
return isRefreshVisible;
}
public boolean isLoadingVisible() {
return isLoadingVisible;
}
public void setRefreshing(boolean refreshing) {
isRefreshVisible = refreshing;
}
public void setLoadingMore(boolean loadingMore) {
isLoadingVisible = loadingMore;
}
public boolean isOpenFloatRefresh() {
return floatRefresh;
}
public boolean autoLoadMore() {
return autoLoadMore;
}
public boolean isPureScrollModeOn() {
return isPureScrollModeOn;
}
public boolean isOverScrollTopShow() {
return isOverScrollTopShow;
}
public boolean isOverScrollBottomShow() {
return isOverScrollBottomShow;
}
public void onPullingDown(float offsetY) {
pullListener.onPullingDown(TwinklingRefreshLayout.this, offsetY / mHeadHeight);
}
public void onPullingUp(float offsetY) {
pullListener.onPullingUp(TwinklingRefreshLayout.this, offsetY / mBottomHeight);
}
public void onRefresh() {
pullListener.onRefresh(TwinklingRefreshLayout.this);
}
public void onLoadMore() {
pullListener.onLoadMore(TwinklingRefreshLayout.this);
}
public void onFinishRefresh() {
pullListener.onFinishRefresh();
}
public void onFinishLoadMore() {
pullListener.onFinishLoadMore();
}
public void onPullDownReleasing(float offsetY) {
pullListener.onPullDownReleasing(TwinklingRefreshLayout.this, offsetY / mHeadHeight);
}
public void onPullUpReleasing(float offsetY) {
pullListener.onPullUpReleasing(TwinklingRefreshLayout.this, offsetY / mBottomHeight);
}
public void onRefreshCanceled(){
pullListener.onRefreshCanceled();
}
public void onLoadmoreCanceled(){
pullListener.onLoadmoreCanceled();
}
public void setStatePTD() {
state = PULLING_TOP_DOWN;
}
public void setStatePBU() {
state = PULLING_BOTTOM_UP;
}
public boolean isStatePTD() {
return PULLING_TOP_DOWN == state;
}
public boolean isStatePBU() {
return PULLING_BOTTOM_UP == state;
}
}
}