| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717 |
- /*******************************************************************************
- * Copyright 2011, 2012 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.handmark.pulltorefresh.library;
- import android.annotation.TargetApi;
- import android.content.Context;
- import android.content.res.TypedArray;
- import android.graphics.drawable.Drawable;
- import android.os.Build.VERSION;
- import android.os.Build.VERSION_CODES;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Parcelable;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.Gravity;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewConfiguration;
- import android.view.ViewGroup;
- import android.view.animation.DecelerateInterpolator;
- import android.view.animation.Interpolator;
- import android.widget.FrameLayout;
- import android.widget.LinearLayout;
- import com.handmark.pulltorefresh.library.internal.FlipLoadingLayout;
- import com.handmark.pulltorefresh.library.internal.LoadingLayout;
- import com.handmark.pulltorefresh.library.internal.RotateLoadingLayout;
- import com.handmark.pulltorefresh.library.internal.Utils;
- import com.handmark.pulltorefresh.library.internal.ViewCompat;
- public abstract class PullToRefreshBase<T extends View> extends LinearLayout implements IPullToRefresh<T> {
- // ===========================================================
- // Constants
- // ===========================================================
- static final boolean DEBUG = false;
- static final boolean USE_HW_LAYERS = false;
- static final String LOG_TAG = "PullToRefresh";
- static final float FRICTION = 4.0f;
- public static final int SMOOTH_SCROLL_DURATION_MS = 500;
- public static final int SMOOTH_SCROLL_LONG_DURATION_MS = 1525;
- static final int DEMO_SCROLL_INTERVAL = 225;
- static final String STATE_STATE = "ptr_state";
- static final String STATE_MODE = "ptr_mode";
- static final String STATE_CURRENT_MODE = "ptr_current_mode";
- static final String STATE_SCROLLING_REFRESHING_ENABLED = "ptr_disable_scrolling";
- static final String STATE_SHOW_REFRESHING_VIEW = "ptr_show_refreshing_view";
- static final String STATE_SUPER = "ptr_super";
- // ===========================================================
- // Fields
- // ===========================================================
- private int mTouchSlop;
- private float mLastMotionX, mLastMotionY;
- private float mInitialMotionX, mInitialMotionY;
- private boolean mIsBeingDragged = false;
- private State mState = State.RESET;
- private Mode mMode = Mode.getDefault();
- private Mode mCurrentMode;
- T mRefreshableView;
- private FrameLayout mRefreshableViewWrapper;
- private boolean mShowViewWhileRefreshing = true;
- private boolean mScrollingWhileRefreshingEnabled = false;
- private boolean mFilterTouchEvents = true;
- private boolean mOverScrollEnabled = true;
- private boolean mLayoutVisibilityChangesEnabled = true;
- private Interpolator mScrollAnimationInterpolator;
- private AnimationStyle mLoadingAnimationStyle = AnimationStyle.getDefault();
- private LoadingLayout mHeaderLayout;
- private LoadingLayout mFooterLayout;
- private OnRefreshListener<T> mOnRefreshListener;
- private OnRefreshListener2<T> mOnRefreshListener2;
- private OnPullEventListener<T> mOnPullEventListener;
- private SmoothScrollRunnable mCurrentSmoothScrollRunnable;
- // ===========================================================
- // Constructors
- // ===========================================================
- private Handler mHandler=new Handler();
-
- public PullToRefreshBase(Context context) {
- super(context);
- init(context, null);
- }
- public PullToRefreshBase(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context, attrs);
- }
- public PullToRefreshBase(Context context, Mode mode) {
- super(context);
- mMode = mode;
- init(context, null);
- }
- public PullToRefreshBase(Context context, Mode mode, AnimationStyle animStyle) {
- super(context);
- mMode = mode;
- mLoadingAnimationStyle = animStyle;
- init(context, null);
- }
- @Override
- public void addView(View child, int index, ViewGroup.LayoutParams params) {
- if (DEBUG) {
- Log.d(LOG_TAG, "addView: " + child.getClass().getSimpleName());
- }
- final T refreshableView = getRefreshableView();
- if (refreshableView instanceof ViewGroup) {
- ((ViewGroup) refreshableView).addView(child, index, params);
- } else {
- throw new UnsupportedOperationException("Refreshable View is not a ViewGroup so can't addView");
- }
- }
- @Override
- public final boolean demo() {
- if (mMode.showHeaderLoadingLayout() && isReadyForPullStart()) {
- smoothScrollToAndBack(-getHeaderSize() * 2);
- return true;
- } else if (mMode.showFooterLoadingLayout() && isReadyForPullEnd()) {
- smoothScrollToAndBack(getFooterSize() * 2);
- return true;
- }
- return false;
- }
- @Override
- public final Mode getCurrentMode() {
- return mCurrentMode;
- }
- @Override
- public final boolean getFilterTouchEvents() {
- return mFilterTouchEvents;
- }
- @Override
- public final ILoadingLayout getLoadingLayoutProxy() {
- return getLoadingLayoutProxy(true, true);
- }
- @Override
- public final ILoadingLayout getLoadingLayoutProxy(boolean includeStart, boolean includeEnd) {
- return createLoadingLayoutProxy(includeStart, includeEnd);
- }
- @Override
- public final Mode getMode() {
- return mMode;
- }
- @Override
- public final T getRefreshableView() {
- return mRefreshableView;
- }
- @Override
- public final boolean getShowViewWhileRefreshing() {
- return mShowViewWhileRefreshing;
- }
- @Override
- public final State getState() {
- return mState;
- }
- /**
- * @deprecated See {@link #isScrollingWhileRefreshingEnabled()}.
- */
- public final boolean isDisableScrollingWhileRefreshing() {
- return !isScrollingWhileRefreshingEnabled();
- }
- @Override
- public final boolean isPullToRefreshEnabled() {
- return mMode.permitsPullToRefresh();
- }
- @Override
- public final boolean isPullToRefreshOverScrollEnabled() {
- return VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD && mOverScrollEnabled
- && OverscrollHelper.isAndroidOverScrollEnabled(mRefreshableView);
- }
- @Override
- public final boolean isRefreshing() {
- return mState == State.REFRESHING || mState == State.MANUAL_REFRESHING;
- }
- @Override
- public final boolean isScrollingWhileRefreshingEnabled() {
- return mScrollingWhileRefreshingEnabled;
- }
- @Override
- public final boolean onInterceptTouchEvent(MotionEvent event) {
- if (!isPullToRefreshEnabled()) {
- return false;
- }
- final int action = event.getAction();
- if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
- mIsBeingDragged = false;
- return false;
- }
- if (action != MotionEvent.ACTION_DOWN && mIsBeingDragged) {
- return true;
- }
- switch (action) {
- case MotionEvent.ACTION_MOVE: {
- // If we're refreshing, and the flag is set. Eat all MOVE events
- if (!mScrollingWhileRefreshingEnabled && isRefreshing()) {
- return true;
- }
- if (isReadyForPull()) {
- final float y = event.getY(), x = event.getX();
- final float diff, oppositeDiff, absDiff;
- // We need to use the correct values, based on scroll
- // direction
- switch (getPullToRefreshScrollDirection()) {
- case HORIZONTAL:
- diff = x - mLastMotionX;
- oppositeDiff = y - mLastMotionY;
- break;
- case VERTICAL:
- default:
- diff = y - mLastMotionY;
- oppositeDiff = x - mLastMotionX;
- break;
- }
- absDiff = Math.abs(diff);
- if (absDiff > mTouchSlop && (!mFilterTouchEvents || absDiff > Math.abs(oppositeDiff))) {
- if (mMode.showHeaderLoadingLayout() && diff >= 1f && isReadyForPullStart()) {
- mLastMotionY = y;
- mLastMotionX = x;
- mIsBeingDragged = true;
- if (mMode == Mode.BOTH) {
- mCurrentMode = Mode.PULL_FROM_START;
- }
- } else if (mMode.showFooterLoadingLayout() && diff <= -1f && isReadyForPullEnd()) {
- mLastMotionY = y;
- mLastMotionX = x;
- mIsBeingDragged = true;
- if (mMode == Mode.BOTH) {
- mCurrentMode = Mode.PULL_FROM_END;
- }
- }
- }
- }
- break;
- }
- case MotionEvent.ACTION_DOWN: {
- if (isReadyForPull()) {
- mLastMotionY = mInitialMotionY = event.getY();
- mLastMotionX = mInitialMotionX = event.getX();
- mIsBeingDragged = false;
- }
- break;
- }
- }
- return mIsBeingDragged;
- }
- @Override
- public final void onRefreshComplete() {
- if (isRefreshing()) {
- setState(State.RESET);
- }
- }
-
- public final void onRefreshComplete(int delay) {
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (isRefreshing()) {
- setState(State.RESET);
- }
- }
- }, delay);
- }
-
- public final void onPullUpRefreshComplete() {
- if (isRefreshing()) {
- mCurrentMode=Mode.PULL_FROM_END;
- setState(State.RESET);
- }
- }
-
- public final void onPullDownRefreshComplete() {
- if (isRefreshing()) {
- mCurrentMode=Mode.PULL_FROM_START;
- setState(State.RESET);
- }
- }
- @Override
- public final boolean onTouchEvent(MotionEvent event) {
- if (!isPullToRefreshEnabled()) {
- return false;
- }
- // If we're refreshing, and the flag is set. Eat the event
- if (!mScrollingWhileRefreshingEnabled && isRefreshing()) {
- return true;
- }
- if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0) {
- return false;
- }
- switch (event.getAction()) {
- case MotionEvent.ACTION_MOVE: {
- if (mIsBeingDragged) {
- mLastMotionY = event.getY();
- mLastMotionX = event.getX();
- pullEvent();
- return true;
- }
- break;
- }
- case MotionEvent.ACTION_DOWN: {
- if (isReadyForPull()) {
- mLastMotionY = mInitialMotionY = event.getY();
- mLastMotionX = mInitialMotionX = event.getX();
- return true;
- }
- break;
- }
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP: {
- if (mIsBeingDragged) {
- mIsBeingDragged = false;
- if (mState == State.RELEASE_TO_REFRESH
- && (null != mOnRefreshListener || null != mOnRefreshListener2)) {
- setState(State.REFRESHING, true);
- return true;
- }
- // If we're already refreshing, just scroll back to the top
- if (isRefreshing()) {
- smoothScrollTo(0);
- return true;
- }
- // If we haven't returned by here, then we're not in a state
- // to pull, so just reset
- setState(State.RESET);
- return true;
- }
- break;
- }
- }
- return false;
- }
- public final void setScrollingWhileRefreshingEnabled(boolean allowScrollingWhileRefreshing) {
- mScrollingWhileRefreshingEnabled = allowScrollingWhileRefreshing;
- }
- /**
- * @deprecated See {@link #setScrollingWhileRefreshingEnabled(boolean)}
- */
- public void setDisableScrollingWhileRefreshing(boolean disableScrollingWhileRefreshing) {
- setScrollingWhileRefreshingEnabled(!disableScrollingWhileRefreshing);
- }
- @Override
- public final void setFilterTouchEvents(boolean filterEvents) {
- mFilterTouchEvents = filterEvents;
- }
- /**
- * @deprecated You should now call this method on the result of
- * {@link #getLoadingLayoutProxy()}.
- */
- public void setLastUpdatedLabel(CharSequence label) {
- getLoadingLayoutProxy().setLastUpdatedLabel(label);
- }
- /**
- * @deprecated You should now call this method on the result of
- * {@link #getLoadingLayoutProxy()}.
- */
- public void setLoadingDrawable(Drawable drawable) {
- getLoadingLayoutProxy().setLoadingDrawable(drawable);
- }
- /**
- * @deprecated You should now call this method on the result of
- * {@link #getLoadingLayoutProxy(boolean, boolean)}.
- */
- public void setLoadingDrawable(Drawable drawable, Mode mode) {
- getLoadingLayoutProxy(mode.showHeaderLoadingLayout(), mode.showFooterLoadingLayout()).setLoadingDrawable(
- drawable);
- }
- @Override
- public void setLongClickable(boolean longClickable) {
- getRefreshableView().setLongClickable(longClickable);
- }
- @Override
- public final void setMode(Mode mode) {
- if (mode != mMode) {
- if (DEBUG) {
- Log.d(LOG_TAG, "Setting mode to: " + mode);
- }
- mMode = mode;
- updateUIForMode();
- }
- }
- public void setOnPullEventListener(OnPullEventListener<T> listener) {
- mOnPullEventListener = listener;
- }
- @Override
- public final void setOnRefreshListener(OnRefreshListener<T> listener) {
- mOnRefreshListener = listener;
- mOnRefreshListener2 = null;
- }
- @Override
- public final void setOnRefreshListener(OnRefreshListener2<T> listener) {
- mOnRefreshListener2 = listener;
- mOnRefreshListener = null;
- }
- /**
- * @deprecated You should now call this method on the result of
- * {@link #getLoadingLayoutProxy()}.
- */
- public void setPullLabel(CharSequence pullLabel) {
- getLoadingLayoutProxy().setPullLabel(pullLabel);
- }
- /**
- * @deprecated You should now call this method on the result of
- * {@link #getLoadingLayoutProxy(boolean, boolean)}.
- */
- public void setPullLabel(CharSequence pullLabel, Mode mode) {
- getLoadingLayoutProxy(mode.showHeaderLoadingLayout(), mode.showFooterLoadingLayout()).setPullLabel(pullLabel);
- }
- /**
- * @param enable Whether Pull-To-Refresh should be used
- * @deprecated This simple calls setMode with an appropriate mode based on
- * the passed value.
- */
- public final void setPullToRefreshEnabled(boolean enable) {
- setMode(enable ? Mode.getDefault() : Mode.DISABLED);
- }
- @Override
- public final void setPullToRefreshOverScrollEnabled(boolean enabled) {
- mOverScrollEnabled = enabled;
- }
-
- @Override
- public final void setPullDownRefreshing() {
- if(isRefreshing()){
- return;
- }
- mCurrentMode=Mode.PULL_FROM_START;
- setRefreshing(true);
- }
-
- public final void setPullDownRefreshing(int delay) {
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- setPullDownRefreshing();
- }
- }, delay);
- }
-
-
- @Override
- public final void setPullUpRefreshing() {
- if(isRefreshing()){
- return;
- }
- mCurrentMode=Mode.PULL_FROM_END;
- setRefreshing(true);
- }
-
- public final void setPullUpRefreshing(int delay) {
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- setPullUpRefreshing();
- }
- }, delay);
- }
-
-
- @Override
- public final void setRefreshing(boolean doScroll) {
- if (!isRefreshing()) {
- setState(State.MANUAL_REFRESHING, doScroll);
- }
- }
- /**
- * @deprecated You should now call this method on the result of
- * {@link #getLoadingLayoutProxy()}.
- */
- public void setRefreshingLabel(CharSequence refreshingLabel) {
- getLoadingLayoutProxy().setRefreshingLabel(refreshingLabel);
- }
- /**
- * @deprecated You should now call this method on the result of
- * {@link #getLoadingLayoutProxy(boolean, boolean)}.
- */
- public void setRefreshingLabel(CharSequence refreshingLabel, Mode mode) {
- getLoadingLayoutProxy(mode.showHeaderLoadingLayout(), mode.showFooterLoadingLayout()).setRefreshingLabel(
- refreshingLabel);
- }
- /**
- * @deprecated You should now call this method on the result of
- * {@link #getLoadingLayoutProxy()}.
- */
- public void setReleaseLabel(CharSequence releaseLabel) {
- setReleaseLabel(releaseLabel, Mode.BOTH);
- }
- /**
- * @deprecated You should now call this method on the result of
- * {@link #getLoadingLayoutProxy(boolean, boolean)}.
- */
- public void setReleaseLabel(CharSequence releaseLabel, Mode mode) {
- getLoadingLayoutProxy(mode.showHeaderLoadingLayout(), mode.showFooterLoadingLayout()).setReleaseLabel(
- releaseLabel);
- }
- public void setScrollAnimationInterpolator(Interpolator interpolator) {
- mScrollAnimationInterpolator = interpolator;
- }
- @Override
- public final void setShowViewWhileRefreshing(boolean showView) {
- mShowViewWhileRefreshing = showView;
- }
- /**
- * @return Either {@link Orientation#VERTICAL} or
- * {@link Orientation#HORIZONTAL} depending on the scroll direction.
- */
- public abstract Orientation getPullToRefreshScrollDirection();
- final void setState(State state, final boolean... params) {
- mState = state;
- if (DEBUG) {
- Log.d(LOG_TAG, "State: " + mState.name());
- }
- switch (mState) {
- case RESET:
- onReset();
- break;
- case PULL_TO_REFRESH:
- onPullToRefresh();
- break;
- case RELEASE_TO_REFRESH:
- onReleaseToRefresh();
- break;
- case REFRESHING:
- case MANUAL_REFRESHING:
- onRefreshing(params[0]);
- break;
- case OVERSCROLLING:
- // NO-OP
- break;
- }
- // Call OnPullEventListener
- if (null != mOnPullEventListener) {
- mOnPullEventListener.onPullEvent(this, mState, mCurrentMode);
- }
- }
- /**
- * Used internally for adding view. Need because we override addView to
- * pass-through to the Refreshable View
- */
- protected final void addViewInternal(View child, int index, ViewGroup.LayoutParams params) {
- super.addView(child, index, params);
- }
- /**
- * Used internally for adding view. Need because we override addView to
- * pass-through to the Refreshable View
- */
- protected final void addViewInternal(View child, ViewGroup.LayoutParams params) {
- super.addView(child, -1, params);
- }
- protected LoadingLayout createLoadingLayout(Context context, Mode mode, TypedArray attrs) {
- LoadingLayout layout = mLoadingAnimationStyle.createLoadingLayout(context, mode,
- getPullToRefreshScrollDirection(), attrs);
- layout.setVisibility(View.INVISIBLE);
- return layout;
- }
- /**
- * Used internally for {@link #getLoadingLayoutProxy(boolean, boolean)}.
- * Allows derivative classes to include any extra LoadingLayouts.
- */
- protected LoadingLayoutProxy createLoadingLayoutProxy(final boolean includeStart, final boolean includeEnd) {
- LoadingLayoutProxy proxy = new LoadingLayoutProxy();
- if (includeStart && mMode.showHeaderLoadingLayout()) {
- proxy.addLayout(mHeaderLayout);
- }
- if (includeEnd && mMode.showFooterLoadingLayout()) {
- proxy.addLayout(mFooterLayout);
- }
- return proxy;
- }
- /**
- * This is implemented by derived classes to return the created View. If you
- * need to use a custom View (such as a custom ListView), override this
- * method and return an instance of your custom class.
- * <p/>
- * Be sure to set the ID of the view in this method, especially if you're
- * using a ListActivity or ListFragment.
- *
- * @param context Context to create view with
- * @param attrs AttributeSet from wrapped class. Means that anything you
- * include in the XML layout declaration will be routed to the
- * created View
- * @return New instance of the Refreshable View
- */
- protected abstract T createRefreshableView(Context context, AttributeSet attrs);
- protected final void disableLoadingLayoutVisibilityChanges() {
- mLayoutVisibilityChangesEnabled = false;
- }
- protected final LoadingLayout getFooterLayout() {
- return mFooterLayout;
- }
- protected final int getFooterSize() {
- return mFooterLayout.getContentSize();
- }
- protected final LoadingLayout getHeaderLayout() {
- return mHeaderLayout;
- }
- protected final int getHeaderSize() {
- return mHeaderLayout.getContentSize();
- }
- protected int getPullToRefreshScrollDuration() {
- return SMOOTH_SCROLL_DURATION_MS;
- }
- protected int getPullToRefreshScrollDurationLonger() {
- return SMOOTH_SCROLL_LONG_DURATION_MS;
- }
- protected FrameLayout getRefreshableViewWrapper() {
- return mRefreshableViewWrapper;
- }
- /**
- * Allows Derivative classes to handle the XML Attrs without creating a
- * TypedArray themsevles
- *
- * @param a - TypedArray of PullToRefresh Attributes
- */
- protected void handleStyledAttributes(TypedArray a) {
- }
- /**
- * Implemented by derived class to return whether the View is in a state
- * where the user can Pull to Refresh by scrolling from the end.
- *
- * @return true if the View is currently in the correct state (for example,
- * bottom of a ListView)
- */
- protected abstract boolean isReadyForPullEnd();
- /**
- * Implemented by derived class to return whether the View is in a state
- * where the user can Pull to Refresh by scrolling from the start.
- *
- * @return true if the View is currently the correct state (for example, top
- * of a ListView)
- */
- protected abstract boolean isReadyForPullStart();
- /**
- * Called by {@link #onRestoreInstanceState(Parcelable)} so that derivative
- * classes can handle their saved instance state.
- *
- * @param savedInstanceState - Bundle which contains saved instance state.
- */
- protected void onPtrRestoreInstanceState(Bundle savedInstanceState) {
- }
- /**
- * Called by {@link #onSaveInstanceState()} so that derivative classes can
- * save their instance state.
- *
- * @param saveState - Bundle to be updated with saved state.
- */
- protected void onPtrSaveInstanceState(Bundle saveState) {
- }
- /**
- * Called when the UI has been to be updated to be in the
- * {@link State#PULL_TO_REFRESH} state.
- */
- protected void onPullToRefresh() {
- switch (mCurrentMode) {
- case PULL_FROM_END:
- mFooterLayout.pullToRefresh();
- break;
- case PULL_FROM_START:
- mHeaderLayout.pullToRefresh();
- break;
- default:
- // NO-OP
- break;
- }
- }
- /**
- * Called when the UI has been to be updated to be in the
- * {@link State#REFRESHING} or {@link State#MANUAL_REFRESHING} state.
- *
- * @param doScroll - Whether the UI should scroll for this event.
- */
- protected void onRefreshing(final boolean doScroll) {
- if (mMode.showHeaderLoadingLayout()) {
- mHeaderLayout.refreshing();
- }
- if (mMode.showFooterLoadingLayout()) {
- mFooterLayout.refreshing();
- }
- if (doScroll) {
- if (mShowViewWhileRefreshing) {
- // Call Refresh Listener when the Scroll has finished
- OnSmoothScrollFinishedListener listener = new OnSmoothScrollFinishedListener() {
- @Override
- public void onSmoothScrollFinished() {
- callRefreshListener();
- }
- };
- switch (mCurrentMode) {
- case MANUAL_REFRESH_ONLY:
- case PULL_FROM_END:
- smoothScrollTo(getFooterSize(), listener);
- break;
- default:
- case PULL_FROM_START:
- smoothScrollTo(-getHeaderSize(), listener);
- break;
- }
- } else {
- smoothScrollTo(0);
- }
- } else {
- // We're not scrolling, so just call Refresh Listener now
- callRefreshListener();
- }
- }
- /**
- * Called when the UI has been to be updated to be in the
- * {@link State#RELEASE_TO_REFRESH} state.
- */
- protected void onReleaseToRefresh() {
- switch (mCurrentMode) {
- case PULL_FROM_END:
- mFooterLayout.releaseToRefresh();
- break;
- case PULL_FROM_START:
- mHeaderLayout.releaseToRefresh();
- break;
- default:
- // NO-OP
- break;
- }
- }
- /**
- * Called when the UI has been to be updated to be in the
- * {@link State#RESET} state.
- */
- protected void onReset() {
- mIsBeingDragged = false;
- mLayoutVisibilityChangesEnabled = true;
- // Always reset both layouts, just in case...
- mHeaderLayout.reset();
- mFooterLayout.reset();
- smoothScrollTo(0);
- mCurrentMode = (mMode != Mode.BOTH) ? mMode : Mode.PULL_FROM_START;
- }
- @Override
- protected final void onRestoreInstanceState(Parcelable state) {
- if (state instanceof Bundle) {
- Bundle bundle = (Bundle) state;
- setMode(Mode.mapIntToValue(bundle.getInt(STATE_MODE, 0)));
- mCurrentMode = Mode.mapIntToValue(bundle.getInt(STATE_CURRENT_MODE, 0));
- mScrollingWhileRefreshingEnabled = bundle.getBoolean(STATE_SCROLLING_REFRESHING_ENABLED, false);
- mShowViewWhileRefreshing = bundle.getBoolean(STATE_SHOW_REFRESHING_VIEW, true);
- // Let super Restore Itself
- super.onRestoreInstanceState(bundle.getParcelable(STATE_SUPER));
- State viewState = State.mapIntToValue(bundle.getInt(STATE_STATE, 0));
- if (viewState == State.REFRESHING || viewState == State.MANUAL_REFRESHING) {
- setState(viewState, true);
- }
- // Now let derivative classes restore their state
- onPtrRestoreInstanceState(bundle);
- return;
- }
- super.onRestoreInstanceState(state);
- }
- @Override
- protected final Parcelable onSaveInstanceState() {
- Bundle bundle = new Bundle();
- // Let derivative classes get a chance to save state first, that way we
- // can make sure they don't overrite any of our values
- onPtrSaveInstanceState(bundle);
- bundle.putInt(STATE_STATE, mState.getIntValue());
- bundle.putInt(STATE_MODE, mMode.getIntValue());
- bundle.putInt(STATE_CURRENT_MODE, mCurrentMode.getIntValue());
- bundle.putBoolean(STATE_SCROLLING_REFRESHING_ENABLED, mScrollingWhileRefreshingEnabled);
- bundle.putBoolean(STATE_SHOW_REFRESHING_VIEW, mShowViewWhileRefreshing);
- bundle.putParcelable(STATE_SUPER, super.onSaveInstanceState());
- return bundle;
- }
- @Override
- protected final void onSizeChanged(int w, int h, int oldw, int oldh) {
- if (DEBUG) {
- Log.d(LOG_TAG, String.format("onSizeChanged. W: %d, H: %d", w, h));
- }
- super.onSizeChanged(w, h, oldw, oldh);
- // We need to update the header/footer when our size changes
- refreshLoadingViewsSize();
- // Update the Refreshable View layout
- refreshRefreshableViewSize(w, h);
- /**
- * As we're currently in a Layout Pass, we need to schedule another one
- * to layout any changes we've made here
- */
- post(new Runnable() {
- @Override
- public void run() {
- requestLayout();
- }
- });
- }
- /**
- * Re-measure the Loading Views height, and adjust internal padding as
- * necessary
- */
- protected final void refreshLoadingViewsSize() {
- final int maximumPullScroll = (int) (getMaximumPullScroll() * 1.2f);
- int pLeft = getPaddingLeft();
- int pTop = getPaddingTop();
- int pRight = getPaddingRight();
- int pBottom = getPaddingBottom();
- switch (getPullToRefreshScrollDirection()) {
- case HORIZONTAL:
- if (mMode.showHeaderLoadingLayout()) {
- mHeaderLayout.setWidth(maximumPullScroll);
- pLeft = -maximumPullScroll;
- } else {
- pLeft = 0;
- }
- if (mMode.showFooterLoadingLayout()) {
- mFooterLayout.setWidth(maximumPullScroll);
- pRight = -maximumPullScroll;
- } else {
- pRight = 0;
- }
- break;
- case VERTICAL:
- if (mMode.showHeaderLoadingLayout()) {
- mHeaderLayout.setHeight(maximumPullScroll);
- pTop = -maximumPullScroll;
- } else {
- pTop = 0;
- }
- if (mMode.showFooterLoadingLayout()) {
- mFooterLayout.setHeight(maximumPullScroll);
- pBottom = -maximumPullScroll;
- } else {
- pBottom = 0;
- }
- break;
- }
- if (DEBUG) {
- Log.d(LOG_TAG, String.format("Setting Padding. L: %d, T: %d, R: %d, B: %d", pLeft, pTop, pRight, pBottom));
- }
- setPadding(pLeft, pTop, pRight, pBottom);
- }
- protected final void refreshRefreshableViewSize(int width, int height) {
- // We need to set the Height of the Refreshable View to the same as
- // this layout
- LayoutParams lp = (LayoutParams) mRefreshableViewWrapper.getLayoutParams();
- switch (getPullToRefreshScrollDirection()) {
- case HORIZONTAL:
- if (lp.width != width) {
- lp.width = width;
- mRefreshableViewWrapper.requestLayout();
- }
- break;
- case VERTICAL:
- if (lp.height != height) {
- lp.height = height;
- mRefreshableViewWrapper.requestLayout();
- }
- break;
- }
- }
- /**
- * Helper method which just calls scrollTo() in the correct scrolling
- * direction.
- *
- * @param value - New Scroll value
- */
- @TargetApi(VERSION_CODES.HONEYCOMB)
- protected final void setHeaderScroll(int value) {
- if (DEBUG) {
- Log.d(LOG_TAG, "setHeaderScroll: " + value);
- }
- // Clamp value to with pull scroll range
- final int maximumPullScroll = getMaximumPullScroll();
- value = Math.min(maximumPullScroll, Math.max(-maximumPullScroll, value));
- if (mLayoutVisibilityChangesEnabled) {
- if (value < 0) {
- mHeaderLayout.setVisibility(View.VISIBLE);
- } else if (value > 0) {
- mFooterLayout.setVisibility(View.VISIBLE);
- } else {
- mHeaderLayout.setVisibility(View.INVISIBLE);
- mFooterLayout.setVisibility(View.INVISIBLE);
- }
- }
- if (USE_HW_LAYERS) {
- /**
- * Use a Hardware Layer on the Refreshable View if we've scrolled at
- * all. We don't use them on the Header/Footer Views as they change
- * often, which would negate any HW layer performance boost.
- */
- ViewCompat.setLayerType(mRefreshableViewWrapper, value != 0 ? View.LAYER_TYPE_HARDWARE
- : View.LAYER_TYPE_NONE);
- }
- switch (getPullToRefreshScrollDirection()) {
- case VERTICAL:
- scrollTo(0, value);
- break;
- case HORIZONTAL:
- scrollTo(value, 0);
- break;
- }
- }
- /**
- * Smooth Scroll to position using the default duration of
- * {@value #SMOOTH_SCROLL_DURATION_MS} ms.
- *
- * @param scrollValue - Position to scroll to
- */
- protected final void smoothScrollTo(int scrollValue) {
- smoothScrollTo(scrollValue, getPullToRefreshScrollDuration());
- }
- /**
- * Smooth Scroll to position using the default duration of
- * {@value #SMOOTH_SCROLL_DURATION_MS} ms.
- *
- * @param scrollValue - Position to scroll to
- * @param listener - Listener for scroll
- */
- protected final void smoothScrollTo(int scrollValue, OnSmoothScrollFinishedListener listener) {
- smoothScrollTo(scrollValue, getPullToRefreshScrollDuration(), 0, listener);
- }
- /**
- * Smooth Scroll to position using the longer default duration of
- * {@value #SMOOTH_SCROLL_LONG_DURATION_MS} ms.
- *
- * @param scrollValue - Position to scroll to
- */
- protected final void smoothScrollToLonger(int scrollValue) {
- smoothScrollTo(scrollValue, getPullToRefreshScrollDurationLonger());
- }
- /**
- * Updates the View State when the mode has been set. This does not do any
- * checking that the mode is different to current state so always updates.
- */
- protected void updateUIForMode() {
- // We need to use the correct LayoutParam values, based on scroll
- // direction
- final LayoutParams lp = getLoadingLayoutLayoutParams();
- // Remove Header, and then add Header Loading View again if needed
- if (this == mHeaderLayout.getParent()) {
- removeView(mHeaderLayout);
- }
- if (mMode.showHeaderLoadingLayout()) {
- addViewInternal(mHeaderLayout, 0, lp);
- }
- // Remove Footer, and then add Footer Loading View again if needed
- if (this == mFooterLayout.getParent()) {
- removeView(mFooterLayout);
- }
- if (mMode.showFooterLoadingLayout()) {
- addViewInternal(mFooterLayout, lp);
- }
- // Hide Loading Views
- refreshLoadingViewsSize();
- // If we're not using Mode.BOTH, set mCurrentMode to mMode, otherwise
- // set it to pull down
- mCurrentMode = (mMode != Mode.BOTH) ? mMode : Mode.PULL_FROM_START;
- }
- private void addRefreshableView(Context context, T refreshableView) {
- mRefreshableViewWrapper = new FrameLayout(context);
- mRefreshableViewWrapper.addView(refreshableView, ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT);
- addViewInternal(mRefreshableViewWrapper, new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT));
- }
- private void callRefreshListener() {
- if (null != mOnRefreshListener) {
- mOnRefreshListener.onRefresh(this);
- } else if (null != mOnRefreshListener2) {
- if (mCurrentMode == Mode.PULL_FROM_START) {
- mOnRefreshListener2.onPullDownToRefresh(this);
- } else if (mCurrentMode == Mode.PULL_FROM_END) {
- mOnRefreshListener2.onPullUpToRefresh(this);
- }
- }
- }
- @SuppressWarnings("deprecation")
- private void init(Context context, AttributeSet attrs) {
- switch (getPullToRefreshScrollDirection()) {
- case HORIZONTAL:
- setOrientation(LinearLayout.HORIZONTAL);
- break;
- case VERTICAL:
- default:
- setOrientation(LinearLayout.VERTICAL);
- break;
- }
- setGravity(Gravity.CENTER);
- ViewConfiguration config = ViewConfiguration.get(context);
- mTouchSlop = config.getScaledTouchSlop();
- // Styleables from XML
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PullToRefresh);
- if (a.hasValue(R.styleable.PullToRefresh_ptrMode)) {
- mMode = Mode.mapIntToValue(a.getInteger(R.styleable.PullToRefresh_ptrMode, 0));
- }
- if (a.hasValue(R.styleable.PullToRefresh_ptrAnimationStyle)) {
- mLoadingAnimationStyle = AnimationStyle.mapIntToValue(a.getInteger(
- R.styleable.PullToRefresh_ptrAnimationStyle, 0));
- }
- // Refreshable View
- // By passing the attrs, we can add ListView/GridView params via XML
- mRefreshableView = createRefreshableView(context, attrs);
- addRefreshableView(context, mRefreshableView);
- // We need to create now layouts now
- mHeaderLayout = createLoadingLayout(context, Mode.PULL_FROM_START, a);
- mFooterLayout = createLoadingLayout(context, Mode.PULL_FROM_END, a);
- /**
- * Styleables from XML
- */
- if (a.hasValue(R.styleable.PullToRefresh_ptrRefreshableViewBackground)) {
- Drawable background = a.getDrawable(R.styleable.PullToRefresh_ptrRefreshableViewBackground);
- if (null != background) {
- mRefreshableView.setBackgroundDrawable(background);
- }
- } else if (a.hasValue(R.styleable.PullToRefresh_ptrAdapterViewBackground)) {
- Utils.warnDeprecation("ptrAdapterViewBackground", "ptrRefreshableViewBackground");
- Drawable background = a.getDrawable(R.styleable.PullToRefresh_ptrAdapterViewBackground);
- if (null != background) {
- mRefreshableView.setBackgroundDrawable(background);
- }
- }
- if (a.hasValue(R.styleable.PullToRefresh_ptrOverScroll)) {
- mOverScrollEnabled = a.getBoolean(R.styleable.PullToRefresh_ptrOverScroll, true);
- }
- if (a.hasValue(R.styleable.PullToRefresh_ptrScrollingWhileRefreshingEnabled)) {
- mScrollingWhileRefreshingEnabled = a.getBoolean(
- R.styleable.PullToRefresh_ptrScrollingWhileRefreshingEnabled, false);
- }
- // Let the derivative classes have a go at handling attributes, then
- // recycle them...
- handleStyledAttributes(a);
- a.recycle();
- // Finally update the UI for the modes
- updateUIForMode();
- }
- private boolean isReadyForPull() {
- switch (mMode) {
- case PULL_FROM_START:
- return isReadyForPullStart();
- case PULL_FROM_END:
- return isReadyForPullEnd();
- case BOTH:
- return isReadyForPullEnd() || isReadyForPullStart();
- default:
- return false;
- }
- }
- /**
- * Actions a Pull Event
- *
- * @return true if the Event has been handled, false if there has been no
- * change
- */
- private void pullEvent() {
- final int newScrollValue;
- final int itemDimension;
- final float initialMotionValue, lastMotionValue;
- switch (getPullToRefreshScrollDirection()) {
- case HORIZONTAL:
- initialMotionValue = mInitialMotionX;
- lastMotionValue = mLastMotionX;
- break;
- case VERTICAL:
- default:
- initialMotionValue = mInitialMotionY;
- lastMotionValue = mLastMotionY;
- break;
- }
- switch (mCurrentMode) {
- case PULL_FROM_END:
- newScrollValue = Math.round(Math.max(initialMotionValue - lastMotionValue, 0) / FRICTION);
- itemDimension = getFooterSize();
- break;
- case PULL_FROM_START:
- default:
- newScrollValue = Math.round(Math.min(initialMotionValue - lastMotionValue, 0) / FRICTION);
- itemDimension = getHeaderSize();
- break;
- }
- setHeaderScroll(newScrollValue);
- if (newScrollValue != 0 && !isRefreshing()) {
- float scale = Math.abs(newScrollValue) / (float) itemDimension;
- switch (mCurrentMode) {
- case PULL_FROM_END:
- mFooterLayout.onPull(scale);
- break;
- case PULL_FROM_START:
- default:
- mHeaderLayout.onPull(scale);
- break;
- }
- if (mState != State.PULL_TO_REFRESH && itemDimension >= Math.abs(newScrollValue)) {
- setState(State.PULL_TO_REFRESH);
- } else if (mState == State.PULL_TO_REFRESH && itemDimension < Math.abs(newScrollValue)) {
- setState(State.RELEASE_TO_REFRESH);
- }
- }
- }
- @TargetApi(VERSION_CODES.FROYO)
- private LayoutParams getLoadingLayoutLayoutParams() {
- switch (getPullToRefreshScrollDirection()) {
- case HORIZONTAL:
- return new LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.MATCH_PARENT);
- case VERTICAL:
- default:
- return new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.WRAP_CONTENT);
- }
- }
- private int getMaximumPullScroll() {
- switch (getPullToRefreshScrollDirection()) {
- case HORIZONTAL:
- return Math.round(getWidth() / FRICTION);
- case VERTICAL:
- default:
- return Math.round(getHeight() / FRICTION);
- }
- }
- /**
- * Smooth Scroll to position using the specific duration
- *
- * @param scrollValue - Position to scroll to
- * @param duration - Duration of animation in milliseconds
- */
- private final void smoothScrollTo(int scrollValue, long duration) {
- smoothScrollTo(scrollValue, duration, 0, null);
- }
- private final void smoothScrollTo(int newScrollValue, long duration, long delayMillis,
- OnSmoothScrollFinishedListener listener) {
- if (null != mCurrentSmoothScrollRunnable) {
- mCurrentSmoothScrollRunnable.stop();
- }
- final int oldScrollValue;
- switch (getPullToRefreshScrollDirection()) {
- case HORIZONTAL:
- oldScrollValue = getScrollX();
- break;
- case VERTICAL:
- default:
- oldScrollValue = getScrollY();
- break;
- }
- if (oldScrollValue != newScrollValue) {
- if (null == mScrollAnimationInterpolator) {
- // Default interpolator is a Decelerate Interpolator
- mScrollAnimationInterpolator = new DecelerateInterpolator();
- }
- mCurrentSmoothScrollRunnable = new SmoothScrollRunnable(oldScrollValue, newScrollValue, duration, listener);
- if (delayMillis > 0) {
- postDelayed(mCurrentSmoothScrollRunnable, delayMillis);
- } else {
- post(mCurrentSmoothScrollRunnable);
- }
- }
- }
- private final void smoothScrollToAndBack(int y) {
- smoothScrollTo(y, SMOOTH_SCROLL_DURATION_MS, 0, new OnSmoothScrollFinishedListener() {
- @Override
- public void onSmoothScrollFinished() {
- smoothScrollTo(0, SMOOTH_SCROLL_DURATION_MS, DEMO_SCROLL_INTERVAL, null);
- }
- });
- }
- public static enum AnimationStyle {
- /**
- * This is the default for Android-PullToRefresh. Allows you to use any
- * drawable, which is automatically rotated and used as a Progress Bar.
- */
- ROTATE,
- /**
- * This is the old default, and what is commonly used on iOS. Uses an
- * arrow image which flips depending on where the user has scrolled.
- */
- FLIP;
- static AnimationStyle getDefault() {
- return ROTATE;
- }
- /**
- * Maps an int to a specific mode. This is needed when saving state, or
- * inflating the view from XML where the mode is given through a attr
- * int.
- *
- * @param modeInt - int to map a Mode to
- * @return Mode that modeInt maps to, or ROTATE by default.
- */
- static AnimationStyle mapIntToValue(int modeInt) {
- switch (modeInt) {
- case 0x0:
- default:
- return ROTATE;
- case 0x1:
- return FLIP;
- }
- }
- LoadingLayout createLoadingLayout(Context context, Mode mode, Orientation scrollDirection, TypedArray attrs) {
- switch (this) {
- case ROTATE:
- default:
- return new RotateLoadingLayout(context, mode, scrollDirection, attrs);
- case FLIP:
- return new FlipLoadingLayout(context, mode, scrollDirection, attrs);
- }
- }
- }
- public static enum Mode {
- /**
- * Disable all Pull-to-Refresh gesture and Refreshing handling
- */
- DISABLED(0x0),
- /**
- * Only allow the user to Pull from the start of the Refreshable View to
- * refresh. The start is either the Top or Left, depending on the
- * scrolling direction.
- */
- PULL_FROM_START(0x1),
- /**
- * Only allow the user to Pull from the end of the Refreshable View to
- * refresh. The start is either the Bottom or Right, depending on the
- * scrolling direction.
- */
- PULL_FROM_END(0x2),
- /**
- * Allow the user to both Pull from the start, from the end to refresh.
- */
- BOTH(0x3),
- /**
- * Disables Pull-to-Refresh gesture handling, but allows manually
- * setting the Refresh state via
- */
- MANUAL_REFRESH_ONLY(0x4);
- /**
- * @deprecated Use {@link #PULL_FROM_START} from now on.
- */
- public static Mode PULL_DOWN_TO_REFRESH = Mode.PULL_FROM_START;
- /**
- * @deprecated Use {@link #PULL_FROM_END} from now on.
- */
- public static Mode PULL_UP_TO_REFRESH = Mode.PULL_FROM_END;
- /**
- * Maps an int to a specific mode. This is needed when saving state, or
- * inflating the view from XML where the mode is given through a attr
- * int.
- *
- * @param modeInt - int to map a Mode to
- * @return Mode that modeInt maps to, or PULL_FROM_START by default.
- */
- static Mode mapIntToValue(final int modeInt) {
- for (Mode value : Mode.values()) {
- if (modeInt == value.getIntValue()) {
- return value;
- }
- }
- // If not, return default
- return getDefault();
- }
- static Mode getDefault() {
- return PULL_FROM_START;
- }
- private int mIntValue;
- // The modeInt values need to match those from attrs.xml
- Mode(int modeInt) {
- mIntValue = modeInt;
- }
- /**
- * @return true if the mode permits Pull-to-Refresh
- */
- boolean permitsPullToRefresh() {
- return !(this == DISABLED || this == MANUAL_REFRESH_ONLY);
- }
- /**
- * @return true if this mode wants the Loading Layout Header to be shown
- */
- public boolean showHeaderLoadingLayout() {
- return this == PULL_FROM_START || this == BOTH;
- }
- /**
- * @return true if this mode wants the Loading Layout Footer to be shown
- */
- public boolean showFooterLoadingLayout() {
- return this == PULL_FROM_END || this == BOTH || this == MANUAL_REFRESH_ONLY;
- }
- int getIntValue() {
- return mIntValue;
- }
- }
- // ===========================================================
- // Inner, Anonymous Classes, and Enumerations
- // ===========================================================
- /**
- * Simple Listener that allows you to be notified when the user has scrolled
- * to the end of the AdapterView. See (
- * {@link PullToRefreshAdapterViewBase#setOnLastItemVisibleListener}.
- *
- * @author Chris Banes
- */
- public static interface OnLastItemVisibleListener {
- /**
- * Called when the user has scrolled to the end of the list
- */
- public void onLastItemVisible();
- }
- /**
- * Listener that allows you to be notified when the user has started or
- * finished a touch event. Useful when you want to append extra UI events
- * (such as sounds). See (
- * {@link PullToRefreshAdapterViewBase#setOnPullEventListener}.
- *
- * @author Chris Banes
- */
- public static interface OnPullEventListener<V extends View> {
- /**
- * Called when the internal state has been changed, usually by the user
- * pulling.
- *
- * @param refreshView - View which has had it's state change.
- * @param state - The new state of View.
- * @param direction - One of {@link Mode#PULL_FROM_START} or
- * {@link Mode#PULL_FROM_END} depending on which direction
- * the user is pulling. Only useful when <var>state</var> is
- * {@link State#PULL_TO_REFRESH} or
- * {@link State#RELEASE_TO_REFRESH}.
- */
- public void onPullEvent(final PullToRefreshBase<V> refreshView, State state, Mode direction);
- }
- /**
- * Simple Listener to listen for any callbacks to Refresh.
- *
- * @author Chris Banes
- */
- public static interface OnRefreshListener<V extends View> {
- /**
- * onRefresh will be called for both a Pull from start, and Pull from
- * end
- */
- public void onRefresh(final PullToRefreshBase<V> refreshView);
- }
- /**
- * An advanced version of the Listener to listen for callbacks to Refresh.
- * This listener is different as it allows you to differentiate between Pull
- * Ups, and Pull Downs.
- *
- * @author Chris Banes
- */
- public static interface OnRefreshListener2<V extends View> {
- // These methods need renaming to START/END rather than DOWN/UP
- /**
- * onPullDownToRefresh will be called only when the user has Pulled from
- * the start, and released.
- */
- public void onPullDownToRefresh(final PullToRefreshBase<V> refreshView);
- /**
- * onPullUpToRefresh will be called only when the user has Pulled from
- * the end, and released.
- */
- public void onPullUpToRefresh(final PullToRefreshBase<V> refreshView);
- }
- public static enum Orientation {
- VERTICAL, HORIZONTAL;
- }
- public static enum State {
- /**
- * When the UI is in a state which means that user is not interacting
- * with the Pull-to-Refresh function.
- */
- RESET(0x0),
- /**
- * When the UI is being pulled by the user, but has not been pulled far
- * enough so that it refreshes when released.
- */
- PULL_TO_REFRESH(0x1),
- /**
- * When the UI is being pulled by the user, and <strong>has</strong>
- * been pulled far enough so that it will refresh when released.
- */
- RELEASE_TO_REFRESH(0x2),
- /**
- * When the UI is currently refreshing, caused by a pull gesture.
- */
- REFRESHING(0x8),
- /**
- * When the UI is currently refreshing, caused by a call to
- */
- MANUAL_REFRESHING(0x9),
- /**
- * When the UI is currently overscrolling, caused by a fling on the
- * Refreshable View.
- */
- OVERSCROLLING(0x10);
- /**
- * Maps an int to a specific state. This is needed when saving state.
- *
- * @param stateInt - int to map a State to
- * @return State that stateInt maps to
- */
- static State mapIntToValue(final int stateInt) {
- for (State value : State.values()) {
- if (stateInt == value.getIntValue()) {
- return value;
- }
- }
- // If not, return default
- return RESET;
- }
- private int mIntValue;
- State(int intValue) {
- mIntValue = intValue;
- }
- int getIntValue() {
- return mIntValue;
- }
- }
- final class SmoothScrollRunnable implements Runnable {
- private final Interpolator mInterpolator;
- private final int mScrollToY;
- private final int mScrollFromY;
- private final long mDuration;
- private OnSmoothScrollFinishedListener mListener;
- private boolean mContinueRunning = true;
- private long mStartTime = -1;
- private int mCurrentY = -1;
- public SmoothScrollRunnable(int fromY, int toY, long duration, OnSmoothScrollFinishedListener listener) {
- mScrollFromY = fromY;
- mScrollToY = toY;
- mInterpolator = mScrollAnimationInterpolator;
- mDuration = duration;
- mListener = listener;
- }
- @Override
- public void run() {
- /**
- * Only set mStartTime if this is the first time we're starting,
- * else actually calculate the Y delta
- */
- if (mStartTime == -1) {
- mStartTime = System.currentTimeMillis();
- } else {
- /**
- * We do do all calculations in long to reduce software float
- * calculations. We use 1000 as it gives us good accuracy and
- * small rounding errors
- */
- long normalizedTime = (1000 * (System.currentTimeMillis() - mStartTime)) / mDuration;
- normalizedTime = Math.max(Math.min(normalizedTime, 1000), 0);
- final int deltaY = Math.round((mScrollFromY - mScrollToY)
- * mInterpolator.getInterpolation(normalizedTime / 1000f));
- mCurrentY = mScrollFromY - deltaY;
- setHeaderScroll(mCurrentY);
- }
- // If we're not at the target Y, keep going...
- if (mContinueRunning && mScrollToY != mCurrentY) {
- ViewCompat.postOnAnimation(PullToRefreshBase.this, this);
- } else {
- if (null != mListener) {
- mListener.onSmoothScrollFinished();
- }
- }
- }
- public void stop() {
- mContinueRunning = false;
- removeCallbacks(this);
- }
- }
- static interface OnSmoothScrollFinishedListener {
- void onSmoothScrollFinished();
- }
- }
|