android 常规的下拉刷新

本文介绍了一种定制化的XListView实现方案,解决了在Android应用中常见的下拉刷新及加载更多功能。通过修改源码,修复了item较少时下拉刷新会误触加载更多的BUG。文中详细展示了头部和尾部视图的自定义过程,以及核心的XListView类的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  android实现刷新有多种方式,不过上个项目用的dragdown类,发现总是有bug,看到就心赛!突然回忆起以前用过的xlistview多不错的,于是就在github,找了下!花了几分钟就找到了,用了一下确实好用!不过用的时候发现了一个bug,就是item 很少的时候(就是头部和底部在同一视线时),下拉刷新也会加载更多,然后使用起来总是怪怪的!,太影响用户体验了!于是就花了点时间改了一下,加了一句判断就ok了!

好了是时候贴代码了!

先来一个头部:

package com.zhiyisoft.associations.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.zhiyisoft.associations.R;

/**
 * 
 * 下拉刷新的头部
 * 
 * @author markmjw
 * @date 2013-10-08
 */
public class XHeaderView extends LinearLayout {
	public final static int STATE_NORMAL = 0; // 普通状态
	public final static int STATE_READY = 1; // 松开可以刷新的时候那个状态
	public final static int STATE_REFRESHING = 2; // 正在刷新的状态

	private final int ROTATE_ANIM_DURATION = 180;

	private LinearLayout mContainer;

	private ImageView mArrowImageView;

	private ProgressBar mProgressBar;

	private TextView mHintTextView;

	private int mState = STATE_NORMAL;

	private Animation mRotateUpAnim;
	private Animation mRotateDownAnim;

	private boolean mIsFirst;

	public XHeaderView(Context context) {
		super(context);
		initView(context);
	}

	public XHeaderView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initView(context);
	}

	private void initView(Context context) {
		// 初始化时候,把头部高度设置问0
		LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
				LayoutParams.MATCH_PARENT, 0);
		mContainer = (LinearLayout) LayoutInflater.from(context).inflate(
				R.layout.vw_header, null);
		addView(mContainer, lp); // 布局文件生成的view放到线性布局文件里面,并放到底部
		setGravity(Gravity.BOTTOM);

		mArrowImageView = (ImageView) findViewById(R.id.header_arrow);
		mHintTextView = (TextView) findViewById(R.id.header_hint_text);
		mProgressBar = (ProgressBar) findViewById(R.id.header_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);
	}

	/**
	 * 设置状态
	 * 
	 * @param state
	 */
	public void setState(int state) {
		if (state == mState && mIsFirst) {
			mIsFirst = true;
			return;
		}

		if (state == STATE_REFRESHING) {
			// show progress
			mArrowImageView.clearAnimation();
			mArrowImageView.setVisibility(View.INVISIBLE);
			mProgressBar.setVisibility(View.VISIBLE);
		} else {
			// show arrow image
			mArrowImageView.setVisibility(View.VISIBLE);
			mProgressBar.setVisibility(View.INVISIBLE);
		}
		// 这里是典型的状态模式,所以可以用状态模式实现它
		switch (state) {
		case STATE_NORMAL:
			if (mState == STATE_READY) {
				mArrowImageView.startAnimation(mRotateDownAnim);
			}

			if (mState == STATE_REFRESHING) {
				mArrowImageView.clearAnimation();
			}

			mHintTextView.setText(R.string.header_hint_refresh_normal);
			break;

		case STATE_READY:
			if (mState != STATE_READY) {
				mArrowImageView.clearAnimation();
				mArrowImageView.startAnimation(mRotateUpAnim);
				mHintTextView.setText(R.string.header_hint_refresh_ready);
			}
			break;

		case STATE_REFRESHING:
			mHintTextView.setText(R.string.header_hint_refresh_loading);
			break;

		default:
			break;
		}

		mState = state;
	}

	/**
	 * 设置头部可见的高度
	 *
	 * @param height
	 */
	public void setVisibleHeight(int height) {
		if (height < 0)
			height = 0;
		LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContainer
				.getLayoutParams();
		lp.height = height;
		mContainer.setLayoutParams(lp);
	}

	/**
	 * 获取头部可见的高度
	 *
	 * @return
	 */
	public int getVisibleHeight() {
		return mContainer.getHeight();
	}

}
再来一个尾部嘛!

