The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .gitignore
├── .idea
    ├── .name
    ├── compiler.xml
    ├── copyright
    │   └── profiles_settings.xml
    ├── gradle.xml
    ├── misc.xml
    ├── modules.xml
    ├── runConfigurations.xml
    └── vcs.xml
├── FlyRefresh.iml
├── LICENSE
├── README.md
├── app
    ├── .gitignore
    ├── app.iml
    ├── build.gradle
    ├── proguard-rules.pro
    └── src
    │   ├── androidTest
    │       └── java
    │       │   └── com
    │       │       └── race604
    │       │           └── flyrefresh
    │       │               └── ApplicationTest.java
    │   └── main
    │       ├── AndroidManifest.xml
    │       ├── java
    │           └── com
    │           │   └── race604
    │           │       └── flyrefresh
    │           │           └── sample
    │           │               ├── ItemData.java
    │           │               ├── MainActivity.java
    │           │               └── SampleItemAnimator.java
    │       └── res
    │           ├── layout
    │               ├── activity_main.xml
    │               └── view_list_item.xml
    │           ├── menu
    │               └── menu_main.xml
    │           ├── mipmap-hdpi
    │               └── ic_launcher.png
    │           ├── mipmap-mdpi
    │               └── ic_launcher.png
    │           ├── mipmap-xhdpi
    │               └── ic_launcher.png
    │           ├── mipmap-xxhdpi
    │               ├── ic_assessment_white_24dp.png
    │               ├── ic_folder_white_24dp.png
    │               ├── ic_info_grey600_18dp.png
    │               ├── ic_launcher.png
    │               ├── ic_search_white_24dp.png
    │               └── ic_smartphone_white_24dp.png
    │           ├── values-w820dp
    │               └── dimens.xml
    │           └── values
    │               ├── dimens.xml
    │               ├── strings.xml
    │               └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
    └── wrapper
    │   ├── gradle-wrapper.jar
    │   └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
    ├── designed.gif
    └── flyrefresh.gif
├── library
    ├── .gitignore
    ├── build.gradle
    ├── library.iml
    ├── proguard-rules.pro
    └── src
    │   ├── androidTest
    │       └── java
    │       │   └── com
    │       │       └── race604
    │       │           └── flyrefresh
    │       │               └── ApplicationTest.java
    │   └── main
    │       ├── AndroidManifest.xml
    │       ├── java
    │           └── com
    │           │   └── race604
    │           │       ├── flyrefresh
    │           │           ├── FlyRefreshLayout.java
    │           │           ├── HeaderController.java
    │           │           ├── IPullHeader.java
    │           │           ├── PullHeaderLayout.java
    │           │           └── internal
    │           │           │   ├── ElasticOutInterpolator.java
    │           │           │   ├── MountainSceneDrawable.java
    │           │           │   ├── MountanScenceView.java
    │           │           │   ├── RotatableDrawable.java
    │           │           │   ├── SimpleAnimatorListener.java
    │           │           │   └── TreeDrawable.java
    │           │       └── utils
    │           │           └── UIUtils.java
    │       └── res
    │           ├── mipmap-xxhdpi
    │               └── ic_send.png
    │           └── values
    │               ├── attrs.xml
    │               ├── colors.xml
    │               └── strings.xml
└── settings.gradle


/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea/workspace.xml
4 | /.idea/libraries
5 | .DS_Store
6 | /build
7 | /captures
8 | 


--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | FlyRefresh


--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="UTF-8"?>
 2 | <project version="4">
 3 |   <component name="CompilerConfiguration">
 4 |     <resourceExtensions />
 5 |     <wildcardResourcePatterns>
 6 |       <entry name="!?*.java" />
 7 |       <entry name="!?*.form" />
 8 |       <entry name="!?*.class" />
 9 |       <entry name="!?*.groovy" />
10 |       <entry name="!?*.scala" />
11 |       <entry name="!?*.flex" />
12 |       <entry name="!?*.kt" />
13 |       <entry name="!?*.clj" />
14 |       <entry name="!?*.aj" />
15 |     </wildcardResourcePatterns>
16 |     <annotationProcessing>
17 |       <profile default="true" name="Default" enabled="false">
18 |         <processorPath useClasspath="true" />
19 |       </profile>
20 |     </annotationProcessing>
21 |   </component>
22 | </project>


--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 | <component name="CopyrightManager">
2 |   <settings default="" />
3 | </component>


--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="UTF-8"?>
 2 | <project version="4">
 3 |   <component name="GradleSettings">
 4 |     <option name="linkedExternalProjectsSettings">
 5 |       <GradleProjectSettings>
 6 |         <option name="distributionType" value="DEFAULT_WRAPPED" />
 7 |         <option name="externalProjectPath" value="$PROJECT_DIR
quot; />
 8 |         <option name="gradleJvm" value="1.7" />
 9 |         <option name="modules">
10 |           <set>
11 |             <option value="$PROJECT_DIR
quot; />
12 |             <option value="$PROJECT_DIR$/app" />
13 |             <option value="$PROJECT_DIR$/library" />
14 |           </set>
15 |         </option>
16 |       </GradleProjectSettings>
17 |     </option>
18 |   </component>
19 | </project>


--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="UTF-8"?>
 2 | <project version="4">
 3 |   <component name="EntryPointsManager">
 4 |     <entry_points version="2.0" />
 5 |   </component>
 6 |   <component name="NullableNotNullManager">
 7 |     <option name="myDefaultNullable" value="android.support.annotation.Nullable" />
 8 |     <option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
 9 |     <option name="myNullables">
10 |       <value>
11 |         <list size="4">
12 |           <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
13 |           <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
14 |           <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
15 |           <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
16 |         </list>
17 |       </value>
18 |     </option>
19 |     <option name="myNotNulls">
20 |       <value>
21 |         <list size="4">
22 |           <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
23 |           <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
24 |           <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
25 |           <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
26 |         </list>
27 |       </value>
28 |     </option>
29 |   </component>
30 |   <component name="ProjectLevelVcsManager" settingsEditedManually="false">
31 |     <OptionsSetting value="true" id="Add" />
32 |     <OptionsSetting value="true" id="Remove" />
33 |     <OptionsSetting value="true" id="Checkout" />
34 |     <OptionsSetting value="true" id="Update" />
35 |     <OptionsSetting value="true" id="Status" />
36 |     <OptionsSetting value="true" id="Edit" />
37 |     <ConfirmationsSetting value="0" id="Add" />
38 |     <ConfirmationsSetting value="0" id="Remove" />
39 |   </component>
40 |   <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK">
41 |     <output url="file://$PROJECT_DIR$/build/classes" />
42 |   </component>
43 |   <component name="ProjectType">
44 |     <option name="id" value="Android" />
45 |   </component>
46 |   <component name="masterDetails">
47 |     <states>
48 |       <state key="ProjectJDKs.UI">
49 |         <settings>
50 |           <last-edited>1.7</last-edited>
51 |           <splitter-proportions>
52 |             <option name="proportions">
53 |               <list>
54 |                 <option value="0.2" />
55 |               </list>
56 |             </option>
57 |           </splitter-proportions>
58 |         </settings>
59 |       </state>
60 |     </states>
61 |   </component>
62 | </project>


--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="UTF-8"?>
 2 | <project version="4">
 3 |   <component name="ProjectModuleManager">
 4 |     <modules>
 5 |       <module fileurl="file://$PROJECT_DIR$/FlyRefresh.iml" filepath="$PROJECT_DIR$/FlyRefresh.iml" />
 6 |       <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
 7 |       <module fileurl="file://$PROJECT_DIR$/library/library.iml" filepath="$PROJECT_DIR$/library/library.iml" />
 8 |     </modules>
 9 |   </component>
10 | </project>


--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="UTF-8"?>
 2 | <project version="4">
 3 |   <component name="RunConfigurationProducerService">
 4 |     <option name="ignoredProducers">
 5 |       <set>
 6 |         <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
 7 |         <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
 8 |         <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
 9 |       </set>
10 |     </option>
11 |   </component>
12 | </project>


--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="UTF-8"?>
2 | <project version="4">
3 |   <component name="VcsDirectoryMappings">
4 |     <mapping directory="$PROJECT_DIR
quot; vcs="Git" />
5 |   </component>
6 | </project>


--------------------------------------------------------------------------------
/FlyRefresh.iml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="UTF-8"?>
 2 | <module external.linked.project.id="FlyRefresh" external.linked.project.path="$MODULE_DIR
quot; external.root.project.path="$MODULE_DIR
quot; external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
 3 |   <component name="FacetManager">
 4 |     <facet type="java-gradle" name="Java-Gradle">
 5 |       <configuration>
 6 |         <option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
 7 |         <option name="BUILDABLE" value="false" />
 8 |       </configuration>
 9 |     </facet>
