您的位置:首页 > 其它

自定义View刷新头部,已适配AbsListView、RecyclerView

2017-06-24 21:28 316 查看


简单介绍

WarpLayout

用于捕捉手势操作并决定显示内容。具有以下职能:

计算HeaderView、ContentView、FooterView的显示位置和置放

手势捕捉判断

是否可显示头部、脚部视图的通用判断。具体判断交由Mover进行

AttachView

添加头部和脚步View的抽象View。具有以下职能:

根据展示高度显示不同的内容

根据展示高度判断 WarpLayout 是否可刷新操作

AttachViewMover

根据 ContentView 类型不同采取不同的判断逻辑。

代码

WarpLayout

public boolean onInterceptTouchEvent(MotionEvent event) {

boolean headerMovable = false;
boolean footerMovable = false;

switch (event.getAction()) {

// 所以在此记录按下位置,判断是否消费点击时间
case MotionEvent.ACTION_DOWN:
mDownX = event.getX();
mDownY = event.getY();
mLastY = mDownY;
mIsVerticalMove = false;
//              mScroller.forceFinished(true);
break;

// 拦截满足条件的滑动事件,用来显示头部和脚部
case MotionEvent.ACTION_MOVE:
float currY = event.getY();
float absDx = Math.abs(event.getX() - mDownX);
float absDy = Math.abs(currY - mDownY);

// 最小滑动距离、手指滑动在 X、Y方向上的比例筛选
if (absDx >= mTouchSlop || absDy >= mTouchSlop) {
if (absDy / absDx > SCROLL_JUDGE_VERTICAL_HORIZONTAL) {
mIsVerticalMove = true;
}
}
if (mIsVerticalMove) {
headerMovable = isMovable4HeaderView(currY > mLastY);// ViewGroup下滑
footerMovable = isMovable4FooterView(currY < mLastY);// ViewGroup上滑
}
break;
}
Log.d("RefreshWarpLayout", "headerMovable:" + headerMovable + ";footerMovable:" + footerMovable);
return headerMovable || footerMovable;
}

public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {

// onInterceptTouchEvent 中 ACTION_DOWN 事件一定返回false
// MotionEvent.ACTION_DOWN 此事件可能在此处捕获
case MotionEvent.ACTION_DOWN:
// The incident is unlikely to happen
break;

// 消费 move 事件
case MotionEvent.ACTION_MOVE:

// 如果滑动没有到指定的距离,积累滑动事件
float fingerScrollY = event.getY() - mLastY;
if (Math.abs(fingerScrollY) < SCROLL_REFRESH_UI_VIEW_MINI_DISTANCE) {
return true;
}

// 申请想要的偏移量:offsetY4Ask,手指滑动距离 * 阻尼
int offsetY4Ask = (int) (fingerScrollY * (1 - damping));
mLastY = event.getY();

// 计算允许的偏移量
int offsetY4End = planMoveDistance(offsetY4Ask);

// 截取反向操作,下滑 --> 上滑:导致不合理出现 FooterView。将这种情况标记为非法操作
int scrolledDistanceAgo = getScrollY();
int scrolledNumAfter = scrolledDistanceAgo + offsetY4End;
mIsIllegalMove = scrolledDistanceAgo * scrolledNumAfter < 0;
//              Log.d("RefreshWarpLayout", "mIsIllegalMove:" + mIsIllegalMove + ";ago【" + scrolledDistanceAgo + "】,after【" + scrolledNumAfter + "】");

// 恢复接近初始样子,但是留一点空间方便下次判断
if (mIsIllegalMove) {
// -1、1为 保留态,方便下次滑动,如:下拉 - 上滑回归 - 下拉 ... 等情况
// 因为此前忽略了 mTouchSlop 单位一下的移动量,所以避过了会产生BUG的临界值
if (Math.abs(scrolledDistanceAgo) == 1) {
offsetY4End = 0;
}else {
offsetY4End = (scrolledDistanceAgo < 0 ? -1 : 1) - scrolledDistanceAgo;
}
}

// 移动操作
scrollBy(0, offsetY4End);
callMoved();
break;

// 取消事件,以后的手势操作不会传递到本 View
case MotionEvent.ACTION_CANCEL:
// 松手操作
case MotionEvent.ACTION_UP:
mIsIllegalMove = false;
autoSelectActionAfterUp();
break;
}