package com.zhiyisoft.associations.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.zhiyisoft.associations.R;

/**
 * 下拉刷新的尾部
 *
 * @author markmjw
 * @date 2013-10-08
 */
public class XFooterView extends LinearLayout {
	public final static int STATE_NORMAL = 0;
	public final static int STATE_READY = 1;
	public final static int STATE_LOADING = 2;

	private final int ROTATE_ANIM_DURATION = 180;

	private View mLayout;

	private View mProgressBar;

	private TextView mHintView;

	// private ImageView mHintImage;

	private Animation mRotateUpAnim;
	private Animation mRotateDownAnim;

	private int mState = STATE_NORMAL;

	public XFooterView(Context context) {
		super(context);
		initView(context);
	}

	public XFooterView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initView(context);
	}

	private void initView(Context context) {
		mLayout = LayoutInflater.from(context)
				.inflate(R.layout.vw_footer, null);
		mLayout.setLayoutParams(new LinearLayout.LayoutParams(
				LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
		addView(mLayout);

		mProgressBar = mLayout.findViewById(R.id.footer_progressbar);
		mHintView = (TextView) mLayout.findViewById(R.id.footer_hint_text);
		// mHintImage = (ImageView) mLayout.findViewById(R.id.footer_arrow);

		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);
	}

	/**
	 * Set footer view state
	 *
	 * @see #STATE_LOADING
	 * @see #STATE_NORMAL
	 * @see #STATE_READY
	 *
	 * @param state
	 */
	public void setState(int state) {
		// 当前后状态相同的时候就return;
		if (state == mState)
			return;

		if (state == STATE_LOADING) {
			// mHintImage.clearAnimation();
			// mHintImage.setVisibility(View.INVISIBLE);
			mProgressBar.setVisibility(View.VISIBLE);
			mHintView.setVisibility(View.INVISIBLE);
		} else {
			mHintView.setVisibility(View.VISIBLE);
			// mHintImage.setVisibility(View.VISIBLE);
			mProgressBar.setVisibility(View.INVISIBLE);
		}

		switch (state) {
		case STATE_NORMAL:
			mHintView.setText(R.string.footer_hint_load_normal);
			break;

		case STATE_READY:
			if (mState != STATE_READY) {
				mHintView.setText(R.string.footer_hint_load_ready);
			}
			break;

		case STATE_LOADING:
			break;
		}

		mState = state;
	}

	/**
	 * Set footer view bottom margin.
	 *
	 * @param margin
	 */
	public void setBottomMargin(int margin) {
		if (margin < 0)
			return;
		LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mLayout
				.getLayoutParams();
		lp.bottomMargin = margin;
		mLayout.setLayoutParams(lp);
	}

	/**
	 * Get footer view bottom margin.
	 *
	 * @return
	 */
	public int getBottomMargin() {
		LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mLayout
				.getLayoutParams();
		return lp.bottomMargin;
	}

	/**
	 * normal status
	 */
	public void normal() {
		mHintView.setVisibility(View.VISIBLE);
		mProgressBar.setVisibility(View.GONE);
	}

	/**
	 * loading status
	 */
	public void loading() {
		mHintView.setVisibility(View.GONE);
		mProgressBar.setVisibility(View.VISIBLE);
	}

	/**
	 * hide footer when disable pull load more
	 */
	public void hide() {
		LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mLayout
				.getLayoutParams();
		lp.height = 0;
		mLayout.setLayoutParams(lp);
	}

	/**
	 * show footer
	 */
	public void show() {
		LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mLayout
				.getLayoutParams();
		lp.height = LayoutParams.WRAP_CONTENT;
		mLayout.setLayoutParams(lp);
	}

}
呵呵,别急!现在推出了关键的的代码

package com.zhiyisoft.associations.widget;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.DecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Scroller;
import android.widget.TextView;

import com.zhiyisoft.associations.R;

/**
 * 
 *
 * @author markmjw
 * @date 2013-10-08
 */
public class XListView extends ListView implements OnScrollListener {
	// private static final String TAG = "XListView";

	private final static int SCROLL_BACK_HEADER = 0;
	private final static int SCROLL_BACK_FOOTER = 1;

	private final static int SCROLL_DURATION = 400;

	// when pull up >= 50px
	private final static int PULL_LOAD_MORE_DELTA = 50;

	// support iOS like pull
	private final static float OFFSET_RADIO = 1.8f;