10 |   </component>
11 |   <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
12 |     <exclude-output />
13 |     <content url="file://$MODULE_DIR
quot;>
14 |       <excludeFolder url="file://$MODULE_DIR$/.gradle" />
15 |     </content>
16 |     <orderEntry type="inheritedJdk" />
17 |     <orderEntry type="sourceFolder" forTests="false" />
18 |   </component>
19 | </module>


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2015 race604
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 
23 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | # FlyRefresh
 2 | The Android implementation of [Replace](https://dribbble.com/shots/2067564-Replace), designed by [Zee Youn](https://dribbble.com/zeeyoung).
 3 | I implement this as a **FlyRefresh** layout. The content of the layout can be any `NestedScrollingChild`, such as a RecyclerView, NestedScrollView, VerticalGridView, etc.
 4 | This library can also work with `NestedScrollingParent` as parent, such as CoordinatorLayout.
 5 | 
 6 | # How it looks
 7 | ![flyrefresh](./images/flyrefresh.gif)
 8 | 
 9 | # Features
10 | * Work with all [NestedScrollingParent](https://developer.android.com/reference/android/support/v4/view/NestedScrollingParent.html) and [NestedScrollingChild](https://developer.android.com/reference/android/support/v4/view/NestedScrollingChild.html)
11 | * Default minimize configuration for [Replace](https://dribbble.com/shots/2067564-Replace) animation
12 | * Expendable/Shrinkable header
13 | * Support custom header view
14 | * Support custom refresh animation
15 | 
16 | # How to use
17 | 
18 | Add Gradle dependency:
19 | 
20 | ```gradle
21 | dependencies {
22 |    compile 'com.race604.flyrefresh:library:2.0.0'
23 | }
24 | ```
25 | 
26 | An example of basic usage in `layout.xml`:
27 | 
28 | ```xml
29 | <com.race604.flyrefresh.FlyRefreshLayout
30 |   android:id="@+id/fly_layout"
31 |   android:layout_width="match_parent"
32 |   android:layout_height="match_parent">
33 | 
34 |     <android.support.v7.widget.RecyclerView
35 |       android:id="@+id/list"
36 |       android:layout_width="match_parent"
37 |       android:layout_height="match_parent"
38 |       android:paddingTop="24dp"
39 |       android:background="#FFFFFF"/>
40 | </com.race604.flyrefresh.FlyRefreshLayout>
41 | ```
42 | 
43 | Or you can use `PullHeaderLayout` for more configurations, you can set custom attributes as shown below:
44 | 
45 | ```xml
46 | <declare-styleable name="PullHeaderLayout">
47 |     <!-- hader size -->
48 |     <attr name="phl_header_height" format="dimension" />
49 |     <attr name="phl_header_expand_height" format="dimension" />
50 |     <attr name="phl_header_shrink_height" format="dimension" />
51 |     <!-- header view id -->
52 |     <attr name="phl_header" format="reference" />
53 |     <!-- content view id -->
54 |     <attr name="phl_content" format="reference" />
55 |     <!-- Float action button icon -->
56 |     <attr name="phl_action" format="reference" />
57 | </declare-styleable>
58 | ```
59 | For more, please turn to the source code.
60 | 
61 | # License
62 | `FlyRefresh` is available under the MIT license.
63 | 


--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | 


--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
  1 | <?xml version="1.0" encoding="UTF-8"?>
  2 | <module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR
quot; external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="FlyRefresh" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
  3 |   <component name="FacetManager">
  4 |     <facet type="android-gradle" name="Android-Gradle">
  5 |       <configuration>
  6 |         <option name="GRADLE_PROJECT_PATH" value=":app" />
  7 |       </configuration>
  8 |     </facet>
  9 |     <facet type="android" name="Android">
 10 |       <configuration>
 11 |         <option name="SELECTED_BUILD_VARIANT" value="debug" />
 12 |         <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
 13 |         <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
 14 |         <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
 15 |         <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
 16 |         <option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
 17 |         <afterSyncTasks>
 18 |           <task>generateDebugAndroidTestSources</task>
 19 |           <task>generateDebugSources</task>
 20 |         </afterSyncTasks>
 21 |         <option name="ALLOW_USER_CONFIGURATION" value="false" />
 22 |         <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
 23 |         <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
 24 |         <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
 25 |         <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
 26 |       </configuration>
 27 |     </facet>
 28 |   </component>
 29 |   <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
 30 |     <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
 31 |     <output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
 32 |     <exclude-output />
 33 |     <content url="file://$MODULE_DIR
quot;>
 34 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
 35 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
 36 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
 37 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
 38 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
 39 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" />
 40 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
 41 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
 42 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
 43 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
 44 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
 45 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" />
 46 |       <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
 47 |       <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
 48 |       <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
 49 |       <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
 50 |       <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
 51 |       <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
 52 |       <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
 53 |       <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
 54 |       <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
 55 |       <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
 56 |       <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
 57 |       <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
 58 |       <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
 59 |       <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
 60 |       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
 61 |       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
 62 |       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
 63 |       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
 64 |       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
 65 |       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
 66 |       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
 67 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
 68 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
 69 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
 70 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
 71 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
 72 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
 73 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
 74 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.0.0/jars" />
 75 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/23.0.0/jars" />
 76 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.0.0/jars" />
 77 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/jp.wasabeef/recyclerview-animators/1.2.0/jars" />
 78 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
 79 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
 80 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
 81 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
 82 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
 83 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
 84 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
 85 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
 86 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
 87 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
 88 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
 89 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
 90 |       <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
 91 |       <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
 92 |     </content>
 93 |     <orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
 94 |     <orderEntry type="sourceFolder" forTests="false" />
 95 |     <orderEntry type="library" exported="" name="appcompat-v7-23.0.0" level="project" />
 96 |     <orderEntry type="library" exported="" name="recyclerview-v7-23.0.0" level="project" />
 97 |     <orderEntry type="library" exported="" name="recyclerview-animators-1.2.0" level="project" />
 98 |     <orderEntry type="library" exported="" name="support-v4-23.0.0" level="project" />
 99 |     <orderEntry type="library" exported="" name="support-annotations-23.0.0" level="project" />
100 |     <orderEntry type="module" module-name="library" exported="" />
101 |   </component>
102 | </module>


--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
 1 | apply plugin: 'com.android.application'
 2 | 
 3 | android {
 4 |     compileSdkVersion 23
 5 |     buildToolsVersion "22.0.1"
 6 | 
 7 |     defaultConfig {
 8 |         applicationId "com.race604.flyrefresh"
 9 |         minSdkVersion 11
10 |         targetSdkVersion 23
11 |         versionCode 1
12 |         versionName "1.0"
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 |     compile 'com.android.support:appcompat-v7:23.0.0'
25 |     compile project(':library')
26 |     compile 'com.android.support:recyclerview-v7:23.0.0'
27 |     compile 'jp.wasabeef:recyclerview-animators:1.2.0@aar'
28 | }
29 | 


--------------------------------------------------------------------------------
/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 /home/jing/Tools/android-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/race604/flyrefresh/ApplicationTest.java:
--------------------------------------------------------------------------------
 1 | package com.race604.flyrefresh;
 2 | 
 3 | import android.app.Application;
 4 | import android.test.ApplicationTestCase;
 5 | 
 6 | /**
 7 |  * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
 8 |  */
 9 | public class ApplicationTest extends ApplicationTestCase<Application> {
10 |     public ApplicationTest() {
11 |         super(Application.class);
12 |     }
13 | }


--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 3 |     package="com.race604.flyrefresh.sample" >
 4 | 
 5 |     <application
 6 |         android:allowBackup="true"
 7 |         android:icon="@mipmap/ic_launcher"
 8 |         android:label="@string/app_name"
 9 |         android:theme="@style/AppTheme" >
10 |         <activity
11 |             android:name=".MainActivity"
12 |             android:label="@string/app_name" >
13 |             <intent-filter>
14 |                 <action android:name="android.intent.action.MAIN" />
15 | 
16 |                 <category android:name="android.intent.category.LAUNCHER" />
17 |             </intent-filter>
18 |         </activity>
19 |     </application>
20 | 
21 | </manifest>
22 | 


--------------------------------------------------------------------------------
/app/src/main/java/com/race604/flyrefresh/sample/ItemData.java:
--------------------------------------------------------------------------------
 1 | package com.race604.flyrefresh.sample;
 2 | 
 3 | import android.graphics.Color;
 4 | 
 5 | import java.util.Date;
 6 | 
 7 | /**
 8 |  * Created by Jing on 15/5/27.
 9 |  */
10 | public class ItemData {
11 |     int color;
12 |     public int icon;
13 |     public String title;
14 |     public Date time;
15 | 
16 |     public ItemData(int color, int icon, String title, Date time) {
17 |         this.color = color;
18 |         this.icon = icon;
19 |         this.title = title;
20 |         this.time = time;
21 |     }
22 | 
23 |     public ItemData(int icon, String title) {
24 |         this(Color.DKGRAY, icon, title, new Date());
25 |     }
26 | }
27 | 


--------------------------------------------------------------------------------
/app/src/main/java/com/race604/flyrefresh/sample/MainActivity.java:
--------------------------------------------------------------------------------
  1 | package com.race604.flyrefresh.sample;
  2 | 
  3 | import android.animation.Animator;
  4 | import android.animation.AnimatorSet;
  5 | import android.animation.ObjectAnimator;
  6 | import android.content.Context;
  7 | import android.graphics.Color;
  8 | import android.graphics.drawable.ShapeDrawable;
  9 | import android.graphics.drawable.shapes.OvalShape;
 10 | import android.os.Bundle;
 11 | import android.os.Handler;
 12 | import android.support.v7.app.AppCompatActivity;
 13 | import android.support.v7.widget.LinearLayoutManager;
 14 | import android.support.v7.widget.RecyclerView;
 15 | import android.support.v7.widget.Toolbar;
 16 | import android.view.LayoutInflater;
 17 | import android.view.Menu;
 18 | import android.view.MenuItem;
 19 | import android.view.View;
 20 | import android.view.ViewGroup;
 21 | import android.view.animation.AccelerateInterpolator;
 22 | import android.widget.ImageView;
 23 | import android.widget.TextView;
 24 | 
 25 | import com.race604.flyrefresh.FlyRefreshLayout;
 26 | 
 27 | import java.text.DateFormat;
 28 | import java.text.SimpleDateFormat;
 29 | import java.util.ArrayList;
 30 | import java.util.Date;
 31 | import java.util.Locale;
 32 | 
 33 | public class MainActivity extends AppCompatActivity implements FlyRefreshLayout.OnPullRefreshListener {
 34 | 
 35 |     private FlyRefreshLayout mFlylayout;
 36 |     private RecyclerView mListView;
 37 | 
 38 |     private ItemAdapter mAdapter;
 39 | 
 40 |     private ArrayList<ItemData> mDataSet = new ArrayList<>();
 41 |     private Handler mHandler = new Handler();
 42 |     private LinearLayoutManager mLayoutManager;
 43 | 
 44 |     @Override
 45 |     protected void onCreate(Bundle savedInstanceState) {
 46 |         super.onCreate(savedInstanceState);
 47 |         initDataSet();
 48 |         setContentView(R.layout.activity_main);
 49 | 
 50 |         Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
 51 |         setSupportActionBar(toolbar);
 52 |         getSupportActionBar().setDisplayShowTitleEnabled(false);
 53 | 
 54 |         mFlylayout = (FlyRefreshLayout) findViewById(R.id.fly_layout);
 55 | 
 56 |         mFlylayout.setOnPullRefreshListener(this);
 57 | 
 58 |         mListView = (RecyclerView) findViewById(R.id.list);
 59 | 
 60 |         mLayoutManager = new LinearLayoutManager(this);
 61 |         mListView.setLayoutManager(mLayoutManager);
 62 |         mAdapter = new ItemAdapter(this);
 63 | 
 64 |         mListView.setAdapter(mAdapter);
 65 | 
 66 |         mListView.setItemAnimator(new SampleItemAnimator());
 67 | 
 68 |         View actionButton = mFlylayout.getHeaderActionButton();
 69 |         if (actionButton != null) {
 70 |             actionButton.setOnClickListener(new View.OnClickListener() {
 71 |                 @Override
 72 |                 public void onClick(View v) {
 73 |                     mFlylayout.startRefresh();
 74 |                 }
 75 |             });
 76 |         }
 77 |     }
 78 | 
 79 |     private void initDataSet() {
 80 |         mDataSet.add(new ItemData(Color.parseColor("#76A9FC"), R.mipmap.ic_assessment_white_24dp, "Meeting Minutes", new Date(2014 - 1900, 2, 9)));
 81 |         mDataSet.add(new ItemData(Color.GRAY, R.mipmap.ic_folder_white_24dp, "Favorites Photos", new Date(2014 - 1900, 1, 3)));
 82 |         mDataSet.add(new ItemData(Color.GRAY, R.mipmap.ic_folder_white_24dp, "Photos", new Date(2014 - 1900, 0, 9)));
 83 |     }
 84 | 
 85 |     private void addItemData() {
 86 |         ItemData itemData = new ItemData(Color.parseColor("#FFC970"), R.mipmap.ic_smartphone_white_24dp, "Magic Cube Show", new Date());
 87 |         mDataSet.add(0, itemData);
 88 |         mAdapter.notifyItemInserted(0);
 89 |         mLayoutManager.scrollToPosition(0);
 90 |     }
 91 | 
 92 |     @Override
 93 |     public boolean onCreateOptionsMenu(Menu menu) {
 94 |         getMenuInflater().inflate(R.menu.menu_main, menu);
 95 |         return true;
 96 |     }
 97 | 
 98 |     @Override
 99 |     public boolean onOptionsItemSelected(MenuItem item) {
100 |         int id = item.getItemId();
101 | 
102 |         if (id == R.id.action_settings) {
103 |             return true;
104 |         }
105 | 
106 |         return super.onOptionsItemSelected(item);
107 |     }
108 | 
109 |     @Override
110 |     public void onRefresh(FlyRefreshLayout view) {
111 |         View child = mListView.getChildAt(0);
112 |         if (child != null) {
113 |             bounceAnimateView(child.findViewById(R.id.icon));
114 |         }
115 | 
116 |         mHandler.postDelayed(new Runnable() {
117 |             @Override
118 |             public void run() {
119 |                 mFlylayout.onRefreshFinish();
120 |             }
121 |         }, 2000);
122 |     }
123 | 
124 |     private void bounceAnimateView(View view) {
125 |         if (view == null) {
126 |             return;
127 |         }
128 | 
129 |         Animator swing = ObjectAnimator.ofFloat(view, "rotationX", 0, 30, -20, 0);
130 |         swing.setDuration(400);
131 |         swing.setInterpolator(new AccelerateInterpolator());
132 |         swing.start();
133 |     }
134 | 
135 |     @Override
136 |     public void onRefreshAnimationEnd(FlyRefreshLayout view) {
137 |         addItemData();
138 |     }
139 | 
140 |     private class ItemAdapter extends RecyclerView.Adapter<ItemViewHolder> {
141 | 
142 |         private LayoutInflater mInflater;
143 |         private DateFormat dateFormat;
144 | 
145 |         public ItemAdapter(Context context) {
146 |             mInflater = LayoutInflater.from(context);
147 |             dateFormat = SimpleDateFormat.getDateInstance(DateFormat.DEFAULT, Locale.ENGLISH);
148 |         }
149 | 
150 |         @Override
151 |         public ItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
152 |             View view = mInflater.inflate(R.layout.view_list_item, viewGroup, false);
153 |             return new ItemViewHolder(view);
154 |         }
155 | 
156 |         @Override
157 |         public void onBindViewHolder(ItemViewHolder itemViewHolder, int i) {
158 |             final ItemData data = mDataSet.get(i);
159 |             ShapeDrawable drawable = new ShapeDrawable(new OvalShape());
160 |             drawable.getPaint().setColor(data.color);
161 |             itemViewHolder.icon.setBackgroundDrawable(drawable);
162 |             itemViewHolder.icon.setImageResource(data.icon);
163 |             itemViewHolder.title.setText(data.title);
164 |             itemViewHolder.subTitle.setText(dateFormat.format(data.time));
165 |         }
166 | 
167 |         @Override
168 |         public int getItemCount() {
169 |             return mDataSet.size();
170 |         }
171 |     }
172 | 
173 |     private static class ItemViewHolder extends RecyclerView.ViewHolder {
174 | 
175 |         ImageView icon;
176 |         TextView title;
177 |         TextView subTitle;
178 | 
179 |         public ItemViewHolder(View itemView) {
180 |             super(itemView);
181 |             icon = (ImageView) itemView.findViewById(R.id.icon);
182 |             title = (TextView) itemView.findViewById(R.id.title);
183 |             subTitle = (TextView) itemView.findViewById(R.id.subtitle);
184 |         }
185 | 
186 |     }
187 | }
188 | 


--------------------------------------------------------------------------------
/app/src/main/java/com/race604/flyrefresh/sample/SampleItemAnimator.java:
--------------------------------------------------------------------------------
 1 | package com.race604.flyrefresh.sample;
 2 | 
 3 | import android.animation.Animator;
 4 | import android.animation.AnimatorSet;
 5 | import android.animation.ObjectAnimator;
 6 | import android.support.v4.view.ViewCompat;
 7 | import android.support.v7.widget.RecyclerView;
 8 | import android.view.View;
 9 | import android.view.animation.DecelerateInterpolator;
10 | import android.view.animation.OvershootInterpolator;
11 | 
12 | import jp.wasabeef.recyclerview.animators.BaseItemAnimator;
13 | 
14 | /**
15 |  * Created by jing on 15-5-28.
16 |  */
17 | public class SampleItemAnimator extends BaseItemAnimator {
18 | 
19 |     @Override
20 |     protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) {
21 |         View icon = holder.itemView.findViewById(R.id.icon);
22 |         icon.setRotationX(30);
23 |         View right = holder.itemView.findViewById(R.id.right);
24 |         right.setPivotX(0);
25 |         right.setPivotY(0);
26 |         right.setRotationY(90);
27 |     }
28 | 
29 |     @Override
30 |     protected void animateRemoveImpl(RecyclerView.ViewHolder viewHolder) {
31 |     }
32 | 
33 |     @Override
34 |     protected void animateAddImpl(final RecyclerView.ViewHolder holder) {
35 |         View target = holder.itemView;
36 |         View icon = target.findViewById(R.id.icon);
37 |         Animator swing = ObjectAnimator.ofFloat(icon, "rotationX", 45, 0);
38 |         swing.setInterpolator(new OvershootInterpolator(5));
39 | 
40 |         View right = holder.itemView.findViewById(R.id.right);
41 |         Animator rotateIn = ObjectAnimator.ofFloat(right, "rotationY", 90, 0);
42 |         rotateIn.setInterpolator(new DecelerateInterpolator());
43 | 
44 |         AnimatorSet animator = new AnimatorSet();
45 |         animator.setDuration(getAddDuration());
46 |         animator.playTogether(swing, rotateIn);
47 | 
48 |         animator.start();
49 |     }
50 | 
51 | }
52 | 


--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
 1 | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     xmlns:app="http://schemas.android.com/apk/res-auto"
 3 |     xmlns:tools="http://schemas.android.com/tools"
 4 |     android:layout_width="match_parent"
 5 |     android:layout_height="match_parent">
 6 |     <com.race604.flyrefresh.FlyRefreshLayout
 7 |         android:id="@+id/fly_layout"
 8 |         android:layout_width="match_parent"
 9 |         android:layout_height="match_parent"
10 |         tools:context=".MainActivity">
11 | 
12 |         <android.support.v7.widget.RecyclerView
13 |             android:id="@+id/list"
14 |             android:layout_width="match_parent"
15 |             android:layout_height="match_parent"
16 |             android:paddingTop="24dp"
17 |             android:background="#FFFFFF"/>
18 |     </com.race604.flyrefresh.FlyRefreshLayout>
19 | 
20 |     <android.support.v7.widget.Toolbar
21 |         android:id="@+id/toolbar"
22 |         android:layout_width="match_parent"
23 |         android:layout_height="?attr/actionBarSize"
24 |         android:background="@android:color/transparent"
25 |         android:elevation="4dp"
26 |         android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
27 |         app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
28 | </RelativeLayout>


--------------------------------------------------------------------------------
/app/src/main/res/layout/view_list_item.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3 |     android:layout_width="match_parent"
 4 |     android:layout_height="wrap_content"
 5 |     android:gravity="center_vertical"
 6 |     android:orientation="horizontal"
 7 |     android:paddingBottom="4dp"
 8 |     android:paddingLeft="16dp"
 9 |     android:paddingRight="16dp"
10 |     android:paddingTop="4dp">
11 | 
12 |     <ImageView
13 |         android:id="@+id/icon"
14 |         android:layout_width="40dp"
15 |         android:layout_height="40dp"
16 |         android:layout_margin="4dp"
17 |         android:scaleType="centerInside" />
18 | 
19 |     <RelativeLayout
20 |         android:id="@+id/right"
21 |         android:layout_width="0dp"
22 |         android:layout_height="wrap_content"
23 |         android:layout_margin="4dp"
24 |         android:layout_weight="1"
25 |         android:orientation="vertical">
26 | 
27 |         <TextView
28 |             android:id="@+id/title"
29 |             android:layout_width="wrap_content"
30 |             android:layout_height="wrap_content"
31 |             android:textAppearance="?attr/titleTextAppearance"
32 |             android:textSize="18sp" />
33 | 
34 |         <TextView
35 |             android:id="@+id/subtitle"
36 |             android:layout_width="wrap_content"
37 |             android:layout_height="wrap_content"
38 |             android:layout_below="@id/title"
39 |             android:layout_marginTop="4dp"
40 |             android:textColor="#888888"
41 |             android:textSize="16sp" />
42 | 
43 |         <ImageView
44 |             android:layout_width="30dp"
45 |             android:layout_height="30dp"
46 |             android:layout_alignParentRight="true"
47 |             android:layout_centerVertical="true"
48 |             android:scaleType="centerInside"
49 |             android:src="@mipmap/ic_info_grey600_18dp" />
50 |     </RelativeLayout>
51 | 
52 | 
53 | </LinearLayout>


--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
 1 | <menu xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     xmlns:app="http://schemas.android.com/apk/res-auto"
 3 |     xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
 4 |     <item android:id="@+id/action_search"
 5 |         android:icon="@mipmap/ic_search_white_24dp" android:title="@string/action_search"
 6 |         android:orderInCategory="100" app:showAsAction="always" />
 7 |     <item android:id="@+id/action_settings" android:title="@string/action_settings"
 8 |         android:orderInCategory="100" app:showAsAction="never" />
 9 | </menu>
10 | 


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/race604/FlyRefresh/b3049ab0ec2053b23c6a921ddd23bb6e40820ce9/app/src/main/res/mipmap-hdpi/ic_launcher.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/race604/FlyRefresh/b3049ab0ec2053b23c6a921ddd23bb6e40820ce9/app/src/main/res/mipmap-mdpi/ic_launcher.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/race604/FlyRefresh/b3049ab0ec2053b23c6a921ddd23bb6e40820ce9/app/src/main/res/mipmap-xhdpi/ic_launcher.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_assessment_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/race604/FlyRefresh/b3049ab0ec2053b23c6a921ddd23bb6e40820ce9/app/src/main/res/mipmap-xxhdpi/ic_assessment_white_24dp.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_folder_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/race604/FlyRefresh/b3049ab0ec2053b23c6a921ddd23bb6e40820ce9/app/src/main/res/mipmap-xxhdpi/ic_folder_white_24dp.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_info_grey600_18dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/race604/FlyRefresh/b3049ab0ec2053b23c6a921ddd23bb6e40820ce9/app/src/main/res/mipmap-xxhdpi/ic_info_grey600_18dp.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/race604/FlyRefresh/b3049ab0ec2053b23c6a921ddd23bb6e40820ce9/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_search_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/race604/FlyRefresh/b3049ab0ec2053b23c6a921ddd23bb6e40820ce9/app/src/main/res/mipmap-xxhdpi/ic_search_white_24dp.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_smartphone_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/race604/FlyRefresh/b3049ab0ec2053b23c6a921ddd23bb6e40820ce9/app/src/main/res/mipmap-xxhdpi/ic_smartphone_white_24dp.png


--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 | <resources>
2 |     <!-- Example customization of dimensions originally defined in res/values/dimens.xml
3 |          (such as screen margins) for screens with more than 820dp of available width. This
4 |          would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
5 |     <dimen name="activity_horizontal_margin">64dp</dimen>
6 | </resources>
7 | 


--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 | <resources>
2 |     <!-- Default screen margins, per the Android Design guidelines. -->
3 |     <dimen name="activity_horizontal_margin">16dp</dimen>
4 |     <dimen name="activity_vertical_margin">16dp</dimen>
5 | </resources>
6 | 


--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 | <resources>
2 |     <string name="app_name">FlyRefresh</string>
3 | 
4 |     <string name="hello_world">Hello world!</string>
5 |     <string name="action_settings">Settings</string>
6 |     <string name="action_search">Search</string>
7 | </resources>
8 | 


--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
 1 | <resources>
 2 | 
 3 |     <!-- Base application theme. -->
 4 |     <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
 5 |         <!-- Customize your theme here. -->
 6 |         <item name="colorPrimary">#00BCD4</item>
 7 |         <item name="colorPrimaryDark">#0097A7</item>
 8 |         <item name="colorAccent">#61B3DD</item>
 9 |     </style>
10 | 
11 | </resources>
12 | 


--------------------------------------------------------------------------------
/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:1.2.3'
 9 |         classpath 'com.github.dcendents:android-maven-plugin:1.2'
10 |         classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
11 | 
12 |         // NOTE: Do not place your application dependencies here; they belong
13 |         // in the individual module build.gradle files
14 |     }
15 | }
16 | 
17 | allprojects {
18 |     repositories {
19 |         jcenter()
20 |     }
21 | }
22 | 


