listview 下拉刷新
package import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.animation.DecelerateInterpolator; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.Scroller; impor /** * @name: XSListView * * @date * * @author * * @copyright * * @desc: ScrollView nesting this XSListView. XSListView may display all, and can not wrap part of listview * */ /** * @file XListView.java * @package me.maxwin.view * @create Mar 18, 2012 6:28:41 PM * @author * @description An ListView support (a) Pull down to refresh, (b) Pull up to load more. * Implement IXListViewListener, and see stopRefresh() / stopLoadMore(). */ public class XSListView extends ListView { private float mLastY = -1; // save event y private Scroller mScroller; // used for scroll back // the interface to trigger refresh and load more. private IXListViewListener mListViewListener; // -- header view private XListViewHeader mHeaderView; // header view content, use it to calculate the Header's height. And hide it // when disable pull refresh. private RelativeLayout mHeaderViewContent; private int mHeaderViewHeight; // header view's height private boolean mEnablePullRefresh = true; private boolean mPullRefreshing = false; // is refreashing. // for mScroller, scroll back from header or footer. private int mScrollBack; private final static int SCROLLBACK_HEADER = 0; private final static int SCROLL_DURATION = 400; // scroll back duration // at bottom, trigger // load more. private final static float OFFSET_RADIO = 1.8f; // support iOS like pull // feature. /** * @param context */ public XSListView(Context context) { super(context); initWithContext(context); } public XSListView(Context context, AttributeSet attrs) { super(context, attrs); initWithContext(context); } public XSListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initWithContext(context); } private void initWithContext(Context context) { mScroller = new Scroller(context, new DecelerateInterpolator()); // XListView need the scroll event, and it will dispatch the event to // user's listener (as a proxy). // init header view mHeaderView = new XListViewHeader(context); mHeaderViewContent = (RelativeLayout) mHeaderView.findViewById(R.id.xlistview_header_content); addHeaderView(mHeaderView); // init header height mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { mHeaderViewHeight = mHeaderViewContent.getHeight(); getViewTreeObserver().removeGlobalOnLayoutListener(this); } }); } /** * enable or disable pull down refresh feature. * * @param enable */ public void setPullRefreshEnable(boolean enable) { mEnablePullRefresh = enable; if (!mEnablePullRefresh) { // disable, hide the content mHeaderViewContent.setVisibility(View.INVISIBLE); } else { mHeaderViewContent.setVisibility(View.VISIBLE); } } /** * stop refresh, reset header view. */ public void stopRefresh() { if (mPullRefreshing == true) { mPullRefreshing = false; resetHeaderHeight(); } } private void updateHeaderHeight(float delta) { mHeaderView.setVisiableHeight((int) delta + mHeaderView.getVisiableHeight()); if (mEnablePullRefresh && !mPullRefreshing) { // 未处于刷新状态,更新箭头 if (mHeaderView.getVisiableHeight() > mHeaderViewHeight) { mHeaderView.setState(XListViewHeader.STATE_READY, mListViewListener.lastUpdateTime()); } else { mHeaderView.setState(XListViewHeader.STATE_NORMAL, mListViewListener.lastUpdateTime()); } } setSelection(0); // scroll to top each time } /** * reset header view's height. */ private void resetHeaderHeight() { int height = mHeaderView.getVisiableHeight(); if (height == 0) // not visible. return; // refreshing and header isn't shown fully. do nothing. if (mPullRefreshing && height <= mHeaderViewHeight) { return; } int finalHeight = 0; // default: scroll back to dismiss header. // is refreshing, just scroll back to show all the header. if (mPullRefreshing && height > mHeaderViewHeight) finalHeight = mHeaderViewHeight; mScrollBack = SCROLLBACK_HEADER; mScroller.startScroll(0, height, 0, finalHeight - height, SCROLL_DURATION); // trigger computeScroll invalidate(); } @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 && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) { // the first item is showing, header has shown or pull down. updateHeaderHeight(deltaY / OFFSET_RADIO); } break; default: mLastY = -1; // reset if (getFirstVisiblePosition() == 0) { // invoke refresh if (mEnablePullRefresh && mHeaderView.getVisiableHeight() > mHeaderViewHeight) { mPullRefreshing = true; mHeaderView.setState(XListViewHeader.STATE_REFRESHING,""); if (mListViewListener != null) { mListViewListener.onRefresh(); } } resetHeaderHeight(); } break; } return super.onTouchEvent(ev); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { if (mScrollBack == SCROLLBACK_HEADER) mHeaderView.setVisiableHeight(mScroller.getCurrY()); postInvalidate(); } super.computeScroll(); } public void setXListViewListener(IXListViewListener l) { mListViewListener = l; } /** * implements this interface to get refresh/load more event. */ public interface IXListViewListener { public void onRefresh(); public String lastUpdateTime(); } }
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="wrap_content" 5 android:gravity="bottom" > 6 7 <RelativeLayout 8 android:id="@+id/xlistview_header_content" 9 android:layout_width="fill_parent" 10 android:layout_height="50dp" > 11 12 <LinearLayout 13 android:id="@+id/xlistview_header_text" 14 android:layout_width="wrap_content" 15 android:layout_height="wrap_content" 16 android:layout_centerInParent="true" 17 android:gravity="center" 18 android:orientation="vertical" > 19 20 <TextView 21 android:id="@+id/xlistview_header_hint_textview" 22 android:layout_width="wrap_content" 23 android:layout_height="wrap_content" 24 android:text="@string/xlistview_header_hint_normal" 25 android:textSize="13sp" /> 26 27 <TextView 28 android:id="@+id/xlistview_header_time" 29 android:layout_width="wrap_content" 30 android:layout_height="wrap_content" 31 android:text="更新时间:" 32 android:textSize="12sp" /> 33 </LinearLayout> 34 35 <ImageView 36 android:id="@+id/xlistview_header_arrow" 37 android:layout_width="wrap_content" 38 android:layout_height="wrap_content" 39 android:layout_alignLeft="@id/xlistview_header_text" 40 android:layout_centerVertical="true" 41 android:layout_marginLeft="-35dp" 42 android:contentDescription="@string/app_name" 43 android:src="@drawable/xlistview_arrow" /> 44 45 <ProgressBar 46 android:id="@+id/xlistview_header_progressbar" 47 android:layout_width="30dp" 48 android:layout_height="30dp" 49 android:layout_alignLeft="@id/xlistview_header_text" 50 android:layout_centerVertical="true" 51 android:layout_marginLeft="-40dp" 52 android:visibility="invisible" /> 53 </RelativeLayout> 54 55 </LinearLayout>