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

居然隔了半个月才更新到第(二)节内容,因为最近忙于开学等事情,所以耽误了,另外,在这段时间里,我更加深入、更完美去实现ListView的这些效果,因为在参看他人开源项目的时候,发现许多朋友的ListView在实现上拉加载时候,在Item只有几个时,上拉加载会失效,这与我们要时刻能够使用上拉加载是不相符合的。


今天我将讲解实现ListView的第二种方法,继承ListView来实现。打开你的手机QQ,可以发现,QQ的好友列表是利用这个方法来实现的,另外,在这种方法中,还加入了ListView阻尼回弹效果(类似IOS),所以,接下来我要讲的,就是对这些的完美诠释。


首先还是上效果图(当然,这些效果图和第(一)节内容中介绍的实质上是一样的):






虽然在第(一)节内容中简单介绍了用这个方法的思想,不过在这里还是啰嗦几句:

通过继承ListView来实现,最根本在于addHeaderView和addFooterView。需要注意的一点是,初始状态时,HeaderView的高度是0,只有在发生下拉刷新的时候,通过响应MotionEvent.ACTION_MOVE,来不断设置HeaderView的显示高度,则完成了下拉刷新的效果。


原理图:



以下是源代码的实现:


一、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="invisible"
            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.activity.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.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.Adapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Scroller;
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 long delayMillis)
 * 
 * -设置下拉刷新中的状态结束
 * void RefreshableListView.setPullDownRefreshEnd()
 * 
 * -设置上拉加载中的状态结束
 * void RefreshableListView.setPullUpRefreshEnd()
 * 
 * -设置是否还有更多的数据,默认有
 * void RefreshableListView.setHasMoreData(boolean hasMoreData) 
 * 
 * -设置下拉刷新失败,默认成功
 * void RefreshableListView.setFailToPullDown(boolean failToPullDown)
 * 
 * -设置上拉加载失败,默认成功
 * void RefreshableListView.setFailToPullUp(boolean failToPullUp)
 * 
 * -开启过度上拉回弹效果,默认不开启
 * void RefreshableListView.setPullUpOverScroll(boolean overScroll)
 * 
 * -开启过度下拉回弹效果,默认不开启
 * void RefreshableListView.setPullDownOverScroll(boolean overScroll)
 * 
 */

public class RefreshableListView extends ListView{

//****************************************************************************************************	
	
    /**
	 * @ 定义了刷新监听器中下拉刷新和上拉加载更多的对外接口。
	 */
	public interface OnRefreshListener 
	{
	     
	     //下拉松手后会被调用,刷新所做的工作
	     void onPullDownToRefresh();
	                
	     //加载更多时会被调用或上拉时调用,加载所做的工作        
	     void onPullUpToRefresh();
	        
	} 
	    
//********************************************************************************************************	
	
