ListView下拉刷新上拉加载更多实现

时间:2023-03-08 23:37:09
ListView下拉刷新上拉加载更多实现

这篇文章将带大家了解listview下拉刷新和上拉加载更多的实现过程,先看效果(注:图片中listview中的阴影可以加上属性android:fadingEdge="none"去掉):

1

ListView下拉刷新上拉加载更多实现

2

ListView下拉刷新上拉加载更多实现

3

ListView下拉刷新上拉加载更多实现

4

ListView下拉刷新上拉加载更多实现

5

ListView下拉刷新上拉加载更多实现

接下来再看一下工程文件;

ListView下拉刷新上拉加载更多实现

首先,实现这种效果需要重写ListView控件,工程中的RefreshListView即继承了ListView,并实现了OnScrollListener接口,头部和脚部和主界面其实是连在一起的,只是在RefreshListView中没让它显示出来,只有当特定条件比如下拉上拉时才会显示,这些动作执行完毕后又会重新隐藏,先来看一下RefreshListView代码:

/**
 * @author baiyuliang
 */
public class RefreshListView extends ListView implements OnScrollListener {

	private int downY;		// 按下时y轴的偏移量
	private View headerView;		// 头布局
	private int headerViewHeight;	// 头布局的高度
	private int firstVisibleItemPosition;		// 滚动时界面显示在顶部的item的position
	private DisplayMode currentState = DisplayMode.Pull_Down;		// 头布局当前的状态, 缺省值为下拉状态
	private Animation upAnim,downAnim,loadAnim;		// 向上旋转的动画,向下旋转的动画,刷新数据时load动画
	private ImageView ivArrow;		// 头布局的箭头
	private TextView tvState;		// 头布局刷新状态
	private ImageView loading_img_header,loading_img_footer;	// 头布局和脚布局的进度条
	private TextView tvLastUpdateTime;	// 头布局的最后刷新时间
	private OnRefreshListener mOnRefreshListener;
	private boolean isScroll2Bottom = false;	// 是否滚动到底部
	private View footerView;		// 脚布局
	private int footerViewHeight;	// 脚布局的高度
	private boolean isLoadMoving = false;	// 是否正在加载更多中

	public RefreshListView(Context context, AttributeSet attrs) {
		super(context, attrs);

		initHeader();
		initFooter();
		this.setOnScrollListener(this);
	}

	/**
	 * 初始化脚布局
	 */
	private void initFooter() {

		footerView = LayoutInflater.from(getContext()).inflate(R.layout.activity_listview_refresh_footer, null);

		loading_img_footer=(ImageView) footerView.findViewById(R.id.pb_listview_footer_progress);

		measureView(footerView);		// 测量一下脚布局的高度

		footerViewHeight = footerView.getMeasuredHeight();

		footerView.setPadding(0, -footerViewHeight, 0, 0);		// 隐藏脚布局

		this.addFooterView(footerView);
	}

	/**
	 * 初始化头布局
	 */
	private void initHeader() {
		//头部
		headerView = LayoutInflater.from(getContext()).inflate(R.layout.activity_listview_refresh_header, null);

		ivArrow = (ImageView) headerView.findViewById(R.id.iv_listview_header_down_arrow);
		loading_img_header = (ImageView) headerView.findViewById(R.id.pb_listview_header_progress);
		tvState = (TextView) headerView.findViewById(R.id.tv_listview_header_state);
		tvLastUpdateTime = (TextView) headerView.findViewById(R.id.tv_listview_header_last_update_time);

		ivArrow.setMinimumWidth(50);
		tvLastUpdateTime.setText("上次刷新时间: " + getLastUpdateTime());

		measureView(headerView);
		headerViewHeight = headerView.getMeasuredHeight();

		Log.i("RefreshListView", "头布局的高度: " + headerViewHeight);

		headerView.setPadding(0, -headerViewHeight, 0, 0);  // 隐藏头布局

		this.addHeaderView(headerView);

		initAnimation();
	}

	/**
	 * 获得最后刷新的时间
	 * @return
	 */
	private String getLastUpdateTime() {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return sdf.format(new Date());
	}

