Android Open Source - Todo Pull To Refresh Attacher






From Project

Back to project page Todo.

License

The source code is released under:

GNU General Public License

If you think the Android project Todo 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

/*
 * Copyright 2013 Chris Banes// ww w. j a  v a  2s.c  o  m
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package uk.co.senab.actionbarpulltorefresh.library;

import java.util.Set;
import java.util.WeakHashMap;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;

/**
 * FIXME
 */
public class PullToRefreshAttacher implements View.OnTouchListener {

  /* Default configuration values */
  private static final int DEFAULT_HEADER_LAYOUT = R.layout.default_header;
  private static final int DEFAULT_ANIM_HEADER_IN = R.anim.fade_in;
  private static final int DEFAULT_ANIM_HEADER_OUT = R.anim.fade_out;
  private static final float DEFAULT_REFRESH_SCROLL_DISTANCE = 0.5f;
  private static final boolean DEFAULT_REFRESH_ON_UP = false;
  private static final int DEFAULT_REFRESH_MINIMIZED_DELAY = 3 * 1000;
  private static final boolean DEFAULT_REFRESH_MINIMIZE = true;

  private static final boolean DEBUG = false;
  private static final String LOG_TAG = "PullToRefreshAttacher";

  /* Member Variables */

  private final EnvironmentDelegate mEnvironmentDelegate;
  private final HeaderTransformer mHeaderTransformer;

  private final View mHeaderView;
  private HeaderViewListener mHeaderViewListener;
  private final Animation mHeaderInAnimation, mHeaderOutAnimation;

  private final int mTouchSlop;
  private final float mRefreshScrollDistance;

  private int mInitialMotionY, mLastMotionY, mPullBeginY;
  private boolean mIsBeingDragged, mIsRefreshing, mIsHandlingTouchEvent;

  private final WeakHashMap<View, ViewParams> mRefreshableViews;

  private boolean mEnabled = true;
  private final boolean mRefreshOnUp;
  private final int mRefreshMinimizeDelay;
  private final boolean mRefreshMinimize;

  private final Handler mHandler = new Handler();

  /**
   * Get a PullToRefreshAttacher for this Activity. If there is already a
   * PullToRefreshAttacher attached to the Activity, the existing one is
   * returned, otherwise a new instance is created. This version of the method
   * will use default configuration options for everything.
   * 
   * @param activity
   *            Activity to attach to.
   * @return PullToRefresh attached to the Activity.
   */
  public static PullToRefreshAttacher get(Activity activity) {
    return get(activity, new Options());
  }

  /**
   * Get a PullToRefreshAttacher for this Activity. If there is already a
   * PullToRefreshAttacher attached to the Activity, the existing one is
   * returned, otherwise a new instance is created.
   * 
   * @param activity
   *            Activity to attach to.
   * @param options
   *            Options used when creating the PullToRefreshAttacher.
   * @return PullToRefresh attached to the Activity.
   */
  public static PullToRefreshAttacher get(Activity activity, Options options) {
    return new PullToRefreshAttacher(activity, options);
  }