--------------------------------------------------------------------------------
/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 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 | 
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true


--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/race604/FlyRefresh/b3049ab0ec2053b23c6a921ddd23bb6e40820ce9/gradle/wrapper/gradle-wrapper.jar


--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 10 15:27:10 PDT 2013
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.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 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
 46 | if $cygwin ; then
 47 |     [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
 48 | fi
 49 | 
 50 | # Attempt to set APP_HOME
 51 | # Resolve links: $0 may be a link
 52 | PRG="$0"
 53 | # Need this for relative symlinks.
 54 | while [ -h "$PRG" ] ; do
 55 |     ls=`ls -ld "$PRG"`
 56 |     link=`expr "$ls" : '.*-> \(.*\)
#39;`
 57 |     if expr "$link" : '/.*' > /dev/null; then
 58 |         PRG="$link"
 59 |     else
 60 |         PRG=`dirname "$PRG"`"/$link"
 61 |     fi
 62 | done
 63 | SAVED="`pwd`"
 64 | cd "`dirname \"$PRG\"`/" >&-
 65 | APP_HOME="`pwd -P`"
 66 | cd "$SAVED" >&-
 67 | 
 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
 69 | 
 70 | # Determine the Java command to use to start the JVM.
 71 | if [ -n "$JAVA_HOME" ] ; then
 72 |     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
 73 |         # IBM's JDK on AIX uses strange locations for the executables
 74 |         JAVACMD="$JAVA_HOME/jre/sh/java"
 75 |     else
 76 |         JAVACMD="$JAVA_HOME/bin/java"
 77 |     fi
 78 |     if [ ! -x "$JAVACMD" ] ; then
 79 |         die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
 80 | 
 81 | Please set the JAVA_HOME variable in your environment to match the
 82 | location of your Java installation."
 83 |     fi
 84 | else
 85 |     JAVACMD="java"
 86 |     which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
 87 | 
 88 | Please set the JAVA_HOME variable in your environment to match the
 89 | location of your Java installation."
 90 | fi
 91 | 
 92 | # Increase the maximum file descriptors if we can.
 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
 94 |     MAX_FD_LIMIT=`ulimit -H -n`
 95 |     if [ $? -eq 0 ] ; then
 96 |         if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
 97 |             MAX_FD="$MAX_FD_LIMIT"
 98 |         fi
 99 |         ulimit -n $MAX_FD
100 |         if [ $? -ne 0 ] ; then
101 |             warn "Could not set maximum file descriptor limit: $MAX_FD"
102 |         fi
103 |     else
104 |         warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 |     fi
106 | fi
107 | 
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 |     GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 | 
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 |     APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 |     CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 | 
118 |     # We build the pattern for arguments to be converted via cygpath
119 |     ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 |     SEP=""
121 |     for dir in $ROOTDIRSRAW ; do
122 |         ROOTDIRS="$ROOTDIRS$SEP$dir"
123 |         SEP="|"
124 |     done
125 |     OURCYGPATTERN="(^($ROOTDIRS))"
126 |     # Add a user-defined pattern to the cygpath arguments
127 |     if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 |         OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 |     fi
130 |     # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 |     i=0
132 |     for arg in "$@" ; do
133 |         CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 |         CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
135 | 
136 |         if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
137 |             eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 |         else
139 |             eval `echo args$i`="\"$arg\""
140 |         fi
141 |         i=$((i+1))
142 |     done
143 |     case $i in
144 |         (0) set -- ;;
145 |         (1) set -- "$args0" ;;
146 |         (2) set -- "$args0" "$args1" ;;
147 |         (3) set -- "$args0" "$args1" "$args2" ;;
148 |         (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 |         (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 |         (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 |         (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 |         (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 |         (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 |     esac
155 | fi
156 | 
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 |     JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 | 
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 | 


--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
 1 | @if "%DEBUG%" == "" @echo off
 2 | @rem ##########################################################################
 3 | @rem
 4 | @rem  Gradle startup script for Windows
 5 | @rem
 6 | @rem ##########################################################################
 7 | 
 8 | @rem Set local scope for the variables with windows NT shell
 9 | if "%OS%"=="Windows_NT" setlocal
10 | 
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 | 
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 | 
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 | 
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 | 
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 | 
32 | goto fail
33 | 
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 | 
38 | if exist "%JAVA_EXE%" goto init
39 | 
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 | 
46 | goto fail
47 | 
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 | 
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 | 
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 | 
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 | 
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 | 
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 | 
69 | :execute
70 | @rem Setup the command line
71 | 
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 | 
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 | 
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 | 
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 | 
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 | 
90 | :omega
91 | 


--------------------------------------------------------------------------------
/images/designed.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/race604/FlyRefresh/b3049ab0ec2053b23c6a921ddd23bb6e40820ce9/images/designed.gif


--------------------------------------------------------------------------------
/images/flyrefresh.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/race604/FlyRefresh/b3049ab0ec2053b23c6a921ddd23bb6e40820ce9/images/flyrefresh.gif


--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | 


--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
 1 | apply plugin: 'com.android.library'
 2 | apply plugin: 'com.github.dcendents.android-maven'
 3 | apply plugin: 'com.jfrog.bintray'
 4 | 
 5 | version = "2.0.0"
 6 | 
 7 | android {
 8 |     compileSdkVersion 23
 9 |     buildToolsVersion "22.0.1"
10 |     resourcePrefix "flyrefresh__"
11 | 
12 |     defaultConfig {
13 |         minSdkVersion 11
14 |         targetSdkVersion 23
15 |         versionCode 4
16 |         versionName version
17 |     }
18 |     buildTypes {
19 |         release {
20 |             minifyEnabled false
21 |             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 |         }
23 |     }
24 | }
25 | 
26 | dependencies {
27 |     compile fileTree(dir: 'libs', include: ['*.jar'])
28 |     compile 'com.android.support:appcompat-v7:23.0.0'
29 |     compile 'com.android.support:design:23.0.0'
30 | }
31 | 
32 | def siteUrl = 'https://github.com/race604/FlyRefresh'
33 | def gitUrl = 'https://github.com/race604/FlyRefresh.git'
34 | group = "com.race604.flyrefresh"
35 | 
36 | install {
37 |     repositories.mavenInstaller {
38 |         // This generates POM.xml with proper parameters
39 |         pom {
40 |             project {
41 |                 packaging 'aar'
42 |                 // Add your description here
43 |                 name 'A awsome pull to refresh widget'
44 |                 url siteUrl
45 |                 // Set your license
46 |                 licenses {
47 |                     license {
48 |                         name 'The MIT License (MIT)'
49 |                         url 'http://opensource.org/licenses/MIT'
50 |                     }
51 |                 }
52 |                 developers {
53 |                     developer {
54 |                         id 'race604'
55 |                         name 'Race604'
56 |                         email 'race604@gmail.com'
57 |                     }
58 |                 }
59 |                 scm {
60 |                     connection gitUrl
61 |                     developerConnection gitUrl
62 |                     url siteUrl
63 |                 }
64 |             }
65 |         }
66 |     }
67 | }
68 | task sourcesJar(type: Jar) {
69 |     from android.sourceSets.main.java.srcDirs
70 |     classifier = 'sources'
71 | }
72 | task javadoc(type: Javadoc) {
73 |     source = android.sourceSets.main.java.srcDirs
74 |     classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
75 | }
76 | task javadocJar(type: Jar, dependsOn: javadoc) {
77 |     classifier = 'javadoc'
78 |     from javadoc.destinationDir
79 | }
80 | artifacts {
81 |     archives javadocJar
82 |     archives sourcesJar
83 | }
84 | 
85 | Properties properties = new Properties()
86 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
87 | bintray {
88 |     user = properties.getProperty("bintray.user")
89 |     key = properties.getProperty("bintray.apikey")
90 |     configurations = ['archives']
91 |     pkg {
92 |         repo = "maven"
93 |         name = "FlyRefresh"
94 |         websiteUrl = siteUrl
95 |         vcsUrl = gitUrl
96 |         licenses = ["MIT"]
97 |         publish = true
98 |     }
99 | }


--------------------------------------------------------------------------------
/library/library.iml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="UTF-8"?>
 2 | <module external.linked.project.id=":library" external.linked.project.path="$MODULE_DIR
quot; external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="com.race604.flyrefresh" external.system.module.version="2.0.0" type="JAVA_MODULE" version="4">
 3 |   <component name="FacetManager">
 4 |     <facet type="android-gradle" name="Android-Gradle">
 5 |       <configuration>
 6 |         <option name="GRADLE_PROJECT_PATH" value=":library" />
 7 |       </configuration>
 8 |     </facet>
 9 |     <facet type="android" name="Android">
10 |       <configuration>
11 |         <option name="SELECTED_BUILD_VARIANT" value="debug" />
12 |         <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
13 |         <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
14 |         <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
15 |         <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
16 |         <option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
17 |         <afterSyncTasks>
18 |           <task>generateDebugAndroidTestSources</task>
19 |           <task>generateDebugSources</task>
20 |         </afterSyncTasks>
21 |         <option name="ALLOW_USER_CONFIGURATION" value="false" />
22 |         <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
23 |         <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
24 |         <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
25 |         <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
26 |         <option name="LIBRARY_PROJECT" value="true" />
27 |       </configuration>
28 |     </facet>
29 |   </component>
30 |   <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
31 |     <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
32 |     <output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
33 |     <exclude-output />
34 |     <content url="file://$MODULE_DIR
quot;>
35 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
36 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
37 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
38 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
39 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
40 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" />
41 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
42 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
43 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
44 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
45 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
46 |       <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" />
47 |       <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
48 |       <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
49 |       <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
50 |       <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
51 |       <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
52 |       <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
53 |       <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
54 |       <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
55 |       <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
56 |       <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
57 |       <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
58 |       <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
59 |       <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
60 |       <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
61 |       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
62 |       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
63 |       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
64 |       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
65 |       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
66 |       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
67 |       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
68 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
69 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
70 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
71 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
72 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
73 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
74 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
75 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
76 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
77 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
78 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
79 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
80 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
81 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
82 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
83 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
84 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
85 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
86 |       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
87 |       <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
88 |       <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
89 |     </content>
90 |     <orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
91 |     <orderEntry type="sourceFolder" forTests="false" />
92 |     <orderEntry type="library" exported="" name="appcompat-v7-23.0.0" level="project" />
93 |     <orderEntry type="library" exported="" name="support-v4-23.0.0" level="project" />
94 |     <orderEntry type="library" exported="" name="design-23.0.0" level="project" />
95 |     <orderEntry type="library" exported="" name="support-annotations-23.0.0" level="project" />
96 |   </component>
97 | </module>


--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
 1 | # Add project specific ProGuard rules here.
 2 | # By default, the flags in this file are appended to flags specified
 3 | # in /home/jing/Tools/android-sdk/tools/proguard/proguard-android.txt
 4 | # You can edit the include path and order by changing the proguardFiles
 5 | # directive in build.gradle.
 6 | #
 7 | # For more details, see
 8 | #   http://developer.android.com/guide/developing/tools/proguard.html
 9 | 
10 | # Add any project specific keep options here:
11 | 
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | #   public *;
17 | #}
18 | 


--------------------------------------------------------------------------------
/library/src/androidTest/java/com/race604/flyrefresh/ApplicationTest.java:
--------------------------------------------------------------------------------
 1 | package com.race604.flyrefresh;
 2 | 
 3 | import android.app.Application;
 4 | import android.test.ApplicationTestCase;
 5 | 
 6 | /**
 7 |  * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
 8 |  */
 9 | public class ApplicationTest extends ApplicationTestCase<Application> {
10 |     public ApplicationTest() {
11 |         super(Application.class);
12 |     }
13 | }


--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2 |     package="com.race604.flyrefresh">
3 | 
4 |     <application android:allowBackup="true" android:label="@string/app_name">
5 | 
6 |     </application>
7 | 
8 | </manifest>
9 | 


--------------------------------------------------------------------------------
/library/src/main/java/com/race604/flyrefresh/FlyRefreshLayout.java:
--------------------------------------------------------------------------------
  1 | package com.race604.flyrefresh;
  2 | 
  3 | import android.animation.Animator;
  4 | import android.animation.AnimatorSet;
  5 | import android.animation.ObjectAnimator;
  6 | import android.content.Context;
  7 | import android.support.v4.view.animation.PathInterpolatorCompat;
  8 | import android.util.AttributeSet;
  9 | import android.view.View;
 10 | import android.view.ViewGroup;
 11 | import android.view.animation.AccelerateInterpolator;
 12 | import android.view.animation.DecelerateInterpolator;
 13 | 
 14 | import com.race604.flyrefresh.internal.MountanScenceView;
 15 | import com.race604.flyrefresh.internal.SimpleAnimatorListener;
 16 | import com.race604.utils.UIUtils;
 17 | 
 18 | /**
 19 |  * Created by jing on 15-5-27.
 20 |  */
 21 | public class FlyRefreshLayout extends PullHeaderLayout {
 22 | 
 23 |     private AnimatorSet mFlyAnimator = null;
 24 |     private OnPullRefreshListener mListener;
 25 | 
 26 |     public FlyRefreshLayout(Context context) {
 27 |         super(context);
 28 |         init(context);
 29 |     }
 30 | 
 31 |     public FlyRefreshLayout(Context context, AttributeSet attrs) {
 32 |         super(context, attrs);
 33 |         init(context);
 34 |     }
 35 | 
 36 |     public FlyRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {
 37 |         super(context, attrs, defStyleAttr);
 38 |         init(context);
 39 |     }
 40 | 
 41 |     private void init(Context context) {
 42 |         MountanScenceView headerView = new MountanScenceView(getContext());
 43 |         LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, mHeaderController.getMaxHeight());
 44 |         setHeaderView(headerView, lp);
 45 |     }
 46 | 
 47 |     @Override
 48 |     protected void onFinishInflate() {
 49 |         super.onFinishInflate();
 50 | 
 51 |         // Set default action icon if user not override
 52 |         if (getIconView() == null) {
 53 |             setActionDrawable(getResources().getDrawable(R.mipmap.ic_send));
 54 |         }
 55 |     }
 56 | 
 57 |     @Override
 58 |     public void startRefresh() {
 59 | 
 60 |         if (mFlyAnimator != null) {
 61 |             mFlyAnimator.end();
 62 |         }
 63 | 
 64 |         final View iconView = getIconView();
 65 |         UIUtils.clearAnimator(iconView);
 66 | 
 67 |         AnimatorSet flyUpAnim = new AnimatorSet();
 68 |         flyUpAnim.setDuration(800);
 69 | 
 70 |         ObjectAnimator transX = ObjectAnimator.ofFloat(iconView, "translationX", 0, getWidth());
 71 |         ObjectAnimator transY = ObjectAnimator.ofFloat(iconView, "translationY", 0, -mHeaderController.getHeight());
 72 |         transY.setInterpolator(PathInterpolatorCompat.create(0.7f, 1f));
 73 |         ObjectAnimator rotation = ObjectAnimator.ofFloat(iconView, "rotation", -45, 0);
 74 |         rotation.setInterpolator(new DecelerateInterpolator());
 75 |         ObjectAnimator rotationX = ObjectAnimator.ofFloat(iconView, "rotationX", 0, 60);
 76 |         rotationX.setInterpolator(new DecelerateInterpolator());
 77 | 
 78 |         flyUpAnim.playTogether(transX, transY, rotationX,
 79 |                 ObjectAnimator.ofFloat(iconView, "scaleX", 1, 0.5f),
 80 |                 ObjectAnimator.ofFloat(iconView, "scaleY", 1, 0.5f),
 81 |                 rotation
 82 |         );
 83 | 
 84 |         mFlyAnimator = flyUpAnim;
 85 |         mFlyAnimator.start();
 86 | 
 87 |         if (mListener != null) {
 88 |             mListener.onRefresh(FlyRefreshLayout.this);
 89 |         }
 90 |     }
 91 | 
 92 |     public void setOnPullRefreshListener(OnPullRefreshListener listener) {
 93 |         mListener = listener;
 94 |     }
 95 | 
 96 |     public void onRefreshFinish() {
 97 |         if (mFlyAnimator != null) {
 98 |             mFlyAnimator.cancel();
 99 |         }
100 | 
101 |         final View iconView = getIconView();
102 |         UIUtils.clearAnimator(iconView);
103 | 
104 |         final int offDistX = -iconView.getRight();
105 |         final int offDistY = -UIUtils.dpToPx(10);
106 |         AnimatorSet flyDownAnim = new AnimatorSet();
107 |         flyDownAnim.setDuration(800);
108 |         ObjectAnimator transX1 = ObjectAnimator.ofFloat(iconView, "translationX", getWidth(), offDistX);
109 |         ObjectAnimator transY1 = ObjectAnimator.ofFloat(iconView, "translationY", -mHeaderController.getHeight(), offDistY);
110 |         transY1.setInterpolator(PathInterpolatorCompat.create(0.1f, 1f));
111 |         ObjectAnimator rotation1 = ObjectAnimator.ofFloat(iconView, "rotation", iconView.getRotation(), 0);
112 |         rotation1.setInterpolator(new AccelerateInterpolator());
113 |         flyDownAnim.playTogether(transX1, transY1,
114 |                 ObjectAnimator.ofFloat(iconView, "scaleX", 0.5f, 0.9f),
115 |                 ObjectAnimator.ofFloat(iconView, "scaleY", 0.5f, 0.9f),
116 |                 rotation1
117 |         );
118 |         flyDownAnim.addListener(new SimpleAnimatorListener() {
119 |             @Override
120 |             public void onAnimationStart(Animator animation) {
121 |                 iconView.setRotationY(180);
122 |             }
123 |         });
124 | 
125 |         AnimatorSet flyInAnim = new AnimatorSet();
126 |         flyInAnim.setDuration(400);
127 |         flyInAnim.setInterpolator(new DecelerateInterpolator());
128 |         ObjectAnimator tranX2 = ObjectAnimator.ofFloat(iconView, "translationX", offDistX, 0);
129 |         ObjectAnimator tranY2 = ObjectAnimator.ofFloat(iconView, "translationY", offDistY, 0);
130 |         ObjectAnimator rotationX2 = ObjectAnimator.ofFloat(iconView, "rotationX", 30, 0);
131 |         flyInAnim.playTogether(tranX2, tranY2, rotationX2,
132 |                 ObjectAnimator.ofFloat(iconView, "scaleX", 0.9f, 1f),
133 |                 ObjectAnimator.ofFloat(iconView, "scaleY", 0.9f, 1f));
134 |         flyInAnim.setStartDelay(100);
135 |         flyInAnim.addListener(new SimpleAnimatorListener() {
136 |             @Override
137 |             public void onAnimationStart(Animator animation) {
138 |                 iconView.setRotationY(0);
139 |             }
140 |         });
141 | 
142 |         mFlyAnimator = new AnimatorSet();
143 |         mFlyAnimator.playSequentially(flyDownAnim, flyInAnim);
144 |         mFlyAnimator.addListener(new SimpleAnimatorListener() {
145 |             @Override
146 |             public void onAnimationEnd(Animator animation) {
147 |                 if (mListener != null) {
148 |                     mListener.onRefreshAnimationEnd(FlyRefreshLayout.this);
149 |                 }
150 |             }
151 |         });
152 |         mFlyAnimator.start();
153 |     }
154 | 
155 |     @Override
156 |     protected void onMoveHeader(int state, float progress) {
157 |         super.onMoveHeader(state, progress);
158 |         if (mHeaderController.isOverHeight()) {
159 |             getIconView().setRotation((-45) * progress);
160 |         }
161 |     }
162 | 
163 |     public interface OnPullRefreshListener {
164 |         void onRefresh(FlyRefreshLayout view);
165 |         void onRefreshAnimationEnd(FlyRefreshLayout view);
166 |     }
167 | }
168 | 


--------------------------------------------------------------------------------
/library/src/main/java/com/race604/flyrefresh/HeaderController.java:
--------------------------------------------------------------------------------
  1 | package com.race604.flyrefresh;
  2 | 
  3 | /**
  4 |  * Created by jing on 15-5-19.
  5 |  */
  6 | public class HeaderController {
  7 | 
  8 |     private int mHeight;
  9 |     private int mMaxHegiht;
 10 |     private int mMinHegiht;
 11 |     private float mOverDistance;
 12 | 
 13 |     private float mResistance = 0.5f;
 14 |     private boolean mIsInTouch = false;
 15 |     private float mScroll = 0;
 16 |     private int mMaxScroll = 0;
 17 |     private int mMinScroll = 0;
 18 | 
 19 |     public HeaderController(int height, int maxHeight, int minHeight) {
 20 | 
 21 |         if (maxHeight <= 0) {
 22 |             throw new IllegalArgumentException("maxHeight must > 0");
 23 |         }
 24 | 
 25 |         setSize(height, maxHeight, minHeight);
 26 |     }
 27 | 
 28 |     public void setSize(int height, int maxHeight, int minHeight) {
 29 |         mHeight = Math.max(0, height);
 30 |         mMaxHegiht = Math.max(0, maxHeight);
 31 |         mMinHegiht = Math.max(0, minHeight);
 32 |         mOverDistance = mMaxHegiht - mHeight;
 33 | 
 34 |         mScroll = 0;
 35 |         mMaxScroll = mHeight - mMinHegiht;
 36 |         mMinScroll = mHeight - mMaxHegiht;
 37 |     }
 38 | 
 39 |     public int getMaxHeight() {
 40 |         return mMaxHegiht;
 41 |     }
 42 | 
 43 |     public int getMinHeight() {
 44 |         return mMinHegiht;
 45 |     }
 46 | 
 47 |     public int getHeight() {
 48 |         return mHeight;
 49 |     }
 50 | 
 51 |     public int getScroll() {
 52 |         return (int) mScroll;
 53 |     }
 54 | 
 55 |     public int getMaxScroll() {
 56 |         return mMaxScroll;
 57 |     }
 58 | 
 59 |     public int getMinScroll() {
 60 |         return mMinScroll;
 61 |     }
 62 | 
 63 |     public int getCurPosition() {
 64 |         return (int) (mHeight - mScroll);
 65 |     }
 66 | 
 67 |     public boolean isInTouch() {
 68 |         return mIsInTouch;
 69 |     }
 70 | 
 71 |     /**
 72 |      * Check if can scroll down to show top
 73 |      * @return
 74 |      */
 75 |     public boolean canScrollDown() {
 76 |         return mScroll > mMinScroll;
 77 |     }
 78 | 
 79 |     /**
 80 |      * Check if can scroll up to show bottom
 81 |      * @return
 82 |      */
 83 |     public boolean canScrollUp() {
 84 |         return mScroll < mMaxScroll;
 85 |     }
 86 | 
 87 |     public int move(float deltaY) {
 88 |         float willTo;
 89 |         float consumed = deltaY;
 90 |         if (mScroll >= 0) {
 91 |             willTo = mScroll + deltaY;
 92 |             if (willTo < 0) {
 93 |                 willTo = willTo * mResistance;
 94 |                 if (willTo < mMinScroll) {
 95 |                     consumed -= (willTo - mMinScroll) / mResistance;
 96 |                     willTo = mMinScroll;
 97 |                 }
 98 |             } else if (willTo > mMaxScroll) {
 99 |                 consumed -= willTo - mMaxScroll;
100 |                 willTo = mMaxScroll;
101 |             }
102 |         } else {
103 |             willTo = mScroll + deltaY * mResistance;
104 |             if (willTo > 0) {
105 |                 willTo = willTo / mResistance;
106 |                 if (willTo > mMaxScroll) {
107 |                     consumed -= willTo - mMaxScroll;
108 |                     willTo = mMaxScroll;
109 |                 }
110 |             } else if (willTo < mMinScroll) {
111 |                 consumed -= willTo - mMinScroll;
112 |                 willTo = mMinScroll;
113 |             }
114 |         }
115 | 
116 |         mScroll = willTo;
117 |         return (int) consumed;
118 |     }
119 | 
120 |     public boolean isOverHeight() {
121 |         return mScroll < 0;
122 |     }
123 | 
124 |     public float getMovePercentage() {
125 |         return -mScroll / mOverDistance;
126 |     }
127 | 
128 |     public boolean needSendRefresh() {
129 |         return getMovePercentage() > 0.9f;
130 |     }
131 | }
132 | 


--------------------------------------------------------------------------------
/library/src/main/java/com/race604/flyrefresh/IPullHeader.java:
--------------------------------------------------------------------------------
1 | package com.race604.flyrefresh;
2 | 
3 | /**
4 |  * Created by jing on 15-5-19.
5 |  */
6 | public interface IPullHeader {
7 |     void onPullProgress(PullHeaderLayout parent, int state, float progress);
8 | }
9 | 


--------------------------------------------------------------------------------
/library/src/main/java/com/race604/flyrefresh/PullHeaderLayout.java:
--------------------------------------------------------------------------------
  1 | package com.race604.flyrefresh;
  2 | 
  3 | import android.animation.Animator;
  4 | import android.animation.ObjectAnimator;
  5 | import android.animation.ValueAnimator;
  6 | import android.content.Context;
  7 | import android.content.res.TypedArray;
  8 | import android.graphics.drawable.Drawable;
  9 | import android.graphics.drawable.ShapeDrawable;
 10 | import android.graphics.drawable.shapes.OvalShape;
 11 | import android.support.annotation.Nullable;
 12 | import android.support.design.widget.FloatingActionButton;
 13 | import android.support.v4.view.MotionEventCompat;
 14 | import android.support.v4.view.NestedScrollingChild;
 15 | import android.support.v4.view.NestedScrollingChildHelper;
 16 | import android.support.v4.view.NestedScrollingParent;
 17 | import android.support.v4.view.NestedScrollingParentHelper;
 18 | import android.support.v4.view.VelocityTrackerCompat;
 19 | import android.support.v4.view.ViewCompat;
 20 | import android.support.v4.widget.ScrollerCompat;
 21 | import android.util.AttributeSet;
 22 | import android.util.Log;
 23 | import android.view.MotionEvent;
 24 | import android.view.VelocityTracker;
 25 | import android.view.View;
 26 | import android.view.ViewConfiguration;
 27 | import android.view.ViewGroup;
 28 | import android.view.ViewParent;
 29 | import android.widget.ImageView;
 30 | 
 31 | import com.race604.flyrefresh.internal.ElasticOutInterpolator;
 32 | import com.race604.flyrefresh.internal.SimpleAnimatorListener;
 33 | import com.race604.utils.UIUtils;
 34 | 
 35 | /**
 36 |  * Created by Jing on 15/5/18.
 37 |  */
 38 | public class PullHeaderLayout extends ViewGroup implements NestedScrollingParent, NestedScrollingChild {
 39 | 
 40 |     private static final String TAG = PullHeaderLayout.class.getCanonicalName();
 41 |     private static final boolean D = true;
 42 | 
 43 |     public static final int STATE_IDLE = 0;
 44 |     public static final int STATE_DRAGE = 1;
 45 |     public static final int STATE_FLING = 2;
 46 |     public static final int STATE_BOUNCE = 3;
 47 | 
 48 |     final static int ACTION_BUTTON_CENTER = UIUtils.dpToPx(40);
 49 |     final static int ACTION_ICON_SIZE = UIUtils.dpToPx(32);
 50 |     private final static int DEFAULT_EXPAND = UIUtils.dpToPx(300);
 51 |     private final static int DEFAULT_HEIGHT = UIUtils.dpToPx(240);
 52 |     private final static int DEFAULT_SHRINK = UIUtils.dpToPx(48);
 53 | 
 54 |     private int mHeaderId = 0;
 55 |     private int mContentId = 0;
 56 | 
 57 |     private Drawable mActionDrawable;
 58 |     private FloatingActionButton mActionView;
 59 |     private ImageView mFlyView;
 60 |     private View mHeaderView;
 61 |     private IPullHeader mPullHeaderView;
 62 |     protected View mContent;
 63 |     protected HeaderController mHeaderController;
 64 | 
 65 |     private VelocityTracker mVelocityTracker;
 66 |     private ValueAnimator mBounceAnim;
 67 |     private int mPullState = STATE_IDLE;
 68 | 
 69 |     private static final int INVALID_POINTER = -1;
 70 |     private int mActivePointerId = INVALID_POINTER;
 71 |     private boolean mIsBeingDragged = false;
 72 |     private int mLastMotionY = 0;
 73 | 
 74 |     private int mTouchSlop;
 75 |     private int mMinimumVelocity;
 76 |     private int mMaximumVelocity;
 77 |     private int mNestedYOffset;
 78 | 
 79 |     private final int[] mScrollOffset = new int[2];
 80 |     private final int[] mScrollConsumed = new int[2];
 81 | 
 82 |     private final NestedScrollingParentHelper mParentHelper;
 83 |     private final NestedScrollingChildHelper mChildHelper;
 84 | 
 85 |     private ScrollerCompat mScroller;
 86 | 
 87 |     private OnPullListener mPullListener;
 88 | 
 89 |     public PullHeaderLayout(Context context) {
 90 |         this(context, null);
 91 |     }
 92 | 
 93 |     public PullHeaderLayout(Context context, AttributeSet attrs) {
 94 |         this(context, attrs, 0);
 95 |     }
 96 | 
 97 |     public PullHeaderLayout(Context context, AttributeSet attrs, int defStyleAttr) {
 98 |         super(context, attrs, defStyleAttr);
 99 | 
100 |         int headerHeight = DEFAULT_HEIGHT;
101 |         int headerExpandHeight = DEFAULT_EXPAND;
102 |         int headerShrinkHeight = DEFAULT_SHRINK;
103 | 
104 |         if (attrs != null) {
105 |             TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.PullHeaderLayout);
106 |             headerHeight = arr.getDimensionPixelOffset(R.styleable.PullHeaderLayout_phl_header_height,
107 |                     DEFAULT_HEIGHT);
108 |             headerExpandHeight = arr.getDimensionPixelOffset(R.styleable.PullHeaderLayout_phl_header_expand_height,
109 |                     DEFAULT_EXPAND);
110 |             headerShrinkHeight = arr.getDimensionPixelOffset(R.styleable.PullHeaderLayout_phl_header_shrink_height,
111 |                     DEFAULT_SHRINK);
112 | 
113 |             mHeaderId = arr.getResourceId(R.styleable.PullHeaderLayout_phl_header, mHeaderId);
114 |             mContentId = arr.getResourceId(R.styleable.PullHeaderLayout_phl_content, mContentId);
115 | 
116 |             mActionDrawable = arr.getDrawable(R.styleable.PullHeaderLayout_phl_action);
117 | 
118 |             arr.recycle();
119 |         }
120 | 
121 |         mHeaderController = new HeaderController(headerHeight, headerExpandHeight, headerShrinkHeight);
122 | 
123 |         final ViewConfiguration conf = ViewConfiguration.get(getContext());
124 | 
125 |         mParentHelper = new NestedScrollingParentHelper(this);
126 |         mChildHelper = new NestedScrollingChildHelper(this);
127 | 
128 |         setNestedScrollingEnabled(true);
129 | 
130 |         init();
131 |     }
132 | 
133 |     private void init() {
134 |         mScroller = ScrollerCompat.create(getContext());
135 |         final ViewConfiguration configuration = ViewConfiguration.get(getContext());
136 |         mTouchSlop = configuration.getScaledTouchSlop();
137 |         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
138 |         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
139 |     }
140 | 
141 |     // NestedScrollingChild
142 | 
143 |     @Override
144 |     public void setNestedScrollingEnabled(boolean enabled) {
145 |         mChildHelper.setNestedScrollingEnabled(enabled);
146 |     }
147 | 
148 |     @Override
149 |     public boolean isNestedScrollingEnabled() {
150 |         return mChildHelper.isNestedScrollingEnabled();
151 |     }
152 | 
153 |     @Override
154 |     public boolean startNestedScroll(int axes) {
155 |         return mChildHelper.startNestedScroll(axes);
156 |     }
157 | 
158 |     @Override
159 |     public void stopNestedScroll() {
160 |         mChildHelper.stopNestedScroll();
161 |     }
162 | 
163 |     @Override
164 |     public boolean hasNestedScrollingParent() {
165 |         return mChildHelper.hasNestedScrollingParent();
166 |     }
167 | 
168 |     @Override
169 |     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
170 |                                         int dyUnconsumed, int[] offsetInWindow) {
171 |         return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
172 |                 offsetInWindow);
173 |     }
174 | 
175 |     @Override
176 |     public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
177 |         return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
178 |     }
179 | 
180 |     @Override
181 |     public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
182 |         return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
183 |     }
184 | 
185 |     @Override
186 |     public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
187 |         return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
188 |     }
189 | 
190 |     // NestedScrollingParent
191 | 
192 |     @Override
193 |     public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
194 |         return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
195 |     }
196 | 
197 |     @Override
198 |     public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
199 |         mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
200 |         startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
201 |     }
202 | 
203 |     @Override
204 |     public void onStopNestedScroll(View target) {
205 |         stopNestedScroll();
206 |     }
207 | 
208 |     @Override
209 |     public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
210 |                                int dyUnconsumed) {
211 |         final int myConsumed = moveBy(dyUnconsumed);
212 |         final int myUnconsumed = dyUnconsumed - myConsumed;
213 |         dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null);
214 |     }
215 | 
216 |     @Override
217 |     public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
218 |         if (dy > 0 && mHeaderController.canScrollUp()) {
219 |             final int delta = moveBy(dy);
220 |             consumed[0] = 0;
221 |             consumed[1] = delta;
222 |             //dispatchNestedScroll(0, myConsumed, 0, consumed[1], null);
223 |         }
224 |     }
225 | 
226 |     @Override
227 |     public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
228 |         if (!consumed) {
229 |             flingWithNestedDispatch((int) velocityY);
230 |             return true;
231 |         }
232 |         return false;
233 |     }
234 | 
235 |     private boolean flingWithNestedDispatch(int velocityY) {
236 |         final boolean canFling = (mHeaderController.canScrollUp() && velocityY > 0) ||
237 |                 (mHeaderController.canScrollDown() && velocityY < 0);
238 |         if (!dispatchNestedPreFling(0, velocityY)) {
239 |             dispatchNestedFling(0, velocityY, canFling);
240 |             if (canFling) {
241 |                 fling(velocityY);
242 |             }
243 |         }
244 |         return canFling;
245 |     }
246 | 
247 |     @Override
248 |     public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
249 |         return flingWithNestedDispatch((int) velocityY);
250 |     }
251 | 
252 |     @Override
253 |     public int getNestedScrollAxes() {
254 |         return mParentHelper.getNestedScrollAxes();
255 |     }
256 | 
257 |     public void setHeaderSize(int height, int maxHeight, int minHeight) {
258 |         mHeaderController.setSize(height, maxHeight, minHeight);
259 |         if (isLayoutRequested()) {
260 |             requestLayout();
261 |         }
262 |     }
263 | 
264 |     public void setOnPullListener(OnPullListener listener) {
265 |         mPullListener = listener;
266 |     }
267 | 
268 |     public void setActionDrawable(Drawable actionDrawable) {
269 |         mActionDrawable = actionDrawable;
270 |         if (mActionDrawable != null) {
271 |             if (mActionView == null) {
272 |                 final int bgColor = UIUtils.getThemeColorFromAttrOrRes(getContext(), R.attr.colorAccent, R.color.accent);
273 |                 final int pressedColor = UIUtils.darkerColor(bgColor, 0.8f);
274 | 
275 |                 final ShapeDrawable bgDrawable = new ShapeDrawable(new OvalShape());
276 |                 bgDrawable.getPaint().setColor(bgColor);
277 |                 mActionView = new FloatingActionButton(getContext());
278 |                 mActionView.setRippleColor(pressedColor);
279 |                 mActionView.setBackgroundDrawable(bgDrawable);
280 |                 addView(mActionView, new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
281 |             }
282 | 
283 |             if (mFlyView == null) {
284 |                 mFlyView = new ImageView(getContext());
285 |                 mFlyView.setScaleType(ImageView.ScaleType.FIT_XY);
286 |                 addView(mFlyView, new LayoutParams(ACTION_ICON_SIZE, ACTION_ICON_SIZE));
287 |                 mFlyView.bringToFront();
288 |                 float elevation = ViewCompat.getElevation(mActionView);
289 |                 ViewCompat.setElevation(mFlyView, elevation + 1);
290 |             }
291 |             mFlyView.setImageDrawable(mActionDrawable);
292 | 
293 |         } else {
294 |             if (mActionView != null) {
295 |                 removeView(mActionView);
296 |                 removeView(mFlyView);
297 |                 mActionView = null;
298 |                 mFlyView = null;
299 |             }
300 |         }
301 |     }
302 | 
303 |     @Nullable
304 |     public View getIconView() {
305 |         return mFlyView;
306 |     }
307 | 
308 |     @Nullable
309 |     public FloatingActionButton getHeaderActionButton() {
310 |         return mActionView;
311 |     }
312 | 
313 |     public void setHeaderView(View headerView, LayoutParams lp) {
314 |         if (mHeaderView != null) {
315 |             removeView(mHeaderView);
316 |             mPullHeaderView = null;
317 |         }
318 | 
319 |         addView(headerView, 0, lp);
320 |         mHeaderView = headerView;
321 | 
322 |         if (mHeaderView instanceof IPullHeader) {
323 |             mPullHeaderView = (IPullHeader) mHeaderView;
324 |         }
325 |     }
326 | 
327 |     @Override
328 |     protected void onFinishInflate() {
329 |         final int childCount = getChildCount();
330 |         if (childCount > 2) {
331 |             throw new IllegalStateException("FlyRefreshLayout only can host 2 elements");
332 |         } else if (childCount == 2) {
333 |             if (mHeaderId != 0 && mHeaderView == null) {
334 |                 mHeaderView = findViewById(mHeaderId);
335 |             }
336 | 
337 |             if (mContentId != 0 && mContent == null) {
338 |                 mContent = findViewById(mContentId);
339 |             }
340 | 
341 |             // not specify header or content
342 |             if (mContent == null || mHeaderView == null) {
343 | 
344 |                 View child1 = getChildAt(0);
345 |                 View child2 = getChildAt(1);
346 |                 if (child1 instanceof IPullHeader) {
347 |                     mHeaderView = child1;
348 |                     mContent = child2;
349 |                     mPullHeaderView = (IPullHeader) mHeaderView;
350 |                 } else if (child2 instanceof IPullHeader) {
351 |                     mHeaderView = child2;
352 |                     mContent = child1;
353 |                     mPullHeaderView = (IPullHeader) mHeaderView;
354 |                 } else {
355 |                     // both are not specified
356 |                     if (mContent == null && mHeaderView == null) {
357 |                         mHeaderView = child1;
358 |                         mContent = child2;
359 |                     }
360 |                     // only one is specified
361 |                     else {
362 |                         if (mHeaderView == null) {
363 |                             mHeaderView = mContent == child1 ? child2 : child1;
364 |                         } else {
365 |                             mContent = mHeaderView == child1 ? child2 : child1;
366 |                         }
367 |                     }
368 |                 }
369 |             }
370 |         } else if (childCount == 1) {
371 |             mContent = getChildAt(0);
372 |         }
373 | 
374 |         setActionDrawable(mActionDrawable);
375 | 
376 |         super.onFinishInflate();
377 |     }
378 | 
379 |     @Override
380 |     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
381 |         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
382 | 
383 |         if (mHeaderView != null) {
384 |             measureChildWithMargins(mHeaderView, widthMeasureSpec, 0, heightMeasureSpec, 0);
385 |         }
386 | 
387 |         if (mContent != null) {
388 |             measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, mHeaderController.getMinHeight());
389 |         }
390 | 
391 |         if (mActionView != null) {
392 |             measureChild(mActionView, widthMeasureSpec, heightMeasureSpec);
393 |             measureChild(mFlyView, widthMeasureSpec, heightMeasureSpec);
394 |         }
395 |     }
396 | 
397 |     @Override
398 |     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
399 |         layoutChildren();
400 |     }
401 | 
402 |     private void layoutChildren() {
403 |         int offsetY = mHeaderController.getCurPosition();
404 |         int paddingLeft = getPaddingLeft();
405 |         int paddingTop = getPaddingTop();
406 | 
407 |         if (mHeaderView != null) {
408 |             MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams();
409 |             final int left = paddingLeft + lp.leftMargin;
410 |             final int top = paddingTop + lp.topMargin;
411 |             final int right = left + mHeaderView.getMeasuredWidth();
412 |             final int bottom = top + mHeaderView.getMeasuredHeight();
413 |             mHeaderView.layout(left, top, right, bottom);
414 |         }
415 | 
416 |         if (mContent != null) {
417 |             MarginLayoutParams lp = (MarginLayoutParams) mContent.getLayoutParams();
418 |             final int left = paddingLeft + lp.leftMargin;
419 |             final int top = paddingTop + lp.topMargin + offsetY;
420 |             final int right = left + mContent.getMeasuredWidth();
421 |             final int bottom = top + mContent.getMeasuredHeight();
422 |             mContent.layout(left, top, right, bottom);
423 |         }
424 | 
425 |         if (mActionView != null) {
426 |             final int center = ACTION_BUTTON_CENTER;
427 |             int halfWidth = (mActionView.getMeasuredWidth() + 1) / 2;
428 |             int halfHeight = (mActionView.getMeasuredHeight() + 1) / 2;
429 | 
430 |             mActionView.layout(center - halfWidth , offsetY - halfHeight,
431 |                     center + halfWidth, offsetY + halfHeight);
432 | 
433 |             halfWidth = (mFlyView.getMeasuredWidth() + 1) / 2;
434 |             halfHeight = (mFlyView.getMeasuredHeight() + 1) / 2;
435 |             mFlyView.layout(center - halfWidth, offsetY - halfHeight,
436 |                     center + halfWidth, offsetY + halfHeight);
437 |         }
438 | 
439 |     }
440 | 
441 |     private void obtainVelocityTracker(MotionEvent event) {
442 |         if (mVelocityTracker == null) {
443 |             mVelocityTracker = VelocityTracker.obtain();
444 |         }
445 |         mVelocityTracker.addMovement(event);
446 |     }
447 | 
448 |     private void initOrResetVelocityTracker() {
449 |         if (mVelocityTracker == null) {
450 |             mVelocityTracker = VelocityTracker.obtain();
451 |         } else {
452 |             mVelocityTracker.clear();
453 |         }
454 |     }
455 | 
456 |     private void releaseVelocityTracker() {
457 |         if (mVelocityTracker != null) {
458 |             mVelocityTracker.recycle();
459 |             mVelocityTracker = null;
460 |         }
461 |     }
462 | 
463 |     private void onSecondaryPointerUp(MotionEvent ev) {
464 |         final int pointerIndex = (ev.getAction() & MotionEventCompat.ACTION_POINTER_INDEX_MASK) >>
465 |                 MotionEventCompat.ACTION_POINTER_INDEX_SHIFT;
466 |         final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
467 |         if (pointerId == mActivePointerId) {
468 |             // This was our active pointer going up. Choose a new
469 |             // active pointer and adjust accordingly.
470 |             // TODO: Make this decision more intelligent.
471 |             final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
472 |             mLastMotionY = (int) MotionEventCompat.getY(ev, newPointerIndex);
473 |             mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
474 |             if (mVelocityTracker != null) {
475 |                 mVelocityTracker.clear();
476 |             }
477 |         }
478 |     }
479 | 
480 |     private void endDrag() {
481 |         mIsBeingDragged = false;
482 |         releaseVelocityTracker();
483 |     }
484 | 
485 |     @Override
486 |     public boolean onInterceptTouchEvent(MotionEvent ev) {
487 |         final int action = ev.getAction();
488 |         if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
489 |             return true;
490 |         }
491 | 
492 |         if(!isEnabled()) {
493 |             return false;
494 |         }
495 | 
496 |         switch (action & MotionEventCompat.ACTION_MASK) {
497 |             case MotionEvent.ACTION_MOVE: {
498 |                 final int activePointerId = mActivePointerId;
499 |                 if (activePointerId == INVALID_POINTER) {
500 |                     // If we don't have a valid id, the touch down wasn't on content.
501 |                     break;
502 |                 }
503 | 
504 |                 final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);
505 |                 if (pointerIndex == -1) {
506 |                     Log.e(TAG, "Invalid pointerId=" + activePointerId
507 |                             + " in onInterceptTouchEvent");
508 |                     break;
509 |                 }
510 | 
511 |                 final int y = (int) MotionEventCompat.getY(ev, pointerIndex);
512 |                 final int yDiff = Math.abs(y - mLastMotionY);
513 |                 if (yDiff > mTouchSlop
514 |                         && (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0) {
515 |                     mIsBeingDragged = true;
516 |                     mLastMotionY = y;
517 |                     obtainVelocityTracker(ev);
518 |                     mNestedYOffset = 0;
519 |                     final ViewParent parent = getParent();
520 |                     if (parent != null) {
521 |                         parent.requestDisallowInterceptTouchEvent(true);
522 |                     }
523 |                 }
524 |                 break;
525 |             }
526 | 
527 |             case MotionEvent.ACTION_DOWN: {
528 |                 final int y = (int) ev.getY();
529 | 
530 |                 /*
531 |                  * Remember location of down touch.
532 |                  * ACTION_DOWN always refers to pointer index 0.
533 |                  */
534 |                 mLastMotionY = y;
535 |                 mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
536 | 
537 |                 initOrResetVelocityTracker();
538 |                 mVelocityTracker.addMovement(ev);
539 |                 /*
540 |                 * If being flinged and user touches the screen, initiate drag;
541 |                 * otherwise don't.  mScroller.isFinished should be false when
542 |                 * being flinged.
543 |                 */
544 |                 mIsBeingDragged = !mScroller.isFinished();
545 |                 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
546 |                 break;
547 |             }
548 | 
549 |             case MotionEvent.ACTION_CANCEL:
550 |             case MotionEvent.ACTION_UP:
551 |                 /* Release the drag */
552 |                 mIsBeingDragged = false;
553 |                 mActivePointerId = INVALID_POINTER;
554 |                 endDrag();
555 |                 stopNestedScroll();
556 |                 break;
557 |             case MotionEventCompat.ACTION_POINTER_UP:
558 |                 onSecondaryPointerUp(ev);
559 |                 break;
560 |         }
561 | 
562 |         return mIsBeingDragged;
563 |     }
564 | 
565 |     @Override
566 |     public boolean dispatchTouchEvent(MotionEvent ev) {
567 |         final int actionMasked = MotionEventCompat.getActionMasked(ev);
568 |         if (actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL) {
569 |             tryBounceBack();
570 |         }
571 |         return super.dispatchTouchEvent(ev);
572 |     }
573 | 
574 |     @Override
575 |     public boolean onTouchEvent(MotionEvent ev) {
576 | 
577 |         MotionEvent vtev = MotionEvent.obtain(ev);
578 | 
579 |         final int actionMasked = MotionEventCompat.getActionMasked(ev);
580 | 
581 |         if (actionMasked == MotionEvent.ACTION_DOWN) {
582 |             mNestedYOffset = 0;
583 |         }
584 |         vtev.offsetLocation(0, mNestedYOffset);
585 | 
586 |         switch (actionMasked) {
587 |             case MotionEvent.ACTION_DOWN: {
588 |                 if ((mIsBeingDragged = !mScroller.isFinished())) {
589 |                     final ViewParent parent = getParent();
590 |                     if (parent != null) {
591 |                         parent.requestDisallowInterceptTouchEvent(true);
592 |                     }
593 |                 }
594 | 
595 |                 /*
596 |                  * If being flinged and user touches, stop the fling. isFinished
597 |                  * will be false if being flinged.
598 |                  */
599 |                 if (!mScroller.isFinished()) {
600 |                     mScroller.abortAnimation();
601 |                 }
602 | 
603 |                 // Remember where the motion event started
604 |                 mLastMotionY = (int) ev.getY();
605 |                 mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
606 |                 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
607 |                 break;
608 |             }
609 |             case MotionEvent.ACTION_MOVE:
610 |                 final int activePointerIndex = MotionEventCompat.findPointerIndex(ev,
611 |                         mActivePointerId);
612 |                 if (activePointerIndex == -1) {
613 |                     Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
614 |                     break;
615 |                 }
616 | 
617 |                 final int y = (int) MotionEventCompat.getY(ev, activePointerIndex);
618 |                 int deltaY = mLastMotionY - y;
619 |                 if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
620 |                     deltaY -= mScrollConsumed[1];
621 |                     vtev.offsetLocation(0, mScrollOffset[1]);
622 |                     mNestedYOffset += mScrollOffset[1];
623 |                 }
624 |                 if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
625 |                     final ViewParent parent = getParent();
626 |                     if (parent != null) {
627 |                         parent.requestDisallowInterceptTouchEvent(true);
628 |                     }
629 |                     mIsBeingDragged = true;
630 |                     if (deltaY > 0) {
631 |                         deltaY -= mTouchSlop;
632 |                     } else {
633 |                         deltaY += mTouchSlop;
634 |                     }
635 |                 }
636 |                 if (mIsBeingDragged) {
637 |                     // Scroll to follow the motion event
638 |                     mLastMotionY = y - mScrollOffset[1];
639 | 
640 |                     final int scrolledDeltaY = moveBy(deltaY);
641 |                     final int unconsumedY = deltaY - scrolledDeltaY;
642 |                     if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset)) {
643 |                         mLastMotionY -= mScrollOffset[1];
644 |                         vtev.offsetLocation(0, mScrollOffset[1]);
645 |                         mNestedYOffset += mScrollOffset[1];
646 |                     }
647 |                 }
648 |                 break;
649 |             case MotionEvent.ACTION_UP:
650 |                 if (mIsBeingDragged) {
651 |                     final VelocityTracker velocityTracker = mVelocityTracker;
652 |                     velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
653 |                     int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(velocityTracker,
654 |                             mActivePointerId);
655 | 
656 |                     if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
657 |                         flingWithNestedDispatch(-initialVelocity);
658 |                     }
659 | 
660 |                     mActivePointerId = INVALID_POINTER;
661 |                     endDrag();
662 |                 }
663 |                 break;
664 |             case MotionEvent.ACTION_CANCEL:
665 |                 if (mIsBeingDragged && getChildCount() > 0) {
666 |                     mActivePointerId = INVALID_POINTER;
667 |                     endDrag();
668 |                 }
669 |                 break;
670 |             case MotionEventCompat.ACTION_POINTER_DOWN: {
671 |                 final int index = MotionEventCompat.getActionIndex(ev);
672 |                 mLastMotionY = (int) MotionEventCompat.getY(ev, index);
673 |                 mActivePointerId = MotionEventCompat.getPointerId(ev, index);
674 |                 break;
675 |             }
676 |             case MotionEventCompat.ACTION_POINTER_UP:
677 |                 onSecondaryPointerUp(ev);
678 |                 mLastMotionY = (int) MotionEventCompat.getY(ev,
679 |                         MotionEventCompat.findPointerIndex(ev, mActivePointerId));
680 |                 break;
681 |         }
682 | 
683 |         if (mVelocityTracker != null) {
684 |             mVelocityTracker.addMovement(vtev);
685 |         }
686 |         vtev.recycle();
687 |         return true;
688 |     }
689 | 
690 |     protected void onMoveHeader(int state, float progress) {}
691 | 
692 |     /**
693 |      * Fling the scroll view
694 |      *
695 |      * @param velocityY The initial velocity in the Y direction. Positive
696 |      *                  numbers mean that the finger/cursor is moving down the screen,
697 |      *                  which means we want to scroll towards the top.
698 |      */
699 |     public void fling(int velocityY) {
700 |         mPullState = STATE_FLING;
701 |         mScroller.abortAnimation();
702 |         mScroller.fling(0, mHeaderController.getScroll(), 0, velocityY, 0, 0,
703 |                 mHeaderController.getMinScroll(), mHeaderController.getMaxScroll(),
704 |                 0, 0);
705 |         ViewCompat.postInvalidateOnAnimation(this);
706 |     }
707 | 
708 |     private int moveBy(float deltaY) {
709 |         int oldScroll = mHeaderController.getScroll();
710 |         int consumed = mHeaderController.move(deltaY);
711 | 
712 |         final int delta = mHeaderController.getScroll() - oldScroll;
713 |         if (delta == 0) {
714 |             return 0;
715 |         }
716 | 
717 |         if (mPullState != STATE_DRAGE) {
718 |             mPullState = STATE_DRAGE;
719 |         }
720 | 
721 |         if (mContent != null) {
722 |             mContent.offsetTopAndBottom(-delta);
723 |         }
724 | 
725 |         if (mActionView != null) {
726 |             mActionView.offsetTopAndBottom(-delta);
727 | 
728 |             mFlyView.offsetTopAndBottom(-delta);
729 | 
730 |             float percentage = mHeaderController.getMovePercentage();
731 |             onMoveHeader(mPullState, percentage);
732 | 
733 |             if (mPullHeaderView != null) {
734 |                 mPullHeaderView.onPullProgress(this, mPullState, percentage);
735 |             }
736 | 
737 |             if (mPullListener != null) {
738 |                 mPullListener.onPullProgress(this, mPullState, percentage);
739 |             }
740 | 
741 |         }
742 | 
743 |         return consumed;
744 |     }
745 | 
746 |     @Override
747 |     public void computeScroll() {
748 |         if (mScroller.computeScrollOffset()) {
749 |             int oldY = mHeaderController.getScroll();
750 |             int y = mScroller.getCurrY();
751 | 
752 |             if (oldY != y) {
753 |                 moveBy(y - oldY);
754 |             }
755 |             ViewCompat.postInvalidateOnAnimation(this);
756 |         } else {
757 |             tryBounceBack();
758 |         }
759 |     }
760 | 
761 |     private void tryBounceBack() {
762 |         if (mHeaderController.isOverHeight()) {
763 |             mBounceAnim = ObjectAnimator.ofFloat(mHeaderController.getScroll(), 0);
764 |             mBounceAnim.setInterpolator(new ElasticOutInterpolator());
765 |             mBounceAnim.setDuration(500);
766 |             mBounceAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
767 |                 @Override
768 |                 public void onAnimationUpdate(ValueAnimator animation) {
769 |                     float deltaY = (float) animation.getAnimatedValue() - mHeaderController.getScroll();
770 |                     moveBy(deltaY);
771 |                 }
772 |             });
773 |             mBounceAnim.addListener(new SimpleAnimatorListener() {
774 |                 @Override
775 |                 public void onAnimationEnd(Animator animation) {
776 |                     mPullState = STATE_IDLE;
777 |                 }
778 |             });
779 | 
780 |             mBounceAnim.start();
781 |             mPullState = STATE_BOUNCE;
782 | 
783 |             if (mHeaderController.needSendRefresh()) {
784 |                 startRefresh();
785 |             }
786 |         } else {
787 |             mPullState = STATE_IDLE;
788 |         }
789 |     }
790 | 
791 |     public void startRefresh() {}
792 | 
793 |     @Override
794 |     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
795 |         return p instanceof LayoutParams;
796 |     }
797 | 
798 |     @Override
799 |     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
800 |         return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
801 |     }
802 | 
803 |     @Override
804 |     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
805 |         return new LayoutParams(p);
806 |     }
807 | 
808 |     @Override
809 |     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
810 |         return new LayoutParams(getContext(), attrs);
811 |     }
812 | 
813 |     public static class LayoutParams extends MarginLayoutParams {
814 | 
815 |         public LayoutParams(Context c, AttributeSet attrs) {
816 |             super(c, attrs);
817 |         }
818 | 
819 |         public LayoutParams(int width, int height) {
820 |             super(width, height);
821 |         }
822 | 
823 |         @SuppressWarnings({"unused"})
824 |         public LayoutParams(MarginLayoutParams source) {
825 |             super(source);
826 |         }
827 | 
828 |         public LayoutParams(ViewGroup.LayoutParams source) {
829 |             super(source);
830 |         }
831 |     }
832 | 
833 |     public interface OnPullListener {
834 |         void onPullProgress(PullHeaderLayout view, int state, float progress);
835 |     }
836 | }
837 | 


