├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── bettycc │ │ └── com │ │ └── animatepulltorefresh │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── squirrel.gif │ ├── java │ └── bettycc │ │ └── com │ │ └── animatepulltorefresh │ │ ├── MainActivity.java │ │ └── MyActivity.java │ └── res │ ├── drawable-hdpi │ └── ic_launcher.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ └── ic_launcher.png │ ├── drawable-xxhdpi │ └── ic_launcher.png │ ├── layout │ ├── activity_eating.xml │ ├── activity_main.xml │ ├── activity_squirrel.xml │ ├── eating_item.xml │ └── squirrel_item.xml │ ├── menu │ └── my.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── bettycc │ │ └── animatepulltorefresh │ │ └── library │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── handmark │ │ └── pulltorefresh │ │ └── library │ │ ├── ILoadingLayout.java │ │ ├── IPullToRefresh.java │ │ ├── LoadingLayoutProxy.java │ │ ├── OverscrollHelper.java │ │ ├── PullToRefreshAdapterViewBase.java │ │ ├── PullToRefreshBase.java │ │ ├── PullToRefreshExpandableListView.java │ │ ├── PullToRefreshGridView.java │ │ ├── PullToRefreshHorizontalScrollView.java │ │ ├── PullToRefreshListView.java │ │ ├── PullToRefreshScrollView.java │ │ ├── PullToRefreshWebView.java │ │ ├── extras │ │ ├── PullToRefreshWebView2.java │ │ └── SoundPullEventListener.java │ │ └── internal │ │ ├── EmptyViewMethodAccessor.java │ │ ├── FlipLoadingLayout.java │ │ ├── GifAnimation.java │ │ ├── GifLoadingLayout.java │ │ ├── IndicatorLayout.java │ │ ├── LoadingLayout.java │ │ ├── RotateLoadingLayout.java │ │ ├── Utils.java │ │ └── ViewCompat.java │ └── res │ ├── anim │ ├── slide_in_from_bottom.xml │ ├── slide_in_from_top.xml │ ├── slide_out_to_bottom.xml │ └── slide_out_to_top.xml │ ├── drawable-hdpi │ ├── default_ptr_flip.png │ ├── default_ptr_rotate.png │ ├── ic_launcher.png │ └── indicator_arrow.png │ ├── drawable-mdpi │ ├── default_ptr_flip.png │ ├── default_ptr_rotate.png │ ├── ic_launcher.png │ └── indicator_arrow.png │ ├── drawable-xhdpi │ ├── default_ptr_flip.png │ ├── default_ptr_rotate.png │ ├── dropdown_anim_00.png │ ├── dropdown_anim_01.png │ ├── dropdown_anim_02.png │ ├── dropdown_anim_03.png │ ├── dropdown_anim_04.png │ ├── dropdown_anim_05.png │ ├── dropdown_anim_06.png │ ├── dropdown_anim_07.png │ ├── dropdown_anim_08.png │ ├── dropdown_anim_09.png │ ├── dropdown_anim_10.png │ ├── dropdown_loading_00.png │ ├── dropdown_loading_01.png │ ├── dropdown_loading_02.png │ ├── gif_loading1.gif │ ├── ic_launcher.png │ ├── indicator_arrow.png │ ├── loading.png │ └── ok.png │ ├── drawable-xxhdpi │ └── ic_launcher.png │ ├── drawable │ ├── indicator_bg_bottom.xml │ └── indicator_bg_top.xml │ ├── layout │ ├── pull_to_refresh_header_horizontal.xml │ └── pull_to_refresh_header_vertical.xml │ ├── values-ar │ └── pull_refresh_strings.xml │ ├── values-cs │ └── pull_refresh_strings.xml │ ├── values-de │ └── pull_refresh_strings.xml │ ├── values-es │ └── pull_refresh_strings.xml │ ├── values-fi │ └── pull_refresh_strings.xml │ ├── values-fr │ └── pull_refresh_strings.xml │ ├── values-he │ └── pull_refresh_strings.xml │ ├── values-it │ └── pull_refresh_strings.xml │ ├── values-iw │ └── pull_refresh_strings.xml │ ├── values-ja │ └── pull_refresh_strings.xml │ ├── values-ko │ └── pull_refresh_strings.xml │ ├── values-nl │ └── pull_refresh_strings.xml │ ├── values-pl │ └── pull_refresh_strings.xml │ ├── values-pt-rBR │ └── pull_refresh_strings.xml │ ├── values-pt │ └── pull_refresh_strings.xml │ ├── values-ro │ └── pull_refresh_strings.xml │ ├── values-ru │ └── pull_refresh_strings.xml │ ├── values-zh │ └── pull_refresh_strings.xml │ └── values │ ├── attrs.xml │ ├── dimens.xml │ ├── ids.xml │ ├── pull_refresh_strings.xml │ ├── strings.xml │ └── styles.xml ├── settings.gradle └── slide.gif /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | .DS_Store 5 | /build 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #AnimatePullToRefresh 2 | 3 | 这是一个各种下拉刷新动画的合集.项目是基于[chrisbanes的PullToRefresh](https://github.com/chrisbanes/Android-PullToRefresh)完成的。 4 | 5 | #吃包子动画 6 | 7 | 一个变拉边吃的listview, 动画师由多张图片切换完成的,模仿“大众点评” 8 | 9 | ![](./slide.gif) 10 | 11 | ###使用方法 12 | 13 | 使用默认的chrisbanes的PullToRefresh即可. 14 | 15 | #自定义GIF动画 16 | 17 | 使用自己的gif文件作为动画。 18 | 19 | ### 使用方法 20 | 21 | 1. 把gif文件放到asset文件夹下. 22 | 2. 配置xml文件,加入gif文件名. 23 | 24 | ```xml 25 | 34 | ``` 35 | 36 | #TODO 37 | 38 | 添加更多动画 -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 19 5 | buildToolsVersion "19.1.0" 6 | 7 | defaultConfig { 8 | applicationId "bettycc.com.animatepulltorefresh" 9 | minSdkVersion 14 10 | targetSdkVersion 19 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | runProguard false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile project(':library') 25 | } 26 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/ccheng/program/adt-bundle-mac-x86_64-20130219/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bettycc/com/animatepulltorefresh/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package bettycc.com.animatepulltorefresh; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/assets/squirrel.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/app/src/main/assets/squirrel.gif -------------------------------------------------------------------------------- /app/src/main/java/bettycc/com/animatepulltorefresh/MainActivity.java: -------------------------------------------------------------------------------- 1 | package bettycc.com.animatepulltorefresh; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | 8 | /** 9 | * Created by ccheng on 8/4/14. 10 | */ 11 | public class MainActivity extends Activity { 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_main); 17 | } 18 | 19 | public void onEating(View view) { 20 | launchItem(MyActivity.TYPE_EATING); 21 | } 22 | 23 | public void onSqurriel(View view) { 24 | launchItem(MyActivity.TYPE_SQUIRRIEL); 25 | } 26 | 27 | private void launchItem(int type) { 28 | Intent intent = new Intent(this, MyActivity.class); 29 | intent.putExtra("type", type); 30 | startActivity(intent); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/bettycc/com/animatepulltorefresh/MyActivity.java: -------------------------------------------------------------------------------- 1 | package bettycc.com.animatepulltorefresh; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.res.Resources; 6 | import android.os.Bundle; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | import android.widget.ArrayAdapter; 10 | import android.widget.ListView; 11 | 12 | import com.handmark.pulltorefresh.library.PullToRefreshBase; 13 | import com.handmark.pulltorefresh.library.PullToRefreshListView; 14 | 15 | 16 | public class MyActivity extends Activity { 17 | 18 | public static final int TYPE_EATING = 0; 19 | public static final int TYPE_SQUIRRIEL = 1; 20 | 21 | private PullToRefreshListView mRefreshListView; 22 | private ListView mListView; 23 | private String[] mDemoStrs = new String[]{ 24 | "妈妈", 25 | "再也", 26 | "不用", 27 | "担心我的列", 28 | "表没有", 29 | "动画", 30 | "了", 31 | }; 32 | private int mType; 33 | private ResourceChooser mRc; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | mType = getIntent().getIntExtra("type", TYPE_EATING); 39 | mRc = new ResourceChooser(this, mType); 40 | setContentView(mRc.getMainLayout()); 41 | mRefreshListView = (PullToRefreshListView) findViewById(R.id.pull_to_refresh_listview); 42 | mListView = mRefreshListView.getRefreshableView(); 43 | ArrayAdapter adapter = new ArrayAdapter(this, mRc.getItemLayout(), mDemoStrs); 44 | mListView.setAdapter(adapter); 45 | mRefreshListView.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener2() { 46 | @Override 47 | public void onPullDownToRefresh(PullToRefreshBase refreshView) { 48 | new Thread(new Runnable() { 49 | @Override 50 | public void run() { 51 | try { 52 | Thread.sleep(3000); 53 | runOnUiThread(new Runnable() { 54 | @Override 55 | public void run() { 56 | mRefreshListView.onRefreshComplete(); 57 | } 58 | }); 59 | } catch (InterruptedException e) { 60 | e.printStackTrace(); 61 | } 62 | } 63 | }).start(); 64 | } 65 | 66 | @Override 67 | public void onPullUpToRefresh(PullToRefreshBase refreshView) { 68 | 69 | } 70 | }); 71 | } 72 | 73 | 74 | @Override 75 | public boolean onCreateOptionsMenu(Menu menu) { 76 | // Inflate the menu; this adds items to the action bar if it is present. 77 | getMenuInflater().inflate(R.menu.my, menu); 78 | return true; 79 | } 80 | 81 | @Override 82 | public boolean onOptionsItemSelected(MenuItem item) { 83 | // Handle action bar item clicks here. The action bar will 84 | // automatically handle clicks on the Home/Up button, so long 85 | // as you specify a parent activity in AndroidManifest.xml. 86 | int id = item.getItemId(); 87 | if (id == R.id.action_settings) { 88 | return true; 89 | } 90 | return super.onOptionsItemSelected(item); 91 | } 92 | 93 | private static class ResourceChooser { 94 | private final Resources mRes; 95 | private Context mContext; 96 | private int mType; 97 | 98 | public ResourceChooser(Context context, int type) { 99 | mContext = context; 100 | mType = type; 101 | mRes = mContext.getResources(); 102 | } 103 | 104 | private int getMainLayout() { 105 | int layout; 106 | switch (mType) { 107 | case TYPE_EATING: 108 | default: 109 | layout = R.layout.activity_eating; 110 | break; 111 | 112 | case TYPE_SQUIRRIEL: 113 | layout = R.layout.activity_squirrel; 114 | break; 115 | } 116 | return layout; 117 | } 118 | 119 | private int getItemLayout() { 120 | int layout; 121 | switch (mType) { 122 | case TYPE_EATING: 123 | default: 124 | layout = R.layout.eating_item; 125 | break; 126 | 127 | case TYPE_SQUIRRIEL: 128 | layout = R.layout.squirrel_item; 129 | break; 130 | } 131 | return layout; 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_eating.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 21 | 22 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_squirrel.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/eating_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/squirrel_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/menu/my.xml: -------------------------------------------------------------------------------- 1 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #99cc00 4 | #33b5be 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AnimatePullToRefresh 5 | Hello world! 6 | Settings 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:0.12.+' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Settings specified in this file will override any Gradle settings 5 | # configured through the IDE. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 10 15:27:10 PDT 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.12-all.zip 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 19 5 | buildToolsVersion "19.1.0" 6 | 7 | defaultConfig { 8 | applicationId "com.bettycc.animatepulltorefresh.library" 9 | minSdkVersion 9 10 | targetSdkVersion 19 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | runProguard false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'pl.droidsonroids.gif:android-gif-drawable:1.0.+' 25 | } 26 | -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/ccheng/program/adt-bundle-mac-x86_64-20130219/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/bettycc/animatepulltorefresh/library/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.bettycc.animatepulltorefresh.library; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/ILoadingLayout.java: -------------------------------------------------------------------------------- 1 | package com.handmark.pulltorefresh.library; 2 | 3 | import android.graphics.Typeface; 4 | import android.graphics.drawable.Drawable; 5 | 6 | public interface ILoadingLayout { 7 | 8 | /** 9 | * Set the Last Updated Text. This displayed under the main label when 10 | * Pulling 11 | * 12 | * @param label - Label to set 13 | */ 14 | public void setLastUpdatedLabel(CharSequence label); 15 | 16 | /** 17 | * Set the drawable used in the loading layout. This is the same as calling 18 | * setLoadingDrawable(drawable, Mode.BOTH) 19 | * 20 | * @param drawable - Drawable to display 21 | */ 22 | public void setLoadingDrawable(Drawable drawable); 23 | 24 | /** 25 | * Set Text to show when the Widget is being Pulled 26 | * setPullLabel(releaseLabel, Mode.BOTH) 27 | * 28 | * @param pullLabel - CharSequence to display 29 | */ 30 | public void setPullLabel(CharSequence pullLabel); 31 | 32 | /** 33 | * Set Text to show when the Widget is refreshing 34 | * setRefreshingLabel(releaseLabel, Mode.BOTH) 35 | * 36 | * @param refreshingLabel - CharSequence to display 37 | */ 38 | public void setRefreshingLabel(CharSequence refreshingLabel); 39 | 40 | /** 41 | * Set Text to show when the Widget is being pulled, and will refresh when 42 | * released. This is the same as calling 43 | * setReleaseLabel(releaseLabel, Mode.BOTH) 44 | * 45 | * @param releaseLabel - CharSequence to display 46 | */ 47 | public void setReleaseLabel(CharSequence releaseLabel); 48 | 49 | /** 50 | * Set's the Sets the typeface and style in which the text should be 51 | * displayed. Please see 52 | * {@link android.widget.TextView#setTypeface(Typeface) 53 | * TextView#setTypeface(Typeface)}. 54 | */ 55 | public void setTextTypeface(Typeface tf); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/IPullToRefresh.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library; 17 | 18 | import android.view.View; 19 | import android.view.animation.Interpolator; 20 | 21 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode; 22 | import com.handmark.pulltorefresh.library.PullToRefreshBase.OnPullEventListener; 23 | import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener; 24 | import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener2; 25 | import com.handmark.pulltorefresh.library.PullToRefreshBase.State; 26 | 27 | public interface IPullToRefresh { 28 | 29 | /** 30 | * Demos the Pull-to-Refresh functionality to the user so that they are 31 | * aware it is there. This could be useful when the user first opens your 32 | * app, etc. The animation will only happen if the Refresh View (ListView, 33 | * ScrollView, etc) is in a state where a Pull-to-Refresh could occur by a 34 | * user's touch gesture (i.e. scrolled to the top/bottom). 35 | * 36 | * @return true - if the Demo has been started, false if not. 37 | */ 38 | public boolean demo(); 39 | 40 | /** 41 | * Get the mode that this view is currently in. This is only really useful 42 | * when using Mode.BOTH. 43 | * 44 | * @return Mode that the view is currently in 45 | */ 46 | public Mode getCurrentMode(); 47 | 48 | /** 49 | * Returns whether the Touch Events are filtered or not. If true is 50 | * returned, then the View will only use touch events where the difference 51 | * in the Y-axis is greater than the difference in the X-axis. This means 52 | * that the View will not interfere when it is used in a horizontal 53 | * scrolling View (such as a ViewPager). 54 | * 55 | * @return boolean - true if the View is filtering Touch Events 56 | */ 57 | public boolean getFilterTouchEvents(); 58 | 59 | /** 60 | * Returns a proxy object which allows you to call methods on all of the 61 | * LoadingLayouts (the Views which show when Pulling/Refreshing). 62 | *

