自定义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
原创粉丝点击