	/**
	 * 初始下拉刷新时箭头动画
	 */
	private void initAnimation() {
		//向上
		upAnim = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
		upAnim.setDuration(500);
		upAnim.setFillAfter(true);
		//向下
		downAnim = new RotateAnimation(-180, -360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
		downAnim.setDuration(500);
		downAnim.setFillAfter(true);
		//刷新数据时load动画
		loadAnim=new RotateAnimation(0, 359, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
		loadAnim.setDuration(2000);
		loadAnim.setInterpolator(new LinearInterpolator());
		loadAnim.setFillAfter(true);
		loadAnim.setRepeatCount(-1);
	}

	/**
	 * 测量给定的View的宽和高, 测量之后, 可以得到view的宽和高
	 * @param child
	 */
	private void measureView(View child) {
        ViewGroup.LayoutParams lp = child.getLayoutParams();
        if (lp == null) {
        	lp = new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
        }

        int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0, lp.width);

        int lpHeight = lp.height;
        int childHeightSpec;
        if (lpHeight > 0) {
            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
        } else {
            childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        }
        child.measure(childWidthSpec, childHeightSpec);
    }

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			downY = (int) ev.getY();
			break;
		case MotionEvent.ACTION_MOVE:

			if(currentState == DisplayMode.Refreshing) {
				// 当前的状态是正在刷新中, 不执行下拉操作
				break;
			}

			int moveY = (int) ev.getY();	// 移动中的y轴的偏移量

			int diffY = moveY - downY;

			int paddingTop = -headerViewHeight + (diffY / 2);

			if(firstVisibleItemPosition == 0&& paddingTop > -headerViewHeight) {
				/**
				 * paddingTop > 0   完全显示
				 * currentState == DisplayMode.Pull_Down 当是在下拉状态时
				 */
				if(paddingTop > 0&& currentState == DisplayMode.Pull_Down) {		// 完全显示, 进入到刷新状态
					Log.i("RefreshListView", "松开刷新");
					currentState = DisplayMode.Release_Refresh;		// 把当前的状态改为松开刷新
					refreshHeaderViewState();
				} else if(paddingTop < 0&& currentState == DisplayMode.Release_Refresh) {		// 没有完全显示, 进入到下拉状态
					Log.i("RefreshListView", "下拉刷新");
					currentState = DisplayMode.Pull_Down;
					refreshHeaderViewState();
				}

				headerView.setPadding(0, paddingTop, 0, 0);
				return true;
			}
			break;
		case MotionEvent.ACTION_UP:
			downY = -1;

			if(currentState == DisplayMode.Pull_Down) {		// 松开时, 当前显示的状态为下拉状态, 执行隐藏headerView的操作

				headerView.setPadding(0, -headerViewHeight, 0, 0);
			} else if(currentState == DisplayMode.Release_Refresh) {	// 松开时, 当前显示的状态为松开刷新状态, 执行刷新的操作
				headerView.setPadding(0, 0, 0, 0);
				currentState = DisplayMode.Refreshing;
				refreshHeaderViewState();

				if(mOnRefreshListener != null) {
					mOnRefreshListener.onRefresh();
				}
			}

			break;

		default:
			break;
		}
		return super.onTouchEvent(ev);
	}

	/**
	 * 当刷新任务执行完毕时, 回调此方法, 去刷新界面
	 */
	public void onRefreshFinish() {
		if(isLoadMoving) {	// 隐藏脚布局
			loading_img_footer.clearAnimation();
			isLoadMoving = false;
			isScroll2Bottom = false;
			footerView.setPadding(0, -footerViewHeight, 0, 0);
		} else {	// 隐藏头布局
			headerView.setPadding(0, -headerViewHeight, 0, 0);
			loading_img_header.clearAnimation();
			loading_img_header.setVisibility(View.GONE);
			ivArrow.setVisibility(View.VISIBLE);
			tvState.setText("下拉刷新");
			tvLastUpdateTime.setText("上次刷新时间: " + getLastUpdateTime());
			currentState = DisplayMode.Pull_Down;
		}
	}

	/**
	 * 刷新头布局的状态
	 */
	private void refreshHeaderViewState() {
		if(currentState == DisplayMode.Pull_Down) {	// 当前进入下拉状态
			ivArrow.startAnimation(downAnim);
			tvState.setText("下拉刷新");
		} else if(currentState == DisplayMode.Release_Refresh) { //当前进入松开刷新状态
			ivArrow.startAnimation(upAnim);
			tvState.setText("释放立即刷新");
		} else if(currentState == DisplayMode.Refreshing) {  //当前进入正在刷新中
			ivArrow.clearAnimation();
			ivArrow.setVisibility(View.GONE);
			loading_img_header.setVisibility(View.VISIBLE);
			loading_img_header.startAnimation(loadAnim);
			tvState.setText("正在获取新内容");
		}
	}

	/**
	 * 刷新头布局的状态
	 * 当ListView滚动状态改变时回调
	 * SCROLL_STATE_IDLE		// 当ListView滚动停止时
	 * SCROLL_STATE_TOUCH_SCROLL // 当ListView触摸滚动时
	 * SCROLL_STATE_FLING		// 快速的滚动(手指快速的触摸移动)
	 */
	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {

		if(scrollState == OnScrollListener.SCROLL_STATE_IDLE || scrollState == OnScrollListener.SCROLL_STATE_FLING) {
			if(isScroll2Bottom && !isLoadMoving) {		// 滚动到底部
				// 加载更多
				loading_img_footer.startAnimation(loadAnim);
				footerView.setPadding(0, 0, 0, 0);
				this.setSelection(this.getCount());		// 滚动到ListView的底部
				isLoadMoving = true;

				if(mOnRefreshListener != null) {
					mOnRefreshListener.onLoadMoring();
				}
			}
		}
	}

	/**
	 * 当ListView滚动时触发
	 * firstVisibleItem 屏幕上显示的第一个Item的position
	 * visibleItemCount 当前屏幕显示的总个数
	 * totalItemCount   ListView的总条数
	 */
	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {
		firstVisibleItemPosition = firstVisibleItem;

		Log.i("RefreshListView", "onScroll: " + firstVisibleItem + ", " + visibleItemCount + ", " + totalItemCount);

		if((firstVisibleItem + visibleItemCount) >= totalItemCount&& totalItemCount > 0) {
			Log.i("RefreshListView", "加载更多");
			isScroll2Bottom = true;
		} else {
			isScroll2Bottom = false;
		}
	}

	/**
	 * @author andong
	 * 下拉头部的几种显示状态
	 */
	public enum DisplayMode {
		Pull_Down, // 下拉刷新的状态
		Release_Refresh, // 松开刷新的状态
		Refreshing	// 正在刷新中的状态
	}

	/**
	 * 设置刷新的监听事件
	 * @param listener
	 */
	public void setOnRefreshListener(OnRefreshListener listener) {
		this.mOnRefreshListener = listener;
	}
}

