自定义View刷新头部,已适配AbsListView、RecyclerView
来源:互联网 发布:ins相机软件下载 编辑:程序博客网 时间:2024/05/01 22:09
简单介绍
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,还有更多东西哦
阅读全文
0 0
- 自定义View刷新头部,已适配AbsListView、RecyclerView
- Android Recyclerview适配器 加载头部 以及自定义View
- Android View 下拉刷新之头部效果自定义 [水]
- 自定义头部的下拉刷新
- 自定义带头部和尾部的RecyclerView
- Android从零开搞系列:自定义View(2)继承RecyclerView实现下拉刷新和加载更多
- 自定义View刷新问题
- 优雅地为RecyclerView加上头部、下拉刷新、自动加载
- RecyclerView系列(一)添加头部、尾部View
- android 下拉刷新可自定义刷新头部和底部
- RecyclerView子View不刷新(RequestLayout无效),RecyclerView.mEatRequestLayout
- android自定义view的刷新
- 自定义View之圆环刷新
- Android 自定义万能刷新View
- TwinklingRefreshLayout 支持下拉刷新和上拉加载的 RefreshLayout,自带越界回弹效果,支持 RecyclerView,AbsListView,ScrollView,We
- TwinklingRefreshLayout 支持下拉刷新和上拉加载的 RefreshLayout,自带越界回弹效果,支持 RecyclerView,AbsListView,ScrollView等
- 自定义RecyclerView实现下拉刷新,加载更多
- 给RecyclerView自定义上拉,下拉刷新
- ZYNQ系统中实现FAT32文件系统的SD卡读写 之一 硬件介绍
- Hadoop 2.7.2 CentOS x64 环境搭建
- 001. Spring MVC HelloWorld
- AC自动机模板
- poj2794dp
- 自定义View刷新头部,已适配AbsListView、RecyclerView
- lync
- JavaScript闭包基本概念
- 2.7
- jsp页面引入(包含)其他页面的三种实现方式
- 归并排序
- Docker入门六部曲——容器
- Android SDK下的各目录介绍
- Python设计模式-观察者模式