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>