Android之实现ListView的“下拉刷新”、“上拉加载”、“自动加载”功能(一)

发现自己不怎么喜欢直接利用别人做好的放到自己的项目里使用,除非建立在自己会实现的基础上,所以,我还是想自己去实现ListView的这种效果,当然,期间参考了许多开源项目,在此感谢他们的灵感。——前记

首先是效果图:






实现ListView有两种方法,

第一种是直接继承ListView,通过addHeaderView和addFooterView来实现,这个方法相对来说简单,比如QQ的好友列表就是利用这种来实现的,这种方法将在之后的第(二)节中进行讲解。

第二种方法是通过继承布局来实现,通过addView来实现,好处在于WebView、ScrollView都可以利用这种思维进行实现,没有第一种方法的局限性,实现起来相对复杂点,比如微博主界面就是利用这种方法来实现(可以通过刷新时,滑动条的位置来判断出来),第二种方法也是今天我接下来讲的内容。


针对第二种方法,布局结构图如下:


通过上图可以知道,Header和Footer在正常状态下是处于屏幕之外(通过设置padding),只有在发生下拉和上拉的时候,才会显示在屏幕可视范围中(通过scrollTo),所以可以知道,在继承LinearLayout之后,通过把Header、ListView、Footer添加到布局中,然后分别为Header、Footer实现相应的界面布局,为各部分实现相应的响应机制即可。还需要注意一点是,对于ListView还有一个是“查看更多”的Footer,这个应该要通过addFooterView实现在ListView里面。


一、Header:包括LOGO、箭头、刷新提示语、更新的时间、圆形进度条

layout\refreshable_listview_header.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="vertical" 
    android:gravity="bottom"
    android:id="@+id/header">
    
    <!-- LOGO -->
    <ImageView android:layout_width="wrap_content"        
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        />
        
    <RelativeLayout android:layout_width="fill_parent"
        android:layout_height="60dp" >

        <LinearLayout android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:orientation="vertical" 
            android:id="@+id/headerRight">

            <!-- 下拉刷新 -->
            <TextView android:id="@+id/hintText"                       
                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:id="@+id/lastTimeLabel" 
                    android:layout_width="wrap_content"  
                    android:layout_height="wrap_content"
                    android:text="上次更新时间:"
                    android:textSize="12sp" 
                    />

                <!-- 时间 -->
                <TextView android:id="@+id/lastTime"                 
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="12sp" 
                    />
            
            </LinearLayout>
            
        </LinearLayout>

        <!-- 箭头 -->
        <ImageView android:id="@+id/arrow"                        
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@id/headerRight"
            android:layout_centerVertical="true"
            android:layout_marginLeft="-35dp"
            android:src="@drawable/arrow" 
            />
 
        <!-- 圆形进度条 -->
        <ProgressBar android:id="@+id/progressbar"                   
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_alignLeft="@id/headerRight"
            android:layout_centerVertical="true"
            android:layout_marginLeft="-40dp"
            android:visibility="invisible" 
            />
        
    </RelativeLayout>
  
</LinearLayout>


二、Footer:包括提示语、圆形进度条

layout\refreshable_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="match_parent" 
    android:gravity="center">
        
    <LinearLayout android:id="@+id/footer" 
        android:layout_width="fill_parent"
        android:layout_height="60dp"
        android:padding="0dp"
        android:gravity="center" >

        <!-- 圆形进度条 -->
        <ProgressBar android:id="@+id/progressbar" 
            android:layout_width="28dp"
            android:layout_height="28dp"
            android:visibility="visible"
            android:gravity="center"
            android:layout_marginRight="8dp" 
            />

        <!-- 更多 -->
        <TextView android:id="@+id/hintText"   
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="更多"   
            android:textSize="14dp"
            />
        
    </LinearLayout>
    
</LinearLayout>


三、java代码,在这里,我把Header类、Footer类实现成内部类,所以最后的java代码只有一个文件,其实,是我不喜欢在一个项目里导入太多java文件,看的头昏眼花~~

RefreshableListView.java

package t.first.view;

import t.first.R;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.Adapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * @ 对外仅需设置的接口
 * 
 * -设置上拉加载是否可用,默认不可用
 * void RefreshableListView.setPullUpEnabled(boolean pullUpEnabled)
 * 
 * -设置下拉刷新是否可用,默认可用
 * void RefreshableListView.setPullDownEnabled(boolean pullDownEnabled)
 * 
 * -设置滑动到底自动加载是否可用,默认不可用
 * void RefreshableListView.setScrollLoadEnabled(boolean scrollLoadEnabled)
 * 
 * -设置下拉刷新和上拉加载的监听器
 * void RefreshableListView.setOnRefreshListener(OnRefreshListener refreshListener)
 * 
 * -设置下拉刷新的更新时间
 * void RefreshableListView.setUpdatedTime(CharSequence updatedTime)
 * 
 * -设置主动刷新,通常用于进入界面主动调用刷新
 * void RefreshableListView.doPullRefreshing(final boolean smoothScroll, final long delayMillis)
 * 
 * -设置下拉刷新中的状态结束
 * void RefreshableListView.setPullDownRefreshEnd()
 * 
 * -设置上拉加载中的状态结束
 * void RefreshableListView.setPullUpRefreshEnd()
 * 
 * -设置是否还有更多的数据,默认有
 * void RefreshableListView.setHasMoreData(boolean hasMoreData) 
 * 
 * -设置下拉刷新失败,默认成功
 * void RefreshableListView.setFailToPullDown(boolean failToPullDown)
 * 
 * -设置上拉加载失败,默认成功
 * void RefreshableListView.setFailToPullUp(boolean failToPullUp)
 * 
 */

public class RefreshableListView extends LinearLayout{

//****************************************************************************************************	
	
	/**
	 * @ 定义了刷新监听器中下拉刷新和上拉加载更多的对外接口。
	 */
    public interface OnRefreshListener 
    {
     
        //下拉松手后会被调用,刷新所做的工作
        void onPullDownToRefresh();
                
        //加载更多时会被调用或上拉时调用,加载所做的工作        
        void onPullUpToRefresh();
        
    }
	
//********************************************************************************************************
	
