Android 自定义下拉刷新上拉加载更多Listview

本文介绍了一个自定义的Android ListView,支持下拉刷新和上拉加载更多功能。通过修改ListView的触摸事件和滚动监听,实现头部和底部视图的动态显示。提供了详细的实现代码和XML布局。

好久没写了。。先说一下这边隐藏头部布局的原理:

//将padding设置为header的高度的负值来隐藏它
footerView.setPadding(0, -footerViewH, 0, 0);
 

先是预备工作:上拉加载更多底部布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="horizontal">
    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:indeterminate="true"
        android:layout_centerInParent="true"
        android:indeterminateDrawable="@drawable/indeterminate_drawable"
        android:indeterminateDuration="500"
        />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:textSize="30dp"
        android:layout_marginLeft="15dp"
        android:text="正在加载更多..."/>
</LinearLayout>
下拉刷新头部布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal"
    android:orientation="horizontal">

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp">

        <ImageView
            android:id="@+id/iv_arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@drawable/indicator_arrow" />
        <ProgressBar
            android:id="@+id/pb_ratate"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="invisible"
            android:indeterminate="true"
            android:layout_centerInParent="true"
            android:indeterminateDrawable="@drawable/indeterminate_drawable"
           android:indeterminateDuration="500"
            />
    </RelativeLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:orientation="vertical"
        android:layout_gravity="center"
        android:gravity="center">
        <TextView
            android:id="@+id/tv_state"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20dp"
            android:textColor="#aa000000"
            android:text="下拉刷新"
            />
        <TextView
            android:id="@+id/tv_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18dp"
            android:textColor="#99666666"
            android:text="最后刷新:2015-12-03"
            />
    </LinearLayout>
</LinearLayout>

然后就是自定义RefreshListview!

注释很详细,大家慢慢看一下:

