├── .gitignore
├── .idea
├── .name
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── gradle.xml
├── libraries
│ ├── animated_vector_drawable_23_2_1.xml
│ ├── appcompat_v7_23_2_1.xml
│ ├── hamcrest_core_1_3.xml
│ ├── junit_4_12.xml
│ ├── recyclerview_v7_23_2_1.xml
│ ├── support_annotations_23_2_1.xml
│ ├── support_v4_23_2_1.xml
│ └── support_vector_drawable_23_2_1.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── .travis.yml
├── Buff.iml
├── LICENSE
├── README.md
├── app
├── .gitignore
├── app.iml
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── github
│ │ └── nukc
│ │ └── sample
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── github
│ │ │ └── nukc
│ │ │ └── sample
│ │ │ ├── InsideActivity.java
│ │ │ ├── MainActivity.java
│ │ │ ├── OutsideActivity.java
│ │ │ ├── rain
│ │ │ ├── Rain.java
│ │ │ ├── RainRefreshView.java
│ │ │ ├── RainView.java
│ │ │ └── WaveView.java
│ │ │ └── sun
│ │ │ ├── BaseRefreshView.java
│ │ │ ├── SunImageView.java
│ │ │ └── SunRefreshView.java
│ └── res
│ │ ├── drawable-xxxhdpi
│ │ ├── bg.png
│ │ ├── buildings.png
│ │ ├── sky.png
│ │ └── sun.png
│ │ ├── layout
│ │ ├── activity_inside.xml
│ │ ├── activity_main.xml
│ │ ├── activity_outside.xml
│ │ ├── list_item.xml
│ │ └── view_loading.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── github
│ └── nukc
│ └── buff
│ └── ExampleUnitTest.java
├── art
├── inside.gif
├── outside.gif
└── showViews.gif
├── bintray.gradle
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── library
├── .gitignore
├── build.gradle
├── library.iml
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── github
│ │ └── nukc
│ │ └── buff
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── github
│ │ │ └── nukc
│ │ │ └── buff
│ │ │ ├── FooterRefreshView.java
│ │ │ ├── IPullUIHandler.java
│ │ │ ├── LoadRetryLayout.java
│ │ │ ├── PageLayout.java
│ │ │ ├── TextRefreshView.java
│ │ │ └── Utils.java
│ └── res
│ │ ├── layout
│ │ ├── view_empty.xml
│ │ ├── view_loading.xml
│ │ ├── view_refresh_footer.xml
│ │ └── view_retry.xml
│ │ └── values
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ └── strings.xml
│ └── test
│ └── java
│ └── com
│ └── github
│ └── nukc
│ └── library
│ └── ExampleUnitTest.java
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 |
19 | # Local configuration file (sdk path, etc)
20 | local.properties
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Log Files
26 | *.log
27 |
28 | # Android Studio Navigation editor temp files
29 | .navigation/
30 |
31 | # Android Studio captures folder
32 | captures/
33 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | Buff
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.idea/libraries/animated_vector_drawable_23_2_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/appcompat_v7_23_2_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/libraries/hamcrest_core_1_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/junit_4_12.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/recyclerview_v7_23_2_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/libraries/support_annotations_23_2_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/support_v4_23_2_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/libraries/support_vector_drawable_23_2_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 |
3 | notifications:
4 | email: false
5 |
6 | sudo: false
7 |
8 | android:
9 | components:
10 | - tools
11 | - build-tools-23.0.2
12 | - android-23
13 | - extra-android-m2repository
14 | - extra-android-support
15 |
16 | before_install:
17 | - chmod +x gradlew
18 |
19 | script:
20 | - ./gradlew assembleRelease
21 |
22 | before_deploy:
23 | - mv app/build/outputs/apk/app-release.apk app/build/outputs/apk/buff.apk
24 |
25 | deploy:
26 | provider: releases
27 | api_key:
28 | secure: 7f4dc45a19f742dce39cbe4d1e5852fb588593fd
29 | file: app/build/outputs/apk/buff.apk
30 | skip_cleanup: true
31 | on:
32 | tags: true
--------------------------------------------------------------------------------
/Buff.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Nukc
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Buff
2 | [](https://bintray.com/nukc/maven/Buff/_latestVersion)
3 | [](https://travis-ci.org/nukc/Buff)
4 |
5 | Please choose another library:
6 | - refresh -- [DistRefreshLayout](https://github.com/nukc/DistRefreshLayout)
7 | - load more -- [RecyclerAdapter](https://github.com/nukc/RecyclerAdapter)
8 | - show state -- [StateView](https://github.com/nukc/StateView)
9 |
10 | The buff can reduce your development time, has the following features (support api >= 8):
11 | > - pull down to refresh
12 | > - pull up to load more
13 | > - show loading view
14 | > - show retry view (show it when requesting data failed and setOnRetryClickListener)
15 | > - show empty view (when no data)
16 |
17 |
18 | ### show views
19 |
20 | * loading view and retry view
21 |
22 |
23 |
24 | ### refresh / load more
25 |
26 | * Mode: outside (default)
27 |
28 |
29 |
30 | * Mode: inside
31 |
32 | the sample refresh is [Yalantis/Phoenix](https://github.com/Yalantis/Phoenix) style
33 |
34 | if you want to use the refresh/loadmore style, you can see the sample project
35 |
36 |
37 |
38 | ## Usage
39 |
40 | add the dependency to your build.gradle:
41 | ```
42 | dependencies {
43 | compile 'com.github.nukc.buff:library:1.0'
44 | }
45 | ```
46 |
47 | add the PageLayout weight
48 |
49 | ```xml
50 |
54 |
55 |
59 |
60 |
61 | ```
62 |
63 | set in java code
64 |
65 | ```java
66 |
67 | //only pull down to refresh
68 | mPageLayout.setOnRefreshListener(new PageLayout.OnRefreshListener() {
69 | @Override
70 | public void onRefresh() {
71 |
72 | }
73 | });
74 |
75 |
76 | //if you add app:loadRetryEnabled="true" in layout.xml
77 | mPageLayout.setOnRefreshAndLoadMoreListener(new PageLayout.OnRefreshAndLoadMoreListener() {
78 | @Override
79 | public void onRefresh() {
80 |
81 | }
82 |
83 | @Override
84 | public void onLoadMore() {
85 |
86 | }
87 | });
88 |
89 | //refresh complete
90 | mPageLayout.setRefreshing(false);
91 |
92 | //load more complete
93 | mPageLayout.setLoadingMore(false);
94 |
95 | ```
96 |
97 | set retry view click listener
98 |
99 | ```java
100 | mPageLayout.setOnRetryClickListener(new LoadRetryLayout.OnRetryClickListener() {
101 | @Override
102 | public void onRetryClick(View v) {
103 |
104 | }
105 | });
106 |
107 | //Called when requesting data successfully (show content)
108 | mPageLayout.onRequestSuccess();
109 |
110 | //Called when requesting data failed (show retry view)
111 | mPageLayout.onRequestFailure();
112 |
113 | ```
114 |
115 |
116 |
117 | ## Customization
118 |
119 | you can implements IPullUIHandler to customize refresh/loadmore style:
120 |
121 | ```java
122 | public interface IPullUIHandler {
123 | void onPulling(float scrollTop, int targetY, int totalDragDistance);
124 |
125 | void onRefresh(int totalDragDistance);
126 |
127 | void onStop(float dragPercent);
128 | }
129 |
130 | ```
131 |
132 | can set own view
133 |
134 | ```java
135 | mPageLayout.setHeaderView();
136 | mPageLayout.setFooterView();
137 |
138 | mPageLayout.getLoadRetryLayout().setLoadingView( View/LayoutRes );
139 | //setRetryView() setEmptyView()
140 |
141 | ```
142 |
143 | ## Custom Attribute
144 |
145 | ```xml
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 | ```
157 |
158 | ## Thanks
159 | * [SwipeRefreshLayout](https://developer.android.com/reference/android/support/v4/widget/SwipeRefreshLayout.html)
160 | * [Yalantis/Phoenix](https://github.com/Yalantis/Phoenix)
161 |
162 | ## License
163 |
164 | The MIT License (MIT)
165 |
166 | Copyright (c) 2016 Nukc
167 |
168 | Permission is hereby granted, free of charge, to any person obtaining a copy
169 | of this software and associated documentation files (the "Software"), to deal
170 | in the Software without restriction, including without limitation the rights
171 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
172 | copies of the Software, and to permit persons to whom the Software is
173 | furnished to do so, subject to the following conditions:
174 |
175 | The above copyright notice and this permission notice shall be included in all
176 | copies or substantial portions of the Software.
177 |
178 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
179 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
180 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
181 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
182 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
183 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
184 | SOFTWARE.
185 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | generateDebugSources
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.2"
6 |
7 | defaultConfig {
8 | applicationId "com.github.nukc.sample"
9 | minSdkVersion 14
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 | lintOptions {
22 | abortOnError false
23 | }
24 | }
25 |
26 | dependencies {
27 | compile fileTree(dir: 'libs', include: ['*.jar'])
28 | testCompile 'junit:junit:4.12'
29 | compile 'com.android.support:appcompat-v7:23.2.1'
30 | compile 'com.android.support:recyclerview-v7:23.2.1'
31 | compile project(':library')
32 | }
33 |
--------------------------------------------------------------------------------
/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 F:\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/github/nukc/sample/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.sample;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/nukc/sample/InsideActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.sample;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.widget.ArrayAdapter;
6 | import android.widget.ListView;
7 |
8 | import com.github.nukc.buff.PageLayout;
9 | import com.github.nukc.sample.rain.RainRefreshView;
10 | import com.github.nukc.sample.sun.SunImageView;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | public class InsideActivity extends AppCompatActivity {
16 |
17 | private static final int TIME_DELAY = 2000;
18 |
19 | private PageLayout mPageLayout;
20 |
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setContentView(R.layout.activity_inside);
25 |
26 | setupPageLayout();
27 |
28 | List strings = new ArrayList<>();
29 | for (int i = 0; i < 13; i++){
30 | strings.add("Item: " + i);
31 | }
32 |
33 | ListView listView = (ListView) findViewById(R.id.listView);
34 | listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, strings));
35 | }
36 |
37 |
38 | private void setupPageLayout(){
39 | mPageLayout = (PageLayout) findViewById(R.id.pageLayout);
40 |
41 | mPageLayout.setHeaderView(new SunImageView(this, mPageLayout));
42 | mPageLayout.setFooterView(new RainRefreshView(this));
43 |
44 | mPageLayout.setOnRefreshAndLoadMoreListener(new PageLayout.OnRefreshAndLoadMoreListener() {
45 | @Override
46 | public void onRefresh() {
47 | mPageLayout.postDelayed(new Runnable() {
48 | @Override
49 | public void run() {
50 | mPageLayout.setRefreshing(false);
51 | }
52 | }, TIME_DELAY);
53 | }
54 |
55 | @Override
56 | public void onLoadMore() {
57 | mPageLayout.postDelayed(new Runnable() {
58 | @Override
59 | public void run() {
60 | mPageLayout.setLoadingMore(false);
61 | }
62 | }, TIME_DELAY);
63 | }
64 | });
65 | }
66 |
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/nukc/sample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.sample;
2 |
3 | import android.content.Intent;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.os.Bundle;
6 | import android.view.View;
7 |
8 | public class MainActivity extends AppCompatActivity implements View.OnClickListener {
9 |
10 | @Override
11 | protected void onCreate(Bundle savedInstanceState) {
12 | super.onCreate(savedInstanceState);
13 | setContentView(R.layout.activity_main);
14 |
15 | findViewById(R.id.btnOutside).setOnClickListener(this);
16 | findViewById(R.id.btnInside).setOnClickListener(this);
17 | }
18 |
19 | @Override
20 | public void onClick(View v) {
21 | switch (v.getId()){
22 | case R.id.btnOutside:
23 | startActivity(new Intent(this, OutsideActivity.class));
24 | break;
25 | case R.id.btnInside:
26 | startActivity(new Intent(this, InsideActivity.class));
27 | break;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/nukc/sample/OutsideActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.sample;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.TextView;
12 |
13 | import com.github.nukc.buff.LoadRetryLayout;
14 | import com.github.nukc.buff.PageLayout;
15 |
16 | public class OutsideActivity extends AppCompatActivity {
17 |
18 | private static final int TIME_DELAY = 2000;
19 | private PageLayout mPageLayout;
20 |
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setContentView(R.layout.activity_outside);
25 |
26 | int colors[] = {
27 | android.R.color.background_light,
28 | R.color.yellow_a200, R.color.yellow_500,
29 | R.color.amber_a200, R.color.amber_500,
30 | R.color.orange_a200, R.color.orange_500,
31 | R.color.orange_deep_a200, R.color.orange_deep_500
32 | };
33 |
34 | RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
35 | LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
36 | recyclerView.setLayoutManager(linearLayoutManager);
37 | SampleAdapter sampleAdapter = new SampleAdapter(this, colors);
38 | recyclerView.setAdapter(sampleAdapter);
39 |
40 | setupPageLayout();
41 |
42 | mPageLayout.postDelayed(new Runnable() {
43 | @Override
44 | public void run() {
45 | //调用后显示点击重试界面(请求失败后)
46 | mPageLayout.onRequestFailure();
47 | }
48 | }, TIME_DELAY);
49 |
50 | }
51 |
52 | private void setupPageLayout(){
53 | mPageLayout = (PageLayout) findViewById(R.id.pageLayout);
54 |
55 | //设置自定义正在加载
56 | mPageLayout.getLoadRetryLayout().setLoadingView(R.layout.view_loading);
57 |
58 | mPageLayout.setOnRefreshAndLoadMoreListener(new PageLayout.OnRefreshAndLoadMoreListener() {
59 | @Override
60 | public void onRefresh() {
61 | mPageLayout.postDelayed(new Runnable() {
62 | @Override
63 | public void run() {
64 | mPageLayout.setRefreshing(false);
65 | }
66 | }, TIME_DELAY);
67 | }
68 |
69 | @Override
70 | public void onLoadMore() {
71 | mPageLayout.postDelayed(new Runnable() {
72 | @Override
73 | public void run() {
74 | mPageLayout.setLoadingMore(false);
75 | }
76 | }, TIME_DELAY);
77 | }
78 | });
79 |
80 | mPageLayout.setOnRetryClickListener(new LoadRetryLayout.OnRetryClickListener() {
81 | @Override
82 | public void onRetryClick(View v) {
83 | mPageLayout.postDelayed(new Runnable() {
84 | @Override
85 | public void run() {
86 | //调用后显示内容(请求成功后)
87 | mPageLayout.onRequestSuccess();
88 | }
89 | }, TIME_DELAY);
90 | }
91 | });
92 | }
93 |
94 | static class SampleAdapter extends RecyclerView.Adapter{
95 |
96 | private final LayoutInflater mInflater;
97 | private int colors[];
98 |
99 | public SampleAdapter(Context context, int colors[]) {
100 | mInflater = LayoutInflater.from(context);
101 | this.colors = colors;
102 | }
103 |
104 | @Override
105 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
106 | View view = mInflater.inflate(R.layout.list_item, parent, false);
107 | return new ItemHolder(view);
108 | }
109 |
110 | @Override
111 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
112 | ItemHolder _holder = (ItemHolder) holder;
113 | _holder.bg.setBackgroundColor(_holder.itemView.getResources().getColor(colors[position]));
114 | _holder.tv.setText(position + "");
115 | }
116 |
117 | @Override
118 | public int getItemCount() {
119 | return colors.length;
120 | }
121 |
122 | static class ItemHolder extends RecyclerView.ViewHolder{
123 | View bg;
124 | TextView tv;
125 |
126 | public ItemHolder(View itemView) {
127 | super(itemView);
128 | bg = itemView.findViewById(R.id.bg);
129 | tv = (TextView) itemView.findViewById(R.id.tv);
130 | }
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/nukc/sample/rain/Rain.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.sample.rain;
2 |
3 | import android.graphics.Paint;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | /**
9 | * Created by CJJ.
10 | */
11 | public class Rain {
12 | private Paint paint;
13 | private float speedY;
14 | private float speedX;
15 | private float x;
16 | private float y;
17 | private float w;
18 | private float pos;
19 |
20 | public Paint getPaint() {
21 | return paint;
22 | }
23 |
24 | public void setPaint(Paint paint) {
25 | this.paint = paint;
26 | }
27 |
28 | public float getW() {
29 | return w;
30 | }
31 |
32 | public void setW(float w) {
33 | this.w = w;
34 | }
35 |
36 | public float getX() {
37 | return x;
38 | }
39 |
40 | public void setX(float x) {
41 | this.x = x;
42 | }
43 |
44 | public float getY() {
45 | return y;
46 | }
47 |
48 | public void setY(float y) {
49 | this.y = y;
50 | }
51 |
52 | public float getSpeedY() {
53 | return speedY;
54 | }
55 |
56 | public void setSpeedY(float speedY) {
57 | this.speedY = speedY;
58 | }
59 |
60 | public float getSpeedX() {
61 | return speedX;
62 | }
63 |
64 | public void setSpeedX(float speedX) {
65 | this.speedX = speedX;
66 | }
67 |
68 | public float getPos() {
69 | return pos;
70 | }
71 |
72 | public void setPos(float pos) {
73 | this.pos = pos;
74 | }
75 |
76 | @Override
77 | protected Rain clone() throws CloneNotSupportedException {
78 | Rain rain = null;
79 | try {
80 | rain = (Rain) super.clone();
81 | } catch (CloneNotSupportedException e) {
82 | e.printStackTrace();
83 | }
84 | return rain;
85 | }
86 |
87 | public List clone(int number) {
88 | List clones = null;
89 | Rain clone;
90 | try {
91 | clones = new ArrayList<>();
92 | for (int i = 0; i < number; i++) {
93 | clone = clone();
94 | clone.getPaint().setStrokeWidth((float) Math.random() * (clone.getW() - 1) + 1);
95 | clone.setX((int) Math.random() * getPos());
96 | clones.add(clone);
97 | }
98 | } catch (CloneNotSupportedException e) {
99 | e.printStackTrace();
100 | }
101 | return clones;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/nukc/sample/rain/RainRefreshView.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.sample.rain;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.content.Context;
5 | import android.util.AttributeSet;
6 | import android.view.Gravity;
7 | import android.view.animation.BounceInterpolator;
8 | import android.view.animation.DecelerateInterpolator;
9 | import android.widget.FrameLayout;
10 | import android.widget.TextView;
11 |
12 | import com.github.nukc.buff.IPullUIHandler;
13 | import com.github.nukc.buff.Utils;
14 | import com.github.nukc.sample.R;
15 |
16 | /**
17 | * Created by C on 2016/1/27.
18 | */
19 | public class RainRefreshView extends FrameLayout implements IPullUIHandler {
20 | private int mHeight;
21 |
22 | private RainView mRainView;
23 | private WaveView mWaveView;
24 | private TextView tvTip;
25 |
26 | private int mCurrentTargetY;
27 |
28 | public RainRefreshView(Context context) {
29 | this(context, null);
30 | }
31 |
32 | public RainRefreshView(Context context, AttributeSet attrs) {
33 | super(context, attrs);
34 |
35 | int rainViewHeight = 0;
36 | if (!isInEditMode()) {
37 | mHeight = getResources().getDimensionPixelSize(R.dimen.view_footer_height);
38 | rainViewHeight = getResources().getDimensionPixelSize(com.github.nukc.buff.R.dimen.inside_target);
39 | }
40 |
41 | LayoutParams _layoutParams = generateDefaultLayoutParams();
42 | _layoutParams.height = mHeight;
43 | setLayoutParams(_layoutParams);
44 |
45 | mWaveView = new WaveView(context);
46 | addView(mWaveView);
47 |
48 | mRainView = new RainView(context);
49 | FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, rainViewHeight);
50 | layoutParams.gravity = Gravity.BOTTOM;
51 | mRainView.setLayoutParams(layoutParams);
52 | addView(mRainView);
53 |
54 | tvTip = new TextView(context);
55 | layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
56 | layoutParams.setMargins(0, 0, 0, Utils.dpToPx(10));
57 | layoutParams.gravity = Gravity.BOTTOM;
58 | tvTip.setLayoutParams(layoutParams);
59 | tvTip.setGravity(Gravity.CENTER);
60 | tvTip.setTextColor(getResources().getColor(android.R.color.white));
61 | addView(tvTip);
62 |
63 | mRainView.setVisibility(GONE);
64 | }
65 |
66 |
67 | @Override
68 | public void onPulling(float scrollTop, int targetY, int totalDragDistance) {
69 | mCurrentTargetY = targetY;
70 | mWaveView.setChange(targetY, totalDragDistance);
71 |
72 | if (Math.abs(scrollTop) > totalDragDistance)
73 | tvTip.setText(getContext().getString(com.github.nukc.buff.R.string.release_to_load_more));
74 | else
75 | tvTip.setText(getContext().getString(com.github.nukc.buff.R.string.pull_up_to_load_more));
76 | }
77 |
78 | @Override
79 | public void onRefresh(int totalDragDistance) {
80 | ValueAnimator animatorY = ValueAnimator.ofInt(mCurrentTargetY, totalDragDistance);
81 | animatorY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
82 | @Override
83 | public void onAnimationUpdate(ValueAnimator animation) {
84 | mWaveView.setTargetY((int)animation.getAnimatedValue());
85 | }
86 | });
87 | animatorY.setDuration(200);
88 | animatorY.setInterpolator(new DecelerateInterpolator(2f));
89 | animatorY.start();
90 |
91 | tvTip.setText(getContext().getString(R.string.loading));
92 | mRainView.setVisibility(VISIBLE);
93 | mRainView.StartRain();
94 |
95 | ValueAnimator animator = ValueAnimator.ofInt(totalDragDistance >> 2, totalDragDistance);
96 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
97 | @Override
98 | public void onAnimationUpdate(ValueAnimator animation) {
99 | mWaveView.setWaveHeight((int) animation.getAnimatedValue());
100 | }
101 | });
102 | animator.setInterpolator(new BounceInterpolator());
103 | animator.setDuration(800);
104 | animator.start();
105 |
106 | }
107 |
108 | @Override
109 | public void onStop(float dragPercent) {
110 | mRainView.stopRain();
111 | mRainView.setVisibility(GONE);
112 | tvTip.setText(null);
113 |
114 | ValueAnimator animator = ValueAnimator.ofInt(mWaveView.getTargetY(), 0);
115 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
116 | @Override
117 | public void onAnimationUpdate(ValueAnimator animation) {
118 | mWaveView.setTargetY((int) animation.getAnimatedValue());
119 | }
120 | });
121 | animator.setInterpolator(new DecelerateInterpolator(2f));
122 | animator.setDuration(200);
123 | animator.setStartDelay(200);
124 | animator.start();
125 | }
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/nukc/sample/rain/RainView.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.sample.rain;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 |
9 | import com.github.nukc.buff.Utils;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 | import java.util.Random;
14 |
15 | /**
16 | * Created by C on 2015/9/27.
17 | */
18 | public class RainView extends View {
19 | private List mRains;
20 | private Random mRandom = new Random();
21 |
22 | private float mStartY = -20;
23 | double mGradient = 8 * Math.PI / 180;
24 | float mSpeedY = 25;
25 | float mRainWidth;
26 | float mRainLength;
27 | private volatile boolean mIsStop = false;
28 |
29 | private volatile Thread mThread;
30 | private List mCopyRains;
31 |
32 | public RainView(Context context) {
33 | this(context, null);
34 | }
35 |
36 | public RainView(Context context, AttributeSet attrs) {
37 | super(context, attrs);
38 |
39 | mRains = new ArrayList<>();
40 | mCopyRains = new ArrayList<>();
41 |
42 | mRainWidth = Utils.dpToPx(1);
43 | mRainLength = Utils.dpToPx(8);
44 | }
45 |
46 | public void StartRain() {
47 | mIsStop = false;
48 | mThread = new Thread(new Runnable() {
49 | @Override
50 | public void run() {
51 | while (!mIsStop) {
52 | Paint paint = new Paint();
53 | for (int i = 0; i < mRandom.nextInt() * 50 + 100; i++) {
54 | Rain rain = new Rain();
55 | rain.setW(mRainWidth);
56 | rain.setSpeedY(mSpeedY);
57 | paint.reset();
58 | paint.setColor(0Xffffff);
59 | paint.setAlpha(90);
60 | paint.setStrokeWidth((float) Math.random() * (mRainWidth - 1) + 1);
61 | rain.setPaint(paint);
62 | int x = mRandom.nextInt(getMeasuredWidth() + 1);
63 | rain.setX(x);
64 | rain.setY(mStartY);
65 | float speedX = (float) (Math.tan(mGradient) * mSpeedY);
66 | rain.setSpeedX(speedX);
67 | mRains.add(rain);
68 | }
69 | postInvalidate();
70 | try {
71 | Thread.sleep(30);
72 | } catch (InterruptedException e) {
73 | e.printStackTrace();
74 | }
75 | }
76 | }
77 | });
78 | mThread.start();
79 | }
80 |
81 |
82 | public void stopRain() {
83 | mIsStop = true;
84 | mThread = null;
85 | mRains.clear();
86 | invalidate();
87 | }
88 |
89 | @Override
90 | protected void onAttachedToWindow() {
91 | super.onAttachedToWindow();
92 | }
93 |
94 | @Override
95 | protected void onDetachedFromWindow() {
96 | super.onDetachedFromWindow();
97 | }
98 |
99 | @Override
100 | protected void onDraw(Canvas canvas) {
101 | super.onDraw(canvas);
102 | if (mRains == null || mRains.size() <= 0) {
103 | return;
104 | }
105 |
106 | mCopyRains.clear();
107 | mCopyRains.addAll(mRains);
108 | for (Rain rain : mCopyRains) {
109 | if (rain == null) return;
110 |
111 | if (rain.getX() <= 0) {
112 | mRains.remove(rain);
113 | } else if (rain.getX() >= getWidth()) {
114 | mRains.remove(rain);
115 | } else if (rain.getY() > mStartY + getHeight()) {
116 | mRains.remove(rain);
117 | }
118 |
119 | rain.setX(rain.getX() + rain.getSpeedX());
120 | rain.setY(rain.getY() + rain.getSpeedY());
121 | Rain nextRain = RainNext(rain, mRainLength);
122 | canvas.drawLine(rain.getX(), rain.getY(), nextRain.getX(), nextRain.getY(), rain.getPaint());
123 | }
124 |
125 | }
126 |
127 | private Rain RainNext(Rain bubble, float length) {
128 | Rain next = new Rain();
129 | next.setX((float) (bubble.getX() + length * Math.sin(mGradient)));
130 | next.setY((float) (bubble.getY() + length * Math.cos(mGradient)));
131 | return next;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/nukc/sample/rain/WaveView.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.sample.rain;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.Paint;
9 | import android.graphics.Path;
10 | import android.util.AttributeSet;
11 | import android.view.View;
12 |
13 | import com.github.nukc.sample.R;
14 |
15 | /**
16 | * Created by C on 2016/1/27.
17 | */
18 | public class WaveView extends View {
19 |
20 | private int mMaxHeight;
21 | private int mTargetY = 0;
22 | private int mWaveHeight = 0;
23 | private int mWaveMinHeight = 0;
24 |
25 | private Path mPath;
26 | private Paint mPaint;
27 |
28 | private Bitmap mBgBitmap;
29 |
30 | public WaveView(Context context) {
31 | this(context, null);
32 | }
33 |
34 | public WaveView(Context context, AttributeSet attrs) {
35 | super(context, attrs);
36 | init();
37 | }
38 |
39 | private void init() {
40 | mPath = new Path();
41 | mPaint = new Paint();
42 | mPaint.setColor(Color.argb(150, 43, 43, 43));
43 | mPaint.setAntiAlias(true);
44 |
45 | final BitmapFactory.Options options = new BitmapFactory.Options();
46 | options.inPreferredConfig = Bitmap.Config.RGB_565;
47 |
48 | mMaxHeight = getResources().getDimensionPixelSize(R.dimen.view_footer_height);
49 |
50 | mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bg, options);
51 | mBgBitmap = Bitmap.createScaledBitmap(mBgBitmap, mMaxHeight * 2, mMaxHeight, true);
52 | }
53 |
54 | @Override
55 | protected void onDraw(Canvas canvas) {
56 | super.onDraw(canvas);
57 | final int saveCount = canvas.save();
58 |
59 | // canvas.translate(0, -mTargetY);
60 | canvas.clipRect(0, getMeasuredHeight() - mTargetY, getMeasuredWidth(), getMeasuredHeight());
61 |
62 | drawBg(canvas);
63 | drawWave(canvas);
64 |
65 | canvas.restoreToCount(saveCount);
66 | }
67 |
68 | private void drawBg(Canvas canvas){
69 | canvas.drawBitmap(mBgBitmap, 0, 0, null);
70 | }
71 |
72 | private void drawWave(Canvas canvas){
73 | mPath.reset();
74 | mPath.moveTo(0, mMaxHeight);
75 | mPath.lineTo(0, mWaveMinHeight);
76 | mPath.quadTo(getMeasuredWidth() / 2, mMaxHeight - mWaveHeight, getMeasuredWidth(), mWaveMinHeight);
77 | mPath.lineTo(getMeasuredWidth(), mMaxHeight);
78 |
79 | canvas.drawPath(mPath, mPaint);
80 | }
81 |
82 | public int getTargetY() {
83 | return mTargetY;
84 | }
85 |
86 | public void setTargetY(int targetY) {
87 | this.mTargetY = targetY;
88 | invalidate();
89 | }
90 |
91 | public void setWaveHeight(int waveHeight) {
92 | this.mWaveHeight = waveHeight;
93 | invalidate();
94 | }
95 |
96 | public void setChange(int targetY, int totalDragDistance){
97 | this.mTargetY = targetY;
98 | this.mWaveHeight = targetY;
99 | this.mWaveMinHeight = mMaxHeight - totalDragDistance;
100 | invalidate();
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/nukc/sample/sun/BaseRefreshView.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.sample.sun;
2 |
3 | import android.content.Context;
4 | import android.graphics.ColorFilter;
5 | import android.graphics.PixelFormat;
6 | import android.graphics.drawable.Animatable;
7 | import android.graphics.drawable.Drawable;
8 | import android.support.annotation.NonNull;
9 |
10 | import com.github.nukc.buff.PageLayout;
11 |
12 | public abstract class BaseRefreshView extends Drawable implements Drawable.Callback, Animatable {
13 |
14 | private PageLayout mRefreshLayout;
15 |
16 | public BaseRefreshView(Context context, PageLayout layout) {
17 | mRefreshLayout = layout;
18 | }
19 |
20 | public Context getContext() {
21 | return mRefreshLayout != null ? mRefreshLayout.getContext() : null;
22 | }
23 |
24 | public PageLayout getRefreshLayout() {
25 | return mRefreshLayout;
26 | }
27 |
28 | public abstract void setPercent(float percent, boolean invalidate);
29 |
30 | public abstract void offsetTopAndBottom(int offset);
31 |
32 | @Override
33 | public void invalidateDrawable(@NonNull Drawable who) {
34 | final Callback callback = getCallback();
35 | if (callback != null) {
36 | callback.invalidateDrawable(this);
37 | }
38 | }
39 |
40 | @Override
41 | public void scheduleDrawable(Drawable who, Runnable what, long when) {
42 | final Callback callback = getCallback();
43 | if (callback != null) {
44 | callback.scheduleDrawable(this, what, when);
45 | }
46 | }
47 |
48 | @Override
49 | public void unscheduleDrawable(Drawable who, Runnable what) {
50 | final Callback callback = getCallback();
51 | if (callback != null) {
52 | callback.unscheduleDrawable(this, what);
53 | }
54 | }
55 |
56 | @Override
57 | public int getOpacity() {
58 | return PixelFormat.TRANSLUCENT;
59 | }
60 |
61 | @Override
62 | public void setAlpha(int alpha) {
63 |
64 | }
65 |
66 | @Override
67 | public void setColorFilter(ColorFilter cf) {
68 |
69 | }
70 |
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/nukc/sample/sun/SunImageView.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.sample.sun;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.content.Context;
5 | import android.util.AttributeSet;
6 | import android.util.Log;
7 | import android.widget.ImageView;
8 |
9 | import com.github.nukc.buff.IPullUIHandler;
10 | import com.github.nukc.buff.PageLayout;
11 |
12 | /**
13 | * Created by C on 29/1/2016.
14 | * Nukc
15 | */
16 | public class SunImageView extends ImageView implements IPullUIHandler {
17 |
18 | private PageLayout mPageLayout;
19 | private BaseRefreshView mBaseRefreshView;
20 |
21 | private int mTotalDragDistance;
22 | private float mCurrentPercent;
23 |
24 | public SunImageView(Context context, PageLayout pageLayout) {
25 | this(context, null, pageLayout);
26 | }
27 |
28 | public SunImageView(Context context, AttributeSet attrs, PageLayout pageLayout) {
29 | super(context, attrs);
30 | mBaseRefreshView = new SunRefreshView(context, pageLayout);
31 | setImageDrawable(mBaseRefreshView);
32 | mPageLayout = pageLayout;
33 | mTotalDragDistance = pageLayout.getTotalDragDistance();
34 | }
35 |
36 | @Override
37 | public void onPulling(float scrollTop, int targetY, int totalDragDistance) {
38 | float percent = scrollTop / totalDragDistance;
39 | if (percent < 0) {
40 | return;
41 | }
42 |
43 | mCurrentPercent = percent;
44 | mBaseRefreshView.setPercent(percent, true);
45 | mBaseRefreshView.offsetTopAndBottom(targetY);
46 | }
47 |
48 | @Override
49 | public void onRefresh(int totalDragDistance) {
50 | ValueAnimator animator = ValueAnimator.ofFloat(mCurrentPercent, 1f);
51 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
52 | @Override
53 | public void onAnimationUpdate(ValueAnimator animation) {
54 | mBaseRefreshView.setPercent((float) animation.getAnimatedValue(), true);
55 | mBaseRefreshView.offsetTopAndBottom((int) (mTotalDragDistance * (float)animation.getAnimatedValue()));
56 | }
57 | });
58 | animator.setDuration(200);
59 | animator.setInterpolator(mPageLayout.getDecelerateInterpolator());
60 | animator.start();
61 |
62 | mBaseRefreshView.start();
63 | }
64 |
65 | @Override
66 | public void onStop(float dragPercent) {
67 | ValueAnimator animator = ValueAnimator.ofInt((int)(mTotalDragDistance * Math.min(1, Math.abs(dragPercent))), 0);
68 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
69 | @Override
70 | public void onAnimationUpdate(ValueAnimator animation) {
71 | mBaseRefreshView.setPercent((float)(((int)(animation.getAnimatedValue())) / mTotalDragDistance - 1), false);
72 | mBaseRefreshView.offsetTopAndBottom((int) animation.getAnimatedValue());
73 |
74 | }
75 | });
76 | animator.setInterpolator(mPageLayout.getDecelerateInterpolator());
77 | animator.setDuration(200);
78 | animator.setStartDelay(200);
79 | animator.start();
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/nukc/sample/sun/SunRefreshView.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.sample.sun;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Canvas;
7 | import android.graphics.Matrix;
8 | import android.graphics.Rect;
9 | import android.graphics.drawable.Animatable;
10 | import android.view.animation.Animation;
11 | import android.view.animation.Interpolator;
12 | import android.view.animation.LinearInterpolator;
13 | import android.view.animation.Transformation;
14 |
15 | import com.github.nukc.buff.PageLayout;
16 | import com.github.nukc.buff.Utils;
17 | import com.github.nukc.sample.R;
18 |
19 | /**
20 | * Created by Oleksii Shliama on 22/12/2014.
21 | * https://dribbble.com/shots/1650317-Pull-to-Refresh-Rentals
22 | */
23 | public class SunRefreshView extends BaseRefreshView implements Animatable {
24 |
25 | private static final float SCALE_START_PERCENT = 0.5f;
26 | private static final int ANIMATION_DURATION = 1000;
27 |
28 | private final static float SKY_RATIO = 0.65f;
29 | private static final float SKY_INITIAL_SCALE = 1.05f;
30 |
31 | private final static float TOWN_RATIO = 0.22f;
32 | private static final float TOWN_INITIAL_SCALE = 1.20f;
33 | private static final float TOWN_FINAL_SCALE = 1.30f;
34 |
35 | private static final float SUN_FINAL_SCALE = 0.75f;
36 | private static final float SUN_INITIAL_ROTATE_GROWTH = 1.2f;
37 | private static final float SUN_FINAL_ROTATE_GROWTH = 1.5f;
38 |
39 | private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
40 |
41 | private PageLayout mParent;
42 | private Matrix mMatrix;
43 | private Animation mAnimation;
44 |
45 | // private int mTop;
46 | private int mScreenWidth;
47 |
48 | private int mSkyHeight;
49 | private float mSkyTopOffset;
50 | private float mSkyMoveOffset;
51 |
52 | private int mTownHeight;
53 | private float mTownInitialTopOffset;
54 | private float mTownFinalTopOffset;
55 | private float mTownMoveOffset;
56 |
57 | private int mSunSize = 100;
58 | private float mSunLeftOffset;
59 | private float mSunTopOffset;
60 |
61 | private float mPercent = 0.0f;
62 | private float mRotate = 0.0f;
63 |
64 | private Bitmap mSky;
65 | private Bitmap mSun;
66 | private Bitmap mTown;
67 |
68 | private boolean isRefreshing = false;
69 |
70 | private int mTargetY = 0;
71 |
72 | public SunRefreshView(Context context, final PageLayout parent) {
73 | super(context, parent);
74 | mParent = parent;
75 | mMatrix = new Matrix();
76 |
77 | setupAnimations();
78 | parent.post(new Runnable() {
79 | @Override
80 | public void run() {
81 | initiateDimens(parent.getWidth());
82 | }
83 | });
84 | }
85 |
86 | public void initiateDimens(int viewWidth) {
87 | if (viewWidth <= 0 || viewWidth == mScreenWidth) return;
88 |
89 | mScreenWidth = viewWidth;
90 | mSkyHeight = (int) (SKY_RATIO * mScreenWidth);
91 | mSkyTopOffset = (mSkyHeight * 0.38f);
92 | mSkyMoveOffset = Utils.dpToPx(15);
93 |
94 | mTownHeight = (int) (TOWN_RATIO * mScreenWidth);
95 | mTownInitialTopOffset = (mParent.getTotalDragDistance() - mTownHeight * TOWN_INITIAL_SCALE);
96 | mTownFinalTopOffset = (mParent.getTotalDragDistance() - mTownHeight * TOWN_FINAL_SCALE);
97 | mTownMoveOffset = Utils.dpToPx(10);
98 |
99 | mSunLeftOffset = 0.3f * (float) mScreenWidth;
100 | mSunTopOffset = (mParent.getTotalDragDistance() * 0.1f);
101 |
102 | // mTop = -mParent.getTotalDragDistance();
103 |
104 | createBitmaps();
105 | }
106 |
107 | private void createBitmaps() {
108 | final BitmapFactory.Options options = new BitmapFactory.Options();
109 | options.inPreferredConfig = Bitmap.Config.RGB_565;
110 |
111 | mSky = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.sky, options);
112 | mSky = Bitmap.createScaledBitmap(mSky, mScreenWidth, mSkyHeight, true);
113 | mTown = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.buildings, options);
114 | mTown = Bitmap.createScaledBitmap(mTown, mScreenWidth, (int) (mScreenWidth * TOWN_RATIO), true);
115 | mSun = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.sun, options);
116 | mSun = Bitmap.createScaledBitmap(mSun, mSunSize, mSunSize, true);
117 | }
118 |
119 | @Override
120 | public void setPercent(float percent, boolean invalidate) {
121 | setPercent(percent);
122 | if (invalidate) setRotate(percent);
123 | }
124 |
125 | @Override
126 | public void offsetTopAndBottom(int offset) {
127 | // mTop += offset;
128 | mTargetY = offset;
129 | invalidateSelf();
130 | }
131 |
132 | @Override
133 | public void draw(Canvas canvas) {
134 | if (mScreenWidth <= 0) return;
135 |
136 | final int saveCount = canvas.save();
137 |
138 | // canvas.translate(0, mTop);
139 | // canvas.clipRect(0, -mTop, mScreenWidth, mParent.getTotalDragDistance());
140 | canvas.clipRect(0, 0, mScreenWidth, mTargetY);
141 |
142 | drawSky(canvas);
143 | drawSun(canvas);
144 | drawTown(canvas);
145 |
146 | canvas.restoreToCount(saveCount);
147 | }
148 |
149 | private void drawSky(Canvas canvas) {
150 | Matrix matrix = mMatrix;
151 | matrix.reset();
152 |
153 | float dragPercent = Math.min(1f, Math.abs(mPercent));
154 |
155 | float skyScale;
156 | float scalePercentDelta = dragPercent - SCALE_START_PERCENT;
157 | if (scalePercentDelta > 0) {
158 | /** Change skyScale between {@link #SKY_INITIAL_SCALE} and 1.0f depending on {@link #mPercent} */
159 | float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT);
160 | skyScale = SKY_INITIAL_SCALE - (SKY_INITIAL_SCALE - 1.0f) * scalePercent;
161 | } else {
162 | skyScale = SKY_INITIAL_SCALE;
163 | }
164 |
165 | float offsetX = -(mScreenWidth * skyScale - mScreenWidth) / 2.0f;
166 | float offsetY = (1.0f - dragPercent) * mParent.getTotalDragDistance() - mSkyTopOffset // Offset canvas moving
167 | - mSkyHeight * (skyScale - 1.0f) / 2 // Offset sky scaling
168 | + mSkyMoveOffset * dragPercent; // Give it a little move top -> bottom
169 |
170 | matrix.postScale(skyScale, skyScale);
171 | matrix.postTranslate(offsetX, offsetY);
172 | canvas.drawBitmap(mSky, matrix, null);
173 | }
174 |
175 | private void drawTown(Canvas canvas) {
176 | Matrix matrix = mMatrix;
177 | matrix.reset();
178 |
179 | float dragPercent = Math.min(1f, Math.abs(mPercent));
180 |
181 | float townScale;
182 | float townTopOffset;
183 | float townMoveOffset;
184 | float scalePercentDelta = dragPercent - SCALE_START_PERCENT;
185 | if (scalePercentDelta > 0) {
186 | /**
187 | * Change townScale between {@link #TOWN_INITIAL_SCALE} and {@link #TOWN_FINAL_SCALE} depending on {@link #mPercent}
188 | * Change townTopOffset between {@link #mTownInitialTopOffset} and {@link #mTownFinalTopOffset} depending on {@link #mPercent}
189 | */
190 | float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT);
191 | townScale = TOWN_INITIAL_SCALE + (TOWN_FINAL_SCALE - TOWN_INITIAL_SCALE) * scalePercent;
192 | townTopOffset = mTownInitialTopOffset - (mTownFinalTopOffset - mTownInitialTopOffset) * scalePercent;
193 | townMoveOffset = mTownMoveOffset * (1.0f - scalePercent);
194 | } else {
195 | float scalePercent = dragPercent / SCALE_START_PERCENT;
196 | townScale = TOWN_INITIAL_SCALE;
197 | townTopOffset = mTownInitialTopOffset;
198 | townMoveOffset = mTownMoveOffset * scalePercent;
199 | }
200 |
201 | float offsetX = -(mScreenWidth * townScale - mScreenWidth) / 2.0f;
202 | float offsetY = (1.0f - dragPercent) * mParent.getTotalDragDistance() // Offset canvas moving
203 | + townTopOffset
204 | - mTownHeight * (townScale - 1.0f) / 2 // Offset town scaling
205 | + townMoveOffset; // Give it a little move
206 |
207 | matrix.postScale(townScale, townScale);
208 | matrix.postTranslate(offsetX, offsetY);
209 |
210 | canvas.drawBitmap(mTown, matrix, null);
211 | }
212 |
213 | private void drawSun(Canvas canvas) {
214 | Matrix matrix = mMatrix;
215 | matrix.reset();
216 |
217 | float dragPercent = mPercent;
218 | if (dragPercent > 1.0f) { // Slow down if pulling over set height
219 | dragPercent = (dragPercent + 9.0f) / 10;
220 | }
221 |
222 | float sunRadius = (float) mSunSize / 2.0f;
223 | float sunRotateGrowth = SUN_INITIAL_ROTATE_GROWTH;
224 |
225 | float offsetX = mSunLeftOffset;
226 | float offsetY = mSunTopOffset
227 | + (mParent.getTotalDragDistance() / 2) * (1.0f - dragPercent) // Move the sun up
228 | - mTargetY + mParent.getTotalDragDistance();
229 | // - mTop; // Depending on Canvas position
230 |
231 | float scalePercentDelta = dragPercent - SCALE_START_PERCENT;
232 | if (scalePercentDelta > 0) {
233 | float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT);
234 | float sunScale = 1.0f - (1.0f - SUN_FINAL_SCALE) * scalePercent;
235 | sunRotateGrowth += (SUN_FINAL_ROTATE_GROWTH - SUN_INITIAL_ROTATE_GROWTH) * scalePercent;
236 |
237 | matrix.preTranslate(offsetX + (sunRadius - sunRadius * sunScale), offsetY * (2.0f - sunScale));
238 | matrix.preScale(sunScale, sunScale);
239 |
240 | offsetX += sunRadius;
241 | offsetY = offsetY * (2.0f - sunScale) + sunRadius * sunScale;
242 | } else {
243 | matrix.postTranslate(offsetX, offsetY);
244 |
245 | offsetX += sunRadius;
246 | offsetY += sunRadius;
247 | }
248 |
249 | matrix.postRotate(
250 | (isRefreshing ? -360 : 360) * mRotate * (isRefreshing ? 1 : sunRotateGrowth),
251 | offsetX,
252 | offsetY);
253 |
254 | canvas.drawBitmap(mSun, matrix, null);
255 | }
256 |
257 | public void setPercent(float percent) {
258 | mPercent = percent;
259 | }
260 |
261 | public void setRotate(float rotate) {
262 | mRotate = rotate;
263 | invalidateSelf();
264 | }
265 |
266 | public void resetOriginals() {
267 | setPercent(0);
268 | setRotate(0);
269 | }
270 |
271 | @Override
272 | protected void onBoundsChange(Rect bounds) {
273 | super.onBoundsChange(bounds);
274 | }
275 |
276 | @Override
277 | public void setBounds(int left, int top, int right, int bottom) {
278 | super.setBounds(left, top, right, mSkyHeight + top);
279 | }
280 |
281 | @Override
282 | public boolean isRunning() {
283 | return false;
284 | }
285 |
286 | @Override
287 | public void start() {
288 | mAnimation.reset();
289 | isRefreshing = true;
290 | mParent.startAnimation(mAnimation);
291 | }
292 |
293 | @Override
294 | public void stop() {
295 | mParent.clearAnimation();
296 | isRefreshing = false;
297 | resetOriginals();
298 | }
299 |
300 | private void setupAnimations() {
301 | mAnimation = new Animation() {
302 | @Override
303 | public void applyTransformation(float interpolatedTime, Transformation t) {
304 | setRotate(interpolatedTime);
305 | }
306 | };
307 | mAnimation.setRepeatCount(Animation.INFINITE);
308 | mAnimation.setRepeatMode(Animation.RESTART);
309 | mAnimation.setInterpolator(LINEAR_INTERPOLATOR);
310 | mAnimation.setDuration(ANIMATION_DURATION);
311 | }
312 |
313 | }
314 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukc/Buff/0770e12302d58e6b131a1cd252f97943bf40f7b2/app/src/main/res/drawable-xxxhdpi/bg.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/buildings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukc/Buff/0770e12302d58e6b131a1cd252f97943bf40f7b2/app/src/main/res/drawable-xxxhdpi/buildings.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/sky.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukc/Buff/0770e12302d58e6b131a1cd252f97943bf40f7b2/app/src/main/res/drawable-xxxhdpi/sky.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/sun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukc/Buff/0770e12302d58e6b131a1cd252f97943bf40f7b2/app/src/main/res/drawable-xxxhdpi/sun.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_inside.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_outside.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukc/Buff/0770e12302d58e6b131a1cd252f97943bf40f7b2/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukc/Buff/0770e12302d58e6b131a1cd252f97943bf40f7b2/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukc/Buff/0770e12302d58e6b131a1cd252f97943bf40f7b2/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukc/Buff/0770e12302d58e6b131a1cd252f97943bf40f7b2/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukc/Buff/0770e12302d58e6b131a1cd252f97943bf40f7b2/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #263238
4 | #212121
5 | #5677fc
6 |
7 | #ffff00
8 | #ffeb3b
9 |
10 | #ffd740
11 | #ffc107
12 |
13 | #ffab40
14 | #ff9800
15 | #ff6e40
16 | #ff5722
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 | 220dp
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Buff
3 | Outside
4 | Inside
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/github/nukc/buff/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.sample;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/art/inside.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukc/Buff/0770e12302d58e6b131a1cd252f97943bf40f7b2/art/inside.gif
--------------------------------------------------------------------------------
/art/outside.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukc/Buff/0770e12302d58e6b131a1cd252f97943bf40f7b2/art/outside.gif
--------------------------------------------------------------------------------
/art/showViews.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukc/Buff/0770e12302d58e6b131a1cd252f97943bf40f7b2/art/showViews.gif
--------------------------------------------------------------------------------
/bintray.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.github.dcendents.android-maven'
2 | apply plugin: 'com.jfrog.bintray'
3 |
4 | def siteUrl = 'https://github.com/nukc/buff'
5 | def gitUrl = 'https://github.com/nukc/buff.git'
6 |
7 | group = "com.github.nukc.buff"
8 | version = "1.0"
9 |
10 | install {
11 | repositories.mavenInstaller {
12 | // This generates POM.xml with proper parameters
13 | pom {
14 | project {
15 | packaging 'aar'
16 | name 'Pull up to load more' //项目描述
17 | url siteUrl
18 | licenses {
19 | license {
20 | name 'The MIT License'
21 | url 'https://opensource.org/licenses/MIT'
22 | }
23 | }
24 | developers {
25 | developer {
26 | id 'nukc' //填写的一些基本信息
27 | name 'c'
28 | email '353932158@qq.com'
29 | }
30 | }
31 | scm {
32 | connection gitUrl
33 | developerConnection gitUrl
34 | url siteUrl
35 | }
36 | }
37 | }
38 | }
39 | }
40 |
41 | // This is the library version used when deploying the artifact
42 |
43 | task sourcesJar(type: Jar) {
44 | from android.sourceSets.main.java.srcDirs
45 | classifier = 'sources'
46 | }
47 |
48 | task javadoc(type: Javadoc) {
49 | source = android.sourceSets.main.java.srcDirs
50 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
51 | options.encoding = 'UTF-8'
52 | }
53 |
54 | task javadocJar(type: Jar, dependsOn: javadoc) {
55 | classifier = 'javadoc'
56 | from javadoc.destinationDir
57 | }
58 |
59 | artifacts {
60 | archives javadocJar
61 | archives sourcesJar
62 | }
63 |
64 | Properties properties = new Properties()
65 | boolean isHasFile = false
66 | if (project.rootProject.findProject('local.properties') != null){
67 | isHasFile = true
68 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
69 | }
70 | bintray {
71 | user = isHasFile ? properties.getProperty("bintray.user") : System.getenv("bintray.user")
72 | key = isHasFile ? properties.getProperty("bintray.apikey") : System.getenv("bintray.apikey")
73 | configurations = ['archives']
74 | pkg {
75 | repo = "maven"
76 | name = "Buff" //发布到JCenter上的项目名字
77 | websiteUrl = siteUrl
78 | vcsUrl = gitUrl
79 | licenses = ["MIT"]
80 | publish = true
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.1.0-alpha5'
9 |
10 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
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 |
23 | task clean(type: Delete) {
24 | delete rootProject.buildDir
25 | }
26 |
--------------------------------------------------------------------------------
/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/nukc/Buff/0770e12302d58e6b131a1cd252f97943bf40f7b2/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Oct 21 11:34:03 PDT 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.2"
6 |
7 | defaultConfig {
8 | minSdkVersion 8
9 | targetSdkVersion 23
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 |
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 |
21 | lintOptions {
22 | abortOnError false
23 | }
24 |
25 | }
26 |
27 | dependencies {
28 | compile fileTree(dir: 'libs', include: ['*.jar'])
29 | testCompile 'junit:junit:4.12'
30 | compile 'com.android.support:appcompat-v7:23.2.1'
31 | }
32 |
33 | apply from: '../bintray.gradle'
--------------------------------------------------------------------------------
/library/library.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | generateDebugSources
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/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 F:\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/github/nukc/buff/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.sample;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/library/src/main/java/com/github/nukc/buff/FooterRefreshView.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.buff;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.LayoutInflater;
6 | import android.widget.FrameLayout;
7 | import android.widget.TextView;
8 |
9 | /**
10 | * Created by C on 5/1/2016.
11 | * Nukc
12 | */
13 | public class FooterRefreshView extends FrameLayout implements IPullUIHandler {
14 |
15 | private TextView mTextView;
16 | private int mHeight;
17 |
18 | private String sReleaseToLoadMore;
19 | private String sPullUpToLoadMore;
20 |
21 | public FooterRefreshView(Context context) {
22 | this(context, null);
23 | }
24 |
25 | public FooterRefreshView(Context context, AttributeSet attrs) {
26 | super(context, attrs);
27 | LayoutInflater.from(context).inflate(R.layout.view_refresh_footer, this);
28 | mTextView = (TextView) findViewById(R.id.text);
29 |
30 | mHeight = Utils.dpToPx(64);
31 | sReleaseToLoadMore = context.getString(R.string.release_to_load_more);
32 | sPullUpToLoadMore = context.getString(R.string.pull_up_to_load_more);
33 | }
34 |
35 | @Override
36 | public void onPulling(float scrollTop, int targetY, int totalDragDistance) {
37 | if (-scrollTop < mHeight){
38 | mTextView.setText(sPullUpToLoadMore);
39 | }else {
40 | mTextView.setText(sReleaseToLoadMore);
41 | }
42 | }
43 |
44 | @Override
45 | public void onRefresh(int totalDragDistance) {
46 | mTextView.setText(getContext().getString(R.string.loading));
47 | }
48 |
49 | @Override
50 | public void onStop(float dragPercent) {
51 |
52 | }
53 |
54 | @Override
55 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
56 | super.onMeasure(widthMeasureSpec, mHeight);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/library/src/main/java/com/github/nukc/buff/IPullUIHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.buff;
2 |
3 | /**
4 | * Created by C on 20/1/2016.
5 | * Nukc
6 | */
7 | public interface IPullUIHandler {
8 | void onPulling(float scrollTop, int targetY, int totalDragDistance);
9 |
10 | void onRefresh(int totalDragDistance);
11 |
12 | void onStop(float dragPercent);
13 | }
14 |
--------------------------------------------------------------------------------
/library/src/main/java/com/github/nukc/buff/LoadRetryLayout.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.buff;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.LayoutRes;
5 | import android.util.AttributeSet;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.Button;
10 | import android.widget.FrameLayout;
11 |
12 | /**
13 | * Created by C on 24/12/2015.
14 | * Nukc
15 | */
16 | public class LoadRetryLayout extends FrameLayout {
17 |
18 | private LayoutInflater mInflater;
19 | private View mLoadingView;
20 | private View mRetryView;
21 | private View mEmptyView;
22 |
23 | private OnRetryClickListener mListener;
24 |
25 | public LoadRetryLayout(Context context) {
26 | this(context, null);
27 | }
28 |
29 | public LoadRetryLayout(Context context, AttributeSet attrs) {
30 | super(context, attrs);
31 | mInflater = LayoutInflater.from(context);
32 | setBackgroundResource(android.R.color.background_light);
33 |
34 | setLoadingView(R.layout.view_loading);
35 | if (isInEditMode()) mLoadingView.setVisibility(GONE);
36 |
37 | setRetryView(R.layout.view_retry);
38 | mRetryView.setVisibility(GONE);
39 |
40 | setEmptyView(R.layout.view_empty);
41 | mEmptyView.setVisibility(GONE);
42 | }
43 |
44 | public void setLoadingView(@LayoutRes int resource){
45 | View view = mInflater.inflate(resource, this, false);
46 | setLoadingView(view);
47 | }
48 |
49 | public void setLoadingView(View view){
50 | if (mLoadingView != null){
51 | removeView(mLoadingView);
52 | }
53 | mLoadingView = view;
54 | addView(mLoadingView);
55 | }
56 |
57 | public void setRetryView(@LayoutRes int resource){
58 | View view = mInflater.inflate(resource, this, false);
59 | setRetryView(view);
60 | }
61 |
62 | public void setRetryView(View view){
63 | if (mRetryView != null){
64 | removeView(mRetryView);
65 | }
66 | mRetryView = view;
67 | addView(mRetryView);
68 | setRetryBtnClick();
69 | }
70 |
71 | public void setEmptyView(@LayoutRes int resource){
72 | View view = mInflater.inflate(resource, this, false);
73 | setEmptyView(view);
74 | }
75 |
76 | public void setEmptyView(View view){
77 | if (mEmptyView != null){
78 | removeView(mEmptyView);
79 | }
80 | mEmptyView = view;
81 | addView(mEmptyView);
82 | }
83 |
84 | public void showLoadingView(boolean show){
85 | mLoadingView.setVisibility(show ? VISIBLE : GONE);
86 | mRetryView.setVisibility(GONE);
87 | mEmptyView.setVisibility(GONE);
88 | showIf();
89 | }
90 |
91 | public void showRetryView(boolean show){
92 | mRetryView.setVisibility(show ? VISIBLE : GONE);
93 | mLoadingView.setVisibility(GONE);
94 | mEmptyView.setVisibility(GONE);
95 | showIf();
96 | }
97 |
98 | public void showEmptyView(boolean show){
99 | mEmptyView.setVisibility(show ? VISIBLE : GONE);
100 | mLoadingView.setVisibility(GONE);
101 | mRetryView.setVisibility(GONE);
102 | showIf();
103 | }
104 |
105 | public void showIf(){
106 | int visibilityCount = 0;
107 | for (int i = 0; i < getChildCount(); i++){
108 | if (getChildAt(i).getVisibility() == VISIBLE)
109 | visibilityCount++;
110 | }
111 |
112 | if (visibilityCount > 0){
113 | setVisibility(VISIBLE);
114 | }else {
115 | setVisibility(GONE);
116 | }
117 | }
118 |
119 | private void setRetryBtnClick(){
120 | if (mRetryView instanceof ViewGroup) {
121 | ViewGroup parent = (ViewGroup) mRetryView;
122 | for (int i = 0; i < parent.getChildCount(); i++){
123 | View childView = parent.getChildAt(i);
124 | if (childView instanceof Button){
125 | childView.setOnClickListener(new OnClickListener() {
126 | @Override
127 | public void onClick(View v) {
128 | showLoadingView(true);
129 | if (mListener != null){
130 | mListener.onRetryClick(v);
131 | }
132 | }
133 | });
134 | }
135 | }
136 | }
137 | }
138 |
139 | public void setOnRetryClickListener(OnRetryClickListener mListener) {
140 | this.mListener = mListener;
141 | }
142 |
143 | public interface OnRetryClickListener{
144 | void onRetryClick(View v);
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/library/src/main/java/com/github/nukc/buff/PageLayout.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.buff;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.support.v4.view.MotionEventCompat;
6 | import android.support.v4.view.ViewCompat;
7 | import android.util.AttributeSet;
8 | import android.util.Log;
9 | import android.view.MotionEvent;
10 | import android.view.View;
11 | import android.view.ViewConfiguration;
12 | import android.view.ViewGroup;
13 | import android.view.animation.DecelerateInterpolator;
14 | import android.widget.AbsListView;
15 |
16 | /**
17 | * Created by C on 21/12/2015.
18 | * Nukc
19 | */
20 | public class PageLayout extends ViewGroup {
21 |
22 | private static final String TAG = PageLayout.class.getSimpleName();
23 |
24 | private static final int INVALID_POINTER = -1;
25 | private static final float DRAG_RATE = .5f;
26 |
27 | private static final float DECELERATE_INTERPOLATION_FACTOR = 2f;
28 | private static final int ANIMATE_TO_TRIGGER_DURATION = 200;
29 | private static final int ANIMATE_TO_START_DURATION = 200;
30 | private static final int ANIMATE_REFRESH_STOP_DURATION = 200;
31 |
32 | private static final int MODE_INSIDE = 0;
33 | private static final int MODE_OUTSIDE = 1;
34 |
35 | private View mHeaderView;
36 | private View mTarget;
37 | private IPullUIHandler mHeaderPullHandler;
38 | private LoadRetryLayout mLoadRetryLayout;
39 | private View mFooterView;
40 | private IPullUIHandler mFooterPullHandler;
41 |
42 | private final DecelerateInterpolator mDecelerateInterpolator;
43 |
44 | private int mTouchSlop;
45 | private int mActivePointerId;
46 | private float mInitialMotionY;
47 | private boolean mIsBeingDragged;
48 | private int mTotalDragDistance;
49 |
50 | private float mCurrentDragPercent;
51 |
52 | private boolean mRefreshing = false;
53 | private boolean mFirstInitOnLayout = false;
54 | private boolean mLoadRetryEnabled;
55 | private boolean mLoadMoreEnabled;
56 | private boolean mIsLoadingMore = false;
57 |
58 | private static final byte PULL_UP = 0;
59 | private static final byte PULL_DOWN = 1;
60 |
61 | private byte mPullDirection;
62 | private int mLayoutMode;
63 |
64 | private OnRefreshListener mOnRefreshListener;
65 | private OnRefreshAndLoadMoreListener mOnRefreshAndLoadMoreListener;
66 |
67 | public PageLayout(Context context) {
68 | this(context, null);
69 | }
70 |
71 | public PageLayout(Context context, AttributeSet attrs) {
72 | super(context, attrs);
73 | mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
74 | mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR);
75 |
76 | final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PageLayout);
77 | mLoadRetryEnabled = a.getBoolean(R.styleable.PageLayout_loadRetryEnabled, false);
78 | mLoadMoreEnabled = a.getBoolean(R.styleable.PageLayout_loadMoreEnabled, false);
79 | mLayoutMode = a.getInteger(R.styleable.PageLayout_layoutMode, MODE_OUTSIDE);
80 | a.recycle();
81 |
82 | if (mLayoutMode == MODE_INSIDE) {
83 | mTotalDragDistance = getResources().getDimensionPixelSize(R.dimen.inside_target);
84 | }else {
85 | mTotalDragDistance = getResources().getDimensionPixelSize(R.dimen.outside_target);
86 | }
87 | }
88 |
89 | @Override
90 | protected void onFinishInflate() {
91 | final int childCount = getChildCount();
92 | if (childCount > 2) {
93 | throw new IllegalStateException("PageLayout only can host 2 elements");
94 | } else if (childCount == 2) {
95 | // not specify header or content
96 | if (mTarget == null || mHeaderView == null) {
97 | View child1 = getChildAt(0);
98 | View child2 = getChildAt(1);
99 | if (child1 instanceof IPullUIHandler) {
100 | mHeaderView = child1;
101 | mTarget = child2;
102 | mHeaderPullHandler = (IPullUIHandler) mHeaderView;
103 | } else if (child2 instanceof IPullUIHandler) {
104 | mHeaderView = child2;
105 | mTarget = child1;
106 | mHeaderPullHandler = (IPullUIHandler) mHeaderView;
107 | } else {
108 | // both are not specified
109 | if (mTarget == null && mHeaderView == null) {
110 | mHeaderView = child1;
111 | mTarget = child2;
112 | }
113 | // only one is specified
114 | else {
115 | if (mHeaderView == null) {
116 | mHeaderView = mTarget == child1 ? child2 : child1;
117 | } else {
118 | mTarget = mHeaderView == child1 ? child2 : child1;
119 | }
120 | }
121 | }
122 | }
123 |
124 | } else if (childCount == 1){
125 | mTarget = getChildAt(0);
126 |
127 | mHeaderView = new TextRefreshView(getContext());
128 | addView(mHeaderView, 0);
129 | mHeaderPullHandler = (IPullUIHandler) mHeaderView;
130 | }
131 |
132 | setFooterRefreshView();
133 |
134 | if (mLoadRetryEnabled) {
135 | mLoadRetryLayout = new LoadRetryLayout(getContext());
136 | addView(mLoadRetryLayout, getChildCount());
137 | }
138 |
139 | super.onFinishInflate();
140 | }
141 |
142 | private void setFooterRefreshView(){
143 | if (mLoadMoreEnabled && mFooterView == null) {
144 | mFooterView = new FooterRefreshView(getContext());
145 | mFooterPullHandler = (IPullUIHandler) mFooterView;
146 | addView(mFooterView, 0);
147 | }
148 | }
149 |
150 | public void setHeaderView(View headerView){
151 | if (mHeaderView != null){
152 | removeView(mHeaderView);
153 | }
154 |
155 | mHeaderView = headerView;
156 | addView(mHeaderView, 0);
157 | if (headerView instanceof IPullUIHandler){
158 | mHeaderPullHandler = (IPullUIHandler) mHeaderView;
159 | }
160 | }
161 |
162 | public void setFooterView(View footerView){
163 | if (mFooterView != null){
164 | removeView(mFooterView);
165 | }
166 |
167 | mFooterView = footerView;
168 | addView(mFooterView, 0);
169 | if (footerView instanceof IPullUIHandler){
170 | mFooterPullHandler = (IPullUIHandler) mFooterView;
171 | }
172 | }
173 |
174 | public void setTotalDragDistance(int totalDragDistance) {
175 | this.mTotalDragDistance = totalDragDistance;
176 | }
177 |
178 | public int getTotalDragDistance() {
179 | return mTotalDragDistance;
180 | }
181 |
182 | public DecelerateInterpolator getDecelerateInterpolator() {
183 | return mDecelerateInterpolator;
184 | }
185 |
186 | @Override
187 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
188 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
189 | if (mHeaderView != null){
190 | measureChildWithMargins(mHeaderView, widthMeasureSpec, 0, heightMeasureSpec, 0);
191 | }
192 |
193 | if (mTarget != null) {
194 | MarginLayoutParams layoutParams = (MarginLayoutParams) mTarget.getLayoutParams();
195 | int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
196 | getPaddingLeft() + getPaddingRight() + layoutParams.leftMargin + layoutParams.rightMargin,
197 | layoutParams.width);
198 | int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
199 | getPaddingTop() + getPaddingBottom() + layoutParams.topMargin,
200 | layoutParams.height);
201 | mTarget.measure(childWidthMeasureSpec, childHeightMeasureSpec);
202 | }
203 |
204 | if (mLoadRetryLayout != null){
205 | measureChildWithMargins(mLoadRetryLayout, widthMeasureSpec, 0 ,heightMeasureSpec, 0);
206 | }
207 |
208 | if (mFooterView != null){
209 | measureChildWithMargins(mFooterView, widthMeasureSpec, 0 ,heightMeasureSpec, 0);
210 | }
211 |
212 | }
213 |
214 | @Override
215 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
216 | layoutChildren();
217 | }
218 |
219 | private void layoutChildren() {
220 | int paddingLeft = getPaddingLeft();
221 | int paddingTop = getPaddingTop();
222 |
223 | if (mHeaderView != null) {
224 | MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams();
225 | final int left = paddingLeft + lp.leftMargin;
226 | final int top = paddingTop + lp.topMargin;
227 | final int right = left + mHeaderView.getMeasuredWidth();
228 | final int bottom = top + mHeaderView.getMeasuredHeight();
229 | mHeaderView.layout(left, top, right, bottom);
230 |
231 | //第一次初始化的时候设置头视图的位置,避免下拉刷新过程中发生了onLayout事件又设置了头视图的位置,先这样吧- -
232 | if (!mFirstInitOnLayout && mLayoutMode == MODE_OUTSIDE) {
233 | ViewCompat.setTranslationY(mHeaderView, -mHeaderView.getMeasuredHeight());
234 | mFirstInitOnLayout = !mFirstInitOnLayout;
235 | Log.i(TAG, "first init");
236 | }
237 | }
238 |
239 | if (mTarget != null) {
240 | MarginLayoutParams lp = (MarginLayoutParams) mTarget.getLayoutParams();
241 | final int left = paddingLeft + lp.leftMargin;
242 | final int top = paddingTop + lp.topMargin;
243 | final int right = left + mTarget.getMeasuredWidth();
244 | final int bottom = top + mTarget.getMeasuredHeight();
245 | mTarget.layout(left, top, right, bottom);
246 | }
247 |
248 | if (mLoadRetryLayout != null) {
249 | MarginLayoutParams lp = (MarginLayoutParams) mLoadRetryLayout.getLayoutParams();
250 | final int left = paddingLeft + lp.leftMargin;
251 | final int top = paddingTop + lp.topMargin;
252 | final int right = left + mLoadRetryLayout.getMeasuredWidth();
253 | final int bottom = top + mLoadRetryLayout.getMeasuredHeight();
254 | mLoadRetryLayout.layout(left, top, right, bottom);
255 | }
256 |
257 | if (mFooterView != null) {
258 | MarginLayoutParams lp = (MarginLayoutParams) mFooterView.getLayoutParams();
259 | final int left = paddingLeft + lp.leftMargin;
260 | final int top = paddingTop + lp.topMargin;
261 | final int right = left + mFooterView.getMeasuredWidth();
262 | final int bottom = top + mFooterView.getMeasuredHeight();
263 | if (mLayoutMode == MODE_OUTSIDE) {
264 | mFooterView.layout(left, getBottom(), right, getBottom() + bottom);
265 | }else {
266 | mFooterView.layout(left, getBottom() - bottom, right, getBottom());
267 | }
268 | }
269 |
270 | }
271 |
272 | @Override
273 | public boolean onInterceptTouchEvent(MotionEvent ev) {
274 | if (!isEnabled() || mRefreshing || mIsLoadingMore){
275 | return false;
276 | }
277 |
278 | final int action = MotionEventCompat.getActionMasked(ev);
279 |
280 | switch (action){
281 | case MotionEvent.ACTION_DOWN:
282 | mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
283 | mIsBeingDragged = false;
284 | final float initialDownY = getMotionEventY(ev, mActivePointerId);
285 | if (initialDownY == -1) {
286 | return false;
287 | }
288 | mInitialMotionY = initialDownY;
289 | case MotionEvent.ACTION_MOVE:
290 |
291 | if (mActivePointerId == INVALID_POINTER) {
292 | Log.e(TAG, "Got ACTION_MOVE event but don't have an active pointer id.");
293 | return false;
294 | }
295 |
296 | final float y = getMotionEventY(ev, mActivePointerId);
297 | if (y == -1) {
298 | return false;
299 | }
300 | final float yDiff = y - mInitialMotionY;
301 | if (yDiff > mTouchSlop && !mIsBeingDragged && !canChildScrollUp()) {
302 | mIsBeingDragged = true;
303 | mPullDirection = PULL_DOWN;
304 | }else if (yDiff < 0 && !canChildScrollDown() && mLoadMoreEnabled){
305 | mIsBeingDragged = true;
306 | mPullDirection = PULL_UP;
307 | }
308 |
309 | break;
310 | case MotionEventCompat.ACTION_POINTER_UP:
311 | onSecondaryPointerUp(ev);
312 | break;
313 |
314 | case MotionEvent.ACTION_UP:
315 | case MotionEvent.ACTION_CANCEL:
316 | mIsBeingDragged = false;
317 | mActivePointerId = INVALID_POINTER;
318 | break;
319 | }
320 |
321 | return mIsBeingDragged;
322 | }
323 |
324 | private float getMotionEventY(MotionEvent ev, int activePointerId) {
325 | final int index = MotionEventCompat.findPointerIndex(ev, activePointerId);
326 | if (index < 0) {
327 | return -1;
328 | }
329 | return MotionEventCompat.getY(ev, index);
330 | }
331 |
332 | @Override
333 | public boolean onTouchEvent(MotionEvent ev) {
334 | if (!mIsBeingDragged) {
335 | return super.onTouchEvent(ev);
336 | }
337 |
338 | final int action = MotionEventCompat.getActionMasked(ev);
339 | switch (action){
340 | // case MotionEvent.ACTION_DOWN:
341 | // mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
342 | // mIsBeingDragged = false;
343 | // break;
344 | case MotionEvent.ACTION_MOVE: {
345 | final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
346 | if (pointerIndex == -1) {
347 | Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
348 | return false;
349 | }
350 |
351 | final float y = MotionEventCompat.getY(ev, pointerIndex);
352 | final float yDiff = y - mInitialMotionY;
353 | final float scrollTop = yDiff * DRAG_RATE;
354 |
355 | mCurrentDragPercent = scrollTop / mTotalDragDistance;
356 |
357 | float dragPercent = Math.min(1f, Math.abs(mCurrentDragPercent));
358 | float extraOS = Math.abs(scrollTop) - mTotalDragDistance;
359 | float slingshotDist = mTotalDragDistance;
360 | float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, slingshotDist * 2)
361 | / slingshotDist);
362 | float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow(
363 | (tensionSlingshotPercent / 4), 2)) * 2f;
364 | float extraMove = (slingshotDist) * tensionPercent * 2;
365 |
366 | int targetY = (int) ((slingshotDist * dragPercent) + extraMove);
367 |
368 | if (scrollTop > 0 && mPullDirection == PULL_DOWN) {
369 | mHeaderPullHandler.onPulling(scrollTop, targetY, mTotalDragDistance);
370 | ViewCompat.setTranslationY(mTarget, targetY);
371 | if (mLayoutMode == MODE_OUTSIDE)
372 | ViewCompat.setTranslationY(mHeaderView, targetY - mHeaderView.getHeight());
373 |
374 | } else if (scrollTop < 0 && mLoadMoreEnabled && mPullDirection == PULL_UP){
375 |
376 | if (mLayoutMode == MODE_OUTSIDE) {
377 | ViewCompat.setTranslationY(mFooterView, -targetY);
378 | mFooterPullHandler.onPulling(scrollTop, targetY, mTotalDragDistance);
379 | ViewCompat.setTranslationY(mTarget, -targetY);
380 | }else if (targetY <= mFooterView.getHeight()){
381 | mFooterPullHandler.onPulling(scrollTop, targetY, mTotalDragDistance);
382 | ViewCompat.setTranslationY(mTarget, -targetY);
383 | }
384 | } else {
385 | return false;
386 | }
387 |
388 | break;
389 | }
390 | case MotionEventCompat.ACTION_POINTER_DOWN: {
391 | final int pointerIndex = MotionEventCompat.getActionIndex(ev);
392 | if (pointerIndex < 0) {
393 | Log.e(TAG, "Got ACTION_POINTER_DOWN event but have an invalid action index.");
394 | return false;
395 | }
396 | mActivePointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
397 | break;
398 | }
399 |
400 | case MotionEventCompat.ACTION_POINTER_UP:
401 | onSecondaryPointerUp(ev);
402 | break;
403 | case MotionEvent.ACTION_CANCEL:
404 | case MotionEvent.ACTION_UP: {
405 | final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
406 | if (pointerIndex < 0) {
407 | Log.e(TAG, "Got ACTION_UP event but don't have an active pointer id.");
408 | return false;
409 | }
410 |
411 | final float y = MotionEventCompat.getY(ev, pointerIndex);
412 | final float overScrollTop = (y - mInitialMotionY) * DRAG_RATE;
413 | mIsBeingDragged = false;
414 | if (overScrollTop > mTotalDragDistance && mPullDirection == PULL_DOWN){
415 | setRefreshing(true);
416 | pullDownToCorrectPosition(); //< -mFooterView.getHeight()
417 | } else if (mLoadMoreEnabled && overScrollTop < -mTotalDragDistance && mPullDirection == PULL_UP){
418 | setLoadingMore(true);
419 | pullUpToCorrectPosition();
420 | } else {
421 | mIsLoadingMore = false;
422 | mRefreshing = false;
423 | animateToStartPosition();
424 | }
425 | mActivePointerId = INVALID_POINTER;
426 | return false;
427 | }
428 |
429 | }
430 |
431 | return true;
432 | }
433 |
434 | private void animateToStartPosition(){
435 | if (mPullDirection == PULL_DOWN) {
436 | mHeaderPullHandler.onStop(mCurrentDragPercent);
437 | }else if (mLoadMoreEnabled){
438 | mFooterPullHandler.onStop(mCurrentDragPercent);
439 | }
440 |
441 | postDelayed(new Runnable() {
442 | @Override
443 | public void run() {
444 | if (mLayoutMode == MODE_OUTSIDE) {
445 | if (mPullDirection == PULL_DOWN) {
446 | ViewCompat.animate(mHeaderView)
447 | .translationY(-mHeaderView.getHeight())
448 | .setInterpolator(mDecelerateInterpolator)
449 | .setDuration(ANIMATE_TO_START_DURATION)
450 | .start();
451 | } else if (mLoadMoreEnabled) {
452 | ViewCompat.animate(mFooterView)
453 | .translationY(0)
454 | .setDuration(ANIMATE_TO_START_DURATION)
455 | .setInterpolator(mDecelerateInterpolator)
456 | .start();
457 | }
458 | }
459 |
460 | ViewCompat.animate(mTarget)
461 | .translationY(0)
462 | .setDuration(ANIMATE_TO_START_DURATION)
463 | .setInterpolator(mDecelerateInterpolator)
464 | .start();
465 | }
466 | }, ANIMATE_REFRESH_STOP_DURATION);
467 |
468 | }
469 |
470 | private void pullDownToCorrectPosition(){
471 | if (mLayoutMode == MODE_OUTSIDE)
472 | ViewCompat.animate(mHeaderView)
473 | .translationY(mTotalDragDistance - mHeaderView.getHeight())
474 | .setDuration(ANIMATE_TO_TRIGGER_DURATION)
475 | .setInterpolator(mDecelerateInterpolator)
476 | .start();
477 |
478 | ViewCompat.animate(mTarget)
479 | .translationY(mTotalDragDistance)
480 | .setDuration(ANIMATE_TO_TRIGGER_DURATION)
481 | .setInterpolator(mDecelerateInterpolator)
482 | .start();
483 | }
484 |
485 | private void pullUpToCorrectPosition(){
486 | if (mLayoutMode == MODE_OUTSIDE) {
487 | ViewCompat.animate(mFooterView)
488 | .translationY(-mTotalDragDistance) //-mFooterView.getHeight()
489 | .setDuration(ANIMATE_TO_TRIGGER_DURATION)
490 | .setInterpolator(mDecelerateInterpolator)
491 | .start();
492 | }
493 |
494 | ViewCompat.animate(mTarget)
495 | .translationY(-mTotalDragDistance)//-mFooterView.getHeight()
496 | .setDuration(ANIMATE_TO_TRIGGER_DURATION)
497 | .setInterpolator(mDecelerateInterpolator)
498 | .start();
499 | }
500 |
501 | private void onSecondaryPointerUp(MotionEvent ev) {
502 | final int pointerIndex = MotionEventCompat.getActionIndex(ev);
503 | final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
504 | if (pointerId == mActivePointerId) {
505 | // This was our active pointer going up. Choose a new
506 | // active pointer and adjust accordingly.
507 | final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
508 | mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
509 | }
510 | }
511 |
512 | private boolean canChildScrollUp() {
513 | if (android.os.Build.VERSION.SDK_INT < 14) {
514 | if (mTarget instanceof AbsListView) {
515 | final AbsListView absListView = (AbsListView) mTarget;
516 | return absListView.getChildCount() > 0
517 | && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
518 | .getTop() < absListView.getPaddingTop());
519 | } else {
520 | return mTarget.getScrollY() > 0;
521 | }
522 | } else {
523 | return ViewCompat.canScrollVertically(mTarget, -1);
524 | }
525 | }
526 |
527 |
528 | public boolean canChildScrollDown() {
529 | if (android.os.Build.VERSION.SDK_INT < 14) {
530 | if (mTarget instanceof AbsListView) {
531 | final AbsListView absListView = (AbsListView) mTarget;
532 | if (absListView.getChildCount() > 0) {
533 | int lastChildBottom = absListView.getChildAt(absListView.getChildCount() - 1).getBottom();
534 | return absListView.getLastVisiblePosition() == absListView.getAdapter().getCount() - 1 && lastChildBottom <= absListView.getMeasuredHeight();
535 | } else {
536 | return false;
537 | }
538 |
539 | } else {
540 | return ViewCompat.canScrollVertically(mTarget, 1) || mTarget.getScrollY() > 0;
541 | }
542 | } else {
543 | return ViewCompat.canScrollVertically(mTarget, 1);
544 | }
545 | }
546 |
547 |
548 | public void setRefreshing(boolean refreshing) {
549 | if (mRefreshing != refreshing) {
550 | mRefreshing = refreshing;
551 | if (mRefreshing) {
552 | callListener();
553 | mHeaderPullHandler.onRefresh(mTotalDragDistance);
554 | } else {
555 | animateToStartPosition();
556 | }
557 | }
558 | }
559 |
560 | public void setLoadingMore(boolean loadingMore){
561 | if (mIsLoadingMore != loadingMore) {
562 | mIsLoadingMore = loadingMore;
563 | if (mIsLoadingMore) {
564 | callListener();
565 | mFooterPullHandler.onRefresh(mTotalDragDistance);
566 | } else {
567 | animateToStartPosition();
568 | }
569 | }
570 | }
571 |
572 | @Override
573 | protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
574 | return p instanceof LayoutParams;
575 | }
576 |
577 | @Override
578 | protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
579 | return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
580 | }
581 |
582 | @Override
583 | protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
584 | return new LayoutParams(p);
585 | }
586 |
587 | @Override
588 | public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
589 | return new LayoutParams(getContext(), attrs);
590 | }
591 |
592 | public static class LayoutParams extends MarginLayoutParams {
593 |
594 | public LayoutParams(Context c, AttributeSet attrs) {
595 | super(c, attrs);
596 | }
597 |
598 | public LayoutParams(int width, int height) {
599 | super(width, height);
600 | }
601 |
602 | @SuppressWarnings({"unused"})
603 | public LayoutParams(MarginLayoutParams source) {
604 | super(source);
605 | }
606 |
607 | public LayoutParams(ViewGroup.LayoutParams source) {
608 | super(source);
609 | }
610 | }
611 |
612 | public void setLoadMoreEnabled(boolean loadMoreEnabled) {
613 | this.mLoadMoreEnabled = loadMoreEnabled;
614 | setFooterRefreshView();
615 | }
616 |
617 | public LoadRetryLayout getLoadRetryLayout() {
618 | if (mLoadRetryLayout == null)
619 | throw new NullPointerException("LoadRetryLayout is null, The LoadRetryEnabled is false? You can set the mLoadRetryEnabled = true.");
620 | return mLoadRetryLayout;
621 | }
622 |
623 | public void showLoading(boolean show){
624 | mLoadRetryLayout.showLoadingView(show);
625 | showContent(!show);
626 | }
627 |
628 | public void showRetry(boolean show){
629 | mLoadRetryLayout.showRetryView(show);
630 | showContent(!show);
631 | }
632 |
633 | public void showEmpty(boolean show){
634 | mLoadRetryLayout.showEmptyView(show);
635 | showContent(!show);
636 | }
637 |
638 | private void showContent(boolean show){
639 | int visibility = show ? VISIBLE : GONE;
640 | mHeaderView.setVisibility(visibility);
641 | mTarget.setVisibility(visibility);
642 | }
643 |
644 | /**
645 | * Called when requesting data successfully
646 | */
647 | public void onRequestSuccess(){
648 | showContent(true);
649 | mLoadRetryLayout.setVisibility(GONE);
650 | }
651 |
652 | /**
653 | * Called when requesting data failed
654 | */
655 | public void onRequestFailure(){
656 | showRetry(true);
657 | }
658 |
659 | /**
660 | * Set the retry button click listener
661 | * @param listener
662 | */
663 | public void setOnRetryClickListener(LoadRetryLayout.OnRetryClickListener listener){
664 | if (mLoadRetryLayout != null){
665 | mLoadRetryLayout.setOnRetryClickListener(listener);
666 | }
667 | }
668 |
669 | public void setOnRefreshListener(OnRefreshListener listener) {
670 | mOnRefreshListener = listener;
671 | }
672 |
673 | public void setOnRefreshAndLoadMoreListener(OnRefreshAndLoadMoreListener listener){
674 | mOnRefreshAndLoadMoreListener = listener;
675 | }
676 |
677 | private void callListener(){
678 | if (mOnRefreshListener != null){
679 | mOnRefreshListener.onRefresh();
680 | }else if (mOnRefreshAndLoadMoreListener != null){
681 | if (mPullDirection == PULL_UP){
682 | mOnRefreshAndLoadMoreListener.onLoadMore();
683 | }else {
684 | mOnRefreshAndLoadMoreListener.onRefresh();
685 | }
686 | }
687 | }
688 |
689 |
690 | public interface OnRefreshListener {
691 | void onRefresh();
692 | }
693 |
694 | public interface OnRefreshAndLoadMoreListener {
695 | void onRefresh();
696 |
697 | void onLoadMore();
698 | }
699 |
700 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/github/nukc/buff/TextRefreshView.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.buff;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.Gravity;
6 | import android.widget.FrameLayout;
7 | import android.widget.TextView;
8 |
9 | /**
10 | * Created by C on 19/11/2015.
11 | */
12 | public class TextRefreshView extends FrameLayout implements IPullUIHandler{
13 |
14 | private int mHeight;
15 |
16 | private TextView mTextView;
17 |
18 | public TextRefreshView(Context context) {
19 | this(context, null);
20 | }
21 |
22 | public TextRefreshView(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 |
25 | if (!isInEditMode()) {
26 | mHeight = Utils.dpToPx(64);
27 | }
28 |
29 | mTextView = new TextView(context);
30 | LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, mHeight);
31 | layoutParams.gravity = Gravity.BOTTOM;
32 | mTextView.setLayoutParams(layoutParams);
33 | mTextView.setGravity(Gravity.CENTER);
34 | mTextView.setTextColor(getResources().getColor(android.R.color.white));
35 | addView(mTextView);
36 |
37 | setBackgroundResource(R.color.gray_dark);
38 | }
39 |
40 | @Override
41 | public void onPulling(float scrollTop, int targetY, int totalDragDistance) {
42 | if (scrollTop < mHeight) {
43 | mTextView.setText(getContext().getString(R.string.pull_down_to_refresh));
44 | } else {
45 | mTextView.setText(getContext().getString(R.string.release_to_refresh));
46 | }
47 | }
48 |
49 | @Override
50 | public void onRefresh(int totalDragDistance) {
51 | mTextView.setText(getContext().getString(R.string.loading));
52 | }
53 |
54 | @Override
55 | public void onStop(float dragPercent) {
56 |
57 | }
58 |
59 | @Override
60 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
61 | super.onMeasure(widthMeasureSpec, mHeight);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/library/src/main/java/com/github/nukc/buff/Utils.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.buff;
2 |
3 | import android.content.res.Resources;
4 |
5 | /**
6 | * Created by C on 11/1/2016.
7 | * Nukc
8 | */
9 | public class Utils {
10 | public static int dpToPx(int dp) {
11 | return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
12 | }
13 |
14 | public static float limitValue(float a, float b) {
15 | float valve = 0;
16 | final float min = Math.min(a, b);
17 | final float max = Math.max(a, b);
18 | valve = valve > min ? valve : min;
19 | valve = valve < max ? valve : max;
20 | return valve;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/view_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/view_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/view_refresh_footer.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
18 |
19 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/view_retry.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
17 |
--------------------------------------------------------------------------------
/library/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/library/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #333
4 |
--------------------------------------------------------------------------------
/library/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 64dp
4 | 120dp
5 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Library
3 |
4 | 松开刷新
5 | 正在加载...
6 | 下拉刷新
7 | ImagePreviewActivity
8 | 加载完成
9 |
10 | 松开加载更多
11 | 正在刷新...
12 | 上拉加载更多
13 |
14 |
--------------------------------------------------------------------------------
/library/src/test/java/com/github/nukc/library/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.github.nukc.buff;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 |
--------------------------------------------------------------------------------