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

自定义ListView实现下拉刷新和上拉自动加载

效果图:

下拉效果:

 

上拉效果:

 

实现原理:通过ListView的addFooter与addHeader方法,将下拉布局与上拉布局添加到ListView中,再通过设置padding属性,隐藏头部和脚部

     监听onTouchEvent事件,根据手势滑动距离,动态更改下拉布局的padding,并动态更改头布局内控件效果

     监听onScrollStateChanged,动态显示隐藏脚布局

              设置回调,提供下拉刷新与加载更多的方法

Demo:https://files.cnblogs.com/files/liujingg/PullRefreshDemo.rar

PullListView.java

import java.text.SimpleDateFormat;
import java.util.Date;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * 自定义ListView,下拉刷新、上拉自动加载更多
 * @author liujing
 * @version 1.0
 */
public class PullListView extends ListView implements OnScrollListener {

    private final int PULL_DOWN_REFRESH = 0;//下拉状态
    private final int RELEASE_REFRESH = 1;//松开状态
    private final int REFRESHING = 2;//刷新中状态
    private int currentState = PULL_DOWN_REFRESH;
    private int mListViewOnScreenY = -1;
    private int downY = -1;
    
    private boolean isLoadingMore = false;
    private boolean isEnabledPullDownRefresh = false;
    private boolean isEnabledLoadMore = false;
    private OnPullDownRefresh mOnPullDownRefresh;
    
    //头布局、脚布局及高度
    private View mFootView;
    private LinearLayout mHeaderView;
    private int mFooterViewHeight;
    private int mPullDownHeaderViewHeight;
    
    //mHeaderView中组件及动画
    private View mCustomHeaderView;//用户自定义头布局
    private View mPullDownHeader;//下拉刷新头布局
    private RotateAnimation upAnimation,downAnimation;
    private ImageView ivArrow;
    private ProgressBar mProgressBar;
    private TextView tv_statue,tv_time;
    

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

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

