自定义ListView的下拉刷新和上拉加载更多

 

/**
 * Created by Ext on 2016/8/5.
 */
public class LoadMoreListView extends ListView implements AbsListView.OnScrollListener {

    private View mFooter;
    private View mHeader;

    private int mHeaderHeight;

    private int totalItemCount;
    private int lastVisibleItem; // 最后一个可见的item
    private int firstVisibleItem;
    private boolean isRemark; // 标记,当前是否在listView最顶端按下
    private int startY; // 按下时的Y
    private int state; // 当前的状态
    private final int NONE = 0; // 正常状态
    private final int PULL = 1; // 提示下拉状态
    private final int RELEASE = 2; // 提示释放状态
    private final int REFRESHING = 3; // 刷新状态
    private int scrollState; // 当前滚动状态

    private TextView tipTv;
    private ImageView arrowIv;
    private ProgressBar progressBar;
    private TextView updateTimeTv;


    private boolean isLoading;
    private ILoadMoreListener mLoadMoreListener;
    private RotateAnimation animDown;
    private RotateAnimation animUp;
    private View mLoadingLayout;

    public LoadMoreListView(Context context) {
        this(context, null);
    }

    public LoadMoreListView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LoadMoreListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        initView(context);
    }

    /**
     * 添加底部加载提示布局到ListView
     * @param context
     */
    private void initView(Context context) {

        LayoutInflater inflater = LayoutInflater.from(context);
        mFooter = inflater.inflate(R.layout.footer_layout, null);
        mLoadingLayout = mFooter.findViewById(R.id.load_layout);
        mLoadingLayout.setVisibility(View.GONE);
        this.addFooterView(mFooter);

        mHeader = inflater.inflate(R.layout.header_layout, null);
        measureView(mHeader);
        mHeaderHeight = mHeader.getMeasuredHeight();
        topPadding(-mHeaderHeight);
        this.addHeaderView(mHeader);

        this.setOnScrollListener(this);
    }

    /**
     * 通知父布局,占用的宽、高
     * @param view
     */
    private void measureView(View view) {
        ViewGroup.LayoutParams p = view.getLayoutParams();
        if (p == null) {
            p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }
        // 宽度测量规格
        int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);
        // 高度测量规格
        int height;
        int tempHeight = p.height;
        if (tempHeight > 0) {
            height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);
        } else {
            height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        }
        view.measure(width, height);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                if (firstVisibleItem == 0) {
                    isRemark = true;
                    startY = (int) ev.getY();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                onMove(ev);
                break;

            case MotionEvent.ACTION_UP:
                // 松手后头布局处于两种状态
                if (state == RELEASE) {
                    state = REFRESHING;
                    refreshViewByState();
                    // 加载数据
                    if (mLoadMoreListener != null) {
                        mLoadMoreListener.onRefresh();
                    }
                } else if (state == PULL) {
                    state = NONE;
                    isRemark = false;
                    refreshViewByState();
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 判断移动过程操作
     * @param ev
     */
    private void onMove(MotionEvent ev){
        if (!isRemark) {
            return;
        }
        int tempY = (int)ev.getY();
        // space 摁下后滑动的垂直距离
        int space = tempY - startY;

        int topPadding = space - mHeaderHeight;
        if (topPadding > mHeaderHeight + 20) {
            topPadding = mHeaderHeight + 20;
        }

        switch (state) {
            case NONE:
                if (space > 0) {
                    state = PULL;
                    refreshViewByState();
                }
                break;
            case PULL:
                // 滑动距离大于一定高度之后就更新状态
                topPadding(topPadding);
                if (space > mHeaderHeight + 10 && scrollState == SCROLL_STATE_TOUCH_SCROLL) {
                    state = RELEASE;
                    refreshViewByState();
                }
                break;
            case RELEASE:
                // 限制头部布局的topPadding
                if (topPadding >= mHeaderHeight + 20) {
                    topPadding = mHeaderHeight;
                }
                topPadding(topPadding);
                if (space < mHeaderHeight + 10) {
                    state = PULL;
                    refreshViewByState();
                } else if (space <= 0) {
                    state = NONE;
                    isRemark = false;
                    refreshViewByState();
                }
                break;
            case REFRESHING:
                break;

        }
    }

    /**
     * 根据当前状态,改变界面显示(只要在滑动时状态变化就需要调用)
     */
    private void refreshViewByState() {
        if (tipTv == null) {
            tipTv = (TextView) mHeader.findViewById(R.id.tip_tv);
        }
        if (arrowIv == null) {
            arrowIv = (ImageView) mHeader.findViewById(R.id.arrow);
        }
        if (progressBar == null) {
            progressBar = (ProgressBar) mHeader.findViewById(R.id.progress);
        }

        if (animDown == null) {
            animDown = new RotateAnimation(0, 180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            animDown.setDuration(500);
            animDown.setFillAfter(true);
        }
        if (animUp == null) {
            animUp = new RotateAnimation(180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            animUp.setDuration(500);
            animUp.setFillAfter(true);
        }

        switch (state) {
            case NONE:
                topPadding(-mHeaderHeight);
                break;

            case PULL:
                arrowIv.setVisibility(VISIBLE);
                progressBar.setVisibility(GONE);
                tipTv.setText("下拉可以刷新");
                arrowIv.clearAnimation();
                arrowIv.setAnimation(animDown);
                break;

            case RELEASE:
                arrowIv.setVisibility(VISIBLE);
                progressBar.setVisibility(GONE);
                tipTv.setText("松开可以刷新");
                arrowIv.clearAnimation();
                arrowIv.setAnimation(animUp);
                break;

            case REFRESHING:
                topPadding(10);
                arrowIv.setVisibility(GONE);
                progressBar.setVisibility(VISIBLE);
                tipTv.setText("正在刷新");
                arrowIv.clearAnimation();
                break;
        }
    }


    /**
     * 加载完更多数据
     */
    public void loadComplete() {
        isLoading = false;
        mLoadingLayout.setVisibility(View.GONE);
        invalidate();
    }

    /**
     * 获取完数据
     */
    public void refreshComplete() {

        state = NONE;
        isRemark = false;
        refreshViewByState();

        if (updateTimeTv == null) {
            updateTimeTv = (TextView) mHeader.findViewById(R.id.time_tv);
        }
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd hh:mm:ss");
        Date date = new Date(System.currentTimeMillis());
        updateTimeTv.setText(format.format(date));
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

        // 针对加载更多
        if (totalItemCount == lastVisibleItem && scrollState == SCROLL_STATE_IDLE) {

            if (!isLoading) {
                isLoading = true;
                mLoadingLayout.setVisibility(View.VISIBLE);
                if (mLoadMoreListener != null) {
                    mLoadMoreListener.onLoadMore();
                }
            }
        }
        // 针对下拉刷新
        this.scrollState = scrollState;
    }

    /**
     *
     * @param view
     * @param firstVisibleItem 第一个可见的item的数据在数据集中的索引
     * @param visibleItemCount 当前屏幕可见的item总数
     * @param totalItemCount 数据集中数据的总数
     */
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

        this.firstVisibleItem = firstVisibleItem;
        this.lastVisibleItem = firstVisibleItem + visibleItemCount;
        this.totalItemCount = totalItemCount;
    }

    private void topPadding(int topPadding) {

        mHeader.setPadding(mHeader.getPaddingLeft(), topPadding, mHeader.getPaddingRight(), mHeader.getPaddingBottom());
    }


    public void setLoadMoreListener(ILoadMoreListener mLoadMoreListener) {
        this.mLoadMoreListener = mLoadMoreListener;
    }

    public interface ILoadMoreListener {
        public void onLoadMore();

        public void onRefresh();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值