实现方式:
- 资源准备
上拉或者下拉时出现的headerview与footerview
header_bga_dodo.xml :下拉刷新时出现的view
footer_bga_dodo.xml:上拉加载更多时出现的view
headerview或者footerview里进度条的动画:header_dodo_bga_refresh_loading.xml
attrs.xml
- 工具类准备
dp、sp 转换为 px 的工具类
/**
* dp、sp 转换为 px 的工具类
*
* @author wangtt
*
*/
public class UnitConverterTools {
/**
* 将px值转换为dip或dp值,保证尺寸大小不变
*
* @param pxValue
* (DisplayMetrics类中属性density)
* @return
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
/**
* 将dip或dp值转换为px值,保证尺寸大小不变
*
* @param dipValue
* (DisplayMetrics类中属性density)
* @return
*/
public static int dip2px(Context context, float dipValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
/**
* 将px值转换为sp值,保证文字大小不变
*
* @param pxValue
* (DisplayMetrics类中属性scaledDensity)
* @return
*/
public static int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
/**
* 将sp值转换为px值,保证文字大小不变
*
* @param spValue
* (DisplayMetrics类中属性scaledDensity)
* @return
*/
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
/*
* double保留小数点后两位
*/
public static double round(Double v, int scale) {
if (scale < 0) {
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b = null == v ? new BigDecimal("0.0") : new BigDecimal(
Double.toString(v));
BigDecimal one = new BigDecimal("1");
return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
}
BGANormalRefreshViewHolder.java
/**
* wangtt
* 15/5/21 13:05
* 类似新浪微博下拉刷新风格
*/
public class BGANormalRefreshViewHolder extends BGARefreshViewHolder {
private TextView mHeaderStatusTv;
private ImageView mHeaderArrowIv;
private ImageView mHeaderChrysanthemumIv;
private AnimationDrawable mHeaderChrysanthemumAd;
private RotateAnimation mUpAnim;
private RotateAnimation mDownAnim;
private String mPullDownRefreshText = "下拉刷新";
private String mReleaseRefreshText = "释放更新";
private String mRefreshingText = "加载中...";
/**
* @param context
* @param isLoadingMoreEnabled 上拉加载更多是否可用
*/
public BGANormalRefreshViewHolder(Context context, boolean isLoadingMoreEnabled) {
super(context, isLoadingMoreEnabled);
initAnimation();
}
private void initAnimation() {
mUpAnim = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
mUpAnim.setDuration(150);
mUpAnim.setFillAfter(true);
mDownAnim = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
mDownAnim.setFillAfter(true);
}
/**
* 设置未满足刷新条件,提示继续往下拉的文本
*
* @param pullDownRefreshText
*/
public void setPullDownRefreshText(String pullDownRefreshText) {
mPullDownRefreshText = pullDownRefreshText;
}
/**
* 设置满足刷新条件时的文本
*
* @param releaseRefreshText
*/
public void setReleaseRefreshText(String releaseRefreshText) {
mReleaseRefreshText = releaseRefreshText;
}
/**
* 设置正在刷新时的文本
*
* @param refreshingText
*/
public void setRefreshingText(String refreshingText) {
mRefreshingText = refreshingText;
}
@Override
public View getRefreshHeaderView() {
if (mRefreshHeaderView == null) {
mRefreshHeaderView = View.inflate(mContext, R.layout.view_refresh_header_normal, null);
mRefreshHeaderView.setBackgroundColor(Color.TRANSPARENT);
if (mRefreshViewBackgroundColorRes != -1) {
mRefreshHeaderView.setBackgroundResource(mRefreshViewBackgroundColorRes);
}
if (mRefreshViewBackgroundDrawableRes != -1) {
mRefreshHeaderView.setBackgroundResource(mRefreshViewBackgroundDrawableRes);
}
mHeaderStatusTv = (TextView) mRefreshHeaderView.findViewById(R.id.tv_normal_refresh_header_status);
mHeaderArrowIv = (ImageView) mRefreshHeaderView.findViewById(R.id.iv_normal_refresh_header_arrow);
mHeaderChrysanthemumIv = (ImageView) mRefreshHeaderView.findViewById(R.id.iv_normal_refresh_header_chrysanthemum);
mHeaderChrysanthemumAd = (AnimationDrawable) mHeaderChrysanthemumIv.getDrawable();
mHeaderStatusTv.setText(mPullDownRefreshText);
}
return mRefreshHeaderView;
}
@Override
public void handleScale(float scale, int moveYDistance) {
}
@Override
public void changeToIdle() {
}
@Override
public void changeToPullDown() {
mHeaderStatusTv.setText(mPullDownRefreshText);
mHeaderChrysanthemumIv.setVisibility(View.INVISIBLE);
mHeaderChrysanthemumAd.stop();
mHeaderArrowIv.setVisibility(View.VISIBLE);
mDownAnim.setDuration(150);
mHeaderArrowIv.startAnimation(mDownAnim);
}
@Override
public void changeToReleaseRefresh() {
mHeaderStatusTv.setText(mReleaseRefreshText);
mHeaderChrysanthemumIv.setVisibility(View.INVISIBLE);
mHeaderChrysanthemumAd.stop();
mHeaderArrowIv.setVisibility(View.VISIBLE);
mHeaderArrowIv.startAnimation(mUpAnim);
}
@Override
public void changeToRefreshing() {
mHeaderStatusTv.setText(mRefreshingText);
// 必须把动画清空才能隐藏成功
mHeaderArrowIv.clearAnimation();
mHeaderArrowIv.setVisibility(View.INVISIBLE);
mHeaderChrysanthemumIv.setVisibility(View.VISIBLE);
mHeaderChrysanthemumAd.start();
}
@Override
public void onEndRefreshing() {
mHeaderStatusTv.setText(mPullDownRefreshText);
mHeaderChrysanthemumIv.setVisibility(View.INVISIBLE);
mHeaderChrysanthemumAd.stop();
mHeaderArrowIv.setVisibility(View.VISIBLE);
mDownAnim.setDuration(0);
mHeaderArrowIv.startAnimation(mDownAnim);
}
}
BGARefreshLayout.java
/**
* @author wangtt
* 15/5/21 22:35
* 下拉刷新、上拉加载更多、可添加自定义(固定、可滑动)头部控件(例如慕课网app顶部的广告位)
*/
public class BGARefreshLayout extends LinearLayout {
private static final String TAG = BGARefreshLayout.class.getSimpleName();
private BGARefreshViewHolder mRefreshViewHolder;
/**
* 整个头部控件,下拉刷新控件mRefreshHeaderView和下拉刷新控件下方的自定义组件mCustomHeaderView的父控件
*/
private LinearLayout mWholeHeaderView;
/**
* 下拉刷新控件
*/
private View mRefreshHeaderView;
/**
* 下拉刷新控件下方的自定义控件
*/
private View mCustomHeaderView;
/**
* 下拉刷新控件下方的自定义控件是否可滚动,默认不可滚动
*/
private boolean mIsCustomHeaderViewScrollable = false;
/**
* 下拉刷新控件的高度
*/
private int mRefreshHeaderViewHeight;
/**
* 当前刷新状态
*/
private RefreshStatus mCurrentRefreshStatus = RefreshStatus.IDLE;
/**
* 上拉加载更多控件
*/
private View mLoadMoreFooterView;
/**
* 上拉加载更多控件的高度
*/
private int mLoadMoreFooterViewHeight;
/**
* 下拉刷新和上拉加载更多代理
*/
private BGARefreshLayoutDelegate mDelegate;
/**
* 手指按下时,y轴方向的偏移量
*/
private int mWholeHeaderDownY = -1;
/**
* 整个头部控件最小的paddingTop
*/
private int mMinWholeHeaderViewPaddingTop;
/**
* 整个头部控件最大的paddingTop
*/
private int mMaxWholeHeaderViewPaddingTop;
/**
* 是否处于正在加载更多状态
*/
private boolean mIsLoadingMore = false;
private AbsListView mAbsListView;
private ScrollView mScrollView;
private RecyclerView mRecyclerView;
private View mNormalView;
private WebView mWebView;
private BGAStickyNavLayout mStickyNavLayout;
private View mContentView;
private float mInterceptTouchDownX = -1;
private float mInterceptTouchDownY = -1;
/**
* 按下时整个头部控件的paddingTop
*/
private int mWholeHeaderViewDownPaddingTop = 0;
/**
* 记录开始下拉刷新时的downY
*/
private int mRefreshDownY = -1;
/**
* 是否已经设置内容控件滚动监听器
*/
private boolean mIsInitedContentViewScrollListener = false;
/**
* 触发上拉加载更多时是否显示加载更多控件
*/
private boolean mIsShowLoadingMoreView = true;
/**
* 下拉刷新是否可用
*/
private boolean mPullDownRefreshEnable = true;
private Handler mHandler;
private BGARefreshScaleDelegate mRefreshScaleDelegate;
private int mTouchSlop;
public BGARefreshLayout(Context context) {
this(context, null);
}
public BGARefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(LinearLayout.VERTICAL);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mHandler = new Handler(Looper.getMainLooper());
initWholeHeaderView();
}
/**
* 初始化整个头部控件
*/
private void initWholeHeaderView() {
mWholeHeaderView = new LinearLayout(getContext());
mWholeHeaderView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
mWholeHeaderView.setOrientation(LinearLayout.VERTICAL);
addView(mWholeHeaderView);
}
@Override
public void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() != 2) {
throw new RuntimeException(BGARefreshLayout.class.getSimpleName() + "必须有且只有一个子控件");
}
mContentView = getChildAt(1);
if (mContentView instanceof AbsListView) {
mAbsListView = (AbsListView) mContentView;
} else if (mContentView instanceof RecyclerView) {
mRecyclerView = (RecyclerView) mContentView;
} else if (mContentView instanceof ScrollView) {
mScrollView = (ScrollView) mContentView;
} else if (mContentView instanceof WebView) {
mWebView = (WebView) mContentView;
} else if (mContentView instanceof BGAStickyNavLayout) {
mStickyNavLayout = (BGAStickyNavLayout) mContentView;
mStickyNavLayout.setRefreshLayout(this);
}else if (mContentView instanceof FrameLayout){
FrameLayout frameLayout = (FrameLayout) mContentView;
View childView = frameLayout.getChildAt(0);
if (childView instanceof RecyclerView){
mRecyclerView = (RecyclerView) childView;
}
}else {
mNormalView = mContentView;
// 设置为可点击,否则在空白区域无法拖动
mNormalView.setClickable(true);
}
}
public void setRefreshViewHolder(BGARefreshViewHolder refreshViewHolder) {
mRefreshViewHolder = refreshViewHolder;
mRefreshViewHolder.setRefreshLayout(this);
initRefreshHeaderView();
initLoadMoreFooterView();
}
public void startChangeWholeHeaderViewPaddingTop(int distance) {
ValueAnimator animator = ValueAnimator.ofInt(mWholeHeaderView.getPaddingTop(), mWholeHeaderView.getPaddingTop() - distance);
animator.setDuration(mRefreshViewHolder.getTopAnimDuration());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int paddingTop = (int) animation.getAnimatedValue();
mWholeHeaderView.setPadding(0, paddingTop, 0, 0);
}
});
animator.start();
}
/**
* 初始化下拉刷新控件
*
* @return
*/
private void initRefreshHeaderView() {
mRefreshHeaderView = mRefreshViewHolder.getRefreshHeaderView();
if (mRefreshHeaderView != null) {
mRefreshHeaderView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
mRefreshHeaderViewHeight = mRefreshViewHolder.getRefreshHeaderViewHeight();
mMinWholeHeaderViewPaddingTop = -mRefreshHeaderViewHeight;
mMaxWholeHeaderViewPaddingTop = (int) (mRefreshHeaderViewHeight * mRefreshViewHolder.getSpringDistanceScale());
mWholeHeaderView.setPadding(0, mMinWholeHeaderViewPaddingTop, 0, 0);
mWholeHeaderView.addView(mRefreshHeaderView, 0);
}
}
/**
* 设置下拉刷新控件下方的自定义控件
*
* @param customHeaderView 下拉刷新控件下方的自定义控件
* @param scrollable 是否可以滚动
*/
public void setCustomHeaderView(View customHeaderView, boolean scrollable) {
if (mCustomHeaderView != null && mCustomHeaderView.getParent() != null) {
ViewGroup parent = (ViewGroup) mCustomHeaderView.getParent();
parent.removeView(mCustomHeaderView);
}
mCustomHeaderView = customHeaderView;
if (mCustomHeaderView != null) {
mCustomHeaderView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
mWholeHeaderView.addView(mCustomHeaderView);
mIsCustomHeaderViewScrollable = scrollable;
}
}
/**
* 初始化上拉加载更多控件
*
* @return
*/
private void initLoadMoreFooterView() {
mLoadMoreFooterView = mRefreshViewHolder.getLoadMoreFooterView();
if (mLoadMoreFooterView != null) {
// 测量上拉加载更多控件的高度
mLoadMoreFooterView.measure(0, 0);
mLoadMoreFooterViewHeight = mLoadMoreFooterView.getMeasuredHeight();
mLoadMoreFooterView.setVisibility(GONE);
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// 被添加到窗口后再设置监听器,这样开发者就不必烦恼先初始化RefreshLayout还是先设置自定义滚动监听器
if (!mIsInitedContentViewScrollListener && mLoadMoreFooterView != null) {
setRecyclerViewOnScrollListener();
setAbsListViewOnScrollListener();
addView(mLoadMoreFooterView, getChildCount());
mIsInitedContentViewScrollListener = true;
}
}
private void setRecyclerViewOnScrollListener() {
if (mRecyclerView != null) {
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if ((newState == RecyclerView.SCROLL_STATE_IDLE || newState == RecyclerView.SCROLL_STATE_SETTLING) && shouldHandleRecyclerViewLoadingMore(mRecyclerView)) {
beginLoadingMore();
}
}
});
}
}
private void setAbsListViewOnScrollListener() {
if (mAbsListView != null) {
try {
// 通过反射获取开发者自定义的滚动监听器,并将其替换成自己的滚动监听器,触发滚动时也要通知开发者自定义的滚动监听器(非侵入式,不让开发者继承特定的控件)
// mAbsListView.getClass().getDeclaredField("mOnScrollListener")获取不到mOnScrollListener,必须通过AbsListView.class.getDeclaredField("mOnScrollListener")获取
Field field = AbsListView.class.getDeclaredField("mOnScrollListener");
field.setAccessible(true);
// 开发者自定义的滚动监听器
final AbsListView.OnScrollListener onScrollListener = (AbsListView.OnScrollListener) field.get(mAbsListView);
mAbsListView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
if ((scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING) && shouldHandleAbsListViewLoadingMore(mAbsListView)) {
beginLoadingMore();
}
if (onScrollListener != null) {
onScrollListener.onScrollStateChanged(absListView, scrollState);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (onScrollListener != null) {
onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
public boolean shouldHandleAbsListViewLoadingMore(AbsListView absListView) {
if (mIsLoadingMore || mCurrentRefreshStatus == RefreshStatus.REFRESHING || mLoadMoreFooterView == null || mDelegate == null || absListView == null || absListView.getAdapter() == null || absListView.getAdapter().getCount() == 0) {
return false;
}
return BGARefreshScrollingUtil.isAbsListViewToBottom(absListView);
}
public boolean shouldHandleRecyclerViewLoadingMore(RecyclerView recyclerView) {
if (mIsLoadingMore || mCurrentRefreshStatus == RefreshStatus.REFRESHING || mLoadMoreFooterView == null || mDelegate == null || recyclerView.getAdapter() == null || recyclerView.getAdapter().getItemCount() == 0) {
return false;
}
return BGARefreshScrollingUtil.isRecyclerViewToBottom(recyclerView);
}
/**
* 是否满足处理刷新的条件
*
* @return
*/
private boolean shouldHandleLoadingMore() {
if (mIsLoadingMore || mCurrentRefreshStatus == RefreshStatus.REFRESHING || mLoadMoreFooterView == null || mDelegate == null) {
return false;
}
// 内容是普通控件,满足
if (mNormalView != null) {
return true;
}
if (BGARefreshScrollingUtil.isWebViewToBottom(mWebView)) {
return true;
}
if (BGARefreshScrollingUtil.isScrollViewToBottom(mScrollView)) {
return true;
}
if (mAbsListView != null) {
return shouldHandleAbsListViewLoadingMore(mAbsListView);
}
if (mRecyclerView != null) {
return shouldHandleRecyclerViewLoadingMore(mRecyclerView);
}
if (mStickyNavLayout != null) {
return mStickyNavLayout.shouldHandleLoadingMore();
}
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mInterceptTouchDownX = event.getRawX();
mInterceptTouchDownY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
if (!mIsLoadingMore && (mCurrentRefreshStatus != RefreshStatus.REFRESHING)) {
if (mInterceptTouchDownX == -1) {
mInterceptTouchDownX = (int) event.getRawX();
}
if (mInterceptTouchDownY == -1) {
mInterceptTouchDownY = (int) event.getRawY();
}
int interceptTouchMoveDistanceY = (int) (event.getRawY() - mInterceptTouchDownY);
// 可以没有上拉加载更多,但是必须有下拉刷新,否则就不拦截事件
if (Math.abs(event.getRawX() - mInterceptTouchDownX) < Math.abs(interceptTouchMoveDistanceY) && mRefreshHeaderView != null) {
if ((interceptTouchMoveDistanceY > mTouchSlop && shouldHandleRefresh()) || (interceptTouchMoveDistanceY < -mTouchSlop && shouldHandleLoadingMore()) || (interceptTouchMoveDistanceY < -mTouchSlop && !isWholeHeaderViewCompleteInvisible()) || (interceptTouchMoveDistanceY > mTouchSlop && shouldInterceptToMoveCustomHeaderViewDown())) {
// ACTION_DOWN时没有消耗掉事件,子控件会处于按下状态,这里设置ACTION_CANCEL,使子控件取消按下状态
event.setAction(MotionEvent.ACTION_CANCEL);
super.onInterceptTouchEvent(event);
return true;
}
}
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
// 重置
mInterceptTouchDownX = -1;
mInterceptTouchDownY = -1;
break;
}
return super.onInterceptTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mIsCustomHeaderViewScrollable && !isWholeHeaderViewCompleteInvisible()) {
super.dispatchTouchEvent(ev);
return true;
}
return super.dispatchTouchEvent(ev);
}
/**
* 是否满足处理刷新的条件
*
* @return
*/
private boolean shouldHandleRefresh() {
if (!mPullDownRefreshEnable || mIsLoadingMore || mCurrentRefreshStatus == RefreshStatus.REFRESHING || mRefreshHeaderView == null || mDelegate == null) {
return false;
}
return isContentViewToTop();
}
private boolean isContentViewToTop() {
// 内容是普通控件,满足
if (mNormalView != null) {
return true;
}
if (BGARefreshScrollingUtil.isScrollViewOrWebViewToTop(mWebView)) {
return true;
}
if (BGARefreshScrollingUtil.isScrollViewOrWebViewToTop(mScrollView)) {
return true;
}
if (BGARefreshScrollingUtil.isAbsListViewToTop(mAbsListView)) {
return true;
}
if (BGARefreshScrollingUtil.isRecyclerViewToTop(mRecyclerView)) {
return true;
}
if (BGARefreshScrollingUtil.isStickyNavLayoutToTop(mStickyNavLayout)) {
return true;
}
return false;
}
private boolean shouldInterceptToMoveCustomHeaderViewDown() {
return isContentViewToTop() && mCustomHeaderView != null && mIsCustomHeaderViewScrollable && !isCustomHeaderViewCompleteVisible();
}
private boolean shouldInterceptToMoveCustomHeaderViewUp() {
return isContentViewToTop() && mCustomHeaderView != null && mIsCustomHeaderViewScrollable && !isWholeHeaderViewCompleteInvisible();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (null != mRefreshHeaderView) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mWholeHeaderDownY = (int) event.getY();
if (mCustomHeaderView != null) {
mWholeHeaderViewDownPaddingTop = mWholeHeaderView.getPaddingTop();
}
if (mCustomHeaderView == null || !mIsCustomHeaderViewScrollable) {
mRefreshDownY = (int) event.getY();
}
if (isWholeHeaderViewCompleteInvisible()) {
mRefreshDownY = (int) event.getY();
return true;
}
break;
case MotionEvent.ACTION_MOVE:
if (handleActionMove(event)) {
return true;
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (handleActionUpOrCancel(event)) {
return true;
}
break;
default:
break;
}
}
return super.onTouchEvent(event);
}
/**
* 自定义头部控件是否已经完全显示
*
* @return true表示已经完全显示,false表示没有完全显示
*/
private boolean isCustomHeaderViewCompleteVisible() {
if (mCustomHeaderView != null) {
// 0表示x,1表示y
int[] location = new int[2];
getLocationOnScreen(location);
int mOnScreenY = location[1];
mCustomHeaderView.getLocationOnScreen(location);
int customHeaderViewOnScreenY = location[1];
if (mOnScreenY <= customHeaderViewOnScreenY) {
return true;
} else {
return false;
}
}
return true;
}
/**
* 整个头部控件是否已经完全隐藏
*
* @return true表示完全隐藏,false表示没有完全隐藏
*/
private boolean isWholeHeaderViewCompleteInvisible() {
if (mCustomHeaderView != null && mIsCustomHeaderViewScrollable) {
// 0表示x,1表示y
int[] location = new int[2];
getLocationOnScreen(location);
int mOnScreenY = location[1];
mWholeHeaderView.getLocationOnScreen(location);
int wholeHeaderViewOnScreenY = location[1];
if (wholeHeaderViewOnScreenY + mWholeHeaderView.getMeasuredHeight() <= mOnScreenY) {
return true;
} else {
return false;
}
}
return true;
}
/**
* 处理手指滑动事件
*
* @param event
* @return true表示自己消耗掉该事件,false表示不消耗该事件
*/
private boolean handleActionMove(MotionEvent event) {
if (mCurrentRefreshStatus == RefreshStatus.REFRESHING || mIsLoadingMore) {
return false;
}
if ((mCustomHeaderView == null || !mIsCustomHeaderViewScrollable) && mRefreshDownY == -1) {
mRefreshDownY = (int) event.getY();
}
if (mCustomHeaderView != null && mIsCustomHeaderViewScrollable && isCustomHeaderViewCompleteVisible() && mRefreshDownY == -1) {
mRefreshDownY = (int) event.getY();
}
int refreshDiffY = (int) event.getY() - mRefreshDownY;
refreshDiffY = (int) (refreshDiffY / mRefreshViewHolder.getPaddingTopScale());
// 如果是向下拉,并且当前可见的第一个条目的索引等于0,才处理整个头部控件的padding
if (refreshDiffY > 0 && shouldHandleRefresh() && isCustomHeaderViewCompleteVisible()) {
int paddingTop = mMinWholeHeaderViewPaddingTop + refreshDiffY;
if (paddingTop > 0 && mCurrentRefreshStatus != RefreshStatus.RELEASE_REFRESH) {
// 下拉刷新控件完全显示,并且当前状态没有处于释放开始刷新状态
mCurrentRefreshStatus = RefreshStatus.RELEASE_REFRESH;
handleRefreshStatusChanged();
mRefreshViewHolder.handleScale(1.0f, refreshDiffY);
if (mRefreshScaleDelegate != null) {
mRefreshScaleDelegate.onRefreshScaleChanged(1.0f, refreshDiffY);
}
} else if (paddingTop < 0) {
// 下拉刷新控件没有完全显示,并且当前状态没有处于下拉刷新状态
if (mCurrentRefreshStatus != RefreshStatus.PULL_DOWN) {
boolean isPreRefreshStatusNotIdle = mCurrentRefreshStatus != RefreshStatus.IDLE;
mCurrentRefreshStatus = RefreshStatus.PULL_DOWN;
if (isPreRefreshStatusNotIdle) {
handleRefreshStatusChanged();
}
}
float scale = 1 - paddingTop * 1.0f / mMinWholeHeaderViewPaddingTop;
/**
* 往下滑
* paddingTop mMinWholeHeaderViewPaddingTop 到 0
* scale 0 到 1
* 往上滑
* paddingTop 0 到 mMinWholeHeaderViewPaddingTop
* scale 1 到 0
*/
mRefreshViewHolder.handleScale(scale, refreshDiffY);
if (mRefreshScaleDelegate != null) {
mRefreshScaleDelegate.onRefreshScaleChanged(scale, refreshDiffY);
}
}
paddingTop = Math.min(paddingTop, mMaxWholeHeaderViewPaddingTop);
mWholeHeaderView.setPadding(0, paddingTop, 0, 0);
if (mRefreshViewHolder.canChangeToRefreshingStatus()) {
mWholeHeaderDownY = -1;
mRefreshDownY = -1;
beginRefreshing();
}
return true;
}
if (mCustomHeaderView != null && mIsCustomHeaderViewScrollable) {
if (mWholeHeaderDownY == -1) {
mWholeHeaderDownY = (int) event.getY();
if (mCustomHeaderView != null) {
mWholeHeaderViewDownPaddingTop = mWholeHeaderView.getPaddingTop();
}
}
int wholeHeaderDiffY = (int) event.getY() - mWholeHeaderDownY;
if ((mPullDownRefreshEnable && !isWholeHeaderViewCompleteInvisible()) || (wholeHeaderDiffY > 0 && shouldInterceptToMoveCustomHeaderViewDown()) || (wholeHeaderDiffY < 0 && shouldInterceptToMoveCustomHeaderViewUp())) {
int paddingTop = mWholeHeaderViewDownPaddingTop + wholeHeaderDiffY;
if (paddingTop < mMinWholeHeaderViewPaddingTop - mCustomHeaderView.getMeasuredHeight()) {
paddingTop = mMinWholeHeaderViewPaddingTop - mCustomHeaderView.getMeasuredHeight();
}
mWholeHeaderView.setPadding(0, paddingTop, 0, 0);
return true;
}
}
return false;
}
/**
* 处理手指抬起事件
*
* @return true表示自己消耗掉该事件,false表示不消耗该事件
*/
private boolean handleActionUpOrCancel(MotionEvent event) {
boolean isReturnTrue = false;
// 如果当前头部刷新控件没有完全隐藏,则需要返回true,自己消耗ACTION_UP事件
if ((mCustomHeaderView == null || (mCustomHeaderView != null && !mIsCustomHeaderViewScrollable)) && mWholeHeaderView.getPaddingTop() != mMinWholeHeaderViewPaddingTop) {
isReturnTrue = true;
}
if (mCurrentRefreshStatus == RefreshStatus.PULL_DOWN || mCurrentRefreshStatus == RefreshStatus.IDLE) {
// 处于下拉刷新状态,松手时隐藏下拉刷新控件
if (mCustomHeaderView == null || (mCustomHeaderView != null && mWholeHeaderView.getPaddingTop() < 0 && mWholeHeaderView.getPaddingTop() > mMinWholeHeaderViewPaddingTop)) {
hiddenRefreshHeaderView();
}
mCurrentRefreshStatus = RefreshStatus.IDLE;
handleRefreshStatusChanged();
} else if (mCurrentRefreshStatus == RefreshStatus.RELEASE_REFRESH) {
// 处于松开进入刷新状态,松手时完全显示下拉刷新控件,进入正在刷新状态
beginRefreshing();
}
if (mRefreshDownY == -1) {
mRefreshDownY = (int) event.getY();
}
int diffY = (int) event.getY() - mRefreshDownY;
if (shouldHandleLoadingMore() && diffY <= 0) {
// 处理上拉加载更多,需要返回true,自己消耗ACTION_UP事件
isReturnTrue = true;
beginLoadingMore();
}
mWholeHeaderDownY = -1;
mRefreshDownY = -1;
return isReturnTrue;
}
/**
* 处理下拉刷新控件状态变化
*/
private void handleRefreshStatusChanged() {
switch (mCurrentRefreshStatus) {
case IDLE:
mRefreshViewHolder.changeToIdle();
break;
case PULL_DOWN:
mRefreshViewHolder.changeToPullDown();
break;
case RELEASE_REFRESH:
mRefreshViewHolder.changeToReleaseRefresh();
break;
case REFRESHING:
mRefreshViewHolder.changeToRefreshing();
break;
default:
break;
}
}
/**
* 切换到正在刷新状态,会调用delegate的onBGARefreshLayoutBeginRefreshing方法
*/
public void beginRefreshing() {
if (mCurrentRefreshStatus != RefreshStatus.REFRESHING && mDelegate != null) {
mCurrentRefreshStatus = RefreshStatus.REFRESHING;
changeRefreshHeaderViewToZero();
handleRefreshStatusChanged();
mDelegate.onBGARefreshLayoutBeginRefreshing(this);
}
}
/**
* 结束下拉刷新
*/
public void endRefreshing() {
if (mCurrentRefreshStatus == RefreshStatus.REFRESHING) {
mCurrentRefreshStatus = RefreshStatus.IDLE;
hiddenRefreshHeaderView();
handleRefreshStatusChanged();
mRefreshViewHolder.onEndRefreshing();
}
}
/**
* 隐藏下拉刷新控件,带动画
*/
private void hiddenRefreshHeaderView() {
ValueAnimator animator = ValueAnimator.ofInt(mWholeHeaderView.getPaddingTop(), mMinWholeHeaderViewPaddingTop);
animator.setDuration(mRefreshViewHolder.getTopAnimDuration());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int paddingTop = (int) animation.getAnimatedValue();
mWholeHeaderView.setPadding(0, paddingTop, 0, 0);
}
});
animator.start();
}
/**
* 设置下拉刷新控件的paddingTop到0,带动画
*/
private void changeRefreshHeaderViewToZero() {
ValueAnimator animator = ValueAnimator.ofInt(mWholeHeaderView.getPaddingTop(), 0);
animator.setDuration(mRefreshViewHolder.getTopAnimDuration());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int paddingTop = (int) animation.getAnimatedValue();
mWholeHeaderView.setPadding(0, paddingTop, 0, 0);
}
});
animator.start();
}
/**
* 开始上拉加载更多,会触发delegate的onBGARefreshLayoutBeginRefreshing方法
*/
public void beginLoadingMore() {
if (!mIsLoadingMore && mLoadMoreFooterView != null && mDelegate != null && mDelegate.onBGARefreshLayoutBeginLoadingMore(this)) {
mIsLoadingMore = true;
if (mIsShowLoadingMoreView) {
showLoadingMoreView();
}
}
}
/**
* 显示上拉加载更多控件
*/
private void showLoadingMoreView() {
mRefreshViewHolder.changeToLoadingMore();
mLoadMoreFooterView.setVisibility(VISIBLE);
BGARefreshScrollingUtil.scrollToBottom(mScrollView);
BGARefreshScrollingUtil.scrollToBottom(mRecyclerView);
BGARefreshScrollingUtil.scrollToBottom(mAbsListView);
if (mStickyNavLayout != null) {
mStickyNavLayout.scrollToBottom();
}
}
/**
* 结束上拉加载更多
*/
public void endLoadingMore() {
if (mIsLoadingMore) {
if (mIsShowLoadingMoreView) {
// 避免WiFi环境下请求数据太快,加载更多控件一闪而过
mHandler.postDelayed(mDelayHiddenLoadingMoreViewTask, 300);
} else {
mIsLoadingMore = false;
}
}
}
private Runnable mDelayHiddenLoadingMoreViewTask = new Runnable() {
@Override
public void run() {
mIsLoadingMore = false;
mRefreshViewHolder.onEndLoadingMore();
mLoadMoreFooterView.setVisibility(GONE);
}
};
/**
* 上拉加载更多时是否显示加载更多控件
*
* @param isShowLoadingMoreView
*/
public void setIsShowLoadingMoreView(boolean isShowLoadingMoreView) {
mIsShowLoadingMoreView = isShowLoadingMoreView;
}
/**
* 设置下拉刷新是否可用
*
* @param pullDownRefreshEnable
*/
public void setPullDownRefreshEnable(boolean pullDownRefreshEnable) {
mPullDownRefreshEnable = pullDownRefreshEnable;
}
/**
* 设置下拉刷新上拉加载更多代理
*
* @param delegate
*/
public void setDelegate(BGARefreshLayoutDelegate delegate) {
mDelegate = delegate;
}
/**
* 设置下拉刷新控件可见时,上下拉进度的代理
*
* @param refreshScaleDelegate
*/
public void setRefreshScaleDelegate(BGARefreshScaleDelegate refreshScaleDelegate) {
mRefreshScaleDelegate = refreshScaleDelegate;
}
/**
* 获取当前下拉刷新的状态
*
* @return
*/
public RefreshStatus getCurrentRefreshStatus() {
return mCurrentRefreshStatus;
}
/**
* 是否处于正在加载更多状态
*
* @return
*/
public boolean isLoadingMore() {
return mIsLoadingMore;
}
public interface BGARefreshLayoutDelegate {
/**
* 开始刷新
*/
void onBGARefreshLayoutBeginRefreshing(BGARefreshLayout refreshLayout);
/**
* 开始加载更多。由于监听了ScrollView、RecyclerView、AbsListView滚动到底部的事件,所以这里采用返回boolean来处理是否加载更多。否则使用endLoadingMore方法会失效
*
* @param refreshLayout
* @return 如果要开始加载更多则返回true,否则返回false。(返回false的场景:没有网络、一共只有x页数据并且已经加载了x页数据了)
*/
boolean onBGARefreshLayoutBeginLoadingMore(BGARefreshLayout refreshLayout);
}
public interface BGARefreshScaleDelegate {
/**
* 下拉刷新控件可见时,处理上下拉进度
*
* @param scale 下拉过程0 到 1,回弹过程1 到 0,没有加上弹簧距离移动时的比例
* @param moveYDistance 整个下拉刷新控件paddingTop变化的值,如果有弹簧距离,会大于整个下拉刷新控件的高度
*/
void onRefreshScaleChanged(float scale, int moveYDistance);
}
public enum RefreshStatus {
IDLE, PULL_DOWN, RELEASE_REFRESH, REFRESHING
}
public static int dp2px(Context context, int dpValue) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.getResources().getDisplayMetrics());
}
}
BGARefreshScrollingUtil.java
/**
* @author wangtingting
* 15/10/28 上午2:26
*/
public class BGARefreshScrollingUtil {
private BGARefreshScrollingUtil() {
}
public static boolean isScrollViewOrWebViewToTop(View view) {
return view != null && view.getScrollY() == 0;
}
public static boolean isAbsListViewToTop(AbsListView absListView) {
if (absListView != null) {
int firstChildTop = 0;
if (absListView.getChildCount() > 0) {
// 如果AdapterView的子控件数量不为0,获取第一个子控件的top
firstChildTop = absListView.getChildAt(0).getTop() - absListView.getPaddingTop();
}
if (absListView.getFirstVisiblePosition() == 0 && firstChildTop == 0) {
return true;
}
}
return false;
}
public static boolean isRecyclerViewToTop(RecyclerView recyclerView) {
if (recyclerView != null) {
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
if (manager == null) {
return true;
}
if (manager.getItemCount() == 0) {
return true;
}
if (manager instanceof LinearLayoutManager) {
LinearLayoutManager layoutManager = (LinearLayoutManager) manager;
int firstChildTop = 0;
if (recyclerView.getChildCount() > 0) {
// 处理item高度超过一屏幕时的情况
View firstVisibleChild = recyclerView.getChildAt(0);
if (firstVisibleChild != null && firstVisibleChild.getMeasuredHeight() >= recyclerView.getMeasuredHeight()) {
if (android.os.Build.VERSION.SDK_INT < 14) {
return !(ViewCompat.canScrollVertically(recyclerView, -1) || recyclerView.getScrollY() > 0);
} else {
return !ViewCompat.canScrollVertically(recyclerView, -1);
}
}
// 如果RecyclerView的子控件数量不为0,获取第一个子控件的top
// 解决item的topMargin不为0时不能触发下拉刷新
View firstChild = recyclerView.getChildAt(0);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) firstChild.getLayoutParams();
firstChildTop = firstChild.getTop() - layoutParams.topMargin - getRecyclerViewItemTopInset(layoutParams) - recyclerView.getPaddingTop();
}
if (layoutManager.findFirstCompletelyVisibleItemPosition() < 1 && firstChildTop == 0) {
return true;
}
} else if (manager instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) manager;
int[] out = layoutManager.findFirstCompletelyVisibleItemPositions(null);
if (out[0] < 1) {
return true;
}
}
}
return false;
}
/**
* 通过反射获取RecyclerView的item的topInset
*
* @param layoutParams
* @return
*/
private static int getRecyclerViewItemTopInset(RecyclerView.LayoutParams layoutParams) {
try {
Field field = RecyclerView.LayoutParams.class.getDeclaredField("mDecorInsets");
field.setAccessible(true);
// 开发者自定义的滚动监听器
Rect decorInsets = (Rect) field.get(layoutParams);
return decorInsets.top;
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
public static boolean isStickyNavLayoutToTop(BGAStickyNavLayout stickyNavLayout) {
return isScrollViewOrWebViewToTop(stickyNavLayout) && stickyNavLayout.isContentViewToTop();
}
public static boolean isWebViewToBottom(WebView webView) {
return webView != null && webView.getContentHeight() * webView.getScale() == (webView.getScrollY() + webView.getMeasuredHeight());
}
public static boolean isScrollViewToBottom(ScrollView scrollView) {
if (scrollView != null) {
int scrollContentHeight = scrollView.getScrollY() + scrollView.getMeasuredHeight() - scrollView.getPaddingTop() - scrollView.getPaddingBottom();
int realContentHeight = scrollView.getChildAt(0).getMeasuredHeight();
if (scrollContentHeight == realContentHeight) {
return true;
}
}
return false;
}
public static boolean isAbsListViewToBottom(AbsListView absListView) {
if (absListView != null && absListView.getAdapter() != null && absListView.getChildCount() > 0 && absListView.getLastVisiblePosition() == absListView.getAdapter().getCount() - 1) {
View lastChild = absListView.getChildAt(absListView.getChildCount() - 1);
BGAStickyNavLayout stickyNavLayout = getStickyNavLayout(absListView);
if (stickyNavLayout != null) {
// 处理BGAStickyNavLayout中lastChild.getBottom() <= absListView.getMeasuredHeight()失效问题
// 0表示x,1表示y
int[] location = new int[2];
lastChild.getLocationOnScreen(location);
int lastChildBottomOnScreen = location[1] + lastChild.getMeasuredHeight();
stickyNavLayout.getLocationOnScreen(location);
int stickyNavLayoutBottomOnScreen = location[1] + stickyNavLayout.getMeasuredHeight();
return lastChildBottomOnScreen + absListView.getPaddingBottom() <= stickyNavLayoutBottomOnScreen;
} else {
return lastChild.getBottom() <= absListView.getMeasuredHeight();
}
}
return false;
}
public static boolean isRecyclerViewToBottom(RecyclerView recyclerView) {
if (recyclerView != null) {
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
if (manager == null || manager.getItemCount() == 0) {
return false;
}
if (manager instanceof LinearLayoutManager) {
// 处理item高度超过一屏幕时的情况
View lastVisibleChild = recyclerView.getChildAt(recyclerView.getChildCount() - 1);
if (lastVisibleChild != null && lastVisibleChild.getMeasuredHeight() >= recyclerView.getMeasuredHeight()) {
if (android.os.Build.VERSION.SDK_INT < 14) {
return !(ViewCompat.canScrollVertically(recyclerView, 1) || recyclerView.getScrollY() < 0);
} else {
return !ViewCompat.canScrollVertically(recyclerView, 1);
}
}
LinearLayoutManager layoutManager = (LinearLayoutManager) manager;
if (layoutManager.findLastCompletelyVisibleItemPosition() == layoutManager.getItemCount() - 1) {
BGAStickyNavLayout stickyNavLayout = getStickyNavLayout(recyclerView);
if (stickyNavLayout != null) {
// 处理BGAStickyNavLayout中findLastCompletelyVisibleItemPosition失效问题
View lastCompletelyVisibleChild = layoutManager.getChildAt(layoutManager.findLastCompletelyVisibleItemPosition());
if (lastCompletelyVisibleChild == null) {
return true;
} else {
// 0表示x,1表示y
int[] location = new int[2];
lastCompletelyVisibleChild.getLocationOnScreen(location);
int lastChildBottomOnScreen = location[1] + lastCompletelyVisibleChild.getMeasuredHeight();
stickyNavLayout.getLocationOnScreen(location);
int stickyNavLayoutBottomOnScreen = location[1] + stickyNavLayout.getMeasuredHeight();
return lastChildBottomOnScreen <= stickyNavLayoutBottomOnScreen;
}
} else {
return true;
}
}
} else if (manager instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) manager;
int[] out = layoutManager.findLastCompletelyVisibleItemPositions(null);
int lastPosition = layoutManager.getItemCount() - 1;
for (int position : out) {
if (position == lastPosition) {
return true;
}
}
}
}
return false;
}
public static void scrollToBottom(final ScrollView scrollView) {
if (scrollView != null) {
scrollView.post(new Runnable() {
@Override
public void run() {
scrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
});
}
}
public static void scrollToBottom(final AbsListView absListView) {
if (absListView != null) {
if (absListView.getAdapter() != null && absListView.getAdapter().getCount() > 0) {
absListView.post(new Runnable() {
@Override
public void run() {
absListView.setSelection(absListView.getAdapter().getCount() - 1);
}
});
}
}
}
public static void scrollToBottom(final RecyclerView recyclerView) {
if (recyclerView != null) {
if (recyclerView.getAdapter() != null && recyclerView.getAdapter().getItemCount() > 0) {
recyclerView.post(new Runnable() {
@Override
public void run() {
recyclerView.smoothScrollToPosition(recyclerView.getAdapter().getItemCount() - 1);
}
});
}
}
}
public static BGAStickyNavLayout getStickyNavLayout(View view) {
ViewParent viewParent = view.getParent();
while (viewParent != null) {
if (viewParent instanceof BGAStickyNavLayout) {
return (BGAStickyNavLayout) viewParent;
}
viewParent = viewParent.getParent();
}
return null;
}
public static int getScreenHeight(Context context) {
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(dm);
return dm.heightPixels;
}
}
BGARefreshViewHolder.java
/**
* @author wangtt
* 15/5/21 12:56
* 描述:继承该抽象类实现响应的抽象方法,做出各种下拉刷新效果。参考BGANormalRefreshViewHolder、BGAStickinessRefreshViewHolder、BGAMoocStyleRefreshViewHolder、BGAMeiTuanRefreshViewHolder
*/
public abstract class BGARefreshViewHolder {
/**
* 手指移动距离与下拉刷新控件paddingTop移动距离的比值
*/
private static final float PULL_DISTANCE_SCALE = 1.8f;
/**
* 手指移动距离与下拉刷新控件paddingTop移动距离的比值,默认1.8f
*/
private float mPullDistanceScale = PULL_DISTANCE_SCALE;
/**
* 下拉刷新控件paddingTop的弹簧距离与下拉刷新控件高度的比值
*/
private static final float SPRING_DISTANCE_SCALE = 0.4f;
/**
* 下拉刷新控件paddingTop的弹簧距离与下拉刷新控件高度的比值,默认0.4f
*/
private float mSpringDistanceScale = SPRING_DISTANCE_SCALE;
protected Context mContext;
/**
* 下拉刷新上拉加载更多控件
*/
protected BGARefreshLayout mRefreshLayout;
/**
* 下拉刷新控件
*/
protected View mRefreshHeaderView;
/**
* 上拉加载更多控件
*/
protected View mLoadMoreFooterView;
/**
* 底部加载更多提示控件
*/
protected TextView mFooterStatusTv;
/**
* 底部加载更多菊花控件
*/
protected ImageView mFooterChrysanthemumIv;
/**
* 底部加载更多菊花drawable
*/
protected AnimationDrawable mFooterChrysanthemumAd;
/**
* 正在加载更多时的文本
*/
protected String mLodingMoreText = "加载中...";
/**
* 是否开启加载更多功能
*/
private boolean mIsLoadingMoreEnabled = true;
/**
* 整个加载更多控件的背景颜色资源id
*/
private int mLoadMoreBackgroundColorRes = -1;
/**
* 整个加载更多控件的背景drawable资源id
*/
private int mLoadMoreBackgroundDrawableRes = -1;
/**
* 下拉刷新控件的背景颜色资源id
*/
protected int mRefreshViewBackgroundColorRes = -1;
/**
* 下拉刷新控件的背景drawable资源id
*/
protected int mRefreshViewBackgroundDrawableRes = -1;
/**
* 头部控件移动动画时常
*/
private int mTopAnimDuration = 500;
/**
* @param context
* @param isLoadingMoreEnabled 上拉加载更多是否可用
*/
public BGARefreshViewHolder(Context context, boolean isLoadingMoreEnabled) {
mContext = context;
mIsLoadingMoreEnabled = isLoadingMoreEnabled;
}
/**
* 设置正在加载更多时的文本
*
* @param loadingMoreText
*/
public void setLoadingMoreText(String loadingMoreText) {
mLodingMoreText = loadingMoreText;
}
/**
* 设置整个加载更多控件的背景颜色资源id
*
* @param loadMoreBackgroundColorRes
*/
public void setLoadMoreBackgroundColorRes(@ColorRes int loadMoreBackgroundColorRes) {
mLoadMoreBackgroundColorRes = loadMoreBackgroundColorRes;
}
/**
* 设置整个加载更多控件的背景drawable资源id
*
* @param loadMoreBackgroundDrawableRes
*/
public void setLoadMoreBackgroundDrawableRes(@DrawableRes int loadMoreBackgroundDrawableRes) {
mLoadMoreBackgroundDrawableRes = loadMoreBackgroundDrawableRes;
}
/**
* 设置下拉刷新控件的背景颜色资源id
*
* @param refreshViewBackgroundColorRes
*/
public void setRefreshViewBackgroundColorRes(@ColorRes int refreshViewBackgroundColorRes) {
mRefreshViewBackgroundColorRes = refreshViewBackgroundColorRes;
}
/**
* 设置下拉刷新控件的背景drawable资源id
*
* @param refreshViewBackgroundDrawableRes
*/
public void setRefreshViewBackgroundDrawableRes(@DrawableRes int refreshViewBackgroundDrawableRes) {
mRefreshViewBackgroundDrawableRes = refreshViewBackgroundDrawableRes;
}
/**
* 获取顶部未满足下拉刷新条件时回弹到初始状态、满足刷新条件时回弹到正在刷新状态、刷新完毕后回弹到初始状态的动画时间,默认为500毫秒
*
* @return
*/
public int getTopAnimDuration() {
return mTopAnimDuration;
}
/**
* 设置顶部未满足下拉刷新条件时回弹到初始状态、满足刷新条件时回弹到正在刷新状态、刷新完毕后回弹到初始状态的动画时间,默认为300毫秒
*
* @param topAnimDuration
*/
public void setTopAnimDuration(int topAnimDuration) {
mTopAnimDuration = topAnimDuration;
}
/**
* 获取上拉加载更多控件,如果不喜欢这种上拉刷新风格可重写该方法实现自定义LoadMoreFooterView
*
* @return
*/
public View getLoadMoreFooterView() {
if (!mIsLoadingMoreEnabled) {
return null;
}
if (mLoadMoreFooterView == null) {
mLoadMoreFooterView = View.inflate(mContext, R.layout.view_normal_refresh_footer, null);
mLoadMoreFooterView.setBackgroundColor(Color.TRANSPARENT);
if (mLoadMoreBackgroundColorRes != -1) {
mLoadMoreFooterView.setBackgroundResource(mLoadMoreBackgroundColorRes);
}
if (mLoadMoreBackgroundDrawableRes != -1) {
mLoadMoreFooterView.setBackgroundResource(mLoadMoreBackgroundDrawableRes);
}
mFooterStatusTv = (TextView) mLoadMoreFooterView.findViewById(R.id.tv_normal_refresh_footer_status);
mFooterChrysanthemumIv = (ImageView) mLoadMoreFooterView.findViewById(R.id.iv_normal_refresh_footer_chrysanthemum);
mFooterChrysanthemumAd = (AnimationDrawable) mFooterChrysanthemumIv.getDrawable();
mFooterStatusTv.setText(mLodingMoreText);
}
return mLoadMoreFooterView;
}
/**
* 获取头部下拉刷新控件
*
* @return
*/
public abstract View getRefreshHeaderView();
/**
* 下拉刷新控件可见时,处理上下拉进度
*
* @param scale 下拉过程0 到 1,回弹过程1 到 0,没有加上弹簧距离移动时的比例
* @param moveYDistance 整个下拉刷新控件paddingTop变化的值,如果有弹簧距离,会大于整个下拉刷新控件的高度
*/
public abstract void handleScale(float scale, int moveYDistance);
/**
* 进入到未处理下拉刷新状态
*/
public abstract void changeToIdle();
/**
* 进入下拉状态
*/
public abstract void changeToPullDown();
/**
* 进入释放刷新状态
*/
public abstract void changeToReleaseRefresh();
/**
* 进入正在刷新状态
*/
public abstract void changeToRefreshing();
/**
* 结束下拉刷新
*/
public abstract void onEndRefreshing();
/**
* 手指移动距离与下拉刷新控件paddingTop移动距离的比值
*
* @return
*/
public float getPaddingTopScale() {
return mPullDistanceScale;
}
/**
* 设置手指移动距离与下拉刷新控件paddingTop移动距离的比值
*
* @param pullDistanceScale
*/
public void setPullDistanceScale(float pullDistanceScale) {
mPullDistanceScale = pullDistanceScale;
}
/**
* 下拉刷新控件paddingTop的弹簧距离与下拉刷新控件高度的比值
*
* @return
*/
public float getSpringDistanceScale() {
return mSpringDistanceScale;
}
/**
* 设置下拉刷新控件paddingTop的弹簧距离与下拉刷新控件高度的比值,不能小于0,如果刷新控件比较高,建议将该值设置小一些
*
* @param springDistanceScale
*/
public void setSpringDistanceScale(float springDistanceScale) {
if (springDistanceScale < 0) {
throw new RuntimeException("下拉刷新控件paddingTop的弹簧距离与下拉刷新控件高度的比值springDistanceScale不能小于0");
}
mSpringDistanceScale = springDistanceScale;
}
/**
* 是处于能够进入刷新状态
*
* @return
*/
public boolean canChangeToRefreshingStatus() {
return false;
}
/**
* 进入加载更多状态
*/
public void changeToLoadingMore() {
if (mIsLoadingMoreEnabled && mFooterChrysanthemumAd != null) {
mFooterChrysanthemumAd.start();
}
}
/**
* 结束上拉加载更多
*/
public void onEndLoadingMore() {
if (mIsLoadingMoreEnabled && mFooterChrysanthemumAd != null) {
mFooterChrysanthemumAd.stop();
}
}
/**
* 获取下拉刷新控件的高度,如果初始化时的高度和最后展开的最大高度不一致,需重写该方法返回最大高度
*
* @return
*/
public int getRefreshHeaderViewHeight() {
if (mRefreshHeaderView != null) {
// 测量下拉刷新控件的高度
mRefreshHeaderView.measure(0, 0);
return mRefreshHeaderView.getMeasuredHeight();
}
return 0;
}
/**
* 改变整个下拉刷新头部控件移动一定的距离(带动画),自定义刷新控件进入刷新状态前后的高度有变化时可以使用该方法(参考BGAStickinessRefreshView)
*
* @param distance
*/
public void startChangeWholeHeaderViewPaddingTop(int distance) {
mRefreshLayout.startChangeWholeHeaderViewPaddingTop(distance);
}
/**
* 设置下拉刷新上拉加载更多控件,该方法是设置BGARefreshViewHolder给BGARefreshLayout时由BGARefreshLayout调用
*
* @param refreshLayout
*/
public void setRefreshLayout(BGARefreshLayout refreshLayout) {
mRefreshLayout = refreshLayout;
}
}
BGAStickyNavLayout.java
/**
* @author wangtt
*/
public class BGAStickyNavLayout extends LinearLayout {
private View mHeaderView;
private View mNavView;
private View mContentView;
private View mDirectNormalView;
private RecyclerView mDirectRecyclerView;
private AbsListView mDirectAbsListView;
private ScrollView mDirectScrollView;
private WebView mDirectWebView;
private ViewPager mDirectViewPager;
private View mNestedContentView;
private View mNestedNormalView;
private RecyclerView mNestedRecyclerView;
private AbsListView mNestedAbsListView;
private ScrollView mNestedScrollView;
private WebView mNestedWebView;
private OverScroller mOverScroller;
private VelocityTracker mVelocityTracker;
private int mTouchSlop;
private int mMaximumVelocity;
private int mMinimumVelocity;
private boolean mIsInControl = true;
private float mLastDispatchY;
private float mLastTouchY;
public BGARefreshLayout mRefreshLayout;
public BGAStickyNavLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
setOrientation(VERTICAL);
mOverScroller = new OverScroller(context);
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledTouchSlop();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
}
@Override
public void setOrientation(int orientation) {
if (VERTICAL == orientation) {
super.setOrientation(VERTICAL);
}
}
@Override
public void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() != 3) {
throw new IllegalStateException(BGAStickyNavLayout.class.getSimpleName() + "必须有且只有三个子控件");
}
mHeaderView = getChildAt(0);
mNavView = getChildAt(1);
mContentView = getChildAt(2);
if (mContentView instanceof AbsListView) {
mDirectAbsListView = (AbsListView) mContentView;
mDirectAbsListView.setOnScrollListener(mLvOnScrollListener);
} else if (mContentView instanceof RecyclerView) {
mDirectRecyclerView = (RecyclerView) mContentView;
mDirectRecyclerView.addOnScrollListener(mRvOnScrollListener);
} else if (mContentView instanceof ScrollView) {
mDirectScrollView = (ScrollView) mContentView;
} else if (mContentView instanceof WebView) {
mDirectWebView = (WebView) mContentView;
} else if (mContentView instanceof ViewPager) {
mDirectViewPager = (ViewPager) mContentView;
mDirectViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
regetNestedContentView();
}
});
} else {
mDirectNormalView = mContentView;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChild(mContentView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec) - getNavViewHeight(), MeasureSpec.EXACTLY));
}
@Override
public void computeScroll() {
if (mOverScroller.computeScrollOffset()) {
scrollTo(0, mOverScroller.getCurrY());
invalidate();
}
}
public void fling(int velocityY) {
mOverScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, getHeaderViewHeight());
invalidate();
}
@Override
public void scrollTo(int x, int y) {
if (y < 0) {
y = 0;
}
int headerViewHeight = getHeaderViewHeight();
if (y > headerViewHeight) {
y = headerViewHeight;
}
if (y != getScrollY()) {
super.scrollTo(x, y);
}
}
/**
* 获取头部视图高度,包括topMargin和bottomMargin
*
* @return
*/
private int getHeaderViewHeight() {
MarginLayoutParams layoutParams = (MarginLayoutParams) mHeaderView.getLayoutParams();
return mHeaderView.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
}
/**
* 获取导航视图的高度,包括topMargin和bottomMargin
*
* @return
*/
private int getNavViewHeight() {
MarginLayoutParams layoutParams = (MarginLayoutParams) mNavView.getLayoutParams();
return mNavView.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
}
/**
* 头部视图是否已经完全隐藏
*
* @return
*/
private boolean isHeaderViewCompleteInvisible() {
// 0表示x,1表示y
int[] location = new int[2];
getLocationOnScreen(location);
int contentOnScreenTopY = location[1] + getPaddingTop();
mNavView.getLocationOnScreen(location);
MarginLayoutParams params = (MarginLayoutParams) mNavView.getLayoutParams();
int navViewTopOnScreenY = location[1] - params.topMargin;
if (navViewTopOnScreenY == contentOnScreenTopY) {
return true;
} else {
return false;
}
}
private void initVelocityTrackerIfNotExists() {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
}
private void recycleVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
float currentTouchY = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastDispatchY = currentTouchY;
break;
case MotionEvent.ACTION_MOVE:
float differentY = currentTouchY - mLastDispatchY;
mLastDispatchY = currentTouchY;
if (isContentViewToTop() && isHeaderViewCompleteInvisible()) {
if (differentY >= 0 && !mIsInControl) {
mIsInControl = true;
return resetDispatchTouchEvent(ev);
}
if (differentY <= 0 && mIsInControl) {
mIsInControl = false;
return resetDispatchTouchEvent(ev);
}
}
break;
}
return super.dispatchTouchEvent(ev);
}
private boolean resetDispatchTouchEvent(MotionEvent ev) {
MotionEvent newEvent = MotionEvent.obtain(ev);
ev.setAction(MotionEvent.ACTION_CANCEL);
dispatchTouchEvent(ev);
newEvent.setAction(MotionEvent.ACTION_DOWN);
return dispatchTouchEvent(newEvent);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
float currentTouchY = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastTouchY = currentTouchY;
break;
case MotionEvent.ACTION_MOVE:
float differentY = currentTouchY - mLastTouchY;
if (Math.abs(differentY) > mTouchSlop) {
if (!isHeaderViewCompleteInvisible() || (isContentViewToTop() && isHeaderViewCompleteInvisible() && mIsInControl)) {
mLastTouchY = currentTouchY;
return true;
}
}
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(event);
float currentTouchY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!mOverScroller.isFinished()) {
mOverScroller.abortAnimation();
}
mLastTouchY = currentTouchY;
break;
case MotionEvent.ACTION_MOVE:
float differentY = currentTouchY - mLastTouchY;
mLastTouchY = currentTouchY;
if (Math.abs(differentY) > 0) {
scrollBy(0, (int) -differentY);
}
break;
case MotionEvent.ACTION_CANCEL:
recycleVelocityTracker();
if (!mOverScroller.isFinished()) {
mOverScroller.abortAnimation();
}
break;
case MotionEvent.ACTION_UP:
mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) mVelocityTracker.getYVelocity();
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
fling(-initialVelocity);
}
recycleVelocityTracker();
break;
}
return true;
}
public boolean isContentViewToTop() {
if (mDirectNormalView != null) {
return true;
}
if (BGARefreshScrollingUtil.isScrollViewOrWebViewToTop(mDirectWebView)) {
return true;
}
if (BGARefreshScrollingUtil.isScrollViewOrWebViewToTop(mDirectScrollView)) {
return true;
}
if (BGARefreshScrollingUtil.isAbsListViewToTop(mDirectAbsListView)) {
return true;
}
if (BGARefreshScrollingUtil.isRecyclerViewToTop(mDirectRecyclerView)) {
return true;
}
if (mDirectViewPager != null) {
return isViewPagerContentViewToTop();
}
return false;
}
private boolean isViewPagerContentViewToTop() {
if (mNestedContentView == null) {
regetNestedContentView();
}
if (mDirectNormalView != null) {
return true;
}
if (BGARefreshScrollingUtil.isScrollViewOrWebViewToTop(mNestedWebView)) {
return true;
}
if (BGARefreshScrollingUtil.isScrollViewOrWebViewToTop(mNestedScrollView)) {
return true;
}
if (BGARefreshScrollingUtil.isAbsListViewToTop(mNestedAbsListView)) {
return true;
}
if (BGARefreshScrollingUtil.isRecyclerViewToTop(mNestedRecyclerView)) {
return true;
}
return false;
}
/**
* 重新获取嵌套的内容视图
*/
private void regetNestedContentView() {
int currentItem = mDirectViewPager.getCurrentItem();
PagerAdapter adapter = mDirectViewPager.getAdapter();
if (adapter instanceof FragmentPagerAdapter || adapter instanceof FragmentStatePagerAdapter) {
Fragment item = (Fragment) adapter.instantiateItem(mDirectViewPager, currentItem);
mNestedContentView = item.getView();
// 清空之前的
mNestedNormalView = null;
mNestedAbsListView = null;
mNestedRecyclerView = null;
mNestedScrollView = null;
mNestedWebView = null;
if (mNestedContentView instanceof AbsListView) {
mNestedAbsListView = (AbsListView) mNestedContentView;
mNestedAbsListView.setOnScrollListener(mLvOnScrollListener);
if (!isHeaderViewCompleteInvisible()) {
mNestedAbsListView.setSelection(0);
}
} else if (mNestedContentView instanceof RecyclerView) {
mNestedRecyclerView = (RecyclerView) mNestedContentView;
mNestedRecyclerView.removeOnScrollListener(mRvOnScrollListener);
mNestedRecyclerView.addOnScrollListener(mRvOnScrollListener);
if (!isHeaderViewCompleteInvisible()) {
mNestedRecyclerView.scrollToPosition(0);
}
} else if (mNestedContentView instanceof ScrollView) {
mNestedScrollView = (ScrollView) mNestedContentView;
if (!isHeaderViewCompleteInvisible()) {
mNestedScrollView.scrollTo(mNestedScrollView.getScrollX(), 0);
}
} else if (mNestedContentView instanceof WebView) {
mNestedWebView = (WebView) mNestedContentView;
if (!isHeaderViewCompleteInvisible()) {
mNestedWebView.scrollTo(mNestedWebView.getScrollX(), 0);
}
} else {
mNestedNormalView = mNestedContentView;
}
} else {
throw new IllegalStateException(BGAStickyNavLayout.class.getSimpleName() + "的第三个子控件为ViewPager时,其adapter必须是FragmentPagerAdapter或者FragmentStatePagerAdapter");
}
}
public void setRefreshLayout(BGARefreshLayout refreshLayout) {
mRefreshLayout = refreshLayout;
}
private RecyclerView.OnScrollListener mRvOnScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if ((newState == RecyclerView.SCROLL_STATE_IDLE || newState == RecyclerView.SCROLL_STATE_SETTLING) && mRefreshLayout != null && mRefreshLayout.shouldHandleRecyclerViewLoadingMore(recyclerView)) {
mRefreshLayout.beginLoadingMore();
}
}
};
private AbsListView.OnScrollListener mLvOnScrollListener = new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
if ((scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING) && mRefreshLayout != null && mRefreshLayout.shouldHandleAbsListViewLoadingMore(absListView)) {
mRefreshLayout.beginLoadingMore();
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
};
public boolean shouldHandleLoadingMore() {
if (mRefreshLayout == null) {
return false;
}
if (mDirectNormalView != null) {
return true;
}
if (BGARefreshScrollingUtil.isWebViewToBottom(mDirectWebView)) {
return true;
}
if (BGARefreshScrollingUtil.isScrollViewToBottom(mDirectScrollView)) {
return true;
}
if (mDirectAbsListView != null) {
return mRefreshLayout.shouldHandleAbsListViewLoadingMore(mDirectAbsListView);
}
if (mDirectRecyclerView != null) {
return mRefreshLayout.shouldHandleRecyclerViewLoadingMore(mDirectRecyclerView);
}
if (mDirectViewPager != null) {
if (mNestedContentView == null) {
regetNestedContentView();
}
if (mNestedNormalView != null) {
return true;
}
if (BGARefreshScrollingUtil.isWebViewToBottom(mNestedWebView)) {
return true;
}
if (BGARefreshScrollingUtil.isScrollViewToBottom(mNestedScrollView)) {
return true;
}
if (mNestedAbsListView != null) {
return mRefreshLayout.shouldHandleAbsListViewLoadingMore(mNestedAbsListView);
}
if (mNestedRecyclerView != null) {
return mRefreshLayout.shouldHandleRecyclerViewLoadingMore(mNestedRecyclerView);
}
}
return false;
}
public void scrollToBottom() {
BGARefreshScrollingUtil.scrollToBottom(mDirectScrollView);
BGARefreshScrollingUtil.scrollToBottom(mDirectRecyclerView);
BGARefreshScrollingUtil.scrollToBottom(mDirectAbsListView);
if (mDirectViewPager != null) {
if (mNestedContentView == null) {
regetNestedContentView();
}
BGARefreshScrollingUtil.scrollToBottom(mNestedScrollView);
BGARefreshScrollingUtil.scrollToBottom(mNestedRecyclerView);
BGARefreshScrollingUtil.scrollToBottom(mNestedAbsListView);
}
}
}
DividerDecoration.java
public class DividerDecoration extends RecyclerView.ItemDecoration {
private int mOrientation;
private int mDividerSize;
private Paint mPaint;
private Drawable mDividerDrawable;
private int mMarginLeft;
private int mMarginRight;
/**
* 颜色分割线
*
* @param context 上下文
* @param orientation 方向
* @param color 分割线颜色
* @param dividerSize 分割线大小
*/
public DividerDecoration(Context context, int orientation, int color, int dividerSize, int marginLeft, int marginRight) {
if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) {
throw new IllegalArgumentException("Orientation is invalidate");
}
if (dividerSize <= 0) {
throw new IllegalArgumentException("DividerSize must be greated than 0");
}
mOrientation = orientation;
mDividerSize = dividerSize;
mMarginLeft = marginLeft;
mMarginRight = marginRight;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//设置为抗锯齿
mPaint.setColor(color);//设置画笔颜色
mPaint.setStyle(Paint.Style.FILL);//设置为填充
}
/**
* 图片分割线
*
* @param context 上下文
* @param orientation 方向
* @param dividerDrawable 分割线图片
* @param dividerSize 分割线大小
*/
public DividerDecoration(Context context, int orientation, Drawable dividerDrawable, int dividerSize, int marginLeft, int marginRight) {
if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) {
throw new IllegalArgumentException("Orientation is invalidate");
}
mOrientation = orientation;
mDividerDrawable = dividerDrawable;
mMarginLeft = marginLeft;
mMarginRight = marginRight;
if (dividerSize > 0) {
mDividerSize = dividerSize;
} else {
//如果传入的dividerSize不大于0,则使用图片的宽或高
if (mOrientation == LinearLayoutManager.VERTICAL) {
//如果是垂直方向,使用图片的高度
mDividerSize = mDividerDrawable.getIntrinsicHeight();
} else {
mDividerSize = mDividerDrawable.getIntrinsicWidth();
}
}
Log.i("", "mDividerSize: " + mDividerSize);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == LinearLayoutManager.VERTICAL) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
/**
* 绘制垂直分割线
*/
private void drawVertical(Canvas canvas, RecyclerView parent) {
int left = parent.getPaddingLeft() + mMarginLeft;
int right = parent.getMeasuredWidth() - parent.getPaddingRight() - mMarginRight;
int childSize = parent.getChildCount();
for (int i = 0; i < childSize; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + layoutParams.bottomMargin;
int bottom = top + mDividerSize;
if (mDividerDrawable != null) {
//如果是图片分割线,绘制图片
mDividerDrawable.setBounds(left, top, right, bottom);
mDividerDrawable.draw(canvas);
} else {
//绘制矩形
canvas.drawRect(left, top, right, bottom, mPaint);
}
}
}
/**
* 绘制横向水平分割线
*/
private void drawHorizontal(Canvas canvas, RecyclerView parent) {
int top = parent.getPaddingTop();
int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom();
int childSize = parent.getChildCount();
for (int i = 0; i < childSize; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
int left = child.getRight() + layoutParams.rightMargin;
int right = left + mDividerSize;
if (mDividerDrawable != null) {
//如果是图片分割线,绘制图片
mDividerDrawable.setBounds(left, top, right, bottom);
canvas.drawPaint(mPaint);
mDividerDrawable.draw(canvas);
} else {
//绘制矩形
canvas.drawRect(left, top, right, bottom, mPaint);
}
}
}
/**
* 设置item分割线的size
*/
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == LinearLayoutManager.VERTICAL) {
outRect.set(0, 0, 0, mDividerSize);
} else {
outRect.set(0, 0, mDividerSize, 0);
}
}
}
PowerfulRecyclerView.java
/**
* @author wangtt
* @description: 对RecyclerView进行封装
* @date 2017/6/27 14:10
*/
public class PowerfulRecyclerView extends RecyclerView {
private Context mContext;
//分割线的颜色
private int mDividerColor;
//分割线的大小
private int mDividerSize = 1;
//分割线的drawable
private Drawable mDividerDrawable;
//是否使用瀑布流布局,默认不是
private boolean mUseStaggerLayout;
//列数,默认为1
private int mNumColumns = 1;
//RecyclerView的方向,默认为垂直方向
private int mOrientation = LinearLayoutManager.VERTICAL;
private int mMarginLeft;
private int mMarginRight;
private LayoutManager mLayoutManager;
private DividerDecoration mDividerDecoration;
private Drawable mItemDrawable;
public PowerfulRecyclerView(Context context) {
this(context,null);
}
public PowerfulRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public PowerfulRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PowerfulRecyclerView);
mDividerColor = ta.getColor(R.styleable.PowerfulRecyclerView_dividerColor, Color.parseColor("#ffd8d8d8"));
mDividerSize = ta.getDimensionPixelSize(R.styleable.PowerfulRecyclerView_dividerSize, UIUtils.dip2Px(1));
mDividerDrawable = ta.getDrawable(R.styleable.PowerfulRecyclerView_dividerDrawable);
mUseStaggerLayout = ta.getBoolean(R.styleable.PowerfulRecyclerView_useStaggerLayout, mUseStaggerLayout);
mNumColumns = ta.getInt(R.styleable.PowerfulRecyclerView_numColumns,mNumColumns);
mOrientation = ta.getInt(R.styleable.PowerfulRecyclerView_rvOrientation, mOrientation);
mMarginLeft = ta.getDimensionPixelSize(R.styleable.PowerfulRecyclerView_dividerMarginLeft, 0);
mMarginRight = ta.getDimensionPixelSize(R.styleable.PowerfulRecyclerView_dividerMarginRight, 0);
ta.recycle();
setLayoutManager();
setDivider();
}
/**
* 设置layoutManager
*/
private void setLayoutManager() {
if (!mUseStaggerLayout){
//不是瀑布流布局,即是列表或网格布局
if (mOrientation == LinearLayoutManager.VERTICAL){
mLayoutManager = new GridLayoutManager(mContext, mNumColumns);
}else{
mLayoutManager = new GridLayoutManager(mContext, mNumColumns,mOrientation,false);
}
}else{
//瀑布流布局
mLayoutManager = new StaggeredGridLayoutManager(mNumColumns,mOrientation);
}
setLayoutManager(mLayoutManager);
}
/**
* 设置分割线
*/
private void setDivider() {
if (mDividerDrawable == null){
//绘制颜色分割线
mDividerDecoration = new DividerDecoration(mContext, mOrientation,mDividerColor, mDividerSize,mMarginLeft,mMarginRight);
}else{
//绘制图片分割线
mDividerDecoration = new DividerDecoration(mContext,mOrientation,mDividerDrawable,mDividerSize,mMarginLeft,mMarginRight);
}
this.addItemDecoration(mDividerDecoration);
}
}
TipView.java
/**
* @author wangtt
* @description: 顶部提示的View
* @date 2017/6/23 15:57
*/
public class TipView extends LinearLayout {
private Context mContext;
private int mBackGroundColor;
private int mTextColor;
private String mText;
private int mTextSize;
private TextView mTvTip;
//显示所停留的时间
private int mStayTime = 2000;
private boolean isShowing;
private Handler mHandler = new Handler();
public TipView(Context context) {
this(context,null);
}
public TipView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public TipView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TipView);
mBackGroundColor = ta.getColor(R.styleable.TipView_tipBackgroundColor, Color.parseColor("#ffffff"));
mTextColor = ta.getColor(R.styleable.TipView_tipTextColor, Color.parseColor("#ffffff"));
mText = ta.getString(R.styleable.TipView_tipText);
mTextSize = ta.getDimensionPixelSize(R.styleable.TipView_tipTextSize, UIUtils.sp2px(context, 12));
ta.recycle();
init();
}
private void init() {
setGravity(Gravity.CENTER);
setBackgroundColor(mBackGroundColor);//设置背景色
mTvTip = new TextView(mContext);
mTvTip.setGravity(Gravity.CENTER);
mTvTip.getPaint().setTextSize(mTextSize);
mTvTip.setTextColor(mTextColor);
mTvTip.setText(mText);
addView(mTvTip);
}
public void show(String content){
if (TextUtils.isEmpty(content)){
show();
return;
}
mTvTip.setText(content);//设置内容
show();
}
public void show(){
if (isShowing){
return;
}
isShowing = true;
setVisibility(VISIBLE);
AnimatorSet animatorSet = new AnimatorSet();
ObjectAnimator scaleX = ObjectAnimator.ofFloat(mTvTip, "scaleX", 0, 1f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(mTvTip, "scaleY", 0, 1f);
animatorSet.setDuration(500);
animatorSet.play(scaleX).with(scaleY);
animatorSet.start();
animatorSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
hide();
}
},mStayTime);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
/**隐藏,收起*/
private void hide() {
TranslateAnimation hideAnim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_SELF, 0.0f,Animation.RELATIVE_TO_SELF
,0.0f,Animation.RELATIVE_TO_SELF,-1.0f);
hideAnim.setDuration(300);
startAnimation(hideAnim);
hideAnim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
setVisibility(GONE);
isShowing = false;
mTvTip.setText(mText); //重新设置回原来的内容
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
}
- 使用
1.在布局文件里引入自定义的TipView(刷新时,头部的提醒view),BGARefreshLayout(自定义上拉刷新Layout),PowerfulRecyclerView(封装好的RecyclerView)
2.在需要使用的地方进行初始化工作,可封装在父类里供每个界面使用。
/*初始化刷新工具*/
public void initRefreshView(BGARefreshLayout refreshLayout,boolean isCanLoadMore,boolean isCanRefresh){
// 设置下拉刷新和上拉加载更多的风格 参数1:应用程序上下文,参数2:是否具有上拉加载更多功能
BGANormalRefreshViewHolder refreshViewHolder = new BGANormalRefreshViewHolder(getActivity(), isCanLoadMore);
// // 设置下拉刷新
refreshViewHolder.setRefreshViewBackgroundColorRes(R.color.ce0e0e0);//背景色
refreshViewHolder.setPullDownRefreshText(getActivity().getResources().getString(R.string.refresh_pull_down_text));//下拉的提示文字
refreshViewHolder.setReleaseRefreshText(getActivity().getResources().getString(R.string.refresh_release_text));//松开的提示文字
refreshViewHolder.setRefreshingText(getActivity().getResources().getString(R.string.refresh_ing_text));//刷新中的提示文字
refreshViewHolder.setLoadingMoreText("加载更多");
// 设置下拉刷新和上拉加载更多的风格
refreshLayout.setIsShowLoadingMoreView(true);
refreshLayout.setRefreshViewHolder(refreshViewHolder);//是否显示加载更多footerview
refreshLayout.setPullDownRefreshEnable(isCanRefresh);//设置是否可以刷新
}
3.设置下拉刷新上拉加载更多代理
mRefreshLayout.setDelegate(this);
4.重写加载更多和刷新方法
@Override
public void onBGARefreshLayoutBeginRefreshing(BGARefreshLayout refreshLayout) {
//刷新操作
}
@Override
public boolean onBGARefreshLayoutBeginLoadingMore(BGARefreshLayout refreshLayout) {
//加载更多操作
return false;
}
5.停止刷新和加载更多的操作
mRefreshLayout.endRefreshing();
mRefreshLayout.endLoadingMore();