  protected PullToRefreshAttacher(Activity activity, Options options) {
    if (options == null) {
      Log.i(LOG_TAG, "Given null options so using default options.");
      options = new Options();
    }

    mRefreshableViews = new WeakHashMap<View, ViewParams>();

    // Copy necessary values from options
    mRefreshScrollDistance = options.refreshScrollDistance;
    mRefreshOnUp = options.refreshOnUp;
    mRefreshMinimizeDelay = options.refreshMinimizeDelay;
    mRefreshMinimize = options.refreshMinimize;

    // EnvironmentDelegate
    mEnvironmentDelegate = options.environmentDelegate != null ? options.environmentDelegate
        : createDefaultEnvironmentDelegate();

    // Header Transformer
    mHeaderTransformer = options.headerTransformer != null ? options.headerTransformer
        : createDefaultHeaderTransformer();

    // Create animations for use later
    mHeaderInAnimation = AnimationUtils.loadAnimation(activity,
        options.headerInAnimation);
    mHeaderOutAnimation = AnimationUtils.loadAnimation(activity,
        options.headerOutAnimation);
    if (mHeaderOutAnimation != null || mHeaderInAnimation != null) {
      final AnimationCallback callback = new AnimationCallback();
      if (mHeaderInAnimation != null)
        mHeaderInAnimation.setAnimationListener(callback);
      if (mHeaderOutAnimation != null)
        mHeaderOutAnimation.setAnimationListener(callback);
    }

    // Get touch slop for use later
    mTouchSlop = ViewConfiguration.get(activity).getScaledTouchSlop();

    // Get Window Decor View
    final ViewGroup decorView = (ViewGroup) activity.getWindow()
        .getDecorView();

    // Check to see if there is already a Attacher view installed
    if (decorView.getChildCount() == 1
        && decorView.getChildAt(0) instanceof DecorChildLayout) {
      throw new IllegalStateException(
          "View already installed to DecorView. This shouldn't happen.");
    }

    // Create Header view and then add to Decor View
    mHeaderView = LayoutInflater.from(
        mEnvironmentDelegate.getContextForInflater(activity)).inflate(
        options.headerLayout, decorView, false);
    if (mHeaderView == null) {
      throw new IllegalArgumentException(
          "Must supply valid layout id for header.");
    }
    mHeaderView.setVisibility(View.GONE);

    // Create DecorChildLayout which will move all of the system's decor
    // view's children + the
    // Header View to itself. See DecorChildLayout for more info.
    DecorChildLayout decorContents = new DecorChildLayout(activity,
        decorView, mHeaderView);

    // Now add the DecorChildLayout to the decor view
    decorView.addView(decorContents, ViewGroup.LayoutParams.MATCH_PARENT,
        ViewGroup.LayoutParams.MATCH_PARENT);

    // Notify transformer
        mHeaderTransformer.onViewCreated(activity, mHeaderView);
        // TODO Remove the follow deprecated method call before v1.0
    mHeaderTransformer.onViewCreated(mHeaderView);
  }

  /**
   * Add a view which will be used to initiate refresh requests and a listener
   * to be invoked when a refresh is started. This version of the method will
   * try to find a handler for the view from the built-in view delegates.
   * 
   * @param view
   *            View which will be used to initiate refresh requests.
   * @param refreshListener
   *            Listener to be invoked when a refresh is started.
   */
  public void addRefreshableView(View view, OnRefreshListener refreshListener) {
    addRefreshableView(view, null, refreshListener);
  }

  /**
   * Add a view which will be used to initiate refresh requests, along with a
   * delegate which knows how to handle the given view, and a listener to be
   * invoked when a refresh is started.
   * 
   * @param view
   *            View which will be used to initiate refresh requests.
   * @param viewDelegate
   *            delegate which knows how to handle <code>view</code>.
   * @param refreshListener
   *            Listener to be invoked when a refresh is started.
   */
  public void addRefreshableView(View view, ViewDelegate viewDelegate,
      OnRefreshListener refreshListener) {
    addRefreshableView(view, viewDelegate, refreshListener, true);
  }

  /**
   * Add a view which will be used to initiate refresh requests, along with a
   * delegate which knows how to handle the given view, and a listener to be
   * invoked when a refresh is started.
   * 
   * @param view
   *            View which will be used to initiate refresh requests.
   * @param viewDelegate
   *            delegate which knows how to handle <code>view</code>.
   * @param refreshListener
   *            Listener to be invoked when a refresh is started.
   * @param setTouchListener
   *            Whether to set this as the
   *            {@link android.view.View.OnTouchListener}.
   */
  void addRefreshableView(View view, ViewDelegate viewDelegate,
      OnRefreshListener refreshListener, final boolean setTouchListener) {
    // Check to see if view is null
    if (view == null) {
      Log.i(LOG_TAG, "Refreshable View is null.");
      return;
    }

    if (refreshListener == null) {
      throw new IllegalArgumentException(
          "OnRefreshListener not given. Please provide one.");
    }

    // ViewDelegate
    if (viewDelegate == null) {
      viewDelegate = InstanceCreationUtils.getBuiltInViewDelegate(view);
      if (viewDelegate == null) {
        throw new IllegalArgumentException(
            "No view handler found. Please provide one.");
      }
    }

    // View to detect refreshes for
    mRefreshableViews.put(view, new ViewParams(viewDelegate,
        refreshListener));
    if (setTouchListener) {
      view.setOnTouchListener(this);
    }
  }

