XListView 配置文件 需导入项目 类名:XListView



/**
 * @file XListView.java
 * @package me.maxwin.view
 * @create Mar 18, 2012 6:28:41 PM
 * @author Maxwin
 * @description An ListView support (a) Pull down to refresh, (b) Pull up to load more.
 *   Implement IXListViewListener, and see stopRefresh() / stopLoadMore().
 */
package org.com.cctest.view;


import com.example.xlistviewtest.R;

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.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Scroller;
import android.widget.TextView;

public class XListView extends ListView implements OnScrollListener {

 private float mLastY = -1; // save event y
 private Scroller mScroller; // used for scroll back
 private OnScrollListener mScrollListener; // user's scroll listener

 // 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 TextView mHeaderTimeView;
 private int mHeaderViewHeight; // header view's height
 private boolean mEnablePullRefresh = true;
 private boolean mPullRefreshing = false; // is refreashing.

 // -- footer view
 private XListViewFooter mFooterView;
 private boolean mEnablePullLoad;
 private boolean mPullLoading;
 private boolean mIsFooterReady = false;
 
 // total list items, used to detect is at the bottom of listview.
 private int mTotalItemCount;

 // for mScroller, scroll back from header or footer.
 private int mScrollBack;
 private final static int SCROLLBACK_HEADER = 0;
 private final static int SCROLLBACK_FOOTER = 1;

 private final static int SCROLL_DURATION = 400; // scroll back duration
 private final static int PULL_LOAD_MORE_DELTA = 50; // when pull up >= 50px
              // at bottom, trigger
              // load more.
 private final static float OFFSET_RADIO = 1.8f; // support iOS like pull
             // feature.

 /**
  * @param context
  */
 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());
  // XListView need the scroll event, and it will dispatch the event to
  // user's listener (as a proxy).
  super.setOnScrollListener(this);

  // init header view
  mHeaderView = new XListViewHeader(context);
  mHeaderViewContent = (RelativeLayout) mHeaderView
    .findViewById(R.id.xlistview_header_content);
  mHeaderTimeView = (TextView) mHeaderView
    .findViewById(R.id.xlistview_header_time);
  addHeaderView(mHeaderView);

  // init footer view
  mFooterView = new XListViewFooter(context);

  // init header height
  mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(
    new OnGlobalLayoutListener() {
     @Override
     public void onGlobalLayout() {
      mHeaderViewHeight = mHeaderViewContent.getHeight();
      getViewTreeObserver()
        .removeGlobalOnLayoutListener(this);
     }
    });
 }

 @Override
 public void setAdapter(ListAdapter adapter) {
  // make sure XListViewFooter is the last footer view, and only add once.
  if (mIsFooterReady == false) {
   mIsFooterReady = true;
   addFooterView(mFooterView);
  }
  super.setAdapter(adapter);
 }

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

 /**
  * enable or disable pull up load more feature.
  *
  * @param enable
  */
 public void setPullLoadEnable(boolean enable) {
  mEnablePullLoad = enable;
  if (!mEnablePullLoad) {
   mFooterView.hide();
   mFooterView.setOnClickListener(null);
  } else {
   mPullLoading = false;
   mFooterView.show();
   mFooterView.setState(XListViewFooter.STATE_NORMAL);
   // both "pull up" and "click" will invoke load more.
   mFooterView.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
     startLoadMore();
    }
   });
  }
 }

 /**
  * stop refresh, reset header view.
  */
 public void stopRefresh() {
  if (mPullRefreshing == true) {
   mPullRefreshing = false;
   resetHeaderHeight();
  }
 }

 /**
  * stop load more, reset footer view.
  */
 public void stopLoadMore() {
  if (mPullLoading == true) {
   mPullLoading = false;
   mFooterView.setState(XListViewFooter.STATE_NORMAL);
  }
 }

 /**
  * set last refresh time
  *
  * @param time
  */
 public void setRefreshTime(String time) {
  mHeaderTimeView.setText(time);
 }

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

 private void updateHeaderHeight(float delta) {
  mHeaderView.setVisiableHeight((int) delta
    + mHeaderView.getVisiableHeight());
  if (mEnablePullRefresh && !mPullRefreshing) { // δ����ˢ��״̬�����¼�ͷ
   if (mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
    mHeaderView.setState(XListViewHeader.STATE_READY);
   } else {
    mHeaderView.setState(XListViewHeader.STATE_NORMAL);
   }
  }
  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();
 }

 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(XListViewFooter.STATE_READY);
   } else {
    mFooterView.setState(XListViewFooter.STATE_NORMAL);
   }
  }
  mFooterView.setBottomMargin(height);

//  setSelection(mTotalItemCount - 1); // scroll to bottom
 }

 private void resetFooterHeight() {
  int bottomMargin = mFooterView.getBottomMargin();
  if (bottomMargin > 0) {
   mScrollBack = SCROLLBACK_FOOTER;
   mScroller.startScroll(0, bottomMargin, 0, -bottomMargin,
     SCROLL_DURATION);
   invalidate();
  }
 }

 private void startLoadMore() {
  mPullLoading = true;
  mFooterView.setState(XListViewFooter.STATE_LOADING);
  if (mListViewListener != null) {
   mListViewListener.onLoadMore();
  }
 }

 @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);
    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:
   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();
   } 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 == SCROLLBACK_HEADER) {
    mHeaderView.setVisiableHeight(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);
  }
 }

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

 public void setXListViewListener(IXListViewListener l) {
  mListViewListener = l;
 }

 /**
  * you can listen ListView.OnScrollListener or this one. it will invoke
  * onXScrolling when header/footer scroll back.
  */
 public interface OnXScrollListener extends OnScrollListener {
  public void onXScrolling(View view);
 }

 /**
  * implements this interface to get refresh/load more event.
  */
 public interface IXListViewListener {
  public void onRefresh();

  public void onLoadMore();
 }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值