美文网首页工作生活
6.扩展已有的控件:RefreshListView(下拉刷新、上

6.扩展已有的控件:RefreshListView(下拉刷新、上

作者: BusyBunny | 来源:发表于2019-07-03 21:42 被阅读0次

    案例演示:

    GIF.gif

    关键点:

    • 1.下拉刷新
      1. 通过设置Padding为负数来隐藏头/脚布局;
      1. 通过公式int paddingTop=-自身测量高度+(moveY-downY);
      1. 通过偏移量=moveY-downY >0 从而判断是下拉
    • 2.下拉刷新
      1. 通过设置标记位,来判断是否能加载更多;
      1. 通过判断最后一个可见条目为空闲状态并且是刚好等于集合size,且标记位满足条件;
        实现思路:

    实现步骤:

    • 1.下拉刷新:
    • 1.继承自ListView,并自定义头布局填充:
        View mHeaderView = View.inflate(getContext(), R.layout.layout_headview, null);
    
    • 2.设置padding为负值,来隐藏头布局
        // 提前手动测量宽高
        mHeaderView.measure(0, 0);// 按照设置的规则测量
        mHeaderViewHeight = mHeaderView.getMeasuredHeight();
        // 设置内边距, 可以隐藏当前控件 , -自身高度
        mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
        // 在设置数据适配器之前执行添加 头布局/脚布局 的方法.
        addHeaderView(mHeaderView);
    
    • 3.下拉头部显示设置(不断改变修改paddingTop)
    • 4.监听触摸动作
        //公式:
        int paddingTop=-自身测量高度+(moveY-downY)
        
        //触摸监听:
         @Override
        public boolean onTouchEvent(MotionEvent ev) {
            // 判断滑动距离, 给Header设置paddingTop
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    downY = ev.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    moveY = ev.getY();
                    // 如果是正在刷新中, 就执行父类的处理
                    if(currentState == REFRESHING){
                        return super.onTouchEvent(ev);
                    }
                    float offset = moveY - downY; // 移动的偏移量
                    // 只有 偏移量>0, 并且当前第一个可见条目索引是0, 才放大头部
                    if(offset > 0 && getFirstVisiblePosition() == 0){
                        int paddingTop = (int) (- mHeaderViewHeight + offset);
                        mHeaderView.setPadding(0, paddingTop, 0, 0);
    
                        if(paddingTop >= 0 && currentState != RELEASE_REFRESH){// 头布局完全显示
                            // 切换成释放刷新模式
                            currentState = RELEASE_REFRESH;
                            updateHeader(); // 根据最新的状态值更新头布局内容
                        }else if(paddingTop < 0 && currentState != PULL_TO_REFRESH){ // 头布局不完全显示
                            // 切换成下拉刷新模式
                            currentState = PULL_TO_REFRESH;
                            updateHeader(); // 根据最新的状态值更新头布局内容
                        }
                        return true; // 当前事件被我们处理并消费
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    // 根据刚刚设置状态
                    if(currentState == PULL_TO_REFRESH){
                        mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
                    }else if(currentState == RELEASE_REFRESH){
                        mHeaderView.setPadding(0, 0, 0, 0);
                        currentState = REFRESHING;
                        updateHeader();
                    }else{
                        //防止“下拉刷新”和“加载更多”一起出现:
                        if (currentState==REFRESHING && isLoadMore){
                            currentState=PULL_TO_REFRESH;
                        }
                    }
                    break;
                default:
                    break;
            }
            return super.onTouchEvent(ev);
        }
    
    • 5.下拉刷新数据监听设置
        public interface RefreshListener{
            void onRefresh();
            void onLoadMore();
        }
    
        public void setOnRefreshListener(RefreshListener refreshListener){
            this.listener=refreshListener;
        }
    
    • 2.加载更多:
    • 1.隐藏脚布局
    • 2.下拉刷新数据监听设置
    • 3.完整代码:
    public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
    
        private View mHeaderView,mFooterView; // 头布局,脚布局
        private float downY; // 按下的y坐标
        private float moveY; // 移动后的y坐标
        private int mHeaderViewHeight,mFooterViewHeight; // 头布局高度,脚布局高度
        public static final int PULL_TO_REFRESH = 0;// 下拉刷新
        public static final int RELEASE_REFRESH = 1;// 释放刷新
        public static final int REFRESHING = 2; // 刷新中
        private boolean isLoadMore=false;
        private int currentState = PULL_TO_REFRESH; // 当前刷新模式
        private RotateAnimation rotateUpAnim; // 箭头向上动画
        private RotateAnimation rotateDownAnim; // 箭头向下动画
        private View mArrowView;        // 箭头布局
        private TextView mTitleText,mTimeText;  // 头布局标题
        private ProgressBar pb;         // 进度指示器
        private  RefreshListener listener;
        public RefreshListView(Context context) {
            super(context);
            init();
        }
        public RefreshListView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
        public RefreshListView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init();
        }
    
        /**
         * 初始化头布局, 脚布局
         * 滚动监听
         */
        private void init() {
            initHeaderView();
            initFooterView();
            initAnimation();
            setOnScrollListener(this);
        }
    
        /**
         * 初始化头布局的动画
         */
        private void initAnimation() {
            // 向上转, 围绕着自己的中心, 逆时针旋转0 -> -180.
            rotateUpAnim = new RotateAnimation(0f, -180f,
                    Animation.RELATIVE_TO_SELF, 0.5f,
                    Animation.RELATIVE_TO_SELF, 0.5f);
            rotateUpAnim.setDuration(300);
            rotateUpAnim.setFillAfter(true); // 动画停留在结束位置
    
            // 向下转, 围绕着自己的中心, 逆时针旋转 -180 -> -360
            rotateDownAnim = new RotateAnimation(-180f, -360,
                    Animation.RELATIVE_TO_SELF, 0.5f,
                    Animation.RELATIVE_TO_SELF, 0.5f);
            rotateDownAnim.setDuration(300);
            rotateDownAnim.setFillAfter(true); // 动画停留在结束位置
    
        }
    
        /**
         * 初始化头布局
         */
        private void initHeaderView() {
            mHeaderView = View.inflate(getContext(), R.layout.layout_headview, null);
            mArrowView = mHeaderView.findViewById(R.id.iv_arrow);
            pb = mHeaderView.findViewById(R.id.pb);
            mTitleText =  mHeaderView.findViewById(R.id.tv_title);
            mTimeText=mHeaderView.findViewById(R.id.tv_time);
    
            // 提前手动测量宽高
            mHeaderView.measure(0, 0);// 按照设置的规则测量
            mHeaderViewHeight = mHeaderView.getMeasuredHeight();
            // 设置内边距, 可以隐藏当前控件 , -自身高度
            mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
            // 在设置数据适配器之前执行添加 头布局/脚布局 的方法.
            addHeaderView(mHeaderView);
        }
    
        /**
         * 初始化脚布局
         */
        private void initFooterView() {
            mFooterView = View.inflate(getContext(), R.layout.layout_footview, null);
            mFooterView.measure(0,0);
            mFooterViewHeight=mFooterView.getMeasuredHeight();
            mFooterView.setPadding(0,-mFooterViewHeight,0,0);
            addFooterView(mFooterView);
        }
    
    
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            // 判断滑动距离, 给Header设置paddingTop
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    downY = ev.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    moveY = ev.getY();
                    // 如果是正在刷新中, 就执行父类的处理
                    if(currentState == REFRESHING){
                        return super.onTouchEvent(ev);
                    }
                    float offset = moveY - downY; // 移动的偏移量
                    // 只有 偏移量>0, 并且当前第一个可见条目索引是0, 才放大头部
                    if(offset > 0 && getFirstVisiblePosition() == 0){
                        int paddingTop = (int) (- mHeaderViewHeight + offset);
                        mHeaderView.setPadding(0, paddingTop, 0, 0);
    
                        if(paddingTop >= 0 && currentState != RELEASE_REFRESH){// 头布局完全显示
                            // 切换成释放刷新模式
                            currentState = RELEASE_REFRESH;
                            updateHeader(); // 根据最新的状态值更新头布局内容
                        }else if(paddingTop < 0 && currentState != PULL_TO_REFRESH){ // 头布局不完全显示
                            // 切换成下拉刷新模式
                            currentState = PULL_TO_REFRESH;
                            updateHeader(); // 根据最新的状态值更新头布局内容
                        }
                        return true; // 当前事件被我们处理并消费
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    // 根据刚刚设置状态
                    if(currentState == PULL_TO_REFRESH){
                        mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
                    }else if(currentState == RELEASE_REFRESH){
                        mHeaderView.setPadding(0, 0, 0, 0);
                        currentState = REFRESHING;
                        updateHeader();
                    }else{
                        //防止“下拉刷新”和“加载更多”一起出现:
                        if (currentState==REFRESHING && isLoadMore){
                            currentState=PULL_TO_REFRESH;
                        }
                    }
                    break;
                default:
                    break;
            }
            return super.onTouchEvent(ev);
        }
    
    
        /**
         * 根据状态更新头布局内容
         */
        private void updateHeader() {
            switch (currentState) {
                case PULL_TO_REFRESH: // 切换回下拉刷新
                    // 做动画, 改标题
                    mArrowView.startAnimation(rotateDownAnim);
                    mTitleText.setText("下拉刷新");
                    break;
                case RELEASE_REFRESH: // 切换成释放刷新
                    // 做动画, 改标题
                    mArrowView.startAnimation(rotateUpAnim);
                    pb.setVisibility(INVISIBLE);
                    mTitleText.setText("释放刷新");
                    break;
                case REFRESHING: // 刷新中...
                    mArrowView.clearAnimation();
                    mArrowView.setVisibility(View.INVISIBLE);
                    pb.setVisibility(View.VISIBLE);
                    mTitleText.setText("正在刷新中...");
                    //
                    mTimeText.setText("最后更新时间:"+getTime());
                    if (listener!=null){
                        listener.onRefresh();
                    }
                    break;
                default:
                    break;
            }
        }
        private String getTime() {
            long currentTimeMillis = System.currentTimeMillis();
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return format.format(currentTimeMillis);
        }
    
        public void endRefreshing(){
            if (isLoadMore){
                //关闭加载更多:
                isLoadMore=false;
                mFooterView.setPadding(0,-mFooterViewHeight,0,0);
            }else{
                //关闭正在刷新:
                mHeaderView.setPadding(0,-mHeaderViewHeight,0,0);
                mHeaderView.clearAnimation();
                currentState=PULL_TO_REFRESH;
            }
    
        }
    
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            if (scrollState==SCROLL_STATE_FLING || scrollState==SCROLL_STATE_IDLE ){
                //在最后一个条目 且为空闲状态时候才加载更多
                if (getLastVisiblePosition()==getCount()-1 && isLoadMore==false){
                    isLoadMore=true;
                    //脚布局显示:
                    mFooterView.setPadding(0,0,0,0);
                    setSelection(getCount());//跳转到最后一行,使其显示加载更多
    
                    //加载更多:
                    if (listener!=null){
                        listener.onLoadMore();
                    }
                }
            }
        }
    
        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    
        }
    
        public interface RefreshListener{
            void onRefresh();
            void onLoadMore();
        }
    
        public void setOnRefreshListener(RefreshListener refreshListener){
            this.listener=refreshListener;
        }
    }
    

    相关文章

      网友评论

        本文标题:6.扩展已有的控件:RefreshListView(下拉刷新、上

        本文链接:https://www.haomeiwen.com/subject/wddhhctx.html