  /**
   * Remove a view which was previously used to initiate refresh requests.
   * 
   * @param view
   *            - View which will be used to initiate refresh requests.
   */
  public void removeRefreshableView(View view) {
    if (mRefreshableViews.containsKey(view)) {
      mRefreshableViews.remove(view);
      view.setOnTouchListener(null);
    }
  }

  /**
   * Clear all views which were previously used to initiate refresh requests.
   */
  public void clearRefreshableViews() {
    Set<View> views = mRefreshableViews.keySet();
    for (View view : views) {
      view.setOnTouchListener(null);
    }
    mRefreshableViews.clear();
  }

  /**
   * This method should be called by your Activity's or Fragment's
   * onConfigurationChanged method.
   * 
   * @param newConfig
   *            - The new configuration
   */
  public void onConfigurationChanged(Configuration newConfig) {
    mHeaderTransformer.onViewCreated(mHeaderView);
  }

  /**
   * Manually set this Attacher's refreshing state. The header will be
   * displayed or hidden as requested.
   * 
   * @param refreshing
   *            - Whether the attacher should be in a refreshing state,
   */
  public final void setRefreshing(boolean refreshing) {
    setRefreshingInt(null, refreshing, false);
  }

  /**
   * @return true if this Attacher is currently in a refreshing state.
   */
  public final boolean isRefreshing() {
    return mIsRefreshing;
  }

  /**
   * @return true if this PullToRefresh is currently enabled (defaults to
   *         <code>true</code>)
   */
  public boolean isEnabled() {
    return mEnabled;
  }

  /**
   * Allows the enable/disable of this PullToRefreshAttacher. If disabled when
   * refreshing then the UI is automatically reset.
   * 
   * @param enabled
   *            - Whether this PullToRefreshAttacher is enabled.
   */
  public void setEnabled(boolean enabled) {
    mEnabled = enabled;

    if (!enabled) {
      // If we're not enabled, reset any touch handling
      resetTouch();

      // If we're currently refreshing, reset the ptr UI
      if (mIsRefreshing) {
        reset(false);
      }
    }
  }

  /**
   * Call this when your refresh is complete and this view should reset itself
   * (header view will be hidden).
   * 
   * This is the equivalent of calling <code>setRefreshing(false)</code>.
   */
  public final void setRefreshComplete() {
    setRefreshingInt(null, false, false);
  }

  /**
   * Set a {@link uk.co.senab.actionbarpulltorefresh.library.PullToRefreshAttacher.HeaderViewListener} which is called when the visibility
   * state of the Header View has changed.
   * 
   * @param listener
   */
  public final void setHeaderViewListener(HeaderViewListener listener) {
    mHeaderViewListener = listener;
  }

  /**
   * @return The Header View which is displayed when the user is pulling, or
   *         we are refreshing.
   */
  public final View getHeaderView() {
    return mHeaderView;
  }

  /**
   * @return The HeaderTransformer currently used by this Attacher.
   */
  public HeaderTransformer getHeaderTransformer() {
    return mHeaderTransformer;
  }

  @Override
  public final boolean onTouch(final View view, final MotionEvent event) {
    if (!mIsHandlingTouchEvent && onInterceptTouchEvent(view, event)) {
      mIsHandlingTouchEvent = true;
    }

    if (mIsHandlingTouchEvent) {
      onTouchEvent(view, event);
    }

    // Always return false as we only want to observe events
    return false;
  }

