自定义LisetView(AutoListView),实现下拉刷新、上拉加载功能

自定义AutoListView全部文件

(PS:不是项目包,个人认为这样更容易学习和使用): https://download.csdn.net/download/rlw_1009/10579396

直接上代码,自定义代码如下:

package com.lwre.myandroidtest.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import cn.sec.watchcarecloud.R;
import cn.sec.watchcarecloud.utils.SystemUtils;

/**
 * Created by lwren
 */

public class AutoListView extends ListView implements AbsListView.OnScrollListener {

    // 区分当前操作是刷新还是加载
    public static final int REFRESH = 10;
    public static final int LOAD = 11;

    // 定义header的四种状态和当前状态
    private static final int NONE = 0;
    private static final int PULL = 1;
    private static final int RELEASE = 2;
    private static final int REFRESH_END = 3;
    //private int state;
    private int state = NONE;

    private RotateAnimation animation;
    private RotateAnimation reverseAnimation;

    private LayoutInflater inflater;
    //头部
    private View header;
    private TextView tip;
    private TextView lastUpdate;
    private ImageView arrow;
    //底部
    private View footer;
    private TextView noData;
    private TextView loadFull;
    private TextView more;
    private TextView pull_to_load;
    //头部和底部高度
    private int headerContentInitialHeight;
    private int headerContentHeight;
    private int footViewHeight;


    //下拉刷新判断
    private boolean isRecorded = false; //根据是否firstVisibleItem==0,判断是否可以操作下拉刷新
    //上拉加载判断
    private boolean isLoadOperation = false;    //是否是“上拉加载操作”
    private boolean loadEnable = true;  // 开启或者关闭加载更多功能
    private boolean isLoading = false;  // 判断是否正在加载
    private boolean isLoadFull = false; //是否加载全部数据
    private int scrollState;    //记录滑动状态,此处 仅用于上拉加载

    private int startY; //手指'第一次'触摸屏幕y点坐标值
    private int space;  //手指滑动距离,正数:下拉刷新;负数:下拉加载

    private static final int SPACE = 100;   // 区分PULL和RELEASE的距离的大小

    private int pageSize = 10;  //一次加载的数据数量
    private int firstVisibleItem;   //记录显示的第一个item是第几个


    private OnRefreshListener onRefreshListener;
    private OnLoadListener onLoadListener;


    public AutoListView(Context context) {
        super(context);
        initView(context);
    }

