[置顶] PullToRefreshListView总结

PullToRefreshListView工作原理:
       整体是个LinearLayout,包含一个Header,一个Footer,中间夹着一个ListView,其中Header和Footer的位置居于不可见范围内。
以竖向下拉为例,当接收到下拉手势时,如果ListView内容居于置顶位置,则LinearLayout向下滚动,显示出Header的内容,当Header内容展示完全后,如果用户松开,则进行刷新操作,当刷新完成后,LinearLayout向上滚动,回到初始位置。如果Header内容未展示完全,用户即松开,则LinearLayout直接回到初始位置。如果收到下拉手势时,ListView内容居于未置顶位置,LinearLayout会将手势交给ListView消费,ListView进行内容滑动。

问题一. LinearLayout如何加入header和footer
1.首先,PullToRefreshBase的 init()方法中(构造函数中调用)
// Finally update the UI for the modes
updateUIForMode();

2. 看下updateUIForMode()方法
/**
* Updates the View State when the mode has been set. This does not do any
* checking that the mode is different to current state so always updates.
*/
protected void updateUIForMode() {
// We need to use the correct LayoutParam values, based on scroll
// direction
final LinearLayout.LayoutParams lp = getLoadingLayoutLayoutParams();

// Remove Header, and then add Header Loading View again if needed
if (this == mHeaderLayout.getParent()) {
removeView(mHeaderLayout);
}
if (mMode.showHeaderLoadingLayout()) {
addViewInternal(mHeaderLayout, 0, lp);
}

// Remove Footer, and then add Footer Loading View again if needed
if (this == mFooterLayout.getParent()) {
removeView(mFooterLayout);
}
if (mMode.showFooterLoadingLayout()) {
addViewInternal(mFooterLayout, lp);
}

// Hide Loading Views
refreshLoadingViewsSize();

// If we're not using Mode.BOTH, set mCurrentMode to mMode, otherwise
// set it to pull down
mCurrentMode = (mMode != Mode.BOTH) ? mMode : Mode.PULL_FROM_START;
}
       其中 addViewInternal() 是LinearLayout将headerLayout和footerLayout添加的位置。

问题二.如何实现初始位置中不展示header和footer的?
看到问题一中, refreshLoadingViewSize()方法
/**
* Re-measure the Loading Views height, and adjust internal padding as
* necessary
*/
protected final void refreshLoadingViewsSize() {
final int maximumPullScroll = (int) (getMaximumPullScroll() * 1.2f);

int pLeft = getPaddingLeft();
int pTop = getPaddingTop();
int pRight = getPaddingRight();
int pBottom = getPaddingBottom();

switch (getPullToRefreshScrollDirection()) {
case HORIZONTAL:
if (mMode.showHeaderLoadingLayout()) {
mHeaderLayout.setWidth(maximumPullScroll);
pLeft = -maximumPullScroll;
} else {
pLeft = 0;
}

if (mMode.showFooterLoadingLayout()) {
mFooterLayout.setWidth(maximumPullScroll);
pRight = -maximumPullScroll;
} else {
pRight = 0;
}
break;

case VERTICAL:
if (mMode.showHeaderLoadingLayout()) {
mHeaderLayout.setHeight(maximumPullScroll);
pTop = -maximumPullScroll;
} else {
pTop = 0;
}

if (mMode.showFooterLoadingLayout()) {
mFooterLayout.setHeight(maximumPullScroll);
pBottom = -maximumPullScroll;
} else {
pBottom = 0;
}
break;
}

if (DEBUG) {
Log.d(LOG_TAG, String.format(
"Setting Padding. L: %d, T: %d, R: %d, B: %d", pLeft, pTop,
pRight, pBottom));
}
setPadding(pLeft, pTop, pRight, pBottom);
}

问题三.PullToRefreshListView为何添加两个Header 两个Footer
PullToRefreshBase添加
PullToRefreshListView的ListView也添加了
但默认展示中只展示1个 两者互相切换

问题四. LinearLayout如何拦截手势操作
      onInterceptTouchEvent和onTouchEvent配合
      以竖向下拉为例
1.首先,onInterceptTouchEvent接收到ActionDown事件
    如果此时ListView内容居于置顶位置,则记录初始点击位置
//判断ListView内容是否居于置顶位置
private boolean isFirstItemVisible() {
final Adapter adapter = mRefreshableView.getAdapter();

if (null == adapter || adapter.isEmpty()) { //listView中无内容
if (DEBUG) {
Log.d(LOG_TAG, "isFirstItemVisible. Empty View.");
}
return true;
} else {
/**
* This check should really just be:
* mRefreshableView.getFirstVisiblePosition() == 0, but PtRListView
* internally use a HeaderView which messes the positions up. For
* now we'll just add one to account for it and rely on the inner
* condition which checks getTop().
*/
if (mRefreshableView.getFirstVisiblePosition() <= 1) { // 显示ListView第一个元素
final View firstVisibleChild = mRefreshableView.getChildAt(0);
if (firstVisibleChild != null) {
return firstVisibleChild.getTop() >= mRefreshableView.getTop(); //ListView第一个元素的顶部与ListView的顶部相符或者在ListView顶部的下面
}
}
}

return false;
}

2.其次是接收到ActionMove事件, 如果滑动距离大于最小滑动距离,且竖向滑动距离大于横向滑动距离,则 认为处于滑动状态,将mIsBeingDragged设置true,onInterceptTouchEvent返回true。

3.这样交给LinearLayout的onTouchEvent处理,在 ActionMove事件的处理中,调用pullEvent()方法, 获取滑动距离、将 LinearLayout滑动相应距离(显示header), 同时header和footer根据滑动距离进行处理(动画)。 如果滑动距离超过header高度,更改状态为松手刷新,同时传递给LoadingLayout进行处理。 如果滑动距离不超过header高度,更改状态为下拉刷新,同时传递给LoadingLayout进行处理。

问题五.PullToRefreshListView控件是如何封装给用户直接使用的?如果不是封装的控件,自己如何实现PullToRefresh功能?

问题六.PullToRefreshListView如何实现xml属性设置和读取

问题七.PullToRefreshListView如何实现滚动、刷新等动画

问题八.PullToRefreshListView用到的设计模式


你可能感兴趣的:(android)