--------------------------------------------------------------------------------
/library/src/main/java/com/race604/flyrefresh/internal/ElasticOutInterpolator.java:
--------------------------------------------------------------------------------
 1 | package com.race604.flyrefresh.internal;
 2 | 
 3 | import android.view.animation.Interpolator;
 4 | 
 5 | /**
 6 |  * Created by jing on 15-5-22.
 7 |  */
 8 | public class ElasticOutInterpolator implements Interpolator {
 9 | 
10 |     @Override
11 |     public float getInterpolation(float t) {
12 |         if (t == 0) return 0;
13 |         if (t >= 1) return 1;
14 |         float p=.3f;
15 |         float s=p/4;
16 |         return ((float)Math.pow(2,-10*t) * (float)Math.sin( (t-s)*(2*(float)Math.PI)/p) + 1);
17 |     }
18 | }
19 | 


--------------------------------------------------------------------------------
/library/src/main/java/com/race604/flyrefresh/internal/MountainSceneDrawable.java:
--------------------------------------------------------------------------------
  1 | package com.race604.flyrefresh.internal;
  2 | 
  3 | import android.graphics.Canvas;
  4 | import android.graphics.Color;
  5 | import android.graphics.ColorFilter;
  6 | import android.graphics.Matrix;
  7 | import android.graphics.Paint;
  8 | import android.graphics.Path;
  9 | import android.graphics.PixelFormat;
 10 | import android.graphics.RectF;
 11 | import android.graphics.drawable.Drawable;
 12 | import android.support.v4.view.animation.PathInterpolatorCompat;
 13 | import android.view.animation.Interpolator;
 14 | 
 15 | import com.race604.flyrefresh.PullHeaderLayout;
 16 | 
 17 | /**
 18 |  * Created by Jing on 15/5/24.
 19 |  */
 20 | public class MountainSceneDrawable extends Drawable {
 21 | 
 22 |     private static final int COLOR_BACKGROUND = Color.parseColor("#7ECEC9");
 23 |     private static final int COLOR_MOUNTAIN_1 = Color.parseColor("#86DAD7");
 24 |     private static final int COLOR_MOUNTAIN_2 = Color.parseColor("#3C929C");
 25 |     private static final int COLOR_MOUNTAIN_3 = Color.parseColor("#3E5F73");
 26 |     private static final int COLOR_TREE_1_BRANCH = Color.parseColor("#1F7177");
 27 |     private static final int COLOR_TREE_1_BTRUNK = Color.parseColor("#0C3E48");
 28 |     private static final int COLOR_TREE_2_BRANCH = Color.parseColor("#34888F");
 29 |     private static final int COLOR_TREE_2_BTRUNK = Color.parseColor("#1B6169");
 30 |     private static final int COLOR_TREE_3_BRANCH = Color.parseColor("#57B1AE");
 31 |     private static final int COLOR_TREE_3_BTRUNK = Color.parseColor("#62A4AD");
 32 | 
 33 |     private static final int WIDTH = 240;
 34 |     private static final int HEIGHT = 180;
 35 | 
 36 |     private static final int TREE_WIDTH = 100;
 37 |     private static final int TREE_HEIGHT = 200;
 38 | 
 39 |     private Paint mMountPaint = new Paint();
 40 |     private Paint mTrunkPaint = new Paint();
 41 |     private Paint mBranchPaint = new Paint();
 42 |     private Paint mBoarderPaint = new Paint();
 43 | 
 44 |     private Path mMount1 = new Path();
 45 |     private Path mMount2 = new Path();
 46 |     private Path mMount3 = new Path();
 47 |     private Path mTrunk = new Path();
 48 |     private Path mBranch = new Path();
 49 | 
 50 |     private float mScale = 5f;
 51 |     private float mMoveFactor = 0;
 52 |     private float mBounceMax = 1;
 53 |     private float mTreeBendFactor = Float.MAX_VALUE;
 54 |     private Matrix mTransMatrix = new Matrix();
 55 | 
 56 |     public MountainSceneDrawable() {
 57 |         super();
 58 | 
 59 |         mMountPaint.setAntiAlias(true);
 60 |         mMountPaint.setStyle(Paint.Style.FILL);
 61 | 
 62 |         mTrunkPaint.setAntiAlias(true);
 63 |         mBranchPaint.setAntiAlias(true);
 64 |         mBoarderPaint.setAntiAlias(true);
 65 |         mBoarderPaint.setStyle(Paint.Style.STROKE);
 66 |         mBoarderPaint.setStrokeWidth(2);
 67 |         mBoarderPaint.setStrokeJoin(Paint.Join.ROUND);
 68 | 
 69 |         updateMountainPath(mMoveFactor);
 70 |         updateTreePath(0);
 71 |     }
 72 | 
 73 |     private void updateMountainPath(float factor) {
 74 | 
 75 |         mTransMatrix.reset();
 76 |         mTransMatrix.setScale(mScale, mScale);
 77 | 
 78 |         int offset1 = (int) (10 * factor);
 79 |         mMount1.reset();
 80 |         mMount1.moveTo(0, 95 + offset1);
 81 |         mMount1.lineTo(55, 74 + offset1);
 82 |         mMount1.lineTo(146, 104 + offset1);
 83 |         mMount1.lineTo(227, 72 + offset1);
 84 |         mMount1.lineTo(WIDTH, 80 + offset1);
 85 |         mMount1.lineTo(WIDTH, HEIGHT);
 86 |         mMount1.lineTo(0, HEIGHT);
 87 |         mMount1.close();
 88 |         mMount1.transform(mTransMatrix);
 89 | 
 90 |         int offset2 = (int) (20 * factor);
 91 |         mMount2.reset();
 92 |         mMount2.moveTo(0, 103 + offset2);
 93 |         mMount2.lineTo(67, 90 + offset2);
 94 |         mMount2.lineTo(165, 115 + offset2);
 95 |         mMount2.lineTo(221, 87 + offset2);
 96 |         mMount2.lineTo(WIDTH, 100 + offset2);
 97 |         mMount2.lineTo(WIDTH, HEIGHT);
 98 |         mMount2.lineTo(0, HEIGHT);
 99 |         mMount2.close();
100 |         mMount2.transform(mTransMatrix);
101 | 
102 |         int offset3 = (int) (30 * factor);
103 |         mMount3.reset();
104 |         mMount3.moveTo(0, 114 + offset3);
105 |         mMount3.cubicTo(30, 106 + offset3, 196, 97 + offset3, WIDTH, 104 + offset3);
106 |         mMount3.lineTo(WIDTH, HEIGHT);
107 |         mMount3.lineTo(0, HEIGHT);
108 |         mMount3.close();
109 |         mMount3.transform(mTransMatrix);
110 |     }
111 | 
112 |     private void updateTreePath(float factor) {
113 |         if (factor == mTreeBendFactor) {
114 |             return;
115 |         }
116 | 
117 |         final Interpolator interpolator = PathInterpolatorCompat.create(0.8f, -0.5f * factor);
118 | 
119 |         final float width = TREE_WIDTH;
120 |         final float height = TREE_HEIGHT;
121 | 
122 |         final float maxMove = width * 0.3f * factor;
123 |         final float trunkSize = width * 0.05f;
124 |         final float branchSize = width * 0.2f;
125 |         final float x0 = width / 2;
126 |         final float y0 = height;
127 | 
128 |         final int N = 25;
129 |         final float dp = 1f / N;
130 |         final float dy = -dp * height;
131 |         float y = y0;
132 |         float p = 0;
133 |         float[] xx = new float[N + 1];
134 |         float[] yy = new float[N + 1];
135 |         for (int i = 0; i <= N; i++) {
136 |             xx[i] = interpolator.getInterpolation(p) * maxMove + x0;
137 |             yy[i] = y;
138 | 
139 |             y += dy;
140 |             p += dp;
141 |         }
142 | 
143 |         mTrunk.reset();
144 |         mTrunk.moveTo(x0 - trunkSize, y0);
145 |         int max = (int) (N * 0.7f);
146 |         int max1 = (int) (max * 0.5f);
147 |         float diff = max - max1;
148 |         for (int i = 0; i < max; i++) {
149 |             if (i < max1) {
150 |                 mTrunk.lineTo(xx[i] - trunkSize, yy[i]);
151 |             } else {
152 |                 mTrunk.lineTo(xx[i] - trunkSize * (max - i) / diff, yy[i]);
153 |             }
154 |         }
155 | 
156 |         for (int i = max - 1; i >= 0; i--) {
157 |             if (i < max1) {
158 |                 mTrunk.lineTo(xx[i] + trunkSize, yy[i]);
159 |             } else {
160 |                 mTrunk.lineTo(xx[i] + trunkSize * (max - i) / diff, yy[i]);
161 |             }
162 |         }
163 |         mTrunk.close();
164 | 
165 |         mBranch.reset();
166 |         int min = (int) (N * 0.4f);
167 |         diff = N - min;
168 | 
169 |         mBranch.moveTo(xx[min] - branchSize, yy[min]);
170 |         mBranch.addArc(new RectF(xx[min] - branchSize, yy[min] - branchSize, xx[min] + branchSize, yy[min] + branchSize), 0f, 180f);
171 |         for (int i = min; i <= N; i++) {
172 |             float f = (i - min) / diff;
173 |             mBranch.lineTo(xx[i] - branchSize + f * f * branchSize, yy[i]);
174 |         }
175 |         for (int i = N; i >= min; i--) {
176 |             float f = (i - min) / diff;
177 |             mBranch.lineTo(xx[i] + branchSize - f * f * branchSize, yy[i]);
178 |         }
179 | 
180 |     }
181 | 
182 |     public void setMoveFactor(int state, float factor) {
183 | 
184 |         float bendFactor;
185 |         if (state == PullHeaderLayout.STATE_BOUNCE) {
186 |             if (factor < mBounceMax) {
187 |                 mBounceMax = factor;
188 |             }
189 |             bendFactor = factor;
190 |         } else {
191 |             mBounceMax = factor;
192 |             bendFactor = Math.max(0, factor);
193 |         }
194 | 
195 |         mMoveFactor = Math.max(0, mBounceMax);
196 |         updateMountainPath(mMoveFactor);
197 |         updateTreePath(bendFactor);
198 |     }
199 | 
200 |     private void drawTree(Canvas canvas, float scale, float baseX, float baseY,
201 |                           int colorTrunk, int colorBranch) {
202 |         canvas.save();
203 | 
204 |         final float dx = baseX - TREE_WIDTH * scale / 2;
205 |         final float dy = baseY - TREE_HEIGHT * scale;
206 |         canvas.translate(dx, dy);
207 |         canvas.scale(scale, scale);
208 | 
209 |         mBranchPaint.setColor(colorBranch);
210 |         canvas.drawPath(mBranch, mBranchPaint);
211 |         mTrunkPaint.setColor(colorTrunk);
212 |         canvas.drawPath(mTrunk, mTrunkPaint);
213 |         mBoarderPaint.setColor(colorTrunk);
214 |         canvas.drawPath(mBranch, mBoarderPaint);
215 | 
216 |         canvas.restore();
217 |     }
218 | 
219 |     @Override
220 |     public void draw(Canvas canvas) {
221 |         canvas.drawColor(COLOR_BACKGROUND);
222 | 
223 |         mMountPaint.setColor(COLOR_MOUNTAIN_1);
224 |         canvas.drawPath(mMount1, mMountPaint);
225 | 
226 |         canvas.save();
227 |         canvas.scale(-1, 1, getIntrinsicWidth() / 2, 0);
228 |         drawTree(canvas, 0.12f * mScale, 180 * mScale, (93 + 20 * mMoveFactor) * mScale,
229 |                 COLOR_TREE_3_BTRUNK, COLOR_TREE_3_BRANCH);
230 |         drawTree(canvas, 0.1f * mScale, 200 * mScale, (96 + 20 * mMoveFactor) * mScale,
231 |                 COLOR_TREE_3_BTRUNK, COLOR_TREE_3_BRANCH);
232 |         canvas.restore();
233 |         mMountPaint.setColor(COLOR_MOUNTAIN_2);
234 |         canvas.drawPath(mMount2, mMountPaint);
235 | 
236 |         drawTree(canvas, 0.2f * mScale, 160 * mScale, (105 + 30 * mMoveFactor) * mScale,
237 |                 COLOR_TREE_1_BTRUNK, COLOR_TREE_1_BRANCH);
238 | 
239 |         drawTree(canvas, 0.14f * mScale, 180 * mScale, (105 + 30 * mMoveFactor) * mScale,
240 |                 COLOR_TREE_2_BTRUNK ,COLOR_TREE_2_BRANCH);
241 | 
242 |         drawTree(canvas, 0.16f * mScale, 140 * mScale, (105 + 30 * mMoveFactor) * mScale,
243 |                 COLOR_TREE_2_BTRUNK ,COLOR_TREE_2_BRANCH);
244 | 
245 |         mMountPaint.setColor(COLOR_MOUNTAIN_3);
246 |         canvas.drawPath(mMount3, mMountPaint);
247 | 
248 |     }
249 | 
250 |     @Override
251 |     public void setAlpha(int alpha) {
252 |         mMountPaint.setAlpha(alpha);
253 |         mTrunkPaint.setAlpha(alpha);
254 |         mBranchPaint.setAlpha(alpha);
255 |         mBoarderPaint.setAlpha(alpha);
256 |     }
257 | 
258 |     @Override
259 |     public void setColorFilter(ColorFilter cf) {
260 |         mMountPaint.setColorFilter(cf);
261 |         mTrunkPaint.setColorFilter(cf);
262 |         mBranchPaint.setColorFilter(cf);
263 |         mBoarderPaint.setColorFilter(cf);
264 |     }
265 | 
266 |     @Override
267 |     public int getOpacity() {
268 |         return PixelFormat.OPAQUE;
269 |     }
270 | 
271 |     @Override
272 |     public int getIntrinsicHeight() {
273 |         return (int) (HEIGHT * mScale);
274 |     }
275 | 
276 |     @Override
277 |     public int getIntrinsicWidth() {
278 |         return (int) (WIDTH * mScale);
279 |     }
280 | }
281 | 


