进一步封装ListView,实现下拉刷新和分页刷新的功能

来源:互联网 发布:ios软件删不掉怎么办 编辑:程序博客网 时间:2024/05/08 01:34
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.RotateAnimation;import android.widget.AbsListView;import android.widget.ImageView;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.AbsListView.OnScrollListener;public class MyListView extends ListView implements OnScrollListener {    private View footer; //下拉刷新    private View header; //分页    private int firstVisibleItem; //第一个可见列表项的下标    private int lastVisibleItem; //最后一个可见列表项的下标    private int totalItemCount; //总共有多少个列表项    private int scrollState; //listView当前滚动状态    private boolean isLoading;        /**     * 分页加载接口     */    private ILoadListener iLoadListener;    /**     * 刷新数据接口     */    private IReflashListener iReflashListener;        /**     *     */    private LayoutInflater inflater;    /**     * 下拉刷新头部的高度     */    private int headerHeight;        private boolean isTopDown; //标记是否在列表的顶端按下    private int startY; //在列表的顶端按下的Y值    private int state; //当前的状态    private final int NONE = 0; //正常状态    private final int PULL = 1; //提示下拉状态    private final int RELEASE = 2; //提示释放状态    private final int REFLASHING = 3; //刷新状态                    public MyListView(Context context) {        super(context);        // TODO Auto-generated constructor stub        initView(context);    }        public MyListView(Context context, boolean hasHeader, boolean hasFooter) {        super(context);        initView(context);        if (hasHeader) {            setDefaultHeaderView();        }        if (hasFooter) {            setDefaultFooterView();        }    }        public MyListView(Context context, AttributeSet attrs) {        super(context, attrs);        // TODO Auto-generated constructor stub        initView(context);    }    public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        // TODO Auto-generated constructor stub        initView(context);    }        /**     * 设置底部刷新的布局     * @param viewId 布局的id(R.layout....)     */    public void setFooterView(int viewId) {        footer = inflater.inflate(viewId, null);        footer.setVisibility(View.GONE);        this.addFooterView(footer);    }        /**     * 设置默认的底部刷新布局     */    public void setDefaultFooterView() {        footer = inflater.inflate(R.layout.lv_footer_layout, null);        footer.setVisibility(View.GONE);        this.addFooterView(footer);    }        /**     * 设置头部刷新布局     * @param viewId 布局的id(R.layout....)     */    public void setHeaderView(int viewId) {        header = inflater.inflate(viewId, null);        header.setVisibility(View.GONE);        this.addHeaderView(header);    }        /**     * 设置默认的头部刷新布局     */    public void setDefaultHeaderView() {        header = inflater.inflate(R.layout.lv_header_layout, null);        measureView(header);        hideHeader();        this.addHeaderView(header);    }        /**     * 添加底部加载提示布局到listView     * @param context     */    private void initView(Context context) {        this.inflater = LayoutInflater.from(context);        setOnScrollListener(this);    }    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {        // TODO Auto-generated method stub        this.scrollState = scrollState;        /*         * 若滑动到ListView的底部,并且不再滑动状态的时候,显示分页刷新脚布局         * 否则隐藏该布局         */        if (scrollState == SCROLL_STATE_IDLE && lastVisibleItem == totalItemCount) {            if (!isLoading) {                showFooter();                //通过回调方法实现加载数据                iLoadListener.onLoad();                isLoading = false;            }        } else {            hideFooter();        }    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem,            int visibleItemCount, int totalItemCount) {        // TODO Auto-generated method stub        //记录第一行课件列表项的下标        this.firstVisibleItem = firstVisibleItem;        //计算最后一行课件列表项的下标        this.lastVisibleItem = firstVisibleItem + visibleItemCount;        //总共有多少个列表项        this.totalItemCount = totalItemCount;    }        @Override    public boolean onTouchEvent(MotionEvent ev) {        // TODO Auto-generated method stub        /**         * 这里实现下拉刷新的功能,通过监听手势的按下,移动,松开来实现         */        switch (ev.getAction()) {        /*         * 当按下屏幕的时候,如果第一行可见的列表项恰好是列表项的第一个时,把isTopDown标记值设置成true         * 并记录开始的时候的Y坐标         */        case MotionEvent.ACTION_DOWN:            if (firstVisibleItem == 0) {                isTopDown = true;                startY = (int) ev.getY();            }            break;                    /**         * 当动作是移动的时候,这时就要处理图片该如何显示的问题,因为下拉高度不同显示的图片和文字都不同         */        case MotionEvent.ACTION_MOVE:            onMove(ev);            break;                    /**         * 当动作是松开的时候,要判断两种状态:         * 1.如果下拉的高度足够,RELEASE也就是,那么就可以刷新,这时把状态设置成REFLASHING         * 2.如果下拉的状态不够,那么不能刷新,这时把状态设置成NONE,并且把标识isTopDown设置成false         */        case MotionEvent.ACTION_UP:            if (state == RELEASE) {                state = REFLASHING;                //设置界面应该如何显示                reflashViewByState();                setHeaderPaddingTop(10);                //加载数据                iReflashListener.onReflash();            } else if (state == PULL) {                state = NONE;                isTopDown = false;                //设置界面应该如何显示                reflashViewByState();            }            break;        default:            break;        }                return super.onTouchEvent(ev);    }    private void onMove(MotionEvent ev) {        // TODO Auto-generated method stub        //如果该移动操作不是在顶部下拉的,那么不执行操作,直接返回        if (!isTopDown) {            return;        }                        int tempY = (int) ev.getY();        //记录下拉了多少距离        int space = tempY - startY;        int topPadding = space - headerHeight;        switch (state) {        case NONE:            if (space > 0) {                state = PULL;                reflashViewByState();            }            break;                    case PULL:            setHeaderPaddingTop(topPadding);            if (space > headerHeight + 30                    && scrollState == SCROLL_STATE_TOUCH_SCROLL) {                state = RELEASE;                reflashViewByState();            }            break;                    case RELEASE:            setHeaderPaddingTop(topPadding);            if (space < headerHeight + 30) {                state = PULL;                reflashViewByState();            } else if (space <= 0) {                state = NONE;                isTopDown = false;                reflashViewByState();            }                        break;        }    }        private void reflashViewByState() {        TextView tip = (TextView) header.findViewById(R.id.tv_flash);        ImageView arrow = (ImageView) header.findViewById(R.id.iv_flash);        ProgressBar progress = (ProgressBar) header.findViewById(R.id.pb_flash);        RotateAnimation anim1 = new RotateAnimation(0, 180,                RotateAnimation.RELATIVE_TO_SELF, 0.5f,                RotateAnimation.RELATIVE_TO_SELF, 0.5f);        anim1.setDuration(500);        anim1.setFillAfter(true);        RotateAnimation anim2 = new RotateAnimation(180, 0,                RotateAnimation.RELATIVE_TO_SELF, 0.5f,                RotateAnimation.RELATIVE_TO_SELF, 0.5f);        anim2.setDuration(500);        anim2.setFillAfter(true);                switch (state) {        case NONE:            setHeaderPaddingTop(-headerHeight);            arrow.clearAnimation();            break;                    case PULL:            arrow.setVisibility(View.VISIBLE);            progress.setVisibility(View.GONE);            tip.setText("下拉可以刷新");            arrow.clearAnimation();            arrow.setAnimation(anim2);            break;                    case RELEASE:            arrow.setVisibility(View.VISIBLE);            progress.setVisibility(View.GONE);            tip.setText("释放立即刷新");            arrow.clearAnimation();            arrow.setAnimation(anim1);            break;                    case REFLASHING:            setHeaderPaddingTop(headerHeight);            arrow.setVisibility(View.GONE);            progress.setVisibility(View.VISIBLE);            tip.setText("正在刷新");            arrow.clearAnimation();            break;        default:            break;        }    }        public void reflashComplete() {        state = NONE;        isTopDown = false;        reflashViewByState();    }    public void setILoadListener(ILoadListener iLoadListener) {        this.iLoadListener = iLoadListener;    }        public interface ILoadListener {        public void onLoad();    }        /**     * 刷新数据接口     * @author raid     *     */    public interface IReflashListener {        public void onReflash();    }        public void setIReflashListener(IReflashListener iReflashListener) {        this.iReflashListener = iReflashListener;    }        /**     * 加载完毕     */    public void loadComplete() {        isLoading = false;        hideFooter();    }        /**     * 如果用户不需要分页功能,没有设置footer,     * 那么隐藏footer的时候需要判断footer是否为空     */    private void hideFooter() {        if (footer != null) {            footer.setVisibility(View.GONE);        }    }        /**     * 如果用户不需要分页功能,没有设置footer,     * 那么显示footer的时候需要判断footer是否为空     */    private void showFooter() {        if (footer != null) {            footer.setVisibility(View.VISIBLE);        }    }        /**     * 隐藏header     */    private void hideHeader() {        headerHeight = header.getMeasuredHeight();        setHeaderPaddingTop(-headerHeight);    }        /**     * 设置组件的上内边距     * @param paddingTop 上内边距     */    private void setHeaderPaddingTop(int paddingTop) {        header.setPadding(header.getPaddingLeft(),                paddingTop, header.getPaddingRight(),                header.getPaddingBottom());        header.invalidate();    }        /**     *  由于Android程序的运行机制决定了无法再组件类外部使用getWidth和getHeight方法     *  获得高度和宽度(在自定义组件类中可以实现),必须使用View.getMeasuredWidth     *  和View.getMeasureHeight方法获得当前组件的宽度和高度,     *  在调用这两个方法之前,必须调用View.measure方法先测量组件宽度和高度。     *  如果想直接获取在布局文件中定义的组件的宽度和高度,     *  可以直接使用View.getLayoutParams().width和View.getLayoutParams()...     *       *  通知父布局,占用的宽,高     * @param view     */    private void measureView(View view) {        ViewGroup.LayoutParams p = view.getLayoutParams();        if (p == null) {            p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,                    ViewGroup.LayoutParams.WRAP_CONTENT);        }        /**         * 有三个参数,分别代表:         * 1.父View的详细测量值(即MeasureSpec)         * 2.view的内外边距         * 3.子布局的宽度         */        int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);        int height;        int tempHeight = p.height;        if (tempHeight > 0) {            height = MeasureSpec.                    makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);        } else {            height = MeasureSpec.                    makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);        }        view.measure(width, height);    }}



lv_footer_layout.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:orientation="horizontal"    android:padding="15dp"    android:gravity="center" >    <ProgressBar     android:layout_width="wrap_content"    android:layout_height="30dp"    android:layout_gravity="center"/><TextView     android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_gravity="center"    android:paddingLeft="10dp"    android:text="加载中..."/>    </LinearLayout>

lv_header_layout.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:orientation="horizontal"    android:padding="15dp"    android:gravity="center"    android:background="#5E5E5D" >        <RelativeLayout         android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginRight="20dp">        <ImageView             android:id="@+id/iv_flash"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:scaleType="fitCenter"            android:src="@drawable/pull_to_refresh_arrow"/>                <ProgressBar             android:id="@+id/pb_flash"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            style="?android:attr/progressBarStyleSmall"/>    </RelativeLayout>        <TextView         android:id="@+id/tv_flash"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="下拉可以刷新"        android:textSize="20sp"        android:textColor="#EBEDEC"/></LinearLayout>


0 0