    /**
     * @ 主体的实现
     */  
    private RefreshableListViewHeader mHeader;  //头部
	public ListView mListView;                 //中间的ListView
	private RefreshableListViewFooter mMore;    //更多
	private RefreshableListViewFooter mFooter;  //尾部	
    private int mHeaderHeight;         //header的高度
    private int mFooterHeight;         //footer的高度	
    private LinearLayout mViewLayout;  //View的包装布局	
    private int mTouchSlop;            //移动点的保护范围值
    private boolean mPullDownEnabled = true; //下拉刷新功能是否可用,默认可用
    private boolean mPullUpEnabled = false;  //上拉加载功能是否可用 ,默认不可用
    private boolean mScrollLoadEnabled = false; //滑动到底部自动加载功能是否可用,默认不可用   
    private boolean mHasMoreData = true;        //是否还有更多的数据,默认有
    private boolean mFailToPullUp = false;    //是否上拉加载成功,默认成功
    private boolean mFailToPullDown = false;    //是否下拉刷新成功,默认成功    
    private State mPullDownState = State.NONE;  //下拉的状态
    private State mPullUpState = State.NONE;    //上拉的状态
    private boolean mInterceptEventEnable = true; //是否截断touch事件 
    private static final int SCROLL_DURATION = 150; //回滚的时间
    private SmoothScrollRunnable mSmoothScrollRunnable;//平滑滚动的动画效果的Runnable多线程
    private OnRefreshListener mRefreshListener;    //拉刷新和加载更多的监听器  
    private float mLastMotionY = -1;  //上一次移动的点,初始化为-1 
    private boolean mIsHandledTouchEvent = false; //表示是否消费了touch事件,如果是,则不调用父类的onTouchEvent方法
    private static final float OFFSET_RADIO = 2.5f;//阻尼系数,用来辅助计算头部和脚部滑动的距离
    
	
    //构造函数1
	public RefreshableListView(Context context) {
		super(context);
		// TODO 自动生成的构造函数存根
		
		init(context ,null);
	}
	
	//构造函数2
	public RefreshableListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO 自动生成的构造函数存根
		
