├── settings.gradle
├── header.png
├── library
├── gradle.properties
├── AndroidManifest.xml
├── src
│ └── uk
│ │ └── co
│ │ └── senab
│ │ └── actionbarpulltorefresh
│ │ └── library
│ │ ├── sdk
│ │ ├── CompatV16.java
│ │ ├── CompatBase.java
│ │ ├── Compat.java
│ │ └── CompatV11.java
│ │ ├── EnvironmentDelegate.java
│ │ ├── viewdelegates
│ │ ├── WebViewDelegate.java
│ │ ├── ScrollYDelegate.java
│ │ ├── ViewDelegate.java
│ │ └── AbsListViewDelegate.java
│ │ ├── listeners
│ │ ├── OnRefreshListener.java
│ │ └── HeaderViewListener.java
│ │ ├── HeaderTransformer.java
│ │ ├── InstanceCreationUtils.java
│ │ ├── Options.java
│ │ ├── ActionBarPullToRefresh.java
│ │ ├── PullToRefreshLayout.java
│ │ ├── DefaultHeaderTransformer.java
│ │ └── PullToRefreshAttacher.java
├── project.properties
├── res
│ ├── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── ids.xml
│ │ ├── styles.xml
│ │ ├── pull_refresh_strings.xml
│ │ └── attrs.xml
│ ├── values-ru
│ │ └── pull_refresh_strings.xml
│ ├── values-zh
│ │ └── pull_refresh_strings.xml
│ ├── values-ar
│ │ └── pull_refresh_strings.xml
│ ├── values-fi
│ │ └── pull_refresh_strings.xml
│ ├── values-iw
│ │ └── pull_refresh_strings.xml
│ ├── values-ja
│ │ └── pull_refresh_strings.xml
│ ├── values-ko
│ │ └── pull_refresh_strings.xml
│ ├── values-he
│ │ └── pull_refresh_strings.xml
│ ├── values-lt
│ │ └── pull_refresh_strings.xml
│ ├── values-cs
│ │ └── pull_refresh_strings.xml
│ ├── values-it
│ │ └── pull_refresh_strings.xml
│ ├── values-nl
│ │ └── pull_refresh_strings.xml
│ ├── values-pl
│ │ └── pull_refresh_strings.xml
│ ├── values-pt
│ │ └── pull_refresh_strings.xml
│ ├── values-de
│ │ └── pull_refresh_strings.xml
│ ├── values-fr
│ │ └── pull_refresh_strings.xml
│ ├── values-sv
│ │ └── pull_refresh_strings.xml
│ ├── values-pt-rBR
│ │ └── pull_refresh_strings.xml
│ ├── values-sk
│ │ └── pull_refresh_strings.xml
│ ├── values-ro
│ │ └── pull_refresh_strings.xml
│ ├── values-es
│ │ └── pull_refresh_strings.xml
│ └── layout
│ │ └── default_header.xml
└── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── samples
└── stock
│ ├── res
│ ├── drawable-hdpi
│ │ └── ic_launcher.png
│ ├── drawable-ldpi
│ │ └── ic_launcher.png
│ ├── drawable-mdpi
│ │ └── ic_launcher.png
│ ├── drawable-xhdpi
│ │ └── ic_launcher.png
│ ├── drawable-xxhdpi
│ │ └── ic_launcher.png
│ ├── values
│ │ ├── colors.xml
│ │ ├── styles.xml
│ │ └── strings.xml
│ ├── anim
│ │ ├── slide_in_top.xml
│ │ └── slide_out_top.xml
│ ├── layout
│ │ ├── activity_fragment_tabs.xml
│ │ ├── activity_webview.xml
│ │ ├── activity_gridview.xml
│ │ ├── activity_scrollview.xml
│ │ ├── activity_listview_empty.xml
│ │ ├── customised_header.xml
│ │ └── layout_fragment.xml
│ └── menu
│ │ └── sample.xml
│ ├── project.properties
│ ├── src
│ └── uk
│ │ └── co
│ │ └── senab
│ │ └── actionbarpulltorefresh
│ │ └── samples
│ │ └── stock
│ │ ├── Constants.java
│ │ ├── BaseSampleActivity.java
│ │ ├── ScrollViewActivity.java
│ │ ├── WebViewActivity.java
│ │ ├── MainActivity.java
│ │ ├── ListViewActivity.java
│ │ ├── FragmentTabsActivity.java
│ │ └── GridViewActivity.java
│ ├── AndroidManifest.xml
│ └── build.gradle
├── local-deploy.sh
├── ghpages-deploy.sh
├── .gitignore
├── gradle.properties
├── README.md
└── gradlew
/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'library'
2 | include 'samples/stock'
3 |
--------------------------------------------------------------------------------
/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/ActionBar-PullToRefresh/master/header.png
--------------------------------------------------------------------------------
/library/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=ActionBar-PullToRefresh Library
2 | POM_ARTIFACT_ID=library
3 | POM_PACKAGING=aar
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/ActionBar-PullToRefresh/master/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/samples/stock/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/ActionBar-PullToRefresh/master/samples/stock/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/samples/stock/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/ActionBar-PullToRefresh/master/samples/stock/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/samples/stock/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/ActionBar-PullToRefresh/master/samples/stock/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/samples/stock/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/ActionBar-PullToRefresh/master/samples/stock/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/samples/stock/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/ActionBar-PullToRefresh/master/samples/stock/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/library/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/local-deploy.sh:
--------------------------------------------------------------------------------
1 |
2 | VERSION=0.9.7
3 | GROUP_ID=org.wordpress
4 |
5 | # Push to the local maven repo
6 | mvn install:install-file -Dfile=library/build/libs/library.aar \
7 | -DgroupId=$GROUP_ID -DartifactId=pulltorefresh-main -Dpackaging=aar \
8 | -Dversion=$VERSION
9 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Dec 19 13:13:21 CET 2013
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
7 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/sdk/CompatV16.java:
--------------------------------------------------------------------------------
1 | package uk.co.senab.actionbarpulltorefresh.library.sdk;
2 |
3 | import android.view.View;
4 |
5 | class CompatV16 {
6 |
7 | static void postOnAnimation(View view, Runnable runnable) {
8 | view.postOnAnimation(runnable);
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/ghpages-deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | LOCAL_GH_PAGES=file:///Users/max/work/automattic/WordPress-Android-gh-pages/
4 | GROUP_ID=org.wordpress
5 | VERSION=0.9.7
6 |
7 | # Main library
8 | mvn deploy:deploy-file -Dfile=library/build/libs/library.aar \
9 | -Durl=$LOCAL_GH_PAGES -DgroupId=$GROUP_ID \
10 | -DartifactId=pulltorefresh-main -Dversion=$VERSION
11 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/sdk/CompatBase.java:
--------------------------------------------------------------------------------
1 | package uk.co.senab.actionbarpulltorefresh.library.sdk;
2 |
3 | import android.view.View;
4 |
5 | class CompatBase {
6 |
7 | static void setAlpha(View view, float alpha) {
8 | // NO-OP
9 | }
10 |
11 | static void postOnAnimation(View view, Runnable runnable) {
12 | view.postDelayed(runnable, 10l);
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #Android generated
2 | bin
3 | gen
4 | gen*
5 |
6 | #Eclipse
7 | .project
8 | .classpath
9 | .settings
10 |
11 | #IntelliJ IDEA
12 | .idea
13 | *.iml
14 | *.ipr
15 | *.iws
16 | out
17 |
18 | #Maven
19 | target
20 | release.properties
21 | pom.xml.*
22 |
23 | #Ant
24 | build.xml
25 | local.properties
26 | proguard.cfg
27 |
28 | #Gradle
29 | .gradle
30 | build
31 |
32 | #OSX
33 | .DS_Store
34 |
35 | #Personal Files
36 | signing.properties
--------------------------------------------------------------------------------
/library/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-18
15 | android.library=true
16 |
--------------------------------------------------------------------------------
/samples/stock/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-18
15 | android.library.reference.1=../../library
16 |
17 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | VERSION_NAME=0.9.7
2 | VERSION_CODE=96
3 | GROUP=com.github.chrisbanes.actionbarpulltorefresh
4 |
5 | POM_DESCRIPTION=A modern implementation of the pull-to-refresh for Android
6 | POM_URL=https://github.com/chrisbanes/ActionBar-PullToRefresh
7 | POM_SCM_URL=https://github.com/chrisbanes/ActionBar-PullToRefresh
8 | POM_SCM_CONNECTION=scm:git@github.com:chrisbanes/ActionBar-PullToRefresh.git
9 | POM_SCM_DEV_CONNECTION=scm:git@github.com:chrisbanes/ActionBar-PullToRefresh.git
10 | POM_LICENCE_NAME=The Apache Software License, Version 2.0
11 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
12 | POM_LICENCE_DIST=repo
13 | POM_DEVELOPER_ID=chrisbanes
14 | POM_DEVELOPER_NAME=Chris Banes
15 |
16 | ANDROID_BUILD_TARGET_SDK_VERSION=19
17 | ANDROID_BUILD_TOOLS_VERSION=19
18 | ANDROID_BUILD_SDK_VERSION=19
19 |
--------------------------------------------------------------------------------
/library/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 | #FF33B5E5
19 |
--------------------------------------------------------------------------------
/samples/stock/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
20 | #99CC00
21 |
--------------------------------------------------------------------------------
/library/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | 4dp
20 |
21 |
--------------------------------------------------------------------------------
/samples/stock/src/uk/co/senab/actionbarpulltorefresh/samples/stock/Constants.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.samples.stock;
18 |
19 | public class Constants {
20 |
21 | public static final int SIMULATED_REFRESH_LENGTH = 5000;
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/library/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/samples/stock/res/anim/slide_in_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
--------------------------------------------------------------------------------
/samples/stock/res/anim/slide_out_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
--------------------------------------------------------------------------------
/samples/stock/res/layout/activity_fragment_tabs.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
--------------------------------------------------------------------------------
/library/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
22 |
23 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/sdk/Compat.java:
--------------------------------------------------------------------------------
1 | package uk.co.senab.actionbarpulltorefresh.library.sdk;
2 |
3 | import android.os.Build;
4 | import android.view.View;
5 |
6 | public class Compat {
7 |
8 | public static void setAlpha(View view, float alpha) {
9 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
10 | CompatV11.setAlpha(view, alpha);
11 | } else {
12 | CompatBase.setAlpha(view, alpha);
13 | }
14 | }
15 |
16 | public static void postOnAnimation(View view, Runnable runnable) {
17 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
18 | CompatV16.postOnAnimation(view, runnable);
19 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
20 | CompatV11.postOnAnimation(view, runnable);
21 | } else {
22 | CompatBase.postOnAnimation(view, runnable);
23 | }
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/library/res/values-ru/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | потяните
19 | отпустите
20 | обновление
21 |
22 |
--------------------------------------------------------------------------------
/library/res/values-zh/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | 下拉刷新…
20 | 放开以刷新…
21 | 正在载入…
22 |
23 |
--------------------------------------------------------------------------------
/library/res/values-ar/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | اسحب للتحديث…
19 | اترك للتحديث…
20 | تحميل…
21 |
22 |
--------------------------------------------------------------------------------
/library/res/values-fi/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | Päivitä vetämällä alas…
19 | Päivitä vapauttamalla…
20 | Päivitetään…
21 |
--------------------------------------------------------------------------------
/library/res/values-iw/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | משוך לרענון…
19 | שחרר לרענון…
20 | טוען…
21 |
22 |
--------------------------------------------------------------------------------
/library/res/values-ja/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | 画面を引っ張って…
20 | 指を離して更新…
21 | 読み込み中…
22 |
23 |
--------------------------------------------------------------------------------
/library/res/values-ko/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | 당겨서 새로 고침…
20 | 놓아서 새로 고침…
21 | 로드 중…
22 |
23 |
--------------------------------------------------------------------------------
/library/res/values/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 | Pull to refresh…
20 | Release to refresh…
21 | Loading…
22 |
23 |
24 |
--------------------------------------------------------------------------------
/library/res/values-he/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | משוך לרענון…
20 | שחרר לרענון…
21 | טוען…
22 |
23 |
--------------------------------------------------------------------------------
/library/res/values-lt/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | Traukite, kad atnaujinti…
19 | Paleiskite, kad atnaujinti…
20 | Kraunama…
21 |
22 |
--------------------------------------------------------------------------------
/library/res/values-cs/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | Tažením aktualizujete…
19 | Uvolněním aktualizujete…
20 | Načítání…
21 |
22 |
--------------------------------------------------------------------------------
/library/res/values-it/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | Tira per aggiornare…
19 | Rilascia per aggionare…
20 | Caricamento…
21 |
22 |
--------------------------------------------------------------------------------
/library/res/values-nl/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | Sleep om te vernieuwen…
19 | Loslaten om te vernieuwen…
20 | Laden…
21 |
22 |
--------------------------------------------------------------------------------
/library/res/values-pl/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | Pociągnij, aby odświeżyć…
19 | Puść, aby odświeżyć…
20 | Wczytywanie…
21 |
22 |
--------------------------------------------------------------------------------
/library/res/values-pt/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | Puxe para atualizar…
19 | Liberação para atualizar…
20 | A carregar…
21 |
22 |
--------------------------------------------------------------------------------
/library/res/values-de/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | Ziehen zum Aktualisieren…
19 | Loslassen zum Aktualisieren…
20 | Laden…
21 |
22 |
--------------------------------------------------------------------------------
/library/res/values-fr/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | Tirez pour rafraîchir…
19 | Relâcher pour rafraîchir…
20 | Chargement…
21 |
22 |
--------------------------------------------------------------------------------
/library/res/values-sv/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 | Dra nedåt om du vill uppdatera
20 | Släpp om du vill uppdatera
21 | Uppdaterar…
22 |
23 |
24 |
--------------------------------------------------------------------------------
/library/res/values-pt-rBR/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | Puxe para atualizar…
20 | Libere para atualizar…
21 | Carregando…
22 |
23 |
--------------------------------------------------------------------------------
/library/res/values-sk/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | Potiahnite pre načítanie...
19 | Uvoľnite pre načítanie...
20 | Načítavanie...
21 |
22 |
--------------------------------------------------------------------------------
/library/res/values-ro/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | Trage pentru a reîmprospăta…
19 | Eliberează pentru a reîmprospăta…
20 | Încărcare…
21 |
22 |
--------------------------------------------------------------------------------
/library/res/values-es/pull_refresh_strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | Desliza el dedo hacia abajo para actualizar.
20 | Soltar para actualizar…
21 | Cargando…
22 |
23 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | }
5 |
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:0.10.+'
8 | }
9 | }
10 | repositories {
11 | mavenCentral()
12 | }
13 |
14 | apply plugin: 'android-library'
15 |
16 | dependencies {
17 | compile 'com.github.castorflex.smoothprogressbar:library:0.3.3'
18 | }
19 |
20 | android {
21 | lintOptions {
22 | abortOnError false
23 | }
24 |
25 | compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION)
26 | buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
27 |
28 | defaultConfig {
29 | // This should be 14, but is 7 because extra-abc/extra-abs depend on this library
30 | minSdkVersion 7
31 | targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION)
32 | }
33 |
34 | sourceSets {
35 | main {
36 | manifest.srcFile 'AndroidManifest.xml'
37 | java.srcDirs = ['src']
38 | res.srcDirs = ['res']
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/samples/stock/res/menu/sample.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/sdk/CompatV11.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.library.sdk;
18 |
19 | import android.animation.ValueAnimator;
20 | import android.view.View;
21 |
22 | class CompatV11 {
23 |
24 | static void setAlpha(View view, float alpha) {
25 | view.setAlpha(alpha);
26 | }
27 |
28 | static void postOnAnimation(View view, Runnable runnable) {
29 | view.postDelayed(runnable, ValueAnimator.getFrameDelay());
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/EnvironmentDelegate.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.library;
18 |
19 | import android.app.Activity;
20 | import android.content.Context;
21 |
22 | /**
23 | * This is used to provide platform and environment specific functionality for the Attacher.
24 | */
25 | public interface EnvironmentDelegate {
26 |
27 | /**
28 | * @return Context which should be used for inflating the header layout
29 | */
30 | public Context getContextForInflater(Activity activity);
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/viewdelegates/WebViewDelegate.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.library.viewdelegates;
18 |
19 | import android.view.View;
20 | import android.webkit.WebView;
21 |
22 | /**
23 | * FIXME
24 | */
25 | public class WebViewDelegate implements ViewDelegate {
26 |
27 | public static final Class[] SUPPORTED_VIEW_CLASSES = { WebView.class };
28 |
29 | @Override
30 | public boolean isReadyForPull(View view, float x, float y) {
31 | return view.getScrollY() <= 0;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/listeners/OnRefreshListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.library.listeners;
18 |
19 | import android.view.View;
20 |
21 | /**
22 | * Simple Listener to listen for any callbacks to Refresh.
23 | */
24 | public interface OnRefreshListener {
25 | /**
26 | * Called when the user has initiated a refresh by pulling.
27 | *
28 | * @param view
29 | * - View which the user has started the refresh from.
30 | */
31 | public void onRefreshStarted(View view);
32 | }
33 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/viewdelegates/ScrollYDelegate.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.library.viewdelegates;
18 |
19 | import android.view.View;
20 | import android.widget.ScrollView;
21 |
22 | /**
23 | * FIXME
24 | */
25 | public class ScrollYDelegate implements ViewDelegate {
26 |
27 | public static final Class[] SUPPORTED_VIEW_CLASSES = { ScrollView.class };
28 |
29 | @Override
30 | public boolean isReadyForPull(View view, float x, float y) {
31 | return view.getScrollY() <= 0;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/samples/stock/res/layout/activity_webview.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
23 |
24 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/samples/stock/res/layout/activity_gridview.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
23 |
24 |
34 |
35 |
--------------------------------------------------------------------------------
/samples/stock/res/layout/activity_scrollview.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
24 |
25 |
31 |
32 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/samples/stock/res/layout/activity_listview_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
23 |
24 |
28 |
29 |
36 |
37 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/viewdelegates/ViewDelegate.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.library.viewdelegates;
18 |
19 | import android.view.View;
20 |
21 | /**
22 | * ViewDelegates are what are used to de-couple the Attacher from the different types of
23 | * scrollable views.
24 | */
25 | public interface ViewDelegate {
26 |
27 | /**
28 | * Allows you to provide support for View which do not have built-in
29 | * support. In this method you should cast view to it's
30 | * native class, and check if it is scrolled to the top.
31 | *
32 | * @param view
33 | * The view which has should be checked against.
34 | * @param x The X co-ordinate of the touch event
35 | * @param y The Y co-ordinate of the touch event
36 | * @return true if view is scrolled to the top.
37 | */
38 | public boolean isReadyForPull(View view, float x, float y);
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/library/res/layout/default_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
21 |
22 |
27 |
28 |
34 |
35 |
36 |
37 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/samples/stock/res/layout/customised_header.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
24 |
25 |
33 |
34 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/samples/stock/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
24 |
28 |
29 |
33 |
34 |
37 |
38 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/samples/stock/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
23 |
24 |
38 |
39 |
43 |
44 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/listeners/HeaderViewListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.library.listeners;
18 |
19 | import android.view.View;
20 |
21 | public interface HeaderViewListener {
22 | /**
23 | * The state when the header view is completely visible.
24 | */
25 | public static int STATE_VISIBLE = 0;
26 |
27 | /**
28 | * The state when the header view is minimized. By default this means
29 | * that the progress bar is still visible, but the rest of the view is
30 | * hidden, showing the Action Bar behind.
31 | *
32 | * This will not be called in header minimization is disabled.
33 | */
34 | public static int STATE_MINIMIZED = 1;
35 |
36 | /**
37 | * The state when the header view is completely hidden.
38 | */
39 | public static int STATE_HIDDEN = 2;
40 |
41 | /**
42 | * Called when the visibility state of the Header View has changed.
43 | *
44 | * @param headerView
45 | * HeaderView who's state has changed.
46 | * @param state
47 | * The new state. One of {@link #STATE_VISIBLE},
48 | * {@link #STATE_MINIMIZED} and {@link #STATE_HIDDEN}
49 | */
50 | public void onStateChanged(View headerView, int state);
51 | }
52 |
--------------------------------------------------------------------------------
/samples/stock/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | }
5 |
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:0.9.+'
8 | }
9 | }
10 | repositories {
11 | mavenCentral()
12 | }
13 |
14 | apply plugin: 'android'
15 |
16 | dependencies {
17 | compile project(':library')
18 | }
19 |
20 | android {
21 | compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION)
22 | buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
23 |
24 | defaultConfig {
25 | minSdkVersion 14
26 | targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION)
27 | versionName project.VERSION_NAME
28 | versionCode Integer.parseInt(project.VERSION_CODE)
29 | }
30 |
31 | signingConfigs { release }
32 |
33 | buildTypes {
34 | release {
35 | signingConfig signingConfigs.release
36 | }
37 | }
38 |
39 | sourceSets {
40 | main {
41 | manifest.srcFile 'AndroidManifest.xml'
42 | java.srcDirs = ['src']
43 | res.srcDirs = ['res']
44 | }
45 | }
46 | }
47 |
48 | File propFile = file('signing.properties');
49 | if (propFile.exists()) {
50 | def Properties props = new Properties()
51 | props.load(new FileInputStream(propFile))
52 |
53 | if (props.containsKey('STORE_FILE') && props.containsKey('STORE_PASSWORD') &&
54 | props.containsKey('KEY_ALIAS') && props.containsKey('KEY_PASSWORD')) {
55 | android.signingConfigs.release.storeFile = file(props['STORE_FILE'])
56 | android.signingConfigs.release.storePassword = props['STORE_PASSWORD']
57 | android.signingConfigs.release.keyAlias = props['KEY_ALIAS']
58 | android.signingConfigs.release.keyPassword = props['KEY_PASSWORD']
59 | } else {
60 | android.buildTypes.release.signingConfig = null
61 | }
62 | } else {
63 | android.buildTypes.release.signingConfig = null
64 | }
--------------------------------------------------------------------------------
/samples/stock/res/layout/layout_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
22 |
23 |
29 |
30 |
34 |
35 |
43 |
44 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/samples/stock/src/uk/co/senab/actionbarpulltorefresh/samples/stock/BaseSampleActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.samples.stock;
18 |
19 | import android.app.Activity;
20 | import android.app.Fragment;
21 | import android.os.Bundle;
22 | import android.view.Menu;
23 | import android.view.MenuItem;
24 | import android.widget.Toast;
25 |
26 | abstract class BaseSampleActivity extends Activity {
27 |
28 | @Override
29 | protected void onCreate(Bundle savedInstanceState) {
30 | super.onCreate(savedInstanceState);
31 |
32 | // Add the Sample Fragment if there is one
33 | Fragment sampleFragment = getSampleFragment();
34 | if (sampleFragment != null) {
35 | getFragmentManager().beginTransaction()
36 | .replace(android.R.id.content, sampleFragment).commit();
37 | }
38 | }
39 |
40 | @Override
41 | public boolean onCreateOptionsMenu(Menu menu) {
42 | getMenuInflater().inflate(R.menu.sample, menu);
43 | return super.onCreateOptionsMenu(menu);
44 | }
45 |
46 | @Override
47 | public boolean onOptionsItemSelected(MenuItem item) {
48 | switch (item.getItemId()) {
49 | case R.id.action_first:
50 | Toast.makeText(this, "First Action Item", Toast.LENGTH_SHORT).show();
51 | return true;
52 | case R.id.action_second:
53 | Toast.makeText(this, "Second Action Item", Toast.LENGTH_SHORT).show();
54 | return true;
55 | }
56 | return super.onOptionsItemSelected(item);
57 | }
58 |
59 | protected Fragment getSampleFragment() {
60 | return null;
61 | }
62 |
63 | }
--------------------------------------------------------------------------------
/library/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/samples/stock/src/uk/co/senab/actionbarpulltorefresh/samples/stock/ScrollViewActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.samples.stock;
18 |
19 | import android.os.AsyncTask;
20 | import android.os.Bundle;
21 | import android.view.View;
22 |
23 | import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh;
24 | import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener;
25 | import uk.co.senab.actionbarpulltorefresh.library.PullToRefreshLayout;
26 |
27 | /**
28 | * This sample shows how to use ActionBar-PullToRefresh with a
29 | * {@link android.widget.ScrollView ScrollView}.
30 | */
31 | public class ScrollViewActivity extends BaseSampleActivity
32 | implements OnRefreshListener {
33 |
34 | private PullToRefreshLayout mPullToRefreshLayout;
35 |
36 | @Override
37 | public void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.activity_scrollview);
40 |
41 | // Now find the PullToRefreshLayout and set it up
42 | mPullToRefreshLayout = (PullToRefreshLayout) findViewById(R.id.ptr_layout);
43 | ActionBarPullToRefresh.from(this)
44 | .allChildrenArePullable()
45 | .listener(this)
46 | .setup(mPullToRefreshLayout);
47 | }
48 |
49 | @Override
50 | public void onRefreshStarted(View view) {
51 | /**
52 | * Simulate Refresh with 4 seconds sleep
53 | */
54 | new AsyncTask() {
55 |
56 | @Override
57 | protected Void doInBackground(Void... params) {
58 | try {
59 | Thread.sleep(Constants.SIMULATED_REFRESH_LENGTH);
60 | } catch (InterruptedException e) {
61 | e.printStackTrace();
62 | }
63 | return null;
64 | }
65 |
66 | @Override
67 | protected void onPostExecute(Void result) {
68 | super.onPostExecute(result);
69 |
70 | // Notify PullToRefreshLayout that the refresh has finished
71 | mPullToRefreshLayout.setRefreshComplete();
72 | }
73 | }.execute();
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/samples/stock/src/uk/co/senab/actionbarpulltorefresh/samples/stock/WebViewActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.samples.stock;
18 |
19 | import android.os.Bundle;
20 | import android.view.View;
21 | import android.webkit.WebView;
22 | import android.webkit.WebViewClient;
23 |
24 | import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh;
25 | import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener;
26 | import uk.co.senab.actionbarpulltorefresh.library.PullToRefreshLayout;
27 |
28 | /**
29 | * This sample shows how to use ActionBar-PullToRefresh with a
30 | * {@link android.webkit.WebView WebView}, and manually creating (and attaching) a
31 | * {@link PullToRefreshLayout} to the view.
32 | */
33 | public class WebViewActivity extends BaseSampleActivity implements OnRefreshListener {
34 |
35 | private PullToRefreshLayout mPullToRefreshLayout;
36 |
37 | private WebView mWebView;
38 |
39 | @Override
40 | public void onCreate(Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 | setContentView(R.layout.activity_webview);
43 |
44 | // Find WebView and get it ready to display pages
45 | mWebView = (WebView) findViewById(R.id.webview);
46 | mWebView.getSettings().setJavaScriptEnabled(true);
47 | mWebView.setWebViewClient(new SampleWebViewClient());
48 |
49 | // Now find the PullToRefreshLayout and set it up
50 | mPullToRefreshLayout = (PullToRefreshLayout) findViewById(R.id.ptr_layout);
51 | ActionBarPullToRefresh.from(this)
52 | .allChildrenArePullable()
53 | .listener(this)
54 | .setup(mPullToRefreshLayout);
55 |
56 | // Finally make the WebView load something...
57 | mWebView.loadUrl("http://www.google.com");
58 | }
59 |
60 | @Override
61 | public void onRefreshStarted(View view) {
62 | // Here we just reload the webview
63 | mWebView.reload();
64 | }
65 |
66 | private class SampleWebViewClient extends WebViewClient {
67 |
68 | @Override
69 | public boolean shouldOverrideUrlLoading(WebView view, String url) {
70 | // Return false so the WebView loads the url
71 | return false;
72 | }
73 |
74 | @Override
75 | public void onPageFinished(WebView view, String url) {
76 | super.onPageFinished(view, url);
77 |
78 | // If the PullToRefreshAttacher is refreshing, make it as complete
79 | if (mPullToRefreshLayout.isRefreshing()) {
80 | mPullToRefreshLayout.setRefreshComplete();
81 | }
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ActionBar-PullToRefresh
2 |
3 | 
4 |
5 | ActionBar-PullToRefresh provides an easy way to add a modern version of the pull-to-refresh interaction to your application.
6 |
7 | Please note that this is __not__ an update to [Android-PullToRefresh](https://github.com/chrisbanes/Android-PullToRefresh), this has been created from new. You should think of this as Android-PullToRefresh's younger, leaner cousin.
8 |
9 | ### This is a Preview
10 | Please note that this is currently in a preview state. This basically means that the API is not fixed and you should expect changes between releases.
11 |
12 | ---
13 |
14 | ## Sample Apps
15 |
16 | There are two sample applications, the stock sample which uses the standard library and is therefore has a `minSdkVersion` of 14. There is also a sample which uses the ActionBarSherlock extra so has a `minSdkVersion` of 7.
17 |
18 | ### Stock Sample
19 | [](http://play.google.com/store/apps/details?id=uk.co.senab.actionbarpulltorefresh.samples.stock)
20 |
21 | ### ActionBarSherlock Sample
22 | [](http://play.google.com/store/apps/details?id=uk.co.senab.actionbarpulltorefresh.samples.actionbarsherlock)
23 |
24 | ## Video
25 |
26 | [](https://www.youtube.com/watch?v=YOYtPF-4RPg)
27 |
28 | ---
29 |
30 | ## Supported Views
31 |
32 | ActionBar-PullToRefresh has in-built support for:
33 |
34 | * AbsListView derivatives (ListView & GridView).
35 | * ScrollView
36 | * WebView
37 |
38 | If the View you want to use is not listed above, you can easily add support in your own code by providing a `ViewDelegate`. See the `ViewDelegate` section below for more info.
39 |
40 | ---
41 |
42 | ## Usage and Integration
43 | See the Quick Start guides for more information on how to achieve a simple integration:
44 |
45 | * [Quick Start](https://github.com/chrisbanes/ActionBar-PullToRefresh/wiki/QuickStart-Stock) for API v14 and above.
46 | * [Quick Start: ActionBarCompat](https://github.com/chrisbanes/ActionBar-PullToRefresh/wiki/QuickStart-ABC) when using ActionBarCompat (appcompat).
47 | * [Quick Start: ActionBarSherlock](https://github.com/chrisbanes/ActionBar-PullToRefresh/wiki/QuickStart-ABS) when using ActionBarSherlock.
48 |
49 | Then we are some advanced integration information:
50 |
51 | * [ListFragment](https://github.com/chrisbanes/ActionBar-PullToRefresh/wiki/ListFragment) when integrating the library with a ListFragment.
52 |
53 |
54 | ## Customisation
55 | See the [Customisation](https://github.com/chrisbanes/ActionBar-PullToRefresh/wiki/Customisation) page for more information.
56 |
57 | ## License
58 |
59 | Copyright 2013 Chris Banes
60 |
61 | Licensed under the Apache License, Version 2.0 (the "License");
62 | you may not use this file except in compliance with the License.
63 | You may obtain a copy of the License at
64 |
65 | http://www.apache.org/licenses/LICENSE-2.0
66 |
67 | Unless required by applicable law or agreed to in writing, software
68 | distributed under the License is distributed on an "AS IS" BASIS,
69 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
70 | See the License for the specific language governing permissions and
71 | limitations under the License.
72 |
73 |
74 | 
75 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/viewdelegates/AbsListViewDelegate.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.library.viewdelegates;
18 |
19 | import android.annotation.TargetApi;
20 | import android.os.Build;
21 | import android.view.View;
22 | import android.widget.AbsListView;
23 |
24 | /**
25 | * FIXME
26 | */
27 | public class AbsListViewDelegate implements ViewDelegate {
28 |
29 | public static final Class[] SUPPORTED_VIEW_CLASSES = { AbsListView.class };
30 |
31 | @Override
32 | public boolean isReadyForPull(View view, final float x, final float y) {
33 | boolean ready = false;
34 |
35 | // First we check whether we're scrolled to the top
36 | AbsListView absListView = (AbsListView) view;
37 | if (absListView.getCount() == 0) {
38 | ready = true;
39 | } else if (absListView.getFirstVisiblePosition() == 0) {
40 | final View firstVisibleChild = absListView.getChildAt(0);
41 | ready = firstVisibleChild != null && firstVisibleChild.getTop() >= 0;
42 | }
43 |
44 | // Then we have to check whether the fas scroller is enabled, and check we're not starting
45 | // the gesture from the scroller
46 | if (ready && absListView.isFastScrollEnabled() && isFastScrollAlwaysVisible(absListView)) {
47 | switch (getVerticalScrollbarPosition(absListView)) {
48 | case View.SCROLLBAR_POSITION_RIGHT:
49 | ready = x < absListView.getRight() - absListView.getVerticalScrollbarWidth();
50 | break;
51 | case View.SCROLLBAR_POSITION_LEFT:
52 | ready = x > absListView.getVerticalScrollbarWidth();
53 | break;
54 | }
55 | }
56 |
57 | return ready;
58 | }
59 |
60 | int getVerticalScrollbarPosition(AbsListView absListView) {
61 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
62 | CompatV11.getVerticalScrollbarPosition(absListView) :
63 | Compat.getVerticalScrollbarPosition(absListView);
64 | }
65 |
66 | boolean isFastScrollAlwaysVisible(AbsListView absListView) {
67 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
68 | CompatV11.isFastScrollAlwaysVisible(absListView) :
69 | Compat.isFastScrollAlwaysVisible(absListView);
70 | }
71 |
72 | static class Compat {
73 | static int getVerticalScrollbarPosition(AbsListView absListView) {
74 | return View.SCROLLBAR_POSITION_RIGHT;
75 | }
76 | static boolean isFastScrollAlwaysVisible(AbsListView absListView) {
77 | return false;
78 | }
79 | }
80 |
81 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
82 | static class CompatV11 {
83 | static int getVerticalScrollbarPosition(AbsListView absListView) {
84 | return absListView.getVerticalScrollbarPosition();
85 | }
86 | static boolean isFastScrollAlwaysVisible(AbsListView absListView) {
87 | return absListView.isFastScrollAlwaysVisible();
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/HeaderTransformer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.library;
18 |
19 | import android.app.Activity;
20 | import android.content.res.Configuration;
21 | import android.view.View;
22 |
23 | /**
24 | * HeaderTransformers are what controls and update the Header View to reflect the current state
25 | * of the pull-to-refresh interaction. They are responsible for showing and hiding the header
26 | * view, as well as update the state.
27 | */
28 | public abstract class HeaderTransformer {
29 |
30 | /**
31 | * Called whether the header view has been inflated from the resources
32 | * defined in {@link Options#headerLayout}.
33 | *
34 | * @param activity The {@link android.app.Activity} that the header view is attached to.
35 | * @param headerView The inflated header view.
36 | */
37 | public void onViewCreated(Activity activity, View headerView) {}
38 |
39 | /**
40 | * Called when the header should be reset. You should update any child
41 | * views to reflect this.
42 | *
43 | * You should not change the visibility of the header
44 | * view.
45 | */
46 | public void onReset() {}
47 |
48 | /**
49 | * Called the user has pulled on the scrollable view.
50 | *
51 | * @param percentagePulled value between 0.0f and 1.0f depending on how far the
52 | * user has pulled.
53 | */
54 | public void onPulled(float percentagePulled) {}
55 |
56 | /**
57 | * Called when a refresh has begun. Theoretically this call is similar
58 | * to that provided from {@link uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener} but is more suitable
59 | * for header view updates.
60 | */
61 | public void onRefreshStarted() {}
62 |
63 | /**
64 | * Called when a refresh can be initiated when the user ends the touch
65 | * event. This is only called when {@link Options#refreshOnUp} is set to
66 | * true.
67 | */
68 | public void onReleaseToRefresh() {}
69 |
70 | /**
71 | * Called when a subview hit the top scrolled position
72 | */
73 | public void onTopScrollChanged(boolean scrolledOnTop) {}
74 |
75 | /**
76 | * Called when the current refresh has taken longer than the time
77 | * specified in {@link Options#refreshMinimizeDelay}.
78 | */
79 | public void onRefreshMinimized() {}
80 |
81 | /**
82 | * Called when the Header View should be made visible, usually with an animation.
83 | *
84 | * @return true if the visibility has changed.
85 | */
86 | public abstract boolean showHeaderView();
87 |
88 | /**
89 | * Called when the Header View should be made invisible, usually with an animation.
90 | *
91 | * @return true if the visibility has changed.
92 | */
93 | public abstract boolean hideHeaderView();
94 |
95 | /**
96 | * Called when the Activity's configuration has changed.
97 | *
98 | * @param activity The {@link android.app.Activity} that the header view is attached to.
99 | * @param newConfig New configuration.
100 | *
101 | * @see android.app.Activity#onConfigurationChanged(android.content.res.Configuration)
102 | */
103 | public void onConfigurationChanged(Activity activity, Configuration newConfig) {}
104 | }
105 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/InstanceCreationUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.library;
18 |
19 | import android.content.Context;
20 | import android.util.Log;
21 | import android.view.View;
22 |
23 | import java.lang.reflect.Constructor;
24 | import java.util.HashMap;
25 | import java.util.Map;
26 | import java.util.Set;
27 |
28 | import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.AbsListViewDelegate;
29 | import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.ScrollYDelegate;
30 | import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.ViewDelegate;
31 | import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.WebViewDelegate;
32 |
33 | class InstanceCreationUtils {
34 |
35 | private static final String LOG_TAG = "InstanceCreationUtils";
36 |
37 | private static final Class>[] VIEW_DELEGATE_CONSTRUCTOR_SIGNATURE = new Class[]{};
38 | private static final Class>[] TRANSFORMER_CONSTRUCTOR_SIGNATURE = new Class[]{};
39 |
40 | private static final HashMap BUILT_IN_DELEGATES;
41 | static {
42 | BUILT_IN_DELEGATES = new HashMap();
43 | addBuiltinDelegates(AbsListViewDelegate.SUPPORTED_VIEW_CLASSES, AbsListViewDelegate.class);
44 | addBuiltinDelegates(ScrollYDelegate.SUPPORTED_VIEW_CLASSES, ScrollYDelegate.class);
45 | addBuiltinDelegates(WebViewDelegate.SUPPORTED_VIEW_CLASSES, WebViewDelegate.class);
46 | }
47 |
48 | private static void addBuiltinDelegates(Class[] supportedViews, Class> delegateClass) {
49 | for (int i = 0, z = supportedViews.length; i< z ; i++) {
50 | BUILT_IN_DELEGATES.put(supportedViews[i], delegateClass);
51 | }
52 | }
53 |
54 | static ViewDelegate getBuiltInViewDelegate(final View view) {
55 | final Set> entries = BUILT_IN_DELEGATES.entrySet();
56 | for (final Map.Entry entry : entries) {
57 | if (entry.getKey().isInstance(view)) {
58 | return InstanceCreationUtils.newInstance(view.getContext(),
59 | entry.getValue(), VIEW_DELEGATE_CONSTRUCTOR_SIGNATURE);
60 | }
61 | }
62 | return null;
63 | }
64 |
65 | static T instantiateViewDelegate(Context context, String className) {
66 | try {
67 | Class> clazz = context.getClassLoader().loadClass(className);
68 | return newInstance(context, clazz, VIEW_DELEGATE_CONSTRUCTOR_SIGNATURE);
69 | } catch (Exception e) {
70 | Log.w(LOG_TAG, "Cannot instantiate class: " + className, e);
71 | }
72 | return null;
73 | }
74 |
75 | static T instantiateTransformer(Context context, String className) {
76 | try {
77 | Class> clazz = context.getClassLoader().loadClass(className);
78 | return newInstance(context, clazz, TRANSFORMER_CONSTRUCTOR_SIGNATURE);
79 | } catch (Exception e) {
80 | Log.w(LOG_TAG, "Cannot instantiate class: " + className, e);
81 | }
82 | return null;
83 | }
84 |
85 | private static T newInstance(Context context, Class clazz, Class[] constructorSig,
86 | Object... arguments) {
87 | try {
88 | Constructor> constructor = clazz.getConstructor(constructorSig);
89 | return (T) constructor.newInstance(arguments);
90 | } catch (Exception e) {
91 | Log.w(LOG_TAG, "Cannot instantiate class: " + clazz.getName(), e);
92 | }
93 | return null;
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/samples/stock/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | ActionBarPullToRefresh
20 | ScrollView
21 | ListView
22 | GridView
23 | WebView
24 | Fragments + Tabs
25 |
26 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec
27 | sollicitudin mauris varius lacus porttitor eget blandit massa facilisis. Nulla pellentesque
28 | odio sed purus fermentum vitae viverra orci faucibus. Sed ullamcorper condimentum vulputate.
29 | Curabitur sit amet convallis velit. Vestibulum posuere eleifend risus ac adipiscing. Nam
30 | pulvinar nulla a velit faucibus imperdiet. Praesent eget nisi ac justo blandit sagittis.
31 | Maecenas at leo nisi, nec varius nisl.\n\nIn hac habitasse platea dictumst. Morbi neque
32 | tortor, vestibulum sed viverra a, luctus vel lorem. Nunc turpis eros, varius eget commodo
33 | et, euismod at eros. Sed tincidunt mi purus, vel posuere dui. Vestibulum ante lectus, porta
34 | sed mattis bibendum, scelerisque cursus sapien. Cras ultrices imperdiet fermentum. Aenean
35 | nisi nulla, euismod non blandit ac, dictum quis libero. Morbi consectetur tempor mollis.
36 | Suspendisse eget nunc arcu, vel ullamcorper augue. Integer malesuada, diam nec faucibus
37 | mollis, nisl velit euismod enim, ac mattis justo neque sit amet mauris. Vivamus pretium
38 | imperdiet pharetra.\n\nInteger sagittis augue sit amet lectus pulvinar sit amet commodo tortor
39 | mattis. Maecenas quis tellus eget ante eleifend sollicitudin non et nibh. Maecenas luctus
40 | euismod tristique. Fusce in odio nec diam blandit facilisis. Sed nec arcu eros. Vivamus quis
41 | tortor a metus tempus aliquam eget volutpat magna. Pellentesque id ultrices dolor. Sed
42 | blandit aliquet quam. Phasellus dapibus euismod vulputate. Aenean blandit, elit vitae
43 | vestibulum tincidunt, metus dui accumsan nulla, sit amet vehicula mauris lacus in est. Etiam
44 | dignissim pellentesque nulla vel malesuada. Cras vel lorem justo.\n\nSed condimentum nisl sit
45 | amet libero vestibulum hendrerit. Duis auctor tempus placerat. Proin velit ante, ornare nec
46 | dictum nec, hendrerit eu arcu. Etiam ut diam ornare quam venenatis pulvinar vitae vel leo.
47 | Vivamus consectetur, ante id interdum rhoncus, magna eros pulvinar lacus, a gravida nibh
48 | arcu vitae eros. Nulla scelerisque laoreet feugiat. Mauris sit amet gravida felis.\n\nNulla ac
49 | dolor sapien, vestibulum venenatis justo. Cras placerat velit vitae nibh pellentesque
50 | ultricies. Suspendisse adipiscing enim eu justo iaculis eu pretium urna fermentum. Duis
51 | porttitor nunc non nunc mattis vestibulum. Etiam elit tellus, feugiat in bibendum eget,
52 | adipiscing nec metus. Ut ut sem lacus, quis faucibus diam. Curabitur a nulla fermentum
53 | tortor dignissim posuere. Fusce faucibus ante ut sem imperdiet imperdiet eget vitae lorem.
54 | Etiam fringilla ornare ipsum, in sagittis quam ornare vitae. Nullam venenatis orci sit amet
55 | sapien adipiscing gravida. Proin turpis lectus, hendrerit vitae vehicula ut, auctor ac
56 | lectus. Pellentesque sollicitudin blandit ligula quis commodo. Mauris vulputate lectus in
57 | velit luctus aliquam. Quisque eget tincidunt elit. Quisque et augue quam, sed scelerisque
58 | eros.
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/samples/stock/src/uk/co/senab/actionbarpulltorefresh/samples/stock/MainActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.samples.stock;
18 |
19 | import android.app.ListActivity;
20 | import android.content.ComponentName;
21 | import android.content.Context;
22 | import android.content.Intent;
23 | import android.content.pm.ActivityInfo;
24 | import android.content.pm.PackageInfo;
25 | import android.content.pm.PackageManager;
26 | import android.content.pm.PackageManager.NameNotFoundException;
27 | import android.os.Bundle;
28 | import android.text.TextUtils;
29 | import android.view.LayoutInflater;
30 | import android.view.View;
31 | import android.view.ViewGroup;
32 | import android.widget.BaseAdapter;
33 | import android.widget.ListAdapter;
34 | import android.widget.ListView;
35 | import android.widget.TextView;
36 |
37 | import java.util.ArrayList;
38 |
39 | public class MainActivity extends ListActivity {
40 |
41 | @Override
42 | protected void onCreate(Bundle savedInstanceState) {
43 | super.onCreate(savedInstanceState);
44 | setListAdapter(getSampleAdapter());
45 | }
46 |
47 | @Override
48 | protected void onListItemClick(ListView l, View v, int position, long id) {
49 | ActivityInfo info = (ActivityInfo) l.getItemAtPosition(position);
50 | Intent intent = new Intent();
51 | intent.setComponent(new ComponentName(this, info.name));
52 | startActivity(intent);
53 | }
54 |
55 | private ListAdapter getSampleAdapter() {
56 | ArrayList items = new ArrayList();
57 | final String thisClazzName = getClass().getName();
58 |
59 | try {
60 | PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(),
61 | PackageManager.GET_ACTIVITIES);
62 | ActivityInfo[] aInfos = pInfo.activities;
63 |
64 | for (ActivityInfo aInfo : aInfos) {
65 | if (!thisClazzName.equals(aInfo.name)) {
66 | items.add(aInfo);
67 | }
68 | }
69 | } catch (NameNotFoundException e) {
70 | e.printStackTrace();
71 | }
72 |
73 | return new SampleAdapter(this, items);
74 | }
75 |
76 | private static class SampleAdapter extends BaseAdapter {
77 |
78 | private final ArrayList mItems;
79 |
80 | private final LayoutInflater mInflater;
81 |
82 | public SampleAdapter(Context context, ArrayList activities) {
83 | mItems = activities;
84 | mInflater = LayoutInflater.from(context);
85 | }
86 |
87 | @Override
88 | public int getCount() {
89 | return mItems.size();
90 | }
91 |
92 | @Override
93 | public ActivityInfo getItem(int position) {
94 | return mItems.get(position);
95 | }
96 |
97 | @Override
98 | public long getItemId(int position) {
99 | return position;
100 | }
101 |
102 | @Override
103 | public View getView(int position, View convertView, ViewGroup parent) {
104 | TextView tv = (TextView) convertView;
105 | if (tv == null) {
106 | tv = (TextView) mInflater.inflate(android.R.layout.simple_list_item_1, parent,
107 | false);
108 | }
109 | ActivityInfo item = getItem(position);
110 | if (!TextUtils.isEmpty(item.nonLocalizedLabel)) {
111 | tv.setText(item.nonLocalizedLabel);
112 | } else {
113 | tv.setText(item.labelRes);
114 | }
115 | return tv;
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/samples/stock/src/uk/co/senab/actionbarpulltorefresh/samples/stock/ListViewActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.samples.stock;
18 |
19 | import android.app.Fragment;
20 | import android.app.ListFragment;
21 | import android.os.AsyncTask;
22 | import android.os.Bundle;
23 | import android.view.View;
24 | import android.view.ViewGroup;
25 | import android.widget.ArrayAdapter;
26 |
27 | import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh;
28 | import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener;
29 | import uk.co.senab.actionbarpulltorefresh.library.PullToRefreshLayout;
30 |
31 | /**
32 | * This sample shows how to use ActionBar-PullToRefresh with a
33 | * {@link android.widget.ListView ListView}, and manually creating (and attaching) a
34 | * {@link PullToRefreshLayout} to the view.
35 | */
36 | public class ListViewActivity extends BaseSampleActivity {
37 |
38 | @Override
39 | protected Fragment getSampleFragment() {
40 | return new SampleListFragment();
41 | }
42 |
43 | /**
44 | * Fragment Class
45 | */
46 | public static class SampleListFragment extends ListFragment implements
47 | OnRefreshListener {
48 |
49 | private static String[] ITEMS = {"Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam",
50 | "Abondance", "Ackawi", "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu",
51 | "Airag", "Airedale", "Aisy Cendre", "Allgauer Emmentaler", "Abbaye de Belloc",
52 | "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", "Acorn", "Adelost",
53 | "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
54 | "Allgauer Emmentaler"};
55 |
56 | private PullToRefreshLayout mPullToRefreshLayout;
57 |
58 | @Override
59 | public void onViewCreated(View view, Bundle savedInstanceState) {
60 | super.onViewCreated(view,savedInstanceState);
61 | ViewGroup viewGroup = (ViewGroup) view;
62 |
63 | // As we're using a ListFragment we create a PullToRefreshLayout manually
64 | mPullToRefreshLayout = new PullToRefreshLayout(viewGroup.getContext());
65 |
66 | // We can now setup the PullToRefreshLayout
67 | ActionBarPullToRefresh.from(getActivity())
68 | // We need to insert the PullToRefreshLayout into the Fragment's ViewGroup
69 | .insertLayoutInto(viewGroup)
70 | // Here we mark just the ListView and it's Empty View as pullable
71 | .theseChildrenArePullable(android.R.id.list, android.R.id.empty)
72 | .listener(this)
73 | .setup(mPullToRefreshLayout);
74 | }
75 |
76 | @Override
77 | public void onActivityCreated(Bundle savedInstanceState) {
78 | super.onActivityCreated(savedInstanceState);
79 |
80 | // Set the List Adapter to display the sample items
81 | setListAdapter(new ArrayAdapter(getActivity(),
82 | android.R.layout.simple_list_item_1, ITEMS));
83 | setListShownNoAnimation(true);
84 | }
85 |
86 | @Override
87 | public void onRefreshStarted(View view) {
88 | // Hide the list
89 | setListShown(false);
90 |
91 | /**
92 | * Simulate Refresh with 4 seconds sleep
93 | */
94 | new AsyncTask() {
95 |
96 | @Override
97 | protected Void doInBackground(Void... params) {
98 | try {
99 | Thread.sleep(Constants.SIMULATED_REFRESH_LENGTH);
100 | } catch (InterruptedException e) {
101 | e.printStackTrace();
102 | }
103 | return null;
104 | }
105 |
106 | @Override
107 | protected void onPostExecute(Void result) {
108 | super.onPostExecute(result);
109 |
110 | // Notify PullToRefreshLayout that the refresh has finished
111 | mPullToRefreshLayout.setRefreshComplete();
112 |
113 | if (getView() != null) {
114 | // Show the list again
115 | setListShown(true);
116 | }
117 | }
118 | }.execute();
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/samples/stock/src/uk/co/senab/actionbarpulltorefresh/samples/stock/FragmentTabsActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.samples.stock;
18 |
19 | import android.app.ActionBar;
20 | import android.app.Fragment;
21 | import android.app.FragmentTransaction;
22 | import android.os.AsyncTask;
23 | import android.os.Bundle;
24 | import android.view.LayoutInflater;
25 | import android.view.View;
26 | import android.view.ViewGroup;
27 | import android.widget.TextView;
28 |
29 | import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh;
30 | import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener;
31 | import uk.co.senab.actionbarpulltorefresh.library.PullToRefreshLayout;
32 |
33 | /**
34 | * A sample which show you how to use {@link PullToRefreshLayout} with Fragments.
35 | */
36 | public class FragmentTabsActivity extends BaseSampleActivity implements ActionBar.TabListener {
37 | private static String EXTRA_TITLE = "extra_title";
38 |
39 | @Override
40 | protected void onCreate(Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 |
43 | setContentView(R.layout.activity_fragment_tabs);
44 |
45 | // Add 3 tabs which will switch fragments
46 | ActionBar ab = getActionBar();
47 | ab.addTab(ab.newTab().setText("Tab 1").setTabListener(this));
48 | ab.addTab(ab.newTab().setText("Tab 2").setTabListener(this));
49 | ab.addTab(ab.newTab().setText("Tab 3").setTabListener(this));
50 | ab.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
51 | }
52 |
53 | // From TabListener
54 | @Override
55 | public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
56 | // Create Fragment
57 | SampleFragment fragment = new SampleFragment();
58 |
59 | // Set title for display purposes
60 | Bundle b = new Bundle();
61 | b.putString(EXTRA_TITLE, tab.getText().toString());
62 | fragment.setArguments(b);
63 |
64 | ft.replace(R.id.ptr_fragment, fragment);
65 | }
66 |
67 | // From TabListener
68 | @Override
69 | public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
70 | }
71 |
72 | // From TabListener
73 | @Override
74 | public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
75 | }
76 |
77 | /**
78 | * Fragment Class
79 | */
80 | public static class SampleFragment extends Fragment implements
81 | OnRefreshListener {
82 | private PullToRefreshLayout mPullToRefreshLayout;
83 |
84 | @Override
85 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
86 | Bundle savedInstanceState) {
87 | // Inflate the layout
88 | View view = inflater.inflate(R.layout.layout_fragment, container, false);
89 |
90 | // Now give the find the PullToRefreshLayout and set it up
91 | mPullToRefreshLayout = (PullToRefreshLayout) view.findViewById(R.id.ptr_layout);
92 | ActionBarPullToRefresh.from(getActivity())
93 | .allChildrenArePullable()
94 | .listener(this)
95 | .setup(mPullToRefreshLayout);
96 |
97 | // Set title in Fragment for display purposes.
98 | TextView title = (TextView) view.findViewById(R.id.tv_title);
99 | Bundle b = getArguments();
100 | if (b != null) {
101 | title.setText(b.getString(EXTRA_TITLE));
102 | }
103 |
104 | return view;
105 | }
106 |
107 | @Override
108 | public void onRefreshStarted(View view) {
109 | /**
110 | * Simulate Refresh with 4 seconds sleep
111 | */
112 | new AsyncTask() {
113 |
114 | @Override
115 | protected Void doInBackground(Void... params) {
116 | try {
117 | Thread.sleep(Constants.SIMULATED_REFRESH_LENGTH);
118 | } catch (InterruptedException e) {
119 | e.printStackTrace();
120 | }
121 | return null;
122 | }
123 |
124 | @Override
125 | protected void onPostExecute(Void result) {
126 | super.onPostExecute(result);
127 |
128 | // Notify PullToRefreshLayout that the refresh has finished
129 | mPullToRefreshLayout.setRefreshComplete();
130 | }
131 | }.execute();
132 | }
133 | }
134 | }
135 |
136 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/Options.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.library;
18 |
19 | /**
20 | * Allows you to specify a number of configuration options when setting up a {@link PullToRefreshLayout}.
21 | */
22 | public final class Options {
23 |
24 | /* Default configuration values */
25 | private static final int DEFAULT_HEADER_LAYOUT = R.layout.default_header;
26 | private static final float DEFAULT_REFRESH_SCROLL_DISTANCE = 0.5f;
27 | private static final boolean DEFAULT_REFRESH_ON_UP = false;
28 | private static final int DEFAULT_REFRESH_MINIMIZED_DELAY = 1 * 1000;
29 | private static final boolean DEFAULT_REFRESH_MINIMIZE = true;
30 |
31 | public static Builder create() {
32 | return new Builder();
33 | }
34 |
35 | Options() {}
36 |
37 | EnvironmentDelegate environmentDelegate = null;
38 | int headerLayout = DEFAULT_HEADER_LAYOUT;
39 | HeaderTransformer headerTransformer = null;
40 | float refreshScrollDistance = DEFAULT_REFRESH_SCROLL_DISTANCE;
41 | boolean refreshOnUp = DEFAULT_REFRESH_ON_UP;
42 | int refreshMinimizeDelay = DEFAULT_REFRESH_MINIMIZED_DELAY;
43 |
44 | /**
45 | * Enable or disable the header 'minimization', which by default means that the majority of
46 | * the header is hidden, leaving only the progress bar still showing.
47 | *
48 | * If set to true, the header will be minimized after the delay set in
49 | * {@link #refreshMinimizeDelay}. If set to false then the whole header will be displayed
50 | * until the refresh is finished.
51 | */
52 | boolean refreshMinimize = DEFAULT_REFRESH_MINIMIZE;
53 |
54 | public static class Builder {
55 | final Options mOptions = new Options();
56 |
57 | /**
58 | * EnvironmentDelegate instance which will be used. If null, we will
59 | * create an instance of the default class.
60 | */
61 | public Builder environmentDelegate(EnvironmentDelegate environmentDelegate) {
62 | mOptions.environmentDelegate = environmentDelegate;
63 | return this;
64 | }
65 |
66 | /**
67 | * The layout resource ID which should be inflated to be displayed above
68 | * the Action Bar
69 | */
70 | public Builder headerLayout(int headerLayoutId) {
71 | mOptions.headerLayout = headerLayoutId;
72 | return this;
73 | }
74 |
75 | /**
76 | * The header transformer to be used to transfer the header view. If
77 | * null, an instance of {@link DefaultHeaderTransformer} will be used.
78 | */
79 | public Builder headerTransformer(HeaderTransformer headerTransformer) {
80 | mOptions.headerTransformer = headerTransformer;
81 | return this;
82 | }
83 |
84 | /**
85 | * The percentage of the refreshable view that needs to be scrolled
86 | * before a refresh is initiated.
87 | */
88 | public Builder scrollDistance(float refreshScrollDistance) {
89 | mOptions.refreshScrollDistance = refreshScrollDistance;
90 | return this;
91 | }
92 |
93 | /**
94 | * Whether a refresh should only be initiated when the user has finished
95 | * the touch event.
96 | */
97 | public Builder refreshOnUp(boolean enabled) {
98 | mOptions.refreshOnUp = enabled;
99 | return this;
100 | }
101 |
102 | /**
103 | * Disable the header 'minimization', which by default means that the majority of
104 | * the header is hidden, leaving only the progress bar still showing.
105 | */
106 | public Builder noMinimize() {
107 | mOptions.refreshMinimize = false;
108 | return this;
109 | }
110 |
111 | /**
112 | * Enable header 'minimization', which by default means that the majority of
113 | * the header is hidden, leaving only the progress bar still showing.
114 | */
115 | public Builder minimize() {
116 | return minimize(DEFAULT_REFRESH_MINIMIZED_DELAY);
117 | }
118 |
119 | /**
120 | * Enable header 'minimization' and set the delay.
121 | */
122 | public Builder minimize(int delay) {
123 | mOptions.refreshMinimizeDelay = delay;
124 | mOptions.refreshMinimize = true;
125 | return this;
126 | }
127 |
128 | /**
129 | * @return the built {@link Options} instance.
130 | */
131 | public Options build() {
132 | return mOptions;
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/ActionBarPullToRefresh.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.library;
18 |
19 |
20 | import android.app.Activity;
21 | import android.view.View;
22 | import android.view.ViewGroup;
23 |
24 | import java.util.HashMap;
25 | import java.util.Map;
26 | import java.util.Set;
27 |
28 | import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener;
29 | import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.ViewDelegate;
30 |
31 | public class ActionBarPullToRefresh {
32 |
33 | public static SetupWizard from(Activity activity) {
34 | return new SetupWizard(activity);
35 | }
36 |
37 | public static final class SetupWizard {
38 | private final Activity mActivity;
39 | private Options mOptions;
40 | private int[] refreshableViewIds;
41 | private View[] refreshableViews;
42 | private OnRefreshListener mOnRefreshListener;
43 | private ViewGroup mViewGroupToInsertInto;
44 | private HashMap mViewDelegates;
45 |
46 | private SetupWizard(Activity activity) {
47 | mActivity = activity;
48 | }
49 |
50 | public SetupWizard options(Options options) {
51 | mOptions = options;
52 | return this;
53 | }
54 |
55 | public SetupWizard allChildrenArePullable() {
56 | refreshableViewIds = null;
57 | refreshableViews = null;
58 | return this;
59 | }
60 |
61 | public SetupWizard theseChildrenArePullable(int... viewIds) {
62 | refreshableViewIds = viewIds;
63 | refreshableViews = null;
64 | return this;
65 | }
66 |
67 | public SetupWizard theseChildrenArePullable(View... views) {
68 | refreshableViews = views;
69 | refreshableViewIds = null;
70 | return this;
71 | }
72 |
73 | public SetupWizard useViewDelegate(Class> viewClass, ViewDelegate delegate) {
74 | if (mViewDelegates == null) {
75 | mViewDelegates = new HashMap();
76 | }
77 | mViewDelegates.put(viewClass, delegate);
78 | return this;
79 | }
80 |
81 | public SetupWizard listener(OnRefreshListener listener) {
82 | mOnRefreshListener = listener;
83 | return this;
84 | }
85 |
86 | public SetupWizard insertLayoutInto(ViewGroup viewGroup) {
87 | mViewGroupToInsertInto = viewGroup;
88 | return this;
89 | }
90 |
91 | public void setup(PullToRefreshLayout pullToRefreshLayout) {
92 | PullToRefreshAttacher attacher = pullToRefreshLayout.createPullToRefreshAttacher(
93 | mActivity, mOptions);
94 | attacher.setOnRefreshListener(mOnRefreshListener);
95 |
96 | if (mViewGroupToInsertInto != null) {
97 | insertLayoutIntoViewGroup(mViewGroupToInsertInto, pullToRefreshLayout);
98 | }
99 |
100 | pullToRefreshLayout.setPullToRefreshAttacher(attacher);
101 |
102 | // First add the pullable child views
103 | if (refreshableViewIds != null) {
104 | pullToRefreshLayout.addChildrenAsPullable(refreshableViewIds);
105 | } else if (refreshableViews != null) {
106 | pullToRefreshLayout.addChildrenAsPullable(refreshableViews);
107 | } else {
108 | pullToRefreshLayout.addAllChildrenAsPullable();
109 | }
110 |
111 | // Now set any custom view delegates
112 | if (mViewDelegates != null) {
113 | final Set> entries = mViewDelegates.entrySet();
114 | for (final Map.Entry entry : entries) {
115 | attacher.useViewDelegate(entry.getKey(), entry.getValue());
116 | }
117 | }
118 | }
119 |
120 | private static void insertLayoutIntoViewGroup(ViewGroup viewGroup,
121 | PullToRefreshLayout pullToRefreshLayout) {
122 | // Move all children to PullToRefreshLayout. This code looks a bit silly but the child
123 | // indices change every time we remove a View (so we can't just iterate through)
124 | View child = viewGroup.getChildAt(0);
125 | while (child != null) {
126 | viewGroup.removeViewAt(0);
127 | pullToRefreshLayout.addView(child);
128 | child = viewGroup.getChildAt(0);
129 | }
130 |
131 | viewGroup.addView(pullToRefreshLayout, ViewGroup.LayoutParams.MATCH_PARENT,
132 | ViewGroup.LayoutParams.MATCH_PARENT);
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/samples/stock/src/uk/co/senab/actionbarpulltorefresh/samples/stock/GridViewActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.samples.stock;
18 |
19 | import android.app.Activity;
20 | import android.os.AsyncTask;
21 | import android.os.Bundle;
22 | import android.view.View;
23 | import android.widget.AbsListView;
24 | import android.widget.ArrayAdapter;
25 | import android.widget.GridView;
26 | import android.widget.ListAdapter;
27 | import android.widget.TextView;
28 |
29 | import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh;
30 | import uk.co.senab.actionbarpulltorefresh.library.HeaderTransformer;
31 | import uk.co.senab.actionbarpulltorefresh.library.Options;
32 | import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener;
33 | import uk.co.senab.actionbarpulltorefresh.library.PullToRefreshLayout;
34 | import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.AbsListViewDelegate;
35 |
36 | /**
37 | * This sample shows how to use ActionBar-PullToRefresh with a {@link android.widget.GridView
38 | * GridView}, and manually creating (and attaching) a {@link PullToRefreshLayout} to the view.
39 | */
40 | public class GridViewActivity extends BaseSampleActivity
41 | implements OnRefreshListener {
42 |
43 | private static String[] ITEMS = {"Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam",
44 | "Abondance", "Ackawi", "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu",
45 | "Airag", "Airedale", "Aisy Cendre", "Allgauer Emmentaler", "Abbaye de Belloc",
46 | "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", "Acorn", "Adelost",
47 | "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
48 | "Allgauer Emmentaler"};
49 |
50 | private PullToRefreshLayout mPullToRefreshLayout;
51 |
52 | @Override
53 | public void onCreate(Bundle savedInstanceState) {
54 | super.onCreate(savedInstanceState);
55 | setContentView(R.layout.activity_gridview);
56 |
57 | GridView gridView = (GridView) findViewById(R.id.ptr_gridview);
58 | ListAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1,
59 | ITEMS);
60 | gridView.setAdapter(adapter);
61 |
62 | // Now find the PullToRefreshLayout and set it up
63 | mPullToRefreshLayout = (PullToRefreshLayout) findViewById(R.id.ptr_layout);
64 | ActionBarPullToRefresh.from(this)
65 | .options(Options.create()
66 | // Here we make the refresh scroll distance to 75% of the GridView height
67 | .scrollDistance(.75f)
68 | // Here we define a custom header layout which will be inflated and used
69 | .headerLayout(R.layout.customised_header)
70 | // Here we define a custom header transformer which will alter the header
71 | // based on the current pull-to-refresh state
72 | .headerTransformer(new CustomisedHeaderTransformer())
73 | .build())
74 | .allChildrenArePullable()
75 | .listener(this)
76 | // Here we'll set a custom ViewDelegate
77 | .useViewDelegate(GridView.class, new AbsListViewDelegate())
78 | .setup(mPullToRefreshLayout);
79 | }
80 |
81 | @Override
82 | public void onRefreshStarted(View view) {
83 | /**
84 | * Simulate Refresh with 4 seconds sleep
85 | */
86 | new AsyncTask() {
87 |
88 | @Override
89 | protected Void doInBackground(Void... params) {
90 | try {
91 | Thread.sleep(Constants.SIMULATED_REFRESH_LENGTH);
92 | } catch (InterruptedException e) {
93 | e.printStackTrace();
94 | }
95 | return null;
96 | }
97 |
98 | @Override
99 | protected void onPostExecute(Void result) {
100 | super.onPostExecute(result);
101 |
102 | // Notify PullToRefreshLayout that the refresh has finished
103 | mPullToRefreshLayout.setRefreshComplete();
104 | }
105 | }.execute();
106 | }
107 |
108 | /**
109 | * Here's a customised header transformer which displays the scroll progress as text.
110 | */
111 | static class CustomisedHeaderTransformer extends HeaderTransformer {
112 |
113 | private View mHeaderView;
114 | private TextView mMainTextView;
115 | private TextView mProgressTextView;
116 |
117 | @Override
118 | public void onViewCreated(Activity activity, View headerView) {
119 | mHeaderView = headerView;
120 | mMainTextView = (TextView) headerView.findViewById(R.id.ptr_text);
121 | mProgressTextView = (TextView) headerView.findViewById(R.id.ptr_text_secondary);
122 | }
123 |
124 | @Override
125 | public void onReset() {
126 | mMainTextView.setVisibility(View.VISIBLE);
127 | mMainTextView.setText(R.string.pull_to_refresh_pull_label);
128 |
129 | mProgressTextView.setVisibility(View.GONE);
130 | mProgressTextView.setText("");
131 | }
132 |
133 | @Override
134 | public void onPulled(float percentagePulled) {
135 | mProgressTextView.setVisibility(View.VISIBLE);
136 | mProgressTextView.setText(Math.round(100f * percentagePulled) + "%");
137 | }
138 |
139 | @Override
140 | public void onRefreshStarted() {
141 | mMainTextView.setText(R.string.pull_to_refresh_refreshing_label);
142 | mProgressTextView.setVisibility(View.GONE);
143 | }
144 |
145 | @Override
146 | public void onReleaseToRefresh() {
147 | mMainTextView.setText(R.string.pull_to_refresh_release_label);
148 | }
149 |
150 | @Override
151 | public void onRefreshMinimized() {
152 | // In this header transformer, we will ignore this call
153 | }
154 |
155 | @Override
156 | public boolean showHeaderView() {
157 | final boolean changeVis = mHeaderView.getVisibility() != View.VISIBLE;
158 | if (changeVis) {
159 | mHeaderView.setVisibility(View.VISIBLE);
160 | }
161 | return changeVis;
162 | }
163 |
164 | @Override
165 | public boolean hideHeaderView() {
166 | final boolean changeVis = mHeaderView.getVisibility() == View.VISIBLE;
167 | if (changeVis) {
168 | mHeaderView.setVisibility(View.GONE);
169 | }
170 | return changeVis;
171 | }
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/PullToRefreshLayout.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.library;
18 |
19 | import android.app.Activity;
20 | import android.content.Context;
21 | import android.content.res.Configuration;
22 | import android.content.res.TypedArray;
23 | import android.text.TextUtils;
24 | import android.util.AttributeSet;
25 | import android.util.Log;
26 | import android.view.MotionEvent;
27 | import android.view.View;
28 | import android.widget.FrameLayout;
29 |
30 | import uk.co.senab.actionbarpulltorefresh.library.listeners.HeaderViewListener;
31 | import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.ViewDelegate;
32 |
33 | /**
34 | * The main component of the library. You wrap the views you wish to be 'pullable' within this layout.
35 | * This layout is setup by using the {@link ActionBarPullToRefresh} setup-wizard return by
36 | * @link ActionBarPullToRefresh#from(android.app.Activity)}.
37 | */
38 | public class PullToRefreshLayout extends FrameLayout {
39 |
40 | private static final boolean DEBUG = false;
41 | private static final String LOG_TAG = "PullToRefreshLayout";
42 |
43 | private PullToRefreshAttacher mPullToRefreshAttacher;
44 |
45 | public PullToRefreshLayout(Context context) {
46 | this(context, null);
47 | }
48 |
49 | public PullToRefreshLayout(Context context, AttributeSet attrs) {
50 | this(context, attrs, 0);
51 | }
52 |
53 | public PullToRefreshLayout(Context context, AttributeSet attrs, int defStyle) {
54 | super(context, attrs, defStyle);
55 | }
56 |
57 | /**
58 | * Manually set this Attacher's refreshing state. The header will be
59 | * displayed or hidden as requested.
60 | *
61 | * @param refreshing
62 | * - Whether the attacher should be in a refreshing state,
63 | */
64 | public final void setRefreshing(boolean refreshing) {
65 | ensureAttacher();
66 | mPullToRefreshAttacher.setRefreshing(refreshing);
67 | }
68 |
69 | /**
70 | * @return true if this Attacher is currently in a refreshing state.
71 | */
72 | public final boolean isRefreshing() {
73 | ensureAttacher();
74 | return mPullToRefreshAttacher.isRefreshing();
75 | }
76 |
77 | /**
78 | * Call this when your refresh is complete and this view should reset itself
79 | * (header view will be hidden).
80 | *
81 | * This is the equivalent of calling setRefreshing(false).
82 | */
83 | public final void setRefreshComplete() {
84 | ensureAttacher();
85 | mPullToRefreshAttacher.setRefreshComplete();
86 | }
87 |
88 | /**
89 | * Set a {@link uk.co.senab.actionbarpulltorefresh.library.listeners.HeaderViewListener} which is called when the visibility
90 | * state of the Header View has changed.
91 | *
92 | * @param listener
93 | */
94 | public final void setHeaderViewListener(HeaderViewListener listener) {
95 | ensureAttacher();
96 | mPullToRefreshAttacher.setHeaderViewListener(listener);
97 | }
98 |
99 | /**
100 | * @return The Header View which is displayed when the user is pulling, or
101 | * we are refreshing.
102 | */
103 | public final View getHeaderView() {
104 | ensureAttacher();
105 | return mPullToRefreshAttacher.getHeaderView();
106 | }
107 |
108 | /**
109 | * @return The Attacher
110 | */
111 | public final PullToRefreshAttacher getAttacher() {
112 | ensureAttacher();
113 | return mPullToRefreshAttacher;
114 | }
115 |
116 | /**
117 | * @return The HeaderTransformer currently used by this Attacher.
118 | */
119 | public HeaderTransformer getHeaderTransformer() {
120 | ensureAttacher();
121 | return mPullToRefreshAttacher.getHeaderTransformer();
122 | }
123 |
124 | @Override
125 | public final boolean onInterceptTouchEvent(MotionEvent event) {
126 | if (DEBUG) {
127 | Log.d(LOG_TAG, "onInterceptTouchEvent. " + event.toString());
128 | }
129 | if (isEnabled() && mPullToRefreshAttacher != null && getChildCount() > 0) {
130 | return mPullToRefreshAttacher.onInterceptTouchEvent(event);
131 | }
132 | return false;
133 | }
134 |
135 | @Override
136 | public final boolean onTouchEvent(MotionEvent event) {
137 | if (DEBUG) {
138 | Log.d(LOG_TAG, "onTouchEvent. " + event.toString());
139 | }
140 | if (isEnabled() && mPullToRefreshAttacher != null) {
141 | return mPullToRefreshAttacher.onTouchEvent(event);
142 | }
143 | return super.onTouchEvent(event);
144 | }
145 |
146 | @Override
147 | public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
148 | return new PullToRefreshLayout.LayoutParams(getContext(), attrs);
149 | }
150 |
151 | @Override
152 | protected void onDetachedFromWindow() {
153 | // Destroy the PullToRefreshAttacher
154 | if (mPullToRefreshAttacher != null) {
155 | mPullToRefreshAttacher.destroy();
156 | }
157 | super.onDetachedFromWindow();
158 | }
159 |
160 | @Override
161 | protected void onConfigurationChanged(Configuration newConfig) {
162 | if (mPullToRefreshAttacher != null) {
163 | mPullToRefreshAttacher.onConfigurationChanged(newConfig);
164 | }
165 | super.onConfigurationChanged(newConfig);
166 | }
167 |
168 | void setPullToRefreshAttacher(PullToRefreshAttacher attacher) {
169 | if (mPullToRefreshAttacher != null) {
170 | mPullToRefreshAttacher.destroy();
171 | }
172 | mPullToRefreshAttacher = attacher;
173 | }
174 |
175 | void addAllChildrenAsPullable() {
176 | ensureAttacher();
177 | for (int i = 0, z = getChildCount(); i < z; i++) {
178 | addRefreshableView(getChildAt(i));
179 | }
180 | }
181 |
182 | void addChildrenAsPullable(int[] viewIds) {
183 | for (int i = 0, z = viewIds.length; i < z; i++) {
184 | View view = findViewById(viewIds[i]);
185 | if (view != null) {
186 | addRefreshableView(findViewById(viewIds[i]));
187 | }
188 | }
189 | }
190 |
191 | void addChildrenAsPullable(View[] views) {
192 | for (int i = 0, z = views.length; i < z; i++) {
193 | if (views[i] != null) {
194 | addRefreshableView(views[i]);
195 | }
196 | }
197 | }
198 |
199 | void addRefreshableView(View view) {
200 | if (mPullToRefreshAttacher != null) {
201 | mPullToRefreshAttacher.addRefreshableView(view, getViewDelegateFromLayoutParams(view));
202 | }
203 | }
204 |
205 | ViewDelegate getViewDelegateFromLayoutParams(View view) {
206 | if (view != null && view.getLayoutParams() instanceof LayoutParams) {
207 | LayoutParams lp = (LayoutParams) view.getLayoutParams();
208 | String clazzName = lp.getViewDelegateClassName();
209 |
210 | if (!TextUtils.isEmpty(clazzName)) {
211 | // Lets convert any relative class names (i.e. .XYZViewDelegate)
212 | final int firstDot = clazzName.indexOf('.');
213 | if (firstDot == -1) {
214 | clazzName = getContext().getPackageName() + "." + clazzName;
215 | } else if (firstDot == 0) {
216 | clazzName = getContext().getPackageName() + clazzName;
217 | }
218 | return InstanceCreationUtils.instantiateViewDelegate(getContext(), clazzName);
219 | }
220 | }
221 | return null;
222 | }
223 |
224 | protected PullToRefreshAttacher createPullToRefreshAttacher(Activity activity,
225 | Options options) {
226 | return new PullToRefreshAttacher(activity, options != null ? options : new Options());
227 | }
228 |
229 | private void ensureAttacher() {
230 | if (mPullToRefreshAttacher == null) {
231 | throw new IllegalStateException("You need to setup the PullToRefreshLayout before using it");
232 | }
233 | }
234 |
235 | static class LayoutParams extends FrameLayout.LayoutParams {
236 | private final String mViewDelegateClassName;
237 |
238 | LayoutParams(Context c, AttributeSet attrs) {
239 | super(c, attrs);
240 |
241 | TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.PullToRefreshView);
242 | mViewDelegateClassName = a
243 | .getString(R.styleable.PullToRefreshView_ptrViewDelegateClass);
244 | a.recycle();
245 | }
246 |
247 | String getViewDelegateClassName() {
248 | return mViewDelegateClassName;
249 | }
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/DefaultHeaderTransformer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.library;
18 |
19 | import android.animation.Animator;
20 | import android.animation.AnimatorListenerAdapter;
21 | import android.animation.AnimatorSet;
22 | import android.animation.ObjectAnimator;
23 | import android.app.Activity;
24 | import android.content.Context;
25 | import android.content.res.Configuration;
26 | import android.content.res.TypedArray;
27 | import android.graphics.PixelFormat;
28 | import android.graphics.drawable.ClipDrawable;
29 | import android.graphics.drawable.Drawable;
30 | import android.graphics.drawable.ShapeDrawable;
31 | import android.graphics.drawable.shapes.RectShape;
32 | import android.os.Build;
33 | import android.util.TypedValue;
34 | import android.view.Gravity;
35 | import android.view.View;
36 | import android.view.ViewGroup;
37 | import android.view.animation.AccelerateInterpolator;
38 | import android.view.animation.Interpolator;
39 | import android.widget.RelativeLayout;
40 | import android.widget.TextView;
41 |
42 | import fr.castorflex.android.smoothprogressbar.SmoothProgressBar;
43 | import fr.castorflex.android.smoothprogressbar.SmoothProgressDrawable;
44 | import uk.co.senab.actionbarpulltorefresh.library.sdk.Compat;
45 |
46 | /**
47 | * Default Header Transformer.
48 | */
49 | public class DefaultHeaderTransformer extends HeaderTransformer {
50 |
51 | public static final int PROGRESS_BAR_STYLE_INSIDE = 0;
52 | public static final int PROGRESS_BAR_STYLE_OUTSIDE = 1;
53 |
54 | private View mHeaderView;
55 | private ViewGroup mContentLayout;
56 | private TextView mHeaderTextView;
57 | private SmoothProgressBar mHeaderProgressBar;
58 |
59 | private CharSequence mPullRefreshLabel, mRefreshingLabel, mReleaseLabel;
60 |
61 | private int mProgressDrawableColor;
62 |
63 | private long mAnimationDuration;
64 | private int mProgressBarStyle;
65 | private int mProgressBarHeight = RelativeLayout.LayoutParams.WRAP_CONTENT;
66 |
67 | private final Interpolator mInterpolator = new AccelerateInterpolator();
68 |
69 | protected DefaultHeaderTransformer() {
70 | final int min = getMinimumApiLevel();
71 | if (Build.VERSION.SDK_INT < min) {
72 | throw new IllegalStateException("This HeaderTransformer is designed to run on SDK "
73 | + min
74 | + "+. If using ActionBarSherlock or ActionBarCompat you should use the appropriate provided extra.");
75 | }
76 | }
77 |
78 | @Override
79 | public void onViewCreated(Activity activity, View headerView) {
80 | mHeaderView = headerView;
81 |
82 | // Get ProgressBar and TextView
83 | mHeaderProgressBar = (SmoothProgressBar) headerView.findViewById(R.id.ptr_progress);
84 | mHeaderTextView = (TextView) headerView.findViewById(R.id.ptr_text);
85 | mContentLayout = (ViewGroup) headerView.findViewById(R.id.ptr_content);
86 |
87 | // Default Labels to display
88 | mPullRefreshLabel = activity.getString(R.string.pull_to_refresh_pull_label);
89 | mRefreshingLabel = activity.getString(R.string.pull_to_refresh_refreshing_label);
90 | mReleaseLabel = activity.getString(R.string.pull_to_refresh_release_label);
91 |
92 | mAnimationDuration = activity.getResources()
93 | .getInteger(android.R.integer.config_shortAnimTime);
94 |
95 | mProgressDrawableColor = activity.getResources()
96 | .getColor(R.color.default_progress_bar_color);
97 |
98 | // Setup the View styles
99 | setupViewsFromStyles(activity, headerView);
100 |
101 | applyProgressBarStyle();
102 |
103 | // Apply any custom ProgressBar colors and corner radius
104 | applyProgressBarSettings();
105 |
106 | // FIXME: I do not like this call here
107 | onReset();
108 | }
109 |
110 | @Override
111 | public void onConfigurationChanged(Activity activity, Configuration newConfig) {
112 | setupViewsFromStyles(activity, getHeaderView());
113 | }
114 |
115 | @Override
116 | public void onReset() {
117 | // Reset Progress Bar
118 | if (mHeaderProgressBar != null) {
119 | mHeaderProgressBar.setVisibility(View.VISIBLE);
120 | mHeaderProgressBar.setProgress(0);
121 | mHeaderProgressBar.setIndeterminate(false);
122 | }
123 |
124 | // Reset Text View
125 | if (mHeaderTextView != null) {
126 | mHeaderTextView.setVisibility(View.VISIBLE);
127 | mHeaderTextView.setText(mPullRefreshLabel);
128 | }
129 |
130 | // Reset the Content Layout
131 | if (mContentLayout != null) {
132 | mContentLayout.setVisibility(View.VISIBLE);
133 | Compat.setAlpha(mContentLayout, 1f);
134 | }
135 | }
136 |
137 | @Override
138 | public void onPulled(float percentagePulled) {
139 | if (mHeaderProgressBar != null) {
140 | mHeaderProgressBar.setVisibility(View.VISIBLE);
141 | final float progress = mInterpolator.getInterpolation(percentagePulled);
142 | mHeaderProgressBar.setProgress(Math.round(mHeaderProgressBar.getMax() * progress));
143 | }
144 | }
145 |
146 | @Override
147 | public void onRefreshStarted() {
148 | if (mHeaderTextView != null) {
149 | mHeaderTextView.setText(mRefreshingLabel);
150 | }
151 | if (mHeaderProgressBar != null) {
152 | mHeaderProgressBar.setVisibility(View.VISIBLE);
153 | mHeaderProgressBar.setIndeterminate(true);
154 | }
155 | }
156 |
157 | @Override
158 | public void onReleaseToRefresh() {
159 | if (mHeaderTextView != null) {
160 | mHeaderTextView.setText(mReleaseLabel);
161 | }
162 | if (mHeaderProgressBar != null) {
163 | mHeaderProgressBar.setProgress(mHeaderProgressBar.getMax());
164 | }
165 | }
166 |
167 | @Override
168 | public void onRefreshMinimized() {
169 | // Here we fade out most of the header, leaving just the progress bar
170 | if (mContentLayout != null) {
171 | ObjectAnimator.ofFloat(mContentLayout, "alpha", 1f, 0f).start();
172 | }
173 | }
174 |
175 | public View getHeaderView() {
176 | return mHeaderView;
177 | }
178 |
179 | @Override
180 | public boolean showHeaderView() {
181 | final boolean changeVis = mHeaderView.getVisibility() != View.VISIBLE;
182 |
183 | if (changeVis) {
184 | mHeaderView.setVisibility(View.VISIBLE);
185 | AnimatorSet animSet = new AnimatorSet();
186 | ObjectAnimator transAnim = ObjectAnimator.ofFloat(mContentLayout, "translationY",
187 | -mContentLayout.getHeight(), 0f);
188 | ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mHeaderView, "alpha", 0f, 1f);
189 | animSet.playTogether(transAnim, alphaAnim);
190 | animSet.setDuration(mAnimationDuration);
191 | animSet.start();
192 | }
193 |
194 | return changeVis;
195 | }
196 |
197 | @Override
198 | public boolean hideHeaderView() {
199 | final boolean changeVis = mHeaderView.getVisibility() != View.GONE;
200 |
201 | if (changeVis) {
202 | Animator animator;
203 | if (mContentLayout.getAlpha() >= 0.5f) {
204 | // If the content layout is showing, translate and fade out
205 | animator = new AnimatorSet();
206 | ObjectAnimator transAnim = ObjectAnimator.ofFloat(mContentLayout, "translationY",
207 | 0f, -mContentLayout.getHeight());
208 | ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mHeaderView, "alpha", 1f, 0f);
209 | ((AnimatorSet) animator).playTogether(transAnim, alphaAnim);
210 | } else {
211 | // If the content layout isn't showing (minimized), just fade out
212 | animator = ObjectAnimator.ofFloat(mHeaderView, "alpha", 1f, 0f);
213 | }
214 | animator.setDuration(mAnimationDuration);
215 | animator.addListener(new HideAnimationCallback());
216 | animator.start();
217 | }
218 |
219 | return changeVis;
220 | }
221 |
222 | /**
223 | * Set color to apply to the progress bar.
224 | *
225 | * The best way to apply a color is to load the color from resources: {@code
226 | * setProgressBarColor(getResources().getColor(R.color.your_color_name))}.
227 | *
228 | * @param color The color to use.
229 | */
230 | public void setProgressBarColor(int color) {
231 | if (color != mProgressDrawableColor) {
232 | mProgressDrawableColor = color;
233 | applyProgressBarSettings();
234 | }
235 | }
236 |
237 | /**
238 | * Set the progress bar style. {@code style} must be one of {@link #PROGRESS_BAR_STYLE_OUTSIDE}
239 | * or {@link #PROGRESS_BAR_STYLE_INSIDE}.
240 | */
241 | public void setProgressBarStyle(int style) {
242 | if (mProgressBarStyle != style) {
243 | mProgressBarStyle = style;
244 | applyProgressBarStyle();
245 | }
246 | }
247 |
248 | /**
249 | * Set the progress bar height.
250 | */
251 | public void setProgressBarHeight(int height) {
252 | if (mProgressBarHeight != height) {
253 | mProgressBarHeight = height;
254 | applyProgressBarStyle();
255 | }
256 | }
257 |
258 | /**
259 | * Set Text to show to prompt the user is pull (or keep pulling).
260 | *
261 | * @param pullText - Text to display.
262 | */
263 | public void setPullText(CharSequence pullText) {
264 | mPullRefreshLabel = pullText;
265 | if (mHeaderTextView != null) {
266 | mHeaderTextView.setText(mPullRefreshLabel);
267 | }
268 | }
269 |
270 | /**
271 | * Set Text to show to tell the user that a refresh is currently in progress.
272 | *
273 | * @param refreshingText - Text to display.
274 | */
275 | public void setRefreshingText(CharSequence refreshingText) {
276 | mRefreshingLabel = refreshingText;
277 | }
278 |
279 | /**
280 | * Set Text to show to tell the user has scrolled enough to refresh.
281 | *
282 | * @param releaseText - Text to display.
283 | */
284 | public void setReleaseText(CharSequence releaseText) {
285 | mReleaseLabel = releaseText;
286 | }
287 |
288 | private void setupViewsFromStyles(Activity activity, View headerView) {
289 | final TypedArray styleAttrs = obtainStyledAttrsFromThemeAttr(activity,
290 | R.attr.ptrHeaderStyle, R.styleable.PullToRefreshHeader);
291 |
292 | // Retrieve the Action Bar size from the app theme or the Action Bar's style
293 | if (mContentLayout != null) {
294 | final int height = styleAttrs.getDimensionPixelSize(
295 | R.styleable.PullToRefreshHeader_ptrHeaderHeight, getActionBarSize(activity));
296 | mContentLayout.getLayoutParams().height = height;
297 | mContentLayout.requestLayout();
298 | }
299 |
300 | // Retrieve the Action Bar background from the app theme or the Action Bar's style (see #93)
301 | Drawable bg = styleAttrs.hasValue(R.styleable.PullToRefreshHeader_ptrHeaderBackground)
302 | ? styleAttrs.getDrawable(R.styleable.PullToRefreshHeader_ptrHeaderBackground)
303 | : getActionBarBackground(activity);
304 | if (bg != null) {
305 | mHeaderTextView.setBackgroundDrawable(bg);
306 |
307 | // If we have an opaque background we can remove the background from the content layout
308 | if (mContentLayout != null && bg.getOpacity() == PixelFormat.OPAQUE) {
309 | mContentLayout.setBackgroundResource(0);
310 | }
311 | }
312 |
313 | // Retrieve the Progress Bar Color the style
314 | if (styleAttrs.hasValue(R.styleable.PullToRefreshHeader_ptrProgressBarColor)) {
315 | mProgressDrawableColor = styleAttrs.getColor(
316 | R.styleable.PullToRefreshHeader_ptrProgressBarColor, mProgressDrawableColor);
317 | }
318 |
319 | mProgressBarStyle = styleAttrs.getInt(
320 | R.styleable.PullToRefreshHeader_ptrProgressBarStyle, PROGRESS_BAR_STYLE_OUTSIDE);
321 |
322 | if (styleAttrs.hasValue(R.styleable.PullToRefreshHeader_ptrProgressBarHeight)) {
323 | mProgressBarHeight = styleAttrs.getDimensionPixelSize(
324 | R.styleable.PullToRefreshHeader_ptrProgressBarHeight, mProgressBarHeight);
325 | }
326 |
327 | // Retrieve the text strings from the style (if they're set)
328 | if (styleAttrs.hasValue(R.styleable.PullToRefreshHeader_ptrPullText)) {
329 | mPullRefreshLabel = styleAttrs.getString(R.styleable.PullToRefreshHeader_ptrPullText);
330 | }
331 | if (styleAttrs.hasValue(R.styleable.PullToRefreshHeader_ptrRefreshingText)) {
332 | mRefreshingLabel = styleAttrs
333 | .getString(R.styleable.PullToRefreshHeader_ptrRefreshingText);
334 | }
335 | if (styleAttrs.hasValue(R.styleable.PullToRefreshHeader_ptrReleaseText)) {
336 | mReleaseLabel = styleAttrs.getString(R.styleable.PullToRefreshHeader_ptrReleaseText);
337 | }
338 |
339 | styleAttrs.recycle();
340 | }
341 |
342 | private void applyProgressBarStyle() {
343 | RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
344 | RelativeLayout.LayoutParams.MATCH_PARENT, mProgressBarHeight);
345 |
346 | switch (mProgressBarStyle) {
347 | case PROGRESS_BAR_STYLE_INSIDE:
348 | lp.addRule(RelativeLayout.ALIGN_BOTTOM, R.id.ptr_content);
349 | break;
350 | case PROGRESS_BAR_STYLE_OUTSIDE:
351 | lp.addRule(RelativeLayout.BELOW, R.id.ptr_content);
352 | break;
353 | }
354 |
355 | mHeaderProgressBar.setLayoutParams(lp);
356 | }
357 |
358 | private void applyProgressBarSettings() {
359 | if (mHeaderProgressBar != null) {
360 | final int strokeWidth = mHeaderProgressBar.getResources()
361 | .getDimensionPixelSize(R.dimen.ptr_progress_bar_stroke_width);
362 |
363 | mHeaderProgressBar.setIndeterminateDrawable(
364 | new SmoothProgressDrawable.Builder(mHeaderProgressBar.getContext())
365 | .color(mProgressDrawableColor)
366 | .strokeWidth(mProgressBarHeight)
367 | .build());
368 |
369 | ShapeDrawable shape = new ShapeDrawable();
370 | shape.setShape(new RectShape());
371 | shape.getPaint().setColor(mProgressDrawableColor);
372 | ClipDrawable clipDrawable = new ClipDrawable(shape, Gravity.CENTER, ClipDrawable.HORIZONTAL);
373 |
374 | mHeaderProgressBar.setProgressDrawable(clipDrawable);
375 | }
376 | }
377 |
378 | protected Drawable getActionBarBackground(Context context) {
379 | int[] android_styleable_ActionBar = {android.R.attr.background};
380 |
381 | // Now get the action bar style values...
382 | TypedArray abStyle = obtainStyledAttrsFromThemeAttr(context, android.R.attr.actionBarStyle,
383 | android_styleable_ActionBar);
384 | try {
385 | // background is the first attr in the array above so it's index is 0.
386 | return abStyle.getDrawable(0);
387 | } finally {
388 | abStyle.recycle();
389 | }
390 | }
391 |
392 | protected int getActionBarSize(Context context) {
393 | int[] attrs = {android.R.attr.actionBarSize};
394 | TypedArray values = context.getTheme().obtainStyledAttributes(attrs);
395 | try {
396 | return values.getDimensionPixelSize(0, 0);
397 | } finally {
398 | values.recycle();
399 | }
400 | }
401 |
402 | protected int getActionBarTitleStyle(Context context) {
403 | int[] android_styleable_ActionBar = {android.R.attr.titleTextStyle};
404 |
405 | // Now get the action bar style values...
406 | TypedArray abStyle = obtainStyledAttrsFromThemeAttr(context, android.R.attr.actionBarStyle,
407 | android_styleable_ActionBar);
408 | try {
409 | // titleTextStyle is the first attr in the array above so it's index is 0.
410 | return abStyle.getResourceId(0, 0);
411 | } finally {
412 | abStyle.recycle();
413 | }
414 | }
415 |
416 | protected int getMinimumApiLevel() {
417 | return Build.VERSION_CODES.ICE_CREAM_SANDWICH;
418 | }
419 |
420 | class HideAnimationCallback extends AnimatorListenerAdapter {
421 | @Override
422 | public void onAnimationEnd(Animator animation) {
423 | View headerView = getHeaderView();
424 | if (headerView != null) {
425 | headerView.setVisibility(View.GONE);
426 | }
427 | onReset();
428 | }
429 | }
430 |
431 | protected static TypedArray obtainStyledAttrsFromThemeAttr(Context context, int themeAttr,
432 | int[] styleAttrs) {
433 | // Need to get resource id of style pointed to from the theme attr
434 | TypedValue outValue = new TypedValue();
435 | context.getTheme().resolveAttribute(themeAttr, outValue, true);
436 | final int styleResId = outValue.resourceId;
437 |
438 | // Now return the values (from styleAttrs) from the style
439 | return context.obtainStyledAttributes(styleResId, styleAttrs);
440 | }
441 | }
442 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/actionbarpulltorefresh/library/PullToRefreshAttacher.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package uk.co.senab.actionbarpulltorefresh.library;
18 |
19 | import android.annotation.TargetApi;
20 | import android.app.ActionBar;
21 | import android.app.Activity;
22 | import android.content.Context;
23 | import android.content.res.Configuration;
24 | import android.graphics.PixelFormat;
25 | import android.graphics.Rect;
26 | import android.os.Build;
27 | import android.util.Log;
28 | import android.view.Gravity;
29 | import android.view.LayoutInflater;
30 | import android.view.MotionEvent;
31 | import android.view.View;
32 | import android.view.ViewConfiguration;
33 | import android.view.ViewGroup;
34 | import android.view.WindowManager;
35 | import android.widget.AbsListView;
36 |
37 | import java.util.WeakHashMap;
38 |
39 | import uk.co.senab.actionbarpulltorefresh.library.listeners.HeaderViewListener;
40 | import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener;
41 | import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.ViewDelegate;
42 |
43 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
44 | public class PullToRefreshAttacher {
45 |
46 | private static final boolean DEBUG = false;
47 | private static final String LOG_TAG = "PullToRefreshAttacher";
48 |
49 | /* Member Variables */
50 |
51 | private EnvironmentDelegate mEnvironmentDelegate;
52 | private HeaderTransformer mHeaderTransformer;
53 |
54 | private OnRefreshListener mOnRefreshListener;
55 |
56 | private Activity mActivity;
57 | private View mHeaderView;
58 | private HeaderViewListener mHeaderViewListener;
59 |
60 | private final int mTouchSlop;
61 | private final float mRefreshScrollDistance;
62 |
63 | private float mInitialMotionY, mLastMotionY, mPullBeginY;
64 | private float mInitialMotionX;
65 | private boolean mIsBeingDragged, mIsRefreshing, mHandlingTouchEventFromDown;
66 | private View mViewBeingDragged;
67 |
68 | private final WeakHashMap mRefreshableViews;
69 |
70 | private final boolean mRefreshOnUp;
71 | private final int mRefreshMinimizeDelay;
72 | private final boolean mRefreshMinimize;
73 | private boolean mIsDestroyed = false;
74 |
75 | private final int[] mViewLocationResult = new int[2];
76 | private final Rect mRect = new Rect();
77 | private boolean mSubviewScrolledOnTop;
78 |
79 | private final AddHeaderViewRunnable mAddHeaderViewRunnable;
80 |
81 | private boolean mRefreshDisabled;
82 |
83 | protected PullToRefreshAttacher(Activity activity, Options options) {
84 | if (activity == null) {
85 | throw new IllegalArgumentException("activity cannot be null");
86 | }
87 | if (options == null) {
88 | Log.i(LOG_TAG, "Given null options so using default options.");
89 | options = new Options();
90 | }
91 |
92 | mActivity = activity;
93 | mRefreshableViews = new WeakHashMap();
94 |
95 | // Copy necessary values from options
96 | mRefreshScrollDistance = options.refreshScrollDistance;
97 | mRefreshOnUp = options.refreshOnUp;
98 | mRefreshMinimizeDelay = options.refreshMinimizeDelay;
99 | mRefreshMinimize = options.refreshMinimize;
100 |
101 | // EnvironmentDelegate
102 | mEnvironmentDelegate = options.environmentDelegate != null
103 | ? options.environmentDelegate
104 | : createDefaultEnvironmentDelegate();
105 |
106 | // Header Transformer
107 | mHeaderTransformer = options.headerTransformer != null
108 | ? options.headerTransformer
109 | : createDefaultHeaderTransformer();
110 |
111 | // Get touch slop for use later
112 | mTouchSlop = ViewConfiguration.get(activity).getScaledTouchSlop();
113 |
114 | // Get Window Decor View
115 | final ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
116 |
117 | // Create Header view and then add to Decor View
118 | mHeaderView = LayoutInflater.from(
119 | mEnvironmentDelegate.getContextForInflater(activity)).inflate(
120 | options.headerLayout, decorView, false);
121 | if (mHeaderView == null) {
122 | throw new IllegalArgumentException("Must supply valid layout id for header.");
123 | }
124 | // Make Header View invisible so it still gets a layout pass
125 | mHeaderView.setVisibility(View.INVISIBLE);
126 |
127 | // Notify transformer
128 | mHeaderTransformer.onViewCreated(activity, mHeaderView);
129 |
130 | // Now HeaderView to Activity
131 | mAddHeaderViewRunnable = new AddHeaderViewRunnable();
132 | mAddHeaderViewRunnable.start();
133 | }
134 |
135 | /**
136 | * Add a view which will be used to initiate refresh requests.
137 | *
138 | * @param view View which will be used to initiate refresh requests.
139 | */
140 | void addRefreshableView(View view, ViewDelegate viewDelegate) {
141 | if (isDestroyed()) return;
142 |
143 | // Check to see if view is null
144 | if (view == null) {
145 | Log.i(LOG_TAG, "Refreshable View is null.");
146 | return;
147 | }
148 |
149 | // ViewDelegate
150 | if (viewDelegate == null) {
151 | viewDelegate = InstanceCreationUtils.getBuiltInViewDelegate(view);
152 | }
153 |
154 | // View to detect refreshes for
155 | mRefreshableViews.put(view, viewDelegate);
156 | setOnScrollListener(view);
157 | }
158 |
159 | void useViewDelegate(Class> viewClass, ViewDelegate delegate) {
160 | for (View view : mRefreshableViews.keySet()) {
161 | if (viewClass.isInstance(view)) {
162 | mRefreshableViews.put(view, delegate);
163 | setOnScrollListener(view);
164 | }
165 | }
166 | }
167 |
168 | /**
169 | * Set on scroll listener to detect scroll events
170 | */
171 | void setOnScrollListener(View view) {
172 | if (view instanceof AbsListView) {
173 | ((AbsListView) view).setOnScrollListener(new AbsListView.OnScrollListener() {
174 | @Override
175 | public void onScrollStateChanged(AbsListView v, int scrollState) {
176 | }
177 |
178 | @Override
179 | public void onScroll(AbsListView v, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
180 | View childView = v.getChildAt(0);
181 | int offset = 0;
182 | if (childView != null) {
183 | offset = childView.getTop();
184 | }
185 | boolean oldSubviewScrolledOnTop = mSubviewScrolledOnTop;
186 | mSubviewScrolledOnTop = firstVisibleItem == 0 && Math.abs(offset) < 10;
187 | if (mSubviewScrolledOnTop != oldSubviewScrolledOnTop) {
188 | mHeaderTransformer.onTopScrollChanged(mSubviewScrolledOnTop);
189 | }
190 | }
191 | });
192 | }
193 | }
194 |
195 | public void setRefreshDisabled(boolean disabled) {
196 | mRefreshDisabled = disabled;
197 | }
198 |
199 | /**
200 | * Clear all views which were previously used to initiate refresh requests.
201 | */
202 | void clearRefreshableViews() {
203 | mRefreshableViews.clear();
204 | }
205 |
206 | /**
207 | * This method should be called by your Activity's or Fragment's
208 | * onConfigurationChanged method.
209 | *
210 | * @param newConfig The new configuration
211 | */
212 | public void onConfigurationChanged(Configuration newConfig) {
213 | mHeaderTransformer.onConfigurationChanged(mActivity, newConfig);
214 | }
215 |
216 | /**
217 | * Manually set this Attacher's refreshing state. The header will be
218 | * displayed or hidden as requested.
219 | *
220 | * @param refreshing
221 | * - Whether the attacher should be in a refreshing state,
222 | */
223 | final void setRefreshing(boolean refreshing) {
224 | setRefreshingInt(null, refreshing, false);
225 | }
226 |
227 | /**
228 | * @return true if this Attacher is currently in a refreshing state.
229 | */
230 | final boolean isRefreshing() {
231 | return mIsRefreshing;
232 | }
233 |
234 | /**
235 | * Call this when your refresh is complete and this view should reset itself
236 | * (header view will be hidden).
237 | *
238 | * This is the equivalent of calling setRefreshing(false).
239 | */
240 | final void setRefreshComplete() {
241 | setRefreshingInt(null, false, false);
242 | }
243 |
244 | /**
245 | * Set the Listener to be called when a refresh is initiated.
246 | */
247 | void setOnRefreshListener(OnRefreshListener listener) {
248 | mOnRefreshListener = listener;
249 | }
250 |
251 | void destroy() {
252 | if (mIsDestroyed) return; // We've already been destroyed
253 |
254 | // Remove the Header View from the Activity
255 | removeHeaderViewFromActivity(mHeaderView);
256 |
257 | // Lets clear out all of our internal state
258 | clearRefreshableViews();
259 |
260 | mActivity = null;
261 | mHeaderView = null;
262 | mHeaderViewListener = null;
263 | mEnvironmentDelegate = null;
264 | mHeaderTransformer = null;
265 |
266 | mIsDestroyed = true;
267 | }
268 |
269 | /**
270 | * Set a {@link HeaderViewListener} which is called when the visibility
271 | * state of the Header View has changed.
272 | */
273 | final void setHeaderViewListener(HeaderViewListener listener) {
274 | mHeaderViewListener = listener;
275 | }
276 |
277 | /**
278 | * @return The Header View which is displayed when the user is pulling, or
279 | * we are refreshing.
280 | */
281 | final View getHeaderView() {
282 | return mHeaderView;
283 | }
284 |
285 | /**
286 | * @return The HeaderTransformer currently used by this Attacher.
287 | */
288 | HeaderTransformer getHeaderTransformer() {
289 | return mHeaderTransformer;
290 | }
291 |
292 | final boolean onInterceptTouchEvent(MotionEvent event) {
293 | if (DEBUG) {
294 | Log.d(LOG_TAG, "onInterceptTouchEvent: " + event.toString());
295 | }
296 |
297 | // If we're not enabled or currently refreshing don't handle any touch
298 | // events
299 | if (isRefreshing()) {
300 | return false;
301 | }
302 |
303 | final float x = event.getX(), y = event.getY();
304 |
305 | switch (event.getAction()) {
306 | case MotionEvent.ACTION_MOVE: {
307 | // We're not currently being dragged so check to see if the user has
308 | // scrolled enough
309 | if (!mIsBeingDragged && mInitialMotionY > 0f) {
310 | final float yDiff = y - mInitialMotionY;
311 | final float xDiff = x - mInitialMotionX;
312 |
313 | if (yDiff > xDiff && yDiff > mTouchSlop) {
314 | mIsBeingDragged = true;
315 | onPullStarted(y);
316 | } else if (yDiff < -mTouchSlop) {
317 | resetTouch();
318 | }
319 | }
320 | break;
321 | }
322 |
323 | case MotionEvent.ACTION_DOWN: {
324 | // If we're already refreshing, ignore
325 | if (canRefresh(true)) {
326 | for (View view : mRefreshableViews.keySet()) {
327 | if (isViewBeingDragged(view, event)) {
328 | mInitialMotionX = x;
329 | mInitialMotionY = y;
330 | mViewBeingDragged = view;
331 | }
332 | }
333 | }
334 | break;
335 | }
336 |
337 | case MotionEvent.ACTION_CANCEL:
338 | case MotionEvent.ACTION_UP: {
339 | resetTouch();
340 | break;
341 | }
342 | }
343 |
344 | if (DEBUG) Log.d(LOG_TAG, "onInterceptTouchEvent. Returning " + mIsBeingDragged);
345 |
346 | return mIsBeingDragged;
347 | }
348 |
349 | final boolean isViewBeingDragged(View view, MotionEvent event) {
350 | if (view.isShown() && mRefreshableViews.containsKey(view)) {
351 | // First we need to set the rect to the view's screen co-ordinates
352 | view.getLocationOnScreen(mViewLocationResult);
353 | final int viewLeft = mViewLocationResult[0], viewTop = mViewLocationResult[1];
354 | mRect.set(viewLeft, viewTop, viewLeft + view.getWidth(), viewTop + view.getHeight());
355 |
356 | if (DEBUG) Log.d(LOG_TAG, "isViewBeingDragged. View Rect: " + mRect.toString());
357 |
358 | final int rawX = (int) event.getRawX(), rawY = (int) event.getRawY();
359 | if (mRect.contains(rawX, rawY)) {
360 | // The Touch Event is within the View's display Rect
361 | ViewDelegate delegate = mRefreshableViews.get(view);
362 | if (delegate != null) {
363 | // Now call the delegate, converting the X/Y into the View's co-ordinate system
364 | return delegate.isReadyForPull(view, rawX - mRect.left, rawY - mRect.top);
365 | }
366 | }
367 | }
368 | return false;
369 | }
370 |
371 | final boolean onTouchEvent(MotionEvent event) {
372 | if (DEBUG) {
373 | Log.d(LOG_TAG, "onTouchEvent: " + event.toString());
374 | }
375 |
376 | // Record whether our handling is started from ACTION_DOWN
377 | if (event.getAction() == MotionEvent.ACTION_DOWN) {
378 | mHandlingTouchEventFromDown = true;
379 | }
380 |
381 | // If we're being called from ACTION_DOWN then we must call through to
382 | // onInterceptTouchEvent until it sets mIsBeingDragged
383 | if (mHandlingTouchEventFromDown && !mIsBeingDragged) {
384 | onInterceptTouchEvent(event);
385 | return true;
386 | }
387 |
388 | if (mViewBeingDragged == null) {
389 | return false;
390 | }
391 |
392 | switch (event.getAction()) {
393 | case MotionEvent.ACTION_MOVE: {
394 | // If we're already refreshing ignore it
395 | if (isRefreshing()) {
396 | return false;
397 | }
398 | final float y = event.getY();
399 |
400 | if (mIsBeingDragged && y != mLastMotionY) {
401 | final float yDx = y - mLastMotionY;
402 | /**
403 | * Check to see if the user is scrolling the right direction
404 | * (down). We allow a small scroll up which is the check against
405 | * negative touch slop.
406 | */
407 | if (yDx >= -mTouchSlop) {
408 | onPull(mViewBeingDragged, y);
409 | // Only record the y motion if the user has scrolled down.
410 | if (yDx > 0f) {
411 | mLastMotionY = y;
412 | }
413 | } else {
414 | onPullEnded();
415 | resetTouch();
416 | }
417 | }
418 | break;
419 | }
420 |
421 | case MotionEvent.ACTION_CANCEL:
422 | case MotionEvent.ACTION_UP: {
423 | checkScrollForRefresh(mViewBeingDragged);
424 | if (mIsBeingDragged) {
425 | onPullEnded();
426 | }
427 | resetTouch();
428 | break;
429 | }
430 | }
431 |
432 | return true;
433 | }
434 |
435 | void minimizeHeader() {
436 | if (isDestroyed()) return;
437 |
438 | mHeaderTransformer.onRefreshMinimized();
439 |
440 | if (mHeaderViewListener != null) {
441 | mHeaderViewListener.onStateChanged(mHeaderView, HeaderViewListener.STATE_MINIMIZED);
442 | }
443 | }
444 |
445 | void resetTouch() {
446 | mIsBeingDragged = false;
447 | mHandlingTouchEventFromDown = false;
448 | mInitialMotionY = mLastMotionY = mPullBeginY = -1f;
449 | }
450 |
451 | void onPullStarted(float y) {
452 | if (DEBUG) {
453 | Log.d(LOG_TAG, "onPullStarted");
454 | }
455 | showHeaderView();
456 | mPullBeginY = y;
457 | }
458 |
459 | void onPull(View view, float y) {
460 | if (DEBUG) {
461 | Log.d(LOG_TAG, "onPull");
462 | }
463 |
464 | if (mRefreshDisabled) {
465 | return ;
466 | }
467 |
468 | final float pxScrollForRefresh = getScrollNeededForRefresh(view);
469 | final float scrollLength = y - mPullBeginY;
470 |
471 | if (scrollLength < pxScrollForRefresh) {
472 | mHeaderTransformer.onPulled(scrollLength / pxScrollForRefresh);
473 | } else {
474 | if (mRefreshOnUp) {
475 | mHeaderTransformer.onReleaseToRefresh();
476 | } else {
477 | setRefreshingInt(view, true, true);
478 | }
479 | }
480 | }
481 |
482 | void onPullEnded() {
483 | if (DEBUG) {
484 | Log.d(LOG_TAG, "onPullEnded");
485 | }
486 | if (!mIsRefreshing) {
487 | reset(true);
488 | }
489 | }
490 |
491 | void showHeaderView() {
492 | updateHeaderViewPosition(mHeaderView);
493 | if (mHeaderTransformer.showHeaderView()) {
494 | if (mHeaderViewListener != null) {
495 | mHeaderViewListener.onStateChanged(mHeaderView,
496 | HeaderViewListener.STATE_VISIBLE);
497 | }
498 | }
499 | }
500 |
501 | void hideHeaderView() {
502 | if (mHeaderTransformer.hideHeaderView()) {
503 | if (mHeaderViewListener != null) {
504 | mHeaderViewListener.onStateChanged(mHeaderView,
505 | HeaderViewListener.STATE_HIDDEN);
506 | }
507 | }
508 | }
509 |
510 | protected final Activity getAttachedActivity() {
511 | return mActivity;
512 | }
513 |
514 | protected EnvironmentDelegate createDefaultEnvironmentDelegate() {
515 | return new EnvironmentDelegate() {
516 | @Override
517 | public Context getContextForInflater(Activity activity) {
518 | Context context = null;
519 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
520 | ActionBar ab = activity.getActionBar();
521 | if (ab != null) {
522 | context = ab.getThemedContext();
523 | }
524 | }
525 | if (context == null) {
526 | context = activity;
527 | }
528 | return context;
529 | }
530 | };
531 | }
532 |
533 | protected HeaderTransformer createDefaultHeaderTransformer() {
534 | return new DefaultHeaderTransformer();
535 | }
536 |
537 | private boolean checkScrollForRefresh(View view) {
538 | if (mRefreshDisabled) {
539 | return false;
540 | }
541 |
542 | if (mIsBeingDragged && mRefreshOnUp && view != null) {
543 | if (mLastMotionY - mPullBeginY >= getScrollNeededForRefresh(view)) {
544 | setRefreshingInt(view, true, true);
545 | return true;
546 | }
547 | }
548 | return false;
549 | }
550 |
551 | private void setRefreshingInt(View view, boolean refreshing, boolean fromTouch) {
552 | if (isDestroyed()) return;
553 |
554 | if (DEBUG) Log.d(LOG_TAG, "setRefreshingInt: " + refreshing);
555 | // Check to see if we need to do anything
556 | if (mIsRefreshing == refreshing) {
557 | return;
558 | }
559 |
560 | resetTouch();
561 |
562 | if (refreshing && canRefresh(fromTouch)) {
563 | startRefresh(view, fromTouch);
564 | } else {
565 | reset(fromTouch);
566 | }
567 | }
568 |
569 | /**
570 | * @param fromTouch Whether this is being invoked from a touch event
571 | * @return true if we're currently in a state where a refresh can be
572 | * started.
573 | */
574 | private boolean canRefresh(boolean fromTouch) {
575 | return !mIsRefreshing && (!fromTouch || mOnRefreshListener != null);
576 | }
577 |
578 | private float getScrollNeededForRefresh(View view) {
579 | return view.getHeight() * mRefreshScrollDistance;
580 | }
581 |
582 | private void reset(boolean fromTouch) {
583 | // Update isRefreshing state
584 | mIsRefreshing = false;
585 |
586 | // Remove any minimize callbacks
587 | if (mRefreshMinimize) {
588 | getHeaderView().removeCallbacks(mRefreshMinimizeRunnable);
589 | }
590 |
591 | // Hide Header View
592 | hideHeaderView();
593 | }
594 |
595 | private void startRefresh(View view, boolean fromTouch) {
596 | // Update isRefreshing state
597 | mIsRefreshing = true;
598 |
599 | // Call OnRefreshListener if this call has originated from a touch event
600 | if (fromTouch) {
601 | if (mOnRefreshListener != null) {
602 | mOnRefreshListener.onRefreshStarted(view);
603 | }
604 | }
605 |
606 | // Call Transformer
607 | mHeaderTransformer.onRefreshStarted();
608 |
609 | // Show Header View
610 | showHeaderView();
611 |
612 | // Post a runnable to minimize the refresh header
613 | if (mRefreshMinimize) {
614 | if (mRefreshMinimizeDelay > 0) {
615 | getHeaderView().postDelayed(mRefreshMinimizeRunnable, mRefreshMinimizeDelay);
616 | } else {
617 | getHeaderView().post(mRefreshMinimizeRunnable);
618 | }
619 | }
620 | }
621 |
622 | private boolean isDestroyed() {
623 | if (mIsDestroyed) {
624 | Log.i(LOG_TAG, "PullToRefreshAttacher is destroyed.");
625 | }
626 | return mIsDestroyed;
627 | }
628 |
629 | protected void addHeaderViewToActivity(View headerView) {
630 | // Get the Display Rect of the Decor View
631 | mActivity.getWindow().getDecorView().getWindowVisibleDisplayFrame(mRect);
632 |
633 | // Honour the requested layout params
634 | int width = WindowManager.LayoutParams.MATCH_PARENT;
635 | int height = WindowManager.LayoutParams.WRAP_CONTENT;
636 | ViewGroup.LayoutParams requestedLp = headerView.getLayoutParams();
637 | if (requestedLp != null) {
638 | width = requestedLp.width;
639 | height = requestedLp.height;
640 | }
641 |
642 | // Create LayoutParams for adding the View as a panel
643 | WindowManager.LayoutParams wlp = new WindowManager.LayoutParams(width, height,
644 | WindowManager.LayoutParams.TYPE_APPLICATION_PANEL,
645 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
646 | PixelFormat.TRANSLUCENT);
647 | wlp.x = 0;
648 | wlp.y = mRect.top;
649 | wlp.gravity = Gravity.TOP;
650 |
651 | // Workaround for Issue #182
652 | headerView.setTag(wlp);
653 | mActivity.getWindowManager().addView(headerView, wlp);
654 | }
655 |
656 | protected void updateHeaderViewPosition(View headerView) {
657 | // Refresh the Display Rect of the Decor View
658 | mActivity.getWindow().getDecorView().getWindowVisibleDisplayFrame(mRect);
659 |
660 | WindowManager.LayoutParams wlp = null;
661 | if (headerView.getLayoutParams() instanceof WindowManager.LayoutParams) {
662 | wlp = (WindowManager.LayoutParams) headerView.getLayoutParams();
663 | } else if (headerView.getTag() instanceof WindowManager.LayoutParams) {
664 | wlp = (WindowManager.LayoutParams) headerView.getTag();
665 | }
666 |
667 | if (wlp != null && wlp.y != mRect.top) {
668 | wlp.y = mRect.top;
669 | mActivity.getWindowManager().updateViewLayout(headerView, wlp);
670 | }
671 | }
672 |
673 | protected void removeHeaderViewFromActivity(View headerView) {
674 | mAddHeaderViewRunnable.finish();
675 |
676 | if (headerView.getWindowToken() != null) {
677 | mActivity.getWindowManager().removeViewImmediate(headerView);
678 | }
679 | }
680 |
681 | private final Runnable mRefreshMinimizeRunnable = new Runnable() {
682 | @Override
683 | public void run() {
684 | minimizeHeader();
685 | }
686 | };
687 |
688 | private class AddHeaderViewRunnable implements Runnable {
689 | @Override
690 | public void run() {
691 | if (isDestroyed()) return;
692 |
693 | if (getDecorView().getWindowToken() != null) {
694 | // The Decor View has a Window Token, so we can add the HeaderView!
695 | addHeaderViewToActivity(mHeaderView);
696 | } else {
697 | // The Decor View doesn't have a Window Token yet, post ourselves again...
698 | start();
699 | }
700 | }
701 |
702 | public void start() {
703 | getDecorView().post(this);
704 | }
705 |
706 | public void finish() {
707 | getDecorView().removeCallbacks(this);
708 | }
709 |
710 | private View getDecorView() {
711 | return getAttachedActivity().getWindow().getDecorView();
712 | }
713 | }
714 | }
715 |
--------------------------------------------------------------------------------