SuperSwipeRefreshLayout源码分析
源码及DEMO
特性
- 支持下拉刷新和上拉加载更多
- 非侵入式,对原来的ListView、RecyclerView没有任何影响,用法和SwipeRefreshLayout类似。
- 可自定义头部View的样式,调用setHeaderView方法即可
- 可自定义页尾View的样式,调用setFooterView方法即可
- 支持RecyclerView,ListView,ScrollView,GridView等等。
- 被包含的View(RecyclerView,ListView etc.)可跟随手指的滑动而滑动
默认是跟随手指的滑动而滑动,也可以设置为不跟随:setTargetScrollWithLayout(false) - 回调方法更多
比如:onRefresh() onPullDistance(int distance)和onPullEnable(boolean enable)
开发人员可以根据下拉过程中distance的值做一系列动画。
思路
自定义一个ViewGroup,往其中添加headerView和footerView,然后在onMeasure中确定它们的大小,在onLayout中确定它们的位置。当子View滑动到最上方的时候,或者最下方的时候,拦截事件,自己处理onTouchEvent事件;其他情况,交给子View自己处理onTouchEvent事件。
基于以上分析:需要重点关注的方法有:
- addView
- onMeasure
- getChildDrawingOrder
- onLayout
- isChildScrollToTop
- isChildScrollToBottom
- onInterceptTouchEvent
- onTouchEvent
addView
主要是添加headerView和footerView,这是第一步。
addView(mHeadViewContainer);
...
addView(mFooterViewContainer);
onMeasure
重写ViewGroup的onMeasure方法:
注意:onMeasure方法中只决定子View的大小,并不决定View的位置
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mTarget == null) {
ensureTarget();
}
if (mTarget == null) {
return;
}
mTarget.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth()
- getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getMeasuredHeight()
- getPaddingTop() - getPaddingBottom(),
MeasureSpec.EXACTLY));
mHeadViewContainer.measure(MeasureSpec.makeMeasureSpec(
mHeaderViewWidth, MeasureSpec.EXACTLY), MeasureSpec
.makeMeasureSpec(mHeaderViewHeight, MeasureSpec.EXACTLY));
mFooterViewContainer.measure(MeasureSpec.makeMeasureSpec(
mFooterViewWidth, MeasureSpec.EXACTLY), MeasureSpec
.makeMeasureSpec(mFooterViewHeight, MeasureSpec.EXACTLY));
...
//查找在ViewGroup的index
mHeaderViewIndex = -1;
for (int index = 0; index < getChildCount(); index++) {
if (getChildAt(index) == mHeadViewContainer) {
mHeaderViewIndex = index;
break;
}
}
mFooterViewIndex = -1;
for (int index = 0; index < getChildCount(); index++) {
if (getChildAt(index) == mFooterViewContainer) {
mFooterViewIndex = index;
break;
}
}
}
getChildDrawingOrder
重写ViewGroup的getChildDrawingOrder,由于HeaderView和FooterView刚开始是不显示的,分别隐藏在屏幕的上方和下方,因此,需要将它们两个的绘制顺序调整到最后。getChildDrawingOrder方法的含义是第i次应该绘制哪一个childView
/**
* 孩子节点绘制的顺序
*
* @param childCount
* @param i
* @return
*/
@Override
protected int getChildDrawingOrder(int childCount, int i) {