	    init( context, attrs);
	}
	
	//初始化
	private void init(Context context, AttributeSet attrs)
	{	
		
		mHeader = new RefreshableListViewHeader(context, attrs);
		mListView = new ListView(context, attrs);
		mMore = new RefreshableListViewFooter(context, attrs);
		mFooter = new RefreshableListViewFooter(context, attrs);
		
		//移动点的保护范围值
		mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        
        //设置垂直布局
        setOrientation(LinearLayout.VERTICAL);
        
        //添加布局的中间部件ListView
        addMiddleView(context);
        
        //添加头部和尾部
        addHeaderAndFooterView();
        
        //因为需要利用到height,直接getHeight会返回0,所以要通过这个方法
        getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
			
			public void onGlobalLayout() {
				// TODO 自动生成的方法存根
				
				 //利用header和footer的高度,把header和footer隐藏起来
				 refreshLoadingViewsSize();
				 
	             getViewTreeObserver().removeGlobalOnLayoutListener(this);
				
			}
		});
        
        //设置监听器
        mListView.setOnScrollListener(new OnScrollListener() {
			
			public void onScrollStateChanged(AbsListView view, int scrollState) {
				// TODO 自动生成的方法存根
				
				if (!isPullUpRefreshing() && isScrollLoadEnabled() && hasMoreData()) 
				{
					    //SCROLL_STATE_IDLE停止滚动,SCROLL_STATE_FLING手指做了抛的动作(手指离开屏幕前,用力滑了一下)
			            if (scrollState == OnScrollListener.SCROLL_STATE_IDLE || scrollState == OnScrollListener.SCROLL_STATE_FLING) 
			            {
			                if (isReadyForPullUp()) 
			                {
			                    startUpRefresh();
			                }
			            }
			    }
			        		
			}
			
			public void onScroll(AbsListView view, int firstVisibleItem,
					int visibleItemCount, int totalItemCount) {
				// TODO 自动生成的方法存根
				
	
			}
		});
						
	}
	
	//初始化这个布局的padding,我们根据header和footer的高度来设置top padding和bottom padding,把header和footer隐藏起来
    private void refreshLoadingViewsSize() 
    {
        //得到header和footer的内容高度,它将会作为拖动刷新的一个临界值,如果拖动距离大于这个高度,然后再松开手,就会触发刷新操作
        mHeaderHeight =  mHeader.getHeight();
        mFooterHeight =  mFooter.getHeight();
           
        //这里得到Header和Footer的高度,是真实的全部长度,设置的padding的top和bottom就应该是header和footer的高度,因为header和footer是完全看不见的
        int headerHeight = mHeader.getMeasuredHeight();
        int footerHeight = mFooter.getMeasuredHeight();
        
        int pLeft = getPaddingLeft();
        int pTop = getPaddingTop();
        int pRight = getPaddingRight();
        int pBottom = getPaddingBottom();
        
        pTop = -headerHeight;
        pBottom = -footerHeight;
        
        setPadding(pLeft, pTop, pRight, pBottom);
    }
	
    //添加Header和Footer
    protected void addHeaderAndFooterView() 
    {
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
       
        addView(mHeader, 0, params);
        addView(mFooter, -1, params);
        
    }
    
    //添加布局的中间部件
    protected void addMiddleView(Context context) 
    {
        
    	int width  = ViewGroup.LayoutParams.MATCH_PARENT;
        int height = ViewGroup.LayoutParams.MATCH_PARENT;
        
        // 创建一个包装容器
        mViewLayout = new LinearLayout(context);
        mViewLayout.addView(mListView, width, height);

        // 这里把middleView的高度设置为一个很小的值,它的高度最终会在onSizeChanged()方法中设置为MATCH_PARENT
        // 这样做的原因是,如果此是它的height是MATCH_PARENT,那么footer得到的高度就是0,所以,我们先设置高度很小
        // 我们就可以得到header和footer的正常高度,当onSizeChanged后,middleView的高度又会变为正常。
        height = 10;
        addView(mViewLayout, new LinearLayout.LayoutParams(width, height));
    }
    
    
    //设置当前上拉加载更多是否可用 
    public void setPullUpEnabled(boolean pullUpEnabled)
    {
    	mPullUpEnabled = pullUpEnabled;
    }
       
    //设置当前下拉刷新是否可用
    public void setPullDownEnabled(boolean pullDownEnabled)
    {
    	mPullDownEnabled = pullDownEnabled;
    	
    }
    
    //设置当前滑动到底部自动加载是否可用
    public void setScrollLoadEnabled(boolean scrollLoadEnabled)
    {
        mScrollLoadEnabled = scrollLoadEnabled;
        
        //ListView专有
        if(scrollLoadEnabled)
        {
            mListView.addFooterView(mMore, null, false);
            mMore.setState(State.RESET);
            mMore.show(true);          
        }
        else 
        {
            mMore.show(false);
        }  	
    }
    
    //设置有没有更多的数据了
    public void setHasMoreData(boolean hasMoreData) 
    {       
    	if(hasMoreData) 
    	{
    		mHasMoreData = true;
    		
    		//ListView专有
            mMore.setState(State.RESET);   	
    	}
    	else
    	{
    		mHasMoreData = false;
            mFooter.setState(State.NO_MORE_DATA); 
           
            //ListView专有
            mMore.setState(State.NO_MORE_DATA); 
    	}
    }
    
     //设置头部的更新时间
	 public void setUpdatedTime(CharSequence updatedTime) 
	 {
	      mHeader.setUpdatedTime(updatedTime);
	 }
	  
    //设置下拉刷新失败
    public void setFailToPullDown(boolean failToPullDown)
    {
    	if(failToPullDown)
    	{
    	   mFailToPullDown = true;
    	   mHeader.setState(State.FAIL_TO_REFRESH);
    	}
    	else
    	{
    	   mFailToPullDown = false;
     	   mHeader.setState(State.RESET);
    		
    	}
    }
    
    //设置上拉加载失败
    public void setFailToPullUp(boolean failToPullUp)
    {
    	if(failToPullUp)
    	{
    	   mFailToPullUp = true;
    	   mFooter.setState(State.FAIL_TO_REFRESH);
    	
    	   //ListView专有
    	   mMore.setState(State.FAIL_TO_REFRESH);
    	}
    	else
    	{
    	   mFailToPullUp = false;
     	   mFooter.setState(State.RESET);
     	
     	   //ListView专有
     	   mMore.setState(State.RESET);
    		
    	}
    }
      
    //判断当前下拉刷新是否可用
    public boolean isPullDownEnabled() 
    {
        return mPullDownEnabled ;
    }
    
    //判断当前上拉加载是否可用
    public boolean isPullUpEnabled() 
    {
        return mPullUpEnabled ;
    }
  
    //判断当前滑动到底部自动加载是否可用
    public boolean isScrollLoadEnabled() 
    {
        return mScrollLoadEnabled;
    }
    
    //判断是否正在下拉刷新中 
    public boolean isPullDownRefreshing()
    {
        return (mPullDownState == State.REFRESHING);
    }
    
    //判断是否正在上拉加载中
    protected boolean isPullUpRefreshing() 
    {
        return (mPullUpState == State.REFRESHING);
    }
    
    //判断中间的View是否滑动到顶部
    protected boolean isReadyForPullDown() 
    {
        return isFirstItemVisible();
    }

    
    //判断中间的View是否滑动到底部 
    protected boolean isReadyForPullUp()
    {
    	return isLastItemVisible();
    }
    
    //判断是否还有更多数据 
    private boolean hasMoreData() 
    {
        if (!mHasMoreData) 
        {
            return false;
        }
        
        return true;
    }
    
    //判断是否下拉刷新失败
    private boolean isFailToPullDown()
    {
    	if(mFailToPullDown)
    	{
    		return true;
    	}
    	
    	return false;
    }
    
    //判断是否上拉加载失败
    private boolean isFailToPullUp()
    {
    	if(mFailToPullUp)
    	{
    		return true;
    	}
    	
    	return false;
    }
    
    
    //拉头部Header时调用 ,更新箭头、提示语等状态
    protected void pullHeader(float delta) 
    {
        //向上滑动头部,并且当前scrollY为0时,不滑动
        int oldScrollY = getScrollY();
        if (delta < 0 && (oldScrollY - delta) >= 0)
        {
            scrollTo(0, 0);
            return;
        }
        
        // 向下滑动头部
        scrollBy(0, -(int)delta);
        
        // 未处于刷新状态时
        int scrollY = Math.abs(getScrollY());
        if (isPullDownEnabled() && !isPullDownRefreshing()) 
        { 
            if (scrollY > mHeaderHeight) 
            {
                mPullDownState = State.RELEASE_TO_REFRESH;
            } 
            else 
            {
                mPullDownState = State.PULL_TO_REFRESH;
            }
            
            mHeader.setState(mPullDownState);
          
        }
    }
    
    //拉脚部Footer时调用,更新提示语等状态  
    protected void pullFooter(float delta) 
    {
    	//向下滑动脚部
        int oldScrollY = getScrollY();
        if (delta > 0 && (oldScrollY - delta) <= 0) 
        {
            scrollTo(0, 0);
            return;
        }
        
        //向上滑动脚部
        scrollBy(0, -(int)delta);
        
        //未处于刷新状态时
        int scrollY = Math.abs(getScrollY());
        if (isPullUpEnabled() && !isPullUpRefreshing()) 
        {
            if (scrollY > mFooterHeight) 
            {
                mPullUpState = State.RELEASE_TO_REFRESH;
            } 
            else 
            {
                mPullUpState = State.PULL_TO_REFRESH;
            }
            
            mFooter.setState(mPullUpState);
     
        }
    }
    
    //设置下拉刷新中的状态 结束
    public void setPullDownRefreshEnd() 
    {
    	if (isPullDownRefreshing()) 
    	{
            mPullDownState = State.RESET;           //重置
            
            // 回滚动有一个时间,我们在回滚完成后再设置状态为reset
            // 在将Header头部的状态设置为reset之前,我们应该禁止
            // 截断Touch事件,因为这里有一个post状态,如果有post的Runnable
            // 未被执行时,用户再一次发起下拉刷新,如果正在刷新时,这个Runnable
            // 再次被执行到,那么就会把正在刷新的状态改为reset状态,这就不符合期望
            // 延时,这是一个多线程,同时与后面的代码一起执行,不要认为是过了SCROLL_DURATION之后才执行后面的代码
            postDelayed(new Runnable() {
                public void run() {
                    
                	setInterceptTouchEventEnabled(true);
                    
                    //如果刷新成功,重置初始状态
                	if(!isFailToPullDown())
                	{
                	    mHeader.setState(State.RESET);
                	}
                	//如果刷新失败,显示“刷新失败”
                	else 
                	{
                		mHeader.setState(State.FAIL_TO_REFRESH);
                	       
                		//如果加载失败,则延时回滚
                		postDelayed(new Runnable() {
                              public void run() {
                            	 
                            	  resetHeader();
                        
                              }},SCROLL_DURATION * 3);
                		
                	}
                    
                }
            }, SCROLL_DURATION);
            
            if(!isFailToPullDown())
            {
            	resetHeader();
            }
            setInterceptTouchEventEnabled(false);
        }
    }
   
    //重置header头部的位置
    protected void resetHeader() 
    {
        final int scrollY = Math.abs(getScrollY());
        final boolean refreshing = isPullDownRefreshing();
        
        if (refreshing && scrollY <= mHeaderHeight) 
        {
            smoothScrollTo(0);
            return;
        }
        
        if (refreshing) 
        {
            smoothScrollTo(-mHeaderHeight);
        } 
        else 
        {
            smoothScrollTo(0);
        }
    }
    
    
    //设置是否禁止截断touch事件
    private void setInterceptTouchEventEnabled(boolean enabled) 
    {
        mInterceptEventEnable = enabled;
    }
    
    //判断是否截断了touch事件
    private boolean isInterceptTouchEventEnabled() 
    {
        return mInterceptEventEnable;
    }
    
    //截断触摸事件
    @Override
    public final boolean onInterceptTouchEvent(MotionEvent event) 
    {
        if (!isInterceptTouchEventEnabled()) 
        {
            return false;
        }
        
        if (!isPullUpEnabled() && !isPullDownEnabled()) 
        {
            return false;
        }
        
        final int action = event.getAction();
        
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) 
        {
            mIsHandledTouchEvent = false;
            return false;
        }
        
        if (action != MotionEvent.ACTION_DOWN && mIsHandledTouchEvent) 
        {
            return true;
        }
        
        switch (action) 
        {
        case MotionEvent.ACTION_DOWN:
            mLastMotionY = event.getY();
            mIsHandledTouchEvent = false;
            break;
            
        case MotionEvent.ACTION_MOVE:
            final float deltaY = event.getY() - mLastMotionY;
            final float absDiff = Math.abs(deltaY);
                       
            // 这里有三个条件:
            // 1,位移差应大于mTouchSlop,这是为了防止快速拖动引发刷新
            // 2,isPullDownRefreshing(),如果当前正在下拉刷新的话,是允许向上滑动,并把刷新的HeaderView挤上去
            // 3,isPullUpRefreshing(),理由与第2条相同
            // 以下方法过于繁琐是因为在Item少的时候,实现正常的上拉
            if ( absDiff > mTouchSlop || isPullDownRefreshing() || isPullUpRefreshing())   
            {
                mLastMotionY = event.getY();
                
                //表示下拉,两种情况:1、脚部正在显示中,先让脚部下拉   2、脚部没有显示出来,直接发生下拉
                if( deltaY > 0 )
                {
          	
                	 //脚部正在显示中,则先让脚部下拉
                     if ( getScrollY() > 0 && isPullUpEnabled()  && isReadyForPullUp() ) 
                     {
                          // 原理如下
                          mIsHandledTouchEvent = (Math.abs(getScrollY()) > 0 || deltaY < -0.5f);
                                                             
                     }
                     //否则,直接下拉,当第一个Item 显示出来,Header已经显示或拉下
                     else if (isPullDownEnabled() && isReadyForPullDown())  
                     {
                         // 1,Math.abs(getScrollY()) > 0:表示当前滑动的偏移量的绝对值大于0,表示当前HeaderView滑出来了或完全
                         // 不可见,存在这样一种case,当正在刷新时并且RefreshableView已经滑到顶部,向上滑动,那么我们期望的结果是
                         // 依然能向上滑动,直到HeaderView完全不可见
                         // 2,deltaY > 0.5f:表示下拉的值大于0.5f
                         mIsHandledTouchEvent = (Math.abs(getScrollY()) > 0 || deltaY > 0.5f);
                    
                    
                         // 如果截断事件,我们则仍然把这个事件交给刷新View去处理,典型的情况是让ListView/GridView将按下
                         // Child的Selector隐藏
                         if (mIsHandledTouchEvent) 
                         {
                              mListView.onTouchEvent(event);
                         }
                     
                     }                 
                }                              
                //表示上拉,两种情况:1、头部正在显示中,先让头部上拉   2、头部没有显示出来,直接发生上拉            
                else if( deltaY < 0 )
                {   
                	
                     //头部正在显示中,先让头部上拉,当 第一个Item显示出来,Header已经显示或拉下
                     if ( getScrollY() < 0 &&  isPullDownEnabled() && isReadyForPullDown())  //deltaY自己想的 deltaY>0 &&
                     {
                          // 1,Math.abs(getScrollY()) > 0:表示当前滑动的偏移量的绝对值大于0,表示当前HeaderView滑出来了或完全
                          // 不可见,存在这样一种case,当正在刷新时并且RefreshableView已经滑到顶部,向上滑动,那么我们期望的结果是
                          // 依然能向上滑动,直到HeaderView完全不可见
                          // 2,deltaY > 0.5f:表示下拉的值大于0.5f
                          mIsHandledTouchEvent = (Math.abs(getScrollY()) > 0 || deltaY > 0.5f);
                                       
                          // 如果截断事件,我们则仍然把这个事件交给刷新View去处理,典型的情况是让ListView/GridView将按下
                          // Child的Selector隐藏
                          if (mIsHandledTouchEvent) 
                          {
                               mListView.onTouchEvent(event);
                          }
                    }
                    //否则,直接上拉
                    else if (isPullUpEnabled()  && isReadyForPullUp() ) 
                    {
                          // 原理如上
                          mIsHandledTouchEvent = (Math.abs(getScrollY()) > 0 || deltaY < -0.5f);
                                                         
                    }
                
                }
            }
            break; 
            
        default:
            break;
        }
        
        return mIsHandledTouchEvent;
    }
    
    boolean isPullUp = false; //判断是否是上拉还是下拉
    //触摸事件
    @Override
    public final boolean onTouchEvent(MotionEvent ev) 
    {
        boolean handled = false;
             
        switch (ev.getAction()) 
        {
        case MotionEvent.ACTION_DOWN:
            mLastMotionY = ev.getY();
            mIsHandledTouchEvent = false;
                       
            break;
            
        case MotionEvent.ACTION_MOVE:
            final float deltaY = ev.getY() - mLastMotionY;
            mLastMotionY = ev.getY();
                      
            //表示下拉
            if( deltaY > 0 )
            {
            	//脚部正在显示中,先让脚部下拉
                if (getScrollY() > 0 && isPullUpEnabled() && isReadyForPullUp() ) 
                {
                     pullFooter(deltaY / OFFSET_RADIO);
                     handled = true;
                    
                }
                else if (isPullDownEnabled() && isReadyForPullDown())  
                {
                     pullHeader(deltaY / OFFSET_RADIO);
                     handled = true;
                
                     isPullUp = false;
                }            
                else 
                {
                     mIsHandledTouchEvent = false;
                }
            
            }            
            //表示上拉
            else if(deltaY<0)
            {
            	//头部正在显示中,先让头部上拉
                if ( getScrollY() < 0 && isPullDownEnabled() && isReadyForPullDown())  //isPullDownRefreshing() &&自己想的
                {
                    pullHeader(deltaY / OFFSET_RADIO);
                    handled = true;               
                
                } 
                else if (isPullUpEnabled() && isReadyForPullUp() ) 
                {
                    pullFooter(deltaY / OFFSET_RADIO);
                    handled = true;
                
                    isPullUp = true;
                }   
                else 
                {
                    mIsHandledTouchEvent = false;
                }
            }
        
            break;
            
        case MotionEvent.ACTION_CANCEL:
        
        case MotionEvent.ACTION_UP:
            if (mIsHandledTouchEvent) 
            {
                mIsHandledTouchEvent = false;
      
                if (!isPullUp && isReadyForPullDown()) 
                {
                    //释放,调用刷新
                    if ( isPullDownEnabled() && (mPullDownState == State.RELEASE_TO_REFRESH)) 
                    {
                        startDownRefresh();
                        handled = true;
                    }
                    resetHeader();
                } 
                else if (isPullUp && isReadyForPullUp())  
                {
                    // 加载更多
                    if ( isPullUpEnabled() && (mPullUpState == State.RELEASE_TO_REFRESH)) 
                    {
                        startUpRefresh();
                        handled = true;
                    }
                    resetFooter();
                }
            }
            break;

        default:
            break;
        }
        
        return handled;
    }
    
       
    //设置上拉加载中的状态 结束
    public void setPullUpRefreshEnd() 
    {
        if (isPullUpRefreshing()) 
        {
            mPullUpState = State.RESET;

            postDelayed(new Runnable() {
                public void run() {
                    
                	setInterceptTouchEventEnabled(true);
                    
                	//如果还有更多的数据并且加载成功,重置初始状态
                	if(hasMoreData()&&!isFailToPullUp())
                	{
                	    mFooter.setState(State.RESET);
                	}
                	//如果加载失败,不管有没有更多的数据,都显示“加载失败”
                	else if(isFailToPullUp())
                	{
                		mFooter.setState(State.FAIL_TO_REFRESH);
                	       
                		//如果加载失败,则延时回滚
                		postDelayed(new Runnable() {
                              public void run() {
                            	 
                            	  resetFooter();
                        
                              }},SCROLL_DURATION * 3);
                		
                	}
                	//如果加载成功,但是没有更多的数据了,则显示“已经到底了”
                	else if(!hasMoreData())
                	{
                		mFooter.setState(State.NO_MORE_DATA);
       
                		//如果没有更多的数据了,则延时回滚
                		postDelayed(new Runnable() {
                              public void run() {
                            	 
                            	  resetFooter();
                        
                              }},SCROLL_DURATION * 3);
                	}
                	
                	
                    //ListView专有
                	if(hasMoreData()&&!isFailToPullUp())
                	{
                        mMore.setState(State.RESET); 
                	}
                	else if(isFailToPullUp())
                	{
                	    mMore.setState(State.FAIL_TO_REFRESH);
                	}
                	else if(!hasMoreData())
                	{
                    	mMore.setState(State.NO_MORE_DATA);
                    }
                                        
                }
            }, SCROLL_DURATION);
            
            if(hasMoreData()&&!isFailToPullUp())
        	{
               resetFooter();
        	}
            setInterceptTouchEventEnabled(false);
            
        }
    }
    
    //重置footer脚部的位置
    protected void resetFooter() 
    {
    	
    	
        int scrollY = Math.abs(getScrollY());
        boolean refreshing = isPullUpRefreshing();
        
        if (refreshing && scrollY <= mFooterHeight) 
        {
            smoothScrollTo(0);
            return;
        }
        
        if (refreshing) 
        {
            smoothScrollTo(mFooterHeight);
        } 
        else
        {
            smoothScrollTo(0);
        }
    }
    
    //当整个布局的尺寸变了调用
    @Override
    protected final void onSizeChanged(int w, int h, int oldw, int oldh) 
    {
        super.onSizeChanged(w, h, oldw, oldh);
        
        // 更新整个布局的padding
        refreshLoadingViewsSize();
        
        // 更新中间View的大小
        refreshMiddleViewSize(w, h);
        
        post(new Runnable() {
            public void run() {
                requestLayout();
            }
        });
    }
    
    //计算中间的View的大小    
    protected void refreshMiddleViewSize(int width, int height) 
    {

        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mViewLayout.getLayoutParams();
        if (params.height != height) 
        {
            params.height = height;
            mViewLayout.requestLayout();
        }

    }
     
    //开始刷新,通常用于调用者主动刷新,典型的情况是进入界面,开始主动刷新,这个刷新并不是由用户拉动引起的 smoothScroll 表示是否有平滑滚动
    public void doPullRefreshing(final boolean smoothScroll, final long delayMillis)
    {
        postDelayed(new Runnable() {
            public void run() {
                
            	int newScrollValue = -mHeaderHeight;
                
            	int duration = smoothScroll ? SCROLL_DURATION : 0;
                
                startDownRefresh();
                
                smoothScrollTo(newScrollValue, duration, 0);
            }
        }, delayMillis);
    }
    
    //开始刷新,当下拉松开后被调用 
    protected void startDownRefresh() 
    {
        // 如果正在刷新
        if (isPullDownRefreshing()) 
        {
            return;
        }
        
        mPullDownState = State.REFRESHING;
        
        mHeader.setState(State.REFRESHING);
        
        if (null != mRefreshListener) 
        {
            // 因为滚动回原始位置的时间是200,我们需要等回滚完后才执行刷新回调
            postDelayed(new Runnable() {
                public void run() {
                    mRefreshListener.onPullDownToRefresh();
                }
            }, SCROLL_DURATION); 
        }
    }

    //开始加载更多,上拉松开后调用    
    protected void startUpRefresh() 
    {
        // 如果正在加载
        if (isPullUpRefreshing()) 
        {
            return;
        }
        
        mPullUpState = State.REFRESHING;
        
        mFooter.setState(State.REFRESHING);

        //ListView专有
        mMore.setState(State.REFRESHING);
      
        if (null != mRefreshListener) 
        {
            // 因为滚动回原始位置的时间是200,我们需要等回滚完后才执行加载回调
            postDelayed(new Runnable() {
                public void run() {
                    mRefreshListener.onPullUpToRefresh();
                }
            }, SCROLL_DURATION); 
        }
    }
    
    //设置刷新监听器
    public void setOnRefreshListener(OnRefreshListener refreshListener) 
    {
        mRefreshListener = refreshListener;
    }
     
    //ListView专有,判断ListView的第一个child是否完全显示出来    
    private boolean isFirstItemVisible()
    {
        final Adapter adapter = mListView.getAdapter();

        if (null == adapter || adapter.isEmpty()) 
        {
            return true;
        }

        int mostTop = (mListView.getChildCount() > 0) ? mListView.getChildAt(0).getTop() : 0;
        
        if (mostTop >= 0) 
        {
            return true;
        }

        return false;
    }

    //ListView专有,判断ListView最后一个child是否完全显示出来     
    private boolean isLastItemVisible() 
    {
        final Adapter adapter = mListView.getAdapter();

        if (null == adapter || adapter.isEmpty())
        {
            return true;
        }

        //所有Item的最后一个
        final int lastItemPosition = adapter.getCount() - 1;
        
        //屏幕上显示Item的最后一个
        final int lastVisiblePosition = mListView.getLastVisiblePosition();

    
        if (lastVisiblePosition >= lastItemPosition - 1) 
        {
            final int childIndex = lastVisiblePosition - mListView.getFirstVisiblePosition();
            
            //得到屏幕上显示的Item数目
            final int childCount = mListView.getChildCount();
     
            final int index = Math.min(childIndex, childCount - 1);
            
            final View lastVisibleChild = mListView.getChildAt(index);
            
            if (lastVisibleChild != null) 
            {
                return lastVisibleChild.getBottom() <= mListView.getBottom();
            }
        }

        return false;
    }

    //平滑滚动 1
    private void smoothScrollTo(int newScrollValue) 
    {
        smoothScrollTo(newScrollValue, SCROLL_DURATION, 0);
    }
    
    //平滑滚动2   
    private void smoothScrollTo(int newScrollValue, long duration, long delayMillis) 
    {
    	//先停止该线程
        if (null != mSmoothScrollRunnable) 
        {
            mSmoothScrollRunnable.stop();
        }
        
        //判断是否需要滑动
        int oldScrollValue = this.getScrollY();    
        boolean post = (oldScrollValue != newScrollValue);
        
        //如果需要滑动,启动线程
        if (post) 
        {
            mSmoothScrollRunnable = new SmoothScrollRunnable(oldScrollValue, newScrollValue, duration);
            
            if (delayMillis > 0) 
            {
                postDelayed(mSmoothScrollRunnable, delayMillis);
            } 
            else 
            {
                post(mSmoothScrollRunnable);
            }
        }
        
    }
    