	/**
     * @ 主体的实现
     */  
    private RefreshableListViewHeader mHeader;         //头部
	private RefreshableListViewFooter mFooter;         //尾部	
	private OnRefreshListener mRefreshListener;        //下拉刷新和加载更多的监听器  
	private int mHeaderHeight;                         //header的高度
	private boolean mPullDownEnabled = true;           //下拉刷新功能是否可用,默认可用
    private boolean mPullUpEnabled = false;            //上拉加载功能是否可用 ,默认不可用
    private boolean mScrollLoadEnabled = false;        //滑动到底部自动加载功能是否可用,默认不可用 
	private boolean mIsFooterReady = false;            //确定mFooter是否是脚部,保证mFooter是最后的脚部
	private boolean mIsPullUpRefreshing = false;       //判断是否在上拉加载中,默认没有在刷新
 	private boolean mIsPullDownRefreshing = false;     //判断是否在下拉刷新中,默认没有在加载
    private boolean mHasMoreData = true;               //是否还有更多的数据,默认有
    private boolean mFailToPullUp = false;             //是否上拉加载成功,默认成功
    private boolean mFailToPullDown = false;           //是否下拉刷新成功,默认成功   
    private boolean mPullDownOverScroll = false;       //判断是否开启过度下拉回弹效果,默认不开启
    private boolean mPullUpOverScroll = false;         //判断是否开启过度上拉回弹效果,默认不开启
    private float mLastY = -1;                         //上一次移动的点,初始化-1
    private int mTotalItemCount;                       //总的Item数目
    private Scroller mScroller;                        //用于回滚的实例对象,用于控制头部和脚部的滑动
 	private int mScrollBack;                           //回滚的对象,是头部还是脚部
 	private final static int SCROLLBACK_HEADER = 0;    //回滚的对象是头部
 	private final static int SCROLLBACK_FOOTER = 1;    //回滚的对象是脚部
 	private final static int SCROLL_DURATION = 100;    //回滚的时间 
	private final static int PULL_LOAD_MORE_DELTA = 20;//上拉加载更多的基准距离,用于状态的切换													
	private final static 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);
	}

	//构造函数3
	public RefreshableListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO 自动生成的构造函数存根
		init(context, attrs);
	}
	
	//初始化
	public void init(Context context, AttributeSet attrs)
	{	
		mHeader = new RefreshableListViewHeader(context, attrs);
		mFooter = new RefreshableListViewFooter(context, attrs);
		mScroller = new Scroller(context, new DecelerateInterpolator());
		
		//添加头部
        addHeaderView(mHeader);
        
        //设置默认的滑动到底自动加载
        setScrollLoadEnabled(mScrollLoadEnabled);
        
        //因为需要利用到height,直接getHeight会返回0,所以要通过这个方法
        getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        	
     			public void onGlobalLayout() {
     					
     				mHeaderHeight = mHeader.getHeight();    				
     				mHeader.setVisiableHeight(0);
     			   					
                    getViewTreeObserver().removeGlobalOnLayoutListener(this);
                    
     			}
     	});
           
        this.setOnScrollListener(new OnScrollListener() {
			
			public void onScrollStateChanged(AbsListView view, int scrollState) {
				// TODO 自动生成的方法存根
							
				if ( !mIsPullUpRefreshing && mScrollLoadEnabled && mHasMoreData) 
				{
					    //SCROLL_STATE_IDLE停止滚动,SCROLL_STATE_FLING手指做了抛的动作(手指离开屏幕前,用力滑了一下)
			            if (scrollState == OnScrollListener.SCROLL_STATE_IDLE || scrollState == OnScrollListener.SCROLL_STATE_FLING) 
			            {
			                if ( isLastItemVisible()) 
			                {
			                    startUpRefresh();
			                }
			            }
			    }
				
			}
			
			public void onScroll(AbsListView view, int firstVisibleItem,
					int visibleItemCount, int totalItemCount) {
				// TODO 自动生成的方法存根
				
				mTotalItemCount = totalItemCount;
				
			}
		});
               
	}
	
	
	//重载设置适配器
	@Override
	public void setAdapter(ListAdapter adapter) {
		
		if (mIsFooterReady == false) 
		{
			mIsFooterReady = true;
			addFooterView(mFooter);
		}
		super.setAdapter(adapter);
	}
	
	//设置下拉刷新是否可用
	public void setPullDownEnabled(boolean pullDownEnabled) 
	{
		mPullDownEnabled = pullDownEnabled;
		
		if (!mPullDownEnabled) 
		{ 
			mHeader.show(false);
		} 
		else 
		{
			mHeader.show(true);
		}
	}
	
	//设置上拉加载是否可用
	public void setPullUpEnabled(boolean pullUpEnabled) 
	{
		mPullUpEnabled = pullUpEnabled;
		
		if (!pullUpEnabled) 
		{
			mFooter.show(false);
			mFooter.setOnClickListener(null);
		} 
		else 
		{
			mFooter.show(true);
			mFooter.setState(State.RESET);
			
			mFooter.setOnClickListener(new OnClickListener() {
				
				public void onClick(View v) {
					startUpRefresh();
				}
			});
		}
	}
	
	//设置当前滑动到底部自动加载是否可用
    public void setScrollLoadEnabled(boolean scrollLoadEnabled)
    {
        mScrollLoadEnabled = scrollLoadEnabled;
        
        if(scrollLoadEnabled)
        {
            mFooter.setState(State.RESET);
            mFooter.show(true);  
            mFooter.setOnClickListener(null);
        }
        else 
        {
            mFooter.show(false);
        }  	
    }
	
    //设置有没有更多的数据了
    public void setHasMoreData(boolean hasMoreData) 
    {       
    	if(hasMoreData) 
    	{
    		mHasMoreData = true;
   
            mFooter.setState(State.RESET);   	
    	}
    	else
    	{
    		mHasMoreData = false;
            mFooter.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);
    	}
    	else
    	{
    	   mFailToPullUp = false;
     	   mFooter.setState(State.RESET);
     	   		
    	}
    }
    
    //设置过度下拉回弹效果
    public void setPullDownOverScroll(boolean overScroll)
    {
    	mPullDownOverScroll = overScroll;
    	setPullDownEnabled(overScroll);
    	mHeader.show(!overScroll);
    	
    }
    
    //设置过度上拉回弹效果
    public void setPullUpOverScroll(boolean overScroll)
    {
    	mPullUpOverScroll = overScroll;
    	setPullUpEnabled(overScroll);
    	mFooter.show(!overScroll);
    	
    }
    
    //开始刷新,通常用于调用者主动刷新,典型的情况是进入界面,开始主动刷新,这个刷新并不是由用户拉动引起的 smoothScroll 表示是否有平滑滚动
    public void doPullRefreshing(final long delayMillis)
    {
        postDelayed(new Runnable() {
            public void run() {
                            
            	mScroller.startScroll(0, 0, 0, mHeaderHeight,SCROLL_DURATION);      		
        		invalidate();   
        		
                startDownRefresh();
                
            }
        }, delayMillis);
    }
	
	//设置下拉刷新中的状态 结束
	public void setPullDownRefreshEnd() 
	{
		if (mIsPullDownRefreshing == true) 
		{
			mIsPullDownRefreshing= false;
			
			 //如果刷新成功,重置初始状态
        	if(!mFailToPullDown)
        	{
        		resetHeaderHeight();
        	    
        	}
        	//如果刷新失败,显示“刷新失败”
        	else 
        	{
        		mHeader.setState(State.FAIL_TO_REFRESH);
        	       
        		//如果加载失败,则延时回滚
        		postDelayed(new Runnable() {
                      public void run() {
                    	 
                    	  resetHeaderHeight();
                
                      }},SCROLL_DURATION * 3);
        		
        	}
		}
	}
	
	//重置头部的高度
	private void resetHeaderHeight() 
	{
		int height = mHeader.getVisiableHeight();
		
		if (height == 0) 
			return;
	
		if (mIsPullDownRefreshing && height <= mHeaderHeight) 
		{
			return;
		}
		
		int finalHeight = 0; 
		
		if (mIsPullDownRefreshing && height > mHeaderHeight) 
		{
			finalHeight = mHeaderHeight;
		}
		
		mScrollBack = SCROLLBACK_HEADER;
		mScroller.startScroll(0, height, 0, finalHeight - height,SCROLL_DURATION);
		
		invalidate();
	}
	
	
	//拉头部Header时调用 ,更新箭头、提示语等状态
	private void pullHeader(float delta) 
	{
		mHeader.setVisiableHeight((int) delta+ mHeader.getVisiableHeight());
		
		// 未处于刷新状态,更新箭头
		if (mPullDownEnabled && !mIsPullDownRefreshing) 
		{ 
			if (mHeader.getVisiableHeight() > mHeaderHeight) 
			{
				
				mHeader.setState(State.RELEASE_TO_REFRESH);
			} 
			else 
			{
				mHeader.setState(State.PULL_TO_REFRESH);
			}
		}
		
		setSelection(0); 
	}
	
	
	//开始下拉刷新,下拉松开后调用
	private void startDownRefresh() 
	{
		//如果正在刷新
        if (mIsPullDownRefreshing) 
        {
            return;
        }
        
		mIsPullDownRefreshing = true;
			
		mHeader.setState(State.REFRESHING);
			
		if (mRefreshListener != null) 
		{
			mRefreshListener.onPullDownToRefresh();
		}
	}
	
	
	//设置上拉加载的状态 结束
	public void setPullUpRefreshEnd() 
	{
		if (mIsPullUpRefreshing == true) 
		{
			mIsPullUpRefreshing = false;
			
			//如果还有更多的数据并且加载成功,重置初始状态
        	if(mHasMoreData && !mFailToPullUp)
        	{
        	    mFooter.setState(State.RESET);
        	}
        	//如果加载失败,不管有没有更多的数据,都显示“加载失败”
        	else if(mFailToPullUp)
        	{
        		mFooter.setState(State.FAIL_TO_REFRESH);
        	       
        		//如果加载失败,则延时回滚
        		postDelayed(new Runnable() {
                      public void run() {
                    	 
                    	  resetFooterHeight();
                
                      }},SCROLL_DURATION * 3);
        		
        	}
        	//如果加载成功,但是没有更多的数据了,则显示“已经到底了”
        	else if(!mHasMoreData)
        	{
        		mFooter.setState(State.NO_MORE_DATA);

        		//如果没有更多的数据了,则延时回滚
        		postDelayed(new Runnable() {
                      public void run() {
                    	 
                    	  resetFooterHeight();
                
                      }},SCROLL_DURATION * 3);
        	}
        							
		}
		
		
	}
	
	//重置脚部的高度
	private void resetFooterHeight() 
	{
		int bottomMargin = mFooter.getBottomMargin();
		
		if (bottomMargin > 0) 
		{
			mScrollBack = SCROLLBACK_FOOTER;
			mScroller.startScroll(0, bottomMargin, 0, -bottomMargin,SCROLL_DURATION);
				
			invalidate();
		}
		
	}
	
	//拉动脚部Footer时调用,更新提示语
	private void pullFooter(float delta) 
	{
		int height = mFooter.getBottomMargin() + (int) delta;
		
		if (mPullUpEnabled && !mIsPullUpRefreshing) 
		{
			if (height > PULL_LOAD_MORE_DELTA)
			{ 
				mFooter.setState(State.RELEASE_TO_REFRESH);
			} 
			else 
			{
				mFooter.setState(State.PULL_TO_REFRESH);
			}
		}
		
		mFooter.setBottomMargin(height);
	}
	
	
	//开始加载更多,上拉松开后调用
	private void startUpRefresh() 
	{
		//如果正在加载
        if (mIsPullUpRefreshing) 
        {
            return;
        }
        
		mIsPullUpRefreshing = true;
		
		mFooter.setState(State.REFRESHING);
		
		if (mRefreshListener != null) 
		{
			mRefreshListener.onPullUpToRefresh();
		}
	}
	
	
	private float mLastDownY = 0f;  //用于实现“过度拉动回弹效果”中的最后按下的位置
    private int mDistance = 0;      //用于实现“过度拉动回弹效果”中的第一个位置向下拉或者最后一个位置向上拉的 滑动距离
    private boolean mPositive = false; //用于实现“过度拉动回弹效果”中的判断mDistance是否是正数	
    
    //触摸事件
	@Override
	public boolean onTouchEvent(MotionEvent ev) 
	{
		
		if (mLastY == -1) 
		{
			mLastY = ev.getRawY();
		}

		switch (ev.getAction()) 
		{
		case MotionEvent.ACTION_DOWN:
			mLastY = ev.getRawY();
						
			if (mLastDownY == 0f && mDistance == 0) 
	        {                        
	                mLastDownY = ev.getY();  
	                return true;  
	        }  
				
			break;
					
		case MotionEvent.ACTION_MOVE:
			
			final float deltaY = ev.getRawY() - mLastY;
			mLastY = ev.getRawY();
			
			//当前在第一个Item的位置,下拉
			if (mPullDownEnabled && getFirstVisiblePosition() == 0 && (mHeader.getVisiableHeight() > 0 || deltaY > 0)) 
			{
		
				//标记1:在未开启过度下拉回弹效果之前,响应这个函数
				if(!mPullDownOverScroll)
				{
				    pullHeader(deltaY / OFFSET_RADIO);
				}
				//标记2:开启过度下拉回弹效果后,响应这个
				else
				{
					if (mLastDownY != 0f) 
			        {               
			              mDistance = (int) (mLastDownY - ev.getY());  

			              //这个判断的作用是在非顶端的部分不会有此滚动 ,第一个位置向下拉
			              if ((mDistance < 0 && getFirstVisiblePosition() == 0 && getChildAt(0).getTop() == 0)) 
			              {  
			            	   //这里是为了减少滚动的距离       
			                   mDistance /= 2; 
			                   
			                   //滚动 
			                   scrollTo(0, mDistance); 
			                  
			                   return true;  
			               }  
			        }  
			        mDistance = 0;  
					
				}
				
			} 			
			//利用“过度拉动回弹效果”实现当前在最后一个Item的位置,上拉,这样做的原因是,可以在item少的时候正常上拉
			if (mPullUpEnabled && mLastDownY != 0f) 
	        {               
	              mDistance = (int) (mLastDownY - ev.getY());  

	              //这个判断的作用是在非顶端的部分不会有此滚动 ,最后一个位置向上拉    
	              if ((mDistance > 0 && getLastVisiblePosition() == getCount() - 1)) 
	              {  	            	  	            	 
	            	   pullFooter(-deltaY / OFFSET_RADIO);	
	            	  
	            	   //这里是为了减少滚动的距离       
	                   mDistance /= 2; 
	                   
	                   //滚动 
	                   scrollTo(0, mDistance); 
	                   
	                   return true;  
	               }  
	        }  
	        mDistance = 0;  
	          	         		 
			break;
			
		case MotionEvent.ACTION_UP:
			mLastY = -1; 
			
			//在未开启“过度下拉回弹效果”,释放下拉
			if ( !mPullDownOverScroll && getFirstVisiblePosition() == 0) 
			{
		
				if ( mPullDownEnabled && mHeader.getVisiableHeight() > mHeaderHeight) 
				{
					startDownRefresh();
				}
				resetHeaderHeight();
			}
			
			//释放上拉,在开启“过度下拉回弹效果”后,释放下拉也会进入这个响应
			if (mDistance != 0) 
	        {   
				//未开启“过度上拉回弹效果”,更新提示语
				if (!mPullUpOverScroll&& getLastVisiblePosition() == mTotalItemCount - 1 ) 
				{
				     if (mPullUpEnabled && mFooter.getBottomMargin() > PULL_LOAD_MORE_DELTA) 
					 {
						  startUpRefresh();
					 }
					 else if (mPullUpEnabled && !mIsPullUpRefreshing )
					 {
						  mFooter.setState(State.RESET);
							
						  if(!mHasMoreData)
								mFooter.setState(State.NO_MORE_DATA);
					 }
					 resetFooterHeight();
				}
				
				mPositive = (mDistance >= 0);  
	            
				//启动线程,用于滚动条恢复原位
	            post(new ScrollBackRunable(mDistance,1,mPositive)); 
	              
	            mLastDownY = 0f;  
		        mDistance = 0;  
		           
	            return true;  
	        }  
	        mLastDownY = 0f;  
	        mDistance = 0;  
	        			
			break;
			
		default:
            break;
            
		}
		return super.onTouchEvent(ev);
	}

	
	//用于完成滑动效果,要不然startScroll会没有滑动效果,对应标记1
	@Override
	public void computeScroll() 
	{
		if (mScroller.computeScrollOffset()) 
		{
			//如果是头部
			if (mScrollBack == SCROLLBACK_HEADER) 
			{
				mHeader.setVisiableHeight(mScroller.getCurrY());
			} 
			//否则是脚部
			else 
			{
				mFooter.setBottomMargin(mScroller.getCurrY());
			}
			
			postInvalidate();
		}
		super.computeScroll();
	}

	//设置监听器
	public void setOnRefreshListener(OnRefreshListener refreshListener) 
	{
		mRefreshListener = refreshListener;
	}
	
	//用此方法判断ListView最后一个child是否完全显示出来,主要是为了在滑动到底自动加载中,能让用户看到显示出的“更多”标签,完善用户体验     
    private boolean isLastItemVisible() 
    {
        final Adapter adapter = getAdapter();

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

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

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

        return false;
    }