--------------------------------------------------------------------------------
/library/src/main/java/com/race604/flyrefresh/internal/MountanScenceView.java:
--------------------------------------------------------------------------------
  1 | package com.race604.flyrefresh.internal;
  2 | 
  3 | import android.annotation.TargetApi;
  4 | import android.content.Context;
  5 | import android.graphics.Canvas;
  6 | import android.graphics.Color;
  7 | import android.graphics.Matrix;
  8 | import android.graphics.Paint;
  9 | import android.graphics.Path;
 10 | import android.graphics.RectF;
 11 | import android.os.Build;
 12 | import android.support.v4.view.animation.PathInterpolatorCompat;
 13 | import android.util.AttributeSet;
 14 | import android.view.View;
 15 | import android.view.animation.Interpolator;
 16 | 
 17 | import com.race604.flyrefresh.IPullHeader;
 18 | import com.race604.flyrefresh.PullHeaderLayout;
 19 | 
 20 | /**
 21 |  * Created by jing on 15-5-28.
 22 |  */
 23 | public class MountanScenceView extends View implements IPullHeader {
 24 | 
 25 |     private static final int COLOR_BACKGROUND = Color.parseColor("#7ECEC9");
 26 |     private static final int COLOR_MOUNTAIN_1 = Color.parseColor("#86DAD7");
 27 |     private static final int COLOR_MOUNTAIN_2 = Color.parseColor("#3C929C");
 28 |     private static final int COLOR_MOUNTAIN_3 = Color.parseColor("#3E5F73");
 29 |     private static final int COLOR_TREE_1_BRANCH = Color.parseColor("#1F7177");
 30 |     private static final int COLOR_TREE_1_BTRUNK = Color.parseColor("#0C3E48");
 31 |     private static final int COLOR_TREE_2_BRANCH = Color.parseColor("#34888F");
 32 |     private static final int COLOR_TREE_2_BTRUNK = Color.parseColor("#1B6169");
 33 |     private static final int COLOR_TREE_3_BRANCH = Color.parseColor("#57B1AE");
 34 |     private static final int COLOR_TREE_3_BTRUNK = Color.parseColor("#62A4AD");
 35 | 
 36 |     private static final int WIDTH = 240;
 37 |     private static final int HEIGHT = 180;
 38 | 
 39 |     private static final int TREE_WIDTH = 100;
 40 |     private static final int TREE_HEIGHT = 200;
 41 | 
 42 |     private Paint mMountPaint = new Paint();
 43 |     private Paint mTrunkPaint = new Paint();
 44 |     private Paint mBranchPaint = new Paint();
 45 |     private Paint mBoarderPaint = new Paint();
 46 | 
 47 |     private Path mMount1 = new Path();
 48 |     private Path mMount2 = new Path();
 49 |     private Path mMount3 = new Path();
 50 |     private Path mTrunk = new Path();
 51 |     private Path mBranch = new Path();
 52 | 
 53 |     private float mScaleX = 5f;
 54 |     private float mScaleY = 5f;
 55 |     private float mMoveFactor = 0;
 56 |     private float mBounceMax = 1;
 57 |     private float mTreeBendFactor = Float.MAX_VALUE;
 58 |     private Matrix mTransMatrix = new Matrix();
 59 | 
 60 |     public MountanScenceView(Context context) {
 61 |         super(context);
 62 |         init();
 63 |     }
 64 | 
 65 |     public MountanScenceView(Context context, AttributeSet attrs) {
 66 |         super(context, attrs);
 67 |         init();
 68 |     }
 69 | 
 70 |     public MountanScenceView(Context context, AttributeSet attrs, int defStyleAttr) {
 71 |         super(context, attrs, defStyleAttr);
 72 |         init();
 73 |     }
 74 | 
 75 |     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 76 |     public MountanScenceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
 77 |         super(context, attrs, defStyleAttr, defStyleRes);
 78 |         init();
 79 |     }
 80 | 
 81 |     @Override
 82 |     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 83 |         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 84 |         final float width = getMeasuredWidth();
 85 |         final float height = getMeasuredHeight();
 86 |         mScaleX = width / WIDTH;
 87 |         mScaleY = height / HEIGHT;
 88 | 
 89 |         updateMountainPath(mMoveFactor);
 90 |         updateTreePath(mMoveFactor, true);
 91 |     }
 92 | 
 93 |     private void init() {
 94 |         mMountPaint.setAntiAlias(true);
 95 |         mMountPaint.setStyle(Paint.Style.FILL);
 96 | 
 97 |         mTrunkPaint.setAntiAlias(true);
 98 |         mBranchPaint.setAntiAlias(true);
 99 |         mBoarderPaint.setAntiAlias(true);
100 |         mBoarderPaint.setStyle(Paint.Style.STROKE);
101 |         mBoarderPaint.setStrokeWidth(2);
102 |         mBoarderPaint.setStrokeJoin(Paint.Join.ROUND);
103 | 
104 |         updateMountainPath(mMoveFactor);
105 |         updateTreePath(mMoveFactor, true);
106 |     }
107 | 
108 |     private void updateMountainPath(float factor) {
109 | 
110 |         mTransMatrix.reset();
111 |         mTransMatrix.setScale(mScaleX, mScaleY);
112 | 
113 |         int offset1 = (int) (10 * factor);
114 |         mMount1.reset();
115 |         mMount1.moveTo(0, 95 + offset1);
116 |         mMount1.lineTo(55, 74 + offset1);
117 |         mMount1.lineTo(146, 104 + offset1);
118 |         mMount1.lineTo(227, 72 + offset1);
119 |         mMount1.lineTo(WIDTH, 80 + offset1);
120 |         mMount1.lineTo(WIDTH, HEIGHT);
121 |         mMount1.lineTo(0, HEIGHT);
122 |         mMount1.close();
123 |         mMount1.transform(mTransMatrix);
124 | 
125 |         int offset2 = (int) (20 * factor);
126 |         mMount2.reset();
127 |         mMount2.moveTo(0, 103 + offset2);
128 |         mMount2.lineTo(67, 90 + offset2);
129 |         mMount2.lineTo(165, 115 + offset2);
130 |         mMount2.lineTo(221, 87 + offset2);
131 |         mMount2.lineTo(WIDTH, 100 + offset2);
132 |         mMount2.lineTo(WIDTH, HEIGHT);
133 |         mMount2.lineTo(0, HEIGHT);
134 |         mMount2.close();
135 |         mMount2.transform(mTransMatrix);
136 | 
137 |         int offset3 = (int) (30 * factor);
138 |         mMount3.reset();
139 |         mMount3.moveTo(0, 114 + offset3);
140 |         mMount3.cubicTo(30, 106 + offset3, 196, 97 + offset3, WIDTH, 104 + offset3);
141 |         mMount3.lineTo(WIDTH, HEIGHT);
142 |         mMount3.lineTo(0, HEIGHT);
143 |         mMount3.close();
144 |         mMount3.transform(mTransMatrix);
145 |     }
146 | 
147 |     private void updateTreePath(float factor, boolean force) {
148 |         if (factor == mTreeBendFactor && !force) {
149 |             return;
150 |         }
151 | 
152 |         final Interpolator interpolator = PathInterpolatorCompat.create(0.8f, -0.5f * factor);
153 | 
154 |         final float width = TREE_WIDTH;
155 |         final float height = TREE_HEIGHT;
156 | 
157 |         final float maxMove = width * 0.3f * factor;
158 |         final float trunkSize = width * 0.05f;
159 |         final float branchSize = width * 0.2f;
160 |         final float x0 = width / 2;
161 |         final float y0 = height;
162 | 
163 |         final int N = 25;
164 |         final float dp = 1f / N;
165 |         final float dy = -dp * height;
166 |         float y = y0;
167 |         float p = 0;
168 |         float[] xx = new float[N + 1];
169 |         float[] yy = new float[N + 1];
170 |         for (int i = 0; i <= N; i++) {
171 |             xx[i] = interpolator.getInterpolation(p) * maxMove + x0;
172 |             yy[i] = y;
173 | 
174 |             y += dy;
175 |             p += dp;
176 |         }
177 | 
178 |         mTrunk.reset();
179 |         mTrunk.moveTo(x0 - trunkSize, y0);
180 |         int max = (int) (N * 0.7f);
181 |         int max1 = (int) (max * 0.5f);
182 |         float diff = max - max1;
183 |         for (int i = 0; i < max; i++) {
184 |             if (i < max1) {
185 |                 mTrunk.lineTo(xx[i] - trunkSize, yy[i]);
186 |             } else {
187 |                 mTrunk.lineTo(xx[i] - trunkSize * (max - i) / diff, yy[i]);
188 |             }
189 |         }
190 | 
191 |         for (int i = max - 1; i >= 0; i--) {
192 |             if (i < max1) {
193 |                 mTrunk.lineTo(xx[i] + trunkSize, yy[i]);
194 |             } else {
195 |                 mTrunk.lineTo(xx[i] + trunkSize * (max - i) / diff, yy[i]);
196 |             }
197 |         }
198 |         mTrunk.close();
199 | 
200 |         mBranch.reset();
201 |         int min = (int) (N * 0.4f);
202 |         diff = N - min;
203 | 
204 |         mBranch.moveTo(xx[min] - branchSize, yy[min]);
205 |         mBranch.addArc(new RectF(xx[min] - branchSize, yy[min] - branchSize, xx[min] + branchSize, yy[min] + branchSize), 0f, 180f);
206 |         for (int i = min; i <= N; i++) {
207 |             float f = (i - min) / diff;
208 |             mBranch.lineTo(xx[i] - branchSize + f * f * branchSize, yy[i]);
209 |         }
210 |         for (int i = N; i >= min; i--) {
211 |             float f = (i - min) / diff;
212 |             mBranch.lineTo(xx[i] + branchSize - f * f * branchSize, yy[i]);
213 |         }
214 | 
215 |     }
216 | 
217 |     @Override
218 |     public void onPullProgress(PullHeaderLayout parent, int state, float factor) {
219 | 
220 |         float bendFactor;
221 |         if (state == PullHeaderLayout.STATE_BOUNCE) {
222 |             if (factor < mBounceMax) {
223 |                 mBounceMax = factor;
224 |             }
225 |             bendFactor = factor;
226 |         } else {
227 |             mBounceMax = factor;
228 |             bendFactor = Math.max(0, factor);
229 |         }
230 | 
231 |         mMoveFactor = Math.max(0, mBounceMax);
232 |         updateMountainPath(mMoveFactor);
233 |         updateTreePath(bendFactor, false);
234 | 
235 |         postInvalidate();
236 |     }
237 | 
238 |     private void drawTree(Canvas canvas, float scale, float baseX, float baseY,
239 |                           int colorTrunk, int colorBranch) {
240 |         canvas.save();
241 | 
242 |         final float dx = baseX - TREE_WIDTH * scale / 2;
243 |         final float dy = baseY - TREE_HEIGHT * scale;
244 |         canvas.translate(dx, dy);
245 |         canvas.scale(scale, scale);
246 | 
247 |         mBranchPaint.setColor(colorBranch);
248 |         canvas.drawPath(mBranch, mBranchPaint);
249 |         mTrunkPaint.setColor(colorTrunk);
250 |         canvas.drawPath(mTrunk, mTrunkPaint);
251 |         mBoarderPaint.setColor(colorTrunk);
252 |         canvas.drawPath(mBranch, mBoarderPaint);
253 | 
254 |         canvas.restore();
255 |     }
256 | 
257 |     @Override
258 |     protected void onDraw(Canvas canvas) {
259 |         super.onDraw(canvas);
260 |         canvas.drawColor(COLOR_BACKGROUND);
261 | 
262 |         mMountPaint.setColor(COLOR_MOUNTAIN_1);
263 |         canvas.drawPath(mMount1, mMountPaint);
264 | 
265 |         canvas.save();
266 |         canvas.scale(-1, 1, getWidth() / 2, 0);
267 |         drawTree(canvas, 0.12f * mScaleX, 180 * mScaleX, (93 + 20 * mMoveFactor) * mScaleY,
268 |                 COLOR_TREE_3_BTRUNK, COLOR_TREE_3_BRANCH);
269 |         drawTree(canvas, 0.1f * mScaleX, 200 * mScaleX, (96 + 20 * mMoveFactor) * mScaleY,
270 |                 COLOR_TREE_3_BTRUNK, COLOR_TREE_3_BRANCH);
271 |         canvas.restore();
272 |         mMountPaint.setColor(COLOR_MOUNTAIN_2);
273 |         canvas.drawPath(mMount2, mMountPaint);
274 | 
275 |         drawTree(canvas, 0.2f * mScaleX, 160 * mScaleX, (105 + 30 * mMoveFactor) * mScaleY,
276 |                 COLOR_TREE_1_BTRUNK, COLOR_TREE_1_BRANCH);
277 | 
278 |         drawTree(canvas, 0.14f * mScaleX, 180 * mScaleX, (105 + 30 * mMoveFactor) * mScaleY,
279 |                 COLOR_TREE_2_BTRUNK ,COLOR_TREE_2_BRANCH);
280 | 
281 |         drawTree(canvas, 0.16f * mScaleX, 140 * mScaleX, (105 + 30 * mMoveFactor) * mScaleY,
282 |                 COLOR_TREE_2_BTRUNK ,COLOR_TREE_2_BRANCH);
283 | 
284 |         mMountPaint.setColor(COLOR_MOUNTAIN_3);
285 |         canvas.drawPath(mMount3, mMountPaint);
286 |     }
287 | }
288 | 