	private float mLastY = -1;

	// used for scroll back
	private Scroller mScroller;
	// user's scroll listener
	private OnScrollListener mScrollListener;
	// for mScroller, scroll back from header or footer.
	private int mScrollBack;

	// the interface to trigger refresh and load more.
	private IXListViewListener mListener;

	private XHeaderView mHeader;
	// header view content, use it to calculate the Header's height. And hide it
	// when disable pull refresh.
	private RelativeLayout mHeaderContent;
	private TextView mHeaderTime;
	private int mHeaderHeight;

	private LinearLayout mFooterLayout;
	private XFooterView mFooterView;
	private boolean mIsFooterReady = false; // 底部是否准备好

	private boolean mEnablePullRefresh = true;// 能否下拉刷新
	private boolean mPullRefreshing = false;// 是否在刷新

	private boolean mEnablePullLoad = true; // 能否加载更多
	private boolean mEnableAutoLoad = false;// 能否自动加载
	private boolean mPullLoading = false; // 是否在加载更多

	// listview的总的item数量,这个主要是用于程序检测能否上拉加载更多(起了一个判断作用)
	private int mTotalItemCount;

	public XListView(Context context) {
		super(context);
		initWithContext(context);
	}

	public XListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initWithContext(context);
	}

	public XListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		initWithContext(context);
	}

	private void initWithContext(Context context) {
		mScroller = new Scroller(context, new DecelerateInterpolator());
		super.setOnScrollListener(this);

		// 初始化 header view
		mHeader = new XHeaderView(context);
		mHeaderContent = (RelativeLayout) mHeader
				.findViewById(R.id.header_content);
		mHeaderTime = (TextView) mHeader.findViewById(R.id.header_hint_time);
		addHeaderView(mHeader);

		// 初始化 footer view
		mFooterView = new XFooterView(context);
		mFooterLayout = new LinearLayout(context);
		LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
				LinearLayout.LayoutParams.MATCH_PARENT,
				LinearLayout.LayoutParams.MATCH_PARENT);
		params.gravity = Gravity.CENTER;
		mFooterLayout.addView(mFooterView, params);

		// init header height
		ViewTreeObserver observer = mHeader.getViewTreeObserver();
		if (null != observer) {
			observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
				@SuppressWarnings("deprecation")
				@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
				@Override
				public void onGlobalLayout() {
					mHeaderHeight = mHeaderContent.getHeight();
					ViewTreeObserver observer = getViewTreeObserver();

					if (null != observer) {
						if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
							observer.removeGlobalOnLayoutListener(this);
						} else {
							observer.removeOnGlobalLayoutListener(this);
						}
					}
				}
			});
		}
	}

	@Override
	public void setAdapter(ListAdapter adapter) {
		// 保证listview的尾部只能有一个尾部
		if (!mIsFooterReady) {
			mIsFooterReady = true;
			addFooterView(mFooterLayout);
		}
		super.setAdapter(adapter);
	}

	/**
	 * 设置是否能可以下拉刷新,共外部调用选择
	 *
	 * @param enable
	 */
	public void setPullRefreshEnable(boolean enable) {
		mEnablePullRefresh = enable;
		// 如果设置不可以的话,就隐藏头部
		mHeaderContent.setVisibility(enable ? View.VISIBLE : View.INVISIBLE);
	}

	/**
	 * 设置是否能可以底部加载更多,共外部调用选择
	 *
	 * @param enable
	 */
	public void setPullLoadEnable(boolean enable) {
		mEnablePullLoad = enable;

		if (!mEnablePullLoad) {
			mFooterView.setBottomMargin(0);
			mFooterView.hide();
			mFooterView.setPadding(0, 0, 0, 0);
			mFooterView.setOnClickListener(null);

		} else {
			mPullLoading = false;
			mFooterView.setPadding(0, 0, 0, 0);
			mFooterView.show();
			mFooterView.setState(XFooterView.STATE_NORMAL);
			// 设置监听器,让不仅上拉加载更多,点击也会加载更多
			mFooterView.setOnClickListener(new OnClickListener() {
				@Override
				public void onClick(View v) {
					startLoadMore();
				}
			});
		}
	}

	/**
	 * 设置当移动到listview底部的时候是否可以自动加载更多
	 *
	 * @param enable
	 */
	public void setAutoLoadEnable(boolean enable) {
		mEnableAutoLoad = enable;
	}

	/**
	 * 停止刷新, 重置header view.
	 */
	public void stopRefresh() {
		if (mPullRefreshing) {
			mPullRefreshing = false;
			resetHeaderHeight();
		}
	}

	/**
	 * 停止加载更多, 重置 footer view.
	 */
	public void stopLoadMore() {
		if (mPullLoading) {
			mPullLoading = false;
			mFooterView.setState(XFooterView.STATE_NORMAL);
		}
	}

	/**
	 * 设置最后的 refresh time
	 *
	 * @param time
	 */
	public void setRefreshTime(String time) {
		mHeaderTime.setText(time);
	}

	/**
	 * 设置监听器 listener.
	 *
	 * @param listener
	 */
	public void setXListViewListener(IXListViewListener listener) {
		mListener = listener;
	}

	/**
	 * 自动刷新.
	 */
	public void autoRefresh() {
		mHeader.setVisibleHeight(mHeaderHeight);

		if (mEnablePullRefresh && !mPullRefreshing) {
			// update the arrow image not refreshing
			if (mHeader.getVisibleHeight() > mHeaderHeight) {
				mHeader.setState(XHeaderView.STATE_READY);
			} else {
				mHeader.setState(XHeaderView.STATE_NORMAL);
			}
		}

		mPullRefreshing = true;
		mHeader.setState(XHeaderView.STATE_REFRESHING);
		refresh();
	}

	private void invokeOnScrolling() {
		if (mScrollListener instanceof OnXScrollListener) {
			OnXScrollListener listener = (OnXScrollListener) mScrollListener;
			listener.onXScrolling(this);
		}
	}

	private void updateHeaderHeight(float delta) {
		mHeader.setVisibleHeight((int) delta + mHeader.getVisibleHeight());

		if (mEnablePullRefresh && !mPullRefreshing) {
			// update the arrow image unrefreshing
			if (mHeader.getVisibleHeight() > mHeaderHeight) {
				mHeader.setState(XHeaderView.STATE_READY);
			} else {
				mHeader.setState(XHeaderView.STATE_NORMAL);
			}
		}

		// scroll to top each time
		setSelection(0);
	}

	private void resetHeaderHeight() {
		int height = mHeader.getVisibleHeight();
		if (height == 0)
			return;

		// 刷新中,但是头部没有完全显示出来,这个时候不需要干什么事情,(这种情况发现在,正在刷新的时候,然后又故意向上滑动)
		if (mPullRefreshing && height <= mHeaderHeight)
			return;

		int finalHeight = 0;
		// is refreshing, just scroll back to show all the header.
		// 当触发到刷新的临界点,然后继续下拉的时候,松开手就弹到头部的高端
		if (mPullRefreshing && height > mHeaderHeight) {
			finalHeight = mHeaderHeight;
		}

		mScrollBack = SCROLL_BACK_HEADER;
		// 这就是我们看到的弹到原位置的感觉(在这里其实可以实现一弹一弹的感觉)
		mScroller.startScroll(0, height, 0, finalHeight - height,
				SCROLL_DURATION);

		// 调用这个方法请求重绘界面 这个方法又会触发 computeScroll
		invalidate();
	}

	private void updateFooterHeight(float delta) {
		int height = mFooterView.getBottomMargin() + (int) delta;

		if (mEnablePullLoad && !mPullLoading) {
			if (height > PULL_LOAD_MORE_DELTA) {
				// height enough to invoke load more.
				mFooterView.setState(XFooterView.STATE_READY);
			} else {
				mFooterView.setState(XFooterView.STATE_NORMAL);
			}
		}

		mFooterView.setBottomMargin(height);

	}

	private void resetFooterHeight() {
		int bottomMargin = mFooterView.getBottomMargin();

		if (bottomMargin > 0) {
			mScrollBack = SCROLL_BACK_FOOTER;
			mScroller.startScroll(0, bottomMargin, 0, -bottomMargin,
					SCROLL_DURATION);
			invalidate();
		}
	}

	/**
	 * <span style="color:#ff0000;">修复了listview里面item很少的时候,下拉刷新会同时加载更多的bug</span>
	 */
	private void startLoadMore() {
		if (!mPullRefreshing) {
			mPullLoading = true;
			mFooterView.setState(XFooterView.STATE_LOADING);
			loadMore();
		}
	}

	// 核心方法
	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		if (mLastY == -1) {
			mLastY = ev.getRawY();
		}

		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			mLastY = ev.getRawY();
			break;

		case MotionEvent.ACTION_MOVE:
			final float deltaY = ev.getRawY() - mLastY;
			mLastY = ev.getRawY();

			if (getFirstVisiblePosition() == 0
					&& (mHeader.getVisibleHeight() > 0 || deltaY > 0)) {
				// the first item is showing, header has shown or pull down.
				updateHeaderHeight(deltaY / OFFSET_RADIO);
				invokeOnScrolling();
			} else if (getLastVisiblePosition() == mTotalItemCount - 1
					&& (mFooterView.getBottomMargin() > 0 || deltaY < 0)) {
				// last item, already pulled up or want to pull up.
				updateFooterHeight(-deltaY / OFFSET_RADIO);
			}
			break;

		default:
			// reset 其实本质上就是松开手后执行的操作
			mLastY = -1;
			if (getFirstVisiblePosition() == 0) {
				// invoke refresh
				if (mEnablePullRefresh
						&& mHeader.getVisibleHeight() > mHeaderHeight) {
					mPullRefreshing = true;
					mHeader.setState(XHeaderView.STATE_REFRESHING);
					refresh();
				}

				resetHeaderHeight();

			} else if (getLastVisiblePosition() == mTotalItemCount - 1) {
				// invoke load more.
				if (mEnablePullLoad
						&& mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA) {
					startLoadMore();
				}
				resetFooterHeight();
			}
			break;
		}
		return super.onTouchEvent(ev);
	}

	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			if (mScrollBack == SCROLL_BACK_HEADER) {
				mHeader.setVisibleHeight(mScroller.getCurrY());
			} else {
				mFooterView.setBottomMargin(mScroller.getCurrY());
			}

			postInvalidate();
			invokeOnScrolling();
		}

		super.computeScroll();
	}

	@Override
	public void setOnScrollListener(OnScrollListener l) {
		mScrollListener = l;
	}

	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		if (mScrollListener != null) {
			mScrollListener.onScrollStateChanged(view, scrollState);
		}

		if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
			if (mEnableAutoLoad && getLastVisiblePosition() == getCount() - 1) {
				startLoadMore();
			}
		}
	}

	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
		// send to user's listener
		mTotalItemCount = totalItemCount;
		if (mScrollListener != null) {
			mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount,
					totalItemCount);
		}
	}

	private void refresh() {
		if (mEnablePullRefresh && null != mListener) {
			mListener.onRefresh();
		}
	}

	private void loadMore() {
		if (mEnablePullLoad && null != mListener) {
			mListener.onLoadMore();
		}
	}

	/**
	 * You can listen ListView.OnScrollListener or this one. it will invoke
	 * onXScrolling when header/footer scroll back.
	 * 
	 * 自定义OnScrollListener,暂时用不到
	 */
	public interface OnXScrollListener extends OnScrollListener {
		public void onXScrolling(View view);
	}

	/**
	 * 实现这个接口就可以刷新或者加载更多
	 *
	 * 
	 */
	public interface IXListViewListener {
		public void onRefresh();

		public void onLoadMore();
	}
}
下面就是一些相关的配置文件

   <string name="header_hint_refresh_normal"></string>
    <string name="header_hint_refresh_ready">下拉刷新</string>
    <string name="header_hint_refresh_loading">刷新中</string>
    <string name="header_hint_refresh_time">上次刷新时间:</string>
    <string name="footer_hint_load_normal">加载更多</string>
    <string name="footer_hint_load_ready">加载中</string>

