/*
* 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 com.roboo.like.google.views.helper;
import java.util.WeakHashMap;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.roboo.like.google.R;
import com.roboo.like.google.abs.ptr.InstanceCreationUtils;
import com.roboo.like.google.abs.ptr.OnCancleListener;
public class PullToRefreshHelper implements View.OnTouchListener
{
/** 是否DEFAULT_REFRESH_MINIMIZED_DELAY后,自动隐藏header,即使任务没有完成 */
private static final boolean DEFAULT_HIDE_HEADER_IN_FIXED_TIME = false;
private static final int DEFAULT_REFRESH_MINIMIZED_DELAY = 5000;
private static final int DEFAULT_HEADER_LAYOUT = R.layout.ptr_default_header;
private static final int DEFAULT_ANIM_HEADER_IN = R.anim.ptr_fade_in;
private static final int DEFAULT_ANIM_HEADER_OUT = R.anim.ptr_fade_out;
private static final float DEFAULT_REFRESH_SCROLL_DISTANCE = 0.5f;
private static final boolean DEFAULT_REFRESH_ON_UP = false;
private static final boolean DEBUG = false;
private static final String LOG_TAG = "PullToRefreshAttacher";
private static final WeakHashMap<Activity, PullToRefreshHelper> ATTACHERS = new WeakHashMap<Activity, PullToRefreshHelper>();
private final Activity mActivity;
private final EnvironmentDelegate mEnvironmentDelegate;
private final HeaderTransformer mHeaderTransformer;
private final View mHeaderView;
private final Animation mHeaderInAnimation, mHeaderOutAnimation;
private static OnCancleListener mOnCancleListener;
private final int mTouchSlop;
private final float mRefreshScrollDistance;
private float mInitialMotionY, mLastMotionY, mPullBeginY;
private boolean mIsBeingDragged, mIsRefreshing, mIsHandlingTouchEvent;
private final WeakHashMap<View, ViewParams> mRefreshableViews;
private boolean mEnabled = true;
private boolean mRefreshOnUp;
private int mRefreshMinimizeDelay;
private final Handler mHandler = new Handler();
/**
* 获取一个与当前Activity有关联的 PullToRefreshAttacher 实例, 如果已经存在,就返回存在的实例, 否则重新创建一个
* 创建完成后界面中已经在Actionbar的位置添加了刷新标识器布局
*
* @param activity
* Activity to attach to.
* @return PullToRefresh attached to the Activity.
*/
public static PullToRefreshHelper 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 PullToRefreshHelper get(Activity activity, Options options)
{
PullToRefreshHelper attacher = ATTACHERS.get(activity);
if (attacher == null)
{
attacher = new PullToRefreshHelper(activity, options);
ATTACHERS.put(activity, attacher);
}
return attacher;
}
public void setOnCancleListener(OnCancleListener onCancleListener)
{
mOnCancleListener = onCancleListener;
}
protected PullToRefreshHelper(Activity activity, Options options)
{
if (options == null)
{
Log.i(LOG_TAG, "Given null options so using default options.");
options = new Options();
}
mActivity = activity;
mRefreshableViews = new WeakHashMap<View, ViewParams>();
// Copy necessary values from options
mRefreshScrollDistance = options.refreshScrollDistance;
mRefreshOnUp = options.refreshOnUp;
mRefreshMinimizeDelay = options.refreshMinimizeDelay;
// 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)
{
mHeaderOutAnimation.setAnimationListener(new AnimationCallback());
}
// 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);
/* 把封装有HeaderView刷新指示器的View添加到系统的DeCorView(android.R.id.content【FramegLayout】)中去* */
decorView.addView(decorContents, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
// Notify transformer
mHeaderTransformer.onViewCreated(activity, 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}.
*/
public 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);
}
}
/**
* 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(mActivity, 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);
if (!DEFAULT_HIDE_HEADER_IN_FIXED_TIME)
{
mHandler.post(mRefreshMinimizeRunnable);
}
}
/**
* @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;
}
public 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 > 0f)
{
final float y = event.getY();
final float 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 = event.getY();
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
{
resetTouch();
break;
}
}
return mIsBeingDragged;
}
public 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 float y = event.getY();
if (mIsBeingDragged)
{
final float 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 > 0f)
{
mLastMotionY = y;
}
}
else
{
onPullEnded();
resetTouch();
}
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
{
checkScrollForRefresh(view);
if (mIsBeingDragged)
{
onPullEnded();
}
resetTouch();
break;
}
}
return true;
}
private void resetTouch()
{
mIsBeingDragged = false;
mIsHandlingTouchEvent = false;
mInitialMotionY = mLastMotionY = mPullBeginY = -1f;
}
void onPullStarted(float y)
{
if (DEBUG)
{
Log.d(LOG_TAG, "onPullStarted");
}
// Show Header
if (mHeaderInAnimation != null)
{
mHeaderView.startAnimation(mHeaderInAnimation);
}
mHeaderView.setVisibility(View.VISIBLE);
mPullBeginY = y;
}
void onPull(View view, float y)
{
if (DEBUG)
{
Log.d(LOG_TAG, "onPull");
}
final int pxScrollForRefresh = getScrollNeededForRefresh(view);
final float 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);
}
}
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 int getScrollNeededForRefresh(View view)
{
return Math.round(view.getHeight() * mRefreshScrollDistance);
}
private void reset(boolean fromTouch)
{
// Update isRefreshing state
mIsRefreshing = false;
// Remove any minimize callbacks
mHandler.removeCallbacks(mRefreshMinimizeRunnable);
if (mHeaderView.getVisibility() != View.GONE)
{
// Hide Header
if (mHeaderOutAnimation != null)
{
mHeaderView.startAnimation(mHeaderOutAnimation);
// HeaderTransformer.onReset() is called once the animation has
// finished
}
else
{
// As we're not animating, hide the header + call the header
// transformer now
mHeaderView.setVisibility(View.GONE);
mHeaderTransformer.onReset();
}
}
}
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();
// Make sure header is visible.
if (mHeaderView.getVisibility() != View.VISIBLE)
{
if (mHeaderInAnimation != null)
{
mHeaderView.startAnimation(mHeaderInAnimation);
}
mHeaderView.setVisibility(View.VISIBLE);
}
// Post a delay runnable to minimize the refresh header
if (DEFAULT_HIDE_HEADER_IN_FIXED_TIME)
{
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 static abstract class HeaderTransformer
{
/**
* Called whether the header view has been inflated from the resources
* defined in {@link Options#headerLayout}.
*
* @param headerView
* - inflated header view.
*/
public abstract void onViewCreated(Activity activity, 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 abstract 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 abstract 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 abstract 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 abstract void onReleaseToRefresh();
/**
* Called when the current refresh has taken longer than the time
* specified in {@link Options#refreshMinimizeDelay}.
*/
public abstract 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
*/
@SuppressLint("NewApi")
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;
}
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();
}
}
@Override
public void onAnimationRepeat(Animation animation)
{}
}
/**
* Default Header Transformer.
*/
public static class DefaultHeaderTransformer extends HeaderTransformer
{
private ViewGroup mContentLayout;
private TextView mHeaderTextView;
private Button mHeaderButton;
private ImageButton mHeaderImageButton;
private ProgressBar mHeaderProgressBar;
private CharSequence mPullRefreshLabel, mRefreshingLabel, mReleaseLabel;
private final Interpolator mInterpolator = new AccelerateInterpolator();
@Override
public void onViewCreated(Activity activity, View headerView)
{
// Get ProgressBar and TextView. Also set initial text on TextView
mHeaderProgressBar = (ProgressBar) headerView.findViewById(R.id.ptr_progress);
mHeaderTextView = (TextView) headerView.findViewById(R.id.ptr_text);
mHeaderButton = (Button) headerView.findViewById(R.id.ptr_btn);
mHeaderImageButton = (ImageButton) headerView.findViewById(R.id.ptr_ibtn_cancle);
mPullRefreshLabel = "下拉刷新…";
mRefreshingLabel = "正在更新数据…";
mReleaseLabel = "放开以刷新…";
mContentLayout = (ViewGroup) headerView.findViewById(R.id.ptr_content);
if (mContentLayout != null)
{
mContentLayout.getLayoutParams().height = getActionBarSize(activity);
mContentLayout.requestLayout();
}
Drawable abBg = getActionBarBackground(activity);
if (abBg != null)
{
// If we do not have a opaque background we just display a solid
// solid behind it
if (abBg.getOpacity() != PixelFormat.OPAQUE)
{
View view = headerView.findViewById(R.id.ptr_text_opaque_bg);
if (view != null)
{
view.setVisibility(View.VISIBLE);
}
}
// mHeaderTextView.setBackgroundDrawable(abBg);
}
// Call onReset to make sure that the View is consistent
onReset();
}
@Override
public void onReset()
{
// Reset Progress Bar
if (mHeaderProgressBar != null)
{
mHeaderProgressBar.setVisibility(View.GONE);
mHeaderProgressBar.setProgress(0);
mHeaderProgressBar.setIndeterminate(false);
}
// Reset Text View
if (mHeaderTextView != null)
{
mHeaderTextView.setVisibility(View.GONE);
mHeaderTextView.setText(mPullRefreshLabel);
}
if (mHeaderButton != null)
{
mHeaderButton.setVisibility(View.VISIBLE);
mHeaderButton.setText(mPullRefreshLabel);
}
if(mHeaderImageButton != null)
{
mHeaderImageButton.setVisibility(View.GONE);
}
// Reset the Content Layout
if (mContentLayout != null)
{
mContentLayout.setVisibility(View.VISIBLE);
}
}
@Override
public void onPulled(float percentagePulled)
{
if (mHeaderProgressBar != null)
{
mHeaderProgressBar.setVisibility(View.VISIBLE);
final float progress = mInterpolator.getInterpolation(percentagePulled);
mHeaderProgressBar.setProgress(Math.round(mHeaderProgressBar.getMax() * progress));
}
if(mContentLayout != null)
{
mContentLayout.setVisibility(View.VISIBLE);
}
}
@Override
public void onRefreshStarted()
{
if (mHeaderTextView != null)
{
mHeaderTextView.setText(mRefreshingLabel);
}
if (mHeaderButton != null)
{
mHeaderButton.setText(mRefreshingLabel);
}
if(mHeaderImageButton != null)
{
mHeaderImageButton.setVisibility(View.VISIBLE);
mHeaderImageButton.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
if(mOnCancleListener != null)
{
mOnCancleListener.onCancle();
}
}
});
}
if (mHeaderProgressBar != null)
{
mHeaderProgressBar.setVisibility(View.VISIBLE);
mHeaderProgressBar.setIndeterminate(true);
}
}
@Override
public void onReleaseToRefresh()
{
if (mHeaderTextView != null)
{
mHeaderTextView.setText(mReleaseLabel);
}
if (mHeaderButton != null)
{
mHeaderButton.setText(mReleaseLabel);
}
if (mHeaderProgressBar != null)
{
mHeaderProgressBar.setProgress(mHeaderProgressBar.getMax());
}
}
@Override
public void onRefreshMinimized()
{
// Here we fade out most of the header, leaving just the progress bar
if (mContentLayout != null)
{
mContentLayout.startAnimation(AnimationUtils.loadAnimation(mContentLayout.getContext(), R.anim.ptr_fade_out));
mContentLayout.setVisibility(View.INVISIBLE);
}
}
/**
* Set Text to show to prompt the user is pull (or keep pulling).
*
* @param pullText
* - Text to display.
*/
public void setPullText(CharSequence pullText)
{
mPullRefreshLabel = pullText;
if (mHeaderTextView != null)
{
mHeaderTextView.setText(mPullRefreshLabel);
}
if (mHeaderButton != null)
{
mHeaderButton.setText(mPullRefreshLabel);
}
}
/**
* Set Text to show to tell the user that a refresh is currently in
* progress.
*
* @param refreshingText
* - Text to display.
*/
public void setRefreshingText(CharSequence refreshingText)
{
mRefreshingLabel = refreshingText;
}
/**
* Set Text to show to tell the user has scrolled enough to refresh.
*
* @param releaseText
* - Text to display.
*/
public void setReleaseText(CharSequence releaseText)
{
mReleaseLabel = releaseText;
}
protected Drawable getActionBarBackground(Context context)
{
int[] android_styleable_ActionBar = { android.R.attr.background };
// Need to get resource id of style pointed to from actionBarStyle
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.actionBarStyle, outValue, true);
// Now get action bar style values...
TypedArray abStyle = context.getTheme().obtainStyledAttributes(outValue.resourceId, android_styleable_ActionBar);
try
{
return abStyle.getDrawable(0);
}
finally
{
abStyle.recycle();
}
}
/** 获取ActionBar的高度 */
protected int getActionBarSize(Context context)
{
int[] attrs = { android.R.attr.actionBarSize };
TypedArray values = context.getTheme().obtainStyledAttributes(attrs);
try
{
return values.getDimensionPixelSize(0, 0);// 第一个参数数组索引,第二个参数 默认值
}
finally
{
values.recycle();
}
}
}
/**
* 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 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);
int minHeight = (int) (48 * getResources().getDisplayMetrics().density);
int[] attrs = { android.R.attr.actionBarSize };
TypedArray values = context.getTheme().obtainStyledAttributes(attrs);
try
{
minHeight = values.getDimensionPixelSize(0, 0);// 第一个参数数组索引,第二个参数 默认值
}
finally
{
values.recycle();
}
mHeaderViewWrapper.setMinimumHeight(minHeight);
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();
}
};
}