--------------------------------------------------------------------------------
/library/src/main/java/com/race604/flyrefresh/internal/RotatableDrawable.java:
--------------------------------------------------------------------------------
 1 | package com.race604.flyrefresh.internal;
 2 | 
 3 | import android.graphics.Canvas;
 4 | import android.graphics.Rect;
 5 | import android.graphics.drawable.Drawable;
 6 | import android.graphics.drawable.LayerDrawable;
 7 | 
 8 | /**
 9 |  * Created by jing on 15-5-21.
10 |  */
11 | public class RotatableDrawable extends LayerDrawable {
12 | 
13 |     private float mDegree = 0;
14 | 
15 |     /**
16 |      * Create a new layer drawable with the list of specified layers.
17 |      *
18 |      * @param layers A list of drawables to use as layers in this new drawable.
19 |      */
20 |     public RotatableDrawable(Drawable[] layers) {
21 |         super(layers);
22 |     }
23 | 
24 |     public RotatableDrawable(Drawable drawable) {
25 |         this(new Drawable[]{drawable});
26 |     }
27 | 
28 |     public void setDegree(float degree) {
29 |         mDegree = degree;
30 |     }
31 | 
32 |     public float getDegree() {
33 |         return mDegree;
34 |     }
35 | 
36 |     @Override
37 |     public void draw(Canvas canvas) {
38 |         canvas.save();
39 |         Rect bounds = getBounds();
40 |         canvas.rotate(mDegree, bounds.centerX(), bounds.centerY());
41 |         super.draw(canvas);
42 |         canvas.restore();
43 |     }
44 | 
45 | }
46 | 