public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
    private View headView;
    private ImageView iv_arrow;//箭头图标
    private ProgressBar pb_ratate;//刷新中图标
    private TextView tv_state;//刷新状态文本
    private TextView tv_time;//最新刷新时间
    private View footerView;

    private int headViewH;
    private int footerViewH;
    //触摸点Y的坐标
    private int downY;

    private final int PULL_REFRESH = 0;//下拉刷新的状态
    private final int RELEASE_REFRESH = 1;//松开刷新的状态
    private final int REFRESHING = 2;//正在刷新状态
    private int currentState = PULL_REFRESH;//当前状态,默认为下拉刷新状态
    private boolean isLoadingMore = false;//是否加载更多

    private RotateAnimation upAnimation;
    private RotateAnimation downAnimation;


    public RefreshListView(Context context) {
        super(context);
        init();
    }

    public RefreshListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        setOnScrollListener(this);
        initHeaderView();
        initRotateAnimation();
        initFooterView();
    }

    /**
     * 初始化headerview
     */
    private void initHeaderView() {

        headView = View.inflate(getContext(), R.layout.layout_header, null);
        iv_arrow = (ImageView) headView.findViewById(R.id.iv_arrow);
        pb_ratate = (ProgressBar) headView.findViewById(R.id.pb_ratate);
        tv_state = (TextView) headView.findViewById(R.id.tv_state);
        tv_time = (TextView) headView.findViewById(R.id.tv_time);
        //通知系统主动去测量
        headView.measure(0, 0);
        //getMeasuredHeight()获取测量后的高度

        headViewH = headView.getMeasuredHeight();
        //将padding设置为header的高度的负值来隐藏它
        headView.setPadding(0, -headViewH, 0, 0);
        addHeaderView(headView);
    }

    private void initRotateAnimation() {
        upAnimation = new RotateAnimation(0, -180,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        upAnimation.setDuration(300);
        upAnimation.setFillAfter(true);
        downAnimation = new RotateAnimation(-180, -360,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        downAnimation.setDuration(300);
        downAnimation.setFillAfter(true);
    }

    /**
     * 初始化FooterView
     */
    private void initFooterView() {
        footerView = View.inflate(getContext(), R.layout.layout_footer, null);
        //通知系统主动去测量
        footerView.measure(0, 0);
        //getMeasuredHeight()获取测量后的高度
        footerViewH = footerView.getMeasuredHeight();
        //将padding设置为header的高度的负值来隐藏它
        footerView.setPadding(0, -footerViewH, 0, 0);
        addFooterView(footerView);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downY = (int) ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (currentState == REFRESHING) {
                    break;
                }
                int delaY = (int) (ev.getY() - downY);
                //距离顶部的大小
                int paddingTop = -headViewH + delaY;
                //只有当paddingTop> -headViewH,且第一项为首项时才进行拦截
                if (paddingTop > -headViewH && getFirstVisiblePosition() == 0) {
                    headView.setPadding(0, paddingTop, 0, 0);
                    if (paddingTop >= 0 && currentState == PULL_REFRESH) {
                        //从下拉刷新进去松开刷新状态
                        currentState = RELEASE_REFRESH;
                        refreshHeadView();
                    } else if (paddingTop < 0 && currentState == RELEASE_REFRESH) {
                        //进去下拉刷新状态
                        currentState = PULL_REFRESH;
                        refreshHeadView();
                    }
                    return true;//拦截TOUCH_MOVE,不让listview处理move事件
                }
                break;
            case MotionEvent.ACTION_UP:
                if (currentState == PULL_REFRESH) {
                    headView.setPadding(0, -headViewH, 0, 0);
                } else if (currentState == RELEASE_REFRESH) {
                    headView.setPadding(0, 0, 0, 0);
                    currentState = REFRESHING;
                    refreshHeadView();
                    if (onRefreshListener != null) {
                        onRefreshListener.onPullRefresh();
                    }
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 根据currentState改变headview
     */
    private void refreshHeadView() {
        switch (currentState) {
            case PULL_REFRESH:
                tv_state.setText("下拉刷新");
                iv_arrow.startAnimation(downAnimation);
                break;
            case RELEASE_REFRESH:
                tv_state.setText("松开刷新");
                iv_arrow.startAnimation(upAnimation);
                break;
            case REFRESHING:
                tv_state.setText("正在拼命加载中...");
                iv_arrow.clearAnimation();//因为向上旋转的动画有可能还未完成
                iv_arrow.setVisibility(View.INVISIBLE);
                pb_ratate.setVisibility(VISIBLE);
                break;
        }
    }

    /**
     * 完成刷新,重置状态,在获取完数据并更新完adapter之后,在UI线程中调用
     */
    public void completeRefresh() {
        if (isLoadingMore){
            footerView.setPadding(0,0,0,-footerViewH);
            isLoadingMore=false;
        }else {
            headView.setPadding(0, -headViewH, 0, 0);
            currentState = PULL_REFRESH;
            pb_ratate.setVisibility(INVISIBLE);
            iv_arrow.setVisibility(VISIBLE);
            tv_state.setText("下拉刷新");
            tv_time.setText("最后刷新" + getCurrentTime());
        }

    }

    /**
     * 获取当前系统时间,并格式化
     *
     * @return
     */
    private String getCurrentTime() {
        SimpleDateFormat format = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
        return format.format(new Date());
    }

    /**
     * 提供监听接口,监听加载数据
     */
    private OnRefreshListener onRefreshListener;

    public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
        this.onRefreshListener = onRefreshListener;
    }

    public interface OnRefreshListener {
        void onPullRefresh();
        void onLoadingMore();
    }

    /**
     * scrollState:
     * SCROLL_STATE_IDLE = 0;闲置状态,手指触摸结束
     * SCROLL_STATE_TOUCH_SCROLL = 1;手指触摸滚动,就是手指按着滑动
     * SCROLL_STATE_FLING = 2;惯性滑动,就是手指快速滑动
     *
     * @param view
     * @param scrollState
     */
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        /**
         * @return The number of items owned by the Adapter associated with this
         *         AdapterView. (This is the number of data items, which may be
         *         larger than the number of visible views.)
         */
        if (scrollState == SCROLL_STATE_IDLE
                && getLastVisiblePosition() == (getCount() - 1)
                && !isLoadingMore) {
            isLoadingMore=true;
            footerView.setPadding(0, 0, 0, 0);
            setSelection(getCount());
            if (onRefreshListener!=null){
                onRefreshListener.onLoadingMore();
            }
        }
    }

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

这样就可以在布局中使用自己的RefreshListview了:

 <com.chenshi.myrefleshview.RefreshListView
        android:id="@+id/refreshlistview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


demo下载:demo下载链接

能看到大家的评论,我会很开心的!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值