return true;
}

private int planMoveDistance(int offsetY4Ask){
int offsetY4End = -offsetY4Ask;// 方向相反
// ViewGroup向下滚动,返回值 < 0,只有HeaderView全部显示了才不能向下滚动
// 最高点是:-HeaderView.height
if (offsetY4Ask > 0) {
int distanceLeft = Math.abs(-mHeaderView.getMeasuredHeight() - getScrollY());
if (offsetY4Ask > distanceLeft) {
offsetY4End = -distanceLeft;
}
}
// ViewGroup向上滚动,返回值 > 0,只有FooterView全部显示了才不能向上滚动
// 最低点是:FooterView.height
else if (offsetY4Ask < 0) {
int distanceLeft =  Math.abs(mFooterView.getMeasuredHeight() - getScrollY());
if (Math.abs(offsetY4Ask) > distanceLeft) {
offsetY4End = distanceLeft;
}
}
return offsetY4End;
}


AttachView

继承
AbsAttachView
实现添加的View

public class DefaultFooterView extends AbsAttachView {

private TextView tvShow;
private float canLoadPointPercent = 0.75f;

public DefaultFooterView(Context context) {
super(context);
}

@Override
public int getLayoutId() {
return R.layout.v_refresh_default_head;
}

@Override
public void bindView(View layoutView) {
tvShow = (TextView) layoutView.findViewById(R.id.tv_textView);
tvShow.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
}

@Override
public boolean canLoad(int offsetY) {
return Math.abs(offsetY) >= getMeasuredHeight() * canLoadPointPercent;
}

@Override
public void onMove(int offsetY) {
if (canLoad(offsetY)) {
onReady();
}else {
onNormal();
}
}

private void onNormal() {
tvShow.setText("上拉加载更多");
}

private void onReady() {
tvShow.setText("松手加载更多");
}

@Override
protected void onLoading() {
tvShow.setText("正在加载更多...");
}

@Override
protected void onComplete() {
tvShow.setText("加载更多完成");
}
}


AttachViewMover

用于适配不同的 ContentView 判断是否到顶或到底

private static class AbsListViewMover extends IAttachViewMover {

private AbsListView view = null;

AbsListViewMover(View contentView) {
view = (AbsListView) contentView;
}

@Override
public boolean canMovable4HeaderView() {
return view != null
// 第一个Item是否可见
&& view.getFirstVisiblePosition() == 0
// 没有数据也显示刷新头部
// ListView滑动到顶部没有,由于之前的判断,此时第一个ChildView展示的一定是第一个Item的内容
&& (view.getChildAt(0) == null || view.getChildAt(0).getTop() >= 0);
}

@Override
public boolean canMovable4FooterView() {
return view != null
// AbsListView适配器不能为空
&& view.getAdapter() != null
// 最后一个Item是否可见
&& view.getLastVisiblePosition() == view.getAdapter().getCount() - 1
// ListView滑动到底部没有,由于之前的判断,此时最后一个ChildView展示的一定是最后一个Item的内容。
// ChildView.bottom 参考的是 AbsListView,而不是本View
&& view.getHeight() == view.getChildAt(view.getChildCount() - 1).getBottom();

}
}


使用实例

XML

<org.hjf.view.refreshlayout.RefreshWarpLayout
android:id="@+id/refresh_layout"
android:layout_width="match_parent"
android:layout_height="@dimen/y600">

<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</org.hjf.view.refreshlayout.RefreshWarpLayout>


JAVA

refreshWarpLayout.setLoadingListener(new RefreshWarpLayout.RefreshLoadingListener() {
@Override
public void onRefresh() {
TaskMgr.execTaskOnBGThread(Task.RUNNABLE_PREFERENCE_HIGH, new Task() {
@Override
protected void doTask() {
try {
TimeUnit.MILLISECONDS.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
// 通知动作完成
refreshWarpLayout.loadComplete();
}
});
}
});
}

@Override
public void onLoadMore() {
TaskMgr.execTaskOnBGThread(Task.RUNNABLE_PREFERENCE_HIGH, new Task() {
@Override
protected void doTask() {
try {
TimeUnit.MILLISECONDS.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
refreshWarpLayout.loadComplete();
}
});
}
});
}
});


具体例子在Demo中,还会更具需求和适配更多的View,还有更多东西哦
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