//***************************************************************************************************
    
    
       /**
        * @ 利用Runnable多线程实现“过度滑动回弹效果”中的头部和脚部平滑滚动的动画效果,更新UI界面,对应标记2
        */ 
       class ScrollBackRunable implements Runnable
       {
    	    private int mDistance = 0;      //用于第一个位置向下拉或者最后一个位置向上拉的 滑动距离
    	    private int mStep = 0;          //用于回弹时,滑动的步数
    	    private boolean mPositive = false; //判断mDistance是否是正数
 
    	    public ScrollBackRunable(int distance,int step,boolean positive)
    	    {
    	    	this.mDistance = distance;
    	    	this.mStep = step;
    	    	this.mPositive = positive;
    	    	  
    	    }
    	   
	        public void run() {
		    // TODO 自动生成的方法存根
	        	  
	        	 mDistance += mDistance > 0 ? -mStep : mStep;  
	             scrollTo(0, mDistance);  
	             
	             if ((mPositive && mDistance <= 0) || (!mPositive && mDistance >= 0)) 
	             {  
	                   scrollTo(0, 0);  
	                    	                
	                   return;  
	             }  
	            
	             //设置回弹的速率
	             mStep += 20;  
	      
	             post(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 void setVisiableHeight(int height) 
			 {
				  if (height < 0)
						height = 0;
					
				  LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContainer.getLayoutParams();
				  
				  lp.height = height;
					
				  mContainer.setLayoutParams(lp);
			}

			//返回可见的高度
			public int getVisiableHeight() 
			{
				return mContainer.getHeight();
			}
			
			 
		}

//*****************************************************************************************************
		
		/**
		 * @ 脚部类
		 */
		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;
			 }
			 
			 
			 int height;  	 
			 //设置底部的Margin
			 public void setBottomMargin(int height) 
			 {
				  if (height < 0) return ;
				  
				  this.height=height;
				  
			 }
			
			 //得到底部的Margin
			 public int getBottomMargin() 
			 {				
			      return height;
			 }
			 
		}
		
}


