├── .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 | 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 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.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 | 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 | [![Download](https://api.bintray.com/packages/nukc/maven/Buff/images/download.svg)](https://bintray.com/nukc/maven/Buff/_latestVersion) 3 | [![Build Status](https://travis-ci.org/nukc/Buff.svg?branch=master)](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 | 8 | 9 | 10 | 11 | 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 |