布局文件:

<?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="@dimen/footer_height"
    android:alpha="0.5"
    android:padding="@dimen/footer_padding" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <ProgressBar
            android:id="@+id/footer_progressbar"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_centerInParent="true"
            android:visibility="invisible" />

        <TextView
            android:id="@+id/footer_hint_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="@string/footer_hint_load_normal"
            android:textColor="#333333" />

        <ImageView
            android:id="@+id/footer_arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="30dp"
            android:layout_toLeftOf="@id/footer_hint_text"
            android:contentDescription="@null"
            android:src="@drawable/arrow_up"
            android:visibility="gone" />
    </RelativeLayout>

</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:orientation="vertical"
    android:gravity="bottom">

    <RelativeLayout
        android:id="@+id/header_content"
        android:layout_width="match_parent"
        android:layout_height="@dimen/header_height">

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

            <TextView
                android:id="@+id/header_hint_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/header_hint_refresh_normal" />

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="3dp">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@string/header_hint_refresh_time"
                    android:textSize="12sp" />

                <TextView
                    android:id="@+id/header_hint_time"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="12sp" />
            </LinearLayout>
        </LinearLayout>

        <ProgressBar
            android:id="@+id/header_progressbar"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_toLeftOf="@id/header_text_layout"
            android:layout_marginRight="30dp"
            android:layout_centerVertical="true"
            android:visibility="gone" />

        <ImageView
            android:id="@+id/header_arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toLeftOf="@id/header_text_layout"
            android:layout_marginRight="30dp"
            android:layout_centerVertical="true"
            android:contentDescription="@null"
            android:src="@drawable/arrow_down" />

    </RelativeLayout>