    public PullListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initPullDownHeaderView();
        initLoadMoreFooterView();
    }
    
    private void initLoadMoreFooterView() {
        //加载更多的布局文件
        mFootView = View.inflate(getContext(), R.layout.pull_listview_footer,
                null);
        mFootView.measure(0, 0);//测量
        mFooterViewHeight = mFootView.getMeasuredHeight();
        //隐藏脚布局
        mFootView.setPadding(0,-mFooterViewHeight,0,0);
        addFooterView(mFootView);
        setOnScrollListener(this);
    }

    private void initPullDownHeaderView() {
        //下拉刷新的布局文件
        mHeaderView = (LinearLayout) View.inflate(getContext(),
                R.layout.pull_listview_header, null);
        mPullDownHeader = mHeaderView
                .findViewById(R.id.ll_refresh_pull_down_header);
        ivArrow = (ImageView) mHeaderView
                .findViewById(R.id.iv_refresh_header_arrow);
        mProgressBar = (ProgressBar) mHeaderView
                .findViewById(R.id.pb_refresh_header);
        tv_statue = (TextView) mHeaderView
                .findViewById(R.id.tv_refresh_header_status);
        tv_time = (TextView) mHeaderView
                .findViewById(R.id.tv_refresh_header_time);
        mPullDownHeader.measure(0, 0);//测量
        mPullDownHeaderViewHeight = mPullDownHeader.getMeasuredHeight();
        //隐藏头布局
        mPullDownHeader.setPadding(0, -mPullDownHeaderViewHeight, 0, 0);
        addHeaderView(mHeaderView);
        initAnimation();
    }
    
    /**
     * 添加额外的头布局,比如轮播图
     * @param v 自定义头布局
     */
    public void addListViewCustomHeaderView(View v) {
        mCustomHeaderView = v;
        mHeaderView.addView(mCustomHeaderView);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            downY = (int) ev.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            if (downY == -1)
                downY = (int) ev.getY();
            //是否启用下拉刷新
            if (!isEnabledPullDownRefresh)
                break;
            if (currentState == REFRESHING)
                break;
            //解决用户添加header与下拉刷新header的冲突
            if (mCustomHeaderView != null) {
                int[] location = new int[2];
                if (mListViewOnScreenY == -1) {
                    this.getLocationOnScreen(location);
                    mListViewOnScreenY = location[1];
                }
                mCustomHeaderView.getLocationOnScreen(location);
                if (location[1] < mListViewOnScreenY) {
                    break;
                }
            }

            int moveY = (int) ev.getY();
            int diffY = (moveY - downY)/2;
            if (diffY > 0 && getFirstVisiblePosition() == 0) {
                int paddingTop = -mPullDownHeaderViewHeight + diffY;
                if (paddingTop < 0 && currentState != PULL_DOWN_REFRESH) {
                    //当前没有完全显示且当前状态为松开刷新,进入下拉刷新
                    currentState = PULL_DOWN_REFRESH;
                    refreshPullDownState();
                } else if (paddingTop > 0 && currentState != RELEASE_REFRESH) {
                    //当前完全显示且当前状态为下拉刷新,进入松开刷新
                    currentState = RELEASE_REFRESH;
                    refreshPullDownState();
                }
                mPullDownHeader.setPadding(0, paddingTop, 0, 0);
                return true;
            }else if(diffY < 0 && getLastVisiblePosition() == getCount()-1){
                //脚布局可向上滑动
                mFootView.setPadding(0,0,0,0);
            }
            break;
        case MotionEvent.ACTION_UP:
            downY = -1;
            if (currentState == PULL_DOWN_REFRESH) {
                //隐藏header
                mPullDownHeader.setPadding(0, -mPullDownHeaderViewHeight, 0, 0);
            } else if (currentState == RELEASE_REFRESH) {
                currentState = REFRESHING;
                refreshPullDownState();
                mPullDownHeader.setPadding(0, 0, 0, 0);
                //回调刷新方法
                if (mOnPullDownRefresh != null) {
                    mOnPullDownRefresh.onPullDownRefresh();
                }
            }
            break;
        }
        return super.onTouchEvent(ev);
    }
    
    /**
     * 隐藏头布局或脚布局并重置控件
     */
    public void OnRefreshDataFinish() {
        if (isLoadingMore) {
            isLoadingMore = false;
            mFootView.setPadding(0,-mFooterViewHeight,0,0);
        } else {
            ivArrow.setVisibility(View.VISIBLE);
            mProgressBar.setVisibility(View.INVISIBLE);
            tv_statue.setText("下拉刷新");
            tv_time.setText("最后刷新时间:" + getCurrentTime());
            mPullDownHeader.setPadding(0, -mPullDownHeaderViewHeight, 0, 0);
            currentState = PULL_DOWN_REFRESH;

        }
    }

    private String getCurrentTime() {
        SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
        return format.format(new Date());
    }

    private void refreshPullDownState() {
        switch (currentState) {
        case PULL_DOWN_REFRESH:
            ivArrow.startAnimation(downAnimation);
            tv_statue.setText("下拉刷新");
            break;
        case RELEASE_REFRESH:
            ivArrow.startAnimation(upAnimation);
            tv_statue.setText("松开刷新");
            break;
        case REFRESHING:
            ivArrow.clearAnimation();
            ivArrow.setVisibility(View.INVISIBLE);
            mProgressBar.setVisibility(View.VISIBLE);
            tv_statue.setText("正在刷新");
            break;
        default:
            break;
        }
    }
    
    /**
     * 箭头旋转动画
     */
    private void initAnimation() {
        upAnimation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF,
                0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        upAnimation.setDuration(200);
        upAnimation.setFillAfter(true);

        downAnimation = new RotateAnimation(-180, -360,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
        downAnimation.setDuration(200);
        downAnimation.setFillAfter(true);
    }
    
    /**
     * 回调方法,用于刷新数据及加载更多
     * @param listener
     */
    public void setOnPullDownRefresh(OnPullDownRefresh listener) {
        this.mOnPullDownRefresh = listener;
    }

    public interface OnPullDownRefresh {
        public void onPullDownRefresh();
        public void onLoadingMore();
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (!isEnabledLoadMore) {
            return;
        }
        //listView停止状态或惯性滑动状态
        if (scrollState == SCROLL_STATE_IDLE
                || scrollState == SCROLL_STATE_FLING) {
            //listView已到达最底部
            if ((getLastVisiblePosition() == getCount() - 1) && !isLoadingMore) {
                isLoadingMore = true;
                //展示脚布局
                //mFootView.setPadding(0, 0, 0, 0);
                setSelection(getCount());
                if (mOnPullDownRefresh != null) {
                    mOnPullDownRefresh.onLoadingMore();
                }
            }
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        
    }
    
    /**
     * 是否启用下拉刷新
     * @param isEnable
     */
    public void setEnabledPullDownRefresh(boolean isEnable) {
        isEnabledPullDownRefresh = isEnable;
    }
    
    /**
     * 是否启用加载更多
     * @param isEnable
     */
    public void setEnabledLoadMore(boolean isEnable) {
        isEnabledLoadMore = isEnable;
    }
}

 

 pull_listview_header.xml

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

    <LinearLayout
        android:id="@+id/ll_refresh_pull_down_header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <FrameLayout
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_marginBottom="3dp"
            android:layout_marginLeft="28dp"
            android:layout_marginTop="8dp" >

            <ImageView
                android:id="@+id/iv_refresh_header_arrow"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:src="@drawable/common_listview_headview_red_arrow" />

            <ProgressBar
                android:id="@+id/pb_refresh_header"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:indeterminateDrawable="@drawable/custom_progressbar"
                android:visibility="invisible" />
        </FrameLayout>

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_weight="1"
            android:gravity="center_horizontal"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/tv_refresh_header_status"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="下拉刷新"
                android:textColor="#ff0000"
                android:textSize="16sp"
                android:textStyle="bold" >
            </TextView>

            <TextView
                android:id="@+id/tv_refresh_header_time"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="5dp"
                android:text="最后刷新时间:09:23:23"
                android:textColor="@android:color/darker_gray"
                android:textSize="12sp" >
            </TextView>
        </LinearLayout>
        <TextView 
            android:layout_width="40dp"
            android:layout_marginRight="28dp"
            android:layout_height="wrap_content"
            />
    </LinearLayout>

</LinearLayout>

pull_listview_footer.xml

<?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="40dp"
        android:layout_height="40dp"
        android:layout_margin="2dp"
        android:indeterminateDrawable="@drawable/custom_progressbar" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="5dp"
        android:text="加载更多..."
        android:textColor="#ff0000"
        android:textSize="18sp" />

</LinearLayout>

custom_progressbar.xml

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="360" >

    <shape
        android:innerRadiusRatio="4"
        android:shape="ring"
        android:thicknessRatio="10"
        android:useLevel="false" >
        <gradient
            android:centerColor="#ff6666"
            android:endColor="#ff0000"
            android:startColor="#ffffff"
            android:type="sweep" />
    </shape>

</rotate>

 

posted @ 2015-07-09 17:54  coffeesuke  阅读(1568)  评论(2编辑  收藏  举报