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

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>


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值