下拉刷新的简单实现

看了Taurus的下拉刷新代码,参考它的主要思想自己也动手写一个下拉刷新。

首先看一下实现了之后,demo项目中的布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<com.yzx.TestPullToRefreshView
android:id="@+id/pull_to_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list_view"
android:divider="@null"
android:dividerHeight="0dp"
android:fadingEdge="none"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.yzx.TestPullToRefreshView>

这里的TestPullToRefreshView就是我们的下拉刷新自定义view。主要的思路就是在TestPullToRefreshView中添加一个RefreshView作为刷新的View。然后通过onTouchEvent来计算位移,再通过offsetTopAndBottom函数来控制ListView和RefreshView所显示的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEventCompat.ACTION_POINTER_DOWN:
final int index = MotionEventCompat.getActionIndex(ev);
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
final float initialMotionY = getMotionEventY(ev, mActivePointerId);
if (initialMotionY == -1) {
return false;
}
mInitialMotionY = initialMotionY;
break;
case MotionEvent.ACTION_MOVE: {
int offset = calOffset(ev);
if (offset == -1) {
return false;
}
setTargetOffsetTop(offset);
break;
}
case MotionEventCompat.ACTION_POINTER_UP:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
return false;
}
}
return true;
}

calOffset方法根据下拉距离计算RefreshView显示大小,setTargetOffsetTop用来实现ListView的上下滑动。
下面方法是根据Taurus中的计算方法来计算位移值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private int calOffset(MotionEvent ev) {
final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
if (pointerIndex < 0) {
return -1;
}
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float yDiff = y - mInitialMotionY;
final float scrollTop = yDiff * DRAG_RATE;
mCurrentDragPercent = scrollTop / mTotalDragDistance;
if (mCurrentDragPercent < 0) {
return -1;
}
float boundedDragPercent = Math.min(1f, Math.abs(mCurrentDragPercent));
float extraOS = Math.abs(scrollTop) - mTotalDragDistance;
float slingshotDist = mTotalDragDistance;
float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, slingshotDist * 2) / slingshotDist);
float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow(
(tensionSlingshotPercent / 4), 2)) * 2f;
float extraMove = (slingshotDist) * tensionPercent / 2;
int targetY = (int) ((slingshotDist * boundedDragPercent) + extraMove);
return targetY - mCurrentOffsetTop;
}

下面方法是具体条用offsetTopAndBottom的方法。

1
2
3
4
5
6
7
8
private void setTargetOffsetTop(int offset, boolean requiresUpdate) {
mTarget.offsetTopAndBottom(offset);
mTestDrawable.offsetTopAndBottom(offset);
mCurrentOffsetTop = mTarget.getTop();
if (requiresUpdate && android.os.Build.VERSION.SDK_INT < 11) {
invalidate();
}
}