Android Open Source - PulltorefreshListView Refreshable List View






From Project

Back to project page PulltorefreshListView.

License

The source code is released under:

Apache License

If you think the Android project PulltorefreshListView listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package yaochangwei.pulltorefreshlistview.widget;
/*from  w w  w.jav a  2 s.co m*/
/**
 * @author Yaochangwei (yaochangwei@gmail.com)
 * 
 *  Refreshable ListView base class. 
 */
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.ListView;

public class RefreshableListView extends ListView {

  private static final int STATE_NORMAL = 0;
  private static final int STATE_READY = 1;
  private static final int STATE_PULL = 2;
  private static final int STATE_UPDATING = 3;
  private static final int INVALID_POINTER_ID = -1;

  private static final int UP_STATE_READY = 4;
  private static final int UP_STATE_PULL = 5;

  private static final int MIN_UPDATE_TIME = 500;

  protected ListHeaderView mListHeaderView;
  protected ListBottomView mListBottomView;

  private int mActivePointerId;
  private float mLastY;

  private int mState;

  private boolean mPullUpRefreshEnabled = false;

  private OnUpdateTask mOnUpdateTask;
  private OnPullUpUpdateTask mOnPullUpUpdateTask;

  private int mTouchSlop;

  public RefreshableListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initialize();
  }

  public RefreshableListView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    initialize();
  }

  /***
   * Set the Header Content View.
   * 
   * @param id
   *            The view resource.
   */
  public void setContentView(int id) {
    final View view = LayoutInflater.from(getContext()).inflate(id,
        mListHeaderView, false);
    mListHeaderView.addView(view);
  }

  /**
   * Set the bootom content view. and open this feature.
   * 
   * @param id
   */
  public void setBottomContentView(int id) {
    mPullUpRefreshEnabled = true;
    final View view = LayoutInflater.from(getContext()).inflate(id,
        mListBottomView, false);
    mListBottomView.addView(view);
    addFooterView(mListBottomView, null, false);
  }

  public void setBottomContentView(View v) {
    mListBottomView.addView(v);
  }

  public void setContentView(View v) {
    mListHeaderView.addView(v);
  }

  public ListHeaderView getListHeaderView() {
    return mListHeaderView;
  }

  /**
   * Setup the update task.
   * 
   * @param task
   */
  public void setOnUpdateTask(OnUpdateTask task) {
    mOnUpdateTask = task;
  }

  public void setOnPullUpUpdateTask(OnPullUpUpdateTask task) {
    mOnPullUpUpdateTask = task;
  }

  /**
   * Update immediately.
   */
  public void startUpdateImmediate() {
    if (mState == STATE_UPDATING) {
      return;
    }
    setSelectionFromTop(0, 0);
    mListHeaderView.moveToUpdateHeight();
    update();
  }

  /**
   * Set the Header View change listener.
   * 
   * @param listener
   */
  public void setOnHeaderViewChangedListener(
      OnHeaderViewChangedListener listener) {
    mListHeaderView.mOnHeaderViewChangedListener = listener;
  }

  public void setOnBottomViewChangedListener(
      OnBottomViewChangedListener listener) {
    mListBottomView.mOnHeaderViewChangedListener = listener;
  }

  private void initialize() {
    final Context context = getContext();
    mListHeaderView = new ListHeaderView(context, this);
    addHeaderView(mListHeaderView, null, false);
    mListBottomView = new ListBottomView(getContext(), this);
    mState = STATE_NORMAL;
    final ViewConfiguration configuration = ViewConfiguration.get(context);
    mTouchSlop = configuration.getScaledTouchSlop();
  }

  private void pullUpUpdate() {
    if (mListBottomView.isUpdateNeeded()) {
      if (mOnPullUpUpdateTask != null) {
        mOnPullUpUpdateTask.onUpdateStart();
      }

      final int preAdapterCount = this.getAdapter().getCount();

      mListBottomView.startUpdate(new Runnable() {
        public void run() {
          final long b = System.currentTimeMillis();
          if (mOnPullUpUpdateTask != null) {
            mOnPullUpUpdateTask.updateBackground();
          }
          final long delta = MIN_UPDATE_TIME
              - (System.currentTimeMillis() - b);
          if (delta > 0) {
            try {
              Thread.sleep(delta);
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }

          post(new Runnable() {
            public void run() {
              int deltay = mListBottomView.close(STATE_NORMAL);
              postDelayed(new Runnable() {
                public void run() {

                  if (getAdapter().getCount() != preAdapterCount) {
                    throw new IllegalStateException(
                        "You should change the adapter data in updateUI");
                  }

                  if (mOnPullUpUpdateTask != null) {
                    mOnPullUpUpdateTask.updateUI();
                  }
                }
              }, deltay);

            }
          });

        }
      });
      mState = STATE_UPDATING;
    } else {
      mListBottomView.close(STATE_NORMAL);
    }
  }

  private void update() {
    if (mListHeaderView.isUpdateNeeded()) {
      if (mOnUpdateTask != null) {
        mOnUpdateTask.onUpdateStart();
      }
      mListHeaderView.startUpdate(new Runnable() {
        public void run() {
          final long b = System.currentTimeMillis();
          if (mOnUpdateTask != null) {
            mOnUpdateTask.updateBackground();
          }
          final long delta = MIN_UPDATE_TIME
              - (System.currentTimeMillis() - b);
          if (delta > 0) {
            try {
              Thread.sleep(delta);
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
          post(new Runnable() {
            public void run() {
              mListHeaderView.close(STATE_NORMAL);
              if (mOnUpdateTask != null) {
                mOnUpdateTask.updateUI();
              }
            }
          });
        }
      });
      mState = STATE_UPDATING;
    } else {
      mListHeaderView.close(STATE_NORMAL);
    }
  }

  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
    if (mState == STATE_UPDATING) {
      return super.dispatchTouchEvent(ev);
    }
    final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
    switch (action) {
    case MotionEvent.ACTION_DOWN:
      mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
      mLastY = ev.getY();
      isFirstViewTop();
      isLastViewBottom();
      break;
    case MotionEvent.ACTION_MOVE:
      if (mActivePointerId == INVALID_POINTER_ID) {
        break;
      }

      if (mState == STATE_NORMAL) {
        isFirstViewTop();
        isLastViewBottom();
      }

      if (mState == STATE_READY) {
        final int activePointerId = mActivePointerId;
        final int activePointerIndex = MotionEventCompat
            .findPointerIndex(ev, activePointerId);
        final float y = MotionEventCompat.getY(ev, activePointerIndex);
        final int deltaY = (int) (y - mLastY);
        mLastY = y;
        if (deltaY <= 0 || Math.abs(y) < mTouchSlop) {
          mState = STATE_NORMAL;
        } else {
          mState = STATE_PULL;
          ev.setAction(MotionEvent.ACTION_CANCEL);
          super.dispatchTouchEvent(ev);
        }
      } else if (mState == UP_STATE_READY) {
        final int activePointerId = mActivePointerId;
        final int activePointerIndex = MotionEventCompat
            .findPointerIndex(ev, activePointerId);
        final float y = MotionEventCompat.getY(ev, activePointerIndex);
        final int deltaY = (int) (y - mLastY);
        mLastY = y;
        if (deltaY >= 0 || Math.abs(y) < mTouchSlop) {
          mState = STATE_NORMAL;
        } else {
          mState = UP_STATE_PULL;
          ev.setAction(MotionEvent.ACTION_CANCEL);
          super.dispatchTouchEvent(ev);
        }
      }

      if (mState == STATE_PULL) {
        final int activePointerId = mActivePointerId;
        final int activePointerIndex = MotionEventCompat
            .findPointerIndex(ev, activePointerId);
        final float y = MotionEventCompat.getY(ev, activePointerIndex);
        final int deltaY = (int) (y - mLastY);
        mLastY = y;

        final int headerHeight = mListHeaderView.getHeight();
        setHeaderHeight(headerHeight + deltaY * 5 / 9);
        return true;
      } else if (mState == UP_STATE_PULL) {
        final int activePointerId = mActivePointerId;
        final int activePointerIndex = MotionEventCompat
            .findPointerIndex(ev, activePointerId);
        final float y = MotionEventCompat.getY(ev, activePointerIndex);
        final int deltaY = (int) (y - mLastY);
        mLastY = y;
        final int headerHeight = mListBottomView.getHeight();
        setBottomHeight(headerHeight - deltaY * 5 / 9);
        return true;
      }

      break;
    case MotionEvent.ACTION_CANCEL:
    case MotionEvent.ACTION_UP:
      mActivePointerId = INVALID_POINTER_ID;
      if (mState == STATE_PULL) {
        update();
      } else if (mState == UP_STATE_PULL) {
        pullUpUpdate();
      }
      break;
    case MotionEventCompat.ACTION_POINTER_DOWN:
      final int index = MotionEventCompat.getActionIndex(ev);
      final float y = MotionEventCompat.getY(ev, index);
      mLastY = y;
      mActivePointerId = MotionEventCompat.getPointerId(ev, index);
      break;
    case MotionEventCompat.ACTION_POINTER_UP:
      onSecondaryPointerUp(ev);
      break;
    }
    return super.dispatchTouchEvent(ev);
  }

  private void onSecondaryPointerUp(MotionEvent ev) {
    final int pointerIndex = MotionEventCompat.getActionIndex(ev);
    final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
    if (pointerId == mActivePointerId) {
      final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
      mLastY = MotionEventCompat.getY(ev, newPointerIndex);
      mActivePointerId = MotionEventCompat.getPointerId(ev,
          newPointerIndex);
    }
  }

  void setState(int state) {
    mState = state;
  }

  private void setHeaderHeight(int height) {
    mListHeaderView.setHeaderHeight(height);
  }

  private void setBottomHeight(int height) {
    mListBottomView.setBottomHeight(height);
  }

  private boolean isLastViewBottom() {
    final int count = getChildCount();
    if (count == 0 || !mPullUpRefreshEnabled) {
      return false;
    }

    final int lastVisiblePosition = getLastVisiblePosition();
    boolean needs = (lastVisiblePosition == (getAdapter().getCount() - getHeaderViewsCount()))
        && (getChildAt(getChildCount() - 1).getBottom() == (getBottom() - getTop()));
    if (needs) {
      mState = UP_STATE_READY;
    }
    return needs;
  }

  private boolean isFirstViewTop() {
    final int count = getChildCount();
    if (count == 0) {
      return true;
    }
    final int firstVisiblePosition = this.getFirstVisiblePosition();
    final View firstChildView = getChildAt(0);
    boolean needs = firstChildView.getTop() == 0
        && (firstVisiblePosition == 0);
    if (needs) {
      mState = STATE_READY;
    }

    return needs;
  }

  /** When use custom List header view */
  public static interface OnHeaderViewChangedListener {
    /**
     * When user pull the list view, we can change the header status here.
     * for example: the arrow rotate down or up.
     * 
     * @param v
     *            : the list view header
     * @param canUpdate
     *            : if the list view can update.
     */
    void onViewChanged(View v, boolean canUpdate);

    /**
     * Change the header status when we really do the update task. for
     * example: display the progressbar.
     * 
     * @param v
     *            the list view header
     */
    void onViewUpdating(View v);

    /**
     * Will called when the update task finished. for example: hide the
     * progressbar and show the arrow.
     * 
     * @param v
     *            the list view header.
     */
    void onViewUpdateFinish(View v);
  }

  public static interface OnBottomViewChangedListener extends
      OnHeaderViewChangedListener {

  }

  public static interface OnPullUpUpdateTask extends OnUpdateTask {

  }

  /** The callback when the updata task begin, doing. or finish. */
  public static interface OnUpdateTask {

    /**
     * will called before the update task begin. Will Run in the UI thread.
     */
    public void onUpdateStart();

    /**
     * Will called doing the background task. Will Run in the background
     * thread.
     */
    public void updateBackground();

    /**
     * Will called when doing the background task. Will Run in the UI
     * thread.
     */
    public void updateUI();

  }

}




Java Source Code List

yaochangwei.pulltorefreshlistview.TestActivity.java
yaochangwei.pulltorefreshlistview.widget.ListBottomView.java
yaochangwei.pulltorefreshlistview.widget.ListHeaderView.java
yaochangwei.pulltorefreshlistview.widget.MotionEventCompatEclair.java
yaochangwei.pulltorefreshlistview.widget.MotionEventCompat.java
yaochangwei.pulltorefreshlistview.widget.RefreshableListView.java
yaochangwei.pulltorefreshlistview.widget.extend.PullToRefreshListView.java