代码看上去比较复杂,大家可以慢慢研究,主要就是设置监听了几个动作,触摸屏幕,向下滑动,离开屏幕,下上滑动等等,根据不同动作执行相应操作,比如下拉时,头部布局便会显示出来,而当头部拉伸超过一定尺度后,箭头会执行一个动画,又指向下变为指向上,头部布局中的Textview空间内容也会发生相应变化,当松开手指时,又会出现一个动画如上图标记额地方,其实是一张图片,设置了旋转动画而已,而上拉加载更多是同样的道理,而且在动作执行完毕时,仍会有一个监听,主要是销毁动画和隐藏头部或脚部布局。

图中listview的效果是需要写一个自定义的适配器,这个我想大家都明白,也都会想到的吧,就不用我多说了,看代码:

/**
 * @author baiyuliang
 */
public class MyAdapter extends BaseAdapter {

    private Activity activity;
    private ArrayList<HashMap<String, String>> listDate;
    private static LayoutInflater inflater=null;
    public MyAdapter(Activity a, ArrayList<HashMap<String, String>> d) {
        activity = a;
        listDate=d;
        inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    public int getCount() {
        return listDate.size();
    }

    public Object getItem(int position) {
        return position;
    }

    public long getItemId(int position) {
        return position;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        View vi=convertView;
        if(convertView==null)
            vi = inflater.inflate(R.layout.list_item, null);

		TextView username = (TextView) vi.findViewById(R.id.username); // 用户头像
		TextView creatdate=(TextView) vi.findViewById(R.id.creatdate);//时间
		TextView mood=(TextView) vi.findViewById(R.id.mood);//心情内容

		HashMap<String, String> map = new HashMap<String, String>();
		map = listDate.get(position);

		//设置ListView的相关控件
        username.setText(map.get("username"));
        creatdate.setText(map.get("creatdate"));
        mood.setText(map.get("mood"));

        return vi;
    }

}

接下来我们看一下,主界面main.xml布局代码:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <TextView
         android:id="@+id/title"
        android:layout_width="fill_parent"
        android:layout_height="45dip"
        android:text="下拉刷新上拉加载"
        android:textColor="#FFFFFF"
        android:textSize="20sp"
        android:gravity="center"
        android:background="@drawable/tab_footer_bg"
        />

