带下拉刷新的ScrollView

整体来说逻辑上比较简单,遇到过一个小问题,就是 如果当前类不去拦截事件传递,那么自身的 onTouch方法是接收不到down事件的,所以 down事件的坐标来源可以从 dispath分发来,也可以 拦截方法中拿到。

public abstract class RefreshScrollView extends ScrollView {
    protected static final String TAG = "生命周期";
    protected Context mContext;
    private Scroller scroller;
    private LinearLayout mainView;                      //主布局--包含头布局和子类实现布局
    private boolean loadOnce;                           //只执行一次
    private LinearLayout headView;                      //下拉头
    private TextView headTv;                            //下拉头文字

    private int height;                                 //下拉头高度,值为负数
    private MarginLayoutParams headViewLayoutParams;    //下拉头布局参数
    private final static int REFRESH_NORMAL = 0;        //默认状态
    private final static int REFRESH_PULL_DOWN = 1;     //正在下拉状态
    private final static int REFRESH_RELEASE = 2;       //释放刷新状态
    private final static int REFRESH_ING = 3;           //正在刷新状态
    private final static int REFRESH_SUCCESS = 4;       //刷新成功状态
    private final static int REFRESH_ERROR = 5;         //刷新失败状态
    private int refreshCur = REFRESH_NORMAL;            //当前状态
    private static final int DEMP = 2;                  //阻尼系数
    protected boolean refreshAble = true;               //是否允许进行下拉刷新
    private static final int FLAG = 101010;             //刷新成功时,Msg.what = 101010
    private int lastY;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case FLAG:
                    scroller.startScroll(0, 0, 0, height, 2000);
                    invalidate();
                    refreshCur = REFRESH_NORMAL;
                    break;
            }
        }
    };

    public RefreshScrollView(Context context) {
        super(context);
        mContext = context;
        scroller = new Scroller(context);
        setOverScrollMode(View.OVER_SCROLL_NEVER);//魅族下拉悬停
        initView();
    }

    /**
     * 初始化布局文件
     */
    private void initView() {
        mainView = (LinearLayout) inflate(mContext, R.layout.market_refresh_scrollview_mainview, null);
        mainView.addView(createView());
        addView(mainView);
    }


    /**
     * 布局隐藏下拉头
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (changed && !loadOnce) {
            loadOnce = true;
            headView = (LinearLayout) mainView.getChildAt(0);
            headTv = (TextView) mainView.findViewById(R.id.textView);
            height = -headView.getHeight();
            headViewLayoutParams = (MarginLayoutParams) headView.getLayoutParams();
            headViewLayoutParams.topMargin = height;
            headView.setLayoutParams(headViewLayoutParams);
        }
    }


    /**
     * 下拉头显示逻辑
     */

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastY = (int) ev.getY();
                Log.i("Y值", "onInterceptTouchEvent: down时 lastY " + lastY);
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_MOVE:
                int moveY = (int) (ev.getY() - lastY);  //每次移动所滑动的距离
                lastY = (int) ev.getY();
                Log.i("Y值", "onTouchEvent: move时 moveY " + moveY);
                Log.i("Y值", "onTouchEvent: move时 lastY" + lastY);
                int marginTop = moveY / DEMP + headView.getTop();
                Log.i("Y值", "onTouchEvent: move时 marginTop" + marginTop);
                Log.i("Y值", "onTouchEvent: move时 height" + height);
                //正在刷新、刷新成功、失败、Scroller动画执行时禁止下拉,所以把down时的结束动画隐藏掉
                if (getScrollY() == 0 && refreshCur != REFRESH_ING && refreshCur != REFRESH_SUCCESS && refreshCur != REFRESH_ERROR && !scroller.computeScrollOffset() && marginTop > height) {
                    if (refreshAble) {
                        pullDownHeadView(marginTop);
                        return true;
                    }
                }
                break;
            default:
                if (refreshCur == REFRESH_RELEASE) {
                    //TODO 请求网络
                    refreshingHeadView();
                    requestNet();
                }
                if (refreshCur == REFRESH_PULL_DOWN) {
                    //TODO 隐藏下拉头
                    hideHeadView();
                }
        }
        return super.onTouchEvent(ev);
    }


    /**
     * 隐藏下拉
     */
    protected void hideHeadView() {
        refreshCur = REFRESH_NORMAL;
        headTv.setText("下拉刷新");
        headViewLayoutParams.topMargin = height;
        headView.setLayoutParams(headViewLayoutParams);
    }

    /**
     * 下拉刷新
     */
    protected void pullDownHeadView(int marginTop) {
        headViewLayoutParams.topMargin = marginTop;
        headView.setLayoutParams(headViewLayoutParams);
        if (marginTop > 0) {
            refreshCur = REFRESH_RELEASE;
            headTv.setText("释放刷新");
        } else {
            refreshCur = REFRESH_PULL_DOWN;
            headTv.setText("下拉刷新");
        }
    }

    /**
     * 正在刷新
     */
    protected void refreshingHeadView() {
        refreshCur = REFRESH_ING;
        headTv.setText("正在刷新");
        headViewLayoutParams.topMargin = 0;
        headView.setLayoutParams(headViewLayoutParams);
    }

    /**
     * 刷新成功
     * 由于在子类handler中需要调用此函数,需要判断是由下拉触发的此刷新成功
     * 如果是onResume进行的网络请求,则不进行刷新成功动画
     */
    protected void successHeadView() {
        if (refreshCur == REFRESH_ING) {
            refreshCur = REFRESH_SUCCESS;
            headTv.setText("刷新成功");
            handler.sendEmptyMessageDelayed(FLAG, 1000);
        }
    }

    /**
     * 刷新失败
     */

    protected void errorHeadView() {
        if (refreshCur == REFRESH_ING) {
            refreshCur = REFRESH_ERROR;
            headTv.setText("刷新失败");
            handler.sendEmptyMessageDelayed(101010, 1000);
        }
    }

    @Override
    public void computeScroll() {
        if (scroller.computeScrollOffset()) {
            int marginTop = scroller.getCurrY();
            headViewLayoutParams.topMargin = marginTop;
            headView.setLayoutParams(headViewLayoutParams);
            invalidate();
        }
        super.computeScroll();
    }

    /**************子类实现函数****************/

    /**
     * 子类具体View
     */
    public abstract View createView();

    /**
     * 刷新时,请求网络
     */
    protected abstract void requestNet();

    /**
     * 指示器文字
     */
    public abstract CharSequence getTabTitle();

    /**
     * 项目中此方法用于开始网络请求
     */
    protected abstract void onResume();

    /**
     * 项目中此方法用于暂停网络请求
     */
    protected abstract void onPause();

    /**
     * 黑白版颜色
     */
    protected abstract void changeDarkAndLightStyle();


