/*
* Copyright 2013 Chris Banes
*
* 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.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 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 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 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 Options#refreshOnUp} is set to
* true.
*/
public void onReleaseToRefresh() {}
/**
* Called when the current refresh has taken longer than the time
* specified in {@link 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
*/
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);
}
}
};
}