//*********************************************************************************************  
    
    
    /**
     * @ 利用Runnable多线程实现头部和脚部平滑滚动的动画效果,更新UI界面
     */  
    final class SmoothScrollRunnable implements Runnable
    {
        
        private final DecelerateInterpolator mInterpolator; //动画效果
        private final int mScrollFromY; //开始Y
        private final int mScrollToY;//结束Y
        private final long mDuration; //滑动时间
        private boolean mContinueRunning = true;//是否继续运行
        private long mStartTime = -1;  //开始时刻
        private int mCurrentY = -1; //当前Y

        //构造方法         
        public SmoothScrollRunnable(int fromY, int toY, long duration) 
        {
            mScrollFromY = fromY;
            mScrollToY = toY;
            mDuration = duration;
            mInterpolator = new DecelerateInterpolator();
        }

        public void run() 
        {
           
        	//如果持续时间为0,则直接滑动
            if (mDuration <= 0) 
            {
                scrollTo(0, mScrollToY);
                return;
            }
            
            
            if (mStartTime == -1)
            {
                mStartTime = System.currentTimeMillis();
            }
            else 
            {
                
                final long oneSecond = 1000;    
                
                long normalizedTime = (oneSecond * (System.currentTimeMillis() - mStartTime)) / mDuration;
                
                normalizedTime = Math.max(Math.min(normalizedTime, oneSecond), 0);

                final int deltaY = Math.round((mScrollFromY - mScrollToY)* mInterpolator.getInterpolation(normalizedTime / (float) oneSecond));
                
                mCurrentY = mScrollFromY - deltaY;
                
                scrollTo(0, mCurrentY);
            }

            if (mContinueRunning && mScrollToY != mCurrentY) 
            {
                RefreshableListView.this.postDelayed(this, 16);
            }
            
        }

        public void stop()
        {
            mContinueRunning = false;
            
            removeCallbacks(this);
        }
    }
    	