  final boolean onInterceptTouchEvent(View view, MotionEvent event) {
    if (DEBUG) {
      Log.d(LOG_TAG, "onInterceptTouchEvent: " + event.toString());
    }

    // If we're not enabled or currently refreshing don't handle any touch
    // events
    if (!isEnabled() || isRefreshing()) {
      return false;
    }

    final ViewParams params = mRefreshableViews.get(view);
    if (params == null) {
      return false;
    }

    switch (event.getAction()) {
    case MotionEvent.ACTION_MOVE: {
      // We're not currently being dragged so check to see if the user has
      // scrolled enough
      if (!mIsBeingDragged && mInitialMotionY > 0) {
        final int y = (int) event.getY();
        final int yDiff = y - mInitialMotionY;

        if (yDiff > mTouchSlop) {
          mIsBeingDragged = true;
          onPullStarted(y);
        } else if (yDiff < -mTouchSlop) {
          resetTouch();
        }
      }
      break;
    }

    case MotionEvent.ACTION_DOWN: {
      // If we're already refreshing, ignore
      if (canRefresh(true, params.onRefreshListener)
          && params.viewDelegate.isScrolledToTop(view)) {
        mInitialMotionY = (int) event.getY();
      }
      break;
    }

    case MotionEvent.ACTION_CANCEL:
    case MotionEvent.ACTION_UP: {
      resetTouch();
      break;
    }
    }

    return mIsBeingDragged;
  }

  final boolean onTouchEvent(View view, MotionEvent event) {
    if (DEBUG) {
      Log.d(LOG_TAG, "onTouchEvent: " + event.toString());
    }

    // If we're not enabled or currently refreshing don't handle any touch
    // events
    if (!isEnabled()) {
      return false;
    }

    final ViewParams params = mRefreshableViews.get(view);
    if (params == null) {
      return false;
    }

    switch (event.getAction()) {
    case MotionEvent.ACTION_MOVE: {
      // If we're already refreshing ignore it
      if (isRefreshing()) {
        return false;
      }

      final int y = (int) event.getY();

      if (mIsBeingDragged && y != mLastMotionY) {
        final int yDx = y - mLastMotionY;

        /**
         * Check to see if the user is scrolling the right direction
         * (down). We allow a small scroll up which is the check against
         * negative touch slop.
         */
        if (yDx >= -mTouchSlop) {
          onPull(view, y);
          // Only record the y motion if the user has scrolled down.
          if (yDx > 0) {
            mLastMotionY = y;
          }
        } else {
          onPullEnded();
          resetTouch();
        }
      }
      break;
    }

    case MotionEvent.ACTION_CANCEL:
    case MotionEvent.ACTION_UP: {
      checkScrollForRefresh(view);
      if (mIsBeingDragged) {
        onPullEnded();
      }
      resetTouch();
      break;
    }
    }

    return true;
  }

  void resetTouch() {
    mIsBeingDragged = false;
    mIsHandlingTouchEvent = false;
    mInitialMotionY = mLastMotionY = mPullBeginY = -1;
  }

  void onPullStarted(int y) {
    if (DEBUG) {
      Log.d(LOG_TAG, "onPullStarted");
    }
    showHeaderView();
    mPullBeginY = y;
  }

  void onPull(View view, int y) {
    if (DEBUG) {
      Log.d(LOG_TAG, "onPull");
    }

    final float pxScrollForRefresh = getScrollNeededForRefresh(view);
    final int scrollLength = y - mPullBeginY;

    if (scrollLength < pxScrollForRefresh) {
      mHeaderTransformer.onPulled(scrollLength / pxScrollForRefresh);
    } else {
      if (mRefreshOnUp) {
        mHeaderTransformer.onReleaseToRefresh();
      } else {
        setRefreshingInt(view, true, true);
      }
    }
  }

  void onPullEnded() {
    if (DEBUG) {
      Log.d(LOG_TAG, "onPullEnded");
    }
    if (!mIsRefreshing) {
      reset(true);
    }
  }

  void showHeaderView() {
    if (mHeaderView.getVisibility() != View.VISIBLE) {
      // Show Header
      if (mHeaderInAnimation != null) {
        // AnimationListener will call HeaderViewListener
        mHeaderView.startAnimation(mHeaderInAnimation);
      } else {
        // Call HeaderViewListener now as we have no animation
        if (mHeaderViewListener != null) {
          mHeaderViewListener.onStateChanged(mHeaderView,
              HeaderViewListener.STATE_VISIBLE);
        }
      }
      mHeaderView.setVisibility(View.VISIBLE);
    }
  }