--------------------------------------------------------------------------------
/library/src/main/java/com/race604/flyrefresh/internal/SimpleAnimatorListener.java:
--------------------------------------------------------------------------------
 1 | package com.race604.flyrefresh.internal;
 2 | 
 3 | import android.animation.Animator;
 4 | 
 5 | /**
 6 |  * Created by Jing on 15/5/23.
 7 |  */
 8 | public class SimpleAnimatorListener implements Animator.AnimatorListener {
 9 |     @Override
10 |     public void onAnimationStart(Animator animation) {
11 | 
12 |     }
13 | 
14 |     @Override
15 |     public void onAnimationEnd(Animator animation) {
16 | 
17 |     }
18 | 
19 |     @Override
20 |     public void onAnimationCancel(Animator animation) {
21 | 
22 |     }
23 | 
24 |     @Override
25 |     public void onAnimationRepeat(Animator animation) {
26 | 
27 |     }
28 | }
29 | 


--------------------------------------------------------------------------------
/library/src/main/java/com/race604/flyrefresh/internal/TreeDrawable.java:
--------------------------------------------------------------------------------
  1 | package com.race604.flyrefresh.internal;
  2 | 
  3 | import android.graphics.Canvas;
  4 | import android.graphics.Color;
  5 | import android.graphics.ColorFilter;
  6 | import android.graphics.Paint;
  7 | import android.graphics.Path;
  8 | import android.graphics.RectF;
  9 | import android.graphics.drawable.Drawable;
 10 | import android.support.v4.view.animation.PathInterpolatorCompat;
 11 | import android.util.FloatMath;
 12 | import android.view.animation.Interpolator;
 13 | 
 14 | /**
 15 |  * Created by jing on 15-5-22.
 16 |  */
 17 | public class TreeDrawable extends Drawable {
 18 | 
 19 |     private static final int DEFAULT_WIDTH = 100;
 20 |     private static final int DEFAULT_HEIGHT = 200;
 21 |     private float mSizeFactor = 1;
 22 |     private float mBendScale = 0f;
 23 | 
 24 |     private Paint mTrunkPaint = new Paint();
 25 |     private Paint mBranchPaint = new Paint();
 26 |     private Path mTrunk = new Path();
 27 |     private Path mBranch = new Path();
 28 | 
 29 |     private Path mBaseLine = new Path();
 30 |     private Paint mBaseLinePaint = new Paint();
 31 | 
 32 |     public TreeDrawable() {
 33 |         super();
 34 | 
 35 |         mTrunkPaint.setAntiAlias(true);
 36 |         mTrunkPaint.setStrokeWidth(0.1f);
 37 |         mTrunkPaint.setColor(Color.DKGRAY);
 38 | 
 39 |         mBranchPaint.setAntiAlias(true);
 40 |         mBranchPaint.setColor(Color.GREEN);
 41 | 
 42 |         mBaseLinePaint.setAntiAlias(true);
 43 |         mBaseLinePaint.setColor(Color.RED);
 44 |         mBaseLinePaint.setStrokeWidth(0.5f);
 45 |         mBaseLinePaint.setStyle(Paint.Style.STROKE);
 46 |         updateTree();
 47 |     }
 48 | 
 49 |     public void setBendScale(float scale) {
 50 |         mBendScale = scale;
 51 |         updateTree();
 52 |     }
 53 | 
 54 |     public void updateTree() {
 55 | 
 56 |         final Interpolator interpolator = PathInterpolatorCompat.create(0.8f, -0.6f * mBendScale);
 57 | 
 58 |         final float width = DEFAULT_WIDTH * mSizeFactor;
 59 |         final float height = DEFAULT_HEIGHT * mSizeFactor;
 60 | 
 61 |         final float maxMove = width * 0.45f * mBendScale;
 62 |         final float trunkSize = width * 0.05f;
 63 |         final float branchSize = width * 0.2f;
 64 |         final float x0 = width / 2;
 65 |         final float y0 = height;
 66 | 
 67 |         mBaseLine.reset();
 68 |         mBaseLine.moveTo(x0, y0);
 69 |         final int N = 50;
 70 |         final float dp = 1f / N;
 71 |         final float dy = -dp * height;
 72 |         float y = y0;
 73 |         float p = 0;
 74 |         float[] xx = new float[N + 1];
 75 |         float[] yy = new float[N + 1];
 76 |         for (int i = 0; i <= N; i++) {
 77 |             xx[i] = interpolator.getInterpolation(p) * maxMove + x0;
 78 |             yy[i] = y;
 79 |             mBaseLine.lineTo(xx[i], yy[i]);
 80 | 
 81 |             y += dy;
 82 |             p += dp;
 83 |         }
 84 | 
 85 |         mTrunk.reset();
 86 |         mTrunk.moveTo(x0 - trunkSize, y0);
 87 |         int max = (int) (N * 0.6f);
 88 |         int max1 = (int) (max * 0.6f);
 89 |         float diff = max - max1;
 90 |         for (int i = 0; i < max; i++) {
 91 |             if (i < max1) {
 92 |                 mTrunk.lineTo(xx[i] - trunkSize, yy[i]);
 93 |             } else {
 94 |                 mTrunk.lineTo(xx[i] - trunkSize * (max - i) / diff, yy[i]);
 95 |             }
 96 |         }
 97 | 
 98 |         for (int i = max - 1; i >= 0; i--) {
 99 |             if (i < max1) {
100 |                 mTrunk.lineTo(xx[i] + trunkSize, yy[i]);
101 |             } else {
102 |                 mTrunk.lineTo(xx[i] + trunkSize * (max - i) / diff, yy[i]);
103 |             }
104 |         }
105 |         mTrunk.close();
106 | 
107 |         mBranch.reset();
108 |         int min = (int) (N * 0.4f);
109 |         diff = N - min;
110 | 
111 |         mBranch.addArc(new RectF(xx[min] - branchSize, yy[min] - branchSize, xx[min] + branchSize, yy[min] + branchSize), 0f, 180f);
112 |         for (int i = min; i <= N; i++) {
113 |             mBranch.lineTo(xx[i] + branchSize - ((i - min) / diff) * branchSize, yy[i]);
114 |         }
115 |         for (int i = N; i >= min; i--) {
116 |             mBranch.lineTo(xx[i] - branchSize + ((i - min) / diff) * branchSize, yy[i]);
117 |         }
118 | 
119 |         mBranch.close();
120 |     }
121 | 
122 |     @Override
123 |     public int getIntrinsicWidth() {
124 |         return (int) (DEFAULT_WIDTH * mSizeFactor);
125 |     }
126 | 
127 |     @Override
128 |     public int getIntrinsicHeight() {
129 |         return (int) (DEFAULT_HEIGHT * mSizeFactor);
130 |     }
131 | 
132 |     @Override
133 |     public void draw(Canvas canvas) {
134 |         canvas.drawPath(mBranch, mBranchPaint);
135 |         canvas.drawPath(mTrunk, mTrunkPaint);
136 | 
137 |         canvas.drawPath(mBaseLine, mBaseLinePaint);
138 |     }
139 | 
140 |     @Override
141 |     public void setAlpha(int alpha) {
142 | 
143 |     }
144 | 
145 |     @Override
146 |     public void setColorFilter(ColorFilter cf) {
147 | 
148 |     }
149 | 
150 |     @Override
151 |     public int getOpacity() {
152 |         return 0;
153 |     }
154 | 
155 |     private static float getQuadPoint(float x0, float y0, float x1, float y1,
156 |                                       float x2, float y2, float x) {
157 |         float t;
158 |         float a = (x0 - 2 * x1 + x2);
159 |         float b = 2 * (x1 - x0);
160 |         float c = x1 - x;
161 |         if (a < 1e-10) {
162 |             t = (x1 - x) / (2 * (x1 - x0));
163 |         } else {
164 |             t = (float) ((Math.sqrt(b * b - 4 * a * c) + b) / (2 * a));
165 |         }
166 | 
167 |         float t1 = 1 - t;
168 |         return t1 * t1 * y0 + 2 * t * t1 * y1 + t * t * y2;
169 |     }
170 | 
171 | }
172 | 


