使用android-Ultra-Pull-To-Refresh实
2018-05-30 本文已影响0人
flycode
官方地址
简单使用
PtrClassicDefaultHeader header = new PtrClassicDefaultHeader(this);
ptrFrameLayout.addPtrUIHandler(header);
ptrFrameLayout.setHeaderView(header);
ptrFrameLayout.setPtrHandler(new PtrDefaultHandler() {
@Override
public void onRefreshBegin(PtrFrameLayout frame) {
refresh();
}
});
PtrUIHandler接口
它的实现类为PtrClassicDefaultHeader、MaterialHeader、StoreHouseHeader、PtrUIHandlerHolder。
1、前三者为下拉刷新不同样式的实现类,可通过此接口自定义下拉刷新样式,接口方法如下:
- 开始下拉,此方法之后status = 2(PTR_STATUS_PREPARE)
public void onUIRefreshPrepare(PtrFrameLayout frame);
- 开始刷新,此方法之后status = 3(PTR_STATUS_LOADING)
public void onUIRefreshBegin(PtrFrameLayout frame);
- 刷新完成,此方法之后status = 4(PTR_STATUS_COMPLETE)
public void onUIRefreshComplete(PtrFrameLayout frame);
- 恢复UI ,此方法之后status = 1(PTR_STATUS_INIT)
public void onUIReset(PtrFrameLayout frame);
- 下拉刷新过程中位置回调
public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator);
2、PtrUIHandlerHolder是PtrUIHandler的持有者。内置静态方法addHandler、removeHandler,用于添加和移除PtrUIHandler。一个PtrUIHandlerHolder对象可以看作是链表结构的一个节点,在实现PtrUIHandler上述5个方法时,遍历所有的链表节点,调用对应的接口方法。
PtrFrameLayout
自定义属性
// 头部
<attr name="ptr_header" format="reference" />
// 内容
<attr name="ptr_content" format="reference" />
// 阻尼系数,默认1.7f,越大感觉下拉时越吃力
<attr name="ptr_resistance" format="float" />
// 触发刷新时移动的位置比例,默认1.2f,移动达到头部高度1.2倍时可触发刷新操作
<attr name="ptr_ratio_of_header_height_to_refresh" format="float" />
// 回弹延时,默认 200ms,回弹到刷新高度所用时间
<attr name="ptr_duration_to_close" format="integer" />
// 头部回弹时间,默认1000ms
<attr name="ptr_duration_to_close_header" format="integer" />
// 下拉刷新/释放刷新,默认释放刷新
<attr name="ptr_pull_to_fresh" format="boolean" />
// 刷新时保持头部,默认true
<attr name="ptr_keep_header_when_refresh" format="boolean" />
onMeasure方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 测量headerView的宽高
if (mHeaderView != null) {
measureChildWithMargins(mHeaderView, widthMeasureSpec, 0, heightMeasureSpec, 0);
MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams();
mHeaderHeight = mHeaderView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
mPtrIndicator.setHeaderHeight(mHeaderHeight);
}
// 对ContentView进行测量
if (mContent != null) {
measureContentView(mContent, widthMeasureSpec, heightMeasureSpec);
}
}
private void measureContentView(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
getPaddingTop() + getPaddingBottom() + lp.topMargin, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
调用measureChildWithMargins方法对HeaderView进行测量,获得headerView高度
调用getChildMeasureSpec(int spec, int padding, int childDimension)获得子View的MeasureSpec,调用child.measure(childWidthMeasureSpec, childHeightMeasureSpec),对ContentView进行测量
onLayout方法
protected void onLayout(boolean flag, int i, int j, int k, int l) {
layoutChildren();
}
private void layoutChildren() {
int offset = mPtrIndicator.getCurrentPosY();
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
// 对HeaderView进行layout,主要是top的计算
if (mHeaderView != null) {
MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams();
final int left = paddingLeft + lp.leftMargin;
// enhance readability(header is layout above screen when first init)
final int top = -(mHeaderHeight - paddingTop - lp.topMargin - offset);
final int right = left + mHeaderView.getMeasuredWidth();
final int bottom = top + mHeaderView.getMeasuredHeight();
mHeaderView.layout(left, top, right, bottom);
}
// 对ContentView进行layout
if (mContent != null) {
if (isPinContent()) {
offset = 0;
}
MarginLayoutParams lp = (MarginLayoutParams) mContent.getLayoutParams();
final int left = paddingLeft + lp.leftMargin;
final int top = paddingTop + lp.topMargin + offset;
final int right = left + mContent.getMeasuredWidth();
final int bottom = top + mContent.getMeasuredHeight();
mContent.layout(left, top, right, bottom);
}
}
dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent e) {
if (!isEnabled() || mContent == null || mHeaderView == null) {
return dispatchTouchEventSupper(e);
}
int action = e.getAction();
switch (action) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mPtrIndicator.onRelease();
if (mPtrIndicator.hasLeftStartPosition()) {
// 松手的位置大于初始位置
onRelease(false);
if (mPtrIndicator.hasMovedAfterPressedDown()) {
sendCancelEvent();
return true;
}
return dispatchTouchEventSupper(e);
} else {
return dispatchTouchEventSupper(e);
}
case MotionEvent.ACTION_DOWN:
mHasSendCancelEvent = false;
// 记录按下的位置
mPtrIndicator.onPressDown(e.getX(), e.getY());
mScrollChecker.abortIfWorking();
// 重置水平方向阻止标记
mPreventForHorizontal = false;
// The cancel event will be sent once the position is moved.
// So let the event pass to children.
// fix #93, #102
dispatchTouchEventSupper(e);
return true;
case MotionEvent.ACTION_MOVE:
mLastMoveEvent = e;
mPtrIndicator.onMove(e.getX(), e.getY());
// 相对上次位置偏移量(阻尼系数转化后)
float offsetX = mPtrIndicator.getOffsetX();
float offsetY = mPtrIndicator.getOffsetY();
// 水平方向移动较大,事件可以继续分发下去
if (mDisableWhenHorizontalMove && !mPreventForHorizontal && (Math.abs(offsetX) > mPagingTouchSlop && Math.abs(offsetX) > Math.abs(offsetY))) {
if (mPtrIndicator.isInStartPosition()) {
mPreventForHorizontal = true;
}
}
if (mPreventForHorizontal) {
return dispatchTouchEventSupper(e);
}
boolean moveDown = offsetY > 0;
boolean moveUp = !moveDown;
boolean canMoveUp = mPtrIndicator.hasLeftStartPosition();
// disable move when header not reach top
if (moveDown && mPtrHandler != null && !mPtrHandler.checkCanDoRefresh(this, mContent, mHeaderView)) {
return dispatchTouchEventSupper(e);
}
if ((moveUp && canMoveUp) || moveDown) {
movePos(offsetY);
return true;
}
}
return dispatchTouchEventSupper(e);
}
位置更新方法
private void updatePos(int change) {
if (change == 0) {
return;
}
boolean isUnderTouch = mPtrIndicator.isUnderTouch();
// once moved, cancel event will be sent to child
if (isUnderTouch && !mHasSendCancelEvent && mPtrIndicator.hasMovedAfterPressedDown()) {
mHasSendCancelEvent = true;
sendCancelEvent();
}
// leave initiated position or just refresh complete
if ((mPtrIndicator.hasJustLeftStartPosition() && mStatus == PTR_STATUS_INIT) ||
(mPtrIndicator.goDownCrossFinishPosition() && mStatus == PTR_STATUS_COMPLETE && isEnabledNextPtrAtOnce())) {
mStatus = PTR_STATUS_PREPARE;
mPtrUIHandlerHolder.onUIRefreshPrepare(this);
}
// back to initiated position
if (mPtrIndicator.hasJustBackToStartPosition()) {
tryToNotifyReset();
// recover event to children
if (isUnderTouch) {
sendDownEvent();
}
}
// Pull to Refresh
if (mStatus == PTR_STATUS_PREPARE) {
// reach fresh height while moving from top to bottom
if (isUnderTouch && !isAutoRefresh() && mPullToRefresh
&& mPtrIndicator.crossRefreshLineFromTopToBottom()) {
tryToPerformRefresh();
}
// reach header height while auto refresh
if (performAutoRefreshButLater() && mPtrIndicator.hasJustReachedHeaderHeightFromTopToBottom()) {
tryToPerformRefresh();
}
}
// 核心移动方法
mHeaderView.offsetTopAndBottom(change);
if (!isPinContent()) {
mContent.offsetTopAndBottom(change);
}
invalidate();
if (mPtrUIHandlerHolder.hasHandler()) {
mPtrUIHandlerHolder.onUIPositionChange(this, isUnderTouch, mStatus, mPtrIndicator);
}
onPositionChange(isUnderTouch, mStatus, mPtrIndicator);
}