  void hideHeaderView() {
    if (mHeaderView.getVisibility() != View.GONE) {
      // Hide Header
      if (mHeaderOutAnimation != null) {
        // AnimationListener will call HeaderTransformer and
        // HeaderViewListener
        mHeaderView.startAnimation(mHeaderOutAnimation);
      } else {
        // As we're not animating, hide the header + call the header
        // transformer now
        mHeaderView.setVisibility(View.GONE);
        mHeaderTransformer.onReset();

        if (mHeaderViewListener != null) {
          mHeaderViewListener.onStateChanged(mHeaderView,
              HeaderViewListener.STATE_HIDDEN);
        }
      }
    }
  }

  protected EnvironmentDelegate createDefaultEnvironmentDelegate() {
    return new EnvironmentDelegate();
  }

  protected HeaderTransformer createDefaultHeaderTransformer() {
    return new DefaultHeaderTransformer();
  }

  private boolean checkScrollForRefresh(View view) {
    if (mIsBeingDragged && mRefreshOnUp && view != null) {
      if (mLastMotionY - mPullBeginY >= getScrollNeededForRefresh(view)) {
        setRefreshingInt(view, true, true);
        return true;
      }
    }
    return false;
  }

  private void setRefreshingInt(View view, boolean refreshing,
      boolean fromTouch) {
    if (DEBUG) {
      Log.d(LOG_TAG, "setRefreshingInt: " + refreshing);
    }
    // Check to see if we need to do anything
    if (mIsRefreshing == refreshing) {
      return;
    }

    resetTouch();

    if (refreshing
        && canRefresh(fromTouch, getRefreshListenerForView(view))) {
      startRefresh(view, fromTouch);
    } else {
      reset(fromTouch);
    }
  }

  private OnRefreshListener getRefreshListenerForView(View view) {
    if (view != null) {
      ViewParams params = mRefreshableViews.get(view);
      if (params != null) {
        return params.onRefreshListener;
      }
    }
    return null;
  }

  /**
   * @param fromTouch
   *            - Whether this is being invoked from a touch event
   * @return true if we're currently in a state where a refresh can be
   *         started.
   */
  private boolean canRefresh(boolean fromTouch, OnRefreshListener listener) {
    return !mIsRefreshing && (!fromTouch || listener != null);
  }

  private float getScrollNeededForRefresh(View view) {
    return view.getHeight() * mRefreshScrollDistance;
  }

  private void reset(boolean fromTouch) {
    // Update isRefreshing state
    mIsRefreshing = false;

    // Remove any minimize callbacks
        if (mRefreshMinimize) {
        mHandler.removeCallbacks(mRefreshMinimizeRunnable);
        }

    // Hide Header View
    hideHeaderView();
  }

  private void startRefresh(View view, boolean fromTouch) {
    // Update isRefreshing state
    mIsRefreshing = true;

    // Call OnRefreshListener if this call has originated from a touch event
    if (fromTouch) {
      OnRefreshListener listener = getRefreshListenerForView(view);
      if (listener != null) {
        listener.onRefreshStarted(view);
      }
    }

    // Call Transformer
    mHeaderTransformer.onRefreshStarted();

    // Show Header View
    showHeaderView();

    // Post a delay runnable to minimize the refresh header
    if (mRefreshMinimize) {
      mHandler.postDelayed(mRefreshMinimizeRunnable, mRefreshMinimizeDelay);
        }
  }

  /**
   * Simple Listener to listen for any callbacks to Refresh.
   */
  public interface OnRefreshListener {
    /**
     * Called when the user has initiated a refresh by pulling.
     * 
     * @param view
     *            - View which the user has started the refresh from.
     */
    public void onRefreshStarted(View view);
  }

  public interface HeaderViewListener {
    /**
     * The state when the header view is completely visible.
     */
    public static int STATE_VISIBLE = 0;

    /**
     * The state when the header view is minimized. By default this means
     * that the progress bar is still visible, but the rest of the view is
     * hidden, showing the Action Bar behind.
         * <p/>
         * This will not be called in header minimization is disabled.
     */
    public static int STATE_MINIMIZED = 1;

    /**
     * The state when the header view is completely hidden.
     */
    public static int STATE_HIDDEN = 2;

    /**
     * Called when the visibility state of the Header View has changed.
     * 
     * @param headerView
     *            HeaderView who's state has changed.
     * @param state
     *            The new state. One of {@link #STATE_VISIBLE},
     *            {@link #STATE_MINIMIZED} and {@link #STATE_HIDDEN}
     */
    public void onStateChanged(View headerView, int state);
  }