//****************************************************************************************************
	
	/**
	 * @ 状态
	 */
	public enum State {
    	NONE,                        //无
    	RESET,                       //重置初始状态
    	PULL_TO_REFRESH,             //拉动可以刷新
    	RELEASE_TO_REFRESH,          //松开后刷新
    	REFRESHING,                  //刷新中(加载中)
    	NO_MORE_DATA,                //没有更多的数据了
    	FAIL_TO_REFRESH};            //加载(刷新)失败
        
    	
	
//*****************************************************************************************************
    	
	/**
	 * @ 头部类
	 */
	public class RefreshableListViewHeader extends LinearLayout{
		 
	    	
		private View mContainer;                             
		private TextView mLastTimeLabel;                     //时间标签的控件
		private TextView mLastTime;                          //上次更新时间的控件
		private ImageView mArrowImageView;                   //箭头的控件
		private ProgressBar mProgressBar;                    //进度条的控件
		private TextView mHintTextView;                      //状态提示语的控件
		private Animation mRotateUpAnim;                     //向上的动画
		private Animation mRotateDownAnim;                   //向下的动画
		private static final int ROTATE_ANIM_DURATION = 150; //旋转动画的时间
		
		private State mCurState = State.NONE;  //当前的状态
	    private State mPreState = State.NONE;  //前一个的状态
		
		
		//构造函数1
		public RefreshableListViewHeader(Context context) {
			super(context);
			// TODO 自动生成的构造函数存根
			
			init(context);
		}
		
		//构造函数2
		public RefreshableListViewHeader(Context context, AttributeSet attrs) {
			super(context, attrs);
			// TODO 自动生成的构造函数存根
			
			init(context);
		}
			
		//初始化
		private void init(Context context)
		{
			//设置布局规则
			LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
			
			//获取整个header布局
			mContainer = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.refreshable_listview_header, null);
			
			//加进这个容器里
			addView(mContainer, params);
			
			//获取控件
			mLastTimeLabel = (TextView)findViewById(R.id.lastTimeLabel);
			mLastTime = (TextView)findViewById(R.id.lastTime);
			mArrowImageView = (ImageView)findViewById(R.id.arrow);
			mHintTextView = (TextView)findViewById(R.id.hintText);
			mProgressBar = (ProgressBar)findViewById(R.id.progressbar);
			
			//设置旋转到上的动画
			mRotateUpAnim = new RotateAnimation(
					0.0f, 
					-180.0f,
					Animation.RELATIVE_TO_SELF, 
					0.5f, 
					Animation.RELATIVE_TO_SELF,
					0.5f);
			mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);
			mRotateUpAnim.setFillAfter(true);
			
			//设置旋转到下的动画
			mRotateDownAnim = new RotateAnimation(
					-180.0f, 
					0.0f,
					Animation.RELATIVE_TO_SELF, 
					0.5f, 
					Animation.RELATIVE_TO_SELF,
					0.5f);
			mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);
			mRotateDownAnim.setFillAfter(true);	
			
		}
		
		 //显示或隐藏头部布局
		 public void show(boolean show) 
		 {
		     ViewGroup.LayoutParams params = mContainer.getLayoutParams();
	         if (show) 
		     {
		         params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
		     } 
		     else 
		     {
		         params.height = 0;
		     }
		     setVisibility(show ? View.VISIBLE : View.INVISIBLE);
		     
		 }   
		 
		 //设置当前的状态
		 public void setState(State state) 
		 {
		        if (mCurState != state) 
		        {
		            mPreState = mCurState;
		            mCurState = state;
		            stateChanged(state, mPreState);
		        }
		 }
		 
		 //当设置状态改变时调用
		 public void stateChanged(State curState, State preState) 
		 {
			    mArrowImageView.setVisibility(View.VISIBLE);
		        mProgressBar.setVisibility(View.INVISIBLE);
		        
		        switch (curState) 
		        {
		        case RESET:    //重置
		            reset();
		            break;
		            
		        case RELEASE_TO_REFRESH:  //松开后刷新
		            releaseToRefresh();
		            break;
		             
		        case PULL_TO_REFRESH:   //下拉可以刷新
		            pullToRefresh();
		            break;
		            
		        case REFRESHING:    //正在刷新中
		            refreshing();
		            break;
		            
		        case FAIL_TO_REFRESH: //刷新失败
		        	failToRefresh();
		        	break;
		          		            
		        default:
		            break;
		        }
		 }
		 
		 //重置
		 public void reset() 
		 {
			 mArrowImageView.clearAnimation();
		     mHintTextView.setText("下拉可以刷新");
		        
		 }
		    
		 //下拉刷新
		 public void pullToRefresh() 
		 {
			 //如果之前是“松开后刷新”的状态
			 if (getPreState() == State.RELEASE_TO_REFRESH) 
			 {
		         mArrowImageView.clearAnimation();
		         mArrowImageView.startAnimation(mRotateDownAnim);
		     }
		        
		     mHintTextView.setText("下拉可以刷新");
		        
		 }
		    
		 //松开后刷新
		 public void releaseToRefresh() 
		 {
			 mArrowImageView.clearAnimation();
		     mArrowImageView.startAnimation(mRotateUpAnim);
		     mHintTextView.setText("松开后刷新");		        
		 }
		    
		 //刷新中 
		 public void refreshing() 
		 {
			 mArrowImageView.clearAnimation();
		     mArrowImageView.setVisibility(View.INVISIBLE);
		     mProgressBar.setVisibility(View.VISIBLE);
		     mHintTextView.setText("正在刷新中");
		 }
		 
		 //刷新失败
		 public void failToRefresh()
		 {
			 mArrowImageView.setVisibility(View.INVISIBLE);
		     mHintTextView.setText("刷新失败");
		 }
		 
		 //得到当前的状态
		 public State getState() 
		 {
		        return mCurState;
		 }
		    		 
		 //得到上一个状态
		 public State getPreState() 
		 {
		      return mPreState;
		 }
		 
		 //设置当前更新的时间
		 public void setUpdatedTime(CharSequence updatedTime) 
		 {
		      //如果是第一次更新,则隐藏前面的标题
		      mLastTimeLabel.setVisibility(TextUtils.isEmpty(updatedTime) ? View.INVISIBLE : View.VISIBLE);
		      mLastTime.setText(updatedTime);
		 }
		 
	}

