安卓自定义ListView实现上拉加载、下拉刷新

来源:互联网 发布:俄罗斯的聊天软件 编辑:程序博客网 时间:2024/04/28 06:46

自定义ListView实现上拉加载、下来刷新,暂时未加入侧滑功能,可以设置是否需要加载、刷新以及每次加载的item个数,可以根据设置的加载item个数判断第一次获取数据后是否加载(如果少于设置的个数则不许加载)

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">    <RelativeLayout        android:id="@+id/my_list_view_header_content"        android:layout_width="match_parent"        android:layout_height="60dp">        <LinearLayout            android:id="@+id/my_list_view_header_text"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:gravity="center"            android:orientation="vertical">            <TextView                android:id="@+id/my_list_view_header_hint_textview"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:text="下拉刷新" />            <LinearLayout                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_marginTop="3dp">                <TextView                    android:layout_width="wrap_content"                    android:layout_height="wrap_content"                    android:text="上次更新时间:"                    android:textSize="12sp" />                <TextView                    android:id="@+id/my_list_view_header_time"                    android:layout_width="wrap_content"                    android:layout_height="wrap_content"                    android:textSize="12sp" />            </LinearLayout>        </LinearLayout>        <ImageView            android:id="@+id/my_list_view_header_arrow"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_alignLeft="@id/my_list_view_header_text"            android:layout_centerVertical="true"            android:layout_marginLeft="-35dp"            android:src="@drawable/my_listview_arrow" />        <ProgressBar            android:id="@+id/my_list_view_header_progressbar"            android:layout_width="30dp"            android:layout_height="30dp"            android:layout_alignLeft="@id/my_list_view_header_text"            android:layout_centerVertical="true"            android:layout_marginLeft="-40dp"            android:visibility="gone" />    </RelativeLayout></LinearLayout>

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="75dp"    android:clickable="false"    android:orientation="vertical">    <LinearLayout        android:id="@+id/my_listview_footer_load_layout"        android:layout_width="match_parent"        android:layout_height="75dp"        android:gravity="center"        android:orientation="horizontal"        android:paddingBottom="10dp"        android:paddingTop="10dp">        <ProgressBar            android:id="@+id/my_listview_footer_load_progress"            android:layout_width="30dp"            android:layout_height="30dp"            android:layout_marginRight="10dp" />        <TextView            android:id="@+id/my_listview_footer_load_text"            android:layout_width="wrap_content"            android:layout_height="20dp"            android:text="正在加载"            android:textSize="16sp" />    </LinearLayout></LinearLayout>