  public static abstract class HeaderTransformer {

        /**
         * Called whether the header view has been inflated from the resources
         * defined in {@link uk.co.senab.actionbarpulltorefresh.library.PullToRefreshAttacher.Options#headerLayout}.
         *
         * @param activity The {@link Activity} that the header view is attached to.
         * @param headerView The inflated header view.
         */
        public void onViewCreated(Activity activity, View headerView) {}

    /**
     * @deprecated This will be removed before v1.0. Override
         * {@link #onViewCreated(android.app.Activity, android.view.View)} instead.
     */
    public void onViewCreated(View headerView) {}

    /**
     * Called when the header should be reset. You should update any child
     * views to reflect this.
     * <p/>
     * You should <strong>not</strong> change the visibility of the header
     * view.
     */
    public void onReset() {}

    /**
     * Called the user has pulled on the scrollable view.
     * 
     * @param percentagePulled
     *            - value between 0.0f and 1.0f depending on how far the
     *            user has pulled.
     */
    public void onPulled(float percentagePulled) {}

    /**
     * Called when a refresh has begun. Theoretically this call is similar
     * to that provided from {@link uk.co.senab.actionbarpulltorefresh.library.PullToRefreshAttacher.OnRefreshListener} but is more suitable
     * for header view updates.
     */
    public void onRefreshStarted() {}

    /**
     * Called when a refresh can be initiated when the user ends the touch
     * event. This is only called when {@link uk.co.senab.actionbarpulltorefresh.library.PullToRefreshAttacher.Options#refreshOnUp} is set to
     * true.
     */
    public void onReleaseToRefresh() {}

    /**
     * Called when the current refresh has taken longer than the time
     * specified in {@link uk.co.senab.actionbarpulltorefresh.library.PullToRefreshAttacher.Options#refreshMinimizeDelay}.
     */
    public void onRefreshMinimized() {}
  }

  /**
   * FIXME
   */
  public static abstract class ViewDelegate {

    /**
     * Allows you to provide support for View which do not have built-in
     * support. In this method you should cast <code>view</code> to it's
     * native class, and check if it is scrolled to the top.
     * 
     * @param view
     *            The view which has should be checked against.
     * @return true if <code>view</code> is scrolled to the top.
     */
    public abstract boolean isScrolledToTop(View view);
  }

  /**
   * FIXME
   */
  public static class EnvironmentDelegate {

    /**
     * @return Context which should be used for inflating the header layout
     */
        @TargetApi(14)
    public Context getContextForInflater(Activity activity) {
      if (Build.VERSION.SDK_INT >= 14) {
        return activity.getActionBar().getThemedContext();
      } else {
        return activity;
      }
    }
  }

  public static class Options {

    /**
     * EnvironmentDelegate instance which will be used. If null, we will
     * create an instance of the default class.
     */
    public EnvironmentDelegate environmentDelegate = null;

    /**
     * The layout resource ID which should be inflated to be displayed above
     * the Action Bar
     */
    public int headerLayout = DEFAULT_HEADER_LAYOUT;

    /**
     * The header transformer to be used to transfer the header view. If
     * null, an instance of {@link DefaultHeaderTransformer} will be used.
     */
    public HeaderTransformer headerTransformer = null;

    /**
     * The anim resource ID which should be started when the header is being
     * hidden.
     */
    public int headerOutAnimation = DEFAULT_ANIM_HEADER_OUT;

    /**
     * The anim resource ID which should be started when the header is being
     * shown.
     */
    public int headerInAnimation = DEFAULT_ANIM_HEADER_IN;

    /**
     * The percentage of the refreshable view that needs to be scrolled
     * before a refresh is initiated.
     */
    public float refreshScrollDistance = DEFAULT_REFRESH_SCROLL_DISTANCE;

    /**
     * Whether a refresh should only be initiated when the user has finished
     * the touch event.
     */
    public boolean refreshOnUp = DEFAULT_REFRESH_ON_UP;