//*****************************************************************************************************
	
	/**
	 * @ 脚部类
	 */
	public class RefreshableListViewFooter extends LinearLayout{

		private View mContainer; 
	    private ProgressBar mProgressBar; //进度条的控件
	    private TextView mHintView;       //提示语的控件
	    private State mCurState = State.NONE;  //当前的状态
	    private State mPreState = State.NONE;  //前一个的状态
		
		//构造函数1
	    public RefreshableListViewFooter(Context context) {
			super(context);
			// TODO 自动生成的构造函数存根
			
			init(context);
		}
		
	    //构造函数2
		public RefreshableListViewFooter(Context context, AttributeSet attrs) {
			super(context, attrs);
			// TODO 自动生成的构造函数存根
			
			init(context);
		}
		
		
		//初始化
		private void init(Context context)
		{
			//设置布局规则
			LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
			
			//获取整个header布局
			mContainer = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.refreshable_listview_footer, null);
			
			//加进这个容器里
			addView(mContainer, params);
			
			mProgressBar = (ProgressBar) findViewById(R.id.progressbar);
		    mHintView = (TextView) findViewById(R.id.hintText);
		        
		    
		}
				
		 //显示或隐藏脚部布局
		 public void show(boolean show) 
		 {

		     ViewGroup.LayoutParams params = mContainer.getLayoutParams();
		     if (show) 
		     {
		         params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
		     } 
		     else 
		     {
		         params.height = 0;
		     }
		     setVisibility(show ? View.VISIBLE : View.INVISIBLE);
		 }   
		 
		 //设置当前的状态
		 public void setState(State state) 
		 {
		        if (mCurState != state) 
		        {
		            mPreState = mCurState;
		            mCurState = state;
		            stateChanged(state, mPreState);
		        }
		 }
		 
		 //当设置状态改变时调用
		 public void stateChanged(State curState, State preState) 
		 {
			    mProgressBar.setVisibility(View.GONE);
		        mHintView.setVisibility(View.INVISIBLE);
		        
		        switch (curState) 
		        {
		        case RESET:    //重置
		            reset();
		            break;
		            
		        case RELEASE_TO_REFRESH:  //松开后刷新
		            releaseToRefresh();
		            break;
		             
		        case PULL_TO_REFRESH:   //上拉可以刷新
		            pullToRefresh();
		            break;
		            
		        case REFRESHING:    //正在加载中
		            refreshing();
		            break;
		          
		        case NO_MORE_DATA:    //没有更多的数据了
		            noMoreData();
		            break;
		            
		        case FAIL_TO_REFRESH: //加载失败
		        	failToRefresh();
		        	break;
		            
		        default:
		            break;
		        }
		 }
		 
		 //重置
		 public void reset() 
		 {			 
			 mHintView.setVisibility(View.VISIBLE);
			 mHintView.setText("更多");      
		 }
		    
		 //上拉刷新
		 public void pullToRefresh() 
		 {
			 mHintView.setVisibility(View.VISIBLE);
		     mHintView.setText("上拉可以刷新");
		        
		 }
		    
		 //松开后刷新
		 public void releaseToRefresh() 
		 {
			 mHintView.setVisibility(View.VISIBLE);
		     mHintView.setText("松开载入更多");		        
		 }
		    
		 //刷新中 
		 public void refreshing() 
		 {
			 mProgressBar.setVisibility(View.VISIBLE);
		     mHintView.setVisibility(View.VISIBLE);
		     mHintView.setText("正在加载中");
		 }
		 
		 //没有更多的数据
		 protected void noMoreData() 
		 {
		     mHintView.setVisibility(View.VISIBLE);
		     mHintView.setText("已经到底了");
		 }
		 
		 //加载失败
		 public void failToRefresh()
		 {
			 mHintView.setVisibility(View.VISIBLE);
		     mHintView.setText("加载失败");
		 }
		 
		 
		 //得到当前的状态
		 public State getState() 
		 {
		        return mCurState;
		 }
		    		 
		 //得到上一个状态
		 public State getPreState() 
		 {
		      return mPreState;
		 }
		 
	}
	
}



