├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── itheima │ │ └── pulltorefresh │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── itheima │ │ │ └── pulltorefresh │ │ │ └── MainActivity.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── itheima │ └── pulltorefresh │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── pulltorefreshlib ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── itheima │ │ └── pulltorefreshlib │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── itheima │ │ │ └── pulltorefreshlib │ │ │ ├── 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 │ │ │ ├── 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 │ │ └── indicator_arrow.png │ │ ├── drawable-mdpi │ │ ├── default_ptr_flip.png │ │ ├── default_ptr_rotate.png │ │ └── indicator_arrow.png │ │ ├── drawable-xhdpi │ │ ├── default_ptr_flip.png │ │ ├── default_ptr_rotate.png │ │ └── indicator_arrow.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 │ └── test │ └── java │ └── com │ └── itheima │ └── pulltorefreshlib │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 1.8 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PullToRefresh 2 | 3 | 开源地址:[https://github.com/open-android/PullToRefresh](https://github.com/open-android/PullToRefresh) 4 | 5 | PS:如果觉得文章太长,你也可观看该课程的[视频](https://www.boxuegu.com/web/html/video.html?courseId=172§ionId=8a2c9bed5a3a4c7e015a3bbffc6107ed&chapterId=8a2c9bed5a3a4c7e015a3a64deae0002&vId=8a2c9bed5a3a4c7e015a3b0d2f19077b&videoId=5738119AAE4197C89C33DC5901307461),亲,里面还有高清,无码的福利喔 6 | 7 | ## 运行效果 ## 8 | ![pulltorefresh.gif](http://upload-images.jianshu.io/upload_images/4037105-f14baf5a661b9b2d.gif?imageMogr2/auto-orient/strip) 9 | 10 | * 爱生活,爱学习,更爱做代码的搬运工,分类查找更方便请下载黑马助手app 11 | 12 | 13 | ![黑马助手.png](http://upload-images.jianshu.io/upload_images/4037105-f777f1214328dcc4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 14 | 15 | 16 | ## 使用步骤 17 | ### 1. 添加依赖 ### 18 | //在项目下的build.gradle 19 | allprojects { 20 | repositories { 21 | ... 22 | maven { url 'https://jitpack.io' } 23 | } 24 | } 25 | 26 | //在app模块下的build.gradle 27 | dependencies { 28 | compile 'com.github.open-android:PullToRefresh:v1.0' 29 | } 30 | 31 | ![](http://oi5nqn6ce.bkt.clouddn.com/itheima/booster/code/jitpack.png) 32 | 33 | 34 | ### 2. 在布局中添加PullToRefreshListView ### 35 | 36 | 42 | 43 | 47 | 48 | 49 | ### 3. 设置Adapter ### 50 | private ArrayAdapter mArrayAdapter; 51 | private PullToRefreshListView mPullToRefreshListView; 52 | 53 | mPullToRefreshListView = (PullToRefreshListView) findViewById(R.id.pull_to_refresh_list_view); 54 | mArrayAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, mockList(0, 30)); 55 | mPullToRefreshListView.setAdapter(mArrayAdapter); 56 | 57 | ### 4. 添加模拟数据 ### 58 | private ArrayList mItems = new ArrayList(); 59 | /** 60 | * 创建或动态添加测试用的数据列表 61 | * @param start 数据的起始位置 62 | * @param count 数据的个数 63 | * @return 当前的数据列表 64 | */ 65 | private List mockList(int start, int count) { 66 | for (int i = start; i < start + count; i++) { 67 | mItems.add("黑马程序员:" + i); 68 | } 69 | return mItems; 70 | } 71 | 72 | ### 5. 设置模式 ### 73 | //设置模式BOTH: 既能上拉也能下拉, 74 | mPullToRefreshListView.setMode(PullToRefreshBase.Mode.BOTH); 75 | 76 | ### 6. 设置监听 ### 77 | //设置刷新监听 78 | mPullToRefreshListView.setOnRefreshListener(mListViewOnRefreshListener2); 79 | 80 | private PullToRefreshBase.OnRefreshListener2 mListViewOnRefreshListener2 = new PullToRefreshBase.OnRefreshListener2() { 81 | 82 | /** 83 | * 下拉刷新回调 84 | * @param refreshView 85 | */ 86 | @Override 87 | public void onPullDownToRefresh(PullToRefreshBase refreshView) { 88 | //模拟延时三秒刷新 89 | mPullToRefreshListView.postDelayed(new Runnable() { 90 | @Override 91 | public void run() { 92 | mItems.clear(); 93 | mockList(0, 20); 94 | mArrayAdapter.notifyDataSetChanged(); 95 | mPullToRefreshListView.onRefreshComplete();//下拉刷新结束,下拉刷新头复位 96 | 97 | } 98 | }, 3000); 99 | } 100 | 101 | /** 102 | * 上拉加载更多回调 103 | * @param refreshView 104 | */ 105 | @Override 106 | public void onPullUpToRefresh(PullToRefreshBase refreshView) { 107 | //模拟延时三秒加载更多数据 108 | mPullToRefreshListView.postDelayed(new Runnable() { 109 | @Override 110 | public void run() { 111 | mockList(mItems.size(), 20); 112 | mArrayAdapter.notifyDataSetChanged(); 113 | mPullToRefreshListView.onRefreshComplete();//上拉加载更多结束,上拉加载头复位 114 | } 115 | }, 3000); 116 | } 117 | }; 118 | 119 | 120 | 121 | 122 | 123 | * 详细的使用方法在DEMO里面都演示啦,如果你觉得这个库还不错,请赏我一颗star吧~~~ 124 | 125 | * 欢迎关注微信公众号 126 | 127 | ![](http://upload-images.jianshu.io/upload_images/4037105-8f737b5104dd0b5d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 128 | 129 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "com.itheima.pulltorefresh" 8 | minSdkVersion 15 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.1.0' 28 | testCompile 'junit:junit:4.12' 29 | compile project(':pulltorefreshlib') 30 | } 31 | -------------------------------------------------------------------------------- /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 C:\software\AndroidStudio\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/com/itheima/pulltorefresh/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.itheima.pulltorefresh; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.itheima.pulltorefresh", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/itheima/pulltorefresh/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.itheima.pulltorefresh; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.widget.ArrayAdapter; 6 | import android.widget.ListView; 7 | 8 | import com.itheima.pulltorefreshlib.PullToRefreshBase; 9 | import com.itheima.pulltorefreshlib.PullToRefreshListView; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class MainActivity extends AppCompatActivity { 15 | 16 | private ArrayList mItems = new ArrayList(); 17 | ; 18 | private ArrayAdapter mArrayAdapter; 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_main); 24 | final PullToRefreshListView pullToRefreshListView = (PullToRefreshListView) findViewById(R.id.pull_to_refresh_list_view); 25 | mArrayAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, mockList(0, 30)); 26 | pullToRefreshListView.setAdapter(mArrayAdapter); 27 | pullToRefreshListView.setMode(PullToRefreshBase.Mode.BOTH); 28 | pullToRefreshListView.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener2() { 29 | 30 | /** 31 | * 下拉刷新回调 32 | * @param refreshView 33 | */ 34 | @Override 35 | public void onPullDownToRefresh(PullToRefreshBase refreshView) { 36 | //模拟延时三秒刷新 37 | pullToRefreshListView.postDelayed(new Runnable() { 38 | @Override 39 | public void run() { 40 | mItems.clear(); 41 | mockList(0, 20); 42 | mArrayAdapter.notifyDataSetChanged(); 43 | pullToRefreshListView.onRefreshComplete(); 44 | 45 | } 46 | }, 3000); 47 | } 48 | 49 | /** 50 | * 上拉加载更多回调 51 | * @param refreshView 52 | */ 53 | @Override 54 | public void onPullUpToRefresh(PullToRefreshBase refreshView) { 55 | //模拟延时三秒加载更多数据 56 | pullToRefreshListView.postDelayed(new Runnable() { 57 | @Override 58 | public void run() { 59 | mockList(mItems.size(), 20); 60 | mArrayAdapter.notifyDataSetChanged(); 61 | pullToRefreshListView.onRefreshComplete(); 62 | } 63 | }, 3000); 64 | } 65 | }); 66 | } 67 | 68 | 69 | private List mockList(int start, int count) { 70 | for (int i = start; i < start + count; i++) { 71 | mItems.add("黑马程序员:" + i); 72 | } 73 | return mItems; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-android/PullToRefresh/ffbcc1572ea38f63d63ed20ecf8efc60c87aa567/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-android/PullToRefresh/ffbcc1572ea38f63d63ed20ecf8efc60c87aa567/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-android/PullToRefresh/ffbcc1572ea38f63d63ed20ecf8efc60c87aa567/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-android/PullToRefresh/ffbcc1572ea38f63d63ed20ecf8efc60c87aa567/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-android/PullToRefresh/ffbcc1572ea38f63d63ed20ecf8efc60c87aa567/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | PullToRefresh 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/itheima/pulltorefresh/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.itheima.pulltorefresh; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /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:2.2.0' 9 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 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 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 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 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-android/PullToRefresh/ffbcc1572ea38f63d63ed20ecf8efc60c87aa567/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-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 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /pulltorefreshlib/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /pulltorefreshlib/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | 4 | group='com.github.open-android' 5 | 6 | android { 7 | compileSdkVersion 25 8 | buildToolsVersion "25.0.2" 9 | 10 | defaultConfig { 11 | minSdkVersion 15 12 | targetSdkVersion 25 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 17 | 18 | } 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | compile fileTree(dir: 'libs', include: ['*.jar']) 29 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 30 | exclude group: 'com.android.support', module: 'support-annotations' 31 | }) 32 | compile 'com.android.support:appcompat-v7:25.1.0' 33 | testCompile 'junit:junit:4.12' 34 | } 35 | -------------------------------------------------------------------------------- /pulltorefreshlib/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 C:\software\AndroidStudio\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 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/androidTest/java/com/itheima/pulltorefreshlib/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.itheima.pulltorefreshlib; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.itheima.pulltorefreshlib.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/ILoadingLayout.java: -------------------------------------------------------------------------------- 1 | package com.itheima.pulltorefreshlib; 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 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib; 17 | 18 | import android.view.View; 19 | import android.view.animation.Interpolator; 20 | 21 | 22 | 23 | public interface IPullToRefresh { 24 | 25 | /** 26 | * Demos the Pull-to-Refresh functionality to the user so that they are 27 | * aware it is there. This could be useful when the user first opens your 28 | * app, etc. The animation will only happen if the Refresh View (ListView, 29 | * ScrollView, etc) is in a state where a Pull-to-Refresh could occur by a 30 | * user's touch gesture (i.e. scrolled to the top/bottom). 31 | * 32 | * @return true - if the Demo has been started, false if not. 33 | */ 34 | public boolean demo(); 35 | 36 | /** 37 | * Get the mode that this view is currently in. This is only really useful 38 | * when using Mode.BOTH. 39 | * 40 | * @return Mode that the view is currently in 41 | */ 42 | public PullToRefreshBase.Mode getCurrentMode(); 43 | 44 | /** 45 | * Returns whether the Touch Events are filtered or not. If true is 46 | * returned, then the View will only use touch events where the difference 47 | * in the Y-axis is greater than the difference in the X-axis. This means 48 | * that the View will not interfere when it is used in a horizontal 49 | * scrolling View (such as a ViewPager). 50 | * 51 | * @return boolean - true if the View is filtering Touch Events 52 | */ 53 | public boolean getFilterTouchEvents(); 54 | 55 | /** 56 | * Returns a proxy object which allows you to call methods on all of the 57 | * LoadingLayouts (the Views which show when Pulling/Refreshing). 58 | *

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

73 | * You should not keep the result of this method any longer than you need 74 | * it. 75 | * 76 | * @param includeStart - Whether to include the Start/Header Views 77 | * @param includeEnd - Whether to include the End/Footer Views 78 | * @return Object which will proxy any calls you make on it, to the 79 | * LoadingLayouts included. 80 | */ 81 | public ILoadingLayout getLoadingLayoutProxy(boolean includeStart, boolean includeEnd); 82 | 83 | /** 84 | * Get the mode that this view has been set to. If this returns 85 | * Mode.BOTH, you can use getCurrentMode() to 86 | * check which mode the view is currently in 87 | * 88 | * @return Mode that the view has been set to 89 | */ 90 | public PullToRefreshBase.Mode getMode(); 91 | 92 | /** 93 | * Get the Wrapped Refreshable View. Anything returned here has already been 94 | * added to the content view. 95 | * 96 | * @return The View which is currently wrapped 97 | */ 98 | public T getRefreshableView(); 99 | 100 | /** 101 | * Get whether the 'Refreshing' View should be automatically shown when 102 | * refreshing. Returns true by default. 103 | * 104 | * @return - true if the Refreshing View will be show 105 | */ 106 | public boolean getShowViewWhileRefreshing(); 107 | 108 | /** 109 | * @return - The state that the View is currently in. 110 | */ 111 | public PullToRefreshBase.State getState(); 112 | 113 | /** 114 | * Whether Pull-to-Refresh is enabled 115 | * 116 | * @return enabled 117 | */ 118 | public boolean isPullToRefreshEnabled(); 119 | 120 | /** 121 | * Gets whether Overscroll support is enabled. This is different to 122 | * Android's standard Overscroll support (the edge-glow) which is available 123 | * from GINGERBREAD onwards 124 | * 125 | * @return true - if both PullToRefresh-OverScroll and Android's inbuilt 126 | * OverScroll are enabled 127 | */ 128 | public boolean isPullToRefreshOverScrollEnabled(); 129 | 130 | /** 131 | * Returns whether the Widget is currently in the Refreshing mState 132 | * 133 | * @return true if the Widget is currently refreshing 134 | */ 135 | public boolean isRefreshing(); 136 | 137 | /** 138 | * Returns whether the widget has enabled scrolling on the Refreshable View 139 | * while refreshing. 140 | * 141 | * @return true if the widget has enabled scrolling while refreshing 142 | */ 143 | public boolean isScrollingWhileRefreshingEnabled(); 144 | 145 | /** 146 | * Mark the current Refresh as complete. Will Reset the UI and hide the 147 | * Refreshing View 148 | */ 149 | public void onRefreshComplete(); 150 | 151 | /** 152 | * Set the Touch Events to be filtered or not. If set to true, then the View 153 | * will only use touch events where the difference in the Y-axis is greater 154 | * than the difference in the X-axis. This means that the View will not 155 | * interfere when it is used in a horizontal scrolling View (such as a 156 | * ViewPager), but will restrict which types of finger scrolls will trigger 157 | * the View. 158 | * 159 | * @param filterEvents - true if you want to filter Touch Events. Default is 160 | * true. 161 | */ 162 | public void setFilterTouchEvents(boolean filterEvents); 163 | 164 | /** 165 | * Set the mode of Pull-to-Refresh that this view will use. 166 | * 167 | * @param mode - Mode to set the View to 168 | */ 169 | public void setMode(PullToRefreshBase.Mode mode); 170 | 171 | /** 172 | * Set OnPullEventListener for the Widget 173 | * 174 | * @param listener - Listener to be used when the Widget has a pull event to 175 | * propogate. 176 | */ 177 | public void setOnPullEventListener(PullToRefreshBase.OnPullEventListener listener); 178 | 179 | /** 180 | * Set OnRefreshListener for the Widget 181 | * 182 | * @param listener - Listener to be used when the Widget is set to Refresh 183 | */ 184 | public void setOnRefreshListener(PullToRefreshBase.OnRefreshListener listener); 185 | 186 | /** 187 | * Set OnRefreshListener for the Widget 188 | * 189 | * @param listener - Listener to be used when the Widget is set to Refresh 190 | */ 191 | public void setOnRefreshListener(PullToRefreshBase.OnRefreshListener2 listener); 192 | 193 | /** 194 | * Sets whether Overscroll support is enabled. This is different to 195 | * Android's standard Overscroll support (the edge-glow). This setting only 196 | * takes effect when running on device with Android v2.3 or greater. 197 | * 198 | * @param enabled - true if you want Overscroll enabled 199 | */ 200 | public void setPullToRefreshOverScrollEnabled(boolean enabled); 201 | 202 | /** 203 | * Sets the Widget to be in the refresh state. The UI will be updated to 204 | * show the 'Refreshing' view, and be scrolled to show such. 205 | */ 206 | public void setRefreshing(); 207 | 208 | /** 209 | * Sets the Widget to be in the refresh state. The UI will be updated to 210 | * show the 'Refreshing' view. 211 | * 212 | * @param doScroll - true if you want to force a scroll to the Refreshing 213 | * view. 214 | */ 215 | public void setRefreshing(boolean doScroll); 216 | 217 | /** 218 | * Sets the Animation Interpolator that is used for animated scrolling. 219 | * Defaults to a DecelerateInterpolator 220 | * 221 | * @param interpolator - Interpolator to use 222 | */ 223 | public void setScrollAnimationInterpolator(Interpolator interpolator); 224 | 225 | /** 226 | * By default the Widget disables scrolling on the Refreshable View while 227 | * refreshing. This method can change this behaviour. 228 | * 229 | * @param scrollingWhileRefreshingEnabled - true if you want to enable 230 | * scrolling while refreshing 231 | */ 232 | public void setScrollingWhileRefreshingEnabled(boolean scrollingWhileRefreshingEnabled); 233 | 234 | /** 235 | * A mutator to enable/disable whether the 'Refreshing' View should be 236 | * automatically shown when refreshing. 237 | * 238 | * @param showView 239 | */ 240 | public void setShowViewWhileRefreshing(boolean showView); 241 | 242 | } -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/LoadingLayoutProxy.java: -------------------------------------------------------------------------------- 1 | package com.itheima.pulltorefreshlib; 2 | 3 | import android.graphics.Typeface; 4 | import android.graphics.drawable.Drawable; 5 | 6 | import com.itheima.pulltorefreshlib.internal.LoadingLayout; 7 | 8 | import java.util.HashSet; 9 | 10 | 11 | public class LoadingLayoutProxy implements ILoadingLayout { 12 | 13 | private final HashSet mLoadingLayouts; 14 | 15 | LoadingLayoutProxy() { 16 | mLoadingLayouts = new HashSet(); 17 | } 18 | 19 | /** 20 | * This allows you to add extra LoadingLayout instances to this proxy. This 21 | * is only necessary if you keep your own instances, and want to have them 22 | * included in any 23 | * {@link PullToRefreshBase#createLoadingLayoutProxy(boolean, boolean) 24 | * createLoadingLayoutProxy(...)} calls. 25 | * 26 | * @param layout - LoadingLayout to have included. 27 | */ 28 | public void addLayout(LoadingLayout layout) { 29 | if (null != layout) { 30 | mLoadingLayouts.add(layout); 31 | } 32 | } 33 | 34 | @Override 35 | public void setLastUpdatedLabel(CharSequence label) { 36 | for (LoadingLayout layout : mLoadingLayouts) { 37 | layout.setLastUpdatedLabel(label); 38 | } 39 | } 40 | 41 | @Override 42 | public void setLoadingDrawable(Drawable drawable) { 43 | for (LoadingLayout layout : mLoadingLayouts) { 44 | layout.setLoadingDrawable(drawable); 45 | } 46 | } 47 | 48 | @Override 49 | public void setRefreshingLabel(CharSequence refreshingLabel) { 50 | for (LoadingLayout layout : mLoadingLayouts) { 51 | layout.setRefreshingLabel(refreshingLabel); 52 | } 53 | } 54 | 55 | @Override 56 | public void setPullLabel(CharSequence label) { 57 | for (LoadingLayout layout : mLoadingLayouts) { 58 | layout.setPullLabel(label); 59 | } 60 | } 61 | 62 | @Override 63 | public void setReleaseLabel(CharSequence label) { 64 | for (LoadingLayout layout : mLoadingLayouts) { 65 | layout.setReleaseLabel(label); 66 | } 67 | } 68 | 69 | public void setTextTypeface(Typeface tf) { 70 | for (LoadingLayout layout : mLoadingLayouts) { 71 | layout.setTextTypeface(tf); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib; 17 | 18 | import android.annotation.TargetApi; 19 | import android.util.Log; 20 | import android.view.View; 21 | 22 | import com.itheima.pulltorefreshlib.PullToRefreshBase.Mode; 23 | import com.itheima.pulltorefreshlib.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 | if (mode.permitsPullToRefresh() && !isTouchEvent && deltaValue != 0) { 129 | final int newScrollValue = (deltaValue + scrollValue); 130 | 131 | if (PullToRefreshBase.DEBUG) { 132 | Log.d(LOG_TAG, "OverScroll. DeltaX: " + deltaX + ", ScrollX: " + scrollX + ", DeltaY: " + deltaY 133 | + ", ScrollY: " + scrollY + ", NewY: " + newScrollValue + ", ScrollRange: " + scrollRange 134 | + ", CurrentScroll: " + currentScrollValue); 135 | } 136 | 137 | if (newScrollValue < (0 - fuzzyThreshold)) { 138 | // Check the mode supports the overscroll direction, and 139 | // then move scroll 140 | if (mode.showHeaderLoadingLayout()) { 141 | // If we're currently at zero, we're about to start 142 | // overscrolling, so change the state 143 | if (currentScrollValue == 0) { 144 | view.setState(State.OVERSCROLLING); 145 | } 146 | 147 | view.setHeaderScroll((int) (scaleFactor * (currentScrollValue + newScrollValue))); 148 | } 149 | } else if (newScrollValue > (scrollRange + fuzzyThreshold)) { 150 | // Check the mode supports the overscroll direction, and 151 | // then move scroll 152 | if (mode.showFooterLoadingLayout()) { 153 | // If we're currently at zero, we're about to start 154 | // overscrolling, so change the state 155 | if (currentScrollValue == 0) { 156 | view.setState(State.OVERSCROLLING); 157 | } 158 | 159 | view.setHeaderScroll((int) (scaleFactor * (currentScrollValue + newScrollValue - scrollRange))); 160 | } 161 | } else if (Math.abs(newScrollValue) <= fuzzyThreshold 162 | || Math.abs(newScrollValue - scrollRange) <= fuzzyThreshold) { 163 | // Means we've stopped overscrolling, so scroll back to 0 164 | view.setState(State.RESET); 165 | } 166 | } else if (isTouchEvent && State.OVERSCROLLING == view.getState()) { 167 | // This condition means that we were overscrolling from a fling, 168 | // but the user has touched the View and is now overscrolling 169 | // from touch instead. We need to just reset. 170 | view.setState(State.RESET); 171 | } 172 | } 173 | } 174 | 175 | static boolean isAndroidOverScrollEnabled(View view) { 176 | return view.getOverScrollMode() != View.OVER_SCROLL_NEVER; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib; 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.itheima.pulltorefreshlib.internal.EmptyViewMethodAccessor; 36 | import com.itheima.pulltorefreshlib.internal.IndicatorLayout; 37 | 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(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 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib; 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.itheima.pulltorefreshlib.internal.EmptyViewMethodAccessor; 27 | 28 | 29 | public class PullToRefreshExpandableListView extends PullToRefreshAdapterViewBase { 30 | 31 | public PullToRefreshExpandableListView(Context context) { 32 | super(context); 33 | } 34 | 35 | public PullToRefreshExpandableListView(Context context, AttributeSet attrs) { 36 | super(context, attrs); 37 | } 38 | 39 | public PullToRefreshExpandableListView(Context context, Mode mode) { 40 | super(context, mode); 41 | } 42 | 43 | public PullToRefreshExpandableListView(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 ExpandableListView createRefreshableView(Context context, AttributeSet attrs) { 54 | final ExpandableListView lv; 55 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { 56 | lv = new InternalExpandableListViewSDK9(context, attrs); 57 | } else { 58 | lv = new InternalExpandableListView(context, attrs); 59 | } 60 | 61 | // Set it to this so it can be used in ListActivity/ListFragment 62 | lv.setId(android.R.id.list); 63 | return lv; 64 | } 65 | 66 | class InternalExpandableListView extends ExpandableListView implements EmptyViewMethodAccessor { 67 | 68 | public InternalExpandableListView(Context context, AttributeSet attrs) { 69 | super(context, attrs); 70 | } 71 | 72 | @Override 73 | public void setEmptyView(View emptyView) { 74 | PullToRefreshExpandableListView.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 InternalExpandableListViewSDK9 extends InternalExpandableListView { 85 | 86 | public InternalExpandableListViewSDK9(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(PullToRefreshExpandableListView.this, deltaX, scrollX, deltaY, scrollY, 99 | isTouchEvent); 100 | 101 | return returnValue; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib; 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.itheima.pulltorefreshlib.internal.EmptyViewMethodAccessor; 27 | 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 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib; 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 | public class PullToRefreshHorizontalScrollView extends PullToRefreshBase { 27 | 28 | public PullToRefreshHorizontalScrollView(Context context) { 29 | super(context); 30 | } 31 | 32 | public PullToRefreshHorizontalScrollView(Context context, AttributeSet attrs) { 33 | super(context, attrs); 34 | } 35 | 36 | public PullToRefreshHorizontalScrollView(Context context, Mode mode) { 37 | super(context, mode); 38 | } 39 | 40 | public PullToRefreshHorizontalScrollView(Context context, Mode mode, AnimationStyle style) { 41 | super(context, mode, style); 42 | } 43 | 44 | @Override 45 | public final Orientation getPullToRefreshScrollDirection() { 46 | return Orientation.HORIZONTAL; 47 | } 48 | 49 | @Override 50 | protected HorizontalScrollView createRefreshableView(Context context, AttributeSet attrs) { 51 | HorizontalScrollView scrollView; 52 | 53 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { 54 | scrollView = new InternalHorizontalScrollViewSDK9(context, attrs); 55 | } else { 56 | scrollView = new HorizontalScrollView(context, attrs); 57 | } 58 | 59 | scrollView.setId(R.id.scrollview); 60 | return scrollView; 61 | } 62 | 63 | @Override 64 | protected boolean isReadyForPullStart() { 65 | return mRefreshableView.getScrollX() == 0; 66 | } 67 | 68 | @Override 69 | protected boolean isReadyForPullEnd() { 70 | View scrollViewChild = mRefreshableView.getChildAt(0); 71 | if (null != scrollViewChild) { 72 | return mRefreshableView.getScrollX() >= (scrollViewChild.getWidth() - getWidth()); 73 | } 74 | return false; 75 | } 76 | 77 | @TargetApi(9) 78 | final class InternalHorizontalScrollViewSDK9 extends HorizontalScrollView { 79 | 80 | public InternalHorizontalScrollViewSDK9(Context context, AttributeSet attrs) { 81 | super(context, attrs); 82 | } 83 | 84 | @Override 85 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, 86 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 87 | 88 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, 89 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 90 | 91 | // Does all of the hard work... 92 | OverscrollHelper.overScrollBy(PullToRefreshHorizontalScrollView.this, deltaX, scrollX, deltaY, scrollY, 93 | getScrollRange(), isTouchEvent); 94 | 95 | return returnValue; 96 | } 97 | 98 | /** 99 | * Taken from the AOSP ScrollView source 100 | */ 101 | private int getScrollRange() { 102 | int scrollRange = 0; 103 | if (getChildCount() > 0) { 104 | View child = getChildAt(0); 105 | scrollRange = Math.max(0, child.getWidth() - (getWidth() - getPaddingLeft() - getPaddingRight())); 106 | } 107 | return scrollRange; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib; 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.itheima.pulltorefreshlib.internal.EmptyViewMethodAccessor; 33 | import com.itheima.pulltorefreshlib.internal.LoadingLayout; 34 | 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 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib; 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 | public class PullToRefreshScrollView extends PullToRefreshBase { 27 | 28 | public PullToRefreshScrollView(Context context) { 29 | super(context); 30 | } 31 | 32 | public PullToRefreshScrollView(Context context, AttributeSet attrs) { 33 | super(context, attrs); 34 | } 35 | 36 | public PullToRefreshScrollView(Context context, Mode mode) { 37 | super(context, mode); 38 | } 39 | 40 | public PullToRefreshScrollView(Context context, Mode mode, AnimationStyle style) { 41 | super(context, mode, style); 42 | } 43 | 44 | @Override 45 | public final Orientation getPullToRefreshScrollDirection() { 46 | return Orientation.VERTICAL; 47 | } 48 | 49 | @Override 50 | protected ScrollView createRefreshableView(Context context, AttributeSet attrs) { 51 | ScrollView scrollView; 52 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { 53 | scrollView = new InternalScrollViewSDK9(context, attrs); 54 | } else { 55 | scrollView = new ScrollView(context, attrs); 56 | } 57 | 58 | scrollView.setId(R.id.scrollview); 59 | return scrollView; 60 | } 61 | 62 | @Override 63 | protected boolean isReadyForPullStart() { 64 | return mRefreshableView.getScrollY() == 0; 65 | } 66 | 67 | @Override 68 | protected boolean isReadyForPullEnd() { 69 | View scrollViewChild = mRefreshableView.getChildAt(0); 70 | if (null != scrollViewChild) { 71 | return mRefreshableView.getScrollY() >= (scrollViewChild.getHeight() - getHeight()); 72 | } 73 | return false; 74 | } 75 | 76 | @TargetApi(9) 77 | final class InternalScrollViewSDK9 extends ScrollView { 78 | 79 | public InternalScrollViewSDK9(Context context, AttributeSet attrs) { 80 | super(context, attrs); 81 | } 82 | 83 | @Override 84 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, 85 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 86 | 87 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, 88 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 89 | 90 | // Does all of the hard work... 91 | OverscrollHelper.overScrollBy(PullToRefreshScrollView.this, deltaX, scrollX, deltaY, scrollY, 92 | getScrollRange(), isTouchEvent); 93 | 94 | return returnValue; 95 | } 96 | 97 | /** 98 | * Taken from the AOSP ScrollView source 99 | */ 100 | private int getScrollRange() { 101 | int scrollRange = 0; 102 | if (getChildCount() > 0) { 103 | View child = getChildAt(0); 104 | scrollRange = Math.max(0, child.getHeight() - (getHeight() - getPaddingBottom() - getPaddingTop())); 105 | } 106 | return scrollRange; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib; 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 | public class PullToRefreshWebView extends PullToRefreshBase { 29 | 30 | private static final OnRefreshListener defaultOnRefreshListener = new OnRefreshListener() { 31 | 32 | @Override 33 | public void onRefresh(PullToRefreshBase refreshView) { 34 | refreshView.getRefreshableView().reload(); 35 | } 36 | 37 | }; 38 | 39 | private final WebChromeClient defaultWebChromeClient = new WebChromeClient() { 40 | 41 | @Override 42 | public void onProgressChanged(WebView view, int newProgress) { 43 | if (newProgress == 100) { 44 | onRefreshComplete(); 45 | } 46 | } 47 | 48 | }; 49 | 50 | public PullToRefreshWebView(Context context) { 51 | super(context); 52 | 53 | /** 54 | * Added so that by default, Pull-to-Refresh refreshes the page 55 | */ 56 | setOnRefreshListener(defaultOnRefreshListener); 57 | mRefreshableView.setWebChromeClient(defaultWebChromeClient); 58 | } 59 | 60 | public PullToRefreshWebView(Context context, AttributeSet attrs) { 61 | super(context, attrs); 62 | 63 | /** 64 | * Added so that by default, Pull-to-Refresh refreshes the page 65 | */ 66 | setOnRefreshListener(defaultOnRefreshListener); 67 | mRefreshableView.setWebChromeClient(defaultWebChromeClient); 68 | } 69 | 70 | public PullToRefreshWebView(Context context, Mode mode) { 71 | super(context, mode); 72 | 73 | /** 74 | * Added so that by default, Pull-to-Refresh refreshes the page 75 | */ 76 | setOnRefreshListener(defaultOnRefreshListener); 77 | mRefreshableView.setWebChromeClient(defaultWebChromeClient); 78 | } 79 | 80 | public PullToRefreshWebView(Context context, Mode mode, AnimationStyle style) { 81 | super(context, mode, style); 82 | 83 | /** 84 | * Added so that by default, Pull-to-Refresh refreshes the page 85 | */ 86 | setOnRefreshListener(defaultOnRefreshListener); 87 | mRefreshableView.setWebChromeClient(defaultWebChromeClient); 88 | } 89 | 90 | @Override 91 | public final Orientation getPullToRefreshScrollDirection() { 92 | return Orientation.VERTICAL; 93 | } 94 | 95 | @Override 96 | protected WebView createRefreshableView(Context context, AttributeSet attrs) { 97 | WebView webView; 98 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { 99 | webView = new InternalWebViewSDK9(context, attrs); 100 | } else { 101 | webView = new WebView(context, attrs); 102 | } 103 | 104 | webView.setId(R.id.webview); 105 | return webView; 106 | } 107 | 108 | @Override 109 | protected boolean isReadyForPullStart() { 110 | return mRefreshableView.getScrollY() == 0; 111 | } 112 | 113 | @Override 114 | protected boolean isReadyForPullEnd() { 115 | float exactContentHeight = (float) Math.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale()); 116 | return mRefreshableView.getScrollY() >= (exactContentHeight - mRefreshableView.getHeight()); 117 | } 118 | 119 | @Override 120 | protected void onPtrRestoreInstanceState(Bundle savedInstanceState) { 121 | super.onPtrRestoreInstanceState(savedInstanceState); 122 | mRefreshableView.restoreState(savedInstanceState); 123 | } 124 | 125 | @Override 126 | protected void onPtrSaveInstanceState(Bundle saveState) { 127 | super.onPtrSaveInstanceState(saveState); 128 | mRefreshableView.saveState(saveState); 129 | } 130 | 131 | @TargetApi(9) 132 | final class InternalWebViewSDK9 extends WebView { 133 | 134 | // WebView doesn't always scroll back to it's edge so we add some 135 | // fuzziness 136 | static final int OVERSCROLL_FUZZY_THRESHOLD = 2; 137 | 138 | // WebView seems quite reluctant to overscroll so we use the scale 139 | // factor to scale it's value 140 | static final float OVERSCROLL_SCALE_FACTOR = 1.5f; 141 | 142 | public InternalWebViewSDK9(Context context, AttributeSet attrs) { 143 | super(context, attrs); 144 | } 145 | 146 | @Override 147 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, 148 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 149 | 150 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, 151 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 152 | 153 | // Does all of the hard work... 154 | OverscrollHelper.overScrollBy(PullToRefreshWebView.this, deltaX, scrollX, deltaY, scrollY, 155 | getScrollRange(), OVERSCROLL_FUZZY_THRESHOLD, OVERSCROLL_SCALE_FACTOR, isTouchEvent); 156 | 157 | return returnValue; 158 | } 159 | 160 | private int getScrollRange() { 161 | return (int) Math.max(0, Math.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale()) 162 | - (getHeight() - getPaddingBottom() - getPaddingTop())); 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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 | package com.itheima.pulltorefreshlib.extras; 17 | 18 | import android.content.Context; 19 | import android.util.AttributeSet; 20 | import android.webkit.WebView; 21 | 22 | import com.itheima.pulltorefreshlib.PullToRefreshWebView; 23 | 24 | import java.util.concurrent.atomic.AtomicBoolean; 25 | 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 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib.extras; 17 | 18 | import android.content.Context; 19 | import android.media.MediaPlayer; 20 | import android.view.View; 21 | 22 | import com.itheima.pulltorefreshlib.PullToRefreshBase.Mode; 23 | import com.itheima.pulltorefreshlib.PullToRefreshBase.State; 24 | import com.itheima.pulltorefreshlib.PullToRefreshBase; 25 | 26 | import java.util.HashMap; 27 | 28 | 29 | public class SoundPullEventListener implements PullToRefreshBase.OnPullEventListener { 30 | 31 | private final Context mContext; 32 | private final HashMap mSoundMap; 33 | 34 | private MediaPlayer mCurrentMediaPlayer; 35 | 36 | /** 37 | * Constructor 38 | * 39 | * @param context - Context 40 | */ 41 | public SoundPullEventListener(Context context) { 42 | mContext = context; 43 | mSoundMap = new HashMap(); 44 | } 45 | 46 | @Override 47 | public final void onPullEvent(PullToRefreshBase refreshView, State event, Mode direction) { 48 | Integer soundResIdObj = mSoundMap.get(event); 49 | if (null != soundResIdObj) { 50 | playSound(soundResIdObj.intValue()); 51 | } 52 | } 53 | 54 | /** 55 | * Set the Sounds to be played when a Pull Event happens. You specify which 56 | * sound plays for which events by calling this method multiple times for 57 | * each event. 58 | *

59 | * If you've already set a sound for a certain event, and add another sound 60 | * for that event, only the new sound will be played. 61 | * 62 | * @param event - The event for which the sound will be played. 63 | * @param resId - Resource Id of the sound file to be played (e.g. 64 | * R.raw.pull_sound) 65 | */ 66 | public void addSoundEvent(State event, int resId) { 67 | mSoundMap.put(event, resId); 68 | } 69 | 70 | /** 71 | * Clears all of the previously set sounds and events. 72 | */ 73 | public void clearSounds() { 74 | mSoundMap.clear(); 75 | } 76 | 77 | /** 78 | * Gets the current (or last) MediaPlayer instance. 79 | */ 80 | public MediaPlayer getCurrentMediaPlayer() { 81 | return mCurrentMediaPlayer; 82 | } 83 | 84 | private void playSound(int resId) { 85 | // Stop current player, if there's one playing 86 | if (null != mCurrentMediaPlayer) { 87 | mCurrentMediaPlayer.stop(); 88 | mCurrentMediaPlayer.release(); 89 | } 90 | 91 | mCurrentMediaPlayer = MediaPlayer.create(mContext, resId); 92 | if (null != mCurrentMediaPlayer) { 93 | mCurrentMediaPlayer.start(); 94 | } 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib.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 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib.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.itheima.pulltorefreshlib.PullToRefreshBase; 30 | import com.itheima.pulltorefreshlib.R; 31 | 32 | 33 | @SuppressLint("ViewConstructor") 34 | public class FlipLoadingLayout extends LoadingLayout { 35 | 36 | static final int FLIP_ANIMATION_DURATION = 150; 37 | 38 | private final Animation mRotateAnimation, mResetRotateAnimation; 39 | 40 | public FlipLoadingLayout(Context context, final PullToRefreshBase.Mode mode, final PullToRefreshBase.Orientation scrollDirection, TypedArray attrs) { 41 | super(context, mode, scrollDirection, attrs); 42 | 43 | final int rotateAngle = mode == PullToRefreshBase.Mode.PULL_FROM_START ? -180 : 180; 44 | 45 | mRotateAnimation = new RotateAnimation(0, rotateAngle, Animation.RELATIVE_TO_SELF, 0.5f, 46 | Animation.RELATIVE_TO_SELF, 0.5f); 47 | mRotateAnimation.setInterpolator(ANIMATION_INTERPOLATOR); 48 | mRotateAnimation.setDuration(FLIP_ANIMATION_DURATION); 49 | mRotateAnimation.setFillAfter(true); 50 | 51 | mResetRotateAnimation = new RotateAnimation(rotateAngle, 0, Animation.RELATIVE_TO_SELF, 0.5f, 52 | Animation.RELATIVE_TO_SELF, 0.5f); 53 | mResetRotateAnimation.setInterpolator(ANIMATION_INTERPOLATOR); 54 | mResetRotateAnimation.setDuration(FLIP_ANIMATION_DURATION); 55 | mResetRotateAnimation.setFillAfter(true); 56 | } 57 | 58 | @Override 59 | protected void onLoadingDrawableSet(Drawable imageDrawable) { 60 | if (null != imageDrawable) { 61 | final int dHeight = imageDrawable.getIntrinsicHeight(); 62 | final int dWidth = imageDrawable.getIntrinsicWidth(); 63 | 64 | /** 65 | * We need to set the width/height of the ImageView so that it is 66 | * square with each side the size of the largest drawable dimension. 67 | * This is so that it doesn't clip when rotated. 68 | */ 69 | ViewGroup.LayoutParams lp = mHeaderImage.getLayoutParams(); 70 | lp.width = lp.height = Math.max(dHeight, dWidth); 71 | mHeaderImage.requestLayout(); 72 | 73 | /** 74 | * We now rotate the Drawable so that is at the correct rotation, 75 | * and is centered. 76 | */ 77 | mHeaderImage.setScaleType(ScaleType.MATRIX); 78 | Matrix matrix = new Matrix(); 79 | matrix.postTranslate((lp.width - dWidth) / 2f, (lp.height - dHeight) / 2f); 80 | matrix.postRotate(getDrawableRotationAngle(), lp.width / 2f, lp.height / 2f); 81 | mHeaderImage.setImageMatrix(matrix); 82 | } 83 | } 84 | 85 | @Override 86 | protected void onPullImpl(float scaleOfLayout) { 87 | // NO-OP 88 | } 89 | 90 | @Override 91 | protected void pullToRefreshImpl() { 92 | // Only start reset Animation, we've previously show the rotate anim 93 | if (mRotateAnimation == mHeaderImage.getAnimation()) { 94 | mHeaderImage.startAnimation(mResetRotateAnimation); 95 | } 96 | } 97 | 98 | @Override 99 | protected void refreshingImpl() { 100 | mHeaderImage.clearAnimation(); 101 | mHeaderImage.setVisibility(View.INVISIBLE); 102 | mHeaderProgress.setVisibility(View.VISIBLE); 103 | } 104 | 105 | @Override 106 | protected void releaseToRefreshImpl() { 107 | mHeaderImage.startAnimation(mRotateAnimation); 108 | } 109 | 110 | @Override 111 | protected void resetImpl() { 112 | mHeaderImage.clearAnimation(); 113 | mHeaderProgress.setVisibility(View.GONE); 114 | mHeaderImage.setVisibility(View.VISIBLE); 115 | } 116 | 117 | @Override 118 | protected int getDefaultDrawableResId() { 119 | return R.drawable.default_ptr_flip; 120 | } 121 | 122 | private float getDrawableRotationAngle() { 123 | float angle = 0f; 124 | switch (mMode) { 125 | case PULL_FROM_END: 126 | if (mScrollDirection == PullToRefreshBase.Orientation.HORIZONTAL) { 127 | angle = 90f; 128 | } else { 129 | angle = 180f; 130 | } 131 | break; 132 | 133 | case PULL_FROM_START: 134 | if (mScrollDirection == PullToRefreshBase.Orientation.HORIZONTAL) { 135 | angle = 270f; 136 | } 137 | break; 138 | 139 | default: 140 | break; 141 | } 142 | 143 | return angle; 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib.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.itheima.pulltorefreshlib.PullToRefreshBase; 34 | import com.itheima.pulltorefreshlib.R; 35 | 36 | 37 | @SuppressLint("ViewConstructor") 38 | public class IndicatorLayout extends FrameLayout implements AnimationListener { 39 | 40 | static final int DEFAULT_ROTATION_ANIMATION_DURATION = 150; 41 | 42 | private Animation mInAnim, mOutAnim; 43 | private ImageView mArrowImageView; 44 | 45 | private final Animation mRotateAnimation, mResetRotateAnimation; 46 | 47 | public IndicatorLayout(Context context, PullToRefreshBase.Mode mode) { 48 | super(context); 49 | mArrowImageView = new ImageView(context); 50 | 51 | Drawable arrowD = getResources().getDrawable(R.drawable.indicator_arrow); 52 | mArrowImageView.setImageDrawable(arrowD); 53 | 54 | final int padding = getResources().getDimensionPixelSize(R.dimen.indicator_internal_padding); 55 | mArrowImageView.setPadding(padding, padding, padding, padding); 56 | addView(mArrowImageView); 57 | 58 | int inAnimResId, outAnimResId; 59 | switch (mode) { 60 | case PULL_FROM_END: 61 | inAnimResId = R.anim.slide_in_from_bottom; 62 | outAnimResId = R.anim.slide_out_to_bottom; 63 | setBackgroundResource(R.drawable.indicator_bg_bottom); 64 | 65 | // Rotate Arrow so it's pointing the correct way 66 | mArrowImageView.setScaleType(ScaleType.MATRIX); 67 | Matrix matrix = new Matrix(); 68 | matrix.setRotate(180f, arrowD.getIntrinsicWidth() / 2f, arrowD.getIntrinsicHeight() / 2f); 69 | mArrowImageView.setImageMatrix(matrix); 70 | break; 71 | default: 72 | case PULL_FROM_START: 73 | inAnimResId = R.anim.slide_in_from_top; 74 | outAnimResId = R.anim.slide_out_to_top; 75 | setBackgroundResource(R.drawable.indicator_bg_top); 76 | break; 77 | } 78 | 79 | mInAnim = AnimationUtils.loadAnimation(context, inAnimResId); 80 | mInAnim.setAnimationListener(this); 81 | 82 | mOutAnim = AnimationUtils.loadAnimation(context, outAnimResId); 83 | mOutAnim.setAnimationListener(this); 84 | 85 | final Interpolator interpolator = new LinearInterpolator(); 86 | mRotateAnimation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 87 | 0.5f); 88 | mRotateAnimation.setInterpolator(interpolator); 89 | mRotateAnimation.setDuration(DEFAULT_ROTATION_ANIMATION_DURATION); 90 | mRotateAnimation.setFillAfter(true); 91 | 92 | mResetRotateAnimation = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, 93 | Animation.RELATIVE_TO_SELF, 0.5f); 94 | mResetRotateAnimation.setInterpolator(interpolator); 95 | mResetRotateAnimation.setDuration(DEFAULT_ROTATION_ANIMATION_DURATION); 96 | mResetRotateAnimation.setFillAfter(true); 97 | 98 | } 99 | 100 | public final boolean isVisible() { 101 | Animation currentAnim = getAnimation(); 102 | if (null != currentAnim) { 103 | return mInAnim == currentAnim; 104 | } 105 | 106 | return getVisibility() == View.VISIBLE; 107 | } 108 | 109 | public void hide() { 110 | startAnimation(mOutAnim); 111 | } 112 | 113 | public void show() { 114 | mArrowImageView.clearAnimation(); 115 | startAnimation(mInAnim); 116 | } 117 | 118 | @Override 119 | public void onAnimationEnd(Animation animation) { 120 | if (animation == mOutAnim) { 121 | mArrowImageView.clearAnimation(); 122 | setVisibility(View.GONE); 123 | } else if (animation == mInAnim) { 124 | setVisibility(View.VISIBLE); 125 | } 126 | 127 | clearAnimation(); 128 | } 129 | 130 | @Override 131 | public void onAnimationRepeat(Animation animation) { 132 | // NO-OP 133 | } 134 | 135 | @Override 136 | public void onAnimationStart(Animation animation) { 137 | setVisibility(View.VISIBLE); 138 | } 139 | 140 | public void releaseToRefresh() { 141 | mArrowImageView.startAnimation(mRotateAnimation); 142 | } 143 | 144 | public void pullToRefresh() { 145 | mArrowImageView.startAnimation(mResetRotateAnimation); 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib.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.ProgressBar; 36 | import android.widget.TextView; 37 | 38 | import com.itheima.pulltorefreshlib.ILoadingLayout; 39 | import com.itheima.pulltorefreshlib.PullToRefreshBase; 40 | import com.itheima.pulltorefreshlib.R; 41 | 42 | 43 | @SuppressLint("ViewConstructor") 44 | public abstract class LoadingLayout extends FrameLayout implements ILoadingLayout { 45 | 46 | static final String LOG_TAG = "PullToRefresh-LoadingLayout"; 47 | 48 | static final Interpolator ANIMATION_INTERPOLATOR = new LinearInterpolator(); 49 | 50 | private FrameLayout mInnerLayout; 51 | 52 | protected final ImageView mHeaderImage; 53 | protected final ProgressBar mHeaderProgress; 54 | 55 | private boolean mUseIntrinsicAnimation; 56 | 57 | private final TextView mHeaderText; 58 | private final TextView mSubHeaderText; 59 | 60 | protected final PullToRefreshBase.Mode mMode; 61 | protected final PullToRefreshBase.Orientation mScrollDirection; 62 | 63 | private CharSequence mPullLabel; 64 | private CharSequence mRefreshingLabel; 65 | private CharSequence mReleaseLabel; 66 | 67 | public LoadingLayout(Context context, final PullToRefreshBase.Mode mode, final PullToRefreshBase.Orientation scrollDirection, TypedArray attrs) { 68 | super(context); 69 | mMode = mode; 70 | mScrollDirection = scrollDirection; 71 | 72 | switch (scrollDirection) { 73 | case HORIZONTAL: 74 | LayoutInflater.from(context).inflate(R.layout.pull_to_refresh_header_horizontal, this); 75 | break; 76 | case VERTICAL: 77 | default: 78 | LayoutInflater.from(context).inflate(R.layout.pull_to_refresh_header_vertical, this); 79 | break; 80 | } 81 | 82 | mInnerLayout = (FrameLayout) findViewById(R.id.fl_inner); 83 | mHeaderText = (TextView) mInnerLayout.findViewById(R.id.pull_to_refresh_text); 84 | mHeaderProgress = (ProgressBar) mInnerLayout.findViewById(R.id.pull_to_refresh_progress); 85 | mSubHeaderText = (TextView) mInnerLayout.findViewById(R.id.pull_to_refresh_sub_text); 86 | mHeaderImage = (ImageView) mInnerLayout.findViewById(R.id.pull_to_refresh_image); 87 | 88 | LayoutParams lp = (LayoutParams) mInnerLayout.getLayoutParams(); 89 | 90 | switch (mode) { 91 | case PULL_FROM_END: 92 | lp.gravity = scrollDirection == PullToRefreshBase.Orientation.VERTICAL ? Gravity.TOP : Gravity.LEFT; 93 | 94 | // Load in labels 95 | mPullLabel = context.getString(R.string.pull_to_refresh_from_bottom_pull_label); 96 | mRefreshingLabel = context.getString(R.string.pull_to_refresh_from_bottom_refreshing_label); 97 | mReleaseLabel = context.getString(R.string.pull_to_refresh_from_bottom_release_label); 98 | break; 99 | 100 | case PULL_FROM_START: 101 | default: 102 | lp.gravity = scrollDirection == PullToRefreshBase.Orientation.VERTICAL ? Gravity.BOTTOM : Gravity.RIGHT; 103 | 104 | // Load in labels 105 | mPullLabel = context.getString(R.string.pull_to_refresh_pull_label); 106 | mRefreshingLabel = context.getString(R.string.pull_to_refresh_refreshing_label); 107 | mReleaseLabel = context.getString(R.string.pull_to_refresh_release_label); 108 | break; 109 | } 110 | 111 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrHeaderBackground)) { 112 | Drawable background = attrs.getDrawable(R.styleable.PullToRefresh_ptrHeaderBackground); 113 | if (null != background) { 114 | ViewCompat.setBackground(this, background); 115 | } 116 | } 117 | 118 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrHeaderTextAppearance)) { 119 | TypedValue styleID = new TypedValue(); 120 | attrs.getValue(R.styleable.PullToRefresh_ptrHeaderTextAppearance, styleID); 121 | setTextAppearance(styleID.data); 122 | } 123 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrSubHeaderTextAppearance)) { 124 | TypedValue styleID = new TypedValue(); 125 | attrs.getValue(R.styleable.PullToRefresh_ptrSubHeaderTextAppearance, styleID); 126 | setSubTextAppearance(styleID.data); 127 | } 128 | 129 | // Text Color attrs need to be set after TextAppearance attrs 130 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrHeaderTextColor)) { 131 | ColorStateList colors = attrs.getColorStateList(R.styleable.PullToRefresh_ptrHeaderTextColor); 132 | if (null != colors) { 133 | setTextColor(colors); 134 | } 135 | } 136 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrHeaderSubTextColor)) { 137 | ColorStateList colors = attrs.getColorStateList(R.styleable.PullToRefresh_ptrHeaderSubTextColor); 138 | if (null != colors) { 139 | setSubTextColor(colors); 140 | } 141 | } 142 | 143 | // Try and get defined drawable from Attrs 144 | Drawable imageDrawable = null; 145 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrDrawable)) { 146 | imageDrawable = attrs.getDrawable(R.styleable.PullToRefresh_ptrDrawable); 147 | } 148 | 149 | // Check Specific Drawable from Attrs, these overrite the generic 150 | // drawable attr above 151 | switch (mode) { 152 | case PULL_FROM_START: 153 | default: 154 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrDrawableStart)) { 155 | imageDrawable = attrs.getDrawable(R.styleable.PullToRefresh_ptrDrawableStart); 156 | } else if (attrs.hasValue(R.styleable.PullToRefresh_ptrDrawableTop)) { 157 | Utils.warnDeprecation("ptrDrawableTop", "ptrDrawableStart"); 158 | imageDrawable = attrs.getDrawable(R.styleable.PullToRefresh_ptrDrawableTop); 159 | } 160 | break; 161 | 162 | case PULL_FROM_END: 163 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrDrawableEnd)) { 164 | imageDrawable = attrs.getDrawable(R.styleable.PullToRefresh_ptrDrawableEnd); 165 | } else if (attrs.hasValue(R.styleable.PullToRefresh_ptrDrawableBottom)) { 166 | Utils.warnDeprecation("ptrDrawableBottom", "ptrDrawableEnd"); 167 | imageDrawable = attrs.getDrawable(R.styleable.PullToRefresh_ptrDrawableBottom); 168 | } 169 | break; 170 | } 171 | 172 | // If we don't have a user defined drawable, load the default 173 | if (null == imageDrawable) { 174 | imageDrawable = context.getResources().getDrawable(getDefaultDrawableResId()); 175 | } 176 | 177 | // Set Drawable, and save width/height 178 | setLoadingDrawable(imageDrawable); 179 | 180 | reset(); 181 | } 182 | 183 | public final void setHeight(int height) { 184 | ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) getLayoutParams(); 185 | lp.height = height; 186 | requestLayout(); 187 | } 188 | 189 | public final void setWidth(int width) { 190 | ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) getLayoutParams(); 191 | lp.width = width; 192 | requestLayout(); 193 | } 194 | 195 | public final int getContentSize() { 196 | switch (mScrollDirection) { 197 | case HORIZONTAL: 198 | return mInnerLayout.getWidth(); 199 | case VERTICAL: 200 | default: 201 | return mInnerLayout.getHeight(); 202 | } 203 | } 204 | 205 | public final void hideAllViews() { 206 | if (View.VISIBLE == mHeaderText.getVisibility()) { 207 | mHeaderText.setVisibility(View.INVISIBLE); 208 | } 209 | if (View.VISIBLE == mHeaderProgress.getVisibility()) { 210 | mHeaderProgress.setVisibility(View.INVISIBLE); 211 | } 212 | if (View.VISIBLE == mHeaderImage.getVisibility()) { 213 | mHeaderImage.setVisibility(View.INVISIBLE); 214 | } 215 | if (View.VISIBLE == mSubHeaderText.getVisibility()) { 216 | mSubHeaderText.setVisibility(View.INVISIBLE); 217 | } 218 | } 219 | 220 | public final void onPull(float scaleOfLayout) { 221 | if (!mUseIntrinsicAnimation) { 222 | onPullImpl(scaleOfLayout); 223 | } 224 | } 225 | 226 | public final void pullToRefresh() { 227 | if (null != mHeaderText) { 228 | mHeaderText.setText(mPullLabel); 229 | } 230 | 231 | // Now call the callback 232 | pullToRefreshImpl(); 233 | } 234 | 235 | public final void refreshing() { 236 | if (null != mHeaderText) { 237 | mHeaderText.setText(mRefreshingLabel); 238 | } 239 | 240 | if (mUseIntrinsicAnimation) { 241 | ((AnimationDrawable) mHeaderImage.getDrawable()).start(); 242 | } else { 243 | // Now call the callback 244 | refreshingImpl(); 245 | } 246 | 247 | if (null != mSubHeaderText) { 248 | mSubHeaderText.setVisibility(View.GONE); 249 | } 250 | } 251 | 252 | public final void releaseToRefresh() { 253 | if (null != mHeaderText) { 254 | mHeaderText.setText(mReleaseLabel); 255 | } 256 | 257 | // Now call the callback 258 | releaseToRefreshImpl(); 259 | } 260 | 261 | public final void reset() { 262 | if (null != mHeaderText) { 263 | mHeaderText.setText(mPullLabel); 264 | } 265 | mHeaderImage.setVisibility(View.VISIBLE); 266 | 267 | if (mUseIntrinsicAnimation) { 268 | ((AnimationDrawable) mHeaderImage.getDrawable()).stop(); 269 | } else { 270 | // Now call the callback 271 | resetImpl(); 272 | } 273 | 274 | if (null != mSubHeaderText) { 275 | if (TextUtils.isEmpty(mSubHeaderText.getText())) { 276 | mSubHeaderText.setVisibility(View.GONE); 277 | } else { 278 | mSubHeaderText.setVisibility(View.VISIBLE); 279 | } 280 | } 281 | } 282 | 283 | @Override 284 | public void setLastUpdatedLabel(CharSequence label) { 285 | setSubHeaderText(label); 286 | } 287 | 288 | public final void setLoadingDrawable(Drawable imageDrawable) { 289 | // Set Drawable 290 | mHeaderImage.setImageDrawable(imageDrawable); 291 | mUseIntrinsicAnimation = (imageDrawable instanceof AnimationDrawable); 292 | 293 | // Now call the callback 294 | onLoadingDrawableSet(imageDrawable); 295 | } 296 | 297 | public void setPullLabel(CharSequence pullLabel) { 298 | mPullLabel = pullLabel; 299 | } 300 | 301 | public void setRefreshingLabel(CharSequence refreshingLabel) { 302 | mRefreshingLabel = refreshingLabel; 303 | } 304 | 305 | public void setReleaseLabel(CharSequence releaseLabel) { 306 | mReleaseLabel = releaseLabel; 307 | } 308 | 309 | @Override 310 | public void setTextTypeface(Typeface tf) { 311 | mHeaderText.setTypeface(tf); 312 | } 313 | 314 | public final void showInvisibleViews() { 315 | if (View.INVISIBLE == mHeaderText.getVisibility()) { 316 | mHeaderText.setVisibility(View.VISIBLE); 317 | } 318 | if (View.INVISIBLE == mHeaderProgress.getVisibility()) { 319 | mHeaderProgress.setVisibility(View.VISIBLE); 320 | } 321 | if (View.INVISIBLE == mHeaderImage.getVisibility()) { 322 | mHeaderImage.setVisibility(View.VISIBLE); 323 | } 324 | if (View.INVISIBLE == mSubHeaderText.getVisibility()) { 325 | mSubHeaderText.setVisibility(View.VISIBLE); 326 | } 327 | } 328 | 329 | /** 330 | * Callbacks for derivative Layouts 331 | */ 332 | 333 | protected abstract int getDefaultDrawableResId(); 334 | 335 | protected abstract void onLoadingDrawableSet(Drawable imageDrawable); 336 | 337 | protected abstract void onPullImpl(float scaleOfLayout); 338 | 339 | protected abstract void pullToRefreshImpl(); 340 | 341 | protected abstract void refreshingImpl(); 342 | 343 | protected abstract void releaseToRefreshImpl(); 344 | 345 | protected abstract void resetImpl(); 346 | 347 | private void setSubHeaderText(CharSequence label) { 348 | if (null != mSubHeaderText) { 349 | if (TextUtils.isEmpty(label)) { 350 | mSubHeaderText.setVisibility(View.GONE); 351 | } else { 352 | mSubHeaderText.setText(label); 353 | 354 | // Only set it to Visible if we're GONE, otherwise VISIBLE will 355 | // be set soon 356 | if (View.GONE == mSubHeaderText.getVisibility()) { 357 | mSubHeaderText.setVisibility(View.VISIBLE); 358 | } 359 | } 360 | } 361 | } 362 | 363 | private void setSubTextAppearance(int value) { 364 | if (null != mSubHeaderText) { 365 | mSubHeaderText.setTextAppearance(getContext(), value); 366 | } 367 | } 368 | 369 | private void setSubTextColor(ColorStateList color) { 370 | if (null != mSubHeaderText) { 371 | mSubHeaderText.setTextColor(color); 372 | } 373 | } 374 | 375 | private void setTextAppearance(int value) { 376 | if (null != mHeaderText) { 377 | mHeaderText.setTextAppearance(getContext(), value); 378 | } 379 | if (null != mSubHeaderText) { 380 | mSubHeaderText.setTextAppearance(getContext(), value); 381 | } 382 | } 383 | 384 | private void setTextColor(ColorStateList color) { 385 | if (null != mHeaderText) { 386 | mHeaderText.setTextColor(color); 387 | } 388 | if (null != mSubHeaderText) { 389 | mSubHeaderText.setTextColor(color); 390 | } 391 | } 392 | 393 | } 394 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib.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.view.animation.Animation; 23 | import android.view.animation.RotateAnimation; 24 | import android.widget.ImageView.ScaleType; 25 | 26 | import com.itheima.pulltorefreshlib.PullToRefreshBase; 27 | import com.itheima.pulltorefreshlib.R; 28 | 29 | 30 | public class RotateLoadingLayout extends LoadingLayout { 31 | 32 | static final int ROTATION_ANIMATION_DURATION = 1200; 33 | 34 | private final Animation mRotateAnimation; 35 | private final Matrix mHeaderImageMatrix; 36 | 37 | private float mRotationPivotX, mRotationPivotY; 38 | 39 | private final boolean mRotateDrawableWhilePulling; 40 | 41 | public RotateLoadingLayout(Context context, PullToRefreshBase.Mode mode, PullToRefreshBase.Orientation scrollDirection, TypedArray attrs) { 42 | super(context, mode, scrollDirection, attrs); 43 | 44 | mRotateDrawableWhilePulling = attrs.getBoolean(R.styleable.PullToRefresh_ptrRotateDrawableWhilePulling, true); 45 | 46 | mHeaderImage.setScaleType(ScaleType.MATRIX); 47 | mHeaderImageMatrix = new Matrix(); 48 | mHeaderImage.setImageMatrix(mHeaderImageMatrix); 49 | 50 | mRotateAnimation = new RotateAnimation(0, 720, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 51 | 0.5f); 52 | mRotateAnimation.setInterpolator(ANIMATION_INTERPOLATOR); 53 | mRotateAnimation.setDuration(ROTATION_ANIMATION_DURATION); 54 | mRotateAnimation.setRepeatCount(Animation.INFINITE); 55 | mRotateAnimation.setRepeatMode(Animation.RESTART); 56 | } 57 | 58 | public void onLoadingDrawableSet(Drawable imageDrawable) { 59 | if (null != imageDrawable) { 60 | mRotationPivotX = Math.round(imageDrawable.getIntrinsicWidth() / 2f); 61 | mRotationPivotY = Math.round(imageDrawable.getIntrinsicHeight() / 2f); 62 | } 63 | } 64 | 65 | protected void onPullImpl(float scaleOfLayout) { 66 | float angle; 67 | if (mRotateDrawableWhilePulling) { 68 | angle = scaleOfLayout * 90f; 69 | } else { 70 | angle = Math.max(0f, Math.min(180f, scaleOfLayout * 360f - 180f)); 71 | } 72 | 73 | mHeaderImageMatrix.setRotate(angle, mRotationPivotX, mRotationPivotY); 74 | mHeaderImage.setImageMatrix(mHeaderImageMatrix); 75 | } 76 | 77 | @Override 78 | protected void refreshingImpl() { 79 | mHeaderImage.startAnimation(mRotateAnimation); 80 | } 81 | 82 | @Override 83 | protected void resetImpl() { 84 | mHeaderImage.clearAnimation(); 85 | resetImageRotation(); 86 | } 87 | 88 | private void resetImageRotation() { 89 | if (null != mHeaderImageMatrix) { 90 | mHeaderImageMatrix.reset(); 91 | mHeaderImage.setImageMatrix(mHeaderImageMatrix); 92 | } 93 | } 94 | 95 | @Override 96 | protected void pullToRefreshImpl() { 97 | // NO-OP 98 | } 99 | 100 | @Override 101 | protected void releaseToRefreshImpl() { 102 | // NO-OP 103 | } 104 | 105 | @Override 106 | protected int getDefaultDrawableResId() { 107 | return R.drawable.default_ptr_rotate; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/internal/Utils.java: -------------------------------------------------------------------------------- 1 | package com.itheima.pulltorefreshlib.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 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/java/com/itheima/pulltorefreshlib/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.itheima.pulltorefreshlib.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 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/anim/slide_in_from_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 22 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/anim/slide_in_from_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 22 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/anim/slide_out_to_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 22 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/anim/slide_out_to_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 22 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/drawable-hdpi/default_ptr_flip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-android/PullToRefresh/ffbcc1572ea38f63d63ed20ecf8efc60c87aa567/pulltorefreshlib/src/main/res/drawable-hdpi/default_ptr_flip.png -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/drawable-hdpi/default_ptr_rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-android/PullToRefresh/ffbcc1572ea38f63d63ed20ecf8efc60c87aa567/pulltorefreshlib/src/main/res/drawable-hdpi/default_ptr_rotate.png -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/drawable-hdpi/indicator_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-android/PullToRefresh/ffbcc1572ea38f63d63ed20ecf8efc60c87aa567/pulltorefreshlib/src/main/res/drawable-hdpi/indicator_arrow.png -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/drawable-mdpi/default_ptr_flip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-android/PullToRefresh/ffbcc1572ea38f63d63ed20ecf8efc60c87aa567/pulltorefreshlib/src/main/res/drawable-mdpi/default_ptr_flip.png -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/drawable-mdpi/default_ptr_rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-android/PullToRefresh/ffbcc1572ea38f63d63ed20ecf8efc60c87aa567/pulltorefreshlib/src/main/res/drawable-mdpi/default_ptr_rotate.png -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/drawable-mdpi/indicator_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-android/PullToRefresh/ffbcc1572ea38f63d63ed20ecf8efc60c87aa567/pulltorefreshlib/src/main/res/drawable-mdpi/indicator_arrow.png -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/drawable-xhdpi/default_ptr_flip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-android/PullToRefresh/ffbcc1572ea38f63d63ed20ecf8efc60c87aa567/pulltorefreshlib/src/main/res/drawable-xhdpi/default_ptr_flip.png -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/drawable-xhdpi/default_ptr_rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-android/PullToRefresh/ffbcc1572ea38f63d63ed20ecf8efc60c87aa567/pulltorefreshlib/src/main/res/drawable-xhdpi/default_ptr_rotate.png -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/drawable-xhdpi/indicator_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-android/PullToRefresh/ffbcc1572ea38f63d63ed20ecf8efc60c87aa567/pulltorefreshlib/src/main/res/drawable-xhdpi/indicator_arrow.png -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/drawable/indicator_bg_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/drawable/indicator_bg_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/layout/pull_to_refresh_header_horizontal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 18 | 19 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/layout/pull_to_refresh_header_vertical.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 17 | 18 | 23 | 24 | 32 | 33 | 34 | 40 | 41 | 48 | 49 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/values-ar/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | اسحب للتحديث… 4 | اترك للتحديث… 5 | تحميل… 6 | 7 | -------------------------------------------------------------------------------- /pulltorefreshlib/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 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/values-de/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Ziehen zum Aktualisieren… 4 | Loslassen zum Aktualisieren… 5 | Laden… 6 | 7 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/values-es/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tirar para actualizar… 4 | Soltar para actualizar… 5 | Cargando… 6 | 7 | -------------------------------------------------------------------------------- /pulltorefreshlib/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 | -------------------------------------------------------------------------------- /pulltorefreshlib/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 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/values-he/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | משוך לרענון… 4 | שחרר לרענון… 5 | טוען… 6 | 7 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/values-it/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tira per aggiornare… 4 | Rilascia per aggionare… 5 | Caricamento… 6 | 7 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/values-iw/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | משוך לרענון… 4 | שחרר לרענון… 5 | טוען… 6 | 7 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/values-ja/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 画面を引っ張って… 4 | 指を離して更新… 5 | 読み込み中… 6 | 7 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/values-ko/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 당겨서 새로 고침… 4 | 놓아서 새로 고침… 5 | 로드 중… 6 | 7 | -------------------------------------------------------------------------------- /pulltorefreshlib/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 | -------------------------------------------------------------------------------- /pulltorefreshlib/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 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/values-pt-rBR/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Puxe para atualizar… 4 | Libere para atualizar… 5 | Carregando… 6 | 7 | -------------------------------------------------------------------------------- /pulltorefreshlib/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 | -------------------------------------------------------------------------------- /pulltorefreshlib/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 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/values-ru/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Потяните для обновления… 4 | Отпустите для обновления… 5 | Загрузка… 6 | 7 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/values-zh/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 下拉刷新… 4 | 放开以刷新… 5 | 正在载入… 6 | 7 | 上拉加载更多… 8 | 放开以加载… 9 | 正在加载… 10 | 11 | 12 | -------------------------------------------------------------------------------- /pulltorefreshlib/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 | 66 | 67 | 68 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10dp 5 | 12dp 6 | 4dp 7 | 24dp 8 | 12dp 9 | 10 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /pulltorefreshlib/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 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | PullToRefreshLib 3 | 4 | -------------------------------------------------------------------------------- /pulltorefreshlib/src/test/java/com/itheima/pulltorefreshlib/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.itheima.pulltorefreshlib; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':pulltorefreshlib' 2 | --------------------------------------------------------------------------------