    /**
     * The delay after a refresh is started in which the header should be
     * 'minimized'. By default, most of the header is faded out, leaving
     * only the progress bar signifying that a refresh is taking place.
     */
    public int refreshMinimizeDelay = DEFAULT_REFRESH_MINIMIZED_DELAY;

    /**
     * Enable or disable the header 'minimization', which by default means that the majority of
         * the header is hidden, leaving only the progress bar still showing.
         * <p/>
         * If set to true, the header will be minimized after the delay set in
         * {@link #refreshMinimizeDelay}. If set to false then the whole header will be displayed
         * until the refresh is finished.
     */
    public boolean refreshMinimize = DEFAULT_REFRESH_MINIMIZE;
  }

  private class AnimationCallback implements Animation.AnimationListener {

    @Override
    public void onAnimationStart(Animation animation) {
    }

    @Override
    public void onAnimationEnd(Animation animation) {
      if (animation == mHeaderOutAnimation) {
        mHeaderView.setVisibility(View.GONE);
        mHeaderTransformer.onReset();
        if (mHeaderViewListener != null) {
          mHeaderViewListener.onStateChanged(mHeaderView,
              HeaderViewListener.STATE_HIDDEN);
        }
      } else if (animation == mHeaderInAnimation) {
        if (mHeaderViewListener != null) {
          mHeaderViewListener.onStateChanged(mHeaderView,
              HeaderViewListener.STATE_VISIBLE);
        }
      }
    }

    @Override
    public void onAnimationRepeat(Animation animation) {
    }
  }

  /**
   * This class allows us to insert a layer in between the system decor view
   * and the actual decor. (e.g. Action Bar views). This is needed so we can
   * receive a call to fitSystemWindows(Rect) so we can adjust the header view
   * to fit the system windows too.
   */
  final static class DecorChildLayout extends FrameLayout {
    private final ViewGroup mHeaderViewWrapper;

    DecorChildLayout(Context context, ViewGroup systemDecorView,
        View headerView) {
      super(context);

      // Move all children from decor view to here
      for (int i = 0, z = systemDecorView.getChildCount(); i < z; i++) {
        View child = systemDecorView.getChildAt(i);
        systemDecorView.removeView(child);
        addView(child);
      }

      /**
       * Wrap the Header View in a FrameLayout and add it to this view. It
       * is wrapped so any inset changes do not affect the actual header
       * view.
       */
      mHeaderViewWrapper = new FrameLayout(context);
      mHeaderViewWrapper.addView(headerView);
      addView(mHeaderViewWrapper, ViewGroup.LayoutParams.MATCH_PARENT,
          ViewGroup.LayoutParams.WRAP_CONTENT);
    }

    @Override
    protected boolean fitSystemWindows(Rect insets) {
      if (DEBUG) {
        Log.d(LOG_TAG, "fitSystemWindows: " + insets.toString());
      }

      // Adjust the Header View's padding to take the insets into account
      mHeaderViewWrapper.setPadding(insets.left, insets.top,
          insets.right, insets.bottom);

      // Call return super so that the rest of the
      return super.fitSystemWindows(insets);
    }
  }

  private static final class ViewParams {
    final OnRefreshListener onRefreshListener;
    final ViewDelegate viewDelegate;

    ViewParams(ViewDelegate _viewDelegate,
        OnRefreshListener _onRefreshListener) {
      onRefreshListener = _onRefreshListener;
      viewDelegate = _viewDelegate;
    }
  }

  private final Runnable mRefreshMinimizeRunnable = new Runnable() {
    @Override
    public void run() {
      mHeaderTransformer.onRefreshMinimized();

      if (mHeaderViewListener != null) {
        mHeaderViewListener.onStateChanged(mHeaderView,
            HeaderViewListener.STATE_MINIMIZED);
      }
    }
  };

}




Java Source Code List