--------------------------------------------------------------------------------
/library/src/main/java/com/race604/utils/UIUtils.java:
--------------------------------------------------------------------------------
 1 | package com.race604.utils;
 2 | 
 3 | import android.content.Context;
 4 | import android.content.res.Resources;
 5 | import android.graphics.Color;
 6 | import android.support.v4.view.ViewCompat;
 7 | import android.util.TypedValue;
 8 | import android.view.View;
 9 | 
10 | /**
11 |  * Created by Jing on 15/5/18.
12 |  */
13 | public class UIUtils {
14 | 
15 |     public static final int dpToPx(int dp) {
16 |         return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
17 |     }
18 | 
19 |     public static int lighterColor(int color, float factor) {
20 |         int red = (int) ((Color.red(color) * (1 - factor) / 255 + factor) * 255);
21 |         int green = (int) ((Color.green(color) * (1 - factor) / 255 + factor) * 255);
22 |         int blue = (int) ((Color.blue(color) * (1 - factor) / 255 + factor) * 255);
23 |         return Color.argb(Color.alpha(color), red, green, blue);
24 |     }
25 | 
26 |     public static int darkerColor(int color, float factor) {
27 |         int a = Color.alpha( color );
28 |         int r = Color.red( color );
29 |         int g = Color.green( color );
30 |         int b = Color.blue( color );
31 | 
32 |         return Color.argb( a,
33 |                 Math.max( (int)(r * factor), 0 ),
34 |                 Math.max( (int)(g * factor), 0 ),
35 |                 Math.max( (int)(b * factor), 0 ) );
36 |     }
37 | 
38 |     public static int getThemeColor(Context ctx, int attr) {
39 |         TypedValue tv = new TypedValue();
40 |         if (ctx.getTheme().resolveAttribute(attr, tv, true)) {
41 |             return tv.data;
42 |         }
43 |         return 0;
44 |     }
45 | 
46 |     /**
47 |      * helper method to get the color by attr (which is defined in the style) or by resource.
48 |      *
49 |      * @param ctx
50 |      * @param attr
51 |      * @param res
52 |      * @return
53 |      */
54 |     public static int getThemeColorFromAttrOrRes(Context ctx, int attr, int res) {
55 |         int color = getThemeColor(ctx, attr);
56 |         if (color == 0) {
57 |             color = ctx.getResources().getColor(res);
58 |         }
59 |         return color;
60 |     }
61 | 
62 |     public static void clearAnimator(View v) {
63 |         ViewCompat.setAlpha(v, 1);
64 |         ViewCompat.setScaleY(v, 1);
65 |         ViewCompat.setScaleX(v, 1);
66 |         ViewCompat.setTranslationY(v, 0);
67 |         ViewCompat.setTranslationX(v, 0);
68 |         ViewCompat.setRotation(v, 0);
69 |         ViewCompat.setRotationY(v, 0);
70 |         ViewCompat.setRotationX(v, 0);
71 |         // @TODO https://code.google.com/p/android/issues/detail?id=80863
72 |         // ViewCompat.setPivotY(v, v.getMeasuredHeight() / 2);
73 |         v.setPivotY(v.getMeasuredHeight() / 2);
74 |         ViewCompat.setPivotX(v, v.getMeasuredWidth() / 2);
75 |         ViewCompat.animate(v).setInterpolator(null);
76 |     }
77 | }
78 | 


--------------------------------------------------------------------------------
/library/src/main/res/mipmap-xxhdpi/ic_send.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/race604/FlyRefresh/b3049ab0ec2053b23c6a921ddd23bb6e40820ce9/library/src/main/res/mipmap-xxhdpi/ic_send.png


--------------------------------------------------------------------------------
/library/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <resources>
 3 |     <declare-styleable name="PullHeaderLayout">
 4 |         <attr name="phl_header_height" format="dimension" />
 5 |         <attr name="phl_header_expand_height" format="dimension" />
 6 |         <attr name="phl_header_shrink_height" format="dimension" />
 7 |         <attr name="phl_header" format="reference" />
 8 |         <attr name="phl_content" format="reference" />
 9 |         <attr name="phl_action" format="reference" />
10 |     </declare-styleable>
11 | </resources>


--------------------------------------------------------------------------------
/library/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <resources>
 3 |     <color name="primary">#00BCD4</color>
 4 |     <color name="primary_dark">#0097A7</color>
 5 |     <color name="primary_light">#B2EBF2</color>
 6 |     <color name="accent">#61B3DD</color>
 7 |     <color name="primary_text">#212121</color>
 8 |     <color name="secondary_text">#727272</color>
 9 |     <color name="icons">#FFFFFF</color>
10 |     <color name="divider">#B6B6B6</color>
11 | </resources>


--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 | <resources>
2 |     <string name="app_name">Library</string>
3 | </resources>
4 | 


--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 | 


--------------------------------------------------------------------------------