    public AutoListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public AutoListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }


    private void initView(Context context) {

        // 设置箭头特效
        animation = new RotateAnimation(0, -180,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        animation.setInterpolator(new LinearInterpolator());
        animation.setDuration(100);
        animation.setFillAfter(true);

        reverseAnimation = new RotateAnimation(-180, 0,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        reverseAnimation.setInterpolator(new LinearInterpolator());
        reverseAnimation.setDuration(100);
        reverseAnimation.setFillAfter(true);

        inflater = LayoutInflater.from(context);
        //为listview加入Header
        header = inflater.inflate(R.layout.header_view, null);
        arrow = (ImageView) header.findViewById(R.id.arrow);
        tip = (TextView) header.findViewById(R.id.tip);
        lastUpdate = (TextView) header.findViewById(R.id.lastUpdate);
        //为listview加入Footer
        footer = inflater.inflate(R.layout.listview_footer, null);
        loadFull = (TextView) footer.findViewById(R.id.loadFull);
        noData = (TextView) footer.findViewById(R.id.noData);
        more = (TextView) footer.findViewById(R.id.more);
        pull_to_load = (TextView) footer.findViewById(R.id.pull_to_load);
        // loading = (ProgressBar) footer.findViewById(R.id.loading);
        footer.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
            }
        });

        // 为listview添加头部和尾部,并进行初始化
        headerContentInitialHeight = header.getPaddingTop();
        measureView(header);
        headerContentHeight = header.getMeasuredHeight();
        headerTopPadding(-headerContentHeight);

        //测量footview的高度,通过measure()传入两个0参数,系统会不认这个0,自动帮我们测量出加在底部View的长度信息
        footer.measure(0, 0);
        //拿到高度
        footViewHeight = footer.getMeasuredHeight();
        //隐藏view,让头部显示高度为实际测量高度的负数,实现布局的隐藏   //footer.setPadding(0, -footViewHeight, 0, 0);
        footerheaderTopPadding(-footViewHeight);


        this.addHeaderView(header);
        this.addFooterView(footer);
        this.setOnScrollListener(this);


    }

    /*******************Begin***下拉刷新***********************************************************************************************/

    /**
     * 监听触摸事件,解读手势
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startY = (int) ev.getY();
                if (firstVisibleItem == 0) {
                    isRecorded = true;
                    //startY = (int) ev.getY();
                }
                break;

            case MotionEvent.ACTION_MOVE:
                whenMove(ev);
                break;

            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                if (state == PULL) {
                    state = NONE;
                    refreshHeaderViewByState();
                } else if (state == RELEASE) {
                    state = REFRESH_END;
                    refreshHeaderViewByState();

                    //!!!必须firstVisibleItem==0时才可以执行【刷新操作】
                    if (firstVisibleItem == 0) {
                        //执行【刷新操作】
                        onRefresh();
                    }

                }

                break;

        }
        return super.onTouchEvent(ev);
    }


    // 解读手势,刷新header状态
    private void whenMove(MotionEvent ev) {
        if (!isRecorded) {
            return;
        }
        //手指正在触摸的y点坐标
        int tmpY = (int) ev.getY();

        //手指滑动距离(正数:下拉刷新;负数:下拉加载)=‘手指正在触摸的y点坐标’减去‘手指"第一次"触摸屏幕y点坐标值’
        space = tmpY - startY;

        //手指滑动距离 与 headerview高度的差值
        int topPadding = space - headerContentHeight;

        switch (state) {
            case NONE:
                if (space > 0) {
                    state = PULL;
                }
                break;
            case PULL:
                //实时显示下拉的header // topPadding: 手指滑动距离 与 headerview高度的差值
                headerTopPadding(topPadding);
                /** OnScrollListener.SCROLL_STATE_IDLE:    视图不滚动。 注意使用轨迹球导航列表计为处于空闲状态,因为这些转换不是动画的。
                 *  OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:    用户使用触摸滚动,他们的手指仍然在屏幕上
                 *  OnScrollListener.SCROLL_STATE_FLING:      用户之前使用触摸滚动并执行了一次投掷。 动画现在正在停止
                 */
                // 如果“用户使用触摸滚动,他们的手指仍然在屏幕上”,并且“手指滑动距离 与 headerview高度的差值”大于 SPACE ,执行下一步RELEASE状态
                //if (scrollState == SCROLL_STATE_TOUCH_SCROLL && topPadding > SPACE) {
                // 如果“用户使用触摸滚动,他们的手指仍然在屏幕上”,并且“手指滑动距离space 大于(headerview高度加"最小距离")”,执行下一步RELEASE状态
                if (scrollState == SCROLL_STATE_TOUCH_SCROLL && space > (headerContentHeight + SPACE)) {
                    state = RELEASE;
                    refreshHeaderViewByState();
                }
                break;
            case RELEASE:
                //实时显示下拉的header // topPadding: 手指滑动距离 与 headerview高度的差值
                headerTopPadding(topPadding);
                //如果“手指滑动距离space>0”,但是“手指滑动距离space 小于(headerview高度加"最小距离")”,则返回上一步状态,不执行【刷新操作】
                if (space > 0 && space < headerContentHeight + SPACE) {
                    state = PULL;
                    refreshHeaderViewByState();
                } else if (space <= 0) {    //如果“手指滑动距离space=0”,则返回“初始NONE状态”,不执行【刷新操作】
                    state = NONE;
                    refreshHeaderViewByState();
                }
                break;
            //进行到RELEASE状态后,在onTouchEvent的‘ACTION_UP’‘ACTION_UP’进行判断是否执行【刷新操作】

        }

    }


    // 根据当前状态,调整header
    private void refreshHeaderViewByState() {
        switch (state) {
            case NONE:
                headerTopPadding(-headerContentHeight);
                tip.setText(R.string.pull_to_refresh);  //下拉可以刷新
                arrow.clearAnimation();
                arrow.setImageResource(R.drawable.pull_to_refresh_arrow);
                break;
            case PULL:
                arrow.setVisibility(View.VISIBLE);
                tip.setVisibility(View.VISIBLE);
                lastUpdate.setVisibility(View.VISIBLE);
                //提示:下拉可以刷新
                tip.setText(R.string.pull_to_refresh);
                //箭头添加“向下的动画”
                arrow.clearAnimation();
                arrow.setAnimation(reverseAnimation);

                break;
            case RELEASE:
                arrow.setVisibility(View.VISIBLE);
                tip.setVisibility(View.VISIBLE);
                lastUpdate.setVisibility(View.VISIBLE);

                tip.setText(R.string.release_to_refresh);   //提示:松开可以刷新
                //箭头添加“向上的动画”
                arrow.clearAnimation();
                arrow.setAnimation(animation);
                break;
            case REFRESH_END:
                //HeaderView初始状态/恢复初始状态
                initialHeaderState();
                break;
        }
    }

    /**
     * HeaderView初始状态/恢复初始状态
     */
    private void initialHeaderState() {
        /***********下拉刷新恢复初始状态**********/
        //记录最近更新的时间
        onRefreshComplete();
        //根据是否firstVisibleItem==0,判断是否可以操作下拉刷新,恢复默认false
        isRecorded = false;
        state = NONE;
        //是否是“上拉加载操作”
        isLoadOperation = false;

        //是否加载全部数据
        // 此参数比较特殊,在【上拉加载】要保留状态,根据setResultSize(int resultSize)加载的数量判断生效;
        //在此处【下拉刷新】要恢复默认值,否则不能再次进行“上拉加载”
        isLoadFull = false;
        //恢复“默认或初始化”headerview
        arrow.setVisibility(View.GONE);
        tip.setVisibility(View.GONE);
        lastUpdate.setVisibility(View.GONE);
        refreshHeaderViewByState();

    }

    /**
     * 记录最近更新的时间
     */
    public void onRefreshComplete() {
        String currentTime = SystemUtils.getCurrentTime();
        lastUpdate.setText(this.getContext().getString(R.string.lastUpdateTime, SystemUtils.getCurrentTime()));
    }

    /*******************End***下拉刷新***********************************************************************************************/

    // 用来计算header大小的。比较隐晦。因为header的初始高度就是0,貌似可以不用。
    private void measureView(View child) {
        ViewGroup.LayoutParams p = child.getLayoutParams();
        if (p == null) {
            p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }
        int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
        int lpHeight = p.height;
        int childHeightSpec;
        if (lpHeight > 0) {
            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
                    MeasureSpec.EXACTLY);
        } else {
            childHeightSpec = MeasureSpec.makeMeasureSpec(0,
                    MeasureSpec.UNSPECIFIED);
        }
        child.measure(childWidthSpec, childHeightSpec);
    }


    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        this.firstVisibleItem = firstVisibleItem;
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        this.scrollState = scrollState;
        ifNeedLoad(view, scrollState);
    }

    /*##################Begin###上拉加载##############################################################################################*/

    // 根据listview滑动的状态判断是否需要加载更多
    private void ifNeedLoad(AbsListView view, int scrollState) {
        if (!loadEnable) {  // loadEnable: 开启或者关闭加载更多功能
            return;
        }
        try {
            /** OnScrollListener.SCROLL_STATE_IDLE:    视图不滚动。 注意使用轨迹球导航列表计为处于空闲状态,因为这些转换不是动画的。
             *  OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:    用户使用触摸滚动,他们的手指仍然在屏幕上
             *  OnScrollListener.SCROLL_STATE_FLING:      用户之前使用触摸滚动并执行了一次投掷。 动画现在正在停止
             */
            if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
                    && !isLoading
                    && view.getLastVisiblePosition() == view.getPositionForView(footer)
                    && !isLoadFull  //是否加载全部数据
                    && space < -(footViewHeight + SPACE)) {

                //执行【下拉加载】
                onLoad();
                // 是否正在加载
                isLoading = true;

            }
        } catch (Exception e) {
        }
    }


    /**
     * 用于加载更多结束后的回调
     */
    public void onLoadComplete() {
        //FooterView初始状态/恢复初始状态
        initialFooterState();
    }

    /**
     * FooterView初始状态/恢复初始状态
     */
    private void initialFooterState() {
        isLoadOperation = true;
        loadEnable = true;  // 开启或者关闭加载更多功能
        isLoading = false;  // 判断是否正在加载
        //isLoadFull = false; //是否加载全部数据 //此参数比较特殊,要保留状态,根据setResultSize(int resultSize)加载的数量判断生效

        /***********上拉加载恢复初始状态**********/
        loadFull.setVisibility(View.GONE);
        more.setVisibility(View.GONE);
        noData.setVisibility(View.GONE);
        footerheaderTopPadding(-footViewHeight);

    }


    /**
     * 这个方法是根据结果的大小来决定footer显示的。
     * <p>
     * 这里假定每次请求的条数为10。如果请求到了10条。则认为还有数据。如过结果不足10条,则认为数据已经全部加载,这时footer显示已经全部加载
     * </p>
     *
     * @param resultSize
     */
    public void setResultSize(int resultSize) {

        footerheaderTopPadding(footViewHeight);
        //isLoadOperation:是否是“上拉加载操作”
        if (!isLoadOperation) {    //*************下拉刷新操作*************
            if (resultSize == pageSize) {
                //显示“上拉加载更多”
                loadFull.setVisibility(View.GONE);
                more.setVisibility(View.GONE);
                noData.setVisibility(View.GONE);
                pull_to_load.setVisibility(VISIBLE);
            } else if (resultSize == 0) {
                //显示“暂无数据”
                isLoadFull = true;
                loadFull.setVisibility(View.GONE);
                more.setVisibility(View.GONE);
                noData.setVisibility(View.VISIBLE);
                pull_to_load.setVisibility(GONE);
            } else if (resultSize > 0 && resultSize < pageSize) {
                //显示“已加载全部”
                isLoadFull = true;
                loadFull.setVisibility(View.VISIBLE);
                more.setVisibility(View.GONE);
                noData.setVisibility(View.GONE);
                pull_to_load.setVisibility(GONE);
            }

        } else {  //#############上拉加载操作#############

            if (resultSize == 0 || (resultSize > 0 && resultSize < pageSize)) {
                //显示“已加载全部”
                isLoadFull = true;
                loadFull.setVisibility(View.VISIBLE);
                more.setVisibility(View.GONE);
                noData.setVisibility(View.GONE);
                pull_to_load.setVisibility(GONE);

            } else if (resultSize == pageSize) {
                //显示“上拉加载更多”
                loadFull.setVisibility(View.GONE);
                more.setVisibility(View.GONE);
                noData.setVisibility(View.GONE);
                pull_to_load.setVisibility(VISIBLE);

            }

        }


    }


      /*##################End###上拉加载##############################################################################################*/

    // 调整header的大小。其实调整的只是距离顶部的高度。
    private void headerTopPadding(int headerTopPadding) {
        header.setPadding(header.getPaddingLeft(), headerTopPadding, header.getPaddingRight(), header.getPaddingBottom());
        header.invalidate();

    }

    // 调整footer的显示
    private void footerheaderTopPadding(int headerTopPadding) {
        footer.setPadding(header.getPaddingLeft(), headerTopPadding, header.getPaddingRight(), header.getPaddingBottom());
        footer.invalidate();

    }

    /*
     * 定义下拉刷新接口
     */
    public interface OnRefreshListener {
        public void onRefresh();
    }

    /*
     * 定义加载更多接口
     */
    public interface OnLoadListener {
        public void onLoad();
    }

    // 下拉刷新监听
    public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
        this.onRefreshListener = onRefreshListener;
    }

    // 加载更多监听
    public void setOnLoadListener(OnLoadListener onLoadListener) {
        this.loadEnable = true;
        this.onLoadListener = onLoadListener;
    }

    public void onRefresh() {
        if (onRefreshListener != null) {
            onRefreshListener.onRefresh();
        }
    }

    public void onLoad() {
        if (onLoadListener != null) {
            onLoadListener.onLoad();
        }
    }


}
 

猜你喜欢

转载自blog.csdn.net/rlw_1009/article/details/81358634