以上就是实现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.activity;

import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.LinkedList;

import t.first.view.RefreshableListView;
import t.first.view.RefreshableListView.OnRefreshListener;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.widget.ArrayAdapter;

public class MainActivity extends Activity {
	
	private RefreshableListView mListView;
	Handler mHandler;

	LinkedList<String> mListItems;
	private ArrayAdapter<String> mAdapter;
	
	private static final int mLoadDataCount = 100; //一次显示的数目
	private int mCurIndex = 0;                     //当前显示的位置
	
	
    /** Called when the activity is first created. */
    
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
               
		mListView = (RefreshableListView) findViewById(R.id.g);
		
		mHandler = new Handler();
		
		//设置滑动到底自动加载
		mListView.setScrollLoadEnabled(true);
		
		//设置进入界面主动刷新
		mListView.doPullRefreshing(500);
				
		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.setAdapter(mAdapter);

		mListView.setOnRefreshListener(new OnRefreshListener() {
			
			public void onPullUpToRefresh() {
				// TODO 自动生成的方法存根
								
				mHandler.postDelayed(new Runnable() {
					public void run() {
						
						boolean mHasMoreData = true;
						
						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();  
					}
				}, 2000);
				
			}
			
			public void onPullDownToRefresh() {
				// TODO 自动生成的方法存根
				
				mHandler.postDelayed(new Runnable() {
					public void run() {
										
						mListItems.addFirst("在这实现下拉刷新的内容");
						    
		                //下拉刷新中结束
		                mListView.setPullDownRefreshEnd();
		                
		                //更新时间
		                setLastUpdateTime();
		                
		                //更新适配器
				        mAdapter.notifyDataSetChanged();  
					}
				}, 2000);
				
			}
		});
		
		
		
              
    }

	//设置时间
    private void setLastUpdateTime() 
    {
    	SimpleDateFormat mDateFormat = new SimpleDateFormat("MM-dd HH:mm");
    	
    	String text=mDateFormat.format(new Date(System.currentTimeMillis()));
    	
    	mListView.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"
	                 
	    };
	   
}

四、所用素材:




最后,实现ListView“下拉刷新”、“上拉加载”、“自动加载”的两种实现方法到这里就告一段落了,希望对大家有所帮助,该开始其他要做的事情了~~
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值