ListView的下拉刷新和上拉加载

Posted 爱coding的卖油翁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ListView的下拉刷新和上拉加载相关的知识,希望对你有一定的参考价值。

我们公司以前有道面试题,让开发人员当场写一个ListView的下拉刷新和上拉加载,时间2个小时左右,允许有微量Bug。

自己想一想,也不知道能不能写的出来,所以今天有时间,参考了网上的一些资料,先写一个练手下。先看下效果图:

实现原理

ListView有一个addHeaderView()和addFooterView()方法,就是添加一个头布局和一个脚布局。主要通过这两个方法来实现。

初始化的时候,添加头布局和脚布局。然后把头布局的paddingTop设置为负的头布局高度,这样头布局就会看不见了。脚布局同样处理。

在手指滑动的时候,不断的去改变paddingTop的值,这样,就形成下拉的效果了。

注意:对于ListView判断滑动到顶部和底部的监听

ListView自带一个setOnScrollListener()监听器,主要用来监听它的滑动时间的。

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) 
    switch (scrollState) 
        case OnScrollListener.SCROLL_STATE_IDLE:
            //空闲状态,停止滚动
            break;
        case OnScrollListener.SCROLL_STATE_FLING:
            // 手指快速滚动
            break;
        case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
            // 正在滚动
            break;
    


@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) 

调用方法

  • 直接引用PullToRefreshListView类到你的工程,copy布局文件。
  • 由于下拉刷新的头布局和上拉加载的布局是固定的,没有提供接口动态加载。所有如果要引用的话,得去修改xml文件。
  • 可以禁止下拉刷新或者上拉加载,通过setNeedPullToRefresh()和setNeedLoadMore()方法。
  • 每次刷新完毕,请调用refreshComplete()方法。
  • setOnRefreshListener()设置下拉和上拉的监听回调。
package com.geek.widget;

import java.text.SimpleDateFormat;
import java.util.Locale;

import com.zhou.customviewone.R;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * 
 * 下拉刷新和上拉加载的ListView
 * 
 * @author LeeShenzhou
 *
 */