.MainActivity.java
com.todotxt.todotxttouch.AddTask.java
com.todotxt.todotxttouch.Constants.java
com.todotxt.todotxttouch.HelpActivity.java
com.todotxt.todotxttouch.PeriodicSyncStarter.java
com.todotxt.todotxttouch.Preferences.java
com.todotxt.todotxttouch.RelativeLayoutCheckable.java
com.todotxt.todotxttouch.SyncerService.java
com.todotxt.todotxttouch.TodoApplication.java
com.todotxt.todotxttouch.TodoException.java
com.todotxt.todotxttouch.TodoLocationPreference.java
com.todotxt.todotxttouch.TodoPreferences.java
com.todotxt.todotxttouch.TodoTxtTouch.java
com.todotxt.todotxttouch.TodoWidgetProvider.java
com.todotxt.todotxttouch.UpgradeHandler.java
com.todotxt.todotxttouch.remote.Client.java
com.todotxt.todotxttouch.remote.PullTodoResult.java
com.todotxt.todotxttouch.remote.RemoteClientManager.java
com.todotxt.todotxttouch.remote.RemoteClient.java
com.todotxt.todotxttouch.remote.RemoteConflictException.java
com.todotxt.todotxttouch.remote.RemoteException.java
com.todotxt.todotxttouch.remote.RemoteFolderImpl.java
com.todotxt.todotxttouch.remote.RemoteFolder.java
com.todotxt.todotxttouch.task.AndFilter.java
com.todotxt.todotxttouch.task.ByContextFilter.java
com.todotxt.todotxttouch.task.ByPriorityFilter.java
com.todotxt.todotxttouch.task.ByProjectFilter.java
com.todotxt.todotxttouch.task.ByTextFilter.java
com.todotxt.todotxttouch.task.ContextParser.java
com.todotxt.todotxttouch.task.EndpointsTaskBagImpl.java
com.todotxt.todotxttouch.task.FilterFactory.java
com.todotxt.todotxttouch.task.Filter.java
com.todotxt.todotxttouch.task.LinkParser.java
com.todotxt.todotxttouch.task.LocalFileTaskRepository.java
com.todotxt.todotxttouch.task.LocalTaskRepository.java
com.todotxt.todotxttouch.task.MailAddressParser.java
com.todotxt.todotxttouch.task.OrFilter.java
com.todotxt.todotxttouch.task.PhoneNumberParser.java
com.todotxt.todotxttouch.task.PriorityTextSplitter.java
com.todotxt.todotxttouch.task.Priority.java
com.todotxt.todotxttouch.task.ProjectParser.java
com.todotxt.todotxttouch.task.Sort.java
com.todotxt.todotxttouch.task.TaskBagFactory.java
com.todotxt.todotxttouch.task.TaskBagImpl.java
com.todotxt.todotxttouch.task.TaskBag.java
com.todotxt.todotxttouch.task.TaskPersistException.java
com.todotxt.todotxttouch.task.Task.java
com.todotxt.todotxttouch.task.TextSplitter.java
com.todotxt.todotxttouch.util.CursorPositionCalculator.java
com.todotxt.todotxttouch.util.Path.java
com.todotxt.todotxttouch.util.RelativeDate.java
com.todotxt.todotxttouch.util.Strings.java
com.todotxt.todotxttouch.util.TaskIo.java
com.todotxt.todotxttouch.util.Tree.java
com.todotxt.todotxttouch.util.Util.java
com.todotxt.todotxttouch.widget.ListWidgetProvider.java
com.todotxt.todotxttouch.widget.ListWidgetService.java
de.timroes.swipetodismiss.SwipeDismissList.java
uk.co.senab.actionbarpulltorefresh.extras.actionbarsherlock.AbsDefaultHeaderTransformer.java
uk.co.senab.actionbarpulltorefresh.extras.actionbarsherlock.PullToRefreshAttacher.java
uk.co.senab.actionbarpulltorefresh.library.DefaultHeaderTransformer.java
uk.co.senab.actionbarpulltorefresh.library.InstanceCreationUtils.java
uk.co.senab.actionbarpulltorefresh.library.PullToRefreshAttacher.java
uk.co.senab.actionbarpulltorefresh.library.PullToRefreshLayout.java
uk.co.senab.actionbarpulltorefresh.library.viewdelegates.AbsListViewDelegate.java
uk.co.senab.actionbarpulltorefresh.library.viewdelegates.ScrollYDelegate.java
uk.co.senab.actionbarpulltorefresh.library.viewdelegates.WebViewDelegate.java
voodsingular.todo.MyEndpointEndpoint.java
voodsingular.todo.MyEndpoint.java
voodsingular.todo.TaskBean.java