    <com.baiyuliang.pullrefresh.RefreshListView
        android:id="@+id/refresh_listview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_below="@+id/title"/>

</RelativeLayout>

很简单,就是一个标题栏和我们重写的ListView,listview中每个条目的布局list_item布局如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#FFFFFF"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="50dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginTop="10dp" >

        <RelativeLayout
            android:id="@+id/headimg"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_centerVertical="true" >

            <ImageView
                android:id="@+id/head_img"
                android:layout_width="40dp"
                android:layout_height="40dp"
                android:layout_centerVertical="true"
                android:src="@drawable/head_img"/>
        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/name_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="10dp"
            android:layout_toRightOf="@+id/headimg" >

            <TextView
                android:id="@+id/username"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="baiyuliang"
                android:textColor="#00008B"
                android:textSize="20sp" />

            <TextView
                android:id="@+id/creatdate"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignLeft="@+id/username"
                android:layout_below="@+id/username"
                android:layout_marginLeft="2dp"
                android:text="今天12:00"
                android:textSize="12sp" />
        </RelativeLayout>
    </RelativeLayout>

    <TextView
        android:id="@+id/mood"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginTop="3dp"
        android:text="今天心情不错,出来冒个泡!"
        android:textColor="#000000"
        android:textSize="15sp" />

    <ImageView
        android:id="@+id/mood_img"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:visibility="gone"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginTop="5dp" />

    <RelativeLayout
        android:id="@+id/phonetype"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="5dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginTop="10dp" >

        <ImageView
            android:id="@+id/phone_type"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="2dp"
            android:src="@drawable/phone_type" />

        <TextView
            android:id="@+id/phone_type_hanzi"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@+id/phone_type"
            android:text="Android" />

        <ImageView
            android:id="@+id/zan_img"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="3dp"
            android:layout_toLeftOf="@+id/zan_hanzi"
            android:src="@drawable/qz_detail_icon_praise" />

        <TextView
            android:id="@+id/zan_hanzi"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="2dp"
            android:layout_marginRight="10dp"
            android:layout_toLeftOf="@+id/pinglun_img"
            android:text="赞"
            android:textSize="12sp" />

        <ImageView
            android:id="@+id/pinglun_img"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="3dp"
            android:layout_toLeftOf="@+id/pinglun_hanzi"
            android:src="@drawable/qz_detail_icon_comment" />

        <TextView
            android:id="@+id/pinglun_hanzi"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:text="评论"
            android:textSize="12sp" />
    </RelativeLayout>

</LinearLayout>

再看MainActivity:

/**
 * @author baiyuliang
 */
public class MainActivity extends Activity {

	private MyAdapter mAdapter;
	private ArrayList<HashMap<String, String>> listItem;//生成动态数组,加入数据
	private RefreshListView mRefreshListView;
	private HashMap<String, String> map;
    @Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		initView();
		initData();
	}