java代码
import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.animation.LinearInterpolator;import android.view.animation.RotateAnimation;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;import com.jwwl.carrierapp.R;import java.text.SimpleDateFormat;import java.util.Date;/** * 自定义ListView用于上拉加载、下拉刷新、左滑菜单 */public class MyListView extends ListView implements OnScrollListener {    /**     * 滑动开始时,手指按下Y的坐标     */    private int downY;    /**     * 每次加载的Item个数,根据此个数判断第一次获取数据后是否需要加载,默认为0个     */    private int sizeItem = 0;    /**     * 用于保证每次滑动是一个完整的滑动事件,按下的downY坐标值每次事件中只被记录一次(按下->滑动->松开,为一个完整事件),默认为false     * true处于一个事件中     * false未开始事件     */    private boolean isRecored = false;    /**     * 外部调用设置,是否可以下拉刷新,默认为false     * true可以下拉刷新     * false不可以下拉刷新     */    private boolean isRefreshable = false;    /**     * 外部调用设置,是否可以上拉加载数据,默认为false     * true 可以上拉加载     * false 不可以上拉加载     */    private boolean isLoadable = false;    /**     * 是否处于可下拉刷新状态,当Item为0时为true,默认为false     * true可以下拉刷新状态     * false不可以下拉刷新状态     */    private boolean isRefresh = false;    /**     * 是否处于可上拉加载状态,当前屏幕最后一个Item下标为总的Item下标时为true,默认为false     * true可以上拉加载状态     * false不可以上拉加载状态     */    private boolean isLoad = false;    /**     * 用于判断是否是从下拉要刷新状态滑动到下拉要返回状态,默认为false     * true 是     * false 不是     */    private boolean isBack = false;    /**     * 是否处于上拉加载中,默认为false     * true上拉加载中     * false不在上拉加载中     */    private boolean isLoading = false;    /**     * 下拉刷新头部布局,各个控件     */    private View headerView; // 头部布局    private TextView headerTipsTv; //下来刷新提示    private TextView headerTimeTv; //下来刷新时间    private ImageView headerArrowIv; //下来刷新箭头    private ProgressBar headerProgressBar; //下来刷新进度条    /**     * 底部布局     */    private View footerView;    /**     * 加载监听接口     */    private OnLoaderListener onLoaderListener;    /**     * 刷新监听接口     */    private OnRefreshListener refreshListener;    /**     * 下拉刷新头部布局的高度     */    private int headerContentHeight;    /**     * 下来刷新箭头动画     */    private RotateAnimation rotateAnimation; //正向旋转,    private RotateAnimation reverseRotateAnimation; //反向旋转,    /**     * 下拉刷新状态值:下拉状态,松开不刷新     */    private final static int PULL_TO_BACK = 0;    /**     * 下拉刷新状态值:下拉状态,松开会刷新     */    private final static int PULL_TO_REFRESHING = 1;    /**     * 下拉刷新状态值:刷新中     */    private final static int REFRESHING = 2;    /**     * 上拉刷新状态值:完成     */    private final static int DONE = 3;    /**     * 上拉刷新状态,默认为完成状态     */    private int state = DONE;    /**     * 滑动的偏移比例,当滑动距离除以此比例大于等于布局高度时,则达到可以刷新加载状态     */    private final static int RATIO = 3;    //构造方法    public MyListView(Context context) {        super(context);        initView(context);    }    public MyListView(Context context, AttributeSet attrs) {        super(context, attrs);        initView(context);    }    public MyListView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        initView(context);    }    /**     * 初始化View     */    private void initView(Context context) {        LayoutInflater inflater = LayoutInflater.from(context);        setCacheColorHint(context.getResources().getColor(R.color.app_white)); //ListView滑动时背景/*         * 下拉刷新: */        //获取布局        headerView = inflater.inflate(R.layout.my_listview_header, null); //获取头部控件        headerView.setClickable(false); //设置布局不可点击        headerTipsTv = (TextView) headerView.findViewById(R.id.my_list_view_header_hint_textview); //下来刷新标题        headerTimeTv = (TextView) headerView.findViewById(R.id.my_list_view_header_time); //下拉刷新时间        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        headerTimeTv.setText(sdf.format(new Date()));        headerArrowIv = (ImageView) headerView.findViewById(R.id.my_list_view_header_arrow); //下拉刷新箭头        headerProgressBar = (ProgressBar) headerView.findViewById(R.id.my_list_view_header_progressbar); //下拉刷新进度条        //获取下拉刷新控件高度        headerView.measure(0, 0);        headerContentHeight = headerView.getMeasuredHeight();        //设置内边距,正好距离顶部为一个负的整个布局的高度,正好把头部隐藏        headerView.setPadding(0, -headerContentHeight, 0, 0);        //重绘一下        headerView.invalidate();        //将下拉刷新的布局加入ListView的顶部        addHeaderView(headerView, null, false);        //设置正向旋转动画事件        rotateAnimation = new RotateAnimation(0, -180,                RotateAnimation.RELATIVE_TO_SELF, 0.5f,                RotateAnimation.RELATIVE_TO_SELF, 0.5f);        rotateAnimation.setInterpolator(new LinearInterpolator());        rotateAnimation.setDuration(200);        rotateAnimation.setFillAfter(true);        //设置反向旋转动画事件        reverseRotateAnimation = new RotateAnimation(-180, 0,                RotateAnimation.RELATIVE_TO_SELF, 0.5f,                RotateAnimation.RELATIVE_TO_SELF, 0.5f);        reverseRotateAnimation.setInterpolator(new LinearInterpolator());        reverseRotateAnimation.setDuration(200);        reverseRotateAnimation.setFillAfter(true);/*         * 上拉加载 */        //获取布局        footerView = inflater.inflate(R.layout.my_listview_footer, null); //获取底部加载控件        footerView.findViewById(R.id.my_listview_footer_load_layout).setVisibility(GONE);        footerView.setClickable(false);//设置布局不可点击        // 将上拉加载的布局加入ListView的底部        addFooterView(footerView);        /*        滑动监听事件         */        setOnScrollListener(this);    }    /**     * 设置是否可以下拉刷新     *     * @param enable true-可以下拉刷新,false-不可以下拉刷新     */    public void setRefreshEnable(boolean enable) {        isRefreshable = enable;    }    /**     * 设置是否可以上拉加载     *     * @param enable true-可以上拉加载,false-不可以上拉加载     */    public void setLoadEnable(boolean enable) {        isLoadable = enable;    }    /**     * 设置每次加载的Item个数     */    public void setSizeItem(int sizeItem) {        this.sizeItem = sizeItem;    }    @Override    public boolean onTouchEvent(MotionEvent ev) {        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN: //按下                downY = (int) ev.getY(); //记录按下时的y坐标                /*                * 如果可以刷新或加载,并且未开始一个事件,则开始一个滑动事件    */                if (isRefreshable && !isRecored && (isRefresh || isLoad)) {                    isRecored = true;                }                break;            case MotionEvent.ACTION_MOVE: //滑动                int moveY = (int) ev.getY();                /*                 * 如果可以刷新或加载,并且未开始一个事件,则开始一个滑动事件     */                if (isRefreshable && !isRecored && (isRefresh || isLoad)) {                    isRecored = true;                    downY = moveY;                }                /**                 * 根据是否处于滑动事件和刷新加载状态及是否处于可刷新判断刷新                 */                if (isRecored && state != REFRESHING && isRefresh) {                    /*                    如果在完成状态滑动                    当下滑时,状态设置为下拉状态,并更新header的信息                     */                    if (state == DONE) {                        //1、如果滑动值为正,则是往下滑,状态设置为下拉要返回状态                        if (moveY - downY > 0) {                            state = PULL_TO_BACK;                            changeViewByState();//更新hander信息                        }                    }                    /*                    如果在下拉要返回状态滑动                     */                    if (state == PULL_TO_BACK) {                        setSelection(0);                        //1、如果滑动值为负,则是上滑,当滑动超出原按下位置时设为完成状态                        if ((moveY - downY) <= 0) {                            state = DONE;                            changeViewByState();//更新hander信息                        }                        //2、如果滑动值为正,则是下滑,当滑动达到可以刷新时设为下拉可刷新状态                        if ((moveY - downY) > 0 && (moveY - downY) / RATIO >= headerContentHeight) {                            state = PULL_TO_REFRESHING;                            changeViewByState();//更新hander信息                        }                        //3、设置header的padding值                        headerView.setPadding(0, (moveY - downY) / RATIO - headerContentHeight, 0, 0);                    }                    /*                    如果在下拉可刷新状态滑动                     */                    if (state == PULL_TO_REFRESHING) {                        setSelection(0);                        //1、如果滑动值为正,则是下滑,当滑动达回到下拉要返回状态时,设置为下拉要返回状态                        if ((moveY - downY) > 0 && (moveY - downY) / RATIO < headerContentHeight) {                            isBack = true;                            state = PULL_TO_BACK;                            changeViewByState();//更新hander信息                        }                        //2、设置header的padding值                        headerView.setPadding(0, (moveY - downY) / RATIO - headerContentHeight, 0, 0);                    }                }                /**                 * 根据是否处于滑动事件和刷新加载状态及是否处于可刷新判断加载                 */                if (isRecored && !isLoading && isLoad) {                    //1、如果滑动值为负,则是往上滑,状态设置上拉加载状态                    if (moveY - downY < 0 && onLoaderListener != null) {                        isLoading = true;                        Log.i("TAG", "数据加载load");                        footerView.findViewById(R.id.my_listview_footer_load_layout).setVisibility(VISIBLE);                        onLoaderListener.onLoad();                    }                }                break;            case MotionEvent.ACTION_UP: //松开                isBack = false;                isRecored = false; //按下、滑动、松开整个事件结束                /**                 *  根据松开时的刷新状态判断是否刷新                 *  只有不处于刷新中才判断                 */                if (state != REFRESHING) {                    //下拉到要返回状态松开                    if (state == PULL_TO_BACK) {                        state = DONE; //状态设为完成                        changeViewByState();//更新hander信息                    }                    //下拉到要刷新状态松开                    if (state == PULL_TO_REFRESHING) {                        //回调刷新方法                        if (refreshListener != null) {                            state = REFRESHING; //状态设为刷新                            changeViewByState();//更新hander信息                            refreshListener.onRefresh();                        } else {                            state = DONE; //状态设为完成                            changeViewByState();//更新hander信息                        }                    }                }                break;        }        return super.onTouchEvent(ev);    }    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {        /*         * 滑动时会一直回调该方法,直到停止滑动。单击时回调一次该方法。         * firstVisibleItem表示当前屏幕显示的第一个listItem在整个listView里的位置(下标从0开始);         * visibleItemCount表示当前屏幕可见的listItem(部分显示的listItem也算)总数;         * totalItemCount表示listView里listItem总数。         * <p/>         * 当滑动到列表首个Item时,判断是否可以下拉刷新         * 当滑动到列表结尾之前,判断是否加载数据         */        //根据当前首个item下标是否是0并且是否可以下拉刷新,判断是否能够下拉刷新        if (isRefreshable && firstVisibleItem == 0) {            isRefresh = true;        } else {            isRefresh = false;        }        //当前屏幕首个item的下标与当前屏幕item个数之和同item总个数比较,判断是否加载数据        if (isLoadable && totalItemCount == (firstVisibleItem + visibleItemCount) && sizeItem <= totalItemCount && totalItemCount != 0) {            isLoad = true;        } else {            isLoad = false;        }    }    /**     * 当刷新状态改变时候,调用该方法,根据当前状态更新header界面信息     */    private void changeViewByState() {        switch (state) {            case PULL_TO_BACK: //松开不刷新                headerProgressBar.setVisibility(View.GONE); //隐藏进度条                headerTipsTv.setVisibility(View.VISIBLE); //显示刷新提示                headerTipsTv.setText("下拉刷新"); //设置刷新提示内容                headerTimeTv.setVisibility(View.VISIBLE); //显示上次刷新时间                headerArrowIv.setVisibility(View.VISIBLE); //显示刷新箭头                headerArrowIv.clearAnimation(); //清除刷新箭头动画                //根据是否是从下拉要刷新状态滑动到下拉要返回状态,来判断是否调用反向旋转的箭头动画                if (isBack) {                    isBack = false;                    headerArrowIv.startAnimation(reverseRotateAnimation);//设置刷新动画为反向旋转动画                }                break;            case PULL_TO_REFRESHING: //松开要刷新                headerProgressBar.setVisibility(View.GONE);//隐藏进度条                headerTipsTv.setVisibility(View.VISIBLE);//显示刷新提示                headerTipsTv.setText("松开刷新");//设置刷新提示内容                headerTimeTv.setVisibility(View.VISIBLE);//显示上次刷新时间                headerArrowIv.setVisibility(View.VISIBLE);//显示刷新箭头                headerArrowIv.clearAnimation(); //清除刷新箭头动画                headerArrowIv.startAnimation(rotateAnimation);//设置刷新动画为正向旋转动画                break;            case REFRESHING: //刷新中                headerView.setPadding(0, 0, 0, 0); //设置刷新头布局padding                headerProgressBar.setVisibility(View.VISIBLE);//显示进度条                headerTimeTv.setVisibility(View.VISIBLE);//显示刷新提示                headerTipsTv.setText("正在刷新");//设置刷新提示内容                headerArrowIv.clearAnimation();//清除刷新箭头动画                headerArrowIv.setVisibility(View.GONE);//隐藏刷新箭头                break;            case DONE: //刷新完成                headerView.setPadding(0, -1 * headerContentHeight, 0, 0);//设置刷新头部布局padding                headerProgressBar.setVisibility(View.GONE);//隐藏进度条                headerTimeTv.setVisibility(View.VISIBLE);//显示刷新提示                headerTipsTv.setText("下拉刷新");//设置刷新提示内容                headerArrowIv.clearAnimation();//清除刷新箭头动画                headerArrowIv.setVisibility(View.VISIBLE);//显示刷新箭头                break;        }    }    //刷新监听方法,用于刷新接口实现    public void setRefreshListener(OnRefreshListener refreshListener) {        this.refreshListener = refreshListener;    }    //刷新回调接口,用于回调刷新方法    public interface OnRefreshListener {        void onRefresh();    }    //刷新完成调用方法    public void refreshComplete() {        //刷新状态设为完成状态        state = DONE;        //设置刷新时间        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        headerTimeTv.setText(sdf.format(new Date()));        //更新header信息        changeViewByState();    }    //加载监听方法,用于加载接口的实现    public void setLoaderListener(OnLoaderListener onLoaderListener) {        this.onLoaderListener = onLoaderListener;    }    // 加载回调接口,用于回调加载方法    public interface OnLoaderListener {        void onLoad();    }    //加载完毕调用方法    public void loadComplete() {        isLoading = false;        footerView.findViewById(R.id.my_listview_footer_load_layout).setVisibility(GONE);    }}


0 0
原创粉丝点击