以上就是实现ListView的“下拉刷新”、“上拉加载”、“滑动到底自动加载”的功能,大家在做到相关类型的项目时,可以直接导入以上java和xml代码即可,下面将利用这个类演示一遍,非常简单,只需要new一个出来,设置适配器,然后利用上面提到的对外的接口即可:


layout\main.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="fill_parent"
    android:orientation="vertical" >

    <t.first.view.RefreshableListView
        android:id="@+id/g"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />
  
</LinearLayout>

MainActivity.java

package t.first;

import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.LinkedList;
import t.first.view.RefreshableListView;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ArrayAdapter;

public class MainAcitivity extends Activity {
	
	private static final int mLoadDataCount = 100; //一次显示的数目
	private int mCurIndex = 0;                     //当前显示的位置
	private boolean mIsPullDown = true;            //标记是否是下拉刷新
	
	RefreshableListView mListView;                 
	LinkedList<String> mListItems;
	private ArrayAdapter<String> mAdapter;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        //获取控件
        mListView = (RefreshableListView) findViewById(R.id.g);
        
        //设置滑动到底可自动加载
        mListView.setScrollLoadEnabled(true);
        
        mCurIndex = mLoadDataCount;              
        mListItems = new LinkedList<String>();
        mListItems.addAll(Arrays.asList(mStrings).subList(0, mCurIndex));
        