	public void initView() {

        mRefreshListView = (RefreshListView)findViewById(R.id.refresh_listview);
		listItem = new ArrayList<HashMap<String, String>>();

	}

	private void initData() {
		for (int i = 0; i < 10; i++) {
			map=new HashMap<String, String>();
			map.put("username", "baiyuliang"+(i+1));
			map.put("mood", "今天心情不错哈!!!");
			map.put("creatdate", new SimpleDateFormat("HH:mm:ss").format(new Date()));
			listItem.add(map);
		}

        mAdapter = new MyAdapter(this, listItem);
        mRefreshListView.setAdapter(mAdapter);

        mRefreshListView.setOnRefreshListener(new OnRefreshListener() {
			@Override
			public void onRefresh() {
				// 异步查询数据
				new AsyncTask<Void, Void, Void>(){
					@Override
					protected Void doInBackground(Void... params) {
						SystemClock.sleep(1000);
						map=new HashMap<String, String>();
						map.put("username", "new");
						map.put("mood", "我是被下拉刷新出来的");
						map.put("creatdate", new SimpleDateFormat("HH:mm:ss").format(new Date()));
						listItem.add(0,map);
						return null;
					}
					protected void onPostExecute(Void result) {
						mAdapter.notifyDataSetChanged();
						// 隐藏头布局
						mRefreshListView.onRefreshFinish();
					}
				}.execute(new Void[]{});
			}

			@Override
			public void onLoadMoring() {
				new AsyncTask<Void, Void, Void>() {
					@Override
					protected Void doInBackground(Void... params) {
						SystemClock.sleep(2000);
						for (int i = 0; i < 10; i++) {
							map=new HashMap<String, String>();
							map.put("username", "baiyuliang");
							map.put("mood", "上拉刷新出来的数据"+(i+1));
							map.put("creatdate", new SimpleDateFormat("HH:mm:ss").format(new Date()));
							listItem.add(map);
						}
						return null;
					}

					@Override
					protected void onPostExecute(Void result) {
						super.onPostExecute(result);
						mAdapter.notifyDataSetChanged();
						mRefreshListView.onRefreshFinish();
					}

				}.execute(new Void[]{});
			}
		});
	}

}

这个跟普通的listView适配一样,我想大家都知道该怎么做,主要就是我们重写的RefreshListView中包含了两个监听,下拉刷新和上拉加载需要在这里实现,对listview的更新我们需用到notifyDataSetChanged()方法,这个也不需要我多说,我想大家都应该知道,最后我们看一下,头部布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >

    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical|center_horizontal"
        android:padding="5dp">

        <ImageView
            android:id="@+id/iv_listview_header_down_arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:src="@drawable/common_listview_headview_red_arrow" />

        <ImageView
            android:id="@+id/pb_listview_header_progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:src="@drawable/refresh_loading"
            android:visibility="gone" />

    </FrameLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical|center_horizontal"
        android:gravity="center_horizontal"
        android:orientation="vertical"
        android:padding="5dp">

        <TextView
            android:id="@+id/tv_listview_header_state"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="下拉刷新"
            android:textColor="@android:color/darker_gray"
            android:textSize="12sp" />

        <TextView
            android:id="@+id/tv_listview_header_last_update_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dip"
            android:text="最后刷新时间: 1990-09-09 09:00:00"
            android:textColor="@android:color/darker_gray"
            android:textSize="12sp" />
    </LinearLayout>

</LinearLayout>

和脚部布局:

<?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:background="#FFFFFF"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:padding="10dp" >

        <ImageView
            android:id="@+id/pb_listview_footer_progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:src="@drawable/refresh_loading"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dip"
            android:text="加载更多..."
            android:textColor="@android:color/darker_gray"
            android:textSize="12sp" />
    </LinearLayout>

</LinearLayout>

其实就这么多,是不是很简单,主要就是我们重写的ListView控件,如果看不明白,大家可以在下面下载我的源码研究:

http://download.csdn.net/detail/baiyuliang2013/7000317