63 | * You should not keep the result of this method any longer than you need 64 | * it. 65 | * 66 | * @return Object which will proxy any calls you make on it, to all of the 67 | * LoadingLayouts. 68 | */ 69 | public ILoadingLayout getLoadingLayoutProxy(); 70 | 71 | /** 72 | * Returns a proxy object which allows you to call methods on the 73 | * LoadingLayouts (the Views which show when Pulling/Refreshing). The actual 74 | * LoadingLayout(s) which will be affected, are chosen by the parameters you 75 | * give. 76 | *

77 | * You should not keep the result of this method any longer than you need 78 | * it. 79 | * 80 | * @param includeStart - Whether to include the Start/Header Views 81 | * @param includeEnd - Whether to include the End/Footer Views 82 | * @return Object which will proxy any calls you make on it, to the 83 | * LoadingLayouts included. 84 | */ 85 | public ILoadingLayout getLoadingLayoutProxy(boolean includeStart, boolean includeEnd); 86 | 87 | /** 88 | * Get the mode that this view has been set to. If this returns 89 | * Mode.BOTH, you can use getCurrentMode() to 90 | * check which mode the view is currently in 91 | * 92 | * @return Mode that the view has been set to 93 | */ 94 | public Mode getMode(); 95 | 96 | /** 97 | * Get the Wrapped Refreshable View. Anything returned here has already been 98 | * added to the content view. 99 | * 100 | * @return The View which is currently wrapped 101 | */ 102 | public T getRefreshableView(); 103 | 104 | /** 105 | * Get whether the 'Refreshing' View should be automatically shown when 106 | * refreshing. Returns true by default. 107 | * 108 | * @return - true if the Refreshing View will be show 109 | */ 110 | public boolean getShowViewWhileRefreshing(); 111 | 112 | /** 113 | * @return - The state that the View is currently in. 114 | */ 115 | public State getState(); 116 | 117 | /** 118 | * Whether Pull-to-Refresh is enabled 119 | * 120 | * @return enabled 121 | */ 122 | public boolean isPullToRefreshEnabled(); 123 | 124 | /** 125 | * Gets whether Overscroll support is enabled. This is different to 126 | * Android's standard Overscroll support (the edge-glow) which is available 127 | * from GINGERBREAD onwards 128 | * 129 | * @return true - if both PullToRefresh-OverScroll and Android's inbuilt 130 | * OverScroll are enabled 131 | */ 132 | public boolean isPullToRefreshOverScrollEnabled(); 133 | 134 | /** 135 | * Returns whether the Widget is currently in the Refreshing mState 136 | * 137 | * @return true if the Widget is currently refreshing 138 | */ 139 | public boolean isRefreshing(); 140 | 141 | /** 142 | * Returns whether the widget has enabled scrolling on the Refreshable View 143 | * while refreshing. 144 | * 145 | * @return true if the widget has enabled scrolling while refreshing 146 | */ 147 | public boolean isScrollingWhileRefreshingEnabled(); 148 | 149 | /** 150 | * Mark the current Refresh as complete. Will Reset the UI and hide the 151 | * Refreshing View 152 | */ 153 | public void onRefreshComplete(); 154 | 155 | /** 156 | * Set the Touch Events to be filtered or not. If set to true, then the View 157 | * will only use touch events where the difference in the Y-axis is greater 158 | * than the difference in the X-axis. This means that the View will not 159 | * interfere when it is used in a horizontal scrolling View (such as a 160 | * ViewPager), but will restrict which types of finger scrolls will trigger 161 | * the View. 162 | * 163 | * @param filterEvents - true if you want to filter Touch Events. Default is 164 | * true. 165 | */ 166 | public void setFilterTouchEvents(boolean filterEvents); 167 | 168 | /** 169 | * Set the mode of Pull-to-Refresh that this view will use. 170 | * 171 | * @param mode - Mode to set the View to 172 | */ 173 | public void setMode(Mode mode); 174 | 175 | /** 176 | * Set OnPullEventListener for the Widget 177 | * 178 | * @param listener - Listener to be used when the Widget has a pull event to 179 | * propogate. 180 | */ 181 | public void setOnPullEventListener(OnPullEventListener listener); 182 | 183 | /** 184 | * Set OnRefreshListener for the Widget 185 | * 186 | * @param listener - Listener to be used when the Widget is set to Refresh 187 | */ 188 | public void setOnRefreshListener(OnRefreshListener listener); 189 | 190 | /** 191 | * Set OnRefreshListener for the Widget 192 | * 193 | * @param listener - Listener to be used when the Widget is set to Refresh 194 | */ 195 | public void setOnRefreshListener(OnRefreshListener2 listener); 196 | 197 | /** 198 | * Sets whether Overscroll support is enabled. This is different to 199 | * Android's standard Overscroll support (the edge-glow). This setting only 200 | * takes effect when running on device with Android v2.3 or greater. 201 | * 202 | * @param enabled - true if you want Overscroll enabled 203 | */ 204 | public void setPullToRefreshOverScrollEnabled(boolean enabled); 205 | 206 | /** 207 | * Sets the Widget to be in the refresh state. The UI will be updated to 208 | * show the 'Refreshing' view, and be scrolled to show such. 209 | */ 210 | public void setRefreshing(); 211 | 212 | /** 213 | * Sets the Widget to be in the refresh state. The UI will be updated to 214 | * show the 'Refreshing' view. 215 | * 216 | * @param doScroll - true if you want to force a scroll to the Refreshing 217 | * view. 218 | */ 219 | public void setRefreshing(boolean doScroll); 220 | 221 | /** 222 | * Sets the Animation Interpolator that is used for animated scrolling. 223 | * Defaults to a DecelerateInterpolator 224 | * 225 | * @param interpolator - Interpolator to use 226 | */ 227 | public void setScrollAnimationInterpolator(Interpolator interpolator); 228 | 229 | /** 230 | * By default the Widget disables scrolling on the Refreshable View while 231 | * refreshing. This method can change this behaviour. 232 | * 233 | * @param scrollingWhileRefreshingEnabled - true if you want to enable 234 | * scrolling while refreshing 235 | */ 236 | public void setScrollingWhileRefreshingEnabled(boolean scrollingWhileRefreshingEnabled); 237 | 238 | /** 239 | * A mutator to enable/disable whether the 'Refreshing' View should be 240 | * automatically shown when refreshing. 241 | * 242 | * @param showView 243 | */ 244 | public void setShowViewWhileRefreshing(boolean showView); 245 | 246 | } -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/LoadingLayoutProxy.java: -------------------------------------------------------------------------------- 1 | package com.handmark.pulltorefresh.library; 2 | 3 | import android.graphics.Typeface; 4 | import android.graphics.drawable.Drawable; 5 | 6 | import com.handmark.pulltorefresh.library.internal.LoadingLayout; 7 | 8 | import java.util.HashSet; 9 | 10 | public class LoadingLayoutProxy implements ILoadingLayout { 11 | 12 | private final HashSet mLoadingLayouts; 13 | 14 | LoadingLayoutProxy() { 15 | mLoadingLayouts = new HashSet(); 16 | } 17 | 18 | /** 19 | * This allows you to add extra LoadingLayout instances to this proxy. This 20 | * is only necessary if you keep your own instances, and want to have them 21 | * included in any 22 | * {@link PullToRefreshBase#createLoadingLayoutProxy(boolean, boolean) 23 | * createLoadingLayoutProxy(...)} calls. 24 | * 25 | * @param layout - LoadingLayout to have included. 26 | */ 27 | public void addLayout(LoadingLayout layout) { 28 | if (null != layout) { 29 | mLoadingLayouts.add(layout); 30 | } 31 | } 32 | 33 | @Override 34 | public void setLastUpdatedLabel(CharSequence label) { 35 | for (LoadingLayout layout : mLoadingLayouts) { 36 | layout.setLastUpdatedLabel(label); 37 | } 38 | } 39 | 40 | @Override 41 | public void setLoadingDrawable(Drawable drawable) { 42 | for (LoadingLayout layout : mLoadingLayouts) { 43 | layout.setLoadingDrawable(drawable); 44 | } 45 | } 46 | 47 | @Override 48 | public void setRefreshingLabel(CharSequence refreshingLabel) { 49 | for (LoadingLayout layout : mLoadingLayouts) { 50 | layout.setRefreshingLabel(refreshingLabel); 51 | } 52 | } 53 | 54 | @Override 55 | public void setPullLabel(CharSequence label) { 56 | for (LoadingLayout layout : mLoadingLayouts) { 57 | layout.setPullLabel(label); 58 | } 59 | } 60 | 61 | @Override 62 | public void setReleaseLabel(CharSequence label) { 63 | for (LoadingLayout layout : mLoadingLayouts) { 64 | layout.setReleaseLabel(label); 65 | } 66 | } 67 | 68 | public void setTextTypeface(Typeface tf) { 69 | for (LoadingLayout layout : mLoadingLayouts) { 70 | layout.setTextTypeface(tf); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/OverscrollHelper.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library; 17 | 18 | import android.annotation.TargetApi; 19 | import android.util.Log; 20 | import android.view.View; 21 | 22 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode; 23 | import com.handmark.pulltorefresh.library.PullToRefreshBase.State; 24 | 25 | @TargetApi(9) 26 | public final class OverscrollHelper { 27 | 28 | static final String LOG_TAG = "OverscrollHelper"; 29 | static final float DEFAULT_OVERSCROLL_SCALE = 1f; 30 | 31 | /** 32 | * Helper method for Overscrolling that encapsulates all of the necessary 33 | * function. 34 | *

35 | * This should only be used on AdapterView's such as ListView as it just 36 | * calls through to overScrollBy() with the scrollRange = 0. AdapterView's 37 | * do not have a scroll range (i.e. getScrollY() doesn't work). 38 | * 39 | * @param view - PullToRefreshView that is calling this. 40 | * @param deltaX - Change in X in pixels, passed through from from 41 | * overScrollBy call 42 | * @param scrollX - Current X scroll value in pixels before applying deltaY, 43 | * passed through from from overScrollBy call 44 | * @param deltaY - Change in Y in pixels, passed through from from 45 | * overScrollBy call 46 | * @param scrollY - Current Y scroll value in pixels before applying deltaY, 47 | * passed through from from overScrollBy call 48 | * @param isTouchEvent - true if this scroll operation is the result of a 49 | * touch event, passed through from from overScrollBy call 50 | */ 51 | public static void overScrollBy(final PullToRefreshBase view, final int deltaX, final int scrollX, 52 | final int deltaY, final int scrollY, final boolean isTouchEvent) { 53 | overScrollBy(view, deltaX, scrollX, deltaY, scrollY, 0, isTouchEvent); 54 | } 55 | 56 | /** 57 | * Helper method for Overscrolling that encapsulates all of the necessary 58 | * function. This version of the call is used for Views that need to specify 59 | * a Scroll Range but scroll back to it's edge correctly. 60 | * 61 | * @param view - PullToRefreshView that is calling this. 62 | * @param deltaX - Change in X in pixels, passed through from from 63 | * overScrollBy call 64 | * @param scrollX - Current X scroll value in pixels before applying deltaY, 65 | * passed through from from overScrollBy call 66 | * @param deltaY - Change in Y in pixels, passed through from from 67 | * overScrollBy call 68 | * @param scrollY - Current Y scroll value in pixels before applying deltaY, 69 | * passed through from from overScrollBy call 70 | * @param scrollRange - Scroll Range of the View, specifically needed for 71 | * ScrollView 72 | * @param isTouchEvent - true if this scroll operation is the result of a 73 | * touch event, passed through from from overScrollBy call 74 | */ 75 | public static void overScrollBy(final PullToRefreshBase view, final int deltaX, final int scrollX, 76 | final int deltaY, final int scrollY, final int scrollRange, final boolean isTouchEvent) { 77 | overScrollBy(view, deltaX, scrollX, deltaY, scrollY, scrollRange, 0, DEFAULT_OVERSCROLL_SCALE, isTouchEvent); 78 | } 79 | 80 | /** 81 | * Helper method for Overscrolling that encapsulates all of the necessary 82 | * function. This is the advanced version of the call. 83 | * 84 | * @param view - PullToRefreshView that is calling this. 85 | * @param deltaX - Change in X in pixels, passed through from from 86 | * overScrollBy call 87 | * @param scrollX - Current X scroll value in pixels before applying deltaY, 88 | * passed through from from overScrollBy call 89 | * @param deltaY - Change in Y in pixels, passed through from from 90 | * overScrollBy call 91 | * @param scrollY - Current Y scroll value in pixels before applying deltaY, 92 | * passed through from from overScrollBy call 93 | * @param scrollRange - Scroll Range of the View, specifically needed for 94 | * ScrollView 95 | * @param fuzzyThreshold - Threshold for which the values how fuzzy we 96 | * should treat the other values. Needed for WebView as it 97 | * doesn't always scroll back to it's edge. 0 = no fuzziness. 98 | * @param scaleFactor - Scale Factor for overscroll amount 99 | * @param isTouchEvent - true if this scroll operation is the result of a 100 | * touch event, passed through from from overScrollBy call 101 | */ 102 | public static void overScrollBy(final PullToRefreshBase view, final int deltaX, final int scrollX, 103 | final int deltaY, final int scrollY, final int scrollRange, final int fuzzyThreshold, 104 | final float scaleFactor, final boolean isTouchEvent) { 105 | 106 | final int deltaValue, currentScrollValue, scrollValue; 107 | switch (view.getPullToRefreshScrollDirection()) { 108 | case HORIZONTAL: 109 | deltaValue = deltaX; 110 | scrollValue = scrollX; 111 | currentScrollValue = view.getScrollX(); 112 | break; 113 | case VERTICAL: 114 | default: 115 | deltaValue = deltaY; 116 | scrollValue = scrollY; 117 | currentScrollValue = view.getScrollY(); 118 | break; 119 | } 120 | 121 | // Check that OverScroll is enabled and that we're not currently 122 | // refreshing. 123 | if (view.isPullToRefreshOverScrollEnabled() && !view.isRefreshing()) { 124 | final Mode mode = view.getMode(); 125 | 126 | // Check that Pull-to-Refresh is enabled, and the event isn't from 127 | // touch 128 | 129 | if (mode.permitsPullToRefresh() && !isTouchEvent && deltaValue != 0) { 130 | final int newScrollValue = (deltaValue + scrollValue); 131 | 132 | if (PullToRefreshBase.DEBUG) { 133 | Log.d(LOG_TAG, "OverScroll. DeltaX: " + deltaX + ", ScrollX: " + scrollX + ", DeltaY: " + deltaY 134 | + ", ScrollY: " + scrollY + ", NewY: " + newScrollValue + ", ScrollRange: " + scrollRange 135 | + ", CurrentScroll: " + currentScrollValue); 136 | } 137 | 138 | if (newScrollValue < (0 - fuzzyThreshold)) { 139 | // Check the mode supports the overscroll direction, and 140 | // then move scroll 141 | if (mode.showHeaderLoadingLayout()) { 142 | // If we're currently at zero, we're about to start 143 | // overscrolling, so change the state 144 | if (currentScrollValue == 0) { 145 | view.setState(State.OVERSCROLLING); 146 | } 147 | 148 | view.setHeaderScroll((int) (scaleFactor * (currentScrollValue + newScrollValue))); 149 | } 150 | } else if (newScrollValue > (scrollRange + fuzzyThreshold)) { 151 | // Check the mode supports the overscroll direction, and 152 | // then move scroll 153 | if (mode.showFooterLoadingLayout()) { 154 | // If we're currently at zero, we're about to start 155 | // overscrolling, so change the state 156 | if (currentScrollValue == 0) { 157 | view.setState(State.OVERSCROLLING); 158 | } 159 | 160 | view.setHeaderScroll((int) (scaleFactor * (currentScrollValue + newScrollValue - scrollRange))); 161 | } 162 | } else if (Math.abs(newScrollValue) <= fuzzyThreshold 163 | || Math.abs(newScrollValue - scrollRange) <= fuzzyThreshold) { 164 | // Means we've stopped overscrolling, so scroll back to 0 165 | view.setState(State.RESET); 166 | } 167 | } else if (isTouchEvent && State.OVERSCROLLING == view.getState()) { 168 | // This condition means that we were overscrolling from a fling, 169 | // but the user has touched the View and is now overscrolling 170 | // from touch instead. We need to just reset. 171 | view.setState(State.RESET); 172 | } 173 | } 174 | } 175 | 176 | static boolean isAndroidOverScrollEnabled(View view) { 177 | return view.getOverScrollMode() != View.OVER_SCROLL_NEVER; 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/PullToRefreshAdapterViewBase.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library; 17 | 18 | import android.content.Context; 19 | import android.content.res.TypedArray; 20 | import android.util.AttributeSet; 21 | import android.util.Log; 22 | import android.view.Gravity; 23 | import android.view.View; 24 | import android.view.ViewGroup; 25 | import android.view.ViewParent; 26 | import android.widget.AbsListView; 27 | import android.widget.AbsListView.OnScrollListener; 28 | import android.widget.Adapter; 29 | import android.widget.AdapterView; 30 | import android.widget.AdapterView.OnItemClickListener; 31 | import android.widget.FrameLayout; 32 | import android.widget.LinearLayout; 33 | import android.widget.ListAdapter; 34 | 35 | import com.bettycc.animatepulltorefresh.library.R; 36 | import com.handmark.pulltorefresh.library.internal.EmptyViewMethodAccessor; 37 | import com.handmark.pulltorefresh.library.internal.IndicatorLayout; 38 | 39 | public abstract class PullToRefreshAdapterViewBase extends PullToRefreshBase implements 40 | OnScrollListener { 41 | 42 | private static FrameLayout.LayoutParams convertEmptyViewLayoutParams(ViewGroup.LayoutParams lp) { 43 | FrameLayout.LayoutParams newLp = null; 44 | 45 | if (null != lp) { 46 | newLp = new FrameLayout.LayoutParams(lp); 47 | 48 | if (lp instanceof LinearLayout.LayoutParams) { 49 | newLp.gravity = ((LinearLayout.LayoutParams) lp).gravity; 50 | } else { 51 | newLp.gravity = Gravity.CENTER; 52 | } 53 | } 54 | 55 | return newLp; 56 | } 57 | 58 | private boolean mLastItemVisible; 59 | private OnScrollListener mOnScrollListener; 60 | private OnLastItemVisibleListener mOnLastItemVisibleListener; 61 | private View mEmptyView; 62 | 63 | private IndicatorLayout mIndicatorIvTop; 64 | private IndicatorLayout mIndicatorIvBottom; 65 | 66 | private boolean mShowIndicator; 67 | private boolean mScrollEmptyView = true; 68 | 69 | public PullToRefreshAdapterViewBase(Context context) { 70 | super(context); 71 | mRefreshableView.setOnScrollListener(this); 72 | } 73 | 74 | public PullToRefreshAdapterViewBase(Context context, AttributeSet attrs) { 75 | super(context, attrs); 76 | mRefreshableView.setOnScrollListener(this); 77 | } 78 | 79 | public PullToRefreshAdapterViewBase(Context context, Mode mode) { 80 | super(context, mode); 81 | mRefreshableView.setOnScrollListener(this); 82 | } 83 | 84 | public PullToRefreshAdapterViewBase(Context context, Mode mode, AnimationStyle animStyle) { 85 | super(context, mode, animStyle); 86 | mRefreshableView.setOnScrollListener(this); 87 | } 88 | 89 | /** 90 | * Gets whether an indicator graphic should be displayed when the View is in 91 | * a state where a Pull-to-Refresh can happen. An example of this state is 92 | * when the Adapter View is scrolled to the top and the mode is set to 93 | * {@link Mode#PULL_FROM_START}. The default value is true if 94 | * {@link PullToRefreshBase#isPullToRefreshOverScrollEnabled() 95 | * isPullToRefreshOverScrollEnabled()} returns false. 96 | * 97 | * @return true if the indicators will be shown 98 | */ 99 | public boolean getShowIndicator() { 100 | return mShowIndicator; 101 | } 102 | 103 | public final void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount, 104 | final int totalItemCount) { 105 | 106 | if (DEBUG) { 107 | Log.d(LOG_TAG, "First Visible: " + firstVisibleItem + ". Visible Count: " + visibleItemCount 108 | + ". Total Items:" + totalItemCount); 109 | } 110 | 111 | /** 112 | * Set whether the Last Item is Visible. lastVisibleItemIndex is a 113 | * zero-based index, so we minus one totalItemCount to check 114 | */ 115 | if (null != mOnLastItemVisibleListener) { 116 | mLastItemVisible = (totalItemCount > 0) && (firstVisibleItem + visibleItemCount >= totalItemCount - 1); 117 | } 118 | 119 | // If we're showing the indicator, check positions... 120 | if (getShowIndicatorInternal()) { 121 | updateIndicatorViewsVisibility(); 122 | } 123 | 124 | // Finally call OnScrollListener if we have one 125 | if (null != mOnScrollListener) { 126 | mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); 127 | } 128 | } 129 | 130 | public final void onScrollStateChanged(final AbsListView view, final int state) { 131 | /** 132 | * Check that the scrolling has stopped, and that the last item is 133 | * visible. 134 | */ 135 | if (state == OnScrollListener.SCROLL_STATE_IDLE && null != mOnLastItemVisibleListener && mLastItemVisible) { 136 | mOnLastItemVisibleListener.onLastItemVisible(); 137 | } 138 | 139 | if (null != mOnScrollListener) { 140 | mOnScrollListener.onScrollStateChanged(view, state); 141 | } 142 | } 143 | 144 | /** 145 | * Pass-through method for {@link PullToRefreshBase#getRefreshableView() 146 | * getRefreshableView()}. 147 | * {@link AdapterView#setAdapter(android.widget.Adapter)} 148 | * setAdapter(adapter)}. This is just for convenience! 149 | * 150 | * @param adapter - Adapter to set 151 | */ 152 | public void setAdapter(ListAdapter adapter) { 153 | ((AdapterView) mRefreshableView).setAdapter(adapter); 154 | } 155 | 156 | /** 157 | * Sets the Empty View to be used by the Adapter View. 158 | *

159 | * We need it handle it ourselves so that we can Pull-to-Refresh when the 160 | * Empty View is shown. 161 | *

162 | * Please note, you do not usually need to call this method 163 | * yourself. Calling setEmptyView on the AdapterView will automatically call 164 | * this method and set everything up. This includes when the Android 165 | * Framework automatically sets the Empty View based on it's ID. 166 | * 167 | * @param newEmptyView - Empty View to be used 168 | */ 169 | public final void setEmptyView(View newEmptyView) { 170 | FrameLayout refreshableViewWrapper = getRefreshableViewWrapper(); 171 | 172 | if (null != newEmptyView) { 173 | // New view needs to be clickable so that Android recognizes it as a 174 | // target for Touch Events 175 | newEmptyView.setClickable(true); 176 | 177 | ViewParent newEmptyViewParent = newEmptyView.getParent(); 178 | if (null != newEmptyViewParent && newEmptyViewParent instanceof ViewGroup) { 179 | ((ViewGroup) newEmptyViewParent).removeView(newEmptyView); 180 | } 181 | 182 | // We need to convert any LayoutParams so that it works in our 183 | // FrameLayout 184 | FrameLayout.LayoutParams lp = convertEmptyViewLayoutParams(newEmptyView.getLayoutParams()); 185 | if (null != lp) { 186 | refreshableViewWrapper.addView(newEmptyView, lp); 187 | } else { 188 | refreshableViewWrapper.addView(newEmptyView); 189 | } 190 | } 191 | 192 | if (mRefreshableView instanceof EmptyViewMethodAccessor) { 193 | ((EmptyViewMethodAccessor) mRefreshableView).setEmptyViewInternal(newEmptyView); 194 | } else { 195 | mRefreshableView.setEmptyView(newEmptyView); 196 | } 197 | mEmptyView = newEmptyView; 198 | } 199 | 200 | /** 201 | * Pass-through method for {@link PullToRefreshBase#getRefreshableView() 202 | * getRefreshableView()}. 203 | * {@link AdapterView#setOnItemClickListener(OnItemClickListener) 204 | * setOnItemClickListener(listener)}. This is just for convenience! 205 | * 206 | * @param listener - OnItemClickListener to use 207 | */ 208 | public void setOnItemClickListener(OnItemClickListener listener) { 209 | mRefreshableView.setOnItemClickListener(listener); 210 | } 211 | 212 | public final void setOnLastItemVisibleListener(OnLastItemVisibleListener listener) { 213 | mOnLastItemVisibleListener = listener; 214 | } 215 | 216 | public final void setOnScrollListener(OnScrollListener listener) { 217 | mOnScrollListener = listener; 218 | } 219 | 220 | public final void setScrollEmptyView(boolean doScroll) { 221 | mScrollEmptyView = doScroll; 222 | } 223 | 224 | /** 225 | * Sets whether an indicator graphic should be displayed when the View is in 226 | * a state where a Pull-to-Refresh can happen. An example of this state is 227 | * when the Adapter View is scrolled to the top and the mode is set to 228 | * {@link Mode#PULL_FROM_START} 229 | * 230 | * @param showIndicator - true if the indicators should be shown. 231 | */ 232 | public void setShowIndicator(boolean showIndicator) { 233 | mShowIndicator = showIndicator; 234 | 235 | if (getShowIndicatorInternal()) { 236 | // If we're set to Show Indicator, add/update them 237 | addIndicatorViews(); 238 | } else { 239 | // If not, then remove then 240 | removeIndicatorViews(); 241 | } 242 | } 243 | 244 | ; 245 | 246 | @Override 247 | protected void onPullToRefresh() { 248 | super.onPullToRefresh(); 249 | 250 | if (getShowIndicatorInternal()) { 251 | switch (getCurrentMode()) { 252 | case PULL_FROM_END: 253 | mIndicatorIvBottom.pullToRefresh(); 254 | break; 255 | case PULL_FROM_START: 256 | mIndicatorIvTop.pullToRefresh(); 257 | break; 258 | default: 259 | // NO-OP 260 | break; 261 | } 262 | } 263 | } 264 | 265 | protected void onRefreshing(boolean doScroll) { 266 | super.onRefreshing(doScroll); 267 | 268 | if (getShowIndicatorInternal()) { 269 | updateIndicatorViewsVisibility(); 270 | } 271 | } 272 | 273 | @Override 274 | protected void onReleaseToRefresh() { 275 | super.onReleaseToRefresh(); 276 | 277 | if (getShowIndicatorInternal()) { 278 | switch (getCurrentMode()) { 279 | case PULL_FROM_END: 280 | mIndicatorIvBottom.releaseToRefresh(); 281 | break; 282 | case PULL_FROM_START: 283 | mIndicatorIvTop.releaseToRefresh(); 284 | break; 285 | default: 286 | // NO-OP 287 | break; 288 | } 289 | } 290 | } 291 | 292 | @Override 293 | protected void onReset() { 294 | super.onReset(); 295 | 296 | if (getShowIndicatorInternal()) { 297 | updateIndicatorViewsVisibility(); 298 | } 299 | } 300 | 301 | @Override 302 | protected void handleStyledAttributes(TypedArray a) { 303 | // Set Show Indicator to the XML value, or default value 304 | mShowIndicator = a.getBoolean(R.styleable.PullToRefresh_ptrShowIndicator, !isPullToRefreshOverScrollEnabled()); 305 | } 306 | 307 | protected boolean isReadyForPullStart() { 308 | return isFirstItemVisible(); 309 | } 310 | 311 | protected boolean isReadyForPullEnd() { 312 | return isLastItemVisible(); 313 | } 314 | 315 | @Override 316 | protected void onScrollChanged(int l, int t, int oldl, int oldt) { 317 | super.onScrollChanged(l, t, oldl, oldt); 318 | if (null != mEmptyView && !mScrollEmptyView) { 319 | mEmptyView.scrollTo(-l, -t); 320 | } 321 | } 322 | 323 | @Override 324 | protected void updateUIForMode() { 325 | super.updateUIForMode(); 326 | 327 | // Check Indicator Views consistent with new Mode 328 | if (getShowIndicatorInternal()) { 329 | addIndicatorViews(); 330 | } else { 331 | removeIndicatorViews(); 332 | } 333 | } 334 | 335 | private void addIndicatorViews() { 336 | Mode mode = getMode(); 337 | FrameLayout refreshableViewWrapper = getRefreshableViewWrapper(); 338 | 339 | if (mode.showHeaderLoadingLayout() && null == mIndicatorIvTop) { 340 | // If the mode can pull down, and we don't have one set already 341 | mIndicatorIvTop = new IndicatorLayout(getContext(), Mode.PULL_FROM_START); 342 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 343 | ViewGroup.LayoutParams.WRAP_CONTENT); 344 | params.rightMargin = getResources().getDimensionPixelSize(R.dimen.indicator_right_padding); 345 | params.gravity = Gravity.TOP | Gravity.RIGHT; 346 | refreshableViewWrapper.addView(mIndicatorIvTop, params); 347 | 348 | } else if (!mode.showHeaderLoadingLayout() && null != mIndicatorIvTop) { 349 | // If we can't pull down, but have a View then remove it 350 | refreshableViewWrapper.removeView(mIndicatorIvTop); 351 | mIndicatorIvTop = null; 352 | } 353 | 354 | if (mode.showFooterLoadingLayout() && null == mIndicatorIvBottom) { 355 | // If the mode can pull down, and we don't have one set already 356 | mIndicatorIvBottom = new IndicatorLayout(getContext(), Mode.PULL_FROM_END); 357 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 358 | ViewGroup.LayoutParams.WRAP_CONTENT); 359 | params.rightMargin = getResources().getDimensionPixelSize(R.dimen.indicator_right_padding); 360 | params.gravity = Gravity.BOTTOM | Gravity.RIGHT; 361 | refreshableViewWrapper.addView(mIndicatorIvBottom, params); 362 | 363 | } else if (!mode.showFooterLoadingLayout() && null != mIndicatorIvBottom) { 364 | // If we can't pull down, but have a View then remove it 365 | refreshableViewWrapper.removeView(mIndicatorIvBottom); 366 | mIndicatorIvBottom = null; 367 | } 368 | } 369 | 370 | private boolean getShowIndicatorInternal() { 371 | return mShowIndicator && isPullToRefreshEnabled(); 372 | } 373 | 374 | private boolean isFirstItemVisible() { 375 | final Adapter adapter = mRefreshableView.getAdapter(); 376 | 377 | if (null == adapter || adapter.isEmpty()) { 378 | if (DEBUG) { 379 | Log.d(LOG_TAG, "isFirstItemVisible. Empty View."); 380 | } 381 | return true; 382 | 383 | } else { 384 | 385 | /** 386 | * This check should really just be: 387 | * mRefreshableView.getFirstVisiblePosition() == 0, but PtRListView 388 | * internally use a HeaderView which messes the positions up. For 389 | * now we'll just add one to account for it and rely on the inner 390 | * condition which checks getTop(). 391 | */ 392 | if (mRefreshableView.getFirstVisiblePosition() <= 1) { 393 | final View firstVisibleChild = mRefreshableView.getChildAt(0); 394 | if (firstVisibleChild != null) { 395 | return firstVisibleChild.getTop() >= mRefreshableView.getTop(); 396 | } 397 | } 398 | } 399 | 400 | return false; 401 | } 402 | 403 | private boolean isLastItemVisible() { 404 | final Adapter adapter = mRefreshableView.getAdapter(); 405 | 406 | if (null == adapter || adapter.isEmpty()) { 407 | if (DEBUG) { 408 | Log.d(LOG_TAG, "isLastItemVisible. Empty View."); 409 | } 410 | return true; 411 | } else { 412 | final int lastItemPosition = mRefreshableView.getCount() - 1; 413 | final int lastVisiblePosition = mRefreshableView.getLastVisiblePosition(); 414 | 415 | if (DEBUG) { 416 | Log.d(LOG_TAG, "isLastItemVisible. Last Item Position: " + lastItemPosition + " Last Visible Pos: " 417 | + lastVisiblePosition); 418 | } 419 | 420 | /** 421 | * This check should really just be: lastVisiblePosition == 422 | * lastItemPosition, but PtRListView internally uses a FooterView 423 | * which messes the positions up. For me we'll just subtract one to 424 | * account for it and rely on the inner condition which checks 425 | * getBottom(). 426 | */ 427 | if (lastVisiblePosition >= lastItemPosition - 1) { 428 | final int childIndex = lastVisiblePosition - mRefreshableView.getFirstVisiblePosition(); 429 | final View lastVisibleChild = mRefreshableView.getChildAt(childIndex); 430 | if (lastVisibleChild != null) { 431 | return lastVisibleChild.getBottom() <= mRefreshableView.getBottom(); 432 | } 433 | } 434 | } 435 | 436 | return false; 437 | } 438 | 439 | private void removeIndicatorViews() { 440 | if (null != mIndicatorIvTop) { 441 | getRefreshableViewWrapper().removeView(mIndicatorIvTop); 442 | mIndicatorIvTop = null; 443 | } 444 | 445 | if (null != mIndicatorIvBottom) { 446 | getRefreshableViewWrapper().removeView(mIndicatorIvBottom); 447 | mIndicatorIvBottom = null; 448 | } 449 | } 450 | 451 | private void updateIndicatorViewsVisibility() { 452 | if (null != mIndicatorIvTop) { 453 | if (!isRefreshing() && isReadyForPullStart()) { 454 | if (!mIndicatorIvTop.isVisible()) { 455 | mIndicatorIvTop.show(); 456 | } 457 | } else { 458 | if (mIndicatorIvTop.isVisible()) { 459 | mIndicatorIvTop.hide(); 460 | } 461 | } 462 | } 463 | 464 | if (null != mIndicatorIvBottom) { 465 | if (!isRefreshing() && isReadyForPullEnd()) { 466 | if (!mIndicatorIvBottom.isVisible()) { 467 | mIndicatorIvBottom.show(); 468 | } 469 | } else { 470 | if (mIndicatorIvBottom.isVisible()) { 471 | mIndicatorIvBottom.hide(); 472 | } 473 | } 474 | } 475 | } 476 | } 477 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/PullToRefreshExpandableListView.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library; 17 | 18 | import android.annotation.TargetApi; 19 | import android.content.Context; 20 | import android.os.Build.VERSION; 21 | import android.os.Build.VERSION_CODES; 22 | import android.util.AttributeSet; 23 | import android.view.View; 24 | import android.widget.ExpandableListView; 25 | 26 | import com.handmark.pulltorefresh.library.internal.EmptyViewMethodAccessor; 27 | 28 | public class PullToRefreshExpandableListView extends PullToRefreshAdapterViewBase { 29 | 30 | public PullToRefreshExpandableListView(Context context) { 31 | super(context); 32 | } 33 | 34 | public PullToRefreshExpandableListView(Context context, AttributeSet attrs) { 35 | super(context, attrs); 36 | } 37 | 38 | public PullToRefreshExpandableListView(Context context, Mode mode) { 39 | super(context, mode); 40 | } 41 | 42 | public PullToRefreshExpandableListView(Context context, Mode mode, AnimationStyle style) { 43 | super(context, mode, style); 44 | } 45 | 46 | @Override 47 | public final Orientation getPullToRefreshScrollDirection() { 48 | return Orientation.VERTICAL; 49 | } 50 | 51 | @Override 52 | protected ExpandableListView createRefreshableView(Context context, AttributeSet attrs) { 53 | final ExpandableListView lv; 54 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { 55 | lv = new InternalExpandableListViewSDK9(context, attrs); 56 | } else { 57 | lv = new InternalExpandableListView(context, attrs); 58 | } 59 | 60 | // Set it to this so it can be used in ListActivity/ListFragment 61 | lv.setId(android.R.id.list); 62 | return lv; 63 | } 64 | 65 | class InternalExpandableListView extends ExpandableListView implements EmptyViewMethodAccessor { 66 | 67 | public InternalExpandableListView(Context context, AttributeSet attrs) { 68 | super(context, attrs); 69 | } 70 | 71 | @Override 72 | public void setEmptyView(View emptyView) { 73 | PullToRefreshExpandableListView.this.setEmptyView(emptyView); 74 | } 75 | 76 | @Override 77 | public void setEmptyViewInternal(View emptyView) { 78 | super.setEmptyView(emptyView); 79 | } 80 | } 81 | 82 | @TargetApi(9) 83 | final class InternalExpandableListViewSDK9 extends InternalExpandableListView { 84 | 85 | public InternalExpandableListViewSDK9(Context context, AttributeSet attrs) { 86 | super(context, attrs); 87 | } 88 | 89 | @Override 90 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, 91 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 92 | 93 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, 94 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 95 | 96 | // Does all of the hard work... 97 | OverscrollHelper.overScrollBy(PullToRefreshExpandableListView.this, deltaX, scrollX, deltaY, scrollY, 98 | isTouchEvent); 99 | 100 | return returnValue; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/PullToRefreshGridView.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library; 17 | 18 | import android.annotation.TargetApi; 19 | import android.content.Context; 20 | import android.os.Build.VERSION; 21 | import android.os.Build.VERSION_CODES; 22 | import android.util.AttributeSet; 23 | import android.view.View; 24 | import android.widget.GridView; 25 | 26 | import com.bettycc.animatepulltorefresh.library.R; 27 | import com.handmark.pulltorefresh.library.internal.EmptyViewMethodAccessor; 28 | 29 | public class PullToRefreshGridView extends PullToRefreshAdapterViewBase { 30 | 31 | public PullToRefreshGridView(Context context) { 32 | super(context); 33 | } 34 | 35 | public PullToRefreshGridView(Context context, AttributeSet attrs) { 36 | super(context, attrs); 37 | } 38 | 39 | public PullToRefreshGridView(Context context, Mode mode) { 40 | super(context, mode); 41 | } 42 | 43 | public PullToRefreshGridView(Context context, Mode mode, AnimationStyle style) { 44 | super(context, mode, style); 45 | } 46 | 47 | @Override 48 | public final Orientation getPullToRefreshScrollDirection() { 49 | return Orientation.VERTICAL; 50 | } 51 | 52 | @Override 53 | protected final GridView createRefreshableView(Context context, AttributeSet attrs) { 54 | final GridView gv; 55 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { 56 | gv = new InternalGridViewSDK9(context, attrs); 57 | } else { 58 | gv = new InternalGridView(context, attrs); 59 | } 60 | 61 | // Use Generated ID (from res/values/ids.xml) 62 | gv.setId(R.id.gridview); 63 | return gv; 64 | } 65 | 66 | class InternalGridView extends GridView implements EmptyViewMethodAccessor { 67 | 68 | public InternalGridView(Context context, AttributeSet attrs) { 69 | super(context, attrs); 70 | } 71 | 72 | @Override 73 | public void setEmptyView(View emptyView) { 74 | PullToRefreshGridView.this.setEmptyView(emptyView); 75 | } 76 | 77 | @Override 78 | public void setEmptyViewInternal(View emptyView) { 79 | super.setEmptyView(emptyView); 80 | } 81 | } 82 | 83 | @TargetApi(9) 84 | final class InternalGridViewSDK9 extends InternalGridView { 85 | 86 | public InternalGridViewSDK9(Context context, AttributeSet attrs) { 87 | super(context, attrs); 88 | } 89 | 90 | @Override 91 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, 92 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 93 | 94 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, 95 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 96 | 97 | // Does all of the hard work... 98 | OverscrollHelper.overScrollBy(PullToRefreshGridView.this, deltaX, scrollX, deltaY, scrollY, isTouchEvent); 99 | 100 | return returnValue; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/PullToRefreshHorizontalScrollView.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library; 17 | 18 | import android.annotation.TargetApi; 19 | import android.content.Context; 20 | import android.os.Build.VERSION; 21 | import android.os.Build.VERSION_CODES; 22 | import android.util.AttributeSet; 23 | import android.view.View; 24 | import android.widget.HorizontalScrollView; 25 | 26 | import com.bettycc.animatepulltorefresh.library.R; 27 | 28 | public class PullToRefreshHorizontalScrollView extends PullToRefreshBase { 29 | 30 | public PullToRefreshHorizontalScrollView(Context context) { 31 | super(context); 32 | } 33 | 34 | public PullToRefreshHorizontalScrollView(Context context, AttributeSet attrs) { 35 | super(context, attrs); 36 | } 37 | 38 | public PullToRefreshHorizontalScrollView(Context context, Mode mode) { 39 | super(context, mode); 40 | } 41 | 42 | public PullToRefreshHorizontalScrollView(Context context, Mode mode, AnimationStyle style) { 43 | super(context, mode, style); 44 | } 45 | 46 | @Override 47 | public final Orientation getPullToRefreshScrollDirection() { 48 | return Orientation.HORIZONTAL; 49 | } 50 | 51 | @Override 52 | protected HorizontalScrollView createRefreshableView(Context context, AttributeSet attrs) { 53 | HorizontalScrollView scrollView; 54 | 55 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { 56 | scrollView = new InternalHorizontalScrollViewSDK9(context, attrs); 57 | } else { 58 | scrollView = new HorizontalScrollView(context, attrs); 59 | } 60 | 61 | scrollView.setId(R.id.scrollview); 62 | return scrollView; 63 | } 64 | 65 | @Override 66 | protected boolean isReadyForPullStart() { 67 | return mRefreshableView.getScrollX() == 0; 68 | } 69 | 70 | @Override 71 | protected boolean isReadyForPullEnd() { 72 | View scrollViewChild = mRefreshableView.getChildAt(0); 73 | if (null != scrollViewChild) { 74 | return mRefreshableView.getScrollX() >= (scrollViewChild.getWidth() - getWidth()); 75 | } 76 | return false; 77 | } 78 | 79 | @TargetApi(9) 80 | final class InternalHorizontalScrollViewSDK9 extends HorizontalScrollView { 81 | 82 | public InternalHorizontalScrollViewSDK9(Context context, AttributeSet attrs) { 83 | super(context, attrs); 84 | } 85 | 86 | @Override 87 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, 88 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 89 | 90 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, 91 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 92 | 93 | // Does all of the hard work... 94 | OverscrollHelper.overScrollBy(PullToRefreshHorizontalScrollView.this, deltaX, scrollX, deltaY, scrollY, 95 | getScrollRange(), isTouchEvent); 96 | 97 | return returnValue; 98 | } 99 | 100 | /** 101 | * Taken from the AOSP ScrollView source 102 | */ 103 | private int getScrollRange() { 104 | int scrollRange = 0; 105 | if (getChildCount() > 0) { 106 | View child = getChildAt(0); 107 | scrollRange = Math.max(0, child.getWidth() - (getWidth() - getPaddingLeft() - getPaddingRight())); 108 | } 109 | return scrollRange; 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/PullToRefreshListView.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library; 17 | 18 | import android.annotation.TargetApi; 19 | import android.content.Context; 20 | import android.content.res.TypedArray; 21 | import android.graphics.Canvas; 22 | import android.os.Build.VERSION; 23 | import android.os.Build.VERSION_CODES; 24 | import android.util.AttributeSet; 25 | import android.view.Gravity; 26 | import android.view.MotionEvent; 27 | import android.view.View; 28 | import android.widget.FrameLayout; 29 | import android.widget.ListAdapter; 30 | import android.widget.ListView; 31 | 32 | import com.bettycc.animatepulltorefresh.library.R; 33 | import com.handmark.pulltorefresh.library.internal.EmptyViewMethodAccessor; 34 | import com.handmark.pulltorefresh.library.internal.LoadingLayout; 35 | 36 | public class PullToRefreshListView extends PullToRefreshAdapterViewBase { 37 | 38 | private LoadingLayout mHeaderLoadingView; 39 | private LoadingLayout mFooterLoadingView; 40 | 41 | private FrameLayout mLvFooterLoadingFrame; 42 | 43 | private boolean mListViewExtrasEnabled; 44 | 45 | public PullToRefreshListView(Context context) { 46 | super(context); 47 | } 48 | 49 | public PullToRefreshListView(Context context, AttributeSet attrs) { 50 | super(context, attrs); 51 | } 52 | 53 | public PullToRefreshListView(Context context, Mode mode) { 54 | super(context, mode); 55 | } 56 | 57 | public PullToRefreshListView(Context context, Mode mode, AnimationStyle style) { 58 | super(context, mode, style); 59 | } 60 | 61 | @Override 62 | public final Orientation getPullToRefreshScrollDirection() { 63 | return Orientation.VERTICAL; 64 | } 65 | 66 | @Override 67 | protected void onRefreshing(final boolean doScroll) { 68 | /** 69 | * If we're not showing the Refreshing view, or the list is empty, the 70 | * the header/footer views won't show so we use the normal method. 71 | */ 72 | ListAdapter adapter = mRefreshableView.getAdapter(); 73 | if (!mListViewExtrasEnabled || !getShowViewWhileRefreshing() || null == adapter || adapter.isEmpty()) { 74 | super.onRefreshing(doScroll); 75 | return; 76 | } 77 | 78 | super.onRefreshing(false); 79 | 80 | final LoadingLayout origLoadingView, listViewLoadingView, oppositeListViewLoadingView; 81 | final int selection, scrollToY; 82 | 83 | switch (getCurrentMode()) { 84 | case MANUAL_REFRESH_ONLY: 85 | case PULL_FROM_END: 86 | origLoadingView = getFooterLayout(); 87 | listViewLoadingView = mFooterLoadingView; 88 | oppositeListViewLoadingView = mHeaderLoadingView; 89 | selection = mRefreshableView.getCount() - 1; 90 | scrollToY = getScrollY() - getFooterSize(); 91 | break; 92 | case PULL_FROM_START: 93 | default: 94 | origLoadingView = getHeaderLayout(); 95 | listViewLoadingView = mHeaderLoadingView; 96 | oppositeListViewLoadingView = mFooterLoadingView; 97 | selection = 0; 98 | scrollToY = getScrollY() + getHeaderSize(); 99 | break; 100 | } 101 | 102 | // Hide our original Loading View 103 | origLoadingView.reset(); 104 | origLoadingView.hideAllViews(); 105 | 106 | // Make sure the opposite end is hidden too 107 | oppositeListViewLoadingView.setVisibility(View.GONE); 108 | 109 | // Show the ListView Loading View and set it to refresh. 110 | listViewLoadingView.setVisibility(View.VISIBLE); 111 | listViewLoadingView.refreshing(); 112 | 113 | if (doScroll) { 114 | // We need to disable the automatic visibility changes for now 115 | disableLoadingLayoutVisibilityChanges(); 116 | 117 | // We scroll slightly so that the ListView's header/footer is at the 118 | // same Y position as our normal header/footer 119 | setHeaderScroll(scrollToY); 120 | 121 | // Make sure the ListView is scrolled to show the loading 122 | // header/footer 123 | mRefreshableView.setSelection(selection); 124 | 125 | // Smooth scroll as normal 126 | smoothScrollTo(0); 127 | } 128 | } 129 | 130 | @Override 131 | protected void onReset() { 132 | /** 133 | * If the extras are not enabled, just call up to super and return. 134 | */ 135 | if (!mListViewExtrasEnabled) { 136 | super.onReset(); 137 | return; 138 | } 139 | 140 | final LoadingLayout originalLoadingLayout, listViewLoadingLayout; 141 | final int scrollToHeight, selection; 142 | final boolean scrollLvToEdge; 143 | 144 | switch (getCurrentMode()) { 145 | case MANUAL_REFRESH_ONLY: 146 | case PULL_FROM_END: 147 | originalLoadingLayout = getFooterLayout(); 148 | listViewLoadingLayout = mFooterLoadingView; 149 | selection = mRefreshableView.getCount() - 1; 150 | scrollToHeight = getFooterSize(); 151 | scrollLvToEdge = Math.abs(mRefreshableView.getLastVisiblePosition() - selection) <= 1; 152 | break; 153 | case PULL_FROM_START: 154 | default: 155 | originalLoadingLayout = getHeaderLayout(); 156 | listViewLoadingLayout = mHeaderLoadingView; 157 | scrollToHeight = -getHeaderSize(); 158 | selection = 0; 159 | scrollLvToEdge = Math.abs(mRefreshableView.getFirstVisiblePosition() - selection) <= 1; 160 | break; 161 | } 162 | 163 | // If the ListView header loading layout is showing, then we need to 164 | // flip so that the original one is showing instead 165 | if (listViewLoadingLayout.getVisibility() == View.VISIBLE) { 166 | 167 | // Set our Original View to Visible 168 | originalLoadingLayout.showInvisibleViews(); 169 | 170 | // Hide the ListView Header/Footer 171 | listViewLoadingLayout.setVisibility(View.GONE); 172 | 173 | /** 174 | * Scroll so the View is at the same Y as the ListView 175 | * header/footer, but only scroll if: we've pulled to refresh, it's 176 | * positioned correctly 177 | */ 178 | if (scrollLvToEdge && getState() != State.MANUAL_REFRESHING) { 179 | mRefreshableView.setSelection(selection); 180 | setHeaderScroll(scrollToHeight); 181 | } 182 | } 183 | 184 | // Finally, call up to super 185 | super.onReset(); 186 | } 187 | 188 | @Override 189 | protected LoadingLayoutProxy createLoadingLayoutProxy(final boolean includeStart, final boolean includeEnd) { 190 | LoadingLayoutProxy proxy = super.createLoadingLayoutProxy(includeStart, includeEnd); 191 | 192 | if (mListViewExtrasEnabled) { 193 | final Mode mode = getMode(); 194 | 195 | if (includeStart && mode.showHeaderLoadingLayout()) { 196 | proxy.addLayout(mHeaderLoadingView); 197 | } 198 | if (includeEnd && mode.showFooterLoadingLayout()) { 199 | proxy.addLayout(mFooterLoadingView); 200 | } 201 | } 202 | 203 | return proxy; 204 | } 205 | 206 | protected ListView createListView(Context context, AttributeSet attrs) { 207 | final ListView lv; 208 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { 209 | lv = new InternalListViewSDK9(context, attrs); 210 | } else { 211 | lv = new InternalListView(context, attrs); 212 | } 213 | return lv; 214 | } 215 | 216 | @Override 217 | protected ListView createRefreshableView(Context context, AttributeSet attrs) { 218 | ListView lv = createListView(context, attrs); 219 | 220 | // Set it to this so it can be used in ListActivity/ListFragment 221 | lv.setId(android.R.id.list); 222 | return lv; 223 | } 224 | 225 | @Override 226 | protected void handleStyledAttributes(TypedArray a) { 227 | super.handleStyledAttributes(a); 228 | 229 | mListViewExtrasEnabled = a.getBoolean(R.styleable.PullToRefresh_ptrListViewExtrasEnabled, true); 230 | 231 | if (mListViewExtrasEnabled) { 232 | final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, 233 | FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL); 234 | 235 | // Create Loading Views ready for use later 236 | FrameLayout frame = new FrameLayout(getContext()); 237 | mHeaderLoadingView = createLoadingLayout(getContext(), Mode.PULL_FROM_START, a); 238 | mHeaderLoadingView.setVisibility(View.GONE); 239 | frame.addView(mHeaderLoadingView, lp); 240 | mRefreshableView.addHeaderView(frame, null, false); 241 | 242 | mLvFooterLoadingFrame = new FrameLayout(getContext()); 243 | mFooterLoadingView = createLoadingLayout(getContext(), Mode.PULL_FROM_END, a); 244 | mFooterLoadingView.setVisibility(View.GONE); 245 | mLvFooterLoadingFrame.addView(mFooterLoadingView, lp); 246 | 247 | /** 248 | * If the value for Scrolling While Refreshing hasn't been 249 | * explicitly set via XML, enable Scrolling While Refreshing. 250 | */ 251 | if (!a.hasValue(R.styleable.PullToRefresh_ptrScrollingWhileRefreshingEnabled)) { 252 | setScrollingWhileRefreshingEnabled(true); 253 | } 254 | } 255 | } 256 | 257 | @TargetApi(9) 258 | final class InternalListViewSDK9 extends InternalListView { 259 | 260 | public InternalListViewSDK9(Context context, AttributeSet attrs) { 261 | super(context, attrs); 262 | } 263 | 264 | @Override 265 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, 266 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 267 | 268 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, 269 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 270 | 271 | // Does all of the hard work... 272 | OverscrollHelper.overScrollBy(PullToRefreshListView.this, deltaX, scrollX, deltaY, scrollY, isTouchEvent); 273 | 274 | return returnValue; 275 | } 276 | } 277 | 278 | protected class InternalListView extends ListView implements EmptyViewMethodAccessor { 279 | 280 | private boolean mAddedLvFooter = false; 281 | 282 | public InternalListView(Context context, AttributeSet attrs) { 283 | super(context, attrs); 284 | } 285 | 286 | @Override 287 | protected void dispatchDraw(Canvas canvas) { 288 | /** 289 | * This is a bit hacky, but Samsung's ListView has got a bug in it 290 | * when using Header/Footer Views and the list is empty. This masks 291 | * the issue so that it doesn't cause an FC. See Issue #66. 292 | */ 293 | try { 294 | super.dispatchDraw(canvas); 295 | } catch (IndexOutOfBoundsException e) { 296 | e.printStackTrace(); 297 | } 298 | } 299 | 300 | @Override 301 | public boolean dispatchTouchEvent(MotionEvent ev) { 302 | /** 303 | * This is a bit hacky, but Samsung's ListView has got a bug in it 304 | * when using Header/Footer Views and the list is empty. This masks 305 | * the issue so that it doesn't cause an FC. See Issue #66. 306 | */ 307 | try { 308 | return super.dispatchTouchEvent(ev); 309 | } catch (IndexOutOfBoundsException e) { 310 | e.printStackTrace(); 311 | return false; 312 | } 313 | } 314 | 315 | @Override 316 | public void setAdapter(ListAdapter adapter) { 317 | // Add the Footer View at the last possible moment 318 | if (null != mLvFooterLoadingFrame && !mAddedLvFooter) { 319 | addFooterView(mLvFooterLoadingFrame, null, false); 320 | mAddedLvFooter = true; 321 | } 322 | 323 | super.setAdapter(adapter); 324 | } 325 | 326 | @Override 327 | public void setEmptyView(View emptyView) { 328 | PullToRefreshListView.this.setEmptyView(emptyView); 329 | } 330 | 331 | @Override 332 | public void setEmptyViewInternal(View emptyView) { 333 | super.setEmptyView(emptyView); 334 | } 335 | 336 | } 337 | 338 | } 339 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/PullToRefreshScrollView.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library; 17 | 18 | import android.annotation.TargetApi; 19 | import android.content.Context; 20 | import android.os.Build.VERSION; 21 | import android.os.Build.VERSION_CODES; 22 | import android.util.AttributeSet; 23 | import android.view.View; 24 | import android.widget.ScrollView; 25 | 26 | import com.bettycc.animatepulltorefresh.library.R; 27 | 28 | public class PullToRefreshScrollView extends PullToRefreshBase { 29 | 30 | public PullToRefreshScrollView(Context context) { 31 | super(context); 32 | } 33 | 34 | public PullToRefreshScrollView(Context context, AttributeSet attrs) { 35 | super(context, attrs); 36 | } 37 | 38 | public PullToRefreshScrollView(Context context, Mode mode) { 39 | super(context, mode); 40 | } 41 | 42 | public PullToRefreshScrollView(Context context, Mode mode, AnimationStyle style) { 43 | super(context, mode, style); 44 | } 45 | 46 | @Override 47 | public final Orientation getPullToRefreshScrollDirection() { 48 | return Orientation.VERTICAL; 49 | } 50 | 51 | @Override 52 | protected ScrollView createRefreshableView(Context context, AttributeSet attrs) { 53 | ScrollView scrollView; 54 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { 55 | scrollView = new InternalScrollViewSDK9(context, attrs); 56 | } else { 57 | scrollView = new ScrollView(context, attrs); 58 | } 59 | 60 | scrollView.setId(R.id.scrollview); 61 | return scrollView; 62 | } 63 | 64 | @Override 65 | protected boolean isReadyForPullStart() { 66 | return mRefreshableView.getScrollY() == 0; 67 | } 68 | 69 | @Override 70 | protected boolean isReadyForPullEnd() { 71 | View scrollViewChild = mRefreshableView.getChildAt(0); 72 | if (null != scrollViewChild) { 73 | return mRefreshableView.getScrollY() >= (scrollViewChild.getHeight() - getHeight()); 74 | } 75 | return false; 76 | } 77 | 78 | @TargetApi(9) 79 | final class InternalScrollViewSDK9 extends ScrollView { 80 | 81 | public InternalScrollViewSDK9(Context context, AttributeSet attrs) { 82 | super(context, attrs); 83 | } 84 | 85 | @Override 86 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, 87 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 88 | 89 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, 90 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 91 | 92 | // Does all of the hard work... 93 | OverscrollHelper.overScrollBy(PullToRefreshScrollView.this, deltaX, scrollX, deltaY, scrollY, 94 | getScrollRange(), isTouchEvent); 95 | 96 | return returnValue; 97 | } 98 | 99 | /** 100 | * Taken from the AOSP ScrollView source 101 | */ 102 | private int getScrollRange() { 103 | int scrollRange = 0; 104 | if (getChildCount() > 0) { 105 | View child = getChildAt(0); 106 | scrollRange = Math.max(0, child.getHeight() - (getHeight() - getPaddingBottom() - getPaddingTop())); 107 | } 108 | return scrollRange; 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/PullToRefreshWebView.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library; 17 | 18 | import android.annotation.TargetApi; 19 | import android.content.Context; 20 | import android.os.Build.VERSION; 21 | import android.os.Build.VERSION_CODES; 22 | import android.os.Bundle; 23 | import android.util.AttributeSet; 24 | import android.util.FloatMath; 25 | import android.webkit.WebChromeClient; 26 | import android.webkit.WebView; 27 | 28 | import com.bettycc.animatepulltorefresh.library.R; 29 | 30 | public class PullToRefreshWebView extends PullToRefreshBase { 31 | 32 | private static final OnRefreshListener defaultOnRefreshListener = new OnRefreshListener() { 33 | 34 | @Override 35 | public void onRefresh(PullToRefreshBase refreshView) { 36 | refreshView.getRefreshableView().reload(); 37 | } 38 | 39 | }; 40 | 41 | private final WebChromeClient defaultWebChromeClient = new WebChromeClient() { 42 | 43 | @Override 44 | public void onProgressChanged(WebView view, int newProgress) { 45 | if (newProgress == 100) { 46 | onRefreshComplete(); 47 | } 48 | } 49 | 50 | }; 51 | 52 | public PullToRefreshWebView(Context context) { 53 | super(context); 54 | 55 | /** 56 | * Added so that by default, Pull-to-Refresh refreshes the page 57 | */ 58 | setOnRefreshListener(defaultOnRefreshListener); 59 | mRefreshableView.setWebChromeClient(defaultWebChromeClient); 60 | } 61 | 62 | public PullToRefreshWebView(Context context, AttributeSet attrs) { 63 | super(context, attrs); 64 | 65 | /** 66 | * Added so that by default, Pull-to-Refresh refreshes the page 67 | */ 68 | setOnRefreshListener(defaultOnRefreshListener); 69 | mRefreshableView.setWebChromeClient(defaultWebChromeClient); 70 | } 71 | 72 | public PullToRefreshWebView(Context context, Mode mode) { 73 | super(context, mode); 74 | 75 | /** 76 | * Added so that by default, Pull-to-Refresh refreshes the page 77 | */ 78 | setOnRefreshListener(defaultOnRefreshListener); 79 | mRefreshableView.setWebChromeClient(defaultWebChromeClient); 80 | } 81 | 82 | public PullToRefreshWebView(Context context, Mode mode, AnimationStyle style) { 83 | super(context, mode, style); 84 | 85 | /** 86 | * Added so that by default, Pull-to-Refresh refreshes the page 87 | */ 88 | setOnRefreshListener(defaultOnRefreshListener); 89 | mRefreshableView.setWebChromeClient(defaultWebChromeClient); 90 | } 91 | 92 | @Override 93 | public final Orientation getPullToRefreshScrollDirection() { 94 | return Orientation.VERTICAL; 95 | } 96 | 97 | @Override 98 | protected WebView createRefreshableView(Context context, AttributeSet attrs) { 99 | WebView webView; 100 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { 101 | webView = new InternalWebViewSDK9(context, attrs); 102 | } else { 103 | webView = new WebView(context, attrs); 104 | } 105 | 106 | webView.setId(R.id.webview); 107 | return webView; 108 | } 109 | 110 | @Override 111 | protected boolean isReadyForPullStart() { 112 | return mRefreshableView.getScrollY() == 0; 113 | } 114 | 115 | @Override 116 | protected boolean isReadyForPullEnd() { 117 | float exactContentHeight = FloatMath.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale()); 118 | return mRefreshableView.getScrollY() >= (exactContentHeight - mRefreshableView.getHeight()); 119 | } 120 | 121 | @Override 122 | protected void onPtrRestoreInstanceState(Bundle savedInstanceState) { 123 | super.onPtrRestoreInstanceState(savedInstanceState); 124 | mRefreshableView.restoreState(savedInstanceState); 125 | } 126 | 127 | @Override 128 | protected void onPtrSaveInstanceState(Bundle saveState) { 129 | super.onPtrSaveInstanceState(saveState); 130 | mRefreshableView.saveState(saveState); 131 | } 132 | 133 | @TargetApi(9) 134 | final class InternalWebViewSDK9 extends WebView { 135 | 136 | // WebView doesn't always scroll back to it's edge so we add some 137 | // fuzziness 138 | static final int OVERSCROLL_FUZZY_THRESHOLD = 2; 139 | 140 | // WebView seems quite reluctant to overscroll so we use the scale 141 | // factor to scale it's value 142 | static final float OVERSCROLL_SCALE_FACTOR = 1.5f; 143 | 144 | public InternalWebViewSDK9(Context context, AttributeSet attrs) { 145 | super(context, attrs); 146 | } 147 | 148 | @Override 149 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, 150 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 151 | 152 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, 153 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 154 | 155 | // Does all of the hard work... 156 | OverscrollHelper.overScrollBy(PullToRefreshWebView.this, deltaX, scrollX, deltaY, scrollY, 157 | getScrollRange(), OVERSCROLL_FUZZY_THRESHOLD, OVERSCROLL_SCALE_FACTOR, isTouchEvent); 158 | 159 | return returnValue; 160 | } 161 | 162 | private int getScrollRange() { 163 | return (int) Math.max(0, FloatMath.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale()) 164 | - (getHeight() - getPaddingBottom() - getPaddingTop())); 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/extras/PullToRefreshWebView2.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 com.handmark.pulltorefresh.library.extras; 18 | 19 | import android.content.Context; 20 | import android.util.AttributeSet; 21 | import android.webkit.WebView; 22 | 23 | import com.handmark.pulltorefresh.library.PullToRefreshWebView; 24 | 25 | import java.util.concurrent.atomic.AtomicBoolean; 26 | 27 | /** 28 | * An advanced version of {@link PullToRefreshWebView} which delegates the 29 | * triggering of the PullToRefresh gesture to the Javascript running within the 30 | * WebView. This means that you should only use this class if: 31 | *

32 | *

    33 | *
  • {@link PullToRefreshWebView} doesn't work correctly because you're using 34 | * overflow:scroll or something else which means 35 | * {@link WebView#getScrollY()} doesn't return correct values.
  • 36 | *
  • You control the web content being displayed, as you need to write some 37 | * Javascript callbacks.
  • 38 | *
39 | *

40 | *

41 | * The way this call works is that when a PullToRefresh gesture is in action, 42 | * the following Javascript methods will be called: 43 | * isReadyForPullDown() and isReadyForPullUp(), it is 44 | * your job to calculate whether the view is in a state where a PullToRefresh 45 | * can happen, and return the result via the callback mechanism. An example can 46 | * be seen below: 47 | *

48 | * 49 | *

 50 |  * function isReadyForPullDown() {
 51 |  *   var result = ...  // Probably using the .scrollTop DOM attribute
 52 |  *   ptr.isReadyForPullDownResponse(result);
 53 |  * }
 54 |  * 
 55 |  * function isReadyForPullUp() {
 56 |  *   var result = ...  // Probably using the .scrollBottom DOM attribute
 57 |  *   ptr.isReadyForPullUpResponse(result);
 58 |  * }
 59 |  * 
60 | * 61 | * @author Chris Banes 62 | */ 63 | public class PullToRefreshWebView2 extends PullToRefreshWebView { 64 | 65 | static final String JS_INTERFACE_PKG = "ptr"; 66 | static final String DEF_JS_READY_PULL_DOWN_CALL = "javascript:isReadyForPullDown();"; 67 | static final String DEF_JS_READY_PULL_UP_CALL = "javascript:isReadyForPullUp();"; 68 | 69 | public PullToRefreshWebView2(Context context) { 70 | super(context); 71 | } 72 | 73 | public PullToRefreshWebView2(Context context, AttributeSet attrs) { 74 | super(context, attrs); 75 | } 76 | 77 | public PullToRefreshWebView2(Context context, Mode mode) { 78 | super(context, mode); 79 | } 80 | 81 | private JsValueCallback mJsCallback; 82 | private final AtomicBoolean mIsReadyForPullDown = new AtomicBoolean(false); 83 | private final AtomicBoolean mIsReadyForPullUp = new AtomicBoolean(false); 84 | 85 | @Override 86 | protected WebView createRefreshableView(Context context, AttributeSet attrs) { 87 | WebView webView = super.createRefreshableView(context, attrs); 88 | 89 | // Need to add JS Interface so we can get the response back 90 | mJsCallback = new JsValueCallback(); 91 | webView.addJavascriptInterface(mJsCallback, JS_INTERFACE_PKG); 92 | 93 | return webView; 94 | } 95 | 96 | @Override 97 | protected boolean isReadyForPullStart() { 98 | // Call Javascript... 99 | getRefreshableView().loadUrl(DEF_JS_READY_PULL_DOWN_CALL); 100 | 101 | // Response will be given to JsValueCallback, which will update 102 | // mIsReadyForPullDown 103 | 104 | return mIsReadyForPullDown.get(); 105 | } 106 | 107 | @Override 108 | protected boolean isReadyForPullEnd() { 109 | // Call Javascript... 110 | getRefreshableView().loadUrl(DEF_JS_READY_PULL_UP_CALL); 111 | 112 | // Response will be given to JsValueCallback, which will update 113 | // mIsReadyForPullUp 114 | 115 | return mIsReadyForPullUp.get(); 116 | } 117 | 118 | /** 119 | * Used for response from Javascript 120 | * 121 | * @author Chris Banes 122 | */ 123 | final class JsValueCallback { 124 | 125 | public void isReadyForPullUpResponse(boolean response) { 126 | mIsReadyForPullUp.set(response); 127 | } 128 | 129 | public void isReadyForPullDownResponse(boolean response) { 130 | mIsReadyForPullDown.set(response); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/extras/SoundPullEventListener.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library.extras; 17 | 18 | import android.content.Context; 19 | import android.media.MediaPlayer; 20 | import android.view.View; 21 | 22 | import com.handmark.pulltorefresh.library.PullToRefreshBase; 23 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode; 24 | import com.handmark.pulltorefresh.library.PullToRefreshBase.State; 25 | 26 | import java.util.HashMap; 27 | 28 | public class SoundPullEventListener implements PullToRefreshBase.OnPullEventListener { 29 | 30 | private final Context mContext; 31 | private final HashMap mSoundMap; 32 | 33 | private MediaPlayer mCurrentMediaPlayer; 34 | 35 | /** 36 | * Constructor 37 | * 38 | * @param context - Context 39 | */ 40 | public SoundPullEventListener(Context context) { 41 | mContext = context; 42 | mSoundMap = new HashMap(); 43 | } 44 | 45 | @Override 46 | public final void onPullEvent(PullToRefreshBase refreshView, State event, Mode direction) { 47 | Integer soundResIdObj = mSoundMap.get(event); 48 | if (null != soundResIdObj) { 49 | playSound(soundResIdObj.intValue()); 50 | } 51 | } 52 | 53 | /** 54 | * Set the Sounds to be played when a Pull Event happens. You specify which 55 | * sound plays for which events by calling this method multiple times for 56 | * each event. 57 | *

58 | * If you've already set a sound for a certain event, and add another sound 59 | * for that event, only the new sound will be played. 60 | * 61 | * @param event - The event for which the sound will be played. 62 | * @param resId - Resource Id of the sound file to be played (e.g. 63 | * R.raw.pull_sound) 64 | */ 65 | public void addSoundEvent(State event, int resId) { 66 | mSoundMap.put(event, resId); 67 | } 68 | 69 | /** 70 | * Clears all of the previously set sounds and events. 71 | */ 72 | public void clearSounds() { 73 | mSoundMap.clear(); 74 | } 75 | 76 | /** 77 | * Gets the current (or last) MediaPlayer instance. 78 | */ 79 | public MediaPlayer getCurrentMediaPlayer() { 80 | return mCurrentMediaPlayer; 81 | } 82 | 83 | private void playSound(int resId) { 84 | // Stop current player, if there's one playing 85 | if (null != mCurrentMediaPlayer) { 86 | mCurrentMediaPlayer.stop(); 87 | mCurrentMediaPlayer.release(); 88 | } 89 | 90 | mCurrentMediaPlayer = MediaPlayer.create(mContext, resId); 91 | if (null != mCurrentMediaPlayer) { 92 | mCurrentMediaPlayer.start(); 93 | } 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/internal/EmptyViewMethodAccessor.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library.internal; 17 | 18 | import android.view.View; 19 | 20 | /** 21 | * Interface that allows PullToRefreshBase to hijack the call to 22 | * AdapterView.setEmptyView() 23 | * 24 | * @author chris 25 | */ 26 | public interface EmptyViewMethodAccessor { 27 | 28 | /** 29 | * Calls upto AdapterView.setEmptyView() 30 | * 31 | * @param emptyView - to set as Empty View 32 | */ 33 | public void setEmptyViewInternal(View emptyView); 34 | 35 | /** 36 | * Should call PullToRefreshBase.setEmptyView() which will then 37 | * automatically call through to setEmptyViewInternal() 38 | * 39 | * @param emptyView - to set as Empty View 40 | */ 41 | public void setEmptyView(View emptyView); 42 | 43 | } 44 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/internal/FlipLoadingLayout.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library.internal; 17 | 18 | import android.annotation.SuppressLint; 19 | import android.content.Context; 20 | import android.content.res.TypedArray; 21 | import android.graphics.Matrix; 22 | import android.graphics.drawable.Drawable; 23 | import android.view.View; 24 | import android.view.ViewGroup; 25 | import android.view.animation.Animation; 26 | import android.view.animation.RotateAnimation; 27 | import android.widget.ImageView.ScaleType; 28 | 29 | import com.bettycc.animatepulltorefresh.library.R; 30 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode; 31 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Orientation; 32 | 33 | ; 34 | 35 | @SuppressLint("ViewConstructor") 36 | public class FlipLoadingLayout extends LoadingLayout { 37 | 38 | static final int FLIP_ANIMATION_DURATION = 150; 39 | 40 | private final Animation mRotateAnimation, mResetRotateAnimation; 41 | 42 | public FlipLoadingLayout(Context context, final Mode mode, final Orientation scrollDirection, TypedArray attrs) { 43 | super(context, mode, scrollDirection, attrs); 44 | 45 | final int rotateAngle = mode == Mode.PULL_FROM_START ? -180 : 180; 46 | 47 | mRotateAnimation = new RotateAnimation(0, rotateAngle, Animation.RELATIVE_TO_SELF, 0.5f, 48 | Animation.RELATIVE_TO_SELF, 0.5f); 49 | mRotateAnimation.setInterpolator(ANIMATION_INTERPOLATOR); 50 | mRotateAnimation.setDuration(FLIP_ANIMATION_DURATION); 51 | mRotateAnimation.setFillAfter(true); 52 | 53 | mResetRotateAnimation = new RotateAnimation(rotateAngle, 0, Animation.RELATIVE_TO_SELF, 0.5f, 54 | Animation.RELATIVE_TO_SELF, 0.5f); 55 | mResetRotateAnimation.setInterpolator(ANIMATION_INTERPOLATOR); 56 | mResetRotateAnimation.setDuration(FLIP_ANIMATION_DURATION); 57 | mResetRotateAnimation.setFillAfter(true); 58 | } 59 | 60 | @Override 61 | protected void onLoadingDrawableSet(Drawable imageDrawable) { 62 | if (null != imageDrawable) { 63 | final int dHeight = imageDrawable.getIntrinsicHeight(); 64 | final int dWidth = imageDrawable.getIntrinsicWidth(); 65 | 66 | /** 67 | * We need to set the width/height of the ImageView so that it is 68 | * square with each side the size of the largest drawable dimension. 69 | * This is so that it doesn't clip when rotated. 70 | */ 71 | ViewGroup.LayoutParams lp = mHeaderImage.getLayoutParams(); 72 | lp.width = lp.height = Math.max(dHeight, dWidth); 73 | mHeaderImage.requestLayout(); 74 | 75 | /** 76 | * We now rotate the Drawable so that is at the correct rotation, 77 | * and is centered. 78 | */ 79 | mHeaderImage.setScaleType(ScaleType.MATRIX); 80 | Matrix matrix = new Matrix(); 81 | matrix.postTranslate((lp.width - dWidth) / 2f, (lp.height - dHeight) / 2f); 82 | matrix.postRotate(getDrawableRotationAngle(), lp.width / 2f, lp.height / 2f); 83 | mHeaderImage.setImageMatrix(matrix); 84 | } 85 | } 86 | 87 | @Override 88 | protected void onPullImpl(float scaleOfLayout) { 89 | // NO-OP 90 | } 91 | 92 | @Override 93 | protected void pullToRefreshImpl() { 94 | // Only start reset Animation, we've previously show the rotate anim 95 | // if (mRotateAnimation == mHeaderImage.getAnimation()) { 96 | // mHeaderImage.startAnimation(mResetRotateAnimation); 97 | // } 98 | } 99 | 100 | @Override 101 | protected void refreshingImpl() { 102 | mHeaderImage.clearAnimation(); 103 | mHeaderImage.setVisibility(View.INVISIBLE); 104 | mHeaderProgress.setVisibility(View.VISIBLE); 105 | } 106 | 107 | @Override 108 | protected void releaseToRefreshImpl() { 109 | // mHeaderImage.startAnimation(mRotateAnimation); 110 | } 111 | 112 | @Override 113 | protected void resetImpl() { 114 | mHeaderImage.clearAnimation(); 115 | mHeaderProgress.setVisibility(View.GONE); 116 | mHeaderImage.setVisibility(View.VISIBLE); 117 | } 118 | 119 | @Override 120 | protected int getDefaultDrawableResId() { 121 | return R.drawable.default_ptr_flip; 122 | } 123 | 124 | private float getDrawableRotationAngle() { 125 | float angle = 0f; 126 | switch (mMode) { 127 | case PULL_FROM_END: 128 | if (mScrollDirection == Orientation.HORIZONTAL) { 129 | angle = 90f; 130 | } else { 131 | angle = 180f; 132 | } 133 | break; 134 | 135 | case PULL_FROM_START: 136 | if (mScrollDirection == Orientation.HORIZONTAL) { 137 | angle = 270f; 138 | } 139 | break; 140 | 141 | default: 142 | break; 143 | } 144 | 145 | return angle; 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/internal/GifAnimation.java: -------------------------------------------------------------------------------- 1 | package com.handmark.pulltorefresh.library.internal; 2 | 3 | import android.os.Handler; 4 | import android.os.Message; 5 | import android.widget.ImageView; 6 | 7 | /** 8 | * Created by ccheng on 8/4/14. 9 | */ 10 | public class GifAnimation implements Handler.Callback { 11 | 12 | public static final int DELAY_MILLIS = 200; 13 | private final ImageView mHeaderImage; 14 | private final int[] mGifReses; 15 | private final Handler mHandler; 16 | private int mIndex; 17 | private boolean mStart; 18 | 19 | public GifAnimation(ImageView headerImage, int[] gifRes) { 20 | mHeaderImage = headerImage; 21 | mGifReses = gifRes; 22 | 23 | mHandler = new Handler(headerImage.getContext().getMainLooper(), this); 24 | } 25 | 26 | public void start() { 27 | if (!mStart) { 28 | mStart = true; 29 | mIndex = 0; 30 | 31 | nextImage(); 32 | mHandler.sendEmptyMessageDelayed(0, DELAY_MILLIS); 33 | } 34 | } 35 | 36 | private void nextImage() { 37 | mHeaderImage.setImageResource(mGifReses[mIndex]); 38 | mIndex = (mIndex + 1) % 3; 39 | } 40 | 41 | @Override 42 | public boolean handleMessage(Message msg) { 43 | if (!mStart) { 44 | return true; 45 | } 46 | 47 | nextImage(); 48 | mHandler.sendEmptyMessageDelayed(0, DELAY_MILLIS); 49 | return true; 50 | } 51 | 52 | public void stop() { 53 | mStart = false; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/internal/GifLoadingLayout.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library.internal; 17 | 18 | import android.content.Context; 19 | import android.content.res.TypedArray; 20 | import android.graphics.Matrix; 21 | import android.graphics.drawable.Drawable; 22 | import android.widget.ImageView.ScaleType; 23 | 24 | import com.bettycc.animatepulltorefresh.library.R; 25 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode; 26 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Orientation; 27 | 28 | import java.io.IOException; 29 | 30 | import pl.droidsonroids.gif.GifDrawable; 31 | 32 | public class GifLoadingLayout extends LoadingLayout { 33 | 34 | static final int ROTATION_ANIMATION_DURATION = 1200; 35 | 36 | private final Matrix mHeaderImageMatrix; 37 | 38 | private final boolean mRotateDrawableWhilePulling; 39 | private GifAnimation mGifAnimation; 40 | 41 | private int[] mGifRes = { 42 | R.drawable.dropdown_loading_00, 43 | R.drawable.dropdown_loading_01, 44 | R.drawable.dropdown_loading_02, 45 | }; 46 | 47 | private GifDrawable mGifDrawable; 48 | private final String mAssetFile; 49 | 50 | public GifLoadingLayout(Context context, Mode mode, Orientation scrollDirection, TypedArray attrs) { 51 | super(context, mode, scrollDirection, attrs); 52 | 53 | mRotateDrawableWhilePulling = attrs.getBoolean(R.styleable.PullToRefresh_ptrRotateDrawableWhilePulling, true); 54 | mAssetFile = attrs.getString(R.styleable.PullToRefresh_ptrGifAsset); 55 | 56 | mHeaderImage.setScaleType(ScaleType.MATRIX); 57 | mHeaderImageMatrix = new Matrix(); 58 | mHeaderImage.setImageMatrix(mHeaderImageMatrix); 59 | try { 60 | mGifDrawable = new GifDrawable(context.getAssets(), mAssetFile); 61 | mHeaderImage.setImageDrawable(mGifDrawable); 62 | } catch (IOException e) { 63 | e.printStackTrace(); 64 | } 65 | } 66 | 67 | public void onLoadingDrawableSet(Drawable imageDrawable) { 68 | } 69 | 70 | int mPrevIndex = -1; 71 | protected void onPullImpl(float scaleOfLayout) { 72 | if (mPrevIndex == -1 && scaleOfLayout > 0.3) { 73 | mGifDrawable.pause(); 74 | mPrevIndex = 0; 75 | } 76 | } 77 | 78 | @Override 79 | protected void refreshingImpl() { 80 | if (!mGifDrawable.isPlaying()) { 81 | mGifDrawable.start(); 82 | } 83 | } 84 | 85 | @Override 86 | protected void resetImpl() { 87 | pauseGif(); 88 | } 89 | 90 | private void pauseGif() { 91 | if (mGifDrawable != null && mGifDrawable.isPlaying()) { 92 | mGifDrawable.pause(); 93 | } 94 | } 95 | 96 | @Override 97 | protected void pullToRefreshImpl() { 98 | // NO-OP 99 | } 100 | 101 | @Override 102 | protected void releaseToRefreshImpl() { 103 | // NO-OP 104 | } 105 | 106 | @Override 107 | protected int getDefaultDrawableResId() { 108 | return R.drawable.default_ptr_rotate; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/internal/IndicatorLayout.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library.internal; 17 | 18 | import android.annotation.SuppressLint; 19 | import android.content.Context; 20 | import android.graphics.Matrix; 21 | import android.graphics.drawable.Drawable; 22 | import android.view.View; 23 | import android.view.animation.Animation; 24 | import android.view.animation.Animation.AnimationListener; 25 | import android.view.animation.AnimationUtils; 26 | import android.view.animation.Interpolator; 27 | import android.view.animation.LinearInterpolator; 28 | import android.view.animation.RotateAnimation; 29 | import android.widget.FrameLayout; 30 | import android.widget.ImageView; 31 | import android.widget.ImageView.ScaleType; 32 | 33 | import com.bettycc.animatepulltorefresh.library.R; 34 | import com.handmark.pulltorefresh.library.PullToRefreshBase; 35 | 36 | @SuppressLint("ViewConstructor") 37 | public class IndicatorLayout extends FrameLayout implements AnimationListener { 38 | 39 | static final int DEFAULT_ROTATION_ANIMATION_DURATION = 150; 40 | 41 | private Animation mInAnim, mOutAnim; 42 | private ImageView mArrowImageView; 43 | 44 | private final Animation mRotateAnimation, mResetRotateAnimation; 45 | 46 | public IndicatorLayout(Context context, PullToRefreshBase.Mode mode) { 47 | super(context); 48 | mArrowImageView = new ImageView(context); 49 | 50 | Drawable arrowD = getResources().getDrawable(R.drawable.indicator_arrow); 51 | mArrowImageView.setImageDrawable(arrowD); 52 | 53 | final int padding = getResources().getDimensionPixelSize(R.dimen.indicator_internal_padding); 54 | mArrowImageView.setPadding(padding, padding, padding, padding); 55 | addView(mArrowImageView); 56 | 57 | int inAnimResId, outAnimResId; 58 | switch (mode) { 59 | case PULL_FROM_END: 60 | inAnimResId = R.anim.slide_in_from_bottom; 61 | outAnimResId = R.anim.slide_out_to_bottom; 62 | setBackgroundResource(R.drawable.indicator_bg_bottom); 63 | 64 | // Rotate Arrow so it's pointing the correct way 65 | mArrowImageView.setScaleType(ScaleType.MATRIX); 66 | Matrix matrix = new Matrix(); 67 | matrix.setRotate(180f, arrowD.getIntrinsicWidth() / 2f, arrowD.getIntrinsicHeight() / 2f); 68 | mArrowImageView.setImageMatrix(matrix); 69 | break; 70 | default: 71 | case PULL_FROM_START: 72 | inAnimResId = R.anim.slide_in_from_top; 73 | outAnimResId = R.anim.slide_out_to_top; 74 | setBackgroundResource(R.drawable.indicator_bg_top); 75 | break; 76 | } 77 | 78 | mInAnim = AnimationUtils.loadAnimation(context, inAnimResId); 79 | mInAnim.setAnimationListener(this); 80 | 81 | mOutAnim = AnimationUtils.loadAnimation(context, outAnimResId); 82 | mOutAnim.setAnimationListener(this); 83 | 84 | final Interpolator interpolator = new LinearInterpolator(); 85 | mRotateAnimation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 86 | 0.5f); 87 | mRotateAnimation.setInterpolator(interpolator); 88 | mRotateAnimation.setDuration(DEFAULT_ROTATION_ANIMATION_DURATION); 89 | mRotateAnimation.setFillAfter(true); 90 | 91 | mResetRotateAnimation = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, 92 | Animation.RELATIVE_TO_SELF, 0.5f); 93 | mResetRotateAnimation.setInterpolator(interpolator); 94 | mResetRotateAnimation.setDuration(DEFAULT_ROTATION_ANIMATION_DURATION); 95 | mResetRotateAnimation.setFillAfter(true); 96 | 97 | } 98 | 99 | public final boolean isVisible() { 100 | Animation currentAnim = getAnimation(); 101 | if (null != currentAnim) { 102 | return mInAnim == currentAnim; 103 | } 104 | 105 | return getVisibility() == View.VISIBLE; 106 | } 107 | 108 | public void hide() { 109 | startAnimation(mOutAnim); 110 | } 111 | 112 | public void show() { 113 | mArrowImageView.clearAnimation(); 114 | startAnimation(mInAnim); 115 | } 116 | 117 | @Override 118 | public void onAnimationEnd(Animation animation) { 119 | if (animation == mOutAnim) { 120 | mArrowImageView.clearAnimation(); 121 | setVisibility(View.GONE); 122 | } else if (animation == mInAnim) { 123 | setVisibility(View.VISIBLE); 124 | } 125 | 126 | clearAnimation(); 127 | } 128 | 129 | @Override 130 | public void onAnimationRepeat(Animation animation) { 131 | // NO-OP 132 | } 133 | 134 | @Override 135 | public void onAnimationStart(Animation animation) { 136 | setVisibility(View.VISIBLE); 137 | } 138 | 139 | public void releaseToRefresh() { 140 | mArrowImageView.startAnimation(mRotateAnimation); 141 | } 142 | 143 | public void pullToRefresh() { 144 | mArrowImageView.startAnimation(mResetRotateAnimation); 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/internal/LoadingLayout.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library.internal; 17 | 18 | import android.annotation.SuppressLint; 19 | import android.content.Context; 20 | import android.content.res.ColorStateList; 21 | import android.content.res.TypedArray; 22 | import android.graphics.Typeface; 23 | import android.graphics.drawable.AnimationDrawable; 24 | import android.graphics.drawable.Drawable; 25 | import android.text.TextUtils; 26 | import android.util.TypedValue; 27 | import android.view.Gravity; 28 | import android.view.LayoutInflater; 29 | import android.view.View; 30 | import android.view.ViewGroup; 31 | import android.view.animation.Interpolator; 32 | import android.view.animation.LinearInterpolator; 33 | import android.widget.FrameLayout; 34 | import android.widget.ImageView; 35 | import android.widget.TextView; 36 | 37 | import com.bettycc.animatepulltorefresh.library.R; 38 | import com.handmark.pulltorefresh.library.ILoadingLayout; 39 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode; 40 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Orientation; 41 | 42 | import pl.droidsonroids.gif.GifImageView; 43 | 44 | @SuppressLint("ViewConstructor") 45 | public abstract class LoadingLayout extends FrameLayout implements ILoadingLayout { 46 | 47 | static final String LOG_TAG = "PullToRefresh-LoadingLayout"; 48 | 49 | static final Interpolator ANIMATION_INTERPOLATOR = new LinearInterpolator(); 50 | 51 | private FrameLayout mInnerLayout; 52 | 53 | public final GifImageView mHeaderImage; 54 | public final ImageView mTickImage; 55 | protected final View mHeaderProgress; 56 | 57 | private boolean mUseIntrinsicAnimation; 58 | 59 | private final TextView mHeaderText; 60 | private final TextView mSubHeaderText; 61 | 62 | protected final Mode mMode; 63 | protected final Orientation mScrollDirection; 64 | 65 | private CharSequence mPullLabel; 66 | private CharSequence mRefreshingLabel; 67 | private CharSequence mReleaseLabel; 68 | 69 | public LoadingLayout(Context context, final Mode mode, final Orientation scrollDirection, TypedArray attrs) { 70 | super(context); 71 | mMode = mode; 72 | mScrollDirection = scrollDirection; 73 | 74 | switch (scrollDirection) { 75 | case HORIZONTAL: 76 | LayoutInflater.from(context).inflate(R.layout.pull_to_refresh_header_horizontal, this); 77 | break; 78 | case VERTICAL: 79 | default: 80 | LayoutInflater.from(context).inflate(R.layout.pull_to_refresh_header_vertical, this); 81 | break; 82 | } 83 | 84 | mInnerLayout = (FrameLayout) findViewById(R.id.fl_inner); 85 | mHeaderText = (TextView) mInnerLayout.findViewById(R.id.pull_to_refresh_text); 86 | mHeaderProgress = mInnerLayout.findViewById(R.id.pull_to_refresh_progress); 87 | mSubHeaderText = (TextView) mInnerLayout.findViewById(R.id.pull_to_refresh_sub_text); 88 | mHeaderImage = (GifImageView) mInnerLayout.findViewById(R.id.pull_to_refresh_image); 89 | mTickImage = (ImageView) mInnerLayout.findViewById(R.id.pull_to_refresh_tick); 90 | 91 | FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInnerLayout.getLayoutParams(); 92 | 93 | switch (mode) { 94 | case PULL_FROM_END: 95 | lp.gravity = scrollDirection == Orientation.VERTICAL ? Gravity.TOP : Gravity.LEFT; 96 | 97 | // Load in labels 98 | mPullLabel = context.getString(R.string.pull_to_refresh_from_bottom_pull_label); 99 | mRefreshingLabel = context.getString(R.string.pull_to_refresh_from_bottom_refreshing_label); 100 | mReleaseLabel = context.getString(R.string.pull_to_refresh_from_bottom_release_label); 101 | break; 102 | 103 | case PULL_FROM_START: 104 | default: 105 | lp.gravity = scrollDirection == Orientation.VERTICAL ? Gravity.BOTTOM : Gravity.RIGHT; 106 | 107 | // Load in labels 108 | mPullLabel = context.getString(R.string.pull_to_refresh_pull_label); 109 | mRefreshingLabel = context.getString(R.string.pull_to_refresh_refreshing_label); 110 | mReleaseLabel = context.getString(R.string.pull_to_refresh_release_label); 111 | break; 112 | } 113 | 114 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrHeaderBackground)) { 115 | Drawable background = attrs.getDrawable(R.styleable.PullToRefresh_ptrHeaderBackground); 116 | if (null != background) { 117 | ViewCompat.setBackground(this, background); 118 | } 119 | } 120 | 121 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrHeaderTextAppearance)) { 122 | TypedValue styleID = new TypedValue(); 123 | attrs.getValue(R.styleable.PullToRefresh_ptrHeaderTextAppearance, styleID); 124 | setTextAppearance(styleID.data); 125 | } 126 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrSubHeaderTextAppearance)) { 127 | TypedValue styleID = new TypedValue(); 128 | attrs.getValue(R.styleable.PullToRefresh_ptrSubHeaderTextAppearance, styleID); 129 | setSubTextAppearance(styleID.data); 130 | } 131 | 132 | // Text Color attrs need to be set after TextAppearance attrs 133 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrHeaderTextColor)) { 134 | ColorStateList colors = attrs.getColorStateList(R.styleable.PullToRefresh_ptrHeaderTextColor); 135 | if (null != colors) { 136 | setTextColor(colors); 137 | } 138 | } 139 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrHeaderSubTextColor)) { 140 | ColorStateList colors = attrs.getColorStateList(R.styleable.PullToRefresh_ptrHeaderSubTextColor); 141 | if (null != colors) { 142 | setSubTextColor(colors); 143 | } 144 | } 145 | 146 | // Try and get defined drawable from Attrs 147 | Drawable imageDrawable = null; 148 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrDrawable)) { 149 | imageDrawable = attrs.getDrawable(R.styleable.PullToRefresh_ptrDrawable); 150 | } 151 | 152 | // Check Specific Drawable from Attrs, these overrite the generic 153 | // drawable attr above 154 | switch (mode) { 155 | case PULL_FROM_START: 156 | default: 157 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrDrawableStart)) { 158 | imageDrawable = attrs.getDrawable(R.styleable.PullToRefresh_ptrDrawableStart); 159 | } else if (attrs.hasValue(R.styleable.PullToRefresh_ptrDrawableTop)) { 160 | Utils.warnDeprecation("ptrDrawableTop", "ptrDrawableStart"); 161 | imageDrawable = attrs.getDrawable(R.styleable.PullToRefresh_ptrDrawableTop); 162 | } 163 | break; 164 | 165 | case PULL_FROM_END: 166 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrDrawableEnd)) { 167 | imageDrawable = attrs.getDrawable(R.styleable.PullToRefresh_ptrDrawableEnd); 168 | } else if (attrs.hasValue(R.styleable.PullToRefresh_ptrDrawableBottom)) { 169 | Utils.warnDeprecation("ptrDrawableBottom", "ptrDrawableEnd"); 170 | imageDrawable = attrs.getDrawable(R.styleable.PullToRefresh_ptrDrawableBottom); 171 | } 172 | break; 173 | } 174 | 175 | // If we don't have a user defined drawable, load the default 176 | if (null == imageDrawable) { 177 | imageDrawable = context.getResources().getDrawable(getDefaultDrawableResId()); 178 | } 179 | 180 | // Set Drawable, and save width/height 181 | setLoadingDrawable(imageDrawable); 182 | 183 | reset(); 184 | } 185 | 186 | public final void setHeight(int height) { 187 | ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) getLayoutParams(); 188 | lp.height = height; 189 | requestLayout(); 190 | } 191 | 192 | public final void setWidth(int width) { 193 | ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) getLayoutParams(); 194 | lp.width = width; 195 | requestLayout(); 196 | } 197 | 198 | public final int getContentSize() { 199 | switch (mScrollDirection) { 200 | case HORIZONTAL: 201 | return mInnerLayout.getWidth(); 202 | case VERTICAL: 203 | default: 204 | return mInnerLayout.getHeight(); 205 | } 206 | } 207 | 208 | public final void hideAllViews() { 209 | if (View.VISIBLE == mHeaderText.getVisibility()) { 210 | mHeaderText.setVisibility(View.INVISIBLE); 211 | } 212 | if (View.VISIBLE == mHeaderProgress.getVisibility()) { 213 | mHeaderProgress.setVisibility(View.INVISIBLE); 214 | } 215 | if (View.VISIBLE == mHeaderImage.getVisibility()) { 216 | mHeaderImage.setVisibility(View.INVISIBLE); 217 | } 218 | if (View.VISIBLE == mSubHeaderText.getVisibility()) { 219 | mSubHeaderText.setVisibility(View.INVISIBLE); 220 | } 221 | } 222 | 223 | public final void onPull(float scaleOfLayout) { 224 | if (!mUseIntrinsicAnimation) { 225 | onPullImpl(scaleOfLayout); 226 | } 227 | } 228 | 229 | public final void pullToRefresh() { 230 | if (null != mHeaderText) { 231 | mHeaderText.setText(mPullLabel); 232 | } 233 | 234 | // Now call the callback 235 | pullToRefreshImpl(); 236 | } 237 | 238 | public final void refreshing() { 239 | if (null != mHeaderText) { 240 | mHeaderText.setText(mRefreshingLabel); 241 | } 242 | 243 | if (mUseIntrinsicAnimation) { 244 | ((AnimationDrawable) mHeaderImage.getDrawable()).start(); 245 | } else { 246 | // Now call the callback 247 | refreshingImpl(); 248 | } 249 | 250 | if (null != mSubHeaderText) { 251 | mSubHeaderText.setVisibility(View.GONE); 252 | } 253 | } 254 | 255 | public final void releaseToRefresh() { 256 | if (null != mHeaderText) { 257 | mHeaderText.setText(mReleaseLabel); 258 | } 259 | 260 | // Now call the callback 261 | releaseToRefreshImpl(); 262 | } 263 | 264 | public final void reset() { 265 | if (null != mHeaderText) { 266 | mHeaderText.setText(mPullLabel); 267 | } 268 | mHeaderImage.setVisibility(View.VISIBLE); 269 | 270 | if (mUseIntrinsicAnimation) { 271 | ((AnimationDrawable) mHeaderImage.getDrawable()).stop(); 272 | } else { 273 | // Now call the callback 274 | resetImpl(); 275 | } 276 | 277 | if (null != mSubHeaderText) { 278 | if (TextUtils.isEmpty(mSubHeaderText.getText())) { 279 | mSubHeaderText.setVisibility(View.GONE); 280 | } else { 281 | mSubHeaderText.setVisibility(View.VISIBLE); 282 | } 283 | } 284 | } 285 | 286 | @Override 287 | public void setLastUpdatedLabel(CharSequence label) { 288 | setSubHeaderText(label); 289 | } 290 | 291 | public final void setLoadingDrawable(Drawable imageDrawable) { 292 | // Set Drawable 293 | mHeaderImage.setImageDrawable(imageDrawable); 294 | mUseIntrinsicAnimation = (imageDrawable instanceof AnimationDrawable); 295 | 296 | // Now call the callback 297 | onLoadingDrawableSet(imageDrawable); 298 | } 299 | 300 | public void setPullLabel(CharSequence pullLabel) { 301 | mPullLabel = pullLabel; 302 | } 303 | 304 | public void setRefreshingLabel(CharSequence refreshingLabel) { 305 | mRefreshingLabel = refreshingLabel; 306 | } 307 | 308 | public void setReleaseLabel(CharSequence releaseLabel) { 309 | mReleaseLabel = releaseLabel; 310 | } 311 | 312 | @Override 313 | public void setTextTypeface(Typeface tf) { 314 | mHeaderText.setTypeface(tf); 315 | } 316 | 317 | public final void showInvisibleViews() { 318 | if (View.INVISIBLE == mHeaderText.getVisibility()) { 319 | mHeaderText.setVisibility(View.VISIBLE); 320 | } 321 | if (View.INVISIBLE == mHeaderProgress.getVisibility()) { 322 | mHeaderProgress.setVisibility(View.VISIBLE); 323 | } 324 | if (View.INVISIBLE == mHeaderImage.getVisibility()) { 325 | mHeaderImage.setVisibility(View.VISIBLE); 326 | } 327 | if (View.INVISIBLE == mSubHeaderText.getVisibility()) { 328 | mSubHeaderText.setVisibility(View.VISIBLE); 329 | } 330 | } 331 | 332 | /** 333 | * Callbacks for derivative Layouts 334 | */ 335 | 336 | protected abstract int getDefaultDrawableResId(); 337 | 338 | protected abstract void onLoadingDrawableSet(Drawable imageDrawable); 339 | 340 | protected abstract void onPullImpl(float scaleOfLayout); 341 | 342 | protected abstract void pullToRefreshImpl(); 343 | 344 | protected abstract void refreshingImpl(); 345 | 346 | protected abstract void releaseToRefreshImpl(); 347 | 348 | protected abstract void resetImpl(); 349 | 350 | private void setSubHeaderText(CharSequence label) { 351 | if (null != mSubHeaderText) { 352 | if (TextUtils.isEmpty(label)) { 353 | mSubHeaderText.setVisibility(View.GONE); 354 | } else { 355 | mSubHeaderText.setText(label); 356 | 357 | // Only set it to Visible if we're GONE, otherwise VISIBLE will 358 | // be set soon 359 | if (View.GONE == mSubHeaderText.getVisibility()) { 360 | mSubHeaderText.setVisibility(View.VISIBLE); 361 | } 362 | } 363 | } 364 | } 365 | 366 | private void setSubTextAppearance(int value) { 367 | if (null != mSubHeaderText) { 368 | mSubHeaderText.setTextAppearance(getContext(), value); 369 | } 370 | } 371 | 372 | private void setSubTextColor(ColorStateList color) { 373 | if (null != mSubHeaderText) { 374 | mSubHeaderText.setTextColor(color); 375 | } 376 | } 377 | 378 | private void setTextAppearance(int value) { 379 | if (null != mHeaderText) { 380 | mHeaderText.setTextAppearance(getContext(), value); 381 | } 382 | if (null != mSubHeaderText) { 383 | mSubHeaderText.setTextAppearance(getContext(), value); 384 | } 385 | } 386 | 387 | private void setTextColor(ColorStateList color) { 388 | if (null != mHeaderText) { 389 | mHeaderText.setTextColor(color); 390 | } 391 | if (null != mSubHeaderText) { 392 | mSubHeaderText.setTextColor(color); 393 | } 394 | } 395 | 396 | } 397 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/internal/RotateLoadingLayout.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library.internal; 17 | 18 | import android.content.Context; 19 | import android.content.res.TypedArray; 20 | import android.graphics.Bitmap; 21 | import android.graphics.BitmapFactory; 22 | import android.graphics.Matrix; 23 | import android.graphics.drawable.Drawable; 24 | import android.widget.ImageView.ScaleType; 25 | 26 | import com.bettycc.animatepulltorefresh.library.R; 27 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode; 28 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Orientation; 29 | 30 | 31 | public class RotateLoadingLayout extends LoadingLayout { 32 | 33 | static final int ROTATION_ANIMATION_DURATION = 1200; 34 | 35 | private final Matrix mHeaderImageMatrix; 36 | 37 | private float mRotationPivotX, mRotationPivotY; 38 | 39 | private final boolean mRotateDrawableWhilePulling; 40 | private GifAnimation mGifAnimation; 41 | 42 | private int[] mGifRes = { 43 | R.drawable.dropdown_loading_00, 44 | R.drawable.dropdown_loading_01, 45 | R.drawable.dropdown_loading_02, 46 | }; 47 | 48 | public RotateLoadingLayout(Context context, Mode mode, Orientation scrollDirection, TypedArray attrs) { 49 | super(context, mode, scrollDirection, attrs); 50 | 51 | mRotateDrawableWhilePulling = attrs.getBoolean(R.styleable.PullToRefresh_ptrRotateDrawableWhilePulling, true); 52 | 53 | mHeaderImage.setScaleType(ScaleType.MATRIX); 54 | mHeaderImageMatrix = new Matrix(); 55 | mHeaderImage.setImageMatrix(mHeaderImageMatrix); 56 | } 57 | 58 | public void onLoadingDrawableSet(Drawable imageDrawable) { 59 | System.out.println("RotateLoadingLayout.onLoadingDrawableSet"); 60 | if (null != imageDrawable) { 61 | mRotationPivotX = Math.round(imageDrawable.getIntrinsicWidth() / 2f); 62 | mRotationPivotY = Math.round(imageDrawable.getIntrinsicHeight() / 2f); 63 | } 64 | } 65 | 66 | int mPrevIndex = -1; 67 | protected void onPullImpl(float scaleOfLayout) { 68 | float angle; 69 | if (mRotateDrawableWhilePulling) { 70 | angle = scaleOfLayout * 90f; 71 | } else { 72 | angle = Math.max(0f, Math.min(180f, scaleOfLayout * 360f - 180f)); 73 | } 74 | 75 | float max = 1.7f; 76 | int index = (int) (scaleOfLayout / 1f * 10); 77 | if (index == mPrevIndex) { 78 | return; 79 | } else { 80 | if (index > 10) { 81 | index = 10; 82 | } 83 | int res = getResources().getIdentifier(String.format("dropdown_anim_%02d", 84 | index), "drawable", getContext().getPackageName()); 85 | // Bitmap scaledBitmap = getScaledBitmap(res, index); 86 | // mHeaderImage.setImageBitmap(scaledBitmap); 87 | mHeaderImage.setImageResource(res); 88 | mPrevIndex = index; 89 | } 90 | } 91 | 92 | private Bitmap getScaledBitmap(int res, int index) { 93 | float p = ((float) index/10*7 + 3)/10; 94 | Bitmap bitmap = BitmapFactory.decodeResource(getResources(), res); 95 | return Bitmap.createScaledBitmap(bitmap, (int)(mHeaderImage.getWidth()*p), (int)(mHeaderImage.getHeight()*p), false); 96 | } 97 | 98 | @Override 99 | protected void refreshingImpl() { 100 | if (mGifAnimation == null) { 101 | mGifAnimation = new GifAnimation(mHeaderImage, mGifRes); 102 | } 103 | mGifAnimation.start(); 104 | } 105 | 106 | @Override 107 | protected void resetImpl() { 108 | mHeaderImage.clearAnimation(); 109 | if (mGifAnimation != null) { 110 | mGifAnimation.stop(); 111 | } 112 | } 113 | 114 | @Override 115 | protected void pullToRefreshImpl() { 116 | // NO-OP 117 | } 118 | 119 | @Override 120 | protected void releaseToRefreshImpl() { 121 | // NO-OP 122 | } 123 | 124 | @Override 125 | protected int getDefaultDrawableResId() { 126 | return R.drawable.default_ptr_rotate; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/internal/Utils.java: -------------------------------------------------------------------------------- 1 | package com.handmark.pulltorefresh.library.internal; 2 | 3 | import android.util.Log; 4 | 5 | public class Utils { 6 | 7 | static final String LOG_TAG = "PullToRefresh"; 8 | 9 | public static void warnDeprecation(String depreacted, String replacement) { 10 | Log.w(LOG_TAG, "You're using the deprecated " + depreacted + " attr, please switch over to " + replacement); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /library/src/main/java/com/handmark/pulltorefresh/library/internal/ViewCompat.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011, 2012 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 | package com.handmark.pulltorefresh.library.internal; 17 | 18 | import android.annotation.TargetApi; 19 | import android.graphics.drawable.Drawable; 20 | import android.os.Build.VERSION; 21 | import android.os.Build.VERSION_CODES; 22 | import android.view.View; 23 | 24 | @SuppressWarnings("deprecation") 25 | public class ViewCompat { 26 | 27 | public static void postOnAnimation(View view, Runnable runnable) { 28 | if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { 29 | SDK16.postOnAnimation(view, runnable); 30 | } else { 31 | view.postDelayed(runnable, 16); 32 | } 33 | } 34 | 35 | public static void setBackground(View view, Drawable background) { 36 | if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { 37 | SDK16.setBackground(view, background); 38 | } else { 39 | view.setBackgroundDrawable(background); 40 | } 41 | } 42 | 43 | public static void setLayerType(View view, int layerType) { 44 | if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) { 45 | SDK11.setLayerType(view, layerType); 46 | } 47 | } 48 | 49 | @TargetApi(11) 50 | static class SDK11 { 51 | 52 | public static void setLayerType(View view, int layerType) { 53 | view.setLayerType(layerType, null); 54 | } 55 | } 56 | 57 | @TargetApi(16) 58 | static class SDK16 { 59 | 60 | public static void postOnAnimation(View view, Runnable runnable) { 61 | view.postOnAnimation(runnable); 62 | } 63 | 64 | public static void setBackground(View view, Drawable background) { 65 | view.setBackground(background); 66 | } 67 | 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /library/src/main/res/anim/slide_in_from_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 22 | -------------------------------------------------------------------------------- /library/src/main/res/anim/slide_in_from_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 22 | -------------------------------------------------------------------------------- /library/src/main/res/anim/slide_out_to_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 22 | -------------------------------------------------------------------------------- /library/src/main/res/anim/slide_out_to_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 22 | -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/default_ptr_flip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-hdpi/default_ptr_flip.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/default_ptr_rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-hdpi/default_ptr_rotate.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/indicator_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-hdpi/indicator_arrow.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/default_ptr_flip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-mdpi/default_ptr_flip.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/default_ptr_rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-mdpi/default_ptr_rotate.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/indicator_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-mdpi/indicator_arrow.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/default_ptr_flip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/default_ptr_flip.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/default_ptr_rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/default_ptr_rotate.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/dropdown_anim_00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/dropdown_anim_00.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/dropdown_anim_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/dropdown_anim_01.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/dropdown_anim_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/dropdown_anim_02.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/dropdown_anim_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/dropdown_anim_03.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/dropdown_anim_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/dropdown_anim_04.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/dropdown_anim_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/dropdown_anim_05.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/dropdown_anim_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/dropdown_anim_06.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/dropdown_anim_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/dropdown_anim_07.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/dropdown_anim_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/dropdown_anim_08.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/dropdown_anim_09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/dropdown_anim_09.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/dropdown_anim_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/dropdown_anim_10.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/dropdown_loading_00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/dropdown_loading_00.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/dropdown_loading_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/dropdown_loading_01.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/dropdown_loading_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/dropdown_loading_02.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/gif_loading1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/gif_loading1.gif -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/indicator_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/indicator_arrow.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/loading.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xhdpi/ok.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/library/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/drawable/indicator_bg_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/indicator_bg_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /library/src/main/res/layout/pull_to_refresh_header_horizontal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 18 | 19 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /library/src/main/res/layout/pull_to_refresh_header_vertical.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 18 | 19 | 24 | 25 | 33 | 34 | 41 | 42 | 43 | 44 | 51 | 52 | 59 | 60 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /library/src/main/res/values-ar/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | اسحب للتحديث… 4 | اترك للتحديث… 5 | تحميل… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-cs/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tažením aktualizujete… 4 | Uvolněním aktualizujete… 5 | Načítání… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-de/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Ziehen zum Aktualisieren… 4 | Loslassen zum Aktualisieren… 5 | Laden… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-es/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tirar para actualizar… 4 | Soltar para actualizar… 5 | Cargando… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-fi/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Päivitä vetämällä alas… 5 | Päivitä vapauttamalla… 6 | Päivitetään… 7 | 8 | 9 | Päivitä vetämällä ylös… 10 | @string/pull_to_refresh_release_label 11 | @string/pull_to_refresh_refreshing_label 12 | 13 | -------------------------------------------------------------------------------- /library/src/main/res/values-fr/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tirez pour rafraîchir… 4 | Relâcher pour rafraîchir… 5 | Chargement… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-he/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | משוך לרענון… 4 | שחרר לרענון… 5 | טוען… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-it/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tira per aggiornare… 4 | Rilascia per aggionare… 5 | Caricamento… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-iw/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | משוך לרענון… 4 | שחרר לרענון… 5 | טוען… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-ja/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 画面を引っ張って… 4 | 指を離して更新… 5 | 読み込み中… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-ko/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 당겨서 새로 고침… 4 | 놓아서 새로 고침… 5 | 로드 중… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-nl/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sleep om te vernieuwen… 4 | Loslaten om te vernieuwen… 5 | Laden… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-pl/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Pociągnij, aby odświeżyć… 4 | Puść, aby odświeżyć… 5 | Wczytywanie… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-pt-rBR/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Puxe para atualizar… 4 | Libere para atualizar… 5 | Carregando… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-pt/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Puxe para atualizar… 4 | Liberação para atualizar… 5 | A carregar… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-ro/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Trage pentru a reîmprospăta… 4 | Eliberează pentru a reîmprospăta… 5 | Încărcare… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-ru/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Потяните для обновления… 4 | Отпустите для обновления… 5 | Загрузка… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values-zh/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 下拉刷新… 4 | 放开以刷新… 5 | 正在载入… 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 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 | 69 | 70 | 71 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /library/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10dp 5 | 12dp 6 | 4dp 7 | 24dp 8 | 12dp 9 | 10 | -------------------------------------------------------------------------------- /library/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /library/src/main/res/values/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Pull to refresh… 5 | Release to refresh… 6 | Loading… 7 | 8 | 9 | @string/pull_to_refresh_pull_label 10 | @string/pull_to_refresh_release_label 11 | @string/pull_to_refresh_refreshing_label 12 | 13 | -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | My Module 3 | 4 | -------------------------------------------------------------------------------- /library/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':library' 2 | -------------------------------------------------------------------------------- /slide.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufo22940268/Android-AnimatePullToRefreshListView/43f552852b275fb9d5a5b2bf513092e9d260d07e/slide.gif --------------------------------------------------------------------------------