在 UniApp 中,可以使用 `scroll-view` 组件实现下拉刷新的功能。下面是一个简单的示例代码: ```vue <template> <view> <!-- 下拉刷新头部 --> <view class="refresh-header" :style="{ marginTop: `${refreshTop}px` }"> <text v-if="refreshStatus === 'normal'">下拉刷新</text> <text v-else-if="refreshStatus === 'pulling'">释放刷新</text> <text v-else-if="refreshStatus === 'refreshing'">正在刷新...</text> </view> <!-- scroll-view 包裹的内容 --> <scroll-view class="scroll-view" scroll-y @scrolltolower="loadMore" @scroll="scroll" @touchstart="touchStart" @touchend="touchEnd"> <!-- 内容列表 --> <view class="content-list"> <!-- 列表项 --> <view v-for="(item, index) in list" :key="index">{{ item }}</view> </view> <!-- 加载更多 --> <view v-if="showLoadMore" class="load-more"> <text>加载中...</text> </view> </scroll-view> </view> </template> <script> export default { data() { return { refreshTop: -60, // 下拉刷新头部的高度 refreshStatus: 'normal', // 下拉刷新状态 list: [], // 列表数据 showLoadMore: false, // 是否显示加载更多 }; }, methods: { scroll(e) { // 获取 scroll-view 的滚动位置 const scrollTop = e.detail.scrollTop; // 根据滚动位置判断是否触发下拉刷新 if (scrollTop < this.refreshTop && this.refreshStatus !== 'refreshing') { this.refreshStatus = 'pulling'; } else if (scrollTop >= this.refreshTop && this.refreshStatus !== 'refreshing') { this.refreshStatus = 'normal'; } }, touchStart() { // 记录 touchstart 时的滚动位置 this.startScrollTop = this.scrollTop; }, touchEnd() { // 判断是否触发下拉刷新 if (this.startScrollTop < this.refreshTop && this.refreshStatus === 'pulling') { this.refreshStatus = 'refreshing'; // 执行刷新操作,例如请求数据 this.refresh(); } }, refresh() { // 模拟异步请求数据 setTimeout(() => { this.list = [1, 2, 3, 4, 5]; this.refreshStatus = 'normal'; }, 2000); }, loadMore() { // 模拟异步加载更多数据 setTimeout(() => { const start = this.list.length + 1; const end = start + 5; for (let i = start; i <= end; i++) { this.list.push(i); } // 隐藏加载更多的提示 this.showLoadMore = false; }, 2000); }, }, }; </script> <style> .refresh-header { display: flex; justify-content: center; align-items: center; height: 60px; } .scroll-view { flex: 1; } .content-list { padding: 20px; } .load-more { display: flex; justify-content: center; align-items: center; height: 40px; background-color: #f4f4f4; } </style> ``` 以上代码实现了一个简单的下拉刷新和加载更多的功能,当用户下拉超过下拉刷新头部指定的高度时,释放后会触发刷新操作。加载更多则是在滚动到底部时触发并加载更多数据。你可以根据实际需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值