public class PullToRefreshListView extends ListView implements OnScrollListener 

    // 头布局
    private View mHeaderView;
    private ImageView mImageArrow;
    private ProgressBar mProgressBar;
    private TextView mTxtState;
    private TextView mTxtUpdateTime;

    // 脚布局
    private View mFooterView;

    // 头布局的高度
    private int headerHeight;

    // 脚布局的高度
    private int footerHeight;

    private int mDownY;

    // 下拉刷新
    private final int DOWN_REFRESH = 0;
    // 松开刷新
    private final int RELEASE_REFRESH = 1;
    // 正在刷新中
    private final int REFRESHING = 2;

    private int currentState = DOWN_REFRESH;

    private boolean isLoadMore;

    // 是否需要下拉刷新
    private boolean isNeedPullToRefresh;
    // 是否需要上拉加载更多
    private boolean isNeedLoadMore;

    /**
     * 是否需要下拉刷新
     */
    public void setNeedPullToRefresh(boolean need) 
        this.isNeedPullToRefresh = need;
    

    /**
     * 是否需要上拉加载更多
     */
    public void setNeedLoadMore(boolean need) 
        this.isNeedLoadMore = need;
    

    /**
     * 刷新完成
     */
    public void refreshComplete() 
        isLoadMore = false;
        currentState = DOWN_REFRESH;
        mHeaderView.setPadding(0, -headerHeight, 0, 0);
        mFooterView.setPadding(0, -footerHeight, 0, 0);
    

    private OnRefreshListener listener;

    public void setOnRefreshListener(OnRefreshListener listener) 
        this.listener = listener;
    

    public PullToRefreshListView(Context context) 
        super(context);
        init();
    

    public PullToRefreshListView(Context context, AttributeSet attrs) 
        super(context, attrs);
        init();
    

    public PullToRefreshListView(Context context, AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);
        init();
    

    private void init() 
        isNeedLoadMore = true;
        isNeedPullToRefresh = true;

        isLoadMore = false;

        // 初始化头布局
        mHeaderView = View.inflate(getContext(), R.layout.layout_pull_to_refresh_header, null);
        mImageArrow = (ImageView) mHeaderView.findViewById(R.id.header_image_arrow);
        mProgressBar = (ProgressBar) mHeaderView.findViewById(R.id.header_pb);
        mTxtState = (TextView) mHeaderView.findViewById(R.id.header_state);
        mTxtUpdateTime = (TextView) mHeaderView.findViewById(R.id.header_update_time);

        mHeaderView.measure(0, 0); // 系统会帮我们测量出HeaderView的高度
        headerHeight = mHeaderView.getMeasuredHeight();
        mHeaderView.setPadding(0, -headerHeight, 0, 0);
        addHeaderView(mHeaderView);

        // 初始化脚布局
        mFooterView = View.inflate(getContext(), R.layout.layout_pull_to_refresh_footer, null);
        mFooterView.measure(0, 0);
        footerHeight = mFooterView.getMeasuredHeight();
        mFooterView.setPadding(0, -footerHeight, 0, 0);
        addFooterView(mFooterView);

        // 设置最后刷新时间
        setLastUpdateTime();

        setOnScrollListener(this);
    

    private void setLastUpdateTime() 
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm", Locale.getDefault());
        String time = sdf.format(System.currentTimeMillis());
        if (mTxtUpdateTime != null) 
            mTxtUpdateTime.setText("最新更新: " + time);
        
    

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent ev) 
        if (!isNeedPullToRefresh) 
            return super.onTouchEvent(ev);
        

        switch (ev.getAction()) 
        case MotionEvent.ACTION_DOWN:
            mDownY = (int) ev.getY();
            break;

        case MotionEvent.ACTION_MOVE:
            int moveY = (int) ev.getY();
            final int offset = (moveY - mDownY) / 2;
            int paddingTop = -headerHeight + offset;

            if (getFirstVisiblePosition() == 0 && paddingTop > -headerHeight) 
                if (currentState != REFRESHING) 
                    if (offset > headerHeight && currentState == DOWN_REFRESH) 
                        // 完全显示了
                        currentState = RELEASE_REFRESH;
                        changeHeaderView();
                     else if (offset < headerHeight && currentState == RELEASE_REFRESH) 
                        // 没有显示完全
                        currentState = DOWN_REFRESH;
                        changeHeaderView();
                    
                

                if (currentState == REFRESHING) 
                    mHeaderView.setPadding(0, offset, 0, 0);
                 else 
                    mHeaderView.setPadding(0, paddingTop, 0, 0);
                
            
            break;

        case MotionEvent.ACTION_UP:
            if (currentState == REFRESHING) 
                mHeaderView.setPadding(0, 0, 0, 0);
                setSelection(0);
                return super.onTouchEvent(ev);
            

            // 判断当前的状态是松开刷新还是下拉刷新
            if (currentState == RELEASE_REFRESH) 
                mHeaderView.setPadding(0, 0, 0, 0);
                currentState = REFRESHING;
                changeHeaderView();
                setSelection(0);
                if (listener != null) 
                    listener.onPullToRefresh();
                
             else if (currentState == DOWN_REFRESH) 
                mHeaderView.setPadding(0, -headerHeight, 0, 0);
            
            break;
        default:
            break;
        
        return super.onTouchEvent(ev);
    

    private void changeHeaderView() 
        switch (currentState) 
        case DOWN_REFRESH:
            // 下拉刷新
            mTxtState.setText("下拉刷新");
            mProgressBar.setVisibility(View.GONE);
            mImageArrow.setVisibility(View.VISIBLE);
            mImageArrow.setImageResource(R.drawable.arrow_down);
            break;

        case RELEASE_REFRESH:
            // 松开刷新
            mTxtState.setText("释放立即刷新");
            mProgressBar.setVisibility(View.GONE);
            mImageArrow.setVisibility(View.VISIBLE);
            mImageArrow.setImageResource(R.drawable.arrow_pull);
            break;

        case REFRESHING:
            // 正在刷新
            mImageArrow.setVisibility(View.GONE);
            mProgressBar.setVisibility(View.VISIBLE);
            mTxtState.setText("正在刷新...");
            break;

        default:
            break;
        
    

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) 
        if (!isNeedLoadMore || isLoadMore) 
            return;
        
        if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING) 
            // 判断当前是否已经到了底部
            if (getLastVisiblePosition() == (getCount() - 1)) 
                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) 
        if (firstVisibleItem != 0) 
            // 重置刷新状态
            if (currentState != REFRESHING) 
                currentState = DOWN_REFRESH;
            
        
    

    public interface OnRefreshListener 

        /**
         * 下拉刷新
         */
        void onPullToRefresh();

        /**
         * 上拉加载更多
         */
        void onLoadMore();
    


源码下载:https://git.oschina.net/nszKnife/AndroidView

以上是关于ListView的下拉刷新和上拉加载的主要内容,如果未能解决你的问题,请参考以下文章

ListView上拉加载和下拉刷新多种实现方式

ListView上拉加载和下拉刷新多种实现方式

Android实现RecyclerView的下拉刷新和上拉加载更多

vue2.0 自定义 下拉刷新和上拉加载更多(Scroller) 组件

使用SwipeRefreshLayout和RecyclerView实现仿“简书”下拉刷新和上拉加载更多

使用SwipeRefreshLayout和RecyclerView实现仿“简书”下拉刷新和上拉加载更多