        //适配器
        mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mListItems);  
        mListView.mListView.setAdapter(mAdapter);
              
        //监听器
        mListView.setOnRefreshListener(new RefreshableListView.OnRefreshListener() {
			
			public void onPullUpToRefresh() {
				// TODO 自动生成的方法存根
				
				mIsPullDown = false;
                new GetDataTask().execute();
				
			}
			
			public void onPullDownToRefresh() {
				// TODO 自动生成的方法存根
				
				mIsPullDown = true;
                new GetDataTask().execute();
										
			}
		});
         
        mListView.doPullRefreshing(true, 500);
    }
 
    
    /**
     * @ 轻量级多线程更新UI
     *
     */
    private class GetDataTask extends AsyncTask<Void, Void, String[]> {

    	//通过多线程中的doInBackground设置延时,如果在主线程(UI线程)直接延时,UI会卡住,因为android线程是不安全的,需要在工作线程中延时更新,在工作线程中更新UI好处在于,界面不会卡
        @Override
        protected String[] doInBackground(Void... params) {
            try
            {
            	
                Thread.sleep(1500);
            } 
            catch (InterruptedException e) 
            {
            	
            }
            
            return mStrings;
        }

        //在doInBackground执行完成后执行界面更新,onPostExecute方法将被UI thread(主线程)调用
        @Override
        protected void onPostExecute(String[] result) {
            
            boolean mHasMoreData = true;
        	
            if (mIsPullDown) 
            {
                mListItems.addFirst("在这实现下拉刷新的内容");
                          
                //下拉刷新中结束
                mListView.setPullDownRefreshEnd();
                
                //更新时间
                setLastUpdateTime();
                
            } 
            else 
            {
                int start = mCurIndex;
                int end = mCurIndex + mLoadDataCount;
                
                if (end >= mStrings.length) 
                {
                    end = mStrings.length;
                }
                
                if(start == mStrings.length)
                {
                	mHasMoreData = false; 
                }
                
                for (int i = start; i < end; ++i) 
                {
                    mListItems.add(mStrings[i]);
                }
                
                mCurIndex = end;
                   
                
                //设置是否还有更多的数据
                mListView.setHasMoreData(mHasMoreData);
                
                //上拉加载中结束
                mListView.setPullUpRefreshEnd();  
                     
                
            }
            
            //更新适配器
            mAdapter.notifyDataSetChanged();  
            
            super.onPostExecute(result);
        }
    }
    
    //设置时间
    private void setLastUpdateTime() 
    {
    	SimpleDateFormat mDateFormat = new SimpleDateFormat("MM-dd HH:mm");
    	
    	String text=mDateFormat.format(new Date(System.currentTimeMillis()));
    	
    	mListView.mHeader.setUpdatedTime(text);
    }
    
    public static final String[] mStrings = {
        "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
        "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale",
        "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese",
        "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell",
        "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc",
        "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss",
        "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon",
        "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase",
        "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)",
        "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese",
        "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza",
        "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley",
        "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino",
        "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina",
        "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby",
        "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin",
        "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester",
        "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue",
        "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz",
        "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich",
        "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue",
        "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle",
        "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia",
        "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis",
        "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus",
        "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison",
        "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois",
        "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse",
        "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese",
        "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise",
        "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra",
        "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola",
        "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost",
        "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel",
        "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve",
        "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi",
        "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti",
        "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve",
        "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster",
        "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg",      
        "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes",
        "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet",
        "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe",
        "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa",
        "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois",
        "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue",
        "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington",
        "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou",
        "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue",
        "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano"
    };
}



四、所用素材:



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值