还在使用别人的,自定义一个PullToRefreshLayout吧(继承LinearLayout)

新建PullToRefreshLayout继承LinearLayout 

  1. public class PullToRefreshLayout extends LinearLayout {
    
        private static final int STATUS_NORMAL = 1;//普通状态
        private static final int STATUS_PULLING = 2;//正在上/下拉状态
        private static final int STATUS_RELEASE = 3;//释放可刷新
        private static final int STATUS_REFRESHING = 4;//正在刷新
    
        private int currentStatus = STATUS_NORMAL;//当前状态记录
    
        //头部布局及子View
        private View header;
        private ImageView headerIcon;
        private TextView headerInfo;
        //头高度
        private int headerHeight;
        //头部刷新监听
        private OnRefreshListener onRefreshListener;
    
        //中间的被刷新View
        private ViewGroup refresher;
    
        //底部布局及子布局
        private View footer;
        private ImageView footerIcon;
        private TextView footerInfo;
        //底部高度
        private int footerHeight;
        //底部刷新监听
        private OnLoadListener onLoadListener;
    
        //处理滚动效果
        private Scroller scroller;
        //旋转动画
        private Animation rotateAnim;
        //按下时纵坐标,纵向滑动距离(绝对值), refresh高度 ,刷新类型(无刷新,仅上拉,仅下拉,全部)
        private int startY, offsetY, measuredHeight, type;
    
        public PullToRefreshLayout(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            setOrientation(VERTICAL);
            
            //拿到自定义属性值
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PullToRefreshLayout);
            type = array.getInt(R.styleable.PullToRefreshLayout_pull_to_refresh_type, 1);
            array.recycle();//千万记得释放掉
    
            //实例化控件
            scroller = new Scroller(context);
            rotateAnim = AnimationUtils.loadAnimation(context, R.anim.common_loading);
            header = inflate(context, R.layout.common_pull_to_refresh, null);
            headerIcon = header.findViewById(R.id.common_pull_to_refresh_icon);
            headerInfo = header.findViewById(R.id.common_pull_to_refresh_info);
            footer = inflate(context, R.layout.common_pull_to_load, null);
            footerIcon = footer.findViewById(R.id.common_pull_to_load_icon);
            footerInfo = footer.findViewById(R.id.common_pull_to_load_info);
        }
    
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            addView(header, 0);//添加头布局
            refresher = (ViewGroup) getChildAt(1);//获取刷新布局
            addView(footer, getChildCount());//添加底部布局
            if (getChildCount() > 3) {
                throw new IllegalArgumentException("PullToRefreshLayout只能包含一个子View");
            }
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            for (int i = 0; i < getChildCount(); i++) {
                measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
            }
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            //super.onLayout(changed,l,t,r,b);刷新的时候会卡
            //获取测量的高度
            headerHeight = headerHeight == 0 ? header.getMeasuredHeight() : headerHeight;
            footerHeight = footerHeight == 0 ? footer.getMeasuredHeight() : footerHeight;
            measuredHeight = measuredHeight == 0 ? getMeasuredHeight() : measuredHeight;
            //将头文件向上隐藏
            header.layout(l, -headerHeight, r, 0);
            refresher.layout(l, 0, r, measuredHeight);
            //将底部文件向下隐藏
            footer.layout(l, measuredHeight, r, measuredHeight + footerHeight);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    startY = (int) ev.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (type == 1) {//无刷新状态返回false,不处理touch事件
                        return false;
                    }
                    if (currentStatus == STATUS_REFRESHING) {//当前正在刷新,屏蔽掉touch事件
                        return true;
                    }
                    if ((ev.getRawY() > startY && isViewGroupTopper()) || (ev.getRawY() < startY && isViewGroupFooter())) {//向下滑动且refresher在最顶部||向上滑动且refresher在最底部
                        return true;
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    if (currentStatus != STATUS_NORMAL) {//手指抬起时不是正常状态
                        return true;
                    }
                    break;
            }
            return super.onInterceptTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_MOVE:
                    if (currentStatus == STATUS_REFRESHING) {//正在刷新直接return掉
                        return true;
                    }
                    offsetY = Math.abs((int) ((event.getRawY() - startY) * 0.3));//计算纵向偏移 阻尼0.3f
                    if (event.getRawY() > startY && isViewGroupTopper() && (type == 2 || type == 4)) {//向下滑且refresher在最顶部且类型为仅下拉或全部
                        updateHeaderStatus(offsetY < headerHeight ? STATUS_PULLING : STATUS_RELEASE);
                        offsetY = offsetY < headerHeight ? offsetY : headerHeight;
                        scrollTo(getScrollX(), -offsetY);
                    } else if (event.getRawY() < startY && isViewGroupFooter() && (type == 3 || type == 4)) {//向上滑且refresher在最底部且类型为仅上拉或全部
                        updateFooterStatus(offsetY < footerHeight ? STATUS_PULLING : STATUS_RELEASE);
                        offsetY = offsetY < footerHeight ? offsetY : footerHeight;
                        scrollTo(getScrollX(), offsetY);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    if (event.getRawY() > startY && isViewGroupTopper() && (type == 2 || type == 4)) {
                        if (currentStatus == STATUS_PULLING) {//状态是正在拉动就松手 回滚布局
                            updateHeaderStatus(STATUS_NORMAL);
                            scroller.startScroll(getScrollX(), getScrollY(), 0, offsetY, 600);
                            postInvalidate();
                        } else if (currentStatus == STATUS_RELEASE) {//状态是可刷新 
                            updateHeaderStatus(STATUS_REFRESHING);
                            offsetY = headerHeight;
                        }
                    } else if (event.getRawY() < startY && isViewGroupFooter() && (type == 3 || type == 4)) {
                        if (currentStatus == STATUS_PULLING) {
                            updateFooterStatus(STATUS_NORMAL);
                            scroller.startScroll(getScrollX(), getScrollY(), 0, -offsetY, 600);
                            postInvalidate();
                        } else if (currentStatus == STATUS_RELEASE) {
                            updateFooterStatus(STATUS_REFRESHING);
                            offsetY = footerHeight;
                        }
                    }
                    break;
            }
            return true;
        }
    
        //重写computeScroll
        @Override
        public void computeScroll() {
            if (scroller.computeScrollOffset()) {
                ((View) refresher.getParent()).scrollTo(scroller.getCurrX(), scroller.getCurrY());
                postInvalidate();
            }
            super.computeScroll();
        }
    
        private void updateHeaderStatus(int status) {
            currentStatus = status;
            if (currentStatus == STATUS_NORMAL) {
                headerIcon.clearAnimation();
            } else if (currentStatus == STATUS_PULLING) {
                headerInfo.setText("下拉可以刷新");
                header.clearAnimation();
            } else if (currentStatus == STATUS_RELEASE) {
                headerInfo.setText("松开可以刷新");
            } else if (currentStatus == STATUS_REFRESHING) {
                headerInfo.setText("正在努力刷新");
                headerIcon.startAnimation(rotateAnim);
                if (onRefreshListener != null) {
                    onRefreshListener.onRefresh();
                }
            }
        }
    
        private void updateFooterStatus(int status) {
            currentStatus = status;
            if (currentStatus == STATUS_NORMAL) {
                footerIcon.clearAnimation();
            } else if (currentStatus == STATUS_PULLING) {
                footerIcon.clearAnimation();
                footerInfo.setText("上拉加载更多");
            } else if (currentStatus == STATUS_RELEASE) {
                footerInfo.setText("松开加载更多");
            } else if (currentStatus == STATUS_REFRESHING) {
                footerInfo.setText("正在努力加载");
                footerIcon.startAnimation(rotateAnim);
                if (onLoadListener != null) {
                    onLoadListener.onLoad();
                }
            }
        }
    
        //判断refresher是否在最顶部
        public boolean isViewGroupTopper() {
            return !refresher.canScrollVertically(-1);
        }
    
        //判断refresher是否在最底部
        private boolean isViewGroupFooter() {
            return !refresher.canScrollVertically(1);
        }
    
        public void completeRefresh() {
            headerInfo.setText("刷新成功\u3000\u3000");
            handler.sendEmptyMessageDelayed(0, 500);
        }
    
        public void completeLoad() {
            footerInfo.setText("加载成功\u3000\u3000");
            handler.sendEmptyMessageDelayed(1, 500);
        }
    
        public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
            this.onRefreshListener = onRefreshListener;
        }
    
        public void setOnLoadListener(OnLoadListener onLoadListener) {
            this.onLoadListener = onLoadListener;
        }
    
        public interface OnRefreshListener {
            void onRefresh();
        }
    
        public interface OnLoadListener {
            void onLoad();
        }
    
        public ViewGroup getRefresher() {
            return refresher;
        }
    
        private Handler handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                if (msg.what == 0) {
                    scroller.startScroll(getScrollX(), getScrollY(), 0, offsetY, 500);
                    updateHeaderStatus(STATUS_NORMAL);
                } else {
                    scroller.startScroll(getScrollX(), getScrollY(), 0, -offsetY, 500);
                    updateFooterStatus(STATUS_NORMAL);
                }
                postInvalidate();
                return true;
            }
        });
    }

     

  2. style文件 

    <declare-styleable name="PullToRefreshLayout" tools:ignore="ResourceName">
            <attr name="pull_to_refresh_type" format="enum">
                <enum name="none" value="1" />
                <enum name="refresh" value="2" />
                <enum name="load" value="3" />
                <enum name="all" value="4" />
            </attr>
        </declare-styleable>

     

  3.  旋转动画

    <?xml version="1.0" encoding="utf-8"?>
    <rotate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:fromDegrees="0"
        android:interpolator="@android:anim/linear_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="-1"
        android:toDegrees="360" />

     

  4. 布局文件由于公司的原因就不往外放了,很简单 ,使用的话就像ScrollView一样只能有一个子布局

  5.  

    感谢大牛 郭霖的博客启发

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先吐槽一下现在流行的刷新库,一个字大,包涵个人很多集成到项目中不需要的类,也很难找到很满意的效果,所以自己自己动手丰衣足食,撸一个。1.概述对所有基础控件(包括,嵌套滑动例如RecyclerView、NestedScrollView,普通的TextView、ListView、ScrollerView、LinearLayout等)提供下拉刷新、上拉加载的支持,处理了横向滑动冲突(例如:顶部banner的情况) ,且实现无痕过度。gradle (改用bintray-release,2017-7-8 16:00上传,以下暂时不会生效)compile 'com.yan:pullrefreshlayout:1.1.2'2.说明支持所有基础控件 loading 出现效果默认(STATE_FOLLOW、STATE_PLACEHOLDER_FOLLOW、STATE_CENTER、STATE_PLACEHOLDER_CENTER、STATE_FOLLOW_CENTER、STATE_CENTER_FOLLOW)  //-控件设置-     refreshLayout.autoRefresh();// 自动刷新     refreshLayout.setOverScrollDampingRatio(0.2f);//  值越大overscroll越短 default 0.2     refreshLayout.setAdjustTwinkDuring(3);// 值越大overscroll越慢 default 3     refreshLayout.setScrollInterpolator(interpolator);// 设置scroller的插值器     refreshLayout.setLoadMoreEnable(true);// 上拉加载是否可用 default false     refreshLayout.setDuringAdjustValue(10f);// 动画执行时间调节,越大动画执行越慢 default 10f     // 刷新或加载完成后回复动画执行时间,为-1时,根据setDuringAdjustValue()方法实现 default 300     refreshLayout.setRefreshBackTime(300);     refreshLayout.setDragDampingRatio(0.6f);// 阻尼系数 default 0.6     refreshLayout.setPullFlowHeight(400);// 拖拽最大范围,为-1时拖拽范围不受限制 default -1     refreshLayout.setRefreshEnable(false);// 下拉刷新是否可用 default false     refreshLayout.setPullTwinkEnable(true);// 回弹是否可用 default true      refreshLayout.setAutoLoadingEnable(true);// 自动加载是否可用 default false          // headerView和footerView需实现PullRefreshLayout.OnPullListener接口调整状态     refreshLayout.setHeaderView(headerView);// 设置headerView     refreshLayout.setFooterView(footerView);// 设置footerView          /**     * 设置header或者footer的的出现方式,默认7种方式     * STATE_FOLLOW, STATE_PLACEHOLDER_FOLLOW, STATE_PLACEHOLDER_CENTER     * , STATE_CENTER, STATE_CENTER_FOLLOW, STATE_FOLLOW_CENTER     * ,STATE_PLACEHOLDER     */     refreshLayout.setRefreshShowGravity(RefreshShowHelper.STATE_CENTER,RefreshShowHelper.STATE_CENTER);     refreshLayout.setHeaderShowGravity(RefreshShowHelper.STATE_CENTER)// header出现动画     refreshLayout.setFooterShowGravity(RefreshShowHelper.STATE_CENTER)// footer出现动画     // PullRefreshLayout.OnPullListener         public interface OnPullListener {             // 刷新或加载过程中位置相刷新或加载触发位置的百分比,时刻调用             void onPullChange(float percent);             void onPullReset();// 数据重置调用             void onPullHoldTrigger();// 拖拽超过触发位置调用             void onPullHoldUnTrigger();// 拖拽回到触发位置之前调用             void onPullHolding(); // 正在刷新             void onPullFinish();// 刷新完成         }3.demo用到的库loading 动画 AVLoadingIndicatorView(https://github.com/81813780/AVLoadingIndicatorView)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值