</LinearLayout>
差不多了哈,下拉刷新的图片就不贴了哈,自己去找一张就ok!

核心代码都贴出来了,如果要在有下拉刷新的地方都使用他的话,不可能总是引用他赛,并设置是否需要下拉刷新或者加载更多,所以还是写一个类来继承它,这样以后的下拉刷新就可以直接说了,就相当于把xlistview封装一下,不过这样就更加方便使用了哦!

下面贴一下我的封装类baselistview

package com.zhiyisoft.associations.listview.base;

/***********************************************************************
 * Module:  BaseListView.java
 * Author:  qcj qq:260964739
 * Purpose: Defines the Class BaseListView
 ***********************************************************************/

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.Toast;

import com.zhiyisoft.associations.R;
import com.zhiyisoft.associations.adapter.base.BAdapter;
import com.zhiyisoft.associations.application.Association;
import com.zhiyisoft.associations.model.base.Model;
import com.zhiyisoft.associations.widget.XListView;

/** listview的基类 ,任何listview都可以继承它,减少代码的冗余 */
public abstract class BaseListView extends XListView implements
		XListView.IXListViewListener {

	public BaseListView(Context context) {
		super(context, null);
		initSet(context);
		initXListView();
	}

	public BaseListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		mContext = context;
		initSet(context);
		initXListView();
	}

	private Context mContext;

	// /** mlist數據 */
	private List<Model> mList;
	/** activity */
	private BAdapter mAdapter;
	/**
	 * 最后可见的位置
	 */
	private int mLastVisiablPos;

	public Association mApp;

	/** 初始化设置 */
	public void initSet(Context context) {
		this.setScrollbarFadingEnabled(true);
		this.setCacheColorHint(0);
		this.setDividerHeight(2);
		mApp = (Association) context.getApplicationContext();
	}

	private void initXListView() {
		this.setPullRefreshEnable(true);
		this.setPullLoadEnable(true);
		this.setAutoLoadEnable(true);
		this.setRefreshTime(getTime());
		this.setXListViewListener(this);
	}

	@Override
	public void setAdapter(ListAdapter adapter) {
		this.mAdapter = (BAdapter) adapter;
		mAdapter.setListView(this);
		super.setAdapter(adapter);
		this.mList = ((BAdapter) adapter).getList();
		Log.i("hhh", "---------------走到这里没有?");
		this.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				// 点击加载更多
				if (view.getId() == R.id.footer_content) {
					Toast.makeText(mContext, "点击了加載更多", Toast.LENGTH_LONG)
							.show();
					return;
				}
				// 子类去实现他
				onClick(parent, view, position, id);
			}
		});
		
	}

	/** 重新每一個item的點擊事件 */
	public abstract void onClick(AdapterView<?> parent, View view,
			int position, long id);

	@Override
	public void onRefresh() {
		if (mAdapter != null) {
			mAdapter.doRefreshHeader();
		}
	}

	@Override
	public void onLoadMore() {
		if (mAdapter != null) {
			mAdapter.doRefreshFooter();
		}

	}

	private String getTime() {
		return new SimpleDateFormat("MM-dd HH:mm", Locale.CHINA)
				.format(new Date());
	}

	/**
	 * 取消下拉刷新 或者加载更多
	 */
	public void onLoad() {
		this.stopRefresh();
		this.stopLoadMore();
		this.setRefreshTime(getTime());
	}
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值