diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index 9a8b7e5..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
deleted file mode 100644
index e7bedf3..0000000
--- a/.idea/copyright/profiles_settings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
deleted file mode 100644
index 97626ba..0000000
--- a/.idea/encodings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
deleted file mode 100644
index d35a3ac..0000000
--- a/.idea/gradle.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 95f0f03..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 4749aa3..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
deleted file mode 100644
index 7f68460..0000000
--- a/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
index 46f55df..e7dcc27 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-More, please visit:[My CSDN](http://blog.csdn.net/liaoinstan/article/details/51023907) apk demo:[download](https://github.com/liaoinstan/SpringView/blob/master/apk/demo-release.apk?raw=true) 中文文档:[中文文档](https://github.com/liaoinstan/SpringView/blob/master/README_CN.md)
+More, please visit:[My CSDN](http://blog.csdn.net/liaoinstan/article/details/51023907) apk demo:[download](https://github.com/liaoinstan/SpringView/blob/master/apk/DemoSpring-1.7.0-release.apk?raw=true) 中文文档:[中文文档](https://github.com/liaoinstan/SpringView/blob/master/README_CN.md)
SpringView
=====
@@ -21,7 +21,12 @@ SpringView
 
 
 
-
+
+
+**📌new:**
+
+ 
+ 
**How to use SpringView**
--------
@@ -35,7 +40,7 @@ Add Spring View in the layout file, note that Spring View and ScrollView have th
app:header="@layout/myheader"
app:footer="@layout/myfooter">
-
@@ -68,7 +73,17 @@ springView.setListener(new SpringView.OnFreshListener() {
use Gradle:
```
dependencies {
- compile 'com.liaoinstan.springview:library:1.3.2'
+
+ //SpringView core library (only include DefaultHeader/Footer)
+ implementation 'com.liaoinstan.springview:library:1.7.0'
+
+ //other Headers/Footers, choose one or more that you like
+ implementation 'com.liaoinstan.springview:AcfunHeader:1.7.0' //AcFun style (header and footer)
+ implementation 'com.liaoinstan.springview:AliHeader:1.7.0' //Alitrip style (header and footer)
+ implementation 'com.liaoinstan.springview:MeituanHeader:1.7.0' //Meituan style (header and footer)
+ implementation 'com.liaoinstan.springview:RotationHeader:1.7.0' //Mechanical gear style (header and footer)
+ implementation 'com.liaoinstan.springview:WeixinHeader:1.7.0' //WeChat Mini Program header(only header)
+ implementation 'com.liaoinstan.springview:DuHeader:1.7.0' //Du app header(only header)
}
```
or Maven:
@@ -76,7 +91,7 @@ or Maven:
com.liaoinstan.springview
library
- 1.3.2
+ 1.7.0
pom
```
@@ -84,37 +99,61 @@ or Maven:
**Update log**
--------
-####**v1.3.2**
- - fix the bug of the linkage damping of AppBarLayout in the case of null data,
+### **v1.7.0**
+- Add new a header (DuHeader), and a auto-scrolling Footer (AutoFooter).
+- Add a new type SCROLL. Based on this mode, effects such as scrolling to the bottom and automatic loading can be achieved.
+- Header / Footer now has setType () method, you can set different Type for Header and Footer respectively.
+- Fix issus.
+
+### **v1.6.0**
+- Add a new header (WeixinHeaderV2) for new version of WeChat (WeChat 7).
+- The movement parameter of SpringView (MovePara) no longer affects both Header and Footer. BaseHeader/Footer now has a new getMovePara() interface that allows you to set different movement parameters for Header and Footer to match different drag-and-drop feel.(if you don't implement the interface, SpringView is still compatible with the previous rules).
+- Update to Android X.
+- Fixed several drag and drop stickiness issues, and callFresh method callback issues.
+
+### **v1.5.1**
+ - Fix bug when nested layout, fix bug callFresh() can't spring back when set SpringView Give.NONE.
+
+### **v1.5.0**
+ - headers /footers split off from SpringView library(except DefaultHeader/Footer),simplified core library,now core library's size only **26KB**,You can import headers/footers separately
+
+### **v1.4.0**
+ - New function setMovePara(),a new header for weixin:WeixinHeader,a new type DRAG like SwipeRefreshLayout:spring.setType(Type.DRAG)
+
+### **v1.3.3**
+ - Optimize several experiential issues,Added onFinishFreshAndLoadDelay(int delay), callFreshDelay(int delay) method,Optimizing performance with BottomSheetBehavior,Optimize the performance used in BottomSheetDialog
+
+### **v1.3.2**
+ - Fix the bug of the linkage damping of AppBarLayout in the case of null data,
new setEnableFooter(Boolean), setEnableHeader(Boolean) disable or enable header/footer.
-####**v1.3.0**
- - support AppBarLayout,fix sliding conflict
+### **v1.3.0**
+ - Support AppBarLayout,fix sliding conflict
-####**v1.2.7**
- - bug fix
+### **v1.2.7**
+ - Bug fix
-####**v1.2.6**
- - padding bug fix
+### **v1.2.6**
+ - Padding bug fix
-####**v1.2.5**
- - add **setEnable(boolean)** function
+### **v1.2.5**
+ - Add **setEnable(boolean)** function
-####**v1.2.4**
+### **v1.2.4**
- - add **callFresh()** method, used to call the refresh operation manual
- - fixes the lateral sliding conflict
+ - Add **callFresh()** method, used to call the refresh operation manual
+ - Fixes the lateral sliding conflict
-####**v1.2.2**
- - repair the callback refresh many times
+### **v1.2.2**
+ - Repair the callback refresh many times
-####**v1.2.1**
- - repair the click event occasional failure problem
+### **v1.2.1**
+ - Repair the click event occasional failure problem
**Feedback**
--------
-If there are any questions or Suggestions, please feedback to my email: liaoinstan@outlook.com
+If there are any questions or Suggestions, please feedback to my email: liaoinstan@outlook.com;
Or in my blog
If it works to you, please give me a star for my hardwork ,thank you
diff --git a/README_CN.md b/README_CN.md
index f8b00c8..e1354fb 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -1,4 +1,4 @@
-更多请移步至:[我的CSDN博客](http://blog.csdn.net/liaoinstan/article/details/51023907) apk演示:[点击下载](https://github.com/liaoinstan/SpringView/blob/master/apk/demo-release.apk?raw=true) English document:[English document](https://github.com/liaoinstan/SpringView/blob/master/README.md)
+更多请移步至:[我的CSDN博客](http://blog.csdn.net/liaoinstan/article/details/51023907) apk演示:[点击下载](https://github.com/liaoinstan/SpringView/blob/master/apk/DemoSpring-1.7.0-release.apk?raw=true) English document:[English document](https://github.com/liaoinstan/SpringView/blob/master/README.md)
SpringView
=====
@@ -23,6 +23,11 @@ SpringView
 

+**📌新增:**
+
+ 
+ 
+
**如何使用 SpringView**
--------
@@ -35,7 +40,7 @@ SpringView
app:header="@layout/myheader"
app:footer="@layout/myfooter">
-
@@ -68,7 +73,17 @@ springView.setListener(new SpringView.OnFreshListener() {
使用 Gradle:
```
dependencies {
- compile 'com.liaoinstan.springview:library:1.3.2'
+
+ //SpringView核心库 (只包含DefaultHeader/Footer)
+ implementation 'com.liaoinstan.springview:library:1.7.0'
+
+ //以下是各个风格的Header/Footer,选择自己喜欢的引入
+ implementation 'com.liaoinstan.springview:AcfunHeader:1.7.0' //AcFun风格 (header and footer)
+ implementation 'com.liaoinstan.springview:AliHeader:1.7.0' //阿里旅行风格 (header and footer)
+ implementation 'com.liaoinstan.springview:MeituanHeader:1.7.0' //美团风格 (header and footer)
+ implementation 'com.liaoinstan.springview:RotationHeader:1.7.0' //齿轮机械风格 (header and footer)
+ implementation 'com.liaoinstan.springview:WeixinHeader:1.7.0' //微信小程序header(只有header)
+ implementation 'com.liaoinstan.springview:DuHeader:1.7.0' //'毒'App header(只有header)
}
```
或者 Maven:
@@ -76,7 +91,7 @@ dependencies {
com.liaoinstan.springview
library
- 1.3.2
+ 1.7.0
pom
```
@@ -84,30 +99,53 @@ dependencies {
**更新日志**
--------
-####**v1.3.2**
+#### **v1.7.0**
+ - 新增一个仿毒app的header (DuHeader),和一个自动滚动的Footer (AutoFooter)
+ - 新增SCROLL模式,基于这种模式可以实现滚动到底部自动加载等效果
+ - Header/Footer现在新增了setType()方法,你可以分别给Header和Footer设置不同的Type
+ - 修复一些Bug
+
+#### **v1.6.0**
+ - 新增了仿新版微信(微信7)小程序效果的header (WeixinHeaderV2)
+ - SpringView的移动参数(MovePara)不再同时影响Header和Footer了,BaseHeader/Footer新增了getMovePara()接口,你可以分别为header和footer设置不同的移动参数以匹配不同的拖拽手感。(如果你没有实现该接口,SpringView仍然兼容以前的规则)
+ - 依赖更新到Android X
+ - 修复几个拖拽粘滞的问题,和callFresh方法回调的问题
+#### **v1.5.1**
+ - 修复在SpringView内部嵌套布局无法滚动的bug,修复给SpringView设置Give.NONE熟悉时调用callFresh()无法正确回弹的问题。
+
+#### **v1.5.0**
+ - 把所有的header和footer单独从SpringView库中分离出来了(除DefaultHeader/Footer),现在可以只选择单独导入自己想要的headers/footers,彻底精简后的SpringView library只有**26KB**,后期新增的header/footer都将以独立库的形式发布,不再冗余到SpringView核心库里,库链接参见上述[获取SpringView]内容
+
+#### **v1.4.0**
+ - 新增setMovePara()方法用于设置拖拽时的阻尼系数,新增了一个WeixinHeader仿微信小程序header,新增新的模式:DRAG(拖拽模式),可以实现类似于SwipeRefreshLayout的交互效果,重新设计了overLap模式的实现方式,更加稳定,弃用了support过时api,优化拖拽流畅性和交互效果细节
+
+#### **v1.3.3**
+ - 优化几个体验性问题,新增onFinishFreshAndLoadDelay(int delay),callFreshDelay(int delay)方法,优化和BottomSheetBehavior联用的表现,优化在BottomSheetDialog中使用的表现
+
+#### **v1.3.2**
- 修复空数据情况下和AppBarLayout联动阻尼的bug,新增setEnableFooter(boolean),setEnableHeader(boolean)禁用或启用header/footer
-####**v1.3.0**
+#### **v1.3.0**
- 支持和 AppBarLayout 联动,修复滚动冲突的问题
-####**v1.2.7**
+#### **v1.2.7**
- 修复一些bug
-####**v1.2.6**
+#### **v1.2.6**
- 修复了内边距的失效的问题
-####**v1.2.5**
+#### **v1.2.5**
- 新增 **setEnable(boolean)** 方法,用于禁用/启用SpringView
-####**v1.2.4**
+#### **v1.2.4**
- 新增 **callFresh()** 方法, 用于手动调用刷新
- 修复了内置水平滑动控件的事件冲突
-####**v1.2.2**
+#### **v1.2.2**
- 修复了会多次回调刷新的问题
-####**v1.2.1**
+#### **v1.2.1**
- 修复点击事件偶尔失效的问题
diff --git a/apk/DemoSpring-1.7.0-release.apk b/apk/DemoSpring-1.7.0-release.apk
new file mode 100644
index 0000000..14a34ec
Binary files /dev/null and b/apk/DemoSpring-1.7.0-release.apk differ
diff --git a/apk/demo-debug.apk b/apk/demo-debug.apk
deleted file mode 100644
index 6f6f9b9..0000000
Binary files a/apk/demo-debug.apk and /dev/null differ
diff --git a/apk/demo-release.apk b/apk/demo-release.apk
deleted file mode 100644
index 19262e1..0000000
Binary files a/apk/demo-release.apk and /dev/null differ
diff --git a/build.gradle b/build.gradle
index a923934..e46d921 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,39 +1,41 @@
buildscript {
repositories {
+ google()
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.3.3'
- classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.1'
- classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
+ classpath 'com.android.tools.build:gradle:3.5.3'
+ classpath 'com.novoda:bintray-release:0.9.1'
}
}
allprojects {
repositories {
+ google()
jcenter()
}
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
options.addStringOption('encoding', 'UTF-8')
}
-// tasks.withType(Javadoc) {
-// options {
-// encoding "UTF-8"
-// charSet 'UTF-8'
-// links "http://docs.oracle.com/javase/7/docs/api"
-// }
-// options.addStringOption('Xdoclint:none', '-quiet')
-// }
}
task clean(type: Delete) {
delete rootProject.buildDir
}
ext {
- buildToolsVersion = '25.0.2'
- compileSdkVersion = 25
+ compileSdkVersion = 29
minSdkVersion = 16
- targetSdkVersion = 25
- supportLibraryVersion = '25.3.1'
-}
\ No newline at end of file
+ targetSdkVersion = 27
+ supportLibraryVersion = '1.1.0'
+ materialLibraryVersion = '1.2.0-alpha02'
+
+ userOrg = 'liaoinstan'
+ repoName = "maven"
+ groupId = 'com.liaoinstan.springview'
+ desc = 'SpringView For Android'
+ website = 'https://github.com/liaoinstan/SpringView'
+ licences = ['Apache-2.0']
+}
+
+//gradlew clean build bintrayUpload -PbintrayUser=BINTRAY_USERNAME -PbintrayKey=BINTRAY_KEY
\ No newline at end of file
diff --git a/demo/build.gradle b/demo/build.gradle
index 9cddc0c..1f4b07e 100644
--- a/demo/build.gradle
+++ b/demo/build.gradle
@@ -1,35 +1,61 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 25
- buildToolsVersion "25.0.2"
-
+ compileSdkVersion rootProject.ext.compileSdkVersion
+ compileOptions {
+ targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_1_8
+ }
defaultConfig {
applicationId "com.liaoinstan.demospring"
- minSdkVersion 14
- targetSdkVersion 25
- versionCode 1
- versionName "1.0"
+ minSdkVersion rootProject.ext.minSdkVersion
+ targetSdkVersion rootProject.ext.targetSdkVersion
+ versionCode 17
+ versionName "1.7.0"
}
buildTypes {
release {
+ minifyEnabled true //开启代码混淆
+ zipAlignEnabled true //是否zip对齐优化
+ shrinkResources true //移除无用的资源文件
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+ android.applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = "DemoSpring-${defaultConfig.versionName}-${variant.buildType.name}.apk"
+ }
+ }
}
+
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- compile 'com.android.support:appcompat-v7:25.3.1'
- compile 'com.android.support:design:25.3.1'
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "androidx.appcompat:appcompat:$supportLibraryVersion"
+ implementation "com.google.android.material:material:$materialLibraryVersion"
+
+// implementation 'com.liaoinstan.springview:library:1.7.0'
+// implementation 'com.liaoinstan.springview:AcfunHeader:1.7.0'
+// implementation 'com.liaoinstan.springview:AliHeader:1.7.0'
+// implementation 'com.liaoinstan.springview:MeituanHeader:1.7.0'
+// implementation 'com.liaoinstan.springview:RotationHeader:1.7.0'
+// implementation 'com.liaoinstan.springview:WeixinHeader:1.7.0'
+// implementation 'com.liaoinstan.springview:DuHeader:1.7.0'
-// compile 'com.liaoinstan.springview:library:1.3.0'
- compile project(':library')
+ implementation project(':library')
+ implementation project(':header_ali')
+ implementation project(':header_meituan')
+ implementation project(':header_rotation')
+ implementation project(':header_acfun')
+ implementation project(':header_weixin')
+ implementation project(':header_du')
- //recycleView header
- compile 'com.bartoszlipinski:recyclerviewheader2:2.0.1'
+ implementation project(':header_wangyi')
}
diff --git a/demo/liaoinstan.jks b/demo/liaoinstan.jks
new file mode 100644
index 0000000..ac5fa3e
Binary files /dev/null and b/demo/liaoinstan.jks differ
diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml
index 7c9e7dc..9a26faa 100644
--- a/demo/src/main/AndroidManifest.xml
+++ b/demo/src/main/AndroidManifest.xml
@@ -1,5 +1,6 @@
@@ -9,62 +10,34 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
- android:theme="@style/AppTheme">
+ android:theme="@style/AppTheme.NoActionBar"
+ tools:ignore="GoogleAppIndexingWarning">
+ android:label="@string/app_name">
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/src/main/java/com/liaoinstan/demospring/MainActivity.java b/demo/src/main/java/com/liaoinstan/demospring/MainActivity.java
index 0162cf7..f2a6f37 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/MainActivity.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/MainActivity.java
@@ -2,10 +2,16 @@
import android.content.Intent;
import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
import android.view.View;
+import androidx.appcompat.app.AppCompatActivity;
+
import com.liaoinstan.demospring.demo1.Demo1Activity;
+import com.liaoinstan.demospring.demo10.Demo10Activity;
+import com.liaoinstan.demospring.demo11.Demo11Activity;
+import com.liaoinstan.demospring.demo12.Demo12Activity;
+import com.liaoinstan.demospring.demo13.Demo13Activity;
+import com.liaoinstan.demospring.demo14.Demo14Activity;
import com.liaoinstan.demospring.demo2.Demo2Activity;
import com.liaoinstan.demospring.demo3.Demo3Activity;
import com.liaoinstan.demospring.demo4.Demo4Activity;
@@ -14,29 +20,17 @@
import com.liaoinstan.demospring.demo7.Demo7Activity;
import com.liaoinstan.demospring.demo8.Demo8Activity;
import com.liaoinstan.demospring.demo9.Demo9Activity;
+import com.liaoinstan.demospring.demox.DemoXActivity;
import com.liaoinstan.demospring.test.TestActivity;
import com.liaoinstan.demospring.warning.WarningActivity;
-public class MainActivity extends AppCompatActivity implements View.OnClickListener {
+public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
-
- findViewById(R.id.demo1).setOnClickListener(this);
- findViewById(R.id.demo2).setOnClickListener(this);
- findViewById(R.id.demo3).setOnClickListener(this);
- findViewById(R.id.demo4).setOnClickListener(this);
- findViewById(R.id.demo5).setOnClickListener(this);
- findViewById(R.id.demo6).setOnClickListener(this);
- findViewById(R.id.demo7).setOnClickListener(this);
- findViewById(R.id.demo8).setOnClickListener(this);
- findViewById(R.id.demo9).setOnClickListener(this);
- findViewById(R.id.warning).setOnClickListener(this);
- findViewById(R.id.test).setOnClickListener(this);
}
- @Override
public void onClick(View v) {
Intent intent = new Intent();
switch (v.getId()) {
@@ -76,6 +70,30 @@ public void onClick(View v) {
intent.setClass(this, Demo9Activity.class);
startActivity(intent);
break;
+ case R.id.demo10:
+ intent.setClass(this, Demo10Activity.class);
+ startActivity(intent);
+ break;
+ case R.id.demo11:
+ intent.setClass(this, Demo11Activity.class);
+ startActivity(intent);
+ break;
+ case R.id.demo12:
+ intent.setClass(this, Demo12Activity.class);
+ startActivity(intent);
+ break;
+ case R.id.demo13:
+ intent.setClass(this, Demo13Activity.class);
+ startActivity(intent);
+ break;
+ case R.id.demo14:
+ intent.setClass(this, Demo14Activity.class);
+ startActivity(intent);
+ break;
+ case R.id.demox:
+ intent.setClass(this, DemoXActivity.class);
+ startActivity(intent);
+ break;
case R.id.warning:
intent.setClass(this, WarningActivity.class);
startActivity(intent);
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo1/Demo1Activity.java b/demo/src/main/java/com/liaoinstan/demospring/demo1/Demo1Activity.java
index f804a4b..a0e4e74 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/demo1/Demo1Activity.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo1/Demo1Activity.java
@@ -2,8 +2,8 @@
import android.os.Bundle;
import android.os.Handler;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
import com.liaoinstan.demospring.R;
import com.liaoinstan.springview.container.DefaultFooter;
@@ -19,30 +19,20 @@ public class Demo1Activity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo1);
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
- springView = (SpringView) findViewById(R.id.springview);
+ springView = findViewById(R.id.springview);
springView.setType(SpringView.Type.FOLLOW);
springView.setListener(new SpringView.OnFreshListener() {
@Override
public void onRefresh() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
@Override
public void onLoadmore() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
});
springView.setHeader(new DefaultHeader(this));
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo10/Demo10Activity.java b/demo/src/main/java/com/liaoinstan/demospring/demo10/Demo10Activity.java
new file mode 100644
index 0000000..b31bd19
--- /dev/null
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo10/Demo10Activity.java
@@ -0,0 +1,48 @@
+package com.liaoinstan.demospring.demo10;
+
+import android.os.Bundle;
+import android.os.Handler;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
+import com.liaoinstan.demospring.R;
+import com.liaoinstan.springview.meituanheader.MeituanFooter;
+import com.liaoinstan.springview.meituanheader.MeituanHeader;
+import com.liaoinstan.springview.widget.SpringView;
+
+public class Demo10Activity extends AppCompatActivity {
+
+ private SpringView springView;
+
+ //下拉过程动画
+ private int[] pullAnimSrcs = new int[]{R.drawable.mt_pull, R.drawable.mt_pull01, R.drawable.mt_pull02, R.drawable.mt_pull03, R.drawable.mt_pull04, R.drawable.mt_pull05};
+ //刷新中动画
+ private int[] refreshAnimSrcs = new int[]{R.drawable.mt_refreshing01, R.drawable.mt_refreshing02, R.drawable.mt_refreshing03, R.drawable.mt_refreshing04, R.drawable.mt_refreshing05, R.drawable.mt_refreshing06};
+ //加载更多底部动画
+ private int[] loadingAnimSrcs = new int[]{R.drawable.mt_loading01, R.drawable.mt_loading02};
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_demo10);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ springView = findViewById(R.id.springview);
+ springView.setListener(new SpringView.OnFreshListener() {
+ @Override
+ public void onRefresh() {
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 2000);
+ }
+
+ @Override
+ public void onLoadmore() {
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 2000);
+ }
+ });
+ //给header和footer设置为DRAG模式
+ springView.setHeader(new MeituanHeader(this, pullAnimSrcs, refreshAnimSrcs).setType(SpringView.Type.DRAG));
+ springView.setFooter(new MeituanFooter(this, loadingAnimSrcs).setType(SpringView.Type.DRAG));
+ }
+}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo11/Demo11Activity.java b/demo/src/main/java/com/liaoinstan/demospring/demo11/Demo11Activity.java
new file mode 100644
index 0000000..935a572
--- /dev/null
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo11/Demo11Activity.java
@@ -0,0 +1,120 @@
+package com.liaoinstan.demospring.demo11;
+
+import android.os.Bundle;
+import android.os.Handler;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.appcompat.widget.Toolbar;
+import android.widget.Toast;
+
+import com.liaoinstan.demospring.R;
+import com.liaoinstan.springview.aliheader.AliFooter;
+import com.liaoinstan.springview.weixinheader.Program;
+import com.liaoinstan.springview.weixinheader.WeixinHeader;
+import com.liaoinstan.springview.widget.SpringView;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class Demo11Activity extends AppCompatActivity implements WeixinHeader.OnMoreClickListener, WeixinHeader.OnProgramClickListener, WeixinHeader.OnProgramLongClickListener {
+
+ private SpringView springView;
+ private WeixinHeader weixinHeader;
+
+ //测试数据
+ //注意这里本应该给每个对象一个网络图片url链接,再利用图片加载框架加载网络图片,
+ //但是在本demo中不想引入无关框架,所以这里把本地资源图片id转成String类型保存在url字段中,加载图片的时候再转回int类型设置图片,这样做仅为演示
+ private List data = new ArrayList() {{
+ add(new Program("ofo小黄车", String.valueOf(R.drawable.wx_program1)));
+ add(new Program("哈图", String.valueOf(R.drawable.wx_program2)));
+ add(new Program("好货", String.valueOf(R.drawable.wx_program3)));
+ }};
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_demo11);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ springView = findViewById(R.id.springview);
+ springView.setListener(new SpringView.OnFreshListener() {
+ @Override
+ public void onRefresh() {
+ }
+
+ @Override
+ public void onLoadmore() {
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 2000);
+ }
+ });
+ springView.setHeader(weixinHeader = new WeixinHeader());
+ springView.setFooter(new AliFooter(this));
+
+ /////////////////////////////////
+ //// 初始化微信小程序header ////
+ /////////////////////////////////
+
+ //设置加载图片回调方法
+ weixinHeader.setOnLoadImgCallback((imageView, imgUrl, position) -> {
+ //在这个回调中自行使用项目中的图片加载框架加载网络图片,这里因为不想在demo中导入无关框架,所以直接加载本地图片演示
+ imageView.setImageResource(Integer.parseInt(imgUrl));
+ });
+ //item 点击事件
+ weixinHeader.setOnProgramClickListener(this);
+ //item 长点击事件
+ weixinHeader.setOnProgramLongClickListener(this);
+ //“更多”按钮点击事件
+ weixinHeader.setOnMoreClickListener(this);
+
+ //设置小程序数据
+ weixinHeader.freshItem(data);
+ }
+
+ @Override
+ public void onClick(Program program, RecyclerView.ViewHolder holder, int position) {
+ Toast.makeText(Demo11Activity.this, program.getName() + " click", Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onLongClick(Program program, RecyclerView.ViewHolder holder, int position) {
+ //弹出个对话框,点确定就删除该小程序
+ new AlertDialog.Builder(this)
+ .setMessage("确定要移除'" + program.getName() + "'?")
+ .setPositiveButton("确定", (dialog, which) -> {
+ weixinHeader.removeItem(position);
+ })
+ .create()
+ .show();
+ }
+
+ @Override
+ public void onMoreClick() {
+ //弹出个对话框,点确定就随机添加一个小程序
+ new AlertDialog.Builder(this)
+ .setMessage("确定要添加新的小程序?")
+ .setPositiveButton("确定", (dialog, which) -> {
+ switch (new Random().nextInt(5)) {
+ case 0:
+ weixinHeader.addItem(new Program("ofo小黄车", String.valueOf(R.drawable.wx_program1)));
+ break;
+ case 1:
+ weixinHeader.addItem(new Program("哈图", String.valueOf(R.drawable.wx_program2)));
+ break;
+ case 2:
+ weixinHeader.addItem(new Program("好货", String.valueOf(R.drawable.wx_program3)));
+ break;
+ case 3:
+ weixinHeader.addItem(new Program("快闪", String.valueOf(R.drawable.wx_program4)));
+ break;
+ case 4:
+ weixinHeader.addItem(new Program("蘑菇街", String.valueOf(R.drawable.wx_program5)));
+ break;
+ }
+ })
+ .create()
+ .show();
+ }
+}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo12/Demo12Activity.java b/demo/src/main/java/com/liaoinstan/demospring/demo12/Demo12Activity.java
new file mode 100644
index 0000000..ceaa3c1
--- /dev/null
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo12/Demo12Activity.java
@@ -0,0 +1,140 @@
+package com.liaoinstan.demospring.demo12;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.liaoinstan.demospring.R;
+import com.liaoinstan.demospring.utils.StatusBarUtil;
+import com.liaoinstan.springview.weixinheader.Program;
+import com.liaoinstan.springview.weixinheaderv2.WeixinHeaderV2;
+import com.liaoinstan.springview.weixinheaderv2.WeixinTitleBar;
+import com.liaoinstan.springview.widget.SpringView;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class Demo12Activity extends AppCompatActivity implements WeixinHeaderV2.OnMoreClickListener, WeixinHeaderV2.OnProgramClickListener, WeixinHeaderV2.OnProgramDropListener, WeixinHeaderV2.OnSearchClickListener {
+
+ private View bottomView;
+ private WeixinTitleBar weixinTitleBar;
+ private SpringView springView;
+ private WeixinHeaderV2 weixinHeaderV2;
+
+ //测试数据
+ //注意这里本应该给每个对象一个网络图片url链接,再利用图片加载框架加载网络图片,
+ //但是在本demo中不想引入无关框架,所以这里把本地资源图片id转成String类型保存在url字段中,加载图片的时候再转回int类型设置图片,这样做仅为演示
+ private List dataRecent = new ArrayList() {{
+ add(new Program("ofo小黄车", String.valueOf(R.drawable.wx_program1)));
+ add(new Program("哈图", String.valueOf(R.drawable.wx_program2)));
+ add(new Program("好货", String.valueOf(R.drawable.wx_program3)));
+ add(new Program("快闪", String.valueOf(R.drawable.wx_program4)));
+ add(new Program("蘑菇街", String.valueOf(R.drawable.wx_program5)));
+ }};
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_demo12);
+ bottomView = findViewById(R.id.bottom_view);
+ weixinTitleBar = findViewById(R.id.weixin_title_bar);
+ springView = findViewById(R.id.springview);
+ //设置状态栏为透明重叠(沉浸式)
+ StatusBarUtil.setTranslucent(this);
+
+ springView.setListener(new SpringView.OnFreshListener() {
+ @Override
+ public void onRefresh() {
+ }
+
+ @Override
+ public void onLoadmore() {
+ }
+ });
+
+ ///////////////////////////////////
+ //// 初始化微信小程序headerV2 ////
+ ///////////////////////////////////
+
+ //微信headerV2 的构造函数需要bottomView,weixinTitleBar两个参数
+ //bottomView 是页面底部导航条,weixinHeaderV2在拖拽过程中会控制它收起或弹出,可以是任意View,这里为了方便使用了material包下的BottomNavigationView
+ //weixinTitleBar 是页面顶部的标题栏,weixinHeaderV2在拖拽过程中会控制它进行动画交互
+ //这两个参数均可为 null,为null则 weixinHeaderV2不会进行任何处理
+ weixinHeaderV2 = new WeixinHeaderV2(bottomView, weixinTitleBar);
+
+ //设置加载图片回调方法
+ weixinHeaderV2.setOnLoadImgCallback((imageView, imgUrl, position) -> {
+ //在这个回调中自行使用项目中的图片加载框架加载网络图片,这里因为不想在demo中导入无关框架,所以直接加载本地图片演示
+ imageView.setImageResource(Integer.parseInt(imgUrl));
+ });
+ //item 点击事件
+ weixinHeaderV2.setOnProgramClickListener(this);
+ //item drop(拖拽删除)事件
+ weixinHeaderV2.setOnProgramDropListener(this);
+ //“更多”按钮点击事件
+ weixinHeaderV2.setOnMoreClickListener(this);
+ //“搜索框”点击事件
+ weixinHeaderV2.setOnSearchClickListener(this);
+
+ //设置小程序数据('最近使用')
+ //如果要添加数据到'我的小程序',调用addItemMine(...)方法
+ weixinHeaderV2.addItemRecent(dataRecent);
+
+ //设置header到SpringView
+ springView.setHeader(weixinHeaderV2);
+ }
+
+ @Override
+ public void onClick(Program program, RecyclerView.ViewHolder holder, int position) {
+ Toast.makeText(Demo12Activity.this, program.getName() + " click", Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public boolean onDrop(Program program, RecyclerView.ViewHolder holder, int position) {
+ //返回ture会执行删除操作,false不删除,默认会删除
+ return true;
+ }
+
+ @Override
+ public void onSearchClick() {
+ Toast.makeText(this, "onSearchClick", Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onMoreClick() {
+ //弹出个对话框,点确定就随机添加一个小程序
+ new AlertDialog.Builder(this)
+ .setMessage("确定要添加新的小程序?(仅测试)")
+ .setPositiveButton("确定", (dialog, which) -> {
+ if (weixinHeaderV2.getRecentProgramCount() >= 7) {
+ //'最近使用'最多只能有7条数据
+ Toast.makeText(Demo12Activity.this, "已经放不下了", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ switch (new Random().nextInt(5)) {
+ case 0:
+ weixinHeaderV2.addItemRecent(new Program("ofo小黄车", String.valueOf(R.drawable.wx_program1)));
+ break;
+ case 1:
+ weixinHeaderV2.addItemRecent(new Program("哈图", String.valueOf(R.drawable.wx_program2)));
+ break;
+ case 2:
+ weixinHeaderV2.addItemRecent(new Program("好货", String.valueOf(R.drawable.wx_program3)));
+ break;
+ case 3:
+ weixinHeaderV2.addItemRecent(new Program("快闪", String.valueOf(R.drawable.wx_program4)));
+ break;
+ case 4:
+ weixinHeaderV2.addItemRecent(new Program("蘑菇街", String.valueOf(R.drawable.wx_program5)));
+ break;
+ }
+ })
+ .create()
+ .show();
+ }
+}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo13/Demo13Activity.java b/demo/src/main/java/com/liaoinstan/demospring/demo13/Demo13Activity.java
new file mode 100644
index 0000000..e75cf15
--- /dev/null
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo13/Demo13Activity.java
@@ -0,0 +1,147 @@
+package com.liaoinstan.demospring.demo13;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.recyclerview.widget.DefaultItemAnimator;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.StaggeredGridLayoutManager;
+
+import com.liaoinstan.demospring.R;
+import com.liaoinstan.springview.aliheader.AliHeader;
+import com.liaoinstan.springview.container.AutoFooter;
+import com.liaoinstan.springview.utils.DensityUtil;
+import com.liaoinstan.springview.widget.SpringView;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class Demo13Activity extends AppCompatActivity {
+ private List mDatas = new ArrayList<>();
+
+ private RecyclerView recyclerView;
+ private RecyclerViewAdapter recyclerViewAdapter;
+ private SpringView springView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_demo13);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ initData();
+
+ recyclerView = findViewById(R.id.recycle);
+ recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, RecyclerView.VERTICAL));
+// recyclerView.setLayoutManager(new GridLayoutManager(Demo13Activity.this, 2, RecyclerView.VERTICAL, false));
+ recyclerViewAdapter = new RecyclerViewAdapter(mDatas);
+ recyclerView.setAdapter(recyclerViewAdapter);
+ recyclerView.setItemAnimator(new DefaultItemAnimator());
+
+ springView = findViewById(R.id.springview);
+ springView.setListener(new SpringView.OnFreshListener() {
+ @Override
+ public void onRefresh() {
+ Toast.makeText(Demo13Activity.this, "onRefresh", Toast.LENGTH_SHORT).show();
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
+ }
+
+ @Override
+ public void onLoadmore() {
+ if (springView.getFooter(AutoFooter.class).isInProgress()) {
+ //模拟网络请求列表数据
+ new Handler().postDelayed(() -> {
+ List remoteData = getRemoteData();
+ if (remoteData != null) {
+ mDatas.addAll(remoteData);
+ recyclerViewAdapter.notifyItemInserted(mDatas.size() - 1);
+ } else {
+ springView.getFooter(AutoFooter.class).showBottomLine();
+ }
+ }, 1000);
+ }
+ }
+ });
+ springView.setHeader(new AliHeader(this));
+ springView.setFooter(new AutoFooter());
+ }
+
+ //模拟请求服务器数据
+ //如果数据小于25条就每次返回5条,大于25条就返回null,模拟服务器已经没有数据的情况,这只为演示
+ private List getRemoteData() {
+ if (mDatas.size() < 25) {
+ List remoteData = new ArrayList<>();
+ remoteData.add("add item");
+ remoteData.add("add item");
+ remoteData.add("add item");
+ remoteData.add("add item");
+ remoteData.add("add item");
+ return remoteData;
+ } else {
+ return null;
+ }
+ }
+
+ private void initData() {
+ for (int i = 0; i < 15; i++) {
+ mDatas.add(i == 0 ? "We are in RecyclerView" : (i == 1 ? "SpringView新增了SCROLL模式\n\n基于这种模式可以实现自动加载等效果\n\n下拉到底部试试" : ""));
+ }
+ }
+
+ /**
+ * Adapter for RecyclerView
+ */
+ public static class RecyclerViewAdapter extends RecyclerView.Adapter {
+ private List results;
+
+ public RecyclerViewAdapter(List results) {
+ this.results = results;
+ }
+
+ @NonNull
+ @Override
+ public SampleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
+ return new SampleViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull final SampleViewHolder holder, final int position) {
+ holder.text_item.setText(results.get(position));
+ Random random = new Random(position);
+ //瀑布流:给item设置一个随机的颜色
+ int color = Color.argb(88, random.nextInt(256), random.nextInt(256), random.nextInt(256));
+ holder.text_item.setBackgroundColor(color);
+ holder.text_item.setTextColor(Color.WHITE);
+ //瀑布流:给item设置一个随机的高度
+ ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
+ layoutParams.height = 300 + DensityUtil.dp2px(random.nextInt(60));
+ holder.itemView.setLayoutParams(layoutParams);
+ }
+
+ @Override
+ public int getItemCount() {
+ return results.size();
+ }
+
+ class SampleViewHolder extends RecyclerView.ViewHolder {
+ TextView text_item;
+
+ SampleViewHolder(View view) {
+ super(view);
+ text_item = view.findViewById(R.id.item_text);
+ }
+ }
+ }
+}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo14/Demo14Activity.java b/demo/src/main/java/com/liaoinstan/demospring/demo14/Demo14Activity.java
new file mode 100644
index 0000000..05ccb35
--- /dev/null
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo14/Demo14Activity.java
@@ -0,0 +1,47 @@
+package com.liaoinstan.demospring.demo14;
+
+import android.os.Bundle;
+import android.os.Handler;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
+import com.liaoinstan.demospring.R;
+import com.liaoinstan.springview.duheader.DuHeader;
+import com.liaoinstan.springview.duheader.TopBarFrameLayout;
+import com.liaoinstan.springview.widget.SpringView;
+
+public class Demo14Activity extends AppCompatActivity {
+
+ private SpringView springView;
+ private TopBarFrameLayout topBarFrameLayout;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_demo14);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ topBarFrameLayout = findViewById(R.id.top_bar_frame_layout);
+ springView = findViewById(R.id.springview);
+ springView.setListener(new SpringView.OnFreshListener() {
+ @Override
+ public void onRefresh() {
+ new Handler().postDelayed(() -> {
+ //0.5秒后再结束header刷新动画(模仿'毒'的延迟,大概0.5秒左右)
+ springView.onFinishFreshAndLoadDelay(500);
+ //开始展开topBar顶部提示,并在2.5秒回自动收回
+ topBarFrameLayout.setTopBarText("为你更新20条新内容");
+ topBarFrameLayout.showAndHideDelay(2500);
+ }, 2000);
+ }
+
+ @Override
+ public void onLoadmore() {
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 2000);
+ }
+ });
+ springView.setHeader(new DuHeader());
+ }
+}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo2/Demo2Activity.java b/demo/src/main/java/com/liaoinstan/demospring/demo2/Demo2Activity.java
index b606fc6..5e179b6 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/demo2/Demo2Activity.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo2/Demo2Activity.java
@@ -1,12 +1,8 @@
package com.liaoinstan.demospring.demo2;
-import android.content.Context;
import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -14,9 +10,12 @@
import android.widget.ListView;
import android.widget.TextView;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
import com.liaoinstan.demospring.R;
-import com.liaoinstan.springview.container.RotationFooter;
-import com.liaoinstan.springview.container.RotationHeader;
+import com.liaoinstan.springview.rotationheader.RotationFooter;
+import com.liaoinstan.springview.rotationheader.RotationHeader;
import com.liaoinstan.springview.widget.SpringView;
import java.util.ArrayList;
@@ -25,7 +24,7 @@
public class Demo2Activity extends AppCompatActivity {
private SpringView springView;
- private List mDatas = new ArrayList();
+ private List mDatas = new ArrayList<>();
private ListView listView;
private AdapterForList listAdapter;
@@ -33,41 +32,29 @@ public class Demo2Activity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo2);
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- toolbar.setTitleTextColor(Color.parseColor("#999999"));
+ Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
- getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#ffffff")));
initData();
- listView = (ListView) findViewById(R.id.list);
- listAdapter = new AdapterForList(this, mDatas);
+ listView = findViewById(R.id.list);
+ listAdapter = new AdapterForList(mDatas);
listView.setAdapter(listAdapter);
- springView = (SpringView) findViewById(R.id.springview);
+ springView = findViewById(R.id.springview);
springView.setListener(new SpringView.OnFreshListener() {
@Override
public void onRefresh() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
@Override
public void onLoadmore() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
});
- springView.setHeader(new RotationHeader(this));
- springView.setFooter(new RotationFooter(this));
+ springView.setHeader(new RotationHeader());
+ springView.setFooter(new RotationFooter());
}
private void initData() {
@@ -94,12 +81,13 @@ public void onClick(View v) {
}
+ /**
+ * Adapter for ListView
+ */
private class AdapterForList extends BaseAdapter {
- private Context context = null;
private List results;
- public AdapterForList(Context context, List results) {
- this.context = context;
+ AdapterForList(List results) {
this.results = results;
}
@@ -122,8 +110,8 @@ public int getCount() {
public View getView(int position, View convertView, ViewGroup parent) {
TextView item_text;
if (convertView == null) {
- convertView = LayoutInflater.from(context).inflate(R.layout.item, parent, false);
- item_text = (TextView) convertView.findViewById(R.id.item_text);
+ convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
+ item_text = convertView.findViewById(R.id.item_text);
convertView.setTag(item_text);
} else {
item_text = (TextView) convertView.getTag();
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo3/Demo3Activity.java b/demo/src/main/java/com/liaoinstan/demospring/demo3/Demo3Activity.java
index 3c22f68..7c2a7bd 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/demo3/Demo3Activity.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo3/Demo3Activity.java
@@ -1,23 +1,23 @@
package com.liaoinstan.demospring.demo3;
-import android.content.Context;
import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.DefaultItemAnimator;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.recyclerview.widget.DefaultItemAnimator;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
import com.liaoinstan.demospring.R;
-import com.liaoinstan.springview.container.AliFooter;
-import com.liaoinstan.springview.container.AliHeader;
+import com.liaoinstan.springview.aliheader.AliFooter;
+import com.liaoinstan.springview.aliheader.AliHeader;
import com.liaoinstan.springview.widget.SpringView;
import java.util.ArrayList;
@@ -34,42 +34,27 @@ public class Demo3Activity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo3);
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- toolbar.setTitleTextColor(Color.parseColor("#999999"));
+ Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
- getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.WHITE));
initData();
- recyclerView = (RecyclerView) findViewById(R.id.recycle);
- recyclerView.setLayoutManager(new LinearLayoutManager(this));
-// recyclerView.setLayoutManager(new GridLayoutManager(recyclerView.getContext(), 3, GridLayoutManager.VERTICAL, false));
-// recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
- recyclerViewAdapter = new RecyclerViewAdapter(this, mDatas);
+ recyclerView = findViewById(R.id.recycle);
+ recyclerView.setLayoutManager(new GridLayoutManager(this, 2, RecyclerView.VERTICAL, false));
+ recyclerViewAdapter = new RecyclerViewAdapter(mDatas);
recyclerView.setAdapter(recyclerViewAdapter);
recyclerView.setItemAnimator(new DefaultItemAnimator());
- springView = (SpringView) findViewById(R.id.springview);
- springView.setType(SpringView.Type.FOLLOW);
+ springView = findViewById(R.id.springview);
springView.setListener(new SpringView.OnFreshListener() {
@Override
public void onRefresh() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
@Override
public void onLoadmore() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
});
springView.setHeader(new AliHeader(this, R.drawable.ali, true)); //参数为:logo图片资源,是否显示文字
@@ -77,8 +62,8 @@ public void run() {
}
private void initData() {
- for (int i = 0; i < 9; i++) {
- mDatas.add(i == 0 ? "We are in RecyclerView" : (i == 1 ? "SpringView支持RecyclerView\n\n这是一个仿阿里旅行的header,logo可以图片可自行替换" : ""));
+ for (int i = 0; i < 14; i++) {
+ mDatas.add(i == 0 ? "We are in RecyclerView" : (i == 3 ? "SpringView支持RecyclerView\n\n这是一个仿阿里旅行的header\n\nlogo可以图片可自行替换" : ""));
}
}
@@ -100,23 +85,27 @@ public void onClick(View v) {
}
+ /**
+ * Adapter for RecyclerView
+ */
private class RecyclerViewAdapter extends RecyclerView.Adapter {
private List results;
- public RecyclerViewAdapter(Context context, List results) {
+ RecyclerViewAdapter(List results) {
this.results = results;
}
+ @NonNull
@Override
- public SampleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ public SampleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
return new SampleViewHolder(view);
}
@Override
- public void onBindViewHolder(final SampleViewHolder holder, final int position) {
+ public void onBindViewHolder(@NonNull final SampleViewHolder holder, final int position) {
holder.text_item.setText(results.get(position));
- if (position % 2 == 1) {
+ if ((position + 1) / 2 % 2 == 1) {
holder.text_item.setBackgroundColor(Color.parseColor("#e3f1fc"));
holder.text_item.setTextColor(Color.parseColor("#9dd2fc"));
} else {
@@ -130,12 +119,12 @@ public int getItemCount() {
return results.size();
}
- public class SampleViewHolder extends RecyclerView.ViewHolder {
- public TextView text_item;
+ class SampleViewHolder extends RecyclerView.ViewHolder {
+ TextView text_item;
- public SampleViewHolder(View view) {
+ SampleViewHolder(View view) {
super(view);
- text_item = (TextView) view.findViewById(R.id.item_text);
+ text_item = view.findViewById(R.id.item_text);
}
}
}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo4/Demo4Activity.java b/demo/src/main/java/com/liaoinstan/demospring/demo4/Demo4Activity.java
index d998a53..f3a17d3 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/demo4/Demo4Activity.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo4/Demo4Activity.java
@@ -3,8 +3,8 @@
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
import android.webkit.WebSettings;
import android.webkit.WebView;
@@ -14,18 +14,18 @@ public class Demo4Activity extends AppCompatActivity {
private WebView webView;
+ @SuppressWarnings("all")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo4);
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#18cfbe")));
- webView = (WebView) findViewById(R.id.webView);
+ webView = findViewById(R.id.webView);
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
- webView.loadUrl("https://github.com");//https://github.com //http://cn.bing.com/
-
+ webView.loadUrl("https://github.com"); //https://github.com //http://cn.bing.com/
}
}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo5/Demo5Activity.java b/demo/src/main/java/com/liaoinstan/demospring/demo5/Demo5Activity.java
index d3287f9..ed1f746 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/demo5/Demo5Activity.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo5/Demo5Activity.java
@@ -1,14 +1,12 @@
package com.liaoinstan.demospring.demo5;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
import com.liaoinstan.demospring.R;
-import com.liaoinstan.springview.container.AcFunFooter;
-import com.liaoinstan.springview.container.AcFunHeader;
+import com.liaoinstan.springview.acfunheader.AcFunFooter;
+import com.liaoinstan.springview.acfunheader.AcFunHeader;
import com.liaoinstan.springview.widget.SpringView;
public class Demo5Activity extends AppCompatActivity {
@@ -18,11 +16,10 @@ public class Demo5Activity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo5);
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
- getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#ff8277")));
- springView = (SpringView) findViewById(R.id.springview);
+ springView = findViewById(R.id.springview);
springView.setGive(SpringView.Give.NONE);
springView.setListener(new SpringView.OnFreshListener() {
@Override
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo6/Demo6Activity.java b/demo/src/main/java/com/liaoinstan/demospring/demo6/Demo6Activity.java
index f5fbf22..81c5266 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/demo6/Demo6Activity.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo6/Demo6Activity.java
@@ -1,13 +1,12 @@
package com.liaoinstan.demospring.demo6;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
import android.widget.RadioGroup;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
import com.liaoinstan.demospring.R;
import com.liaoinstan.springview.container.DefaultFooter;
import com.liaoinstan.springview.container.DefaultHeader;
@@ -21,40 +20,26 @@ public class Demo6Activity extends AppCompatActivity implements RadioGroup.OnChe
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo6);
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- toolbar.setTitleTextColor(Color.parseColor("#999999"));
+ Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
- getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.WHITE));
((RadioGroup) findViewById(R.id.group_header)).setOnCheckedChangeListener(this);
- springView = (SpringView) findViewById(R.id.springview);
-// springView.setGive(SpringView.Give.NONE);
+ springView = findViewById(R.id.springview);
springView.setListener(new SpringView.OnFreshListener() {
@Override
public void onRefresh() {
//如果当前设置的头部是QQHeader,则不finish
- if (springView.getHeader() instanceof QQHeader)
- return;
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ if (springView.getHeader() instanceof QQHeader) return;
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
@Override
public void onLoadmore() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
});
- springView.setHeader(new QQHeader());
+ springView.setHeader(new QQHeader().setMovePara(1.5f)); //设置拖拽系数(值越大,移动越慢)
springView.setFooter(new DefaultFooter(this, R.drawable.progress_small));
}
@@ -62,11 +47,9 @@ public void run() {
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (checkedId) {
case R.id.drag_header:
- springView.setType(SpringView.Type.OVERLAP); //重叠模式
springView.setHeader(new QQHeader());
break;
case R.id.nomal_header:
- springView.setType(SpringView.Type.FOLLOW); //跟随模式
springView.setHeader(new DefaultHeader(this));
break;
}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo6/QQHeader.java b/demo/src/main/java/com/liaoinstan/demospring/demo6/QQHeader.java
index dbea846..0200a26 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/demo6/QQHeader.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo6/QQHeader.java
@@ -6,21 +6,28 @@
import android.widget.TextView;
import com.liaoinstan.demospring.R;
-import com.liaoinstan.springview.container.BaseHeader;
+import com.liaoinstan.springview.container.BaseSimpleHeader;
+import com.liaoinstan.springview.widget.SpringView;
/**
* Created by liaoinstan on 2016/3/23.
* 简单定制的QQ新年刷红包效果,可以在此基础上自己增加动画特效,这里只是模拟出该效果框架进行演示,故该Header不放在library里面
*/
-public class QQHeader extends BaseHeader {
+//TODO:重构后该demo失效,待修复
+public class QQHeader extends BaseSimpleHeader {
private TextView text_dot;
private int dotcount;
+ public QQHeader() {
+ setType(SpringView.Type.OVERLAP);
+ setMovePara(2.0f);
+ }
+
@Override
public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
- View view = inflater.inflate(R.layout.header_qq, viewGroup, true);
- text_dot = (TextView) view.findViewById(R.id.text_dot);
+ View view = inflater.inflate(R.layout.header_qq, viewGroup, false);
+ text_dot = view.findViewById(R.id.text_dot);
return view;
}
@@ -36,11 +43,17 @@ public int getDragMaxHeight(View rootView) {
@Override
public int getDragSpringHeight(View rootView) {
- return rootView.getMeasuredHeight()-100;
+ return rootView.getMeasuredHeight() - 100;
}
@Override
public void onPreDrag(View rootView) {
+ dotcount++;
+ text_dot.setText("x" + dotcount);
+ }
+
+ @Override
+ public void onFinishDrag(View rootView) {
}
@Override
@@ -53,8 +66,6 @@ public void onLimitDes(View rootView, boolean upORdown) {
@Override
public void onStartAnim() {
- dotcount++;
- text_dot.setText("x"+dotcount);
}
@Override
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo7/Demo7Activity.java b/demo/src/main/java/com/liaoinstan/demospring/demo7/Demo7Activity.java
index f896f84..2dd1d01 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/demo7/Demo7Activity.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo7/Demo7Activity.java
@@ -1,54 +1,44 @@
package com.liaoinstan.demospring.demo7;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
import com.liaoinstan.demospring.R;
-import com.liaoinstan.springview.container.MeituanFooter;
-import com.liaoinstan.springview.container.MeituanHeader;
+import com.liaoinstan.springview.meituanheader.MeituanFooter;
+import com.liaoinstan.springview.meituanheader.MeituanHeader;
import com.liaoinstan.springview.widget.SpringView;
public class Demo7Activity extends AppCompatActivity {
private SpringView springView;
+ //下拉过程动画
private int[] pullAnimSrcs = new int[]{R.drawable.mt_pull, R.drawable.mt_pull01, R.drawable.mt_pull02, R.drawable.mt_pull03, R.drawable.mt_pull04, R.drawable.mt_pull05};
+ //刷新中动画
private int[] refreshAnimSrcs = new int[]{R.drawable.mt_refreshing01, R.drawable.mt_refreshing02, R.drawable.mt_refreshing03, R.drawable.mt_refreshing04, R.drawable.mt_refreshing05, R.drawable.mt_refreshing06};
+ //加载更多底部动画
private int[] loadingAnimSrcs = new int[]{R.drawable.mt_loading01, R.drawable.mt_loading02};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo7);
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
- getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#18cfbe")));
- springView = (SpringView) findViewById(R.id.springview);
- springView.setType(SpringView.Type.FOLLOW);
+ springView = findViewById(R.id.springview);
springView.setListener(new SpringView.OnFreshListener() {
@Override
public void onRefresh() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 2000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 2000);
}
@Override
public void onLoadmore() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 2000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 2000);
}
});
springView.setHeader(new MeituanHeader(this, pullAnimSrcs, refreshAnimSrcs));
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo8/Demo8Activity.java b/demo/src/main/java/com/liaoinstan/demospring/demo8/Demo8Activity.java
index 68f0baa..7664d69 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/demo8/Demo8Activity.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo8/Demo8Activity.java
@@ -1,13 +1,14 @@
package com.liaoinstan.demospring.demo8;
-import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.Toolbar;
-import android.support.v7.widget.helper.ItemTouchHelper;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.appcompat.widget.Toolbar;
+import androidx.recyclerview.widget.ItemTouchHelper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -29,40 +30,29 @@ public class Demo8Activity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo8);
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
initData();
- springView = (SpringView) findViewById(R.id.springview);
- springView.setType(SpringView.Type.FOLLOW);
+ springView = findViewById(R.id.springview);
springView.setListener(new SpringView.OnFreshListener() {
@Override
public void onRefresh() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
@Override
public void onLoadmore() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
});
springView.setHeader(new DefaultHeader(this));
springView.setFooter(new DefaultFooter(this));
- RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle);
+ RecyclerView recyclerView = findViewById(R.id.recycle);
recyclerView.setHasFixedSize(true);
- RecyclerAdapter recyclerAdapter = new RecyclerAdapter(R.layout.item, mDatas);
+ RecyclerAdapter recyclerAdapter = new RecyclerAdapter(mDatas);
recyclerView.setAdapter(recyclerAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
@@ -72,30 +62,29 @@ public void run() {
private void initData() {
for (int i = 0; i < 20; i++) {
- mDatas.add(i == 0 ? "SpringView处理了水平滑动的手势冲突,侧滑删除试试" : "item" + i);
+ mDatas.add(i == 0 ? "SpringView不会和水平滑动有冲突,侧滑删除试试" : "item" + i);
}
}
+ /**
+ * Adapter for RecyclerView
+ */
private class RecyclerAdapter extends RecyclerView.Adapter implements MyItemTouchCallback.ItemTouchAdapter {
-
- private Context context;
- private int src;
private List results;
- public RecyclerAdapter(int src, List results) {
+ RecyclerAdapter(List results) {
this.results = results;
- this.src = src;
}
+ @NonNull
@Override
- public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- this.context = parent.getContext();
- View itemView = LayoutInflater.from(parent.getContext()).inflate(src, parent, false);
+ public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
return new MyViewHolder(itemView);
}
@Override
- public void onBindViewHolder(final MyViewHolder holder, int position) {
+ public void onBindViewHolder(@NonNull final MyViewHolder holder, int position) {
holder.textView.setText(results.get(position));
}
@@ -114,16 +103,16 @@ public void onSwiped(int position) {
notifyItemRemoved(position);
}
- public class MyViewHolder extends RecyclerView.ViewHolder {
+ class MyViewHolder extends RecyclerView.ViewHolder {
- public TextView textView;
+ TextView textView;
- public MyViewHolder(View itemView) {
+ MyViewHolder(View itemView) {
super(itemView);
ViewGroup.LayoutParams layoutParams = itemView.getLayoutParams();
- layoutParams.height = 100;
+ layoutParams.height = 150;
itemView.setLayoutParams(layoutParams);
- textView = (TextView) itemView.findViewById(R.id.item_text);
+ textView = itemView.findViewById(R.id.item_text);
}
}
}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo8/MyItemTouchCallback.java b/demo/src/main/java/com/liaoinstan/demospring/demo8/MyItemTouchCallback.java
index bb01f33..93f952c 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/demo8/MyItemTouchCallback.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo8/MyItemTouchCallback.java
@@ -1,12 +1,14 @@
package com.liaoinstan.demospring.demo8;
import android.graphics.Canvas;
-import android.support.v7.widget.GridLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.helper.ItemTouchHelper;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.RecyclerView;
/**
- * Created by Administrator on 2016/4/12.
+ * Created by liaoinstan on 2016/4/12.
* 实现侧滑删除功能
*/
public class MyItemTouchCallback extends ItemTouchHelper.Callback {
@@ -29,7 +31,7 @@ public boolean isItemViewSwipeEnabled() {
@Override
- public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
+ public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
final int swipeFlags = 0;
@@ -42,7 +44,7 @@ public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder v
}
@Override
- public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
+ public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
int fromPosition = viewHolder.getAdapterPosition();//得到拖动ViewHolder的position
int toPosition = target.getAdapterPosition();//得到目标ViewHolder的position
itemTouchAdapter.onMove(fromPosition, toPosition);
@@ -50,13 +52,13 @@ public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHol
}
@Override
- public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
+ public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
itemTouchAdapter.onSwiped(position);
}
@Override
- public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
+ public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
//滑动时改变Item的透明度
final float alpha = 1 - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9Activity.java b/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9Activity.java
index 606cdcf..aa55166 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9Activity.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9Activity.java
@@ -1,16 +1,19 @@
package com.liaoinstan.demospring.demo9;
import android.os.Bundle;
-import android.support.design.widget.TabLayout;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
+import com.google.android.material.tabs.TabLayout;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentPagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
import com.liaoinstan.demospring.R;
+@SuppressWarnings("all")
public class Demo9Activity extends AppCompatActivity {
private TabLayout tab;
private ViewPager pager;
@@ -20,21 +23,24 @@ public class Demo9Activity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo9);
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
- tab = (TabLayout) findViewById(R.id.tab);
- pager = (ViewPager) findViewById(R.id.pager);
+ tab = findViewById(R.id.tab);
+ pager = findViewById(R.id.pager);
adapterPager = new PagerAdapter(getSupportFragmentManager(), new String[]{"ScrollView", "RecyclerView", "note"});
pager.setAdapter(adapterPager);
tab.setupWithViewPager(pager);
}
+ /**
+ * Adapter for Viewpager
+ */
private class PagerAdapter extends FragmentPagerAdapter {
private String[] titles;
- public PagerAdapter(FragmentManager fm, String[] titles) {
+ PagerAdapter(FragmentManager fm, String[] titles) {
super(fm);
this.titles = titles;
}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9FragmentNote.java b/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9FragmentNote.java
index be16866..fcf14de 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9FragmentNote.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9FragmentNote.java
@@ -2,15 +2,17 @@
import android.os.Bundle;
import android.os.Handler;
-import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
import com.liaoinstan.demospring.R;
-import com.liaoinstan.springview.container.RotationFooter;
-import com.liaoinstan.springview.container.RotationHeader;
+import com.liaoinstan.springview.rotationheader.RotationFooter;
+import com.liaoinstan.springview.rotationheader.RotationHeader;
import com.liaoinstan.springview.widget.SpringView;
@@ -40,7 +42,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
@Nullable
@Override
- public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_demo9_note, container, false);
return rootView;
}
@@ -53,31 +55,18 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
private void initView() {
//初始化SpringView
- springView = (SpringView) rootView.findViewById(R.id.springview);
-// springView.setHeader(new AliHeader(getContext(), false));
-// springView.setFooter(new AliFooter(getContext(), false));
- springView.setType(SpringView.Type.OVERLAP); //设为重叠样式
- springView.setHeader(new RotationHeader(getContext()));
- springView.setFooter(new RotationFooter(getContext()));
+ springView = rootView.findViewById(R.id.springview);
+ springView.setHeader(new RotationHeader());
+ springView.setFooter(new RotationFooter());
springView.setListener(new SpringView.OnFreshListener() {
@Override
public void onRefresh() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
@Override
public void onLoadmore() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
});
}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9FragmentRecyclerView.java b/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9FragmentRecyclerView.java
index 2db6534..b0aeeea 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9FragmentRecyclerView.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9FragmentRecyclerView.java
@@ -3,18 +3,20 @@
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
-import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
import com.liaoinstan.demospring.R;
-import com.liaoinstan.springview.container.AliFooter;
-import com.liaoinstan.springview.container.AliHeader;
+import com.liaoinstan.springview.aliheader.AliFooter;
+import com.liaoinstan.springview.aliheader.AliHeader;
import com.liaoinstan.springview.widget.SpringView;
import java.util.ArrayList;
@@ -50,7 +52,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
@Nullable
@Override
- public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_demo9_recyclerview, container, false);
return rootView;
}
@@ -64,34 +66,26 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
private void initView() {
//初始化recyclerView
- recyclerView = (RecyclerView) rootView.findViewById(R.id.recycle);
+ recyclerView = rootView.findViewById(R.id.recycle);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerViewAdapter = new RecyclerViewAdapter(mDatas);
recyclerView.setAdapter(recyclerViewAdapter);
//初始化SpringView
- springView = (SpringView) rootView.findViewById(R.id.springview);
+ springView = rootView.findViewById(R.id.springview);
springView.setHeader(new AliHeader(getContext(), false));
springView.setFooter(new AliFooter(getContext(), false));
springView.setListener(new SpringView.OnFreshListener() {
@Override
public void onRefresh() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
@Override
public void onLoadmore() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- mDatas.add("add item");
- recyclerViewAdapter.notifyItemInserted(mDatas.size() - 1);
- springView.onFinishFreshAndLoad();
- }
+ new Handler().postDelayed(() -> {
+ mDatas.add("add item");
+ recyclerViewAdapter.notifyItemInserted(mDatas.size() - 1);
+ springView.onFinishFreshAndLoad();
}, 1000);
}
});
@@ -103,21 +97,25 @@ private void initData() {
}
}
+ /**
+ * Adapter for RecyclerView
+ */
private class RecyclerViewAdapter extends RecyclerView.Adapter {
private List results;
- public RecyclerViewAdapter(List results) {
+ RecyclerViewAdapter(List results) {
this.results = results;
}
+ @NonNull
@Override
- public RecyclerViewAdapter.SampleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ public RecyclerViewAdapter.SampleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
return new RecyclerViewAdapter.SampleViewHolder(view);
}
@Override
- public void onBindViewHolder(final RecyclerViewAdapter.SampleViewHolder holder, final int position) {
+ public void onBindViewHolder(@NonNull final RecyclerViewAdapter.SampleViewHolder holder, final int position) {
holder.text_item.setText(results.get(position));
if (position % 2 == 1) {
holder.text_item.setBackgroundColor(Color.parseColor("#ffffff"));
@@ -133,12 +131,12 @@ public int getItemCount() {
return results.size();
}
- public class SampleViewHolder extends RecyclerView.ViewHolder {
- public TextView text_item;
+ class SampleViewHolder extends RecyclerView.ViewHolder {
+ TextView text_item;
- public SampleViewHolder(View view) {
+ SampleViewHolder(View view) {
super(view);
- text_item = (TextView) view.findViewById(R.id.item_text);
+ text_item = view.findViewById(R.id.item_text);
}
}
}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9FragmentScrollView.java b/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9FragmentScrollView.java
index effb7d1..839d15a 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9FragmentScrollView.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/demo9/Demo9FragmentScrollView.java
@@ -2,17 +2,17 @@
import android.os.Bundle;
import android.os.Handler;
-import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.liaoinstan.demospring.R;
-import com.liaoinstan.springview.container.AliFooter;
-import com.liaoinstan.springview.container.AliHeader;
-import com.liaoinstan.springview.container.RotationFooter;
-import com.liaoinstan.springview.container.RotationHeader;
+import com.liaoinstan.springview.aliheader.AliFooter;
+import com.liaoinstan.springview.aliheader.AliHeader;
import com.liaoinstan.springview.widget.SpringView;
@@ -42,7 +42,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
@Nullable
@Override
- public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_demo9_scrollview, container, false);
return rootView;
}
@@ -55,28 +55,18 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
private void initView() {
//初始化SpringView
- springView = (SpringView) rootView.findViewById(R.id.springview);
+ springView = rootView.findViewById(R.id.springview);
springView.setHeader(new AliHeader(getContext(), R.drawable.ali, true));
springView.setFooter(new AliFooter(getContext(), false));
springView.setListener(new SpringView.OnFreshListener() {
@Override
public void onRefresh() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
@Override
public void onLoadmore() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
});
}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/demox/DemoXActivity.java b/demo/src/main/java/com/liaoinstan/demospring/demox/DemoXActivity.java
new file mode 100644
index 0000000..639355b
--- /dev/null
+++ b/demo/src/main/java/com/liaoinstan/demospring/demox/DemoXActivity.java
@@ -0,0 +1,46 @@
+package com.liaoinstan.demospring.demox;
+
+import android.os.Bundle;
+import android.os.Handler;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
+import com.liaoinstan.demospring.R;
+import com.liaoinstan.springview.aliheader.AliFooter;
+import com.liaoinstan.springview.wangyiheader.WangyiHeader;
+import com.liaoinstan.springview.widget.SpringView;
+
+/**
+ * 这是仿网易新闻的例子,使用了SpringView新增的'收场动画'特性
+ * 目前该特性还处于试验阶段,并未正式发布,如果效果不佳可能在后续版本移除,目前暂时移除掉这个例子
+ */
+public class DemoXActivity extends AppCompatActivity {
+
+ private SpringView springView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_demox);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ springView = findViewById(R.id.springview);
+ springView.setMovePara(1.3f);
+ springView.setListener(new SpringView.OnFreshListener() {
+ @Override
+ public void onRefresh() {
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 2000);
+ }
+
+ @Override
+ public void onLoadmore() {
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 2000);
+ }
+ });
+ springView.setHeader(new WangyiHeader());
+ springView.setFooter(new AliFooter(this));
+
+ }
+}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/test/TestActivity.java b/demo/src/main/java/com/liaoinstan/demospring/test/TestActivity.java
index 73e0cb6..9b62cd7 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/test/TestActivity.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/test/TestActivity.java
@@ -26,28 +26,18 @@ protected void onCreate(Bundle savedInstanceState) {
((CheckBox) findViewById(R.id.check_enableFooter)).setOnCheckedChangeListener(this);
- springView = (SpringView) findViewById(R.id.my);
+ springView = findViewById(R.id.my);
springView.setListener(new SpringView.OnFreshListener() {
@Override
public void onRefresh() {
Toast.makeText(TestActivity.this, "onRefresh", Toast.LENGTH_SHORT).show();
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
@Override
public void onLoadmore() {
Toast.makeText(TestActivity.this, "onLoadmore", Toast.LENGTH_SHORT).show();
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
});
}
@@ -61,6 +51,12 @@ public void onCheckedChanged(RadioGroup group, int checkedId) {
case R.id.follow:
springView.setType(SpringView.Type.FOLLOW);
break;
+ case R.id.drag:
+ springView.setType(SpringView.Type.DRAG);
+ break;
+ case R.id.scroll:
+ springView.setType(SpringView.Type.SCROLL);
+ break;
case R.id.both:
springView.setGive(SpringView.Give.BOTH);
break;
@@ -88,8 +84,8 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
}
}
- public void onClick(View v){
- switch (v.getId()){
+ public void onClick(View v) {
+ switch (v.getId()) {
case R.id.btn_callfresh:
springView.callFresh();
break;
diff --git a/demo/src/main/java/com/liaoinstan/demospring/utils/StatusBarUtil.java b/demo/src/main/java/com/liaoinstan/demospring/utils/StatusBarUtil.java
new file mode 100644
index 0000000..6fb9d05
--- /dev/null
+++ b/demo/src/main/java/com/liaoinstan/demospring/utils/StatusBarUtil.java
@@ -0,0 +1,79 @@
+package com.liaoinstan.demospring.utils;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Build;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.ColorRes;
+import androidx.core.content.ContextCompat;
+
+/**
+ * 状态栏样式工具栏
+ */
+public class StatusBarUtil {
+ //修改状态栏为全透明
+ public static void setTranslucent(Activity activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ setBarOverlap(activity);
+ setStatusBarColorInt(activity, Color.TRANSPARENT);
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ Window window = activity.getWindow();
+ window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); //4.4设置的透明样式
+ }
+ }
+
+ public static void setStatusBarColor(Activity activity, @ColorRes int colorResId) {
+ setStatusBarColorInt(activity, ContextCompat.getColor(activity, colorResId));
+ }
+
+ //设置状态栏背景色
+ public static void setStatusBarColorInt(Activity activity, @ColorInt int color) {
+ if (activity == null) return;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ Window window = activity.getWindow();
+ window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); //清除4.4设置的透明样式
+ window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ window.setStatusBarColor(color);
+ }
+ }
+
+ //设置状态栏重叠样式
+ public static void setBarOverlap(Activity activity) {
+ Window window = activity.getWindow();
+ window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+// | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+// | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+ }
+
+ //设置状态栏正常状态(系统默认)
+ public static void setBarFollow(Activity activity) {
+ Window window = activity.getWindow();
+ window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+ }
+
+ //设置状态栏深色文字
+ public static void setTextDark(Activity activity) {
+ Window window = activity.getWindow();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ //6.0以上调用系统方法
+ window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); //清除4.4设置的透明样式
+ window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ int flag = window.getDecorView().getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ window.getDecorView().setSystemUiVisibility(flag);
+ }
+ }
+
+ //设置状态栏浅色文字
+ public static void setTextLight(Activity activity) {
+ Window window = activity.getWindow();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ //6.0以上调用系统方法
+ int flag = window.getDecorView().getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ window.getDecorView().setSystemUiVisibility(flag);
+ }
+ }
+}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/warning/WarningActivity.java b/demo/src/main/java/com/liaoinstan/demospring/warning/WarningActivity.java
index f356871..d99dc20 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/warning/WarningActivity.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/warning/WarningActivity.java
@@ -2,66 +2,35 @@
import android.os.Bundle;
import android.os.Handler;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.ViewGroup;
-import android.widget.RadioGroup;
-import android.widget.TextView;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
import com.liaoinstan.demospring.R;
-import com.liaoinstan.springview.utils.DensityUtil;
import com.liaoinstan.springview.widget.SpringView;
-public class WarningActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener {
+public class WarningActivity extends AppCompatActivity {
- private TextView textView;
private SpringView springView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_warning);
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
- ((RadioGroup) findViewById(R.id.group_text)).setOnCheckedChangeListener(this);
-
- springView = (SpringView) findViewById(R.id.springview);
+ springView = findViewById(R.id.springview);
springView.setListener(new SpringView.OnFreshListener() {
@Override
public void onRefresh() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
@Override
public void onLoadmore() {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- springView.onFinishFreshAndLoad();
- }
- }, 1000);
+ new Handler().postDelayed(() -> springView.onFinishFreshAndLoad(), 1000);
}
});
springView.setHeader(new WarningHeader());
}
-
- @Override
- public void onCheckedChanged(RadioGroup group, int checkedId) {
- switch (checkedId) {
- case R.id.setwrap:
- textView = (TextView) springView.getHeaderView().findViewById(R.id.textView);
- textView.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
- break;
- case R.id.sethard:
- textView = (TextView) springView.getHeaderView().findViewById(R.id.textView);
- textView.getLayoutParams().width = DensityUtil.dp2px(120);
- break;
- }
- }
}
diff --git a/demo/src/main/java/com/liaoinstan/demospring/warning/WarningHeader.java b/demo/src/main/java/com/liaoinstan/demospring/warning/WarningHeader.java
index 72e8097..64ca40f 100644
--- a/demo/src/main/java/com/liaoinstan/demospring/warning/WarningHeader.java
+++ b/demo/src/main/java/com/liaoinstan/demospring/warning/WarningHeader.java
@@ -18,8 +18,8 @@ public class WarningHeader extends BaseHeader {
@Override
public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
- View view = inflater.inflate(R.layout.header_warning, viewGroup, true);
- textView = (TextView) view.findViewById(R.id.textView);
+ View view = inflater.inflate(R.layout.header_warning, viewGroup, false);
+ textView = view.findViewById(R.id.textView);
return view;
}
diff --git a/demo/src/main/res/color/weixin_color_select.xml b/demo/src/main/res/color/weixin_color_select.xml
new file mode 100644
index 0000000..d635501
--- /dev/null
+++ b/demo/src/main/res/color/weixin_color_select.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/demo/src/main/res/drawable-xxhdpi/adver.jpg b/demo/src/main/res/drawable-xxhdpi/adver.jpg
new file mode 100644
index 0000000..eb10887
Binary files /dev/null and b/demo/src/main/res/drawable-xxhdpi/adver.jpg differ
diff --git a/demo/src/main/res/drawable-xxhdpi/wx_program1.png b/demo/src/main/res/drawable-xxhdpi/wx_program1.png
new file mode 100644
index 0000000..5c0182c
Binary files /dev/null and b/demo/src/main/res/drawable-xxhdpi/wx_program1.png differ
diff --git a/demo/src/main/res/drawable-xxhdpi/wx_program2.png b/demo/src/main/res/drawable-xxhdpi/wx_program2.png
new file mode 100644
index 0000000..cd10fe8
Binary files /dev/null and b/demo/src/main/res/drawable-xxhdpi/wx_program2.png differ
diff --git a/demo/src/main/res/drawable-xxhdpi/wx_program3.png b/demo/src/main/res/drawable-xxhdpi/wx_program3.png
new file mode 100644
index 0000000..38eba80
Binary files /dev/null and b/demo/src/main/res/drawable-xxhdpi/wx_program3.png differ
diff --git a/demo/src/main/res/drawable-xxhdpi/wx_program4.png b/demo/src/main/res/drawable-xxhdpi/wx_program4.png
new file mode 100644
index 0000000..e953a34
Binary files /dev/null and b/demo/src/main/res/drawable-xxhdpi/wx_program4.png differ
diff --git a/demo/src/main/res/drawable-xxhdpi/wx_program5.png b/demo/src/main/res/drawable-xxhdpi/wx_program5.png
new file mode 100644
index 0000000..242721e
Binary files /dev/null and b/demo/src/main/res/drawable-xxhdpi/wx_program5.png differ
diff --git a/demo/src/main/res/drawable/ic_dashboard_black_24dp.xml b/demo/src/main/res/drawable/ic_dashboard_black_24dp.xml
new file mode 100644
index 0000000..46fc8de
--- /dev/null
+++ b/demo/src/main/res/drawable/ic_dashboard_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/demo/src/main/res/drawable/ic_home_black_24dp.xml b/demo/src/main/res/drawable/ic_home_black_24dp.xml
new file mode 100644
index 0000000..f8bb0b5
--- /dev/null
+++ b/demo/src/main/res/drawable/ic_home_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/demo/src/main/res/drawable/ic_notifications_black_24dp.xml b/demo/src/main/res/drawable/ic_notifications_black_24dp.xml
new file mode 100644
index 0000000..78b75c3
--- /dev/null
+++ b/demo/src/main/res/drawable/ic_notifications_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/demo/src/main/res/layout/activity_demo1.xml b/demo/src/main/res/layout/activity_demo1.xml
index 79a78c3..fbaf87c 100644
--- a/demo/src/main/res/layout/activity_demo1.xml
+++ b/demo/src/main/res/layout/activity_demo1.xml
@@ -1,5 +1,5 @@
-
-
-
+ app:popupTheme="@style/AppTheme.PopupOverlay"
+ app:title="Demo1Activity" />
-
+
-
-
+ android:layout_height="match_parent">
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_height="200dp"
+ android:background="#68b3f3" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/res/layout/activity_demo10.xml b/demo/src/main/res/layout/activity_demo10.xml
new file mode 100644
index 0000000..08c7d0b
--- /dev/null
+++ b/demo/src/main/res/layout/activity_demo10.xml
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/res/layout/activity_demo11.xml b/demo/src/main/res/layout/activity_demo11.xml
new file mode 100644
index 0000000..4c0f356
--- /dev/null
+++ b/demo/src/main/res/layout/activity_demo11.xml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/res/layout/activity_demo12.xml b/demo/src/main/res/layout/activity_demo12.xml
new file mode 100644
index 0000000..d33f945
--- /dev/null
+++ b/demo/src/main/res/layout/activity_demo12.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/res/layout/activity_demo13.xml b/demo/src/main/res/layout/activity_demo13.xml
new file mode 100644
index 0000000..db18078
--- /dev/null
+++ b/demo/src/main/res/layout/activity_demo13.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/res/layout/activity_demo14.xml b/demo/src/main/res/layout/activity_demo14.xml
new file mode 100644
index 0000000..3d9bc23
--- /dev/null
+++ b/demo/src/main/res/layout/activity_demo14.xml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/res/layout/activity_demo2.xml b/demo/src/main/res/layout/activity_demo2.xml
index 5e7c786..de59053 100644
--- a/demo/src/main/res/layout/activity_demo2.xml
+++ b/demo/src/main/res/layout/activity_demo2.xml
@@ -1,25 +1,27 @@
-
+ tools:context="com.liaoinstan.demospring.demo2.Demo2Activity">
-
-
+ android:background="#ffffff"
+ app:popupTheme="@style/AppTheme.PopupOverlay"
+ app:title="Demo2Activity"
+ app:titleTextColor="#999999" />
-
+
+ android:background="#ffffff">
-
+
diff --git a/demo/src/main/res/layout/activity_demo3.xml b/demo/src/main/res/layout/activity_demo3.xml
index 1f622c9..1ea5e05 100644
--- a/demo/src/main/res/layout/activity_demo3.xml
+++ b/demo/src/main/res/layout/activity_demo3.xml
@@ -1,5 +1,5 @@
-
-
-
-
-
+ android:background="#ffffff"
+ app:popupTheme="@style/AppTheme.PopupOverlay"
+ app:title="Demo3Activity"
+ app:titleTextColor="#999999" />
+
+ android:background="#ffffff">
-
-
@@ -50,8 +47,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
- android:layout_marginBottom="90dp"
android:layout_marginRight="20dp"
+ android:layout_marginBottom="90dp"
android:orientation="vertical">
+
diff --git a/demo/src/main/res/layout/activity_demo4.xml b/demo/src/main/res/layout/activity_demo4.xml
index 9449423..0f84d04 100644
--- a/demo/src/main/res/layout/activity_demo4.xml
+++ b/demo/src/main/res/layout/activity_demo4.xml
@@ -1,5 +1,5 @@
-
-
-
+ android:background="#18cfbe"
+ app:popupTheme="@style/AppTheme.PopupOverlay"
+ app:title="Demo4Activity" />
-
+
+ app:type="overlap">
+ android:layout_height="match_parent" />
+ android:textSize="12sp" />
-
+
diff --git a/demo/src/main/res/layout/activity_demo5.xml b/demo/src/main/res/layout/activity_demo5.xml
index 380c1d4..175e7e6 100644
--- a/demo/src/main/res/layout/activity_demo5.xml
+++ b/demo/src/main/res/layout/activity_demo5.xml
@@ -1,5 +1,5 @@
-
-
-
+ android:background="#ff8277"
+ app:popupTheme="@style/AppTheme.PopupOverlay"
+ app:title="Demo5Activity" />
-
+
-
-
+ android:layout_height="match_parent">
-
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_height="200dp"
+ android:background="#ffd5d1"
+ android:gravity="center"
+ android:text="We are in ScrollView"
+ android:textColor="#ffffff" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/res/layout/activity_demo6.xml b/demo/src/main/res/layout/activity_demo6.xml
index 831dd5f..0eef577 100644
--- a/demo/src/main/res/layout/activity_demo6.xml
+++ b/demo/src/main/res/layout/activity_demo6.xml
@@ -1,5 +1,5 @@
-
-
-
+ android:background="#ffffff"
+ app:popupTheme="@style/AppTheme.PopupOverlay"
+ app:title="Demo6Activity"
+ app:titleTextColor="#999999" />
-
+
+
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ android:text="We are in ScrollView"
+ android:textColor="#cccccc" />
+ android:textColor="#9dd2fc"
+ android:textSize="12sp" />
+ android:background="#ffffff" />
+
+ android:background="#e3f1fc" />
+ android:background="#ffffff" />
+
+ android:background="#e3f1fc" />
+ android:background="#ffffff" />
+
+ android:background="#e3f1fc" />
@@ -94,26 +98,28 @@
+
+ android:text="drag header" />
+
+ android:text="nomal header" />
-
+
diff --git a/demo/src/main/res/layout/activity_demo7.xml b/demo/src/main/res/layout/activity_demo7.xml
index d2e11c8..c3db9b6 100644
--- a/demo/src/main/res/layout/activity_demo7.xml
+++ b/demo/src/main/res/layout/activity_demo7.xml
@@ -1,5 +1,5 @@
-
-
-
+ android:background="#18cfbe"
+ app:popupTheme="@style/AppTheme.PopupOverlay"
+ app:title="Demo7Activity" />
-
+
@@ -37,54 +37,59 @@
+
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ android:text="We are in ScrollView"
+ android:textColor="#cccccc" />
+ android:textColor="#18cfbe"
+ android:textSize="12sp" />
+ android:background="#ffffff" />
+
+ android:background="#dbfbf8" />
+ android:background="#ffffff" />
+
+ android:background="#dbfbf8" />
+ android:background="#ffffff" />
+
+ android:background="#dbfbf8" />
@@ -93,4 +98,4 @@
-
+
diff --git a/demo/src/main/res/layout/activity_demo8.xml b/demo/src/main/res/layout/activity_demo8.xml
index 87d44c4..ddffa4b 100644
--- a/demo/src/main/res/layout/activity_demo8.xml
+++ b/demo/src/main/res/layout/activity_demo8.xml
@@ -1,5 +1,5 @@
-
-
-
-
+ app:popupTheme="@style/AppTheme.PopupOverlay"
+ app:title="Demo8Activity" />
+
-
+
-
-
+
diff --git a/demo/src/main/res/layout/activity_demo9.xml b/demo/src/main/res/layout/activity_demo9.xml
index 59a6bb3..acc564d 100644
--- a/demo/src/main/res/layout/activity_demo9.xml
+++ b/demo/src/main/res/layout/activity_demo9.xml
@@ -1,18 +1,18 @@
-
-
-
-
+ app:popupTheme="@style/AppTheme.PopupOverlay"
+ app:title="Demo9Activity" />
-
+
-
+
-
+ app:tabTextColor="#665793e6"
+ tools:background="#33ff0000" />
-
-
+
diff --git a/demo/src/main/res/layout/activity_demox.xml b/demo/src/main/res/layout/activity_demox.xml
new file mode 100644
index 0000000..8845e9b
--- /dev/null
+++ b/demo/src/main/res/layout/activity_demox.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/res/layout/activity_main.xml b/demo/src/main/res/layout/activity_main.xml
index 78a8961..3895899 100644
--- a/demo/src/main/res/layout/activity_main.xml
+++ b/demo/src/main/res/layout/activity_main.xml
@@ -7,73 +7,198 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:paddingTop="20dp">
+ android:paddingBottom="10dp">
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/demo/src/main/res/layout/activity_test.xml b/demo/src/main/res/layout/activity_test.xml
index 2cba2c7..507358c 100644
--- a/demo/src/main/res/layout/activity_test.xml
+++ b/demo/src/main/res/layout/activity_test.xml
@@ -21,10 +21,10 @@
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_weight="1"
- android:paddingBottom="40dp"
android:paddingLeft="10dp"
- android:paddingRight="20dp"
android:paddingTop="30dp"
+ android:paddingRight="20dp"
+ android:paddingBottom="40dp"
app:footer="@layout/footer"
app:header="@layout/header"
app:type="overlap">
@@ -133,34 +133,54 @@
-
-
-
-
-
+
-
+ android:orientation="horizontal">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+ app:popupTheme="@style/AppTheme.PopupOverlay"
+ app:title="Warning" />
-
+
+ android:layout_height="match_parent"
+ android:fillViewport="true">
-
-
-
-
-
-
-
-
-
-
+ android:layout_marginTop="600dp"
+ android:text="关注我"
+ android:textColor="#ffffff" />
+
+
+
+
-
-
-
-
-
-
-
-
+
diff --git a/demo/src/main/res/layout/content_demo12.xml b/demo/src/main/res/layout/content_demo12.xml
new file mode 100644
index 0000000..e4fd13e
--- /dev/null
+++ b/demo/src/main/res/layout/content_demo12.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/res/layout/fragment_demo9_note.xml b/demo/src/main/res/layout/fragment_demo9_note.xml
index 4f6325f..a7360d4 100644
--- a/demo/src/main/res/layout/fragment_demo9_note.xml
+++ b/demo/src/main/res/layout/fragment_demo9_note.xml
@@ -1,11 +1,10 @@
-
@@ -62,6 +61,6 @@
android:layout_height="200dp"
android:background="#e3f1fc" />
-
+
\ No newline at end of file
diff --git a/demo/src/main/res/layout/fragment_demo9_recyclerview.xml b/demo/src/main/res/layout/fragment_demo9_recyclerview.xml
index 38112c6..064de2e 100644
--- a/demo/src/main/res/layout/fragment_demo9_recyclerview.xml
+++ b/demo/src/main/res/layout/fragment_demo9_recyclerview.xml
@@ -4,7 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
diff --git a/demo/src/main/res/layout/fragment_demo9_scrollview.xml b/demo/src/main/res/layout/fragment_demo9_scrollview.xml
index f89c1bb..58031c2 100644
--- a/demo/src/main/res/layout/fragment_demo9_scrollview.xml
+++ b/demo/src/main/res/layout/fragment_demo9_scrollview.xml
@@ -1,11 +1,10 @@
-
@@ -28,7 +27,7 @@
android:layout_height="200dp"
android:background="#e3f1fc"
android:gravity="center"
- android:text="SpringView支持了AppBarLayout\n\n不会再和AppBarLayout滚动冲突"
+ android:text="SpringView支持了AppBarLayout\n\n不会和AppBarLayout滚动冲突"
android:textColor="#9dd2fc"
android:textSize="12sp" />
@@ -63,6 +62,6 @@
android:background="#e3f1fc" />
-
+
\ No newline at end of file
diff --git a/demo/src/main/res/layout/header_recycle.xml b/demo/src/main/res/layout/header_recycle.xml
deleted file mode 100644
index c42c99b..0000000
--- a/demo/src/main/res/layout/header_recycle.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
diff --git a/demo/src/main/res/layout/lay_test.xml b/demo/src/main/res/layout/lay_test.xml
new file mode 100644
index 0000000..9ab2e6c
--- /dev/null
+++ b/demo/src/main/res/layout/lay_test.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/demo/src/main/res/menu/bottom_nav_menu.xml b/demo/src/main/res/menu/bottom_nav_menu.xml
new file mode 100644
index 0000000..8cf4034
--- /dev/null
+++ b/demo/src/main/res/menu/bottom_nav_menu.xml
@@ -0,0 +1,19 @@
+
+
diff --git a/demo/src/main/res/menu/menu_main.xml b/demo/src/main/res/menu/menu_main.xml
deleted file mode 100644
index 7d5901d..0000000
--- a/demo/src/main/res/menu/menu_main.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/demo/src/main/res/values/colors.xml b/demo/src/main/res/values/colors.xml
index 3ab3e9c..cb09b5e 100644
--- a/demo/src/main/res/values/colors.xml
+++ b/demo/src/main/res/values/colors.xml
@@ -3,4 +3,4 @@
#3F51B5
#303F9F
#FF4081
-
+
\ No newline at end of file
diff --git a/demo/src/main/res/values/strings.xml b/demo/src/main/res/values/strings.xml
index df40af5..00c98a4 100644
--- a/demo/src/main/res/values/strings.xml
+++ b/demo/src/main/res/values/strings.xml
@@ -1,14 +1,3 @@
DemoSpring
- Settings
- Demo1Activity
- Demo2Activity
- Demo3Activity
- Demo4Activity
- Demo5Activity
- Demo6Activity
- Demo7Activity
- Demo8Activity
- Demo9Activity
- Warning
diff --git a/demo/src/main/res/values/styles.xml b/demo/src/main/res/values/styles.xml
index ab01345..8667f54 100644
--- a/demo/src/main/res/values/styles.xml
+++ b/demo/src/main/res/values/styles.xml
@@ -22,4 +22,16 @@
- false
+
+
diff --git a/gradle.properties b/gradle.properties
index 1d3591c..915f0e6 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -15,4 +15,6 @@
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
\ No newline at end of file
+# org.gradle.parallel=true
+android.enableJetifier=true
+android.useAndroidX=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f301b41..7b033ae 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Oct 21 11:34:03 PDT 2015
+#Mon Apr 29 14:42:09 CST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
diff --git a/header_acfun/.gitignore b/header_acfun/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/header_acfun/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/header_acfun/build.gradle b/header_acfun/build.gradle
new file mode 100644
index 0000000..5eef18f
--- /dev/null
+++ b/header_acfun/build.gradle
@@ -0,0 +1,41 @@
+apply plugin: 'com.android.library'
+apply plugin: 'com.novoda.bintray-release'
+
+android {
+ compileSdkVersion rootProject.ext.compileSdkVersion
+
+ defaultConfig {
+ minSdkVersion rootProject.ext.minSdkVersion
+ targetSdkVersion rootProject.ext.targetSdkVersion
+ versionCode 1
+ versionName "1.0"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "androidx.appcompat:appcompat:$supportLibraryVersion"
+ implementation project(':library')
+// implementation 'com.liaoinstan.springview:library:1.7.0'
+}
+
+publish {
+ artifactId = 'AcfunHeader' // artifactId
+ uploadName = 'SpringView:AcfunHeader' // 展示名字
+ publishVersion = '1.7.0' // 版本
+ desc = rootProject.ext.desc
+ userOrg = rootProject.userOrg
+ groupId = rootProject.groupId
+ website = rootProject.website
+ licences = rootProject.licences
+ autoPublish = false
+ dryRun = true
+}
\ No newline at end of file
diff --git a/header_acfun/proguard-rules.pro b/header_acfun/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/header_acfun/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/header_acfun/src/main/AndroidManifest.xml b/header_acfun/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3a1b70e
--- /dev/null
+++ b/header_acfun/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/library/src/main/java/com/liaoinstan/springview/container/AcFunFooter.java b/header_acfun/src/main/java/com/liaoinstan/springview/acfunheader/AcFunFooter.java
similarity index 76%
rename from library/src/main/java/com/liaoinstan/springview/container/AcFunFooter.java
rename to header_acfun/src/main/java/com/liaoinstan/springview/acfunheader/AcFunFooter.java
index adbbbc8..d647915 100644
--- a/library/src/main/java/com/liaoinstan/springview/container/AcFunFooter.java
+++ b/header_acfun/src/main/java/com/liaoinstan/springview/acfunheader/AcFunFooter.java
@@ -1,4 +1,4 @@
-package com.liaoinstan.springview.container;
+package com.liaoinstan.springview.acfunheader;
import android.content.Context;
import android.view.LayoutInflater;
@@ -6,26 +6,30 @@
import android.view.ViewGroup;
import android.widget.ImageView;
-import com.liaoinstan.springview.R;
+import com.liaoinstan.springview.container.BaseFooter;
+import com.liaoinstan.springview.container.BaseSimpleFooter;
import com.liaoinstan.springview.utils.DensityUtil;
+import com.liaoinstan.springview.widget.SpringView;
/**
* Created by Administrator on 2016/3/23.
*/
-public class AcFunFooter extends BaseFooter {
+public class AcFunFooter extends BaseSimpleFooter {
private Context context;
private int imgSrc;
private ImageView acfun_footer_img;
public AcFunFooter(Context context, int imgSrc) {
+ setType(SpringView.Type.OVERLAP);
+ setMovePara(2.0f);
this.context = context;
this.imgSrc = imgSrc;
}
@Override
public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
- View view = inflater.inflate(R.layout.acfun_footer, viewGroup, true);
- acfun_footer_img = (ImageView) view.findViewById(R.id.acfun_footer_img);
+ View view = inflater.inflate(R.layout.acfun_footer, viewGroup, false);
+ acfun_footer_img = view.findViewById(R.id.acfun_footer_img);
acfun_footer_img.setImageResource(imgSrc);
return view;
}
diff --git a/library/src/main/java/com/liaoinstan/springview/container/AcFunHeader.java b/header_acfun/src/main/java/com/liaoinstan/springview/acfunheader/AcFunHeader.java
similarity index 79%
rename from library/src/main/java/com/liaoinstan/springview/container/AcFunHeader.java
rename to header_acfun/src/main/java/com/liaoinstan/springview/acfunheader/AcFunHeader.java
index d93e3c5..8a7d160 100644
--- a/library/src/main/java/com/liaoinstan/springview/container/AcFunHeader.java
+++ b/header_acfun/src/main/java/com/liaoinstan/springview/acfunheader/AcFunHeader.java
@@ -1,4 +1,4 @@
-package com.liaoinstan.springview.container;
+package com.liaoinstan.springview.acfunheader;
import android.content.Context;
import android.view.LayoutInflater;
@@ -6,26 +6,29 @@
import android.view.ViewGroup;
import android.widget.ImageView;
-import com.liaoinstan.springview.R;
+import com.liaoinstan.springview.container.BaseSimpleHeader;
import com.liaoinstan.springview.utils.DensityUtil;
+import com.liaoinstan.springview.widget.SpringView;
/**
* Created by liaoinstan on 2016/3/23.
*/
-public class AcFunHeader extends BaseHeader {
+public class AcFunHeader extends BaseSimpleHeader {
private Context context;
private int imgSrc;
private ImageView acfun_header_img;
public AcFunHeader(Context context, int imgSrc) {
+ setType(SpringView.Type.OVERLAP);
+ setMovePara(2.0f);
this.context = context;
this.imgSrc = imgSrc;
}
@Override
public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
- View view = inflater.inflate(R.layout.acfun_header, viewGroup, true);
- acfun_header_img = (ImageView) view.findViewById(R.id.acfun_header_img);
+ View view = inflater.inflate(R.layout.acfun_header, viewGroup, false);
+ acfun_header_img = view.findViewById(R.id.acfun_header_img);
acfun_header_img.setImageResource(imgSrc);
return view;
}
diff --git a/library/src/main/res/layout/acfun_footer.xml b/header_acfun/src/main/res/layout/acfun_footer.xml
similarity index 100%
rename from library/src/main/res/layout/acfun_footer.xml
rename to header_acfun/src/main/res/layout/acfun_footer.xml
diff --git a/library/src/main/res/layout/acfun_header.xml b/header_acfun/src/main/res/layout/acfun_header.xml
similarity index 100%
rename from library/src/main/res/layout/acfun_header.xml
rename to header_acfun/src/main/res/layout/acfun_header.xml
diff --git a/header_acfun/src/main/res/values/strings.xml b/header_acfun/src/main/res/values/strings.xml
new file mode 100644
index 0000000..7be8c1f
--- /dev/null
+++ b/header_acfun/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ acfunheader
+
diff --git a/header_ali/.gitignore b/header_ali/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/header_ali/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/header_ali/build.gradle b/header_ali/build.gradle
new file mode 100644
index 0000000..bd441bb
--- /dev/null
+++ b/header_ali/build.gradle
@@ -0,0 +1,42 @@
+apply plugin: 'com.android.library'
+apply plugin: 'com.novoda.bintray-release'
+
+android {
+ compileSdkVersion rootProject.ext.compileSdkVersion
+
+ defaultConfig {
+ minSdkVersion rootProject.ext.minSdkVersion
+ targetSdkVersion rootProject.ext.targetSdkVersion
+ versionCode 1
+ versionName "1.0"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "androidx.appcompat:appcompat:$supportLibraryVersion"
+ implementation "com.google.android.material:material:$materialLibraryVersion"
+ implementation project(':library')
+// implementation 'com.liaoinstan.springview:library:1.7.0'
+}
+
+publish {
+ artifactId = 'AliHeader' // artifactId
+ uploadName = 'SpringView:AliHeader' // 展示名字
+ publishVersion = '1.7.0' // 版本
+ desc = rootProject.ext.desc
+ userOrg = rootProject.userOrg
+ groupId = rootProject.groupId
+ website = rootProject.website
+ licences = rootProject.licences
+ autoPublish = false
+ dryRun = true
+}
\ No newline at end of file
diff --git a/header_ali/proguard-rules.pro b/header_ali/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/header_ali/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/header_ali/src/main/AndroidManifest.xml b/header_ali/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3146719
--- /dev/null
+++ b/header_ali/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/library/src/main/java/com/liaoinstan/springview/container/AliFooter.java b/header_ali/src/main/java/com/liaoinstan/springview/aliheader/AliFooter.java
similarity index 86%
rename from library/src/main/java/com/liaoinstan/springview/container/AliFooter.java
rename to header_ali/src/main/java/com/liaoinstan/springview/aliheader/AliFooter.java
index ac351a0..616241c 100644
--- a/library/src/main/java/com/liaoinstan/springview/container/AliFooter.java
+++ b/header_ali/src/main/java/com/liaoinstan/springview/aliheader/AliFooter.java
@@ -1,7 +1,6 @@
-package com.liaoinstan.springview.container;
+package com.liaoinstan.springview.aliheader;
import android.content.Context;
-import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -11,12 +10,15 @@
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.liaoinstan.springview.R;
+import androidx.core.content.ContextCompat;
+
+import com.liaoinstan.springview.container.BaseSimpleFooter;
+import com.liaoinstan.springview.widget.SpringView;
/**
* Created by Administrator on 2016/3/21.
*/
-public class AliFooter extends BaseFooter {
+public class AliFooter extends BaseSimpleFooter {
private Context context;
private int rotationSrc;
private int arrowSrc;
@@ -50,6 +52,8 @@ public AliFooter(Context context, int logoSrc, boolean isShowText) {
}
public AliFooter(Context context, int rotationSrc, int arrowSrc, int logoSrc, boolean isShowText) {
+ setType(SpringView.Type.FOLLOW);
+ setMovePara(2.0f);
this.context = context;
this.rotationSrc = rotationSrc;
this.arrowSrc = arrowSrc;
@@ -65,11 +69,11 @@ public AliFooter(Context context, int rotationSrc, int arrowSrc, int logoSrc, bo
@Override
public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
- View view = inflater.inflate(R.layout.ali_footer, viewGroup, true);
- footerTitle = (TextView) view.findViewById(R.id.ali_footer_text);
- footerArrow = (ImageView) view.findViewById(R.id.ali_footer_arrow);
- footerLogo = (ImageView) view.findViewById(R.id.ali_footer_logo);
- footerProgressbar = (ProgressBar) view.findViewById(R.id.ali_footer_progressbar);
+ View view = inflater.inflate(R.layout.ali_footer, viewGroup, false);
+ footerTitle = view.findViewById(R.id.ali_footer_text);
+ footerArrow = view.findViewById(R.id.ali_footer_arrow);
+ footerLogo = view.findViewById(R.id.ali_footer_logo);
+ footerProgressbar = view.findViewById(R.id.ali_footer_progressbar);
frame = view.findViewById(R.id.ali_frame);
if (logoSrc != 0) footerLogo.setImageResource(logoSrc);
if (!isShowText) footerTitle.setVisibility(View.GONE);
diff --git a/library/src/main/java/com/liaoinstan/springview/container/AliHeader.java b/header_ali/src/main/java/com/liaoinstan/springview/aliheader/AliHeader.java
similarity index 86%
rename from library/src/main/java/com/liaoinstan/springview/container/AliHeader.java
rename to header_ali/src/main/java/com/liaoinstan/springview/aliheader/AliHeader.java
index 039d03d..b5bea5c 100644
--- a/library/src/main/java/com/liaoinstan/springview/container/AliHeader.java
+++ b/header_ali/src/main/java/com/liaoinstan/springview/aliheader/AliHeader.java
@@ -1,7 +1,6 @@
-package com.liaoinstan.springview.container;
+package com.liaoinstan.springview.aliheader;
import android.content.Context;
-import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -11,12 +10,15 @@
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.liaoinstan.springview.R;
+import androidx.core.content.ContextCompat;
+
+import com.liaoinstan.springview.container.BaseSimpleHeader;
+import com.liaoinstan.springview.widget.SpringView;
/**
* Created by Administrator on 2016/3/21.
*/
-public class AliHeader extends BaseHeader {
+public class AliHeader extends BaseSimpleHeader {
private Context context;
private int rotationSrc;
private int arrowSrc;
@@ -50,6 +52,8 @@ public AliHeader(Context context, int logoSrc, boolean isShowText) {
}
public AliHeader(Context context, int rotationSrc, int arrowSrc, int logoSrc, boolean isShowText) {
+ setType(SpringView.Type.FOLLOW);
+ setMovePara(2.0f);
this.context = context;
this.rotationSrc = rotationSrc;
this.arrowSrc = arrowSrc;
@@ -65,11 +69,11 @@ public AliHeader(Context context, int rotationSrc, int arrowSrc, int logoSrc, bo
@Override
public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
- View view = inflater.inflate(R.layout.ali_header, viewGroup, true);
- headerTitle = (TextView) view.findViewById(R.id.ali_header_text);
- headerArrow = (ImageView) view.findViewById(R.id.ali_header_arrow);
- headerLogo = (ImageView) view.findViewById(R.id.ali_header_logo);
- headerProgressbar = (ProgressBar) view.findViewById(R.id.ali_header_progressbar);
+ View view = inflater.inflate(R.layout.ali_header, viewGroup, false);
+ headerTitle = view.findViewById(R.id.ali_header_text);
+ headerArrow = view.findViewById(R.id.ali_header_arrow);
+ headerLogo = view.findViewById(R.id.ali_header_logo);
+ headerProgressbar = view.findViewById(R.id.ali_header_progressbar);
frame = view.findViewById(R.id.ali_frame);
if (logoSrc != 0) headerLogo.setImageResource(logoSrc);
if (!isShowText) headerTitle.setVisibility(View.GONE);
diff --git a/library/src/main/res/layout/ali_footer.xml b/header_ali/src/main/res/layout/ali_footer.xml
similarity index 100%
rename from library/src/main/res/layout/ali_footer.xml
rename to header_ali/src/main/res/layout/ali_footer.xml
diff --git a/library/src/main/res/layout/ali_header.xml b/header_ali/src/main/res/layout/ali_header.xml
similarity index 88%
rename from library/src/main/res/layout/ali_header.xml
rename to header_ali/src/main/res/layout/ali_header.xml
index bdaabf6..0663677 100644
--- a/library/src/main/res/layout/ali_header.xml
+++ b/header_ali/src/main/res/layout/ali_header.xml
@@ -1,4 +1,5 @@
@@ -9,7 +10,9 @@
android:layout_height="wrap_content"
android:layout_marginLeft="80dp"
android:layout_marginRight="80dp"
- android:adjustViewBounds="true"/>
+ android:adjustViewBounds="true"
+ tools:background="#cccccc"
+ tools:layout_height="80dp" />
+ android:adjustViewBounds="true" />
diff --git a/header_ali/src/main/res/values/strings.xml b/header_ali/src/main/res/values/strings.xml
new file mode 100644
index 0000000..b93ef99
--- /dev/null
+++ b/header_ali/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ aliheader
+
diff --git a/header_du/.gitignore b/header_du/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/header_du/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/header_du/build.gradle b/header_du/build.gradle
new file mode 100644
index 0000000..196b511
--- /dev/null
+++ b/header_du/build.gradle
@@ -0,0 +1,42 @@
+apply plugin: 'com.android.library'
+apply plugin: 'com.novoda.bintray-release'
+
+android {
+ compileSdkVersion rootProject.ext.compileSdkVersion
+
+ defaultConfig {
+ minSdkVersion rootProject.ext.minSdkVersion
+ targetSdkVersion rootProject.ext.targetSdkVersion
+ versionCode 1
+ versionName "1.0"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "androidx.appcompat:appcompat:$supportLibraryVersion"
+ implementation "com.google.android.material:material:$materialLibraryVersion"
+ implementation project(':library')
+// implementation 'com.liaoinstan.springview:library:1.7.0'
+}
+
+publish {
+ artifactId = 'DuHeader' // artifactId
+ uploadName = 'SpringView:DuHeader' // 展示名字
+ publishVersion = '1.7.0' // 版本
+ desc = rootProject.ext.desc
+ userOrg = rootProject.userOrg
+ groupId = rootProject.groupId
+ website = rootProject.website
+ licences = rootProject.licences
+ autoPublish = false
+ dryRun = true
+}
diff --git a/header_du/consumer-rules.pro b/header_du/consumer-rules.pro
new file mode 100644
index 0000000..e69de29
diff --git a/header_du/proguard-rules.pro b/header_du/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/header_du/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/header_du/src/main/AndroidManifest.xml b/header_du/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..72638fc
--- /dev/null
+++ b/header_du/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/header_du/src/main/java/com/liaoinstan/springview/duheader/DuHeader.java b/header_du/src/main/java/com/liaoinstan/springview/duheader/DuHeader.java
new file mode 100644
index 0000000..d64c2e7
--- /dev/null
+++ b/header_du/src/main/java/com/liaoinstan/springview/duheader/DuHeader.java
@@ -0,0 +1,49 @@
+package com.liaoinstan.springview.duheader;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.liaoinstan.springview.container.BaseSimpleHeader;
+
+public class DuHeader extends BaseSimpleHeader {
+
+ private DuView duView;
+
+ @Override
+ public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
+ View rootView = inflater.inflate(R.layout.du_header, viewGroup, false);
+ duView = rootView.findViewById(R.id.du_view);
+ return rootView;
+ }
+
+ @Override
+ public void onDropAnim(View rootView, int dy) {
+ int dragLimitHeight = getDragLimitHeight(rootView);
+ float lv = 1 - (float) (dy) / (dragLimitHeight); //1-0
+ duView.setProgress(lv * 100);
+ //向下偏移半个view高度,平滑拖拽
+ float height = duView.getMeasuredHeight() * 0.5f;
+ float tranY = height * lv;
+ duView.setTranslationY(tranY);
+ }
+
+ @Override
+ public int getDragLimitHeight(View rootView) {
+ return rootView.getMeasuredHeight();
+ }
+
+ @Override
+ public void onLimitDes(View rootView, boolean upORdown) {
+ }
+
+ @Override
+ public void onStartAnim() {
+ duView.startAnim();
+ }
+
+ @Override
+ public void onFinishAnim() {
+ duView.resetAnim();
+ }
+}
diff --git a/header_du/src/main/java/com/liaoinstan/springview/duheader/DuView.java b/header_du/src/main/java/com/liaoinstan/springview/duheader/DuView.java
new file mode 100644
index 0000000..f140b8b
--- /dev/null
+++ b/header_du/src/main/java/com/liaoinstan/springview/duheader/DuView.java
@@ -0,0 +1,276 @@
+package com.liaoinstan.springview.duheader;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 仿"毒"App 下拉动画的自定义view,通过不同的拉动系数设置其绘制效果
+ * {@link #setProgress(float)} 参数范围 0 - 100
+ * 当参数为0时,显示完整的'毒'字,为100时笔画完全散落,可以超出100
+ */
+public class DuView extends View {
+
+ private static final float widthDu = 124;
+
+ private Paint paint = new Paint();
+ private List lines = new ArrayList<>();
+
+ public DuView(Context context) {
+ this(context, null);
+ }
+
+ public DuView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DuView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ }
+
+ private void init() {
+ paint.setStrokeWidth(3);
+ paint.setAntiAlias(true);
+
+ //一横
+ lines.add(new Line(20, 24, 61, 24, 4.5f, 1.7f, 57f));
+ lines.add(new Line(61, 24, 101, 24, 4.3f, 1, 53.5f));
+ //二横
+ lines.add(new Line(20, 38, 61, 38, 4.2f, 1.9f, 49f));
+ lines.add(new Line(61, 38, 102, 38, 4, 1.5f, 45.5f));
+ //竖
+ lines.add(new Line(61, 20, 61, 53, 3.7f, 1.7f, 42f));
+ //母:一横
+ lines.add(new Line(19, 52, 61, 52, 3.5f, 1.7f, 38.5f));
+ lines.add(new Line(61, 52, 104, 52, 3.3f, 1.7f, 35f));
+ //母:左竖
+ lines.add(new Line(20, 51, 20, 88, 3.1f, 2, 31.5f));
+ //母:右竖
+ lines.add(new Line(103, 51, 103, 88, 3, 2, 28f));
+ //母:二横
+ lines.add(new Line(16, 69, 62, 69, 3, 2, 24.5f));
+ lines.add(new Line(62, 69, 107, 69, 3, 2, 21f));
+ //母:三横
+ lines.add(new Line(19, 87, 62, 87, 3, 2, 17.5f));
+ lines.add(new Line(62, 87, 104, 87, 3, 3, 14f));
+ //母:中竖
+ lines.add(new Line(65, 52, 60, 69, 3, 2, 10.5f));
+ lines.add(new Line(65, 69, 60, 87, 3, 2.5f, 7f));
+ //勾
+ lines.add(new Line(102, 87, 97, 102, 3, 3, 3.5f));
+ lines.add(new Line(52, 102, 98, 102, 3, 3.5f, 0));
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+ int mWidth = (int) widthDu + getPaddingLeft() + getPaddingRight();
+ int mHeight = (int) widthDu + getPaddingTop() + getPaddingBottom();
+ if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT && getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ setMeasuredDimension(mWidth, mHeight);
+ } else if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ setMeasuredDimension(mWidth, heightSize);
+ } else if (getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ setMeasuredDimension(widthSize, mHeight);
+ }
+ for (Line line : lines) {
+ line.setOffsetX(getMeasuredWidth() / 2 - widthDu / 2);
+ line.setOffsetY(getMeasuredHeight() / 2 - widthDu / 2);
+ }
+ }
+
+ /**
+ * 0-100,可以超出
+ */
+ public void setProgress(float progress) {
+ for (Line line : lines) {
+ line.setProgress(progress);
+ }
+ postInvalidate();
+ }
+
+ //##################################
+ //######### 动画 ###########
+ //##################################
+
+ int value;
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ value++;
+ if (value >= lines.size() + 3) {
+ value = 0;
+ }
+ for (Line line : lines) {
+ line.heightLight = false;
+ }
+ if (value - 1 >= 0 && value - 1 < lines.size()) {
+ lines.get(value - 1).heightLight = true;
+ }
+ if (value - 2 >= 0 && value - 2 < lines.size()) {
+ lines.get(value - 2).heightLight = true;
+ }
+ if (value - 3 >= 0 && value - 3 < lines.size()) {
+ lines.get(value - 3).heightLight = true;
+ }
+ postInvalidate();
+
+ postDelayed(runnable, 70);
+ }
+ };
+
+ public void startAnim() {
+ post(runnable);
+ }
+
+ public void stopAnim() {
+ removeCallbacks(runnable);
+ }
+
+ public void resetAnim() {
+ removeCallbacks(runnable);
+ value = 0;
+ for (Line line : lines) {
+ line.heightLight = false;
+ }
+ postInvalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ for (Line line : lines) {
+ line.drawLine(canvas);
+ }
+ }
+
+ private class Line {
+ float x1, y1, x2, y2;
+ float offsetX, offsetY;
+ float degree;
+ float translateX;
+ float zoom = 1;
+ float zoomSize = 1;
+
+ float degK;
+ float transK;
+ float delay;
+ static final float WIDTH = 3;
+ boolean heightLight;
+
+ Line(float x1, float y1, float x2, float y2) {
+ this(x1, y1, x2, y2, 0, 0, 0);
+ }
+
+ Line(float x1, float y1, float x2, float y2, float degK, float transK, float delay) {
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x2;
+ this.y2 = y2;
+ this.degK = degK;
+ this.transK = transK;
+ this.delay = delay;
+ }
+
+ void setOffsetX(float offsetX) {
+ this.offsetX = offsetX;
+ }
+
+ void setOffsetY(float offsetY) {
+ this.offsetY = offsetY;
+ }
+
+ void setProgress(float progress) {
+ float dProgress = progress - delay > 0 ? progress - delay : 0;
+ degree = dProgress * degK;
+ translateX = dProgress * transK;
+ zoom = 1 - dProgress / 100;
+ zoomSize = 1 - dProgress / (100 - delay);
+ }
+
+ float[] center() {
+ float cx = (getX1() + getX2()) / 2;
+ float cy = (getY1() + getY2()) / 2;
+ return new float[]{cx, cy};
+ }
+
+ Line rotate(float degree) {
+ float[] center = center();
+ float cx = center[0];
+ float cy = center[1];
+ float dx = getX2() - cx;
+ float dy = getY2() - cy;
+ float k = (float) Math.toRadians((double) degree);
+ float dtx = (float) (Math.cos(k) * dx + Math.sin(k) * dy);
+ float dty = (float) (Math.cos(k) * dy - Math.sin(k) * dx);
+ float x1 = cx + dtx;
+ float y1 = cy + dty;
+ float x2 = 2 * cx - x1;
+ float y2 = 2 * cy - y1;
+ return new Line(x1, y1, x2, y2);
+ }
+
+ Line zoom(float zoom) {
+ float[] center = center();
+ float cx = center[0];
+ float cy = center[1];
+ float x1 = (1 - zoom) * cx + zoom * getX1();
+ float y1 = (1 - zoom) * cy + zoom * getY1();
+ float x2 = 2 * cx - x1;
+ float y2 = 2 * cy - y1;
+ return new Line(x1, y1, x2, y2);
+ }
+
+ Line translateX(float tansX) {
+ return new Line(getX1() + tansX, getY1(), getX2() + tansX, getY2());
+ }
+
+ void drawLine(Canvas canvas) {
+ Line drawLine = this.rotate(degree).translateX(translateX).zoom(zoom);
+ if (zoomSize == 0) {
+ paint.setAlpha(0);
+ paint.setStrokeWidth(0.01f);
+ } else {
+ paint.setAlpha(1);
+ paint.setStrokeWidth(WIDTH * zoomSize);
+ }
+ paint.setColor(Color.parseColor(heightLight ? "#aa000000" : "#55000000"));
+ canvas.drawLine(drawLine.getX1(), drawLine.getY1(), drawLine.getX2(), drawLine.getY2(), paint);
+ }
+
+ //get & set
+
+ float getX1() {
+ return x1 + offsetX;
+ }
+
+ float getY1() {
+ return y1 + offsetY;
+ }
+
+ float getX2() {
+ return x2 + offsetX;
+ }
+
+ float getY2() {
+ return y2 + offsetY;
+ }
+ }
+}
diff --git a/header_du/src/main/java/com/liaoinstan/springview/duheader/TopBarFrameLayout.java b/header_du/src/main/java/com/liaoinstan/springview/duheader/TopBarFrameLayout.java
new file mode 100644
index 0000000..ea61691
--- /dev/null
+++ b/header_du/src/main/java/com/liaoinstan/springview/duheader/TopBarFrameLayout.java
@@ -0,0 +1,142 @@
+package com.liaoinstan.springview.duheader;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.ViewTreeObserver;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+
+/**
+ * create by liaoinstan 2019/12/13
+ * 配合DuHeader一起使用,实现和‘毒’App一样的header效果,也可以自行定制单独使用
+ * 继承自FrameLayout
+ */
+public class TopBarFrameLayout extends FrameLayout {
+
+ private TextView textTopBar;
+
+ public TopBarFrameLayout(Context context) {
+ this(context, null);
+ }
+
+ public TopBarFrameLayout(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TopBarFrameLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ LayoutInflater.from(getContext()).inflate(R.layout.view_top_bar, this, true);
+ textTopBar = findViewById(R.id.text_top_bar);
+ super.onFinishInflate();
+ getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ int measuredHeight = textTopBar.getMeasuredHeight();
+ textTopBar.setTranslationY(-measuredHeight);
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ }
+ });
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (isInEditMode()) {
+ int measuredHeight = textTopBar.getMeasuredHeight();
+ textTopBar.setTranslationY(-measuredHeight);
+ }
+ }
+
+ //底部导航条,展开收起动画
+ private void startAnim(boolean showOrHide) {
+ if (textTopBar == null) return;
+ if (showOrHide && textTopBar.getTranslationY() == 0) return;
+ if (!showOrHide && textTopBar.getTranslationY() == textTopBar.getHeight()) return;
+ ValueAnimator anim = ValueAnimator.ofFloat(showOrHide ? textTopBar.getHeight() : 0f, showOrHide ? 0f : textTopBar.getHeight());
+ anim.setInterpolator(new DecelerateInterpolator());
+ anim.setDuration(300);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ Float value = (Float) animation.getAnimatedValue();
+ textTopBar.setTranslationY(-value);
+ }
+ });
+ anim.start();
+ }
+
+ //############################################
+ //################ 对外方法 ##################
+ //############################################
+
+ /**
+ * 获取topBar TextView,如果你想修改样式,直接修改即可
+ */
+ public TextView getTopBarView() {
+ return textTopBar;
+ }
+
+ /**
+ * 设置文字,最常用的调用封装
+ */
+ public void setTopBarText(CharSequence text) {
+ textTopBar.setText(text);
+ }
+
+ /**
+ * 先显示topBar,然后再延迟time后隐藏topBar
+ */
+ public void showAndHideDelay(int time) {
+ show();
+ hideDelay(time);
+ }
+
+ /**
+ * 延迟time后显示topBar
+ */
+ public void showDelay(int time) {
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ show();
+ }
+ }, time);
+ }
+
+ /**
+ * 延迟time后隐藏topBar
+ */
+ public void hideDelay(int time) {
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ hide();
+ }
+ }, time);
+ }
+
+ /**
+ * 显示topBar
+ */
+ public void show() {
+ startAnim(true);
+ }
+
+ /**
+ * 隐藏topBar
+ */
+ public void hide() {
+ startAnim(false);
+ }
+}
diff --git a/header_du/src/main/res/layout/du_header.xml b/header_du/src/main/res/layout/du_header.xml
new file mode 100644
index 0000000..b597de0
--- /dev/null
+++ b/header_du/src/main/res/layout/du_header.xml
@@ -0,0 +1,12 @@
+
+
+
+
\ No newline at end of file
diff --git a/header_du/src/main/res/layout/view_top_bar.xml b/header_du/src/main/res/layout/view_top_bar.xml
new file mode 100644
index 0000000..c6d04ad
--- /dev/null
+++ b/header_du/src/main/res/layout/view_top_bar.xml
@@ -0,0 +1,9 @@
+
diff --git a/header_du/src/main/res/values/strings.xml b/header_du/src/main/res/values/strings.xml
new file mode 100644
index 0000000..c771107
--- /dev/null
+++ b/header_du/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ header_du
+
diff --git a/header_meituan/.gitignore b/header_meituan/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/header_meituan/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/header_meituan/build.gradle b/header_meituan/build.gradle
new file mode 100644
index 0000000..f6e3190
--- /dev/null
+++ b/header_meituan/build.gradle
@@ -0,0 +1,41 @@
+apply plugin: 'com.android.library'
+apply plugin: 'com.novoda.bintray-release'
+
+android {
+ compileSdkVersion rootProject.ext.compileSdkVersion
+
+ defaultConfig {
+ minSdkVersion rootProject.ext.minSdkVersion
+ targetSdkVersion rootProject.ext.targetSdkVersion
+ versionCode 1
+ versionName "1.0"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "androidx.appcompat:appcompat:$supportLibraryVersion"
+ implementation "com.google.android.material:material:$materialLibraryVersion"
+ implementation project(':library')
+// implementation 'com.liaoinstan.springview:library:1.7.0'
+}
+
+publish {
+ artifactId = 'MeituanHeader' // artifactId
+ uploadName = 'SpringView:MeituanHeader' // 展示名字
+ publishVersion = '1.7.0' // 版本
+ desc = rootProject.ext.desc
+ userOrg = rootProject.userOrg
+ groupId = rootProject.groupId
+ website = rootProject.website
+ licences = rootProject.licences
+ autoPublish = false
+ dryRun = true
+}
diff --git a/header_meituan/proguard-rules.pro b/header_meituan/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/header_meituan/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/header_meituan/src/main/AndroidManifest.xml b/header_meituan/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a0f870b
--- /dev/null
+++ b/header_meituan/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/library/src/main/java/com/liaoinstan/springview/container/MeituanFooter.java b/header_meituan/src/main/java/com/liaoinstan/springview/meituanheader/MeituanFooter.java
similarity index 76%
rename from library/src/main/java/com/liaoinstan/springview/container/MeituanFooter.java
rename to header_meituan/src/main/java/com/liaoinstan/springview/meituanheader/MeituanFooter.java
index 5da7a1c..d394ef6 100644
--- a/library/src/main/java/com/liaoinstan/springview/container/MeituanFooter.java
+++ b/header_meituan/src/main/java/com/liaoinstan/springview/meituanheader/MeituanFooter.java
@@ -1,19 +1,21 @@
-package com.liaoinstan.springview.container;
+package com.liaoinstan.springview.meituanheader;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
-import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
-import com.liaoinstan.springview.R;
+import androidx.core.content.ContextCompat;
+
+import com.liaoinstan.springview.container.BaseSimpleFooter;
+import com.liaoinstan.springview.widget.SpringView;
/**
* Created by Administrator on 2016/3/21.
*/
-public class MeituanFooter extends BaseFooter {
+public class MeituanFooter extends BaseSimpleFooter {
private AnimationDrawable animationLoading;
@@ -26,6 +28,8 @@ public MeituanFooter(Context context) {
}
public MeituanFooter(Context context, int[] loadingAnimSrcs) {
+ setType(SpringView.Type.FOLLOW);
+ setMovePara(2.0f);
this.context = context;
if (loadingAnimSrcs != null) this.loadingAnimSrcs = loadingAnimSrcs;
animationLoading = new AnimationDrawable();
@@ -37,18 +41,20 @@ public MeituanFooter(Context context, int[] loadingAnimSrcs) {
@Override
public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
- View view = inflater.inflate(R.layout.meituan_footer, viewGroup, true);
- footer_img = (ImageView) view.findViewById(R.id.meituan_footer_img);
- if (animationLoading != null)
+ View view = inflater.inflate(R.layout.meituan_footer, viewGroup, false);
+ footer_img = view.findViewById(R.id.meituan_footer_img);
+ if (animationLoading != null) {
footer_img.setImageDrawable(animationLoading);
+ }
return view;
}
@Override
public void onPreDrag(View rootView) {
animationLoading.stop();
- if (animationLoading != null && animationLoading.getNumberOfFrames() > 0)
+ if (animationLoading != null && animationLoading.getNumberOfFrames() > 0) {
footer_img.setImageDrawable(animationLoading.getFrame(0));
+ }
}
@Override
@@ -61,15 +67,17 @@ public void onLimitDes(View rootView, boolean upORdown) {
@Override
public void onStartAnim() {
- if (animationLoading != null)
+ if (animationLoading != null) {
footer_img.setImageDrawable(animationLoading);
- animationLoading.start();
+ animationLoading.start();
+ }
}
@Override
public void onFinishAnim() {
animationLoading.stop();
- if (animationLoading != null && animationLoading.getNumberOfFrames() > 0)
+ if (animationLoading != null && animationLoading.getNumberOfFrames() > 0) {
footer_img.setImageDrawable(animationLoading.getFrame(0));
+ }
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/liaoinstan/springview/container/MeituanHeader.java b/header_meituan/src/main/java/com/liaoinstan/springview/meituanheader/MeituanHeader.java
similarity index 88%
rename from library/src/main/java/com/liaoinstan/springview/container/MeituanHeader.java
rename to header_meituan/src/main/java/com/liaoinstan/springview/meituanheader/MeituanHeader.java
index 2085c82..01f5f69 100644
--- a/library/src/main/java/com/liaoinstan/springview/container/MeituanHeader.java
+++ b/header_meituan/src/main/java/com/liaoinstan/springview/meituanheader/MeituanHeader.java
@@ -1,21 +1,23 @@
-package com.liaoinstan.springview.container;
+package com.liaoinstan.springview.meituanheader;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
-import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
-import com.liaoinstan.springview.R;
+import androidx.core.content.ContextCompat;
+
+import com.liaoinstan.springview.container.BaseSimpleHeader;
import com.liaoinstan.springview.utils.DensityUtil;
+import com.liaoinstan.springview.widget.SpringView;
/**
* Created by Administrator on 2016/3/21.
*/
-public class MeituanHeader extends BaseHeader {
+public class MeituanHeader extends BaseSimpleHeader {
private AnimationDrawable animationPull;
private AnimationDrawable animationPullFan;
@@ -31,6 +33,8 @@ public MeituanHeader(Context context) {
}
public MeituanHeader(Context context, int[] pullAnimSrcs, int[] refreshAnimSrcs) {
+ setType(SpringView.Type.FOLLOW);
+ setMovePara(2.0f);
this.context = context;
if (pullAnimSrcs != null) this.pullAnimSrcs = pullAnimSrcs;
if (refreshAnimSrcs != null) this.refreshAnimSrcs = refreshAnimSrcs;
@@ -53,8 +57,8 @@ public MeituanHeader(Context context, int[] pullAnimSrcs, int[] refreshAnimSrcs)
@Override
public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
- View view = inflater.inflate(R.layout.meituan_header, viewGroup, true);
- header_img = (ImageView) view.findViewById(R.id.meituan_header_img);
+ View view = inflater.inflate(R.layout.meituan_header, viewGroup, false);
+ header_img = view.findViewById(R.id.meituan_header_img);
if (pullAnimSrcs != null && pullAnimSrcs.length > 0)
header_img.setImageResource(pullAnimSrcs[0]);
return view;
@@ -76,6 +80,7 @@ public void onDropAnim(View rootView, int dy) {
@Override
public void onLimitDes(View rootView, boolean upORdown) {
+ Log.e("test", "upORdown:" + upORdown);
if (!upORdown) {
header_img.setImageDrawable(animationPull);
animationPull.start();
diff --git a/library/src/main/res/drawable-hdpi/mt_loading01.png b/header_meituan/src/main/res/drawable-hdpi/mt_loading01.png
similarity index 100%
rename from library/src/main/res/drawable-hdpi/mt_loading01.png
rename to header_meituan/src/main/res/drawable-hdpi/mt_loading01.png
diff --git a/library/src/main/res/drawable-hdpi/mt_loading02.png b/header_meituan/src/main/res/drawable-hdpi/mt_loading02.png
similarity index 100%
rename from library/src/main/res/drawable-hdpi/mt_loading02.png
rename to header_meituan/src/main/res/drawable-hdpi/mt_loading02.png
diff --git a/library/src/main/res/drawable-hdpi/mt_pull.png b/header_meituan/src/main/res/drawable-hdpi/mt_pull.png
similarity index 100%
rename from library/src/main/res/drawable-hdpi/mt_pull.png
rename to header_meituan/src/main/res/drawable-hdpi/mt_pull.png
diff --git a/library/src/main/res/drawable-hdpi/mt_pull01.png b/header_meituan/src/main/res/drawable-hdpi/mt_pull01.png
similarity index 100%
rename from library/src/main/res/drawable-hdpi/mt_pull01.png
rename to header_meituan/src/main/res/drawable-hdpi/mt_pull01.png
diff --git a/library/src/main/res/drawable-hdpi/mt_pull02.png b/header_meituan/src/main/res/drawable-hdpi/mt_pull02.png
similarity index 100%
rename from library/src/main/res/drawable-hdpi/mt_pull02.png
rename to header_meituan/src/main/res/drawable-hdpi/mt_pull02.png
diff --git a/library/src/main/res/drawable-hdpi/mt_pull03.png b/header_meituan/src/main/res/drawable-hdpi/mt_pull03.png
similarity index 100%
rename from library/src/main/res/drawable-hdpi/mt_pull03.png
rename to header_meituan/src/main/res/drawable-hdpi/mt_pull03.png
diff --git a/library/src/main/res/drawable-hdpi/mt_pull04.png b/header_meituan/src/main/res/drawable-hdpi/mt_pull04.png
similarity index 100%
rename from library/src/main/res/drawable-hdpi/mt_pull04.png
rename to header_meituan/src/main/res/drawable-hdpi/mt_pull04.png
diff --git a/library/src/main/res/drawable-hdpi/mt_pull05.png b/header_meituan/src/main/res/drawable-hdpi/mt_pull05.png
similarity index 100%
rename from library/src/main/res/drawable-hdpi/mt_pull05.png
rename to header_meituan/src/main/res/drawable-hdpi/mt_pull05.png
diff --git a/library/src/main/res/drawable-hdpi/mt_refreshing01.png b/header_meituan/src/main/res/drawable-hdpi/mt_refreshing01.png
similarity index 100%
rename from library/src/main/res/drawable-hdpi/mt_refreshing01.png
rename to header_meituan/src/main/res/drawable-hdpi/mt_refreshing01.png
diff --git a/library/src/main/res/drawable-hdpi/mt_refreshing02.png b/header_meituan/src/main/res/drawable-hdpi/mt_refreshing02.png
similarity index 100%
rename from library/src/main/res/drawable-hdpi/mt_refreshing02.png
rename to header_meituan/src/main/res/drawable-hdpi/mt_refreshing02.png
diff --git a/library/src/main/res/drawable-hdpi/mt_refreshing03.png b/header_meituan/src/main/res/drawable-hdpi/mt_refreshing03.png
similarity index 100%
rename from library/src/main/res/drawable-hdpi/mt_refreshing03.png
rename to header_meituan/src/main/res/drawable-hdpi/mt_refreshing03.png
diff --git a/library/src/main/res/drawable-hdpi/mt_refreshing04.png b/header_meituan/src/main/res/drawable-hdpi/mt_refreshing04.png
similarity index 100%
rename from library/src/main/res/drawable-hdpi/mt_refreshing04.png
rename to header_meituan/src/main/res/drawable-hdpi/mt_refreshing04.png
diff --git a/library/src/main/res/drawable-hdpi/mt_refreshing05.png b/header_meituan/src/main/res/drawable-hdpi/mt_refreshing05.png
similarity index 100%
rename from library/src/main/res/drawable-hdpi/mt_refreshing05.png
rename to header_meituan/src/main/res/drawable-hdpi/mt_refreshing05.png
diff --git a/library/src/main/res/drawable-hdpi/mt_refreshing06.png b/header_meituan/src/main/res/drawable-hdpi/mt_refreshing06.png
similarity index 100%
rename from library/src/main/res/drawable-hdpi/mt_refreshing06.png
rename to header_meituan/src/main/res/drawable-hdpi/mt_refreshing06.png
diff --git a/library/src/main/res/layout/meituan_footer.xml b/header_meituan/src/main/res/layout/meituan_footer.xml
similarity index 100%
rename from library/src/main/res/layout/meituan_footer.xml
rename to header_meituan/src/main/res/layout/meituan_footer.xml
diff --git a/library/src/main/res/layout/meituan_header.xml b/header_meituan/src/main/res/layout/meituan_header.xml
similarity index 100%
rename from library/src/main/res/layout/meituan_header.xml
rename to header_meituan/src/main/res/layout/meituan_header.xml
diff --git a/header_meituan/src/main/res/values/strings.xml b/header_meituan/src/main/res/values/strings.xml
new file mode 100644
index 0000000..d6882a8
--- /dev/null
+++ b/header_meituan/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ meituanheader
+
diff --git a/header_rotation/.gitignore b/header_rotation/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/header_rotation/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/header_rotation/build.gradle b/header_rotation/build.gradle
new file mode 100644
index 0000000..0aca415
--- /dev/null
+++ b/header_rotation/build.gradle
@@ -0,0 +1,42 @@
+apply plugin: 'com.android.library'
+apply plugin: 'com.novoda.bintray-release'
+
+android {
+ compileSdkVersion rootProject.ext.compileSdkVersion
+
+ defaultConfig {
+ minSdkVersion rootProject.ext.minSdkVersion
+ targetSdkVersion rootProject.ext.targetSdkVersion
+ versionCode 1
+ versionName "1.0"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "androidx.appcompat:appcompat:$supportLibraryVersion"
+ implementation "com.google.android.material:material:$materialLibraryVersion"
+ implementation project(':library')
+// implementation 'com.liaoinstan.springview:library:1.7.0'
+}
+
+publish {
+ artifactId = 'RotationHeader' // artifactId
+ uploadName = 'SpringView:RotationHeader' // 展示名字
+ publishVersion = '1.7.0' // 版本
+ desc = rootProject.ext.desc
+ userOrg = rootProject.userOrg
+ groupId = rootProject.groupId
+ website = rootProject.website
+ licences = rootProject.licences
+ autoPublish = false
+ dryRun = true
+}
\ No newline at end of file
diff --git a/header_rotation/proguard-rules.pro b/header_rotation/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/header_rotation/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/header_rotation/src/main/AndroidManifest.xml b/header_rotation/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ebf2ed8
--- /dev/null
+++ b/header_rotation/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/library/src/main/java/com/liaoinstan/springview/container/RotationFooter.java b/header_rotation/src/main/java/com/liaoinstan/springview/rotationheader/RotationFooter.java
similarity index 59%
rename from library/src/main/java/com/liaoinstan/springview/container/RotationFooter.java
rename to header_rotation/src/main/java/com/liaoinstan/springview/rotationheader/RotationFooter.java
index 80bf321..2eafb80 100644
--- a/library/src/main/java/com/liaoinstan/springview/container/RotationFooter.java
+++ b/header_rotation/src/main/java/com/liaoinstan/springview/rotationheader/RotationFooter.java
@@ -1,7 +1,5 @@
-package com.liaoinstan.springview.container;
+package com.liaoinstan.springview.rotationheader;
-import android.content.Context;
-import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -10,26 +8,24 @@
import android.view.animation.RotateAnimation;
import android.widget.ProgressBar;
-import com.liaoinstan.springview.R;
+import com.liaoinstan.springview.container.BaseSimpleFooter;
+import com.liaoinstan.springview.widget.SpringView;
+
/**
* Created by Administrator on 2016/3/21.
*/
-public class RotationFooter extends BaseFooter {
- private Context context;
- private int rotationSrc;
+public class RotationFooter extends BaseSimpleFooter {
private RotateAnimation mRotateUpAnim;
-
private ProgressBar footer_progress;
- public RotationFooter(Context context) {
- this(context, R.drawable.progress_gear);
- }
+ //记录拖拽是否超过弹动高度
+ private boolean hasOverLimitHeight;
- public RotationFooter(Context context, int rotationSrc) {
- this.context = context;
- this.rotationSrc = rotationSrc;
+ public RotationFooter() {
+ setType(SpringView.Type.OVERLAP);
+ setMovePara(2.0f);
mRotateUpAnim = new RotateAnimation(0.0f, 360.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
mRotateUpAnim.setInterpolator(new LinearInterpolator());
@@ -40,20 +36,28 @@ public RotationFooter(Context context, int rotationSrc) {
@Override
public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
- View view = inflater.inflate(R.layout.rotation_footer, viewGroup, true);
- footer_progress = (ProgressBar) view.findViewById(R.id.rotation_footer_progress);
- footer_progress.setIndeterminateDrawable(ContextCompat.getDrawable(context, rotationSrc));
+ View view = inflater.inflate(R.layout.rotation_footer, viewGroup, false);
+ footer_progress = view.findViewById(R.id.rotation_footer_progress);
return view;
}
+ @Override
+ public int getDragLimitHeight(View rootView) {
+ return rootView.getMeasuredHeight();
+ }
+
@Override
public void onPreDrag(View rootView) {
}
@Override
public void onDropAnim(View rootView, int dy) {
- float rota = 360 * Math.abs(dy) / rootView.getMeasuredHeight();
- footer_progress.setRotation(rota);
+ int dragLimitHeight = getDragLimitHeight(rootView);
+ if (dy >= dragLimitHeight) hasOverLimitHeight = true;
+ if (!hasOverLimitHeight) {
+ float rota = 360 * Math.abs(dy) / rootView.getMeasuredHeight();
+ footer_progress.setRotation(rota);
+ }
}
@Override
@@ -68,5 +72,6 @@ public void onStartAnim() {
@Override
public void onFinishAnim() {
footer_progress.clearAnimation();
+ hasOverLimitHeight = false;
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/liaoinstan/springview/container/RotationHeader.java b/header_rotation/src/main/java/com/liaoinstan/springview/rotationheader/RotationHeader.java
similarity index 65%
rename from library/src/main/java/com/liaoinstan/springview/container/RotationHeader.java
rename to header_rotation/src/main/java/com/liaoinstan/springview/rotationheader/RotationHeader.java
index ce82d9c..14d23d6 100644
--- a/library/src/main/java/com/liaoinstan/springview/container/RotationHeader.java
+++ b/header_rotation/src/main/java/com/liaoinstan/springview/rotationheader/RotationHeader.java
@@ -1,7 +1,5 @@
-package com.liaoinstan.springview.container;
+package com.liaoinstan.springview.rotationheader;
-import android.content.Context;
-import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -10,15 +8,13 @@
import android.view.animation.RotateAnimation;
import android.widget.ProgressBar;
-import com.liaoinstan.springview.R;
+import com.liaoinstan.springview.container.BaseSimpleHeader;
+import com.liaoinstan.springview.widget.SpringView;
/**
* Created by Administrator on 2016/3/21.
*/
-public class RotationHeader extends BaseHeader {
- private Context context;
- private int rotationSrc;
- private int rotationFuSrc;
+public class RotationHeader extends BaseSimpleHeader {
private RotateAnimation mRotateUpAnim;
private RotateAnimation mRotateUpAnim2;
@@ -32,14 +28,12 @@ public class RotationHeader extends BaseHeader {
private ProgressBar progress4;
private ProgressBar progress5;
- public RotationHeader(Context context) {
- this(context, R.drawable.progress_gear, R.drawable.progress_gear_fu);
- }
+ //记录拖拽是否超过弹动高度
+ private boolean hasOverLimitHeight;
- public RotationHeader(Context context, int rotationSrc, int rotationFuSrc) {
- this.context = context;
- this.rotationSrc = rotationSrc;
- this.rotationFuSrc = rotationFuSrc;
+ public RotationHeader() {
+ setType(SpringView.Type.OVERLAP);
+ setMovePara(2.0f);
mRotateUpAnim = new RotateAnimation(0.0f, 360.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
mRotateUpAnim.setInterpolator(new LinearInterpolator());
@@ -74,17 +68,12 @@ public RotationHeader(Context context, int rotationSrc, int rotationFuSrc) {
@Override
public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
- View view = inflater.inflate(R.layout.rotation_header, viewGroup, true);
- progress1 = (ProgressBar) view.findViewById(R.id.progress1);
- progress2 = (ProgressBar) view.findViewById(R.id.progress2);
- progress3 = (ProgressBar) view.findViewById(R.id.progress3);
- progress4 = (ProgressBar) view.findViewById(R.id.progress4);
- progress5 = (ProgressBar) view.findViewById(R.id.progress5);
- progress1.setIndeterminateDrawable(ContextCompat.getDrawable(context, rotationSrc));
- progress2.setIndeterminateDrawable(ContextCompat.getDrawable(context, rotationFuSrc));
- progress3.setIndeterminateDrawable(ContextCompat.getDrawable(context, rotationFuSrc));
- progress4.setIndeterminateDrawable(ContextCompat.getDrawable(context, rotationSrc));
- progress5.setIndeterminateDrawable(ContextCompat.getDrawable(context, rotationFuSrc));
+ View view = inflater.inflate(R.layout.rotation_header, viewGroup, false);
+ progress1 = view.findViewById(R.id.progress1);
+ progress2 = view.findViewById(R.id.progress2);
+ progress3 = view.findViewById(R.id.progress3);
+ progress4 = view.findViewById(R.id.progress4);
+ progress5 = view.findViewById(R.id.progress5);
return view;
}
@@ -93,19 +82,18 @@ public int getDragLimitHeight(View rootView) {
return rootView.getMeasuredHeight() / 4;
}
- @Override
- public void onPreDrag(View rootView) {
-
- }
-
@Override
public void onDropAnim(View rootView, int dy) {
- float rota = 360 * dy / rootView.getMeasuredHeight();
- progress1.setRotation(rota);
- progress2.setRotation(-rota);
- progress3.setRotation(-rota);
- progress4.setRotation(rota);
- progress5.setRotation(-rota);
+ int dragLimitHeight = getDragLimitHeight(rootView);
+ if (dy >= dragLimitHeight) hasOverLimitHeight = true;
+ if (!hasOverLimitHeight) {
+ float rota = 360 * dy / rootView.getMeasuredHeight();
+ progress1.setRotation(rota);
+ progress2.setRotation(-rota);
+ progress3.setRotation(-rota);
+ progress4.setRotation(rota);
+ progress5.setRotation(-rota);
+ }
}
@Override
@@ -128,5 +116,6 @@ public void onFinishAnim() {
progress3.clearAnimation();
progress4.clearAnimation();
progress5.clearAnimation();
+ hasOverLimitHeight = false;
}
}
\ No newline at end of file
diff --git a/library/src/main/res/drawable-hdpi/gear.png b/header_rotation/src/main/res/drawable/gear.png
similarity index 100%
rename from library/src/main/res/drawable-hdpi/gear.png
rename to header_rotation/src/main/res/drawable/gear.png
diff --git a/library/src/main/res/drawable-hdpi/progress_gear.xml b/header_rotation/src/main/res/drawable/progress_gear.xml
similarity index 100%
rename from library/src/main/res/drawable-hdpi/progress_gear.xml
rename to header_rotation/src/main/res/drawable/progress_gear.xml
diff --git a/library/src/main/res/drawable-hdpi/progress_gear_fu.xml b/header_rotation/src/main/res/drawable/progress_gear_fu.xml
similarity index 100%
rename from library/src/main/res/drawable-hdpi/progress_gear_fu.xml
rename to header_rotation/src/main/res/drawable/progress_gear_fu.xml
diff --git a/library/src/main/res/layout/rotation_footer.xml b/header_rotation/src/main/res/layout/rotation_footer.xml
similarity index 75%
rename from library/src/main/res/layout/rotation_footer.xml
rename to header_rotation/src/main/res/layout/rotation_footer.xml
index ef4dd61..1fc423e 100644
--- a/library/src/main/res/layout/rotation_footer.xml
+++ b/header_rotation/src/main/res/layout/rotation_footer.xml
@@ -3,9 +3,10 @@
android:layout_height="50dp">
+ android:indeterminateDrawable="@drawable/progress_gear" />
\ No newline at end of file
diff --git a/library/src/main/res/layout/rotation_header.xml b/header_rotation/src/main/res/layout/rotation_header.xml
similarity index 71%
rename from library/src/main/res/layout/rotation_header.xml
rename to header_rotation/src/main/res/layout/rotation_header.xml
index 5e2f09c..b11fda8 100644
--- a/library/src/main/res/layout/rotation_header.xml
+++ b/header_rotation/src/main/res/layout/rotation_header.xml
@@ -4,46 +4,51 @@
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="-60dp"
+ android:indeterminateDrawable="@drawable/progress_gear" />
+ android:indeterminateDrawable="@drawable/progress_gear_fu" />
+ android:indeterminateDrawable="@drawable/progress_gear_fu" />
+ android:indeterminateDrawable="@drawable/progress_gear" />
+ android:indeterminateDrawable="@drawable/progress_gear_fu" />
\ No newline at end of file
diff --git a/header_rotation/src/main/res/values/strings.xml b/header_rotation/src/main/res/values/strings.xml
new file mode 100644
index 0000000..3caa21b
--- /dev/null
+++ b/header_rotation/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ rotationheader
+
diff --git a/header_wangyi/.gitignore b/header_wangyi/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/header_wangyi/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/header_wangyi/build.gradle b/header_wangyi/build.gradle
new file mode 100644
index 0000000..622ff77
--- /dev/null
+++ b/header_wangyi/build.gradle
@@ -0,0 +1,42 @@
+apply plugin: 'com.android.library'
+apply plugin: 'com.novoda.bintray-release'
+
+android {
+ compileSdkVersion rootProject.ext.compileSdkVersion
+
+ defaultConfig {
+ minSdkVersion rootProject.ext.minSdkVersion
+ targetSdkVersion rootProject.ext.targetSdkVersion
+ versionCode 1
+ versionName "1.0"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "androidx.appcompat:appcompat:$supportLibraryVersion"
+ implementation "com.google.android.material:material:$materialLibraryVersion"
+ implementation project(':library')
+// implementation 'com.liaoinstan.springview:library:1.7.0'
+}
+
+publish {
+ artifactId = 'WangyiHeader' // artifactId
+ uploadName = 'SpringView:WangyiHeader' // 展示名字
+ publishVersion = '1.7.0' // 版本
+ desc = rootProject.ext.desc
+ userOrg = rootProject.userOrg
+ groupId = rootProject.groupId
+ website = rootProject.website
+ licences = rootProject.licences
+ autoPublish = false
+ dryRun = true
+}
diff --git a/header_wangyi/proguard-rules.pro b/header_wangyi/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/header_wangyi/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/header_wangyi/src/main/AndroidManifest.xml b/header_wangyi/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..50e31d1
--- /dev/null
+++ b/header_wangyi/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/header_wangyi/src/main/java/com/liaoinstan/springview/wangyiheader/CircleRoundView.java b/header_wangyi/src/main/java/com/liaoinstan/springview/wangyiheader/CircleRoundView.java
new file mode 100644
index 0000000..a97a00a
--- /dev/null
+++ b/header_wangyi/src/main/java/com/liaoinstan/springview/wangyiheader/CircleRoundView.java
@@ -0,0 +1,93 @@
+package com.liaoinstan.springview.wangyiheader;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.liaoinstan.springview.utils.DensityUtil;
+
+public class CircleRoundView extends View {
+
+ private int color_circle;
+ // 画圆所在的距形区域
+ private RectF mRectF;
+ //扇形线宽
+ private int strokeWidth;
+
+ private Paint mPaint;
+
+ public CircleRoundView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs,0);
+ initBase();
+ }
+
+ public CircleRoundView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initAttr(attrs);
+ initBase();
+ }
+
+ private void initAttr(@Nullable AttributeSet attrs) {
+ TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.WaveTextView, 0, 0);
+ color_circle = a.getColor(R.styleable.WaveTextView_wave_color, Color.parseColor("#aace0000"));
+ a.recycle();
+ }
+
+ private void initBase() {
+ mRectF = new RectF();
+ mPaint = new Paint();
+ strokeWidth = DensityUtil.dp2px(5);
+ // 设置画笔相关属性
+ mPaint.setAntiAlias(true);
+ mPaint.setColor(color_circle);
+ mPaint.setAntiAlias(true);//取消锯齿
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ int width = this.getWidth();
+ int height = this.getHeight();
+
+ int min = Math.min(width, height);
+
+ int space = Math.abs(width - height) / 2;
+
+ // 位置
+ mRectF.left = (int) Math.ceil((double) strokeWidth / 2); // 左上角x
+ mRectF.right = width - (int) Math.ceil((double) strokeWidth / 2); // 左下角x
+ mRectF.top = (int) Math.ceil((double) strokeWidth / 2); // 左上角y
+ mRectF.bottom = height - (int) Math.ceil((double) strokeWidth / 2); // 右下角y
+ if (width > height) {
+ mRectF.left += space;
+ mRectF.right -= space;
+ } else {
+ mRectF.top += space;
+ mRectF.bottom -= space;
+ }
+
+ if (min <= strokeWidth * 2 || isFull) {
+ mPaint.setStrokeWidth(0);
+ mPaint.setStyle(Paint.Style.FILL);
+ canvas.drawCircle(width / 2, height / 2, min / 2, mPaint);
+ } else {
+ mPaint.setStrokeWidth(strokeWidth);
+ mPaint.setStyle(Paint.Style.STROKE);
+ canvas.drawArc(mRectF, 0, 360, false, mPaint);
+ }
+ }
+
+ private boolean isFull;
+
+ public void setFull(boolean full) {
+ isFull = full;
+ invalidate();
+ }
+}
diff --git a/header_wangyi/src/main/java/com/liaoinstan/springview/wangyiheader/WangyiHeader.java b/header_wangyi/src/main/java/com/liaoinstan/springview/wangyiheader/WangyiHeader.java
new file mode 100644
index 0000000..f8a59ae
--- /dev/null
+++ b/header_wangyi/src/main/java/com/liaoinstan/springview/wangyiheader/WangyiHeader.java
@@ -0,0 +1,162 @@
+package com.liaoinstan.springview.wangyiheader;
+
+import android.animation.ValueAnimator;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+
+import com.liaoinstan.springview.container.BaseHeader;
+import com.liaoinstan.springview.utils.DensityUtil;
+
+/**
+ * Created by liaoinstan on 2018/9/4.
+ */
+public class WangyiHeader extends BaseHeader {
+
+ private CircleRoundView circle_round;
+ private WaveTextView text_ending_title;
+ private FrameLayout lay_circle;
+
+ //圆圈动画
+ private ValueAnimator animator;
+ //圆圈最大直径
+ private int maxWidthDot;
+ //圆圈最小直径
+ private int minWidthDot;
+ //圆圈动画过程中最小直径
+ private int minWidthAnimDot;
+
+ public WangyiHeader() {
+ //初始化数据
+ maxWidthDot = DensityUtil.dp2px(22);
+ minWidthDot = DensityUtil.dp2px(7);
+ minWidthAnimDot = DensityUtil.dp2px(17);
+
+ //初始化动画
+ animator = ValueAnimator.ofInt(maxWidthDot, minWidthAnimDot);
+ animator.setInterpolator(new DecelerateInterpolator());
+ animator.setRepeatMode(ValueAnimator.REVERSE);
+ animator.setRepeatCount(ValueAnimator.INFINITE);
+ animator.setDuration(300);
+ }
+
+ @Override
+ public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
+ View view = inflater.inflate(R.layout.wangyi_header, viewGroup, false);
+ circle_round = view.findViewById(R.id.circle_round);
+ text_ending_title = view.findViewById(R.id.text_ending_title);
+ lay_circle = view.findViewById(R.id.lay_circle);
+
+ //动画监听
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ int nowWidth = (Integer) valueAnimator.getAnimatedValue();
+ ViewGroup.LayoutParams layoutParams = circle_round.getLayoutParams();
+ layoutParams.width = nowWidth;
+ layoutParams.height = nowWidth;
+ circle_round.setLayoutParams(layoutParams);
+ }
+ });
+ //初始化可见性
+ circle_round.setVisibility(View.VISIBLE);
+ text_ending_title.setVisibility(View.INVISIBLE);
+ return view;
+ }
+
+ @Override
+ public int getDragMaxHeight(View rootView) {
+ return rootView.getMeasuredHeight();
+ }
+
+ @Override
+ public int getDragLimitHeight(View rootView) {
+ return DensityUtil.dp2px(80);
+ }
+
+ @Override
+ public int getDragSpringHeight(View rootView) {
+ return DensityUtil.dp2px(80);
+ }
+
+ @Override
+ public void onPreDrag(View rootView) {
+ }
+
+ @Override
+ public void onDropAnim(View rootView, int dy) {
+ int dragLimitHeight = getDragLimitHeight(rootView);
+ int layHeight = lay_circle.getMeasuredHeight();
+ if (dy <= 5) reset();
+ if (dy < layHeight) {
+ //如果此时动画还没停止,则停止动画
+ if (animator.isRunning()) animator.cancel();
+ //设置圆圈从 0 - 1 的透明度渐变
+// float lv = dy / (float) layHeight; //0-1
+// circle_round.setAlpha(lv);
+ setCircleSize(minWidthDot);
+ lay_circle.setTranslationY(0);
+ } else if (dy >= layHeight && dy < dragLimitHeight) {
+ float lv = (dy - layHeight) / (float) (dragLimitHeight - layHeight); //0-1
+ int nowWidth = (int) ((maxWidthDot - minWidthDot) * lv + minWidthDot);
+ setCircleSize(nowWidth);
+ lay_circle.setTranslationY(-(dy / 2 - lay_circle.getWidth() / 2));
+ } else {
+ //保持圆圈居中
+ setCircleSize(maxWidthDot);
+ lay_circle.setTranslationY(-(dy / 2 - lay_circle.getWidth() / 2));
+ }
+ }
+
+ @Override
+ public void onLimitDes(View rootView, boolean upORdown) {
+
+ }
+
+ @Override
+ public void onStartAnim() {
+ animator.start();
+ }
+
+ @Override
+ public void onFinishAnim() {
+ reset();
+ }
+
+ private void reset() {
+ circle_round.setVisibility(View.VISIBLE);
+ text_ending_title.setVisibility(View.INVISIBLE);
+ }
+
+ private void setCircleSize(int size) {
+ ViewGroup.LayoutParams layoutParam1 = circle_round.getLayoutParams();
+ if (layoutParam1.width == size) return;
+ layoutParam1.height = size;
+ layoutParam1.width = size;
+ circle_round.setLayoutParams(layoutParam1);
+ }
+
+ @Override
+ public void onEndingAnimStart() {
+ animator.cancel();
+ circle_round.setVisibility(View.INVISIBLE);
+ text_ending_title.setVisibility(View.VISIBLE);
+ text_ending_title.start();
+ }
+
+ @Override
+ public void onEndingAnimEnd() {
+ }
+
+ @Override
+ public int getEndingAnimTime() {
+ return 2000;
+ }
+
+ @Override
+ public int getEndingAnimHeight(View rootView) {
+ return text_ending_title.getMeasuredHeight();
+ }
+}
\ No newline at end of file
diff --git a/header_wangyi/src/main/java/com/liaoinstan/springview/wangyiheader/WaveTextView.java b/header_wangyi/src/main/java/com/liaoinstan/springview/wangyiheader/WaveTextView.java
new file mode 100644
index 0000000..90106c8
--- /dev/null
+++ b/header_wangyi/src/main/java/com/liaoinstan/springview/wangyiheader/WaveTextView.java
@@ -0,0 +1,109 @@
+package com.liaoinstan.springview.wangyiheader;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.AppCompatTextView;
+
+import com.liaoinstan.springview.utils.DensityUtil;
+
+public class WaveTextView extends AppCompatTextView {
+
+ private int color_wave;
+
+ public WaveTextView(Context context) {
+ this(context, null);
+ }
+
+ public WaveTextView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public WaveTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initAttr(attrs);
+ initBase();
+ }
+
+ private void initAttr(@Nullable AttributeSet attrs) {
+ TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.WaveTextView, 0, 0);
+ color_wave = a.getColor(R.styleable.WaveTextView_wave_color, Color.parseColor("#aace0000"));
+ a.recycle();
+ }
+
+ private Paint paint;
+ private float radius;
+ private float centerX;//圆心x
+ private float centerY;//圆心y
+ private float maxRadius;
+ private ValueAnimator valueAnimator;
+
+ private void initBase() {
+ paint = new Paint();
+ paint.setColor(color_wave);
+ paint.setAntiAlias(true);
+ if (isInEditMode()) {
+ //编辑器预览
+ radius = DensityUtil.dp2px(50);
+ }
+ }
+
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ //圆心位置
+ centerX = w / 2;
+ centerY = h / 2;
+ maxRadius = (float) Math.sqrt(Math.pow(w / 2d, 2) + Math.pow(h / 2d, 2)) + 10;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ //绘制扩散的圆
+ canvas.drawCircle(centerX, centerY, radius, paint);
+ super.onDraw(canvas);
+ }
+
+ public void start() {
+ if (valueAnimator != null) {
+ valueAnimator.cancel();
+ }
+ valueAnimator = ValueAnimator.ofFloat(0, maxRadius);
+ valueAnimator.setDuration(200);
+ valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ radius = (float) animation.getAnimatedValue();
+ postInvalidate();
+ }
+ });
+ valueAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+ });
+ valueAnimator.start();
+ }
+
+ public void reset() {
+ radius = 0;
+ postInvalidate();
+ }
+}
diff --git a/header_wangyi/src/main/res/layout/wangyi_header.xml b/header_wangyi/src/main/res/layout/wangyi_header.xml
new file mode 100644
index 0000000..a01bf3d
--- /dev/null
+++ b/header_wangyi/src/main/res/layout/wangyi_header.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/header_wangyi/src/main/res/values/attrs.xml b/header_wangyi/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..9be6bbe
--- /dev/null
+++ b/header_wangyi/src/main/res/values/attrs.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/header_wangyi/src/main/res/values/strings.xml b/header_wangyi/src/main/res/values/strings.xml
new file mode 100644
index 0000000..ed8e164
--- /dev/null
+++ b/header_wangyi/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ header_wangyi
+
diff --git a/header_weixin/.gitignore b/header_weixin/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/header_weixin/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/header_weixin/build.gradle b/header_weixin/build.gradle
new file mode 100644
index 0000000..1496ea5
--- /dev/null
+++ b/header_weixin/build.gradle
@@ -0,0 +1,41 @@
+apply plugin: 'com.android.library'
+apply plugin: 'com.novoda.bintray-release'
+
+android {
+ compileSdkVersion rootProject.ext.compileSdkVersion
+
+ defaultConfig {
+ minSdkVersion rootProject.ext.minSdkVersion
+ targetSdkVersion rootProject.ext.targetSdkVersion
+ versionCode 1
+ versionName "1.0"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "androidx.appcompat:appcompat:$supportLibraryVersion"
+ implementation "com.google.android.material:material:$materialLibraryVersion"
+ implementation project(':library')
+// implementation 'com.liaoinstan.springview:library:1.7.0'
+}
+
+publish {
+ artifactId = 'WeixinHeader' // artifactId
+ uploadName = 'SpringView:WeixinHeader' // 展示名字
+ publishVersion = '1.7.0' // 版本
+ desc = rootProject.ext.desc
+ userOrg = rootProject.userOrg
+ groupId = rootProject.groupId
+ website = rootProject.website
+ licences = rootProject.licences
+ autoPublish = false
+ dryRun = true
+}
diff --git a/header_weixin/proguard-rules.pro b/header_weixin/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/header_weixin/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/header_weixin/src/main/AndroidManifest.xml b/header_weixin/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d32ee2f
--- /dev/null
+++ b/header_weixin/src/main/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/header_weixin/src/main/java/com/liaoinstan/springview/weixinheader/Program.java b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheader/Program.java
new file mode 100644
index 0000000..7a42f65
--- /dev/null
+++ b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheader/Program.java
@@ -0,0 +1,73 @@
+package com.liaoinstan.springview.weixinheader;
+
+import com.liaoinstan.springview.weixinheaderv2.RecycleAdapterWeixinHeaderV2;
+
+/**
+ * 小程序实体类
+ */
+public class Program {
+ private int id;
+ private String name;
+ private String img;
+
+ private int type;
+ private String titleName;
+
+ public Program() {
+ }
+
+ public Program(String name, String img) {
+ this.name = name;
+ this.img = img;
+ this.type = RecycleAdapterWeixinHeaderV2.TYPE_ITEM;
+ }
+
+ public Program(String titleName, int type) {
+ this.titleName = titleName;
+ this.type = type;
+ }
+
+ public Program(int type) {
+ this.type = type;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getImg() {
+ return img;
+ }
+
+ public void setImg(String img) {
+ this.img = img;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ public String getTitleName() {
+ return titleName;
+ }
+
+ public void setTitleName(String titleName) {
+ this.titleName = titleName;
+ }
+}
diff --git a/header_weixin/src/main/java/com/liaoinstan/springview/weixinheader/RecycleAdapterWeixinHeader.java b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheader/RecycleAdapterWeixinHeader.java
new file mode 100644
index 0000000..5c8c32f
--- /dev/null
+++ b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheader/RecycleAdapterWeixinHeader.java
@@ -0,0 +1,101 @@
+package com.liaoinstan.springview.weixinheader;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * 微信Haader小程序列表适配器
+ */
+public class RecycleAdapterWeixinHeader extends RecyclerView.Adapter {
+
+ private WeixinHeader weixinHeader;
+ private int itemWidth;
+
+ public RecycleAdapterWeixinHeader(WeixinHeader weixinHeader) {
+ this.weixinHeader = weixinHeader;
+ }
+
+ @Override
+ public RecycleAdapterWeixinHeader.Holder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return new Holder(LayoutInflater.from(parent.getContext()).inflate(R.layout.weixin_header_item, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(final RecycleAdapterWeixinHeader.Holder holder, int position) {
+ final Program bean = weixinHeader.getResults().get(position);
+
+ if (itemWidth != 0) {
+ ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
+ layoutParams.width = itemWidth;
+ holder.itemView.setLayoutParams(layoutParams);
+ }
+
+ if (position != getItemCount() - 1) {
+ holder.text_name.setText(bean.getName());
+ holder.img_header.setImageResource(R.drawable.shape_oval_dot);
+ if (weixinHeader.getImgLoadCallback() != null) {
+ weixinHeader.getImgLoadCallback().loadImg(holder.img_header, bean.getImg(), position);
+ }
+ } else {
+ //最后一个是“更多”
+ holder.text_name.setText("");
+ holder.img_header.setImageResource(R.drawable.shape_more);
+ }
+
+ holder.img_header.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (holder.getAdapterPosition() != getItemCount() - 1) {
+ if (weixinHeader.getOnProgramClickListener() != null) {
+ weixinHeader.getOnProgramClickListener().onClick(bean, holder, holder.getAdapterPosition());
+ }
+ } else {
+ if (weixinHeader.getOnMoreClickListener() != null) {
+ weixinHeader.getOnMoreClickListener().onMoreClick();
+ }
+ }
+ }
+ });
+ holder.img_header.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ if (holder.getAdapterPosition() != getItemCount() - 1) {
+ if (weixinHeader.getOnProgramLongClickListener() != null) {
+ weixinHeader.getOnProgramLongClickListener().onLongClick(bean, holder, holder.getAdapterPosition());
+ return true;
+ }
+ }
+ return false;
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return weixinHeader.getResults().size();
+ }
+
+ public class Holder extends RecyclerView.ViewHolder {
+ ImageView img_header;
+ TextView text_name;
+
+ public Holder(View itemView) {
+ super(itemView);
+ img_header = itemView.findViewById(R.id.img_header);
+ text_name = itemView.findViewById(R.id.text_name);
+ }
+ }
+
+ public void setItemWidth(int itemWidth) {
+ this.itemWidth = itemWidth;
+ }
+
+ //////////////////////////////////////////////////
+
+
+}
\ No newline at end of file
diff --git a/header_weixin/src/main/java/com/liaoinstan/springview/weixinheader/StartLinearSnapHelper.java b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheader/StartLinearSnapHelper.java
new file mode 100644
index 0000000..b674d1b
--- /dev/null
+++ b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheader/StartLinearSnapHelper.java
@@ -0,0 +1,83 @@
+package com.liaoinstan.springview.weixinheader;
+
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.LinearSnapHelper;
+import androidx.recyclerview.widget.OrientationHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * 粘滞滑动
+ */
+public class StartLinearSnapHelper extends LinearSnapHelper {
+
+ private OrientationHelper mHorizontalHelper;
+
+ @Nullable
+ @Override
+ public int[] calculateDistanceToFinalSnap(RecyclerView.LayoutManager layoutManager, View targetView) {
+ int[] out = new int[2];
+ if (layoutManager.canScrollHorizontally()) {
+ out[0] = distanceToStart(targetView, getHorizontalHelper(layoutManager));
+ } else {
+ out[0] = 0;
+ }
+
+ return out;
+ }
+
+ @Nullable
+ @Override
+ public View findSnapView(RecyclerView.LayoutManager layoutManager) {
+ return findStartView(layoutManager, getHorizontalHelper(layoutManager));
+ }
+
+ private int distanceToStart(View targetView, OrientationHelper helper) {
+ return helper.getDecoratedStart(targetView) - helper.getStartAfterPadding();
+ }
+
+ private View findStartView(RecyclerView.LayoutManager layoutManager, OrientationHelper helper) {
+
+ /**
+ * 0,1,2,3 4,5,6,7 8,9,10,11
+ */
+
+ if (layoutManager instanceof LinearLayoutManager) {
+ //找出第一个可见的ItemView的位置
+ int firstChildPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
+ if (firstChildPosition == RecyclerView.NO_POSITION) {
+ return null;
+ }
+
+ //找到最后一个完全显示的ItemView,如果该ItemView是列表中的最后一个
+ //就说明列表已经滑动最后了,这时候就不应该根据第一个ItemView来对齐了
+ //要不然由于需要跟第一个ItemView对齐最后一个ItemView可能就一直无法完全显示,
+ //所以这时候直接返回null表示不需要对齐
+ if (((LinearLayoutManager) layoutManager).findLastCompletelyVisibleItemPosition() == layoutManager.getItemCount() - 1) {
+ return null;
+ }
+
+ View firstChildView = layoutManager.findViewByPosition(firstChildPosition);
+ //如果第一个ItemView被遮住的长度没有超过一半,就取该ItemView作为snapView
+ //超过一半,就把下一个ItemView作为snapView
+ if (helper.getDecoratedEnd(firstChildView) >= helper.getDecoratedMeasurement(firstChildView) / 2 && helper.getDecoratedEnd(firstChildView) > 0) {
+ return firstChildView;
+ } else {
+ return layoutManager.findViewByPosition(firstChildPosition + 1);
+ }
+ } else {
+ return null;
+ }
+ }
+
+
+ private OrientationHelper getHorizontalHelper(@NonNull RecyclerView.LayoutManager layoutManager) {
+ if (mHorizontalHelper == null) {
+ mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
+ }
+ return mHorizontalHelper;
+ }
+}
\ No newline at end of file
diff --git a/header_weixin/src/main/java/com/liaoinstan/springview/weixinheader/WeixinHeader.java b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheader/WeixinHeader.java
new file mode 100644
index 0000000..f002f8b
--- /dev/null
+++ b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheader/WeixinHeader.java
@@ -0,0 +1,305 @@
+package com.liaoinstan.springview.weixinheader;
+
+import android.Manifest;
+import android.app.Service;
+import android.os.Vibrator;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.widget.ImageView;
+
+import androidx.core.content.PermissionChecker;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.liaoinstan.springview.container.BaseSimpleHeader;
+import com.liaoinstan.springview.utils.DensityUtil;
+import com.liaoinstan.springview.widget.SpringView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Created by liaoinstan on 2018/6/12.
+ */
+public class WeixinHeader extends BaseSimpleHeader {
+
+ private RecyclerView recycler;
+ private RecycleAdapterWeixinHeader adapter;
+ private ImageView img_dot1;
+ private ImageView img_dot2;
+ private ImageView img_dot3;
+ private ViewGroup lay_dot;
+
+ //中小圆点最大宽度
+ private int dotWidth = DensityUtil.dp2px(12);
+ //两边小圆点宽度
+ private int dotWidthSide = DensityUtil.dp2px(6.5f);
+ //小圆点间距
+ private int dotSpace = DensityUtil.dp2px(8);
+ //一行最多现实多少个item
+ private int maxCountLine = 4;
+ //记录小圆点最大位移距离
+ private float dotMaxTranY;
+ //记录拖拽是否超过弹动高度
+ private boolean hasOverSpringHeight;
+
+ private List results = new ArrayList<>();
+ private OnMoreClickListener onMoreClickListener;
+ private OnProgramClickListener onProgramClickListener;
+ private OnProgramLongClickListener onProgramLongClickListener;
+ private OnWeixinHeaderLoadImgCallback imgLoadCallback;
+
+ public WeixinHeader() {
+ setType(SpringView.Type.FOLLOW);
+ setMovePara(1.5f);
+ }
+
+ @Override
+ public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
+ View root = inflater.inflate(R.layout.weixin_header, viewGroup, false);
+ recycler = root.findViewById(R.id.recycler);
+ img_dot1 = root.findViewById(R.id.img_dot1);
+ img_dot2 = root.findViewById(R.id.img_dot2);
+ img_dot3 = root.findViewById(R.id.img_dot3);
+ lay_dot = root.findViewById(R.id.lay_dot);
+
+ adapter = new RecycleAdapterWeixinHeader(this);
+ recycler.setLayoutManager(new LinearLayoutManager(root.getContext(), LinearLayoutManager.HORIZONTAL, false));
+ recycler.setAdapter(adapter);
+
+ StartLinearSnapHelper snapHelper = new StartLinearSnapHelper();
+ snapHelper.attachToRecyclerView(recycler);
+
+ if (results.size() == 0) {
+ results.add(new Program());
+ }
+ adapter.notifyDataSetChanged();
+
+ //添加布局监听,获取父容器宽度并设置item宽度,从而达到一行item平铺效果
+ recycler.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ adapter.setItemWidth(recycler.getWidth() / maxCountLine);
+ adapter.notifyDataSetChanged();
+ recycler.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ }
+ });
+
+ return root;
+ }
+
+ //加载小程序列表
+ public void freshItem(List results) {
+ this.results.clear();
+ this.results.addAll(results);
+ //最后增加一条,因为最后一条是“更多”
+ this.results.add(new Program());
+ if (adapter != null) {
+ adapter.notifyDataSetChanged();
+ }
+ }
+
+ //添加小程序(添加到末尾,'更多'前面)
+ public void addItem(Program program) {
+ if (results.size() > 0) {
+ int position = results.size() - 1;
+ results.add(position, program);
+ if (adapter != null) {
+ if (results.size() <= 4) {
+ adapter.notifyItemInserted(position);
+ adapter.notifyItemRangeChanged(position, results.size() - position);
+ } else {
+ adapter.notifyDataSetChanged();
+ recycler.smoothScrollToPosition(results.size() - 1);
+ }
+ }
+ }
+ }
+
+ //删除指定位置的小程序
+ public void removeItem(int position) {
+ if (results.size() > 0) {
+ results.remove(position);
+ if (adapter != null) {
+ adapter.notifyItemRemoved(position);
+ adapter.notifyItemRangeChanged(position, results.size() - position);
+ }
+ }
+ }
+
+ @Override
+ public int getDragLimitHeight(View rootView) {
+ return DensityUtil.dp2px(100);
+ }
+
+ @Override
+ public int getDragMaxHeight(View rootView) {
+ return rootView.getMeasuredHeight();
+ }
+
+ @Override
+ public int getDragSpringHeight(View rootView) {
+ return rootView.getMeasuredHeight() - DensityUtil.dp2px(200);
+ }
+
+ @Override
+ public void onPreDrag(View rootView) {
+ }
+
+ /**
+ * 根据下拉的距离不断变化,进行动画交互
+ * 包括3个小圆点的动画
+ */
+ @Override
+ public void onDropAnim(View rootView, int dy) {
+ int dragSpringHeight = getDragSpringHeight(rootView);
+ if (dy >= dragSpringHeight) hasOverSpringHeight = true;
+ if (dy > dragSpringHeight) {
+ //如果拖拽超过停顿位置,则给内容设置位移系数保持居中
+ int offset = dy - dragSpringHeight;
+ recycler.setTranslationY(-offset / 2);
+ lay_dot.setAlpha(0);
+ } else {
+ //如果拖拽未超过停顿位置,则执行交互动画
+ if (!hasOverSpringHeight) {
+ //使内容从上向下位移
+ recycler.setTranslationY(dy - dragSpringHeight);
+ //小圆点的一系列位移渐变缩放等动画
+ if (dy < dragSpringHeight / 2) {
+ img_dot1.setTranslationX(0);
+ img_dot3.setTranslationX(0);
+ float lv = dy / ((float) dragSpringHeight / 2); //0-1
+ int nowWidth = (int) (lv * dotWidth);
+ int nowWidthSide = (int) (lv * dotWidthSide);
+ //小圆点不断变大
+ setViewWidthHeight(img_dot1, nowWidthSide, nowWidthSide);
+ setViewWidthHeight(img_dot2, nowWidth, nowWidth);
+ setViewWidthHeight(img_dot3, nowWidthSide, nowWidthSide);
+ //小圆点居中
+ lay_dot.setTranslationY(-((float) dy / 2 - (float) nowWidth / 2));
+ //记录下小圆点最大位移距离
+ if (Math.abs(lay_dot.getTranslationY()) > dotMaxTranY) {
+ dotMaxTranY = Math.abs(lay_dot.getTranslationY());
+ }
+ lay_dot.setAlpha(1);
+ } else if (dy < dragSpringHeight * 2 / 3) {
+ setViewWidthHeight(img_dot1, dotWidthSide, dotWidthSide);
+ setViewWidthHeight(img_dot2, dotWidth, dotWidth);
+ setViewWidthHeight(img_dot3, dotWidthSide, dotWidthSide);
+ //小圆点逐渐分开
+ float lv = (dy - (float) dragSpringHeight / 2) / ((float) dragSpringHeight * 2 / 3 - (float) dragSpringHeight / 2); //0-1
+ int nowSpace = (int) (lv * (dotSpace + dotWidth));
+ img_dot1.setTranslationX(-nowSpace);
+ img_dot3.setTranslationX(nowSpace);
+ //中间的小圆点不断变小
+ int width2 = (int) (dotWidth - (dotWidth - dotWidthSide) * lv);
+ setViewWidthHeight(img_dot2, width2, width2);
+ lay_dot.setAlpha(1);
+ } else if (dy < dragSpringHeight) {
+ setViewWidthHeight(img_dot2, dotWidthSide, dotWidthSide);
+ img_dot1.setTranslationX(-dotSpace - dotWidth);
+ img_dot3.setTranslationX(dotSpace + dotWidth);
+ //分开后小圆点不断向下位移并透明消失
+ float lv = (dy - (float) dragSpringHeight * 2 / 3) / ((float) dragSpringHeight - (float) dragSpringHeight * 2 / 3); //0-1
+ float alpha = 1 - lv;
+ float nowSpace = dotMaxTranY * (1 - lv);
+ lay_dot.setTranslationY(-nowSpace);
+ lay_dot.setAlpha(alpha);
+ } else {
+ lay_dot.setAlpha(0);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onLimitDes(View rootView, boolean upORdown) {
+ //下拉超过临界高度时如果有震动权限就震动一下
+ if (!hasOverSpringHeight && !upORdown && PermissionChecker.checkSelfPermission(rootView.getContext(), Manifest.permission.VIBRATE) == PermissionChecker.PERMISSION_GRANTED) {
+ Vibrator vib = (Vibrator) rootView.getContext().getSystemService(Service.VIBRATOR_SERVICE);
+ if (vib != null) vib.vibrate(10);
+ }
+ }
+
+ @Override
+ public void onStartAnim() {
+ }
+
+ @Override
+ public void onFinishAnim() {
+ reset();
+ }
+
+ private void setViewWidthHeight(View view, int width, int height) {
+ ViewGroup.LayoutParams layoutParam1 = view.getLayoutParams();
+ layoutParam1.height = height;
+ layoutParam1.width = width;
+ view.setLayoutParams(layoutParam1);
+ }
+
+ private void reset() {
+ hasOverSpringHeight = false;
+ img_dot1.setTranslationX(0);
+ img_dot3.setTranslationX(0);
+ lay_dot.setTranslationY(0);
+ }
+
+ //###################### 外部接口 #######################
+
+ public interface OnWeixinHeaderLoadImgCallback {
+ void loadImg(ImageView imageView, String imgUrl, int position);
+ }
+
+ public interface OnMoreClickListener {
+ void onMoreClick();
+ }
+
+ public interface OnProgramClickListener {
+ void onClick(Program program, RecyclerView.ViewHolder holder, int position);
+ }
+
+ public interface OnProgramLongClickListener {
+ void onLongClick(Program program, RecyclerView.ViewHolder holder, int position);
+ }
+
+ //#################### get & set #####################
+
+ public List getResults() {
+ return results;
+ }
+
+ public void setOnMoreClickListener(OnMoreClickListener onMoreClickListener) {
+ this.onMoreClickListener = onMoreClickListener;
+ }
+
+ public void setOnProgramClickListener(OnProgramClickListener onProgramClickListener) {
+ this.onProgramClickListener = onProgramClickListener;
+ }
+
+ public void setOnProgramLongClickListener(OnProgramLongClickListener onProgramLongClickListener) {
+ this.onProgramLongClickListener = onProgramLongClickListener;
+ }
+
+ public void setOnLoadImgCallback(OnWeixinHeaderLoadImgCallback imgLoadCallback) {
+ this.imgLoadCallback = imgLoadCallback;
+ }
+
+ public OnMoreClickListener getOnMoreClickListener() {
+ return onMoreClickListener;
+ }
+
+ public OnProgramClickListener getOnProgramClickListener() {
+ return onProgramClickListener;
+ }
+
+ public OnProgramLongClickListener getOnProgramLongClickListener() {
+ return onProgramLongClickListener;
+ }
+
+ public OnWeixinHeaderLoadImgCallback getImgLoadCallback() {
+ return imgLoadCallback;
+ }
+}
diff --git a/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/DragItemTouchCallback.java b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/DragItemTouchCallback.java
new file mode 100644
index 0000000..9c12eb1
--- /dev/null
+++ b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/DragItemTouchCallback.java
@@ -0,0 +1,338 @@
+package com.liaoinstan.springview.weixinheaderv2;
+
+import android.animation.ValueAnimator;
+import android.graphics.Canvas;
+import android.os.Handler;
+import android.view.View;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.liaoinstan.springview.weixinheader.R;
+
+import java.util.Collections;
+import java.util.List;
+
+import static com.liaoinstan.springview.weixinheaderv2.RecycleAdapterWeixinHeaderV2.TYPE_HEADER;
+import static com.liaoinstan.springview.weixinheaderv2.RecycleAdapterWeixinHeaderV2.TYPE_MORE;
+
+/**
+ * Created by liaoinstan on 2019/11.
+ * 给recyclerview 添加拖拽的帮助类ItemTouchHelper.Callback的实现
+ */
+public class DragItemTouchCallback extends ItemTouchHelper.Callback {
+
+ private RecycleAdapterWeixinHeaderV2 adapter;
+ private TextView deleteView;
+
+ public DragItemTouchCallback(RecycleAdapterWeixinHeaderV2 adapter, TextView deleteView) {
+ this.adapter = adapter;
+ this.deleteView = deleteView;
+ }
+
+ @Override
+ public boolean isLongPressDragEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isItemViewSwipeEnabled() {
+ return true;
+ }
+
+ @Override
+ public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
+ if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
+ final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
+ final int swipeFlags = 0;
+ return makeMovementFlags(dragFlags, swipeFlags);
+ } else {
+ final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
+ final int swipeFlags = 0;
+ return makeMovementFlags(dragFlags, swipeFlags);
+ }
+ }
+
+ @Override
+ public RecyclerView.ViewHolder chooseDropTarget(@NonNull RecyclerView.ViewHolder selected, @NonNull List dropTargets, int curX, int curY) {
+ int right = curX + selected.itemView.getWidth();
+ int bottom = curY + selected.itemView.getHeight();
+ RecyclerView.ViewHolder winner = null;
+ int winnerScore = -1;
+ final int dx = curX - selected.itemView.getLeft();
+ final int dy = curY - selected.itemView.getTop();
+ final int targetsSize = dropTargets.size();
+ for (int i = 0; i < targetsSize; i++) {
+ final RecyclerView.ViewHolder target = dropTargets.get(i);
+ int position = target.getLayoutPosition();
+ boolean isLastItem = Utils.isLastItem(adapter.getResults(), position);
+ boolean hasMineProgram = Utils.hasMineProgram(adapter.getResults());
+ int limitValue = 0;
+ if (isLastItem && !hasMineProgram && target instanceof RecycleAdapterWeixinHeaderV2.HolderHeader) {
+ //如果这个item是最后一个item,并且当前集合里没有'我的小程序',说明target此时是'拖拽区域'
+ //这时设置一个和它高度一致的偏移量
+ limitValue = -target.itemView.getHeight() / 2;
+ areaViewHolder = (RecycleAdapterWeixinHeaderV2.HolderHeader) target;
+ }
+ if (dx > 0) {
+ int diff = target.itemView.getRight() - right;
+ if (diff < 0 && target.itemView.getRight() > selected.itemView.getRight()) {
+ final int score = Math.abs(diff);
+ if (score > winnerScore) {
+ winnerScore = score;
+ winner = target;
+ }
+ }
+ }
+ if (dx < 0) {
+ int diff = target.itemView.getLeft() - curX;
+ if (diff > 0 && target.itemView.getLeft() < selected.itemView.getLeft()) {
+ final int score = Math.abs(diff);
+ if (score > winnerScore) {
+ winnerScore = score;
+ winner = target;
+ }
+ }
+ }
+ if (dy < 0) {
+ int diff = target.itemView.getTop() - curY;
+ if (diff > limitValue && target.itemView.getTop() < selected.itemView.getTop()) {
+ final int score = Math.abs(diff);
+ if (score > winnerScore) {
+ winnerScore = score;
+ winner = target;
+ }
+ }
+ }
+
+ if (dy > 0) {
+ int diff = target.itemView.getBottom() - bottom;
+ if (diff < -limitValue && target.itemView.getBottom() > selected.itemView.getBottom()) {
+ final int score = Math.abs(diff);
+ if (score > winnerScore) {
+ winnerScore = score;
+ winner = target;
+ }
+ }
+ }
+ }
+ return winner;
+ }
+
+ @Override
+ public boolean onMove(@NonNull RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
+ int fromPosition = viewHolder.getAdapterPosition(); //得到拖动ViewHolder的position
+ int toPosition = target.getAdapterPosition(); //得到目标ViewHolder的position
+ return doOnMove(fromPosition, toPosition, false);
+ }
+
+ private boolean doOnMove(int fromPosition, int toPosition, boolean isAreaDrag) {
+ //Log.e("onMove", "fromPosition:" + fromPosition + " toPosition:" + toPosition + " isRecentStart:" + isRecentStart+ " isAreaDrag:" + isAreaDrag);
+ int toViewType = adapter.getItemViewType(toPosition);
+ //header类别不可拖拽(拖到'拖拽区域'的情况例外)
+ if (toViewType == TYPE_HEADER && !isAreaDrag) {
+ return false;
+ }
+ //只有在'最近使用'内容为空时才可拖拽
+ if (toViewType == TYPE_MORE && Utils.getRecentProgramCount(adapter.getResults()) > 0) {
+ return false;
+ }
+ boolean isRecentFrom = Utils.isDragToRecentProgram(adapter.getResults(), fromPosition);
+ boolean isRecentTo = Utils.isDragToRecentProgram(adapter.getResults(), toPosition);
+ //不允许直接从'我的小程序'拖拽到'最近使用'(如果拖拽开始的位置就是'最近使用'的话,那么允许拖回去)
+ if (!isRecentFrom && isRecentTo && !isRecentStart) {
+ return false;
+ }
+ //开始执行拖拽逻辑
+ if (fromPosition < toPosition) {
+ for (int i = fromPosition; i < toPosition; i++) {
+ Collections.swap(adapter.getResults(), i, i + 1);
+ }
+ } else {
+ for (int i = fromPosition; i > toPosition; i--) {
+ Collections.swap(adapter.getResults(), i, i - 1);
+ }
+ }
+ adapter.notifyItemMoved(fromPosition, toPosition);
+ return true;
+ }
+
+ @Override
+ public long getAnimationDuration(@NonNull RecyclerView recyclerView, int animationType, float animateDx, float animateDy) {
+ return 200;
+ }
+
+ @Override
+ public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
+ }
+
+ @Override
+ public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
+ if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
+ super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
+ //获取item在屏幕上位置
+ int[] locationHolder = new int[2];
+ int[] locationDelete = new int[2];
+ viewHolder.itemView.getLocationOnScreen(locationHolder);
+ deleteView.getLocationOnScreen(locationDelete);
+ //检查是否拖拽到‘删除按钮’的区域
+ if (locationHolder[1] + viewHolder.itemView.getHeight() / 2 >= locationDelete[1]) {
+ if (!lastDeleteFlag) {
+ //拖入了‘删除按钮’的区域
+ deleteView.setText(R.string.weixin_v2_delete_in);
+ deleteView.setSelected(true);
+ }
+ lastDeleteFlag = true;
+ } else {
+ if (lastDeleteFlag) {
+ //拖出了‘删除按钮’的区域
+ deleteView.setText(R.string.weixin_v2_delete_out);
+ deleteView.setSelected(false);
+ }
+ lastDeleteFlag = false;
+ }
+ //检查拖拽手势是否进入'拖拽区域'
+ if (areaViewHolder != null) {
+ if (areaViewHolder.text_drag_area.getVisibility() == View.VISIBLE) {
+ int bottom = areaViewHolder.itemView.getBottom();
+ int top = bottom - areaViewHolder.text_drag_area.getHeight();
+ int dragCenter = viewHolder.itemView.getTop() + viewHolder.itemView.getHeight() / 2;
+ if (dY + dragCenter >= top && dY + dragCenter <= bottom) {
+ //拖拽进入'拖拽区域',记录下位置
+ areaFromPosition = viewHolder.getAdapterPosition();
+ areaToPosition = areaViewHolder.getAdapterPosition();
+ } else {
+ //超出区域,撤销记录
+ areaFromPosition = -1;
+ areaToPosition = -1;
+ }
+ } else {
+ areaFromPosition = -1;
+ areaToPosition = -1;
+ }
+ }
+ }
+ }
+
+ private RecycleAdapterWeixinHeaderV2.HolderHeader areaViewHolder;
+ private RecyclerView.ViewHolder nowViewHolder;
+ private int areaFromPosition;
+ private int areaToPosition;
+ private boolean isRecentStart;
+ private boolean lastDeleteFlag;
+
+ @Override
+ public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
+ //Log.e("onSelectedChanged", "onSelectedChanged:" + actionState);
+ if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
+ //长按 按下,开始拖拽
+ isRecentStart = Utils.isRecentProgram(adapter.getResults(), viewHolder.getAdapterPosition());
+ nowViewHolder = viewHolder;
+ if (viewHolder instanceof RecycleAdapterWeixinHeaderV2.HolderItem) {
+ startItemDragAnim((RecycleAdapterWeixinHeaderV2.HolderItem) viewHolder);
+ }
+ if (onDragListener != null) onDragListener.onStartDrag();
+ } else if (actionState == ItemTouchHelper.ACTION_STATE_IDLE) {
+ if (nowViewHolder instanceof RecycleAdapterWeixinHeaderV2.HolderItem) {
+ endItemDragAnim((RecycleAdapterWeixinHeaderV2.HolderItem) nowViewHolder);
+ }
+ if (onDragListener != null) onDragListener.onFinishDrag();
+ if (deleteView.isSelected() && onDragListener != null) {
+ //如果该item需要删除,则隐藏显示效果
+ boolean isDrop = onDragListener.onDrop(nowViewHolder, nowViewHolder.getAdapterPosition());
+ nowViewHolder.itemView.setVisibility(isDrop ? View.GONE : View.VISIBLE);
+ return;
+ }
+ if (areaFromPosition != -1 && areaToPosition != -1) {
+ nowViewHolder.itemView.setVisibility(View.GONE);
+ //调用move把小程序移动到拖拽区域,这时需要隐藏item动画
+ doOnMove(areaFromPosition, areaToPosition, true);
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ nowViewHolder.itemView.setVisibility(View.VISIBLE);
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1); //1-1.5
+ anim.setInterpolator(new DecelerateInterpolator());
+ anim.setDuration(200);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ Float value = (Float) animation.getAnimatedValue();
+ nowViewHolder.itemView.setAlpha(value);
+ }
+ });
+ anim.start();
+ }
+ }, 300);
+ }
+ }
+ super.onSelectedChanged(viewHolder, actionState);
+ }
+
+ @Override
+ public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
+ super.clearView(recyclerView, viewHolder);
+ }
+
+ //############ item动画 ############
+
+ private float MAX_SCALE = 1.5f;
+
+ private void startItemDragAnim(final RecycleAdapterWeixinHeaderV2.HolderItem holder) {
+ holder.text_name.setVisibility(View.GONE);
+ ValueAnimator anim = ValueAnimator.ofFloat(1, MAX_SCALE); //1-1.5
+ anim.setInterpolator(new DecelerateInterpolator());
+ anim.setDuration(300);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ Float value = (Float) animation.getAnimatedValue();
+ holder.itemView.setScaleX(value);
+ holder.itemView.setScaleY(value);
+ float alpha = 2 - value;//1-0.5
+ holder.itemView.setAlpha(alpha);
+ }
+ });
+ anim.start();
+ }
+
+ private void endItemDragAnim(final RecycleAdapterWeixinHeaderV2.HolderItem holder) {
+ holder.text_name.setVisibility(View.VISIBLE);
+ ValueAnimator anim = ValueAnimator.ofFloat(MAX_SCALE, 1); //1.5-1
+ anim.setInterpolator(new DecelerateInterpolator());
+ anim.setDuration(300);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ Float value = (Float) animation.getAnimatedValue();
+ holder.itemView.setScaleX(value);
+ holder.itemView.setScaleY(value);
+ float alpha = 2 - value;//0.5-1
+ holder.itemView.setAlpha(alpha);
+ }
+ });
+ anim.start();
+ }
+
+
+ //############ 对外接口 ############
+
+ private OnDragListener onDragListener;
+
+ public DragItemTouchCallback setOnDragListener(OnDragListener onDragListener) {
+ this.onDragListener = onDragListener;
+ return this;
+ }
+
+ public interface OnDragListener {
+ void onStartDrag();
+
+ void onFinishDrag();
+
+ boolean onDrop(RecyclerView.ViewHolder viewHolder, int position);
+ }
+}
diff --git a/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/RecycleAdapterWeixinHeaderV2.java b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/RecycleAdapterWeixinHeaderV2.java
new file mode 100644
index 0000000..3aa3213
--- /dev/null
+++ b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/RecycleAdapterWeixinHeaderV2.java
@@ -0,0 +1,179 @@
+package com.liaoinstan.springview.weixinheaderv2;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.liaoinstan.springview.weixinheader.Program;
+import com.liaoinstan.springview.weixinheader.R;
+
+import java.util.List;
+
+/**
+ * 微信Haader小程序列表适配器
+ */
+public class RecycleAdapterWeixinHeaderV2 extends RecyclerView.Adapter {
+
+ public final static int TYPE_HEADER = 1;
+ public final static int TYPE_ITEM = 0;
+ public final static int TYPE_MORE = 2;
+
+ private WeixinHeaderV2 weixinHeaderV2;
+
+ public RecycleAdapterWeixinHeaderV2(WeixinHeaderV2 weixinHeaderV2) {
+ this.weixinHeaderV2 = weixinHeaderV2;
+ }
+
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ switch (viewType) {
+ case TYPE_HEADER:
+ return new HolderHeader(LayoutInflater.from(parent.getContext()).inflate(R.layout.weixin_header_item_title_v2, parent, false));
+ case TYPE_ITEM:
+ return new HolderItem(LayoutInflater.from(parent.getContext()).inflate(R.layout.weixin_header_item_v2, parent, false));
+ case TYPE_MORE:
+ return new HolderMore(LayoutInflater.from(parent.getContext()).inflate(R.layout.weixin_header_item_more_v2, parent, false));
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
+ final Program bean = weixinHeaderV2.getResults().get(position);
+ if (holder instanceof HolderHeader) {
+ bindHeaderHolder((HolderHeader) holder, position, bean);
+ } else if (holder instanceof HolderItem) {
+ bindItemHolder((HolderItem) holder, position, bean);
+ } else if (holder instanceof HolderMore) {
+ bindMoreHolder((HolderMore) holder, position, bean);
+ }
+ }
+
+ private void bindItemHolder(final HolderItem holder, int position, final Program bean) {
+ holder.itemView.setVisibility(View.VISIBLE);
+ holder.text_name.setText(bean.getName());
+ if (weixinHeaderV2.getImgLoadCallback() != null) {
+ weixinHeaderV2.getImgLoadCallback().loadImg(holder.img_header, bean.getImg(), position);
+ }
+
+ holder.img_header.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (weixinHeaderV2.getOnProgramClickListener() != null) {
+ weixinHeaderV2.getOnProgramClickListener().onClick(bean, holder, holder.getAdapterPosition());
+ }
+ }
+ });
+ holder.img_header.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ if (onRecyclerLongClickListener != null) {
+ onRecyclerLongClickListener.onLongClick(holder);
+ return true;
+ }
+ return false;
+ }
+ });
+ }
+
+ private void bindHeaderHolder(final HolderHeader holder, int position, Program bean) {
+ holder.text_title.setText(bean.getTitleName());
+ holder.text_drag_area.setVisibility(Utils.isLastItem(weixinHeaderV2.getResults(), position) ? View.VISIBLE : View.GONE);
+ }
+
+ private void bindMoreHolder(final HolderMore holder, int position, Program bean) {
+ holder.img_more.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (weixinHeaderV2.getOnMoreClickListener() != null) {
+ weixinHeaderV2.getOnMoreClickListener().onMoreClick();
+ }
+ }
+ });
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ Program program = weixinHeaderV2.getResults().get(position);
+ return program.getType();
+ }
+
+ @Override
+ public int getItemCount() {
+ return weixinHeaderV2.getResults().size();
+ }
+
+ public List getResults() {
+ return weixinHeaderV2.getResults();
+ }
+
+ @Override
+ public void onAttachedToRecyclerView(@NonNull final RecyclerView recyclerView) {
+ super.onAttachedToRecyclerView(recyclerView);
+ RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
+ if (manager instanceof GridLayoutManager) {
+ final GridLayoutManager gridManager = ((GridLayoutManager) manager);
+ gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ int type = getItemViewType(position);
+ if (type == TYPE_HEADER) {
+ return 4;
+ } else {
+ return 1;
+ }
+ }
+ });
+ }
+ }
+
+ class HolderItem extends RecyclerView.ViewHolder {
+ ImageView img_header;
+ TextView text_name;
+
+ HolderItem(View itemView) {
+ super(itemView);
+ img_header = itemView.findViewById(R.id.img_header);
+ text_name = itemView.findViewById(R.id.text_name);
+ }
+ }
+
+ class HolderHeader extends RecyclerView.ViewHolder {
+ TextView text_title;
+ TextView text_drag_area;
+
+ HolderHeader(View itemView) {
+ super(itemView);
+ text_title = itemView.findViewById(R.id.text_title);
+ text_drag_area = itemView.findViewById(R.id.text_drag_area);
+ }
+ }
+
+ class HolderMore extends RecyclerView.ViewHolder {
+ ImageView img_more;
+
+ HolderMore(View itemView) {
+ super(itemView);
+ img_more = itemView.findViewById(R.id.img_more);
+ }
+ }
+
+ private OnRecyclerLongClickListener onRecyclerLongClickListener;
+
+ public void setOnRecyclerLongClickListener(OnRecyclerLongClickListener onRecyclerLongClickListener) {
+ this.onRecyclerLongClickListener = onRecyclerLongClickListener;
+ }
+
+ //////////////////////////////////////////////////
+ interface OnRecyclerLongClickListener {
+ void onLongClick(RecyclerView.ViewHolder viewHolder);
+ }
+
+}
\ No newline at end of file
diff --git a/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/SmokeView.java b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/SmokeView.java
new file mode 100644
index 0000000..5ec0c5b
--- /dev/null
+++ b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/SmokeView.java
@@ -0,0 +1,160 @@
+package com.liaoinstan.springview.weixinheaderv2;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import androidx.annotation.Nullable;
+
+import com.liaoinstan.springview.weixinheader.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * create by liaoinstan
+ * 仿微信小程序header背景动画
+ * 微信的动画实在看不出规律,只能模仿一个类似的烟雾效果
+ */
+public class SmokeView extends View {
+
+ private Paint paint;
+ private Bitmap bitmap;
+ private int heightParent;
+ private int widthParent;
+ private boolean isStart = false;
+
+ private List drawInfos = new ArrayList<>();
+
+ public SmokeView(Context context) {
+ this(context, null);
+ }
+
+ public SmokeView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SmokeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ setBackgroundResource(R.drawable.shape_rect_gradient_weixinheader_bg);
+ super.onFinishInflate();
+ }
+
+ private void init() {
+ paint = new Paint();
+ getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ paint.setAlpha(90);
+ bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.smoke);
+ for (int i = 0; i < 50; i++) {
+ drawInfos.add(new DrawInfo(bitmap));
+ }
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ }
+ });
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ heightParent = getHeight();
+ widthParent = getWidth();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ for (DrawInfo drawInfo : drawInfos) {
+ drawInfo.drawBitmap(canvas);
+ }
+ if (isStart) {
+ postInvalidateDelayed(17);
+ }
+ }
+
+ public void startAnim() {
+ if (!isStart) {
+ isStart = true;
+ postInvalidate();
+ }
+ }
+
+ public void stopAnim() {
+ isStart = false;
+ }
+
+ private class DrawInfo {
+ Bitmap bitmap;
+ Matrix matrix = new Matrix();
+ float dx;
+ float dy;
+
+ //xy轴速度
+ float moveX;
+ float moveY;
+ //高宽
+ float width;
+ float height;
+ float scale;
+ float rotate;
+
+ DrawInfo(Bitmap bitmap) {
+ this.bitmap = bitmap;
+ Random random = new Random();
+ scale = 3;
+ rotate = random.nextInt(360);
+ width = bitmap.getWidth() * scale;
+ height = bitmap.getHeight() * scale;
+ //初始化一个随机速度
+ moveX = random.nextInt(6) + random.nextFloat() - 3;
+ moveY = random.nextInt(6) + random.nextFloat() - 3;
+ //随机一个初始位置
+ if (widthParent != 0 && heightParent != 0) {
+ dx = random.nextInt(widthParent) + random.nextFloat();
+ dy = random.nextInt(heightParent) + random.nextFloat();
+ }
+
+ if (moveX == 0) moveX = 1;
+ if (moveY == 0) moveY = 1;
+
+ }
+
+ void drawBitmap(Canvas canvas) {
+ //x,y轴速度递加
+ dx += moveX;
+ dy += moveY;
+
+ //draw
+ matrix.setScale(scale, scale);
+ matrix.postRotate(rotate, width / 2, height / 2);
+ matrix.postTranslate(dx, dy);
+ canvas.drawBitmap(bitmap, matrix, paint);
+
+ //碰到边界反向
+ if (dx + width > widthParent + 500 || dx < 0 - 200) {
+ moveX = -moveX;
+ }
+ if (dy + height > heightParent + 500 || dy < 0) {
+ moveY = -moveY;
+ }
+ }
+ }
+}
diff --git a/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/Utils.java b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/Utils.java
new file mode 100644
index 0000000..44986b9
--- /dev/null
+++ b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/Utils.java
@@ -0,0 +1,79 @@
+package com.liaoinstan.springview.weixinheaderv2;
+
+import android.Manifest;
+import android.app.Service;
+import android.content.Context;
+import android.os.Vibrator;
+
+import androidx.core.content.PermissionChecker;
+
+import com.liaoinstan.springview.weixinheader.Program;
+
+import java.util.List;
+
+class Utils {
+ static int getMoreItemIndex(List programList) {
+ for (int i = 0; i < programList.size(); i++) {
+ Program program = programList.get(i);
+ if (program.getType() == RecycleAdapterWeixinHeaderV2.TYPE_MORE) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ //判断当前位置是否是"最近使用"小程序
+ static boolean isRecentProgram(List programList, int position) {
+ int indexMore = getMoreItemIndex(programList);
+ return position < indexMore;
+ }
+
+ //判断是否正要拖拽到'最近使用'类别中
+ static boolean isDragToRecentProgram(List programList, int position) {
+ int indexMore = getMoreItemIndex(programList);
+ return position <= indexMore;
+ }
+
+ static boolean isMineProgram(List programList, int position) {
+ int indexMore = getMoreItemIndex(programList);
+ return position > indexMore;
+ }
+
+ static boolean isLastItem(List programList, int position) {
+ return position == programList.size() - 1;
+ }
+
+ static boolean hasMineProgram(List programList) {
+ return programList.get(programList.size() - 1).getType() != RecycleAdapterWeixinHeaderV2.TYPE_HEADER;
+ }
+
+ static int getMineTitlePosition(List programList) {
+ for (int i = programList.size() - 1; i >= 0; i--) {
+ Program program = programList.get(i);
+ if (program.getType() == RecycleAdapterWeixinHeaderV2.TYPE_HEADER) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ static int getRecentProgramCount(List programList) {
+ int indexMore = getMoreItemIndex(programList);
+ return indexMore - 1;
+ }
+
+ static int getMineProgramCount(List programList) {
+ int indexMore = getMoreItemIndex(programList);
+ return programList.size() - indexMore - 2;
+ }
+
+ /**
+ * 检查如果已经申请了震动权限,就震动一下,震动时长:time
+ */
+ static void vibrate(Context context, int time) {
+ if (PermissionChecker.checkSelfPermission(context, Manifest.permission.VIBRATE) == PermissionChecker.PERMISSION_GRANTED) {
+ Vibrator vib = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE);
+ if (vib != null) vib.vibrate(time);
+ }
+ }
+}
diff --git a/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/WeixinHeaderV2.java b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/WeixinHeaderV2.java
new file mode 100644
index 0000000..9e33294
--- /dev/null
+++ b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/WeixinHeaderV2.java
@@ -0,0 +1,555 @@
+package com.liaoinstan.springview.weixinheaderv2;
+
+import android.animation.ValueAnimator;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.SimpleItemAnimator;
+
+import com.liaoinstan.springview.container.BaseSimpleHeader;
+import com.liaoinstan.springview.utils.DensityUtil;
+import com.liaoinstan.springview.weixinheader.Program;
+import com.liaoinstan.springview.weixinheader.R;
+import com.liaoinstan.springview.widget.SpringView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Created by liaoinstan on 2019/11/06.
+ * 仿微信小程序header V2 ,微信更新后小程序交互界面和之前老版本区别很大,所以这里再次模仿了一个2.0版本
+ */
+@SuppressWarnings("unused")
+public class WeixinHeaderV2 extends BaseSimpleHeader implements DragItemTouchCallback.OnDragListener {
+
+ //根布局
+ private View root;
+ //外部控件
+ private View bottomView;
+ private WeixinTitleBar weixinTitleBar;
+ private View contentView;
+ private View contentLay;
+ private SpringView springViewOut;
+ //内部控件
+ private SmokeView smokeView;
+ private SpringView springViewInner;
+ private RecyclerView recycler;
+ private RecycleAdapterWeixinHeaderV2 adapter;
+ private ImageView img_dot1;
+ private ImageView img_dot2;
+ private ImageView img_dot3;
+ private ViewGroup lay_dot;
+ private ViewGroup lay_content;
+ private TextView text_title;
+ private View bgView;
+
+ //中小圆点最大宽度
+ private int dotWidth = DensityUtil.dp2px(12);
+ //两边小圆点宽度
+ private int dotWidthSide = DensityUtil.dp2px(6.5f);
+ //小圆点间距
+ private int dotSpace = DensityUtil.dp2px(8);
+ //记录拖拽是否超过弹动高度
+ private boolean hasOverSpringHeight;
+ //初始化数据
+ private List results = new ArrayList() {{
+ add(new Program("最近使用", RecycleAdapterWeixinHeaderV2.TYPE_HEADER));
+ add(new Program(RecycleAdapterWeixinHeaderV2.TYPE_MORE)); //更多(...)按钮
+ add(new Program("我的小程序", RecycleAdapterWeixinHeaderV2.TYPE_HEADER));
+ }};
+ private OnMoreClickListener onMoreClickListener;
+ private OnSearchClickListener onSearchClickListener;
+ private OnProgramClickListener onProgramClickListener;
+ private OnProgramDropListener onProgramDropListener;
+ private OnWeixinHeaderLoadImgCallback imgLoadCallback;
+
+ public WeixinHeaderV2(View bottomView, WeixinTitleBar weixinTitleBar) {
+ //header比较长,移动参数设小一点比较合适,这里用1.25感觉和微信手感差不多
+ setMovePara(1.25f);
+ setType(SpringView.Type.OVERLAP);
+ this.bottomView = bottomView;
+ this.weixinTitleBar = weixinTitleBar;
+ }
+
+ @Override
+ public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
+ root = inflater.inflate(R.layout.weixin_header_v2, viewGroup, false);
+ springViewInner = root.findViewById(R.id.spring_inner);
+ recycler = root.findViewById(R.id.recycler);
+ img_dot1 = root.findViewById(R.id.img_dot1);
+ img_dot2 = root.findViewById(R.id.img_dot2);
+ img_dot3 = root.findViewById(R.id.img_dot3);
+ lay_dot = root.findViewById(R.id.lay_dot);
+ lay_content = root.findViewById(R.id.lay_content);
+ bgView = root.findViewById(R.id.bg_view);
+ text_title = root.findViewById(R.id.text_title);
+ smokeView = root.findViewById(R.id.smoke_view);
+
+ //微信header V2 内部的弹动效果也是使用SpringView实现的,springViewInner就是header内部的SpringView
+ //SpringView支持复杂嵌套,手势事件不会产生冲突,其本身只是一个轻量ViewGroup,和普通Layout布局一样简单使用
+ //初始化内部springView
+ springViewInner.setGive(SpringView.Give.TOP);
+ springViewInner.setHeader(new WeixinV2InnerHeader(new WeixinV2InnerHeader.OnSearchClickListener() {
+ @Override
+ public void onSearchClick(View view) {
+ if (onSearchClickListener != null) onSearchClickListener.onSearchClick();
+ }
+ }));
+ springViewInner.setFooter(new WeixinV2InnerFooter(new WeixinV2InnerFooter.OnDragFinishListener() {
+ @Override
+ public void onDragFinish() {
+ SpringView springView = (SpringView) root.getParent();
+ springView.onFinishFreshAndLoad();
+ }
+ }));
+ springViewInner.setListener(new SpringView.OnFreshListener() {
+ @Override
+ public void onRefresh() {
+ }
+
+ @Override
+ public void onLoadmore() {
+ }
+ });
+
+ //初始化recyclerView
+ adapter = new RecycleAdapterWeixinHeaderV2(this);
+ recycler.setLayoutManager(new GridLayoutManager(root.getContext(), 4, GridLayoutManager.VERTICAL, false));
+ recycler.setAdapter(adapter);
+ RecyclerView.ItemAnimator itemAnimator = recycler.getItemAnimator();
+
+ //禁用默认刷新动画(否则调用"notifyItemChanged"等方法会有渐变闪烁效果,这里不希望有这种效果)
+ if (itemAnimator instanceof SimpleItemAnimator)
+ ((SimpleItemAnimator) itemAnimator).setSupportsChangeAnimations(false);
+
+ //拖拽移动,使用ItemTouchHelper实现,拖拽逻辑在"DragItemTouchCallback"中
+ final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new DragItemTouchCallback(adapter, weixinTitleBar != null ? weixinTitleBar.getDeleteView() : null).setOnDragListener(this));
+ itemTouchHelper.attachToRecyclerView(recycler);
+ adapter.setOnRecyclerLongClickListener(new RecycleAdapterWeixinHeaderV2.OnRecyclerLongClickListener() {
+ @Override
+ public void onLongClick(RecyclerView.ViewHolder viewHolder) {
+ if (viewHolder instanceof RecycleAdapterWeixinHeaderV2.HolderItem) {
+ itemTouchHelper.startDrag(viewHolder);
+ Utils.vibrate(recycler.getContext(), 10); //震动10ms
+ }
+ }
+ });
+ adapter.notifyDataSetChanged();
+
+ //点击标题栏收起header
+ if (weixinTitleBar != null) {
+ weixinTitleBar.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (springViewOut != null) springViewOut.onFinishFreshAndLoad();
+ }
+ });
+ }
+ return root;
+ }
+
+ @Override
+ public int getDragLimitHeight(View rootView) {
+ //下拉阈值设置为固定190dp,和微信基本差不多
+ return DensityUtil.dp2px(190);
+ }
+
+ @Override
+ public int getDragMaxHeight(View rootView) {
+ //同时设置header的高度为弹动高度
+ springViewOut = (SpringView) rootView.getParent();
+ //保存内容布局的引用
+ contentLay = springViewOut.getContentLay();
+ contentView = springViewOut.getContentView();
+ ViewGroup.LayoutParams layoutParams = rootView.getLayoutParams();
+ layoutParams.height = springViewOut.getMeasuredHeight();
+ rootView.setLayoutParams(layoutParams);
+ //最大高度和弹动高度一致
+ return getDragSpringHeight(rootView);
+ }
+
+ @Override
+ public int getDragSpringHeight(View rootView) {
+ springViewOut = (SpringView) rootView.getParent();
+ if (weixinTitleBar != null) {
+ return springViewOut.getMeasuredHeight() - weixinTitleBar.getTitleViewHeight();
+ } else {
+ return springViewOut.getMeasuredHeight();
+ }
+ }
+
+ /**
+ * 根据下拉的距离不断变化,进行动画交互
+ * 包括3个小圆点的动画,背景渐变,内容缩放位移,透明度变化等
+ */
+ @Override
+ public void onDropAnim(View rootView, int dy) {
+ int dragSpringHeight = getDragSpringHeight(rootView);
+ int dragLimiteHeight = getDragLimitHeight(rootView);
+ if (dy >= dragSpringHeight) hasOverSpringHeight = true;
+ //计算小圆点居中Y轴偏移量
+ int dotDY = rootView.getMeasuredHeight() - dy / 2 - lay_dot.getHeight() / 2;
+ //小圆点的一系列位移渐变缩放等动画
+ if (dy < DensityUtil.dp2px(50)) {
+ //前50dp作为偏移量,不做任何处理
+ } else if (dy < dragLimiteHeight / 2) {
+ float lv = (dy - (float) DensityUtil.dp2px(50)) / ((float) dragLimiteHeight / 2 - DensityUtil.dp2px(50)); //0-1
+ int nowWidth = (int) (lv * dotWidth);
+ int nowWidthSide = (int) (lv * dotWidthSide);
+ //小圆点不断变大
+ setViewWidthHeight(img_dot1, nowWidthSide, nowWidthSide);
+ setViewWidthHeight(img_dot2, nowWidth, nowWidth);
+ setViewWidthHeight(img_dot3, nowWidthSide, nowWidthSide);
+ lay_dot.setTranslationY(-dotDY + DensityUtil.dp2px(25));
+ img_dot1.setTranslationX(0);
+ img_dot3.setTranslationX(0);
+ } else if (dy < dragLimiteHeight) {
+ setViewWidthHeight(img_dot1, dotWidthSide, dotWidthSide);
+ setViewWidthHeight(img_dot2, dotWidth, dotWidth);
+ setViewWidthHeight(img_dot3, dotWidthSide, dotWidthSide);
+ //小圆点逐渐分开
+ float lv = (dy - (float) dragLimiteHeight / 2) / ((float) dragLimiteHeight / 2); //0-1
+ int nowSpace = (int) (lv * (dotSpace + dotWidth));
+ img_dot1.setTranslationX(-nowSpace);
+ img_dot3.setTranslationX(nowSpace);
+ //中间的小圆点不断变小
+ int width2 = (int) (dotWidth - (dotWidth - dotWidthSide) * lv);
+ ViewGroup.LayoutParams layoutParam2 = img_dot2.getLayoutParams();
+ layoutParam2.height = width2;
+ layoutParam2.width = width2;
+ img_dot2.setLayoutParams(layoutParam2);
+ lay_dot.setTranslationY(-dotDY + DensityUtil.dp2px(25));
+ } else if (dy < dragLimiteHeight + DensityUtil.dp2px(100)) {
+ //分开后小圆点不断透明消失
+ float lv = (dy - (float) dragLimiteHeight) / (float) DensityUtil.dp2px(100); //0-1
+ float alpha = 1 - lv;
+ lay_dot.setAlpha(alpha);
+ lay_dot.setTranslationY(-dotDY + DensityUtil.dp2px(25));
+ } else if (dy >= dragLimiteHeight + DensityUtil.dp2px(100)) {
+ lay_dot.setAlpha(0f);
+ lay_dot.setTranslationY(-dotDY + DensityUtil.dp2px(25));
+ }
+ //背景渐变,内容缩放和平移
+ float MIN_SCALE = 0.8f;
+ //计算内容Y轴位移距离(保持顶部)
+ float springMoveHeight = springViewInner.getHeight() * (1 - MIN_SCALE) / 2;
+ if (dy < dragLimiteHeight) {
+ smokeView.setAlpha(0);
+ lay_content.setAlpha(0);
+ lay_content.setTranslationY(0);
+ if (weixinTitleBar != null) weixinTitleBar.setTitleBgAlpha(1);
+ if (contentView != null) contentView.setAlpha(1);
+ if (!hasOverSpringHeight) {
+ springViewInner.setScaleX(MIN_SCALE);
+ springViewInner.setScaleY(MIN_SCALE);
+ text_title.setScaleX(MIN_SCALE);
+ text_title.setScaleY(MIN_SCALE);
+ springViewInner.setTranslationY(-springMoveHeight);
+ }
+ } else if (dy < dragSpringHeight) {
+ //计算并背景透明度
+ float lv = (dy - (float) dragLimiteHeight) / ((float) dragSpringHeight - dragLimiteHeight); //0-1
+ smokeView.setAlpha(lv);
+ lay_content.setAlpha(lv);
+ if (contentView != null) contentView.setAlpha(1 - 0.9f * lv); //1-0.1
+ if (weixinTitleBar != null) weixinTitleBar.setTitleBgAlpha(0.1f * lv); //0-0.1
+ //计算内容缩放比例
+ float scale = MIN_SCALE + (1 - MIN_SCALE) * lv; //0.7-1
+ if (!hasOverSpringHeight) {
+ text_title.setScaleX(scale);
+ text_title.setScaleY(scale);
+ springViewInner.setScaleX(scale);
+ springViewInner.setScaleY(scale);
+ springViewInner.setTranslationY(-springMoveHeight * (1 - lv));
+ } else {
+ lay_content.setTranslationY(dy - dragSpringHeight);
+ }
+ } else {
+ smokeView.setAlpha(1);
+ lay_content.setAlpha(1);
+ if (weixinTitleBar != null) weixinTitleBar.setTitleBgAlpha(0.1f);
+ if (contentView != null) contentView.setAlpha(0f);
+ text_title.setScaleX(1);
+ text_title.setScaleY(1);
+ springViewInner.setScaleX(1);
+ springViewInner.setScaleY(1);
+ springViewInner.setTranslationY(0);
+ lay_content.setTranslationY(0);
+ }
+ }
+
+ @Override
+ public void onLimitDes(View rootView, boolean upORdown) {
+ //下拉超过临界高度时震动一下
+ if (!hasOverSpringHeight && !upORdown) {
+ Utils.vibrate(rootView.getContext(), 10);
+ }
+ }
+
+ @Override
+ public void onPreDrag(View rootView) {
+ //拖拽开始前,开始绘制烟雾效果
+ smokeView.startAnim();
+ springViewInner.callFresh();
+ }
+
+ @Override
+ public void onFinishDrag(View rootView) {
+ //header关闭后,停止烟雾效果的绘制,节省性能开支
+ smokeView.stopAnim();
+ }
+
+ @Override
+ public void onStartAnim() {
+ //动画:收起隐藏底部导航条
+ startNavAnim(false);
+ }
+
+ @Override
+ public void onFinishAnim() {
+ //动画:展开显示底部导航条
+ startNavAnim(true);
+ reset();
+ }
+
+ private void setViewWidthHeight(View view, int width, int height) {
+ ViewGroup.LayoutParams layoutParam1 = view.getLayoutParams();
+ layoutParam1.height = height;
+ layoutParam1.width = width;
+ view.setLayoutParams(layoutParam1);
+ }
+
+ //重置状态
+ private void reset() {
+ hasOverSpringHeight = false;
+ img_dot1.setTranslationX(0);
+ img_dot3.setTranslationX(0);
+ lay_dot.setTranslationY(0);
+ lay_dot.setAlpha(1f);
+ }
+
+ //开始拖拽item的回调
+ @Override
+ public void onStartDrag() {
+ startHeaderDelAnim(true);
+ }
+
+ //结束拖拽item的回调
+ @Override
+ public void onFinishDrag() {
+ startHeaderDelAnim(false);
+ adapter.notifyItemChanged(Utils.getMineTitlePosition(results));
+ }
+
+ //删除回调(拖拽到了'删除按钮'时)
+ @Override
+ public boolean onDrop(RecyclerView.ViewHolder viewHolder, int position) {
+ if (onProgramDropListener != null) {
+ //如果设置了外部回调接口,先回调外部接口,返回true则执行删除,否则不执行
+ boolean needDrop = onProgramDropListener.onDrop(results.get(position), viewHolder, position);
+ if (needDrop) removeItem(position);
+ return needDrop;
+ } else {
+ //如果没设置外部回调接口,直接执行删除
+ removeItem(position);
+ return true;
+ }
+ }
+
+ //###################### 对外公开的方法(添加、移除小程序) #######################
+
+ //添加小程序到'最近使用'
+ public boolean addItemRecent(List programs) {
+ int recentProgramCount = Utils.getRecentProgramCount(results);
+ if (recentProgramCount < 7) {
+ //如果添加后大于7条数据,则截取掉多余的数据
+ if (recentProgramCount + programs.size() > 7) {
+ programs = programs.subList(0, 7 - recentProgramCount);
+ }
+ int position = Utils.getMoreItemIndex(results);
+ if (position != -1) {
+ results.addAll(position, programs);
+ if (adapter != null) adapter.notifyDataSetChanged();
+ }
+ return true;
+ } else {
+ //最近使用最多只展示7条数据
+ return false;
+ }
+ }
+
+ //添加小程序到'最近使用'
+ public boolean addItemRecent(Program program) {
+ int recentProgramCount = Utils.getRecentProgramCount(results);
+ if (recentProgramCount < 7) {
+ int position = Utils.getMoreItemIndex(results);
+ if (position != -1) {
+ results.add(position, program);
+ if (adapter != null) {
+ adapter.notifyItemInserted(position);
+ }
+ }
+ return true;
+ } else {
+ //最近使用最多只展示7条数据
+ return false;
+ }
+ }
+
+ //添加小程序到'我的小程序'
+ public void addItemMine(Program program) {
+ results.add(program);
+ if (adapter != null) {
+ adapter.notifyItemInserted(results.size());
+ }
+ }
+
+ //添加小程序到'我的小程序'
+ public void addItemMine(List programs) {
+ results.addAll(programs);
+ if (adapter != null) {
+ adapter.notifyDataSetChanged();
+ }
+ }
+
+ //删除指定位置的小程序
+ public void removeItem(int position) {
+ if (results.size() > 0) {
+ results.remove(position);
+ if (adapter != null) {
+ adapter.notifyItemRemoved(position);
+ adapter.notifyItemRangeChanged(position, results.size() - position);
+ if (!Utils.hasMineProgram(results)) {
+ adapter.notifyItemChanged(Utils.getMineTitlePosition(results));
+ }
+ }
+ }
+ }
+
+ //获取当前的'最近使用'小程序的数量
+ public int getRecentProgramCount() {
+ return Utils.getRecentProgramCount(results);
+ }
+
+ //获取当前的'我的'小程序的数量
+ public int getMineProgramCount() {
+ return Utils.getMineProgramCount(results);
+ }
+
+ //###################### 动画 #######################
+
+ //底部导航条,展开收起动画
+ private void startNavAnim(boolean showOrHide) {
+ if (bottomView == null) return;
+ if (showOrHide && bottomView.getTranslationY() == 0) return;
+ if (!showOrHide && bottomView.getTranslationY() == bottomView.getHeight()) return;
+ ValueAnimator anim = ValueAnimator.ofFloat(showOrHide ? bottomView.getHeight() : 0f, showOrHide ? 0f : bottomView.getHeight());
+ anim.setInterpolator(new DecelerateInterpolator());
+ anim.setDuration(200);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ Float value = (Float) animation.getAnimatedValue();
+ bottomView.setTranslationY(value);
+ }
+ });
+ anim.start();
+ }
+
+ //"拖到这里删除"按钮,展开收起动画
+ private void startHeaderDelAnim(boolean showOrHide) {
+ if (contentLay == null) return;
+ if (weixinTitleBar == null) return;
+ ValueAnimator anim = ValueAnimator.ofInt(showOrHide ? 0 : weixinTitleBar.getDeleteViewHeight(), showOrHide ? weixinTitleBar.getDeleteViewHeight() : 0); //show:0-height,hide:height-0
+ anim.setInterpolator(new DecelerateInterpolator());
+ anim.setDuration(300);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ Integer value = (Integer) animation.getAnimatedValue();
+ contentLay.setTranslationY(-value);
+ //逐渐展开删除按钮
+ weixinTitleBar.setExtraHeight(value);
+ bgView.setTranslationY(weixinTitleBar.getDeleteViewHeight() - value);
+ }
+ });
+ anim.start();
+ }
+
+ //###################### 外部接口 #######################
+
+ public interface OnWeixinHeaderLoadImgCallback {
+ void loadImg(ImageView imageView, String imgUrl, int position);
+ }
+
+ public interface OnMoreClickListener {
+ void onMoreClick();
+ }
+
+ public interface OnSearchClickListener {
+ void onSearchClick();
+ }
+
+ public interface OnProgramClickListener {
+ void onClick(Program program, RecyclerView.ViewHolder holder, int position);
+ }
+
+ public interface OnProgramDropListener {
+ //拖拽删除,回调接口,如果返回true则执行删除,false不会删除
+ boolean onDrop(Program program, RecyclerView.ViewHolder holder, int position);
+ }
+
+ //#################### get & set #####################
+
+ public List getResults() {
+ return results;
+ }
+
+ public OnSearchClickListener getOnSearchClickListener() {
+ return onSearchClickListener;
+ }
+
+ public OnMoreClickListener getOnMoreClickListener() {
+ return onMoreClickListener;
+ }
+
+ public OnProgramClickListener getOnProgramClickListener() {
+ return onProgramClickListener;
+ }
+
+ public OnProgramDropListener getOnProgramDropListener() {
+ return onProgramDropListener;
+ }
+
+ public OnWeixinHeaderLoadImgCallback getImgLoadCallback() {
+ return imgLoadCallback;
+ }
+
+ public void setOnSearchClickListener(OnSearchClickListener onSearchClickListener) {
+ this.onSearchClickListener = onSearchClickListener;
+ }
+
+ public void setOnMoreClickListener(OnMoreClickListener onMoreClickListener) {
+ this.onMoreClickListener = onMoreClickListener;
+ }
+
+ public void setOnProgramClickListener(OnProgramClickListener onProgramClickListener) {
+ this.onProgramClickListener = onProgramClickListener;
+ }
+
+ public void setOnProgramDropListener(OnProgramDropListener onProgramDropListener) {
+ this.onProgramDropListener = onProgramDropListener;
+ }
+
+ public void setOnLoadImgCallback(OnWeixinHeaderLoadImgCallback imgLoadCallback) {
+ this.imgLoadCallback = imgLoadCallback;
+ }
+}
diff --git a/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/WeixinTitleBar.java b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/WeixinTitleBar.java
new file mode 100644
index 0000000..871d2fb
--- /dev/null
+++ b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/WeixinTitleBar.java
@@ -0,0 +1,110 @@
+package com.liaoinstan.springview.weixinheaderv2;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.liaoinstan.springview.weixinheader.R;
+
+/**
+ * create by liaoinstan
+ * 微信顶部titleBar,由于weixin header V2 许多拖拽动画需要和顶部标题栏交互,所以这里定义了一个WeixinTitleBar封装内部逻辑
+ */
+public class WeixinTitleBar extends FrameLayout {
+
+ private TextView text_delete;
+ private View title_bg_view;
+ private View lay_title_content;
+ private FrameLayout lay_content;
+
+ public WeixinTitleBar(Context context) {
+ this(context, null);
+ }
+
+ public WeixinTitleBar(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public WeixinTitleBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ View root = LayoutInflater.from(getContext()).inflate(R.layout.view_weixin_title_bar, null, false);
+ text_delete = root.findViewById(R.id.text_delete);
+ title_bg_view = root.findViewById(R.id.title_bg_view);
+ lay_title_content = root.findViewById(R.id.lay_title_content);
+ lay_content = root.findViewById(R.id.lay_content);
+
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View childView = getChildAt(0);
+ removeView(childView);
+ lay_content.addView(childView);
+ }
+ addView(root);
+
+ initView();
+ super.onFinishInflate();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (isInEditMode()) {
+ int height = getMeasuredHeight();
+ ViewGroup.LayoutParams layoutParams = lay_title_content.getLayoutParams();
+ layoutParams.height = height;
+ lay_title_content.setLayoutParams(layoutParams);
+ text_delete.setTranslationY(height);
+ }
+ }
+
+ private void initView() {
+ getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ int height = getMeasuredHeight();
+ ViewGroup.LayoutParams layoutParams = lay_title_content.getLayoutParams();
+ layoutParams.height = height;
+ lay_title_content.setLayoutParams(layoutParams);
+ text_delete.setTranslationY(height);
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ }
+ });
+ }
+
+ public void setExtraHeight(int height) {
+ ViewGroup.LayoutParams layoutParams = getLayoutParams();
+ layoutParams.height = height + getTitleViewHeight();
+ setLayoutParams(layoutParams);
+ }
+
+ public void setTitleBgAlpha(float alpha) {
+ getTitleBgView().setAlpha(alpha);
+ }
+
+ public int getTitleViewHeight() {
+ return lay_title_content.getMeasuredHeight();
+ }
+
+ public int getDeleteViewHeight() {
+ return getDeleteView().getHeight();
+ }
+
+ public View getTitleBgView() {
+ return title_bg_view;
+ }
+
+ public TextView getDeleteView() {
+ return text_delete;
+ }
+}
diff --git a/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/WeixinV2InnerFooter.java b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/WeixinV2InnerFooter.java
new file mode 100644
index 0000000..3d8651c
--- /dev/null
+++ b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/WeixinV2InnerFooter.java
@@ -0,0 +1,69 @@
+package com.liaoinstan.springview.weixinheaderv2;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.liaoinstan.springview.container.BaseSimpleFooter;
+import com.liaoinstan.springview.utils.DensityUtil;
+import com.liaoinstan.springview.widget.SpringView;
+
+public class WeixinV2InnerFooter extends BaseSimpleFooter {
+
+ private OnDragFinishListener onDragFinishListener;
+
+ WeixinV2InnerFooter(OnDragFinishListener onDragFinishListener) {
+ setMovePara(2.0f);
+ setType(SpringView.Type.FOLLOW);
+ this.onDragFinishListener = onDragFinishListener;
+ }
+
+ @Override
+ public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
+ View root = new View(viewGroup.getContext());
+ ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, DensityUtil.dp2px(200));
+ root.setLayoutParams(layoutParams);
+ return root;
+ }
+
+ @Override
+ public int getDragLimitHeight(View rootView) {
+ return DensityUtil.dp2px(60);
+ }
+
+ @Override
+ public int getDragMaxHeight(View rootView) {
+ return rootView.getMeasuredHeight();
+ }
+
+ @Override
+ public int getDragSpringHeight(View rootView) {
+ return getDragMaxHeight(rootView) - 1;
+ }
+
+
+ @Override
+ public void onPreDrag(View rootView) {
+ }
+
+ @Override
+ public void onDropAnim(View rootView, int dy) {
+ }
+
+ @Override
+ public void onLimitDes(View rootView, boolean upORdown) {
+ }
+
+ @Override
+ public void onStartAnim() {
+ if (onDragFinishListener != null) onDragFinishListener.onDragFinish();
+ }
+
+ @Override
+ public void onFinishAnim() {
+ }
+
+ interface OnDragFinishListener {
+ void onDragFinish();
+ }
+}
diff --git a/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/WeixinV2InnerHeader.java b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/WeixinV2InnerHeader.java
new file mode 100644
index 0000000..ca40eff
--- /dev/null
+++ b/header_weixin/src/main/java/com/liaoinstan/springview/weixinheaderv2/WeixinV2InnerHeader.java
@@ -0,0 +1,82 @@
+package com.liaoinstan.springview.weixinheaderv2;
+
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.core.graphics.drawable.DrawableCompat;
+
+import com.liaoinstan.springview.container.BaseSimpleHeader;
+import com.liaoinstan.springview.utils.DensityUtil;
+import com.liaoinstan.springview.weixinheader.R;
+import com.liaoinstan.springview.widget.SpringView;
+
+public class WeixinV2InnerHeader extends BaseSimpleHeader {
+
+ private OnSearchClickListener onSearchClickListener;
+
+ WeixinV2InnerHeader(OnSearchClickListener onSearchClickListener) {
+ setMovePara(2.0f);
+ setType(SpringView.Type.FOLLOW);
+ this.onSearchClickListener = onSearchClickListener;
+ }
+
+ @Override
+ public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
+ View root = inflater.inflate(R.layout.weixin_header_v2_inner, viewGroup, false);
+ TextView text_search = root.findViewById(R.id.text_search);
+ //搜索图标着色,用DrawableCompat着色可以向下兼容,而drawableTint属性不向下兼容
+ Drawable originalSearchDrawable = viewGroup.getContext().getResources().getDrawable(R.drawable.weixin_ic_search_light).mutate();
+ Drawable searchDrawable = DrawableCompat.wrap(originalSearchDrawable);
+ DrawableCompat.setTintList(searchDrawable, text_search.getTextColors());
+ text_search.setCompoundDrawablesWithIntrinsicBounds(searchDrawable, null, null, null);
+ text_search.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (onSearchClickListener != null) onSearchClickListener.onSearchClick(v);
+ }
+ });
+ return root;
+ }
+
+ @Override
+ public int getDragLimitHeight(View rootView) {
+ return getDragSpringHeight(rootView) / 2;
+ }
+
+ @Override
+ public int getDragMaxHeight(View rootView) {
+ return rootView.getMeasuredHeight() + DensityUtil.dp2px(100);
+ }
+
+ @Override
+ public int getDragSpringHeight(View rootView) {
+ return rootView.getMeasuredHeight();
+ }
+
+ @Override
+ public void onPreDrag(View rootView) {
+ }
+
+ @Override
+ public void onDropAnim(View rootView, int dy) {
+ }
+
+ @Override
+ public void onLimitDes(View rootView, boolean upORdown) {
+ }
+
+ @Override
+ public void onStartAnim() {
+ }
+
+ @Override
+ public void onFinishAnim() {
+ }
+
+ interface OnSearchClickListener {
+ void onSearchClick(View view);
+ }
+}
diff --git a/header_weixin/src/main/res/drawable-xxhdpi/smoke.png b/header_weixin/src/main/res/drawable-xxhdpi/smoke.png
new file mode 100644
index 0000000..f3e46b6
Binary files /dev/null and b/header_weixin/src/main/res/drawable-xxhdpi/smoke.png differ
diff --git a/header_weixin/src/main/res/drawable-xxhdpi/weixin_ic_drop.png b/header_weixin/src/main/res/drawable-xxhdpi/weixin_ic_drop.png
new file mode 100644
index 0000000..510ce80
Binary files /dev/null and b/header_weixin/src/main/res/drawable-xxhdpi/weixin_ic_drop.png differ
diff --git a/header_weixin/src/main/res/drawable-xxhdpi/weixin_ic_drop_hot.png b/header_weixin/src/main/res/drawable-xxhdpi/weixin_ic_drop_hot.png
new file mode 100644
index 0000000..03573c3
Binary files /dev/null and b/header_weixin/src/main/res/drawable-xxhdpi/weixin_ic_drop_hot.png differ
diff --git a/header_weixin/src/main/res/drawable-xxhdpi/weixin_ic_search.png b/header_weixin/src/main/res/drawable-xxhdpi/weixin_ic_search.png
new file mode 100644
index 0000000..8b81ac6
Binary files /dev/null and b/header_weixin/src/main/res/drawable-xxhdpi/weixin_ic_search.png differ
diff --git a/header_weixin/src/main/res/drawable-xxhdpi/weixin_ic_search_light.png b/header_weixin/src/main/res/drawable-xxhdpi/weixin_ic_search_light.png
new file mode 100644
index 0000000..9ce06ce
Binary files /dev/null and b/header_weixin/src/main/res/drawable-xxhdpi/weixin_ic_search_light.png differ
diff --git a/header_weixin/src/main/res/drawable/select_delete_bg.xml b/header_weixin/src/main/res/drawable/select_delete_bg.xml
new file mode 100644
index 0000000..2e7aa40
--- /dev/null
+++ b/header_weixin/src/main/res/drawable/select_delete_bg.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/header_weixin/src/main/res/drawable/select_delete_drop.xml b/header_weixin/src/main/res/drawable/select_delete_drop.xml
new file mode 100644
index 0000000..e432fbf
--- /dev/null
+++ b/header_weixin/src/main/res/drawable/select_delete_drop.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/header_weixin/src/main/res/drawable/select_oval_dark_trans.xml b/header_weixin/src/main/res/drawable/select_oval_dark_trans.xml
new file mode 100644
index 0000000..d0dcb55
--- /dev/null
+++ b/header_weixin/src/main/res/drawable/select_oval_dark_trans.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/header_weixin/src/main/res/drawable/shape_more.xml b/header_weixin/src/main/res/drawable/shape_more.xml
new file mode 100644
index 0000000..90ed940
--- /dev/null
+++ b/header_weixin/src/main/res/drawable/shape_more.xml
@@ -0,0 +1,41 @@
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/header_weixin/src/main/res/drawable/shape_none.xml b/header_weixin/src/main/res/drawable/shape_none.xml
new file mode 100644
index 0000000..cc1a528
--- /dev/null
+++ b/header_weixin/src/main/res/drawable/shape_none.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/header_weixin/src/main/res/drawable/shape_oval_dark_trans.xml b/header_weixin/src/main/res/drawable/shape_oval_dark_trans.xml
new file mode 100644
index 0000000..4976b67
--- /dev/null
+++ b/header_weixin/src/main/res/drawable/shape_oval_dark_trans.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/header_weixin/src/main/res/drawable/shape_oval_dot.xml b/header_weixin/src/main/res/drawable/shape_oval_dot.xml
new file mode 100644
index 0000000..344ce0b
--- /dev/null
+++ b/header_weixin/src/main/res/drawable/shape_oval_dot.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/header_weixin/src/main/res/drawable/shape_oval_dot_dark.xml b/header_weixin/src/main/res/drawable/shape_oval_dot_dark.xml
new file mode 100644
index 0000000..355ac70
--- /dev/null
+++ b/header_weixin/src/main/res/drawable/shape_oval_dot_dark.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/header_weixin/src/main/res/drawable/shape_rect_coner_dark.xml b/header_weixin/src/main/res/drawable/shape_rect_coner_dark.xml
new file mode 100644
index 0000000..e016dd6
--- /dev/null
+++ b/header_weixin/src/main/res/drawable/shape_rect_coner_dark.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/header_weixin/src/main/res/drawable/shape_rect_coner_dash_line.xml b/header_weixin/src/main/res/drawable/shape_rect_coner_dash_line.xml
new file mode 100644
index 0000000..633ffd6
--- /dev/null
+++ b/header_weixin/src/main/res/drawable/shape_rect_coner_dash_line.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/header_weixin/src/main/res/drawable/shape_rect_coner_top_bg.xml b/header_weixin/src/main/res/drawable/shape_rect_coner_top_bg.xml
new file mode 100644
index 0000000..76c79e7
--- /dev/null
+++ b/header_weixin/src/main/res/drawable/shape_rect_coner_top_bg.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/header_weixin/src/main/res/drawable/shape_rect_gradient_weixinheader_bg.xml b/header_weixin/src/main/res/drawable/shape_rect_gradient_weixinheader_bg.xml
new file mode 100644
index 0000000..882a0e2
--- /dev/null
+++ b/header_weixin/src/main/res/drawable/shape_rect_gradient_weixinheader_bg.xml
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/header_weixin/src/main/res/layout/view_weixin_title_bar.xml b/header_weixin/src/main/res/layout/view_weixin_title_bar.xml
new file mode 100644
index 0000000..67ec341
--- /dev/null
+++ b/header_weixin/src/main/res/layout/view_weixin_title_bar.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/header_weixin/src/main/res/layout/weixin_header.xml b/header_weixin/src/main/res/layout/weixin_header.xml
new file mode 100644
index 0000000..a655cb6
--- /dev/null
+++ b/header_weixin/src/main/res/layout/weixin_header.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/header_weixin/src/main/res/layout/weixin_header_item.xml b/header_weixin/src/main/res/layout/weixin_header_item.xml
new file mode 100644
index 0000000..14b6540
--- /dev/null
+++ b/header_weixin/src/main/res/layout/weixin_header_item.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
diff --git a/header_weixin/src/main/res/layout/weixin_header_item_more_v2.xml b/header_weixin/src/main/res/layout/weixin_header_item_more_v2.xml
new file mode 100644
index 0000000..9e8900d
--- /dev/null
+++ b/header_weixin/src/main/res/layout/weixin_header_item_more_v2.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/header_weixin/src/main/res/layout/weixin_header_item_title_v2.xml b/header_weixin/src/main/res/layout/weixin_header_item_title_v2.xml
new file mode 100644
index 0000000..da77481
--- /dev/null
+++ b/header_weixin/src/main/res/layout/weixin_header_item_title_v2.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/header_weixin/src/main/res/layout/weixin_header_item_v2.xml b/header_weixin/src/main/res/layout/weixin_header_item_v2.xml
new file mode 100644
index 0000000..5279af5
--- /dev/null
+++ b/header_weixin/src/main/res/layout/weixin_header_item_v2.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
diff --git a/header_weixin/src/main/res/layout/weixin_header_v2.xml b/header_weixin/src/main/res/layout/weixin_header_v2.xml
new file mode 100644
index 0000000..c094863
--- /dev/null
+++ b/header_weixin/src/main/res/layout/weixin_header_v2.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/header_weixin/src/main/res/layout/weixin_header_v2_inner.xml b/header_weixin/src/main/res/layout/weixin_header_v2_inner.xml
new file mode 100644
index 0000000..a1424e4
--- /dev/null
+++ b/header_weixin/src/main/res/layout/weixin_header_v2_inner.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
diff --git a/header_weixin/src/main/res/values/colors.xml b/header_weixin/src/main/res/values/colors.xml
new file mode 100644
index 0000000..d1d0d5f
--- /dev/null
+++ b/header_weixin/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #f1f1f1
+ #191919
+ #2BA245
+ #f9f9f9
+ #f3f3f7
+ #E64340
+ #22000000
+
diff --git a/header_weixin/src/main/res/values/strings.xml b/header_weixin/src/main/res/values/strings.xml
new file mode 100644
index 0000000..7ebb900
--- /dev/null
+++ b/header_weixin/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ weixinheader
+ 松手即可删除
+ 拖到此处删除
+
diff --git a/library/build.gradle b/library/build.gradle
index c958cd6..5e78aa6 100644
--- a/library/build.gradle
+++ b/library/build.gradle
@@ -1,14 +1,12 @@
apply plugin: 'com.android.library'
-apply plugin: 'com.github.dcendents.android-maven'
-apply plugin: 'com.jfrog.bintray'
+apply plugin: 'com.novoda.bintray-release'
android {
- compileSdkVersion 25
- buildToolsVersion "25.0.2"
+ compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
- minSdkVersion 14
- targetSdkVersion 25
+ minSdkVersion rootProject.ext.minSdkVersion
+ targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
}
@@ -21,83 +19,20 @@ android {
}
dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- compile 'com.android.support:appcompat-v7:25.3.1'
- compile 'com.android.support:design:25.3.1'
+ implementation fileTree(include: ['*.jar'], dir: 'libs')
+ implementation "androidx.appcompat:appcompat:$supportLibraryVersion"
+ implementation "com.google.android.material:material:$materialLibraryVersion"
}
-version = "1.3.2" // #CONFIG# // project version
-// 根节点添加
-def siteUrl = 'https://github.com/liaoinstan/SpringView' // #CONFIG# // project homepage
-def gitUrl = 'https://github.com/liaoinstan/SpringView.git' // #CONFIG# // project git
-// 根节点添加
-group = "com.liaoinstan.springview"
-// 根节点添加
-install {
- repositories.mavenInstaller {
- // This generates POM.xml with proper parameters
- pom {
- project {
- packaging 'aar'
- name 'SpringView For Android' // #CONFIG# // project git
- url siteUrl
- licenses {
- license {
- name 'The Apache Software License, Version 2.0'
- url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
- }
- }
- developers {
- developer {
- id 'liaoinstan' // #CONFIG# // your user id (you can write your nickname)
- name 'liaoinstan' // #CONFIG# // your user name
- email 'liaoinstan@outlook.com' // #CONFIG# // your email
- }
- }
- scm {
- connection gitUrl
- developerConnection gitUrl
- url siteUrl
- }
- }
- }
- }
-}
-
-task sourcesJar(type: Jar) {
- from android.sourceSets.main.java.srcDirs
- classifier = 'sources'
-}
-task javadoc(type: Javadoc) {
- options.encoding = "utf-8"
- source = android.sourceSets.main.java.srcDirs
- classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
-}
-task javadocJar(type: Jar, dependsOn: javadoc) {
- classifier = 'javadoc'
- from javadoc.destinationDir
-}
-artifacts {
- archives javadocJar
- archives sourcesJar
-}
-Properties properties = new Properties()
-properties.load(project.rootProject.file('local.properties').newDataInputStream())
-bintray {
- user = properties.getProperty("bintray.user")
- key = properties.getProperty("bintray.apikey")
- configurations = ['archives']
- pkg {
- repo = "maven"
- name = "SpringView" // #CONFIG# project name in jcenter
- websiteUrl = siteUrl
- vcsUrl = gitUrl
- licenses = ["Apache-2.0"]
- publish = true
- }
-}
-
-//gradlew javadocJar
-//gradlew sourcesJar
-//gradlew install
-//gradlew bintrayUpload
\ No newline at end of file
+publish {
+ artifactId = 'library' // artifactId
+ uploadName = 'SpringView' // 展示名字
+ publishVersion = '1.7.0' // 版本
+ desc = rootProject.desc
+ userOrg = rootProject.userOrg
+ groupId = rootProject.groupId
+ website = rootProject.website
+ licences = rootProject.licences
+ autoPublish = false
+ dryRun = true
+}
\ No newline at end of file
diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml
index a48da41..fe1d66a 100644
--- a/library/src/main/AndroidManifest.xml
+++ b/library/src/main/AndroidManifest.xml
@@ -1,11 +1 @@
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/library/src/main/java/com/liaoinstan/springview/container/AutoFooter.java b/library/src/main/java/com/liaoinstan/springview/container/AutoFooter.java
new file mode 100644
index 0000000..c16fcb9
--- /dev/null
+++ b/library/src/main/java/com/liaoinstan/springview/container/AutoFooter.java
@@ -0,0 +1,76 @@
+package com.liaoinstan.springview.container;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.liaoinstan.springview.R;
+import com.liaoinstan.springview.widget.SpringView;
+
+public class AutoFooter extends BaseScrollFooter {
+
+ protected View rootView;
+ protected ProgressBar progress_auto;
+ protected View lay_auto_bottom_line;
+ protected TextView text_bottom;
+ protected boolean isInProgress;
+
+ private String bottomText;
+
+ public AutoFooter() {
+ this("我是有底线的");
+ }
+
+ public AutoFooter(String bottomText) {
+ setType(SpringView.Type.SCROLL);
+ this.bottomText = bottomText;
+ }
+
+ @Override
+ public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
+ rootView = inflater.inflate(R.layout.auto_footer, viewGroup, false);
+ progress_auto = rootView.findViewById(R.id.progress_auto);
+ lay_auto_bottom_line = rootView.findViewById(R.id.lay_auto_bottom_line);
+ text_bottom = rootView.findViewById(R.id.text_bottom);
+ text_bottom.setText(bottomText);
+ showProgressOrBottomLine(true);
+ return rootView;
+ }
+
+ @Override
+ protected void onScrollBottom() {
+ }
+
+ @Override
+ protected void onScrollOut() {
+ }
+
+ @Override
+ protected void onFooterMove(View rootView, int dy) {
+ }
+
+ @Override
+ protected void onScreenFull(View rootView, boolean screenFull) {
+ rootView.setVisibility(screenFull ? View.VISIBLE : View.GONE);
+ }
+
+ private void showProgressOrBottomLine(boolean isProgress) {
+ this.isInProgress = isProgress;
+ progress_auto.setVisibility(isProgress ? View.VISIBLE : View.GONE);
+ lay_auto_bottom_line.setVisibility(!isProgress ? View.VISIBLE : View.GONE);
+ }
+
+ public void showProgress() {
+ showProgressOrBottomLine(true);
+ }
+
+ public void showBottomLine() {
+ showProgressOrBottomLine(false);
+ }
+
+ public boolean isInProgress() {
+ return isInProgress;
+ }
+}
diff --git a/library/src/main/java/com/liaoinstan/springview/container/BaseFooter.java b/library/src/main/java/com/liaoinstan/springview/container/BaseFooter.java
index 92c3b34..a98ab83 100644
--- a/library/src/main/java/com/liaoinstan/springview/container/BaseFooter.java
+++ b/library/src/main/java/com/liaoinstan/springview/container/BaseFooter.java
@@ -11,8 +11,10 @@
*/
public abstract class BaseFooter implements SpringView.DragHander {
+ public String TAG = getClass().getSimpleName();
+
/**
- * 这个方法用于设置当前View的临界高度(limit hight),即拉动到多少会被认定为刷新超作,而没到达该高度则不会执行刷新
+ * 这个方法用于设置当前View的临界高度(limit hight),即拉动到多少会被认定为刷新操作,而没到达该高度则不会执行刷新
* 返回值大于0才有效,如果<=0 则设置为默认footer的高度
* 默认返回0
*/
@@ -39,4 +41,80 @@ public int getDragMaxHeight(View rootView) {
public int getDragSpringHeight(View rootView) {
return 0;
}
+
+ /**
+ * 设置移动参数
+ * 如果你的footer实现了该接口,那么SpringView会优先使用该接口返回的结果(忽略掉:SpringView.setMovePara())
+ * 添加这个接口的目的是为了让不同的footer能够自行定义自己的摩擦阻力,而不再是由SpringView进行设置
+ * 返回值大于0才有效,默认返回0
+ */
+ @Override
+ public float getMovePara() {
+ return 0;
+ }
+
+ /**
+ * 返回header的拖动方式:type
+ * 返回值非null才有效,默认返回null
+ */
+ @Override
+ public SpringView.Type getType() {
+ return null;
+ }
+
+ /**
+ * 即将开始拖拽时的回调,可进行初始化操作
+ */
+ @Override
+ public void onPreDrag(View rootView) {
+ }
+
+ /**
+ * 拖拽结束时回调,不管是否拖拽超过limit阈值
+ */
+ @Override
+ public void onFinishDrag(View rootView) {
+ }
+
+ @Override
+ public void onResetAnim() {
+ }
+
+ //###################################################
+ //############ 2018/9/13 新增收场动画接口 ###########
+ //###################################################
+
+ /**
+ * 收场动画执行时间
+ * 返回值大于0才有效
+ * 默认返回0
+ */
+ @Override
+ public int getEndingAnimTime() {
+ return 0;
+ }
+
+ /**
+ * 收场动画回弹高度
+ * 返回值大于0才有效
+ * 默认返回0
+ */
+ @Override
+ public int getEndingAnimHeight(View rootView) {
+ return 0;
+ }
+
+ /**
+ * 收场动画开始执行,默认空实现,如需要收场动画重写该方法
+ */
+ @Override
+ public void onEndingAnimStart() {
+ }
+
+ /**
+ * 收场动画结束执行,默认空实现,如需要收场动画重写该方法
+ */
+ @Override
+ public void onEndingAnimEnd() {
+ }
}
diff --git a/library/src/main/java/com/liaoinstan/springview/container/BaseHeader.java b/library/src/main/java/com/liaoinstan/springview/container/BaseHeader.java
index e6ce158..035f079 100644
--- a/library/src/main/java/com/liaoinstan/springview/container/BaseHeader.java
+++ b/library/src/main/java/com/liaoinstan/springview/container/BaseHeader.java
@@ -11,6 +11,8 @@
*/
public abstract class BaseHeader implements SpringView.DragHander {
+ public String TAG = getClass().getSimpleName();
+
/**
* 这个方法用于设置当前View的临界高度(limit hight),即拉动到多少会被认定为刷新超作,而没到达该高度则不会执行刷新
* 返回值大于0才有效,如果<=0 则设置为默认header的高度
@@ -40,4 +42,80 @@ public int getDragMaxHeight(View rootView) {
public int getDragSpringHeight(View rootView) {
return 0;
}
+
+ /**
+ * 设置移动参数
+ * 如果你的header实现了该接口,那么SpringView会优先使用该接口返回的结果(忽略掉:SpringView.setMovePara())
+ * 添加这个接口的目的是为了让不同的header能够自行定义自己的摩擦阻力,而不再是由SpringView进行设置
+ * 返回值大于0才有效,默认返回0
+ */
+ @Override
+ public float getMovePara() {
+ return 0;
+ }
+
+ /**
+ * 返回header的拖动方式:type
+ * 返回值非null才有效,默认返回null
+ */
+ @Override
+ public SpringView.Type getType() {
+ return null;
+ }
+
+ /**
+ * 即将开始拖拽时的回调,可进行初始化操作
+ */
+ @Override
+ public void onPreDrag(View rootView) {
+ }
+
+ /**
+ * 拖拽结束时回调,不管是否拖拽超过limit阈值
+ */
+ @Override
+ public void onFinishDrag(View rootView) {
+ }
+
+ @Override
+ public void onResetAnim() {
+ }
+
+ //###################################################
+ //############ 2018/9/13 新增收场动画接口 ###########
+ //###################################################
+
+ /**
+ * 收场动画执行时间
+ * 返回值大于0才有效
+ * 默认返回0
+ */
+ @Override
+ public int getEndingAnimTime() {
+ return 0;
+ }
+
+ /**
+ * 收场动画回弹高度
+ * 返回值大于0才有效
+ * 默认返回0
+ */
+ @Override
+ public int getEndingAnimHeight(View rootView) {
+ return 0;
+ }
+
+ /**
+ * 收场动画开始执行,默认空实现,如需要收场动画重写该方法
+ */
+ @Override
+ public void onEndingAnimStart() {
+ }
+
+ /**
+ * 收场动画结束执行,默认空实现,如需要收场动画重写该方法
+ */
+ @Override
+ public void onEndingAnimEnd() {
+ }
}
diff --git a/library/src/main/java/com/liaoinstan/springview/container/BaseScrollFooter.java b/library/src/main/java/com/liaoinstan/springview/container/BaseScrollFooter.java
new file mode 100644
index 0000000..d2693ef
--- /dev/null
+++ b/library/src/main/java/com/liaoinstan/springview/container/BaseScrollFooter.java
@@ -0,0 +1,90 @@
+package com.liaoinstan.springview.container;
+
+import android.view.View;
+
+public abstract class BaseScrollFooter extends BaseSimpleFooter {
+
+ @Override
+ public final void onDropAnim(View rootView, int dy) {
+ onFooterMove(rootView, dy);
+ }
+
+ @Override
+ public final void onLimitDes(View rootView, boolean upORdown) {
+ onScreenFull(rootView, upORdown);
+ }
+
+ @Override
+ public final void onStartAnim() {
+ onScrollBottom();
+ }
+
+ @Override
+ public final void onFinishAnim() {
+ onScrollOut();
+ }
+
+ @Override
+ public final void onPreDrag(View rootView) {
+ }
+
+ @Override
+ public final void onFinishDrag(View rootView) {
+ }
+
+ @Override
+ public final int getDragLimitHeight(View rootView) {
+ return 0;
+ }
+
+ @Override
+ public final int getDragMaxHeight(View rootView) {
+ return 0;
+ }
+
+ @Override
+ public final int getDragSpringHeight(View rootView) {
+ return 0;
+ }
+
+ @Override
+ public final int getEndingAnimTime() {
+ return 0;
+ }
+
+ @Override
+ public final int getEndingAnimHeight(View rootView) {
+ return 0;
+ }
+
+ @Override
+ public final void onEndingAnimStart() {
+ }
+
+ @Override
+ public final void onEndingAnimEnd() {
+ }
+
+ /**
+ * 当页面滚动到最底部时回调(此时footer已经完全展示)
+ */
+ protected abstract void onScrollBottom();
+
+ /**
+ * 当页面滚动到footer完全不可见时回调(此时footer已经完全滚出页面)
+ */
+ protected abstract void onScrollOut();
+
+ /**
+ * 当正在滚动footer时回调,此时该方法会不断调用,并提供footer滚动距离:dy
+ * dy 范围为 0 - footer最大高度,为0时footer刚好不可见,为footer最大高度时刚好完全可见
+ */
+ protected abstract void onFooterMove(View rootView, int dy);
+
+ /**
+ * 是否一屏可用完全展示内容回调
+ * screenFull 为 false 时,表示此时页面内容高度不足一屏(无法滚动),为true表示大于一屏
+ * 添加这个接口的主要目的是为了,在页面内容高度不足以支持滚动时不显示footer,如果有这种需求就可以在这个回调中处理,否则可用忽略
+ */
+ protected abstract void onScreenFull(View rootView, boolean screenFull);
+}
diff --git a/library/src/main/java/com/liaoinstan/springview/container/BaseScrollHeader.java b/library/src/main/java/com/liaoinstan/springview/container/BaseScrollHeader.java
new file mode 100644
index 0000000..5d1e00c
--- /dev/null
+++ b/library/src/main/java/com/liaoinstan/springview/container/BaseScrollHeader.java
@@ -0,0 +1,45 @@
+package com.liaoinstan.springview.container;
+
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.liaoinstan.springview.R;
+import com.liaoinstan.springview.widget.SpringView;
+
+public class BaseScrollHeader extends BaseHeader {
+
+ private View rootView;
+
+ @Override
+ public SpringView.Type getType() {
+ return SpringView.Type.SCROLL;
+ }
+
+ @Override
+ public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
+ rootView = inflater.inflate(R.layout.default_header, viewGroup, false);
+ return rootView;
+ }
+
+ @Override
+ public void onDropAnim(View rootView, int dy) {
+// Log.e(TAG, "onDropAnim -> dy:" + dy);
+ }
+
+ @Override
+ public void onLimitDes(View rootView, boolean upORdown) {
+ Log.e(TAG, "onLimitDes -> upORdown:" + upORdown);
+ }
+
+ @Override
+ public void onStartAnim() {
+ Log.e(TAG, "onStartAnim");
+ }
+
+ @Override
+ public void onFinishAnim() {
+ Log.e(TAG, "onFinishAnim");
+ }
+}
diff --git a/library/src/main/java/com/liaoinstan/springview/container/BaseSimpleFooter.java b/library/src/main/java/com/liaoinstan/springview/container/BaseSimpleFooter.java
new file mode 100644
index 0000000..d7a562f
--- /dev/null
+++ b/library/src/main/java/com/liaoinstan/springview/container/BaseSimpleFooter.java
@@ -0,0 +1,33 @@
+package com.liaoinstan.springview.container;
+
+import com.liaoinstan.springview.widget.SpringView;
+
+/**
+ * Create by liaoinstan 2019/12/9
+ * 默认尾部的简单封装,主要把摩擦系数和type设置封装在内部,如果继承该类,则可直接调用 footer.setType(..),footer.setMovePara(..) 来改变type类型,和摩擦系数
+ */
+public abstract class BaseSimpleFooter extends BaseHeader {
+
+ private float movePara = 2.0f;
+ private SpringView.Type type = SpringView.Type.FOLLOW;
+
+ @Override
+ public SpringView.Type getType() {
+ return type;
+ }
+
+ @Override
+ public float getMovePara() {
+ return movePara;
+ }
+
+ public BaseSimpleFooter setType(SpringView.Type type) {
+ this.type = type;
+ return this;
+ }
+
+ public BaseSimpleFooter setMovePara(float movePara) {
+ this.movePara = movePara;
+ return this;
+ }
+}
diff --git a/library/src/main/java/com/liaoinstan/springview/container/BaseSimpleHeader.java b/library/src/main/java/com/liaoinstan/springview/container/BaseSimpleHeader.java
new file mode 100644
index 0000000..41b1312
--- /dev/null
+++ b/library/src/main/java/com/liaoinstan/springview/container/BaseSimpleHeader.java
@@ -0,0 +1,33 @@
+package com.liaoinstan.springview.container;
+
+import com.liaoinstan.springview.widget.SpringView;
+
+/**
+ * Create by liaoinstan 2019/12/9
+ * 默认头部的简单封装,主要把摩擦系数和type设置封装在内部,如果继承该类,则可直接调用 header.setType(..),header.setMovePara(..) 来改变type类型,和摩擦系数
+ */
+public abstract class BaseSimpleHeader extends BaseHeader {
+
+ private float movePara = 2.0f;
+ private SpringView.Type type = SpringView.Type.FOLLOW;
+
+ @Override
+ public SpringView.Type getType() {
+ return type;
+ }
+
+ @Override
+ public float getMovePara() {
+ return movePara;
+ }
+
+ public BaseSimpleHeader setType(SpringView.Type type) {
+ this.type = type;
+ return this;
+ }
+
+ public BaseSimpleHeader setMovePara(float movePara) {
+ this.movePara = movePara;
+ return this;
+ }
+}
diff --git a/library/src/main/java/com/liaoinstan/springview/container/DefaultFooter.java b/library/src/main/java/com/liaoinstan/springview/container/DefaultFooter.java
index b644a26..92d61a0 100644
--- a/library/src/main/java/com/liaoinstan/springview/container/DefaultFooter.java
+++ b/library/src/main/java/com/liaoinstan/springview/container/DefaultFooter.java
@@ -1,19 +1,21 @@
package com.liaoinstan.springview.container;
import android.content.Context;
-import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.core.content.ContextCompat;
+
import com.liaoinstan.springview.R;
+import com.liaoinstan.springview.widget.SpringView;
/**
* Created by Administrator on 2016/3/21.
*/
-public class DefaultFooter extends BaseFooter {
+public class DefaultFooter extends BaseSimpleFooter {
private Context context;
private int rotationSrc;
private TextView footerTitle;
@@ -24,15 +26,17 @@ public DefaultFooter(Context context) {
}
public DefaultFooter(Context context, int rotationSrc) {
+ setType(SpringView.Type.FOLLOW);
+ setMovePara(2.0f);
this.context = context;
this.rotationSrc = rotationSrc;
}
@Override
public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
- View view = inflater.inflate(R.layout.default_footer, viewGroup, true);
- footerTitle = (TextView) view.findViewById(R.id.default_footer_title);
- footerProgressbar = (ProgressBar) view.findViewById(R.id.default_footer_progressbar);
+ View view = inflater.inflate(R.layout.default_footer, viewGroup, false);
+ footerTitle = view.findViewById(R.id.default_footer_title);
+ footerProgressbar = view.findViewById(R.id.default_footer_progressbar);
footerProgressbar.setIndeterminateDrawable(ContextCompat.getDrawable(context, rotationSrc));
return view;
}
diff --git a/library/src/main/java/com/liaoinstan/springview/container/DefaultHeader.java b/library/src/main/java/com/liaoinstan/springview/container/DefaultHeader.java
index 141f2c1..2cd3a90 100644
--- a/library/src/main/java/com/liaoinstan/springview/container/DefaultHeader.java
+++ b/library/src/main/java/com/liaoinstan/springview/container/DefaultHeader.java
@@ -1,7 +1,6 @@
package com.liaoinstan.springview.container;
import android.content.Context;
-import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -11,12 +10,15 @@
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.core.content.ContextCompat;
+
import com.liaoinstan.springview.R;
+import com.liaoinstan.springview.widget.SpringView;
/**
* Created by Administrator on 2016/3/21.
*/
-public class DefaultHeader extends BaseHeader {
+public class DefaultHeader extends BaseSimpleHeader {
private Context context;
private int rotationSrc;
private int arrowSrc;
@@ -37,6 +39,8 @@ public DefaultHeader(Context context) {
}
public DefaultHeader(Context context, int rotationSrc, int arrowSrc) {
+ setType(SpringView.Type.FOLLOW);
+ setMovePara(2.0f);
this.context = context;
this.rotationSrc = rotationSrc;
this.arrowSrc = arrowSrc;
@@ -55,11 +59,11 @@ public DefaultHeader(Context context, int rotationSrc, int arrowSrc) {
@Override
public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
- View view = inflater.inflate(R.layout.default_header, viewGroup, true);
- headerTitle = (TextView) view.findViewById(R.id.default_header_title);
- headerTime = (TextView) view.findViewById(R.id.default_header_time);
- headerArrow = (ImageView) view.findViewById(R.id.default_header_arrow);
- headerProgressbar = (ProgressBar) view.findViewById(R.id.default_header_progressbar);
+ View view = inflater.inflate(R.layout.default_header, viewGroup, false);
+ headerTitle = view.findViewById(R.id.default_header_title);
+ headerTime = view.findViewById(R.id.default_header_time);
+ headerArrow = view.findViewById(R.id.default_header_arrow);
+ headerProgressbar = view.findViewById(R.id.default_header_progressbar);
headerProgressbar.setIndeterminateDrawable(ContextCompat.getDrawable(context, rotationSrc));
headerArrow.setImageResource(arrowSrc);
return view;
@@ -73,10 +77,10 @@ public void onPreDrag(View rootView) {
int m = (int) ((System.currentTimeMillis() - freshTime) / 1000 / 60);
if (m >= 1 && m < 60) {
headerTime.setText(m + "分钟前");
- } else if (m >= 60) {
+ } else if (m >= 60 && m < 60 * 24) {
int h = m / 60;
headerTime.setText(h + "小时前");
- } else if (m > 60 * 24) {
+ } else if (m >= 60 * 24) {
int d = m / (60 * 24);
headerTime.setText(d + "天前");
} else if (m == 0) {
@@ -92,7 +96,7 @@ public void onDropAnim(View rootView, int dy) {
@Override
public void onLimitDes(View rootView, boolean upORdown) {
if (!upORdown) {
- headerTitle.setText("松开刷新数据");
+ headerTitle.setText("松开刷新");
if (headerArrow.getVisibility() == View.VISIBLE)
headerArrow.startAnimation(mRotateUpAnim);
} else {
diff --git a/library/src/main/java/com/liaoinstan/springview/container/InnerFooter.java b/library/src/main/java/com/liaoinstan/springview/container/InnerFooter.java
new file mode 100644
index 0000000..28bacdf
--- /dev/null
+++ b/library/src/main/java/com/liaoinstan/springview/container/InnerFooter.java
@@ -0,0 +1,61 @@
+package com.liaoinstan.springview.container;
+
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.LayoutRes;
+
+/**
+ * 直接在布局设置footer布局,会使用该类生成默认Footer
+ */
+public class InnerFooter extends BaseHeader {
+
+ private final String TAG = getClass().getSimpleName();
+ private int layoutId;
+
+ public InnerFooter(@LayoutRes int layoutId) {
+ this.layoutId = layoutId;
+ }
+
+ @Override
+ public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
+ return inflater.inflate(layoutId, viewGroup, false);
+ }
+
+ @Override
+ public void onPreDrag(View rootView) {
+ Log.d(TAG, "onPreDrag");
+ }
+
+ @Override
+ public void onFinishDrag(View rootView) {
+ Log.d(TAG, "onFinishDrag");
+ }
+
+ @Override
+ public void onDropAnim(View rootView, int dy) {
+ Log.v(TAG, "onDropAnim:" + dy);
+ }
+
+ @Override
+ public void onLimitDes(View rootView, boolean upORdown) {
+ Log.d(TAG, "onLimitDes:" + upORdown);
+ }
+
+ @Override
+ public void onStartAnim() {
+ Log.d(TAG, "onStartAnim");
+ }
+
+ @Override
+ public void onFinishAnim() {
+ Log.d(TAG, "onFinishAnim");
+ }
+
+ @Override
+ public void onResetAnim() {
+ Log.d(TAG, "onResetAnim");
+ }
+}
diff --git a/library/src/main/java/com/liaoinstan/springview/container/InnerHeader.java b/library/src/main/java/com/liaoinstan/springview/container/InnerHeader.java
new file mode 100644
index 0000000..75f50d7
--- /dev/null
+++ b/library/src/main/java/com/liaoinstan/springview/container/InnerHeader.java
@@ -0,0 +1,61 @@
+package com.liaoinstan.springview.container;
+
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.LayoutRes;
+
+/**
+ * 直接在布局设置header布局,会使用该类生成默认Header
+ */
+public class InnerHeader extends BaseHeader {
+
+ private final String TAG = getClass().getSimpleName();
+ private int layoutId;
+
+ public InnerHeader(@LayoutRes int layoutId) {
+ this.layoutId = layoutId;
+ }
+
+ @Override
+ public View getView(LayoutInflater inflater, ViewGroup viewGroup) {
+ return inflater.inflate(layoutId, viewGroup, false);
+ }
+
+ @Override
+ public void onPreDrag(View rootView) {
+ Log.d(TAG, "onPreDrag");
+ }
+
+ @Override
+ public void onFinishDrag(View rootView) {
+ Log.d(TAG, "onFinishDrag");
+ }
+
+ @Override
+ public void onDropAnim(View rootView, int dy) {
+ Log.v(TAG, "onDropAnim:" + dy);
+ }
+
+ @Override
+ public void onLimitDes(View rootView, boolean upORdown) {
+ Log.d(TAG, "onLimitDes:" + upORdown);
+ }
+
+ @Override
+ public void onStartAnim() {
+ Log.d(TAG, "onStartAnim");
+ }
+
+ @Override
+ public void onFinishAnim() {
+ Log.d(TAG, "onFinishAnim");
+ }
+
+ @Override
+ public void onResetAnim() {
+ Log.d(TAG, "onResetAnim");
+ }
+}
diff --git a/library/src/main/java/com/liaoinstan/springview/listener/AppBarStateChangeListener.java b/library/src/main/java/com/liaoinstan/springview/listener/AppBarStateChangeListener.java
index 62abdfa..07d398c 100644
--- a/library/src/main/java/com/liaoinstan/springview/listener/AppBarStateChangeListener.java
+++ b/library/src/main/java/com/liaoinstan/springview/listener/AppBarStateChangeListener.java
@@ -1,6 +1,6 @@
package com.liaoinstan.springview.listener;
-import android.support.design.widget.AppBarLayout;
+import com.google.android.material.appbar.AppBarLayout;
/**
* thanks jianghejie
diff --git a/library/src/main/java/com/liaoinstan/springview/utils/DensityUtil.java b/library/src/main/java/com/liaoinstan/springview/utils/DensityUtil.java
index cf2b315..fde3fe0 100644
--- a/library/src/main/java/com/liaoinstan/springview/utils/DensityUtil.java
+++ b/library/src/main/java/com/liaoinstan/springview/utils/DensityUtil.java
@@ -1,6 +1,5 @@
package com.liaoinstan.springview.utils;
-import android.content.Context;
import android.content.res.Resources;
import android.util.TypedValue;
diff --git a/library/src/main/java/com/liaoinstan/springview/widget/Flag.java b/library/src/main/java/com/liaoinstan/springview/widget/Flag.java
new file mode 100644
index 0000000..f48d250
--- /dev/null
+++ b/library/src/main/java/com/liaoinstan/springview/widget/Flag.java
@@ -0,0 +1,26 @@
+package com.liaoinstan.springview.widget;
+
+class Flag {
+ //Flag:调用header/footer回调: startAnim finishAnim resetAnim
+ int callHeaderAnimFlag = 0; //startAnim:0->1 finishAnim: 1->2 resetAnim: 2->0
+ int callFooterAnimFlag = 0; //startAnim:0->1 finishAnim: 1->2 resetAnim: 2->0
+
+ //Flag:全部回弹,刷新动画回弹,收场动画回弹 调用控制
+ boolean hasCallFull = false;
+ boolean hasCallRefresh = false;
+ boolean hasCallEnding = false;
+
+ //Flag:拖拽回调,用于在动画结束时判断动画类别以调用不同回调接口
+ //0:没有拖拽
+ //1:拖拽超过header阈值header.onResetAnim
+ //2:拖拽超过footer阈值footer.onResetAnim
+ //3:拖拽header但未超过阈值header.onFinishDrag
+ //4:拖拽footer但未超过阈值header.onFinishDrag
+ int callFreshOrLoad = 0;
+ //Flag:记录回弹动画类别,用于在动画结束时判断动画类别以调用不同回调接口
+ //回弹动画类别 0:结束动画1:回弹动画2:退场动画
+ int scrollAnimType;
+
+ //Flag:控制preDrag每个手势链只调用一次
+ boolean hasCallPreDrag = false;
+}
diff --git a/library/src/main/java/com/liaoinstan/springview/widget/ScrollCallbackHelper.java b/library/src/main/java/com/liaoinstan/springview/widget/ScrollCallbackHelper.java
new file mode 100644
index 0000000..b9a2e58
--- /dev/null
+++ b/library/src/main/java/com/liaoinstan/springview/widget/ScrollCallbackHelper.java
@@ -0,0 +1,85 @@
+package com.liaoinstan.springview.widget;
+
+import android.view.View;
+
+import com.liaoinstan.springview.widget.SpringView;
+
+/**
+ * Scroll模式的callback处理类
+ */
+class ScrollCallbackHelper {
+
+ private int lastHeaderDropDy = -1;
+ private int lastFooterDropDy = -1;
+ private Boolean lastFooterInScreen = null;
+ private boolean callStartFooter = true;
+ private boolean callFinishFooter = true;
+ private boolean callStartHeader = true;
+ private boolean callFinishHeader = true;
+
+ /**
+ * header 在移动时回调
+ */
+ void onHeaderMove(SpringView.DragHander headerHander, View header, int offsetTop) {
+ if (lastHeaderDropDy != offsetTop) {
+ headerHander.onDropAnim(header, offsetTop);
+ if (offsetTop == 0 && callFinishHeader) {
+ headerHander.onFinishAnim();
+ callFinishHeader = false;
+ callStartHeader = true;
+ }
+ }
+ lastHeaderDropDy = offsetTop;
+ }
+
+ /**
+ * footer 在移动时回调
+ */
+ void onFooterMove(SpringView.DragHander footerHander, View footer, int offsetBottom) {
+ if (lastFooterDropDy != offsetBottom) {
+ footerHander.onDropAnim(footer, offsetBottom);
+ if (offsetBottom == 0 && callFinishFooter) {
+ footerHander.onFinishAnim();
+ callFinishFooter = false;
+ callStartFooter = true;
+ }
+ }
+ lastFooterDropDy = offsetBottom;
+ }
+
+ /**
+ * 滚动到了顶部时回调
+ */
+ void onScrollTop(SpringView.DragHander headerHander, SpringView.OnFreshListener listener) {
+ if (callStartHeader) {
+ if (headerHander != null) headerHander.onStartAnim();
+ if (listener != null) listener.onRefresh();
+ callStartHeader = false;
+ callFinishHeader = true;
+ }
+ }
+
+ /**
+ * 滚动到了底部时回调
+ */
+ void onScrollBottom(SpringView.DragHander footerHander, SpringView.OnFreshListener listener) {
+ if (callStartFooter) {
+ if (footerHander != null) footerHander.onStartAnim();
+ if (listener != null) listener.onLoadmore();
+ callStartFooter = false;
+ callFinishFooter = true;
+ }
+ }
+
+ /**
+ * 页面内容高度是否足够显示footer的回调
+ */
+ void onScreenFull(SpringView.DragHander footerHander, View footer, boolean upORdown) {
+ if (lastFooterInScreen == null || upORdown != lastFooterInScreen) {
+ footerHander.onLimitDes(footer, upORdown);
+ lastFooterInScreen = upORdown;
+ }
+ }
+
+
+}
diff --git a/library/src/main/java/com/liaoinstan/springview/widget/SpringHelper.java b/library/src/main/java/com/liaoinstan/springview/widget/SpringHelper.java
index 3b99d35..2b0a3bf 100644
--- a/library/src/main/java/com/liaoinstan/springview/widget/SpringHelper.java
+++ b/library/src/main/java/com/liaoinstan/springview/widget/SpringHelper.java
@@ -1,11 +1,17 @@
package com.liaoinstan.springview.widget;
-import android.support.design.widget.AppBarLayout;
-import android.support.design.widget.CollapsingToolbarLayout;
-import android.support.design.widget.CoordinatorLayout;
import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewParent;
+import android.webkit.WebView;
+import android.widget.ListView;
+import android.widget.ScrollView;
+
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+import androidx.core.view.ScrollingView;
+
+import com.google.android.material.appbar.AppBarLayout;
/**
* Created by liaoinstan on 2017/8/15.
@@ -18,7 +24,7 @@ class SpringHelper {
* 在当前布局中查找AppBarLayout并返回,没有找到返回null
* #解决和AppBarLayout自身滚动冲突的问题#
*/
- public static AppBarLayout findAppBarLayout(View v) {
+ static AppBarLayout findAppBarLayout(View v) {
AppBarLayout appBarLayout = null;
ViewParent p = v.getParent();
while (p != null) {
@@ -27,7 +33,7 @@ public static AppBarLayout findAppBarLayout(View v) {
}
p = p.getParent();
}
- if (p instanceof CoordinatorLayout) {
+ if (p != null) {
CoordinatorLayout coordinatorLayout = (CoordinatorLayout) p;
final int childCount = coordinatorLayout.getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
@@ -42,7 +48,7 @@ public static AppBarLayout findAppBarLayout(View v) {
}
//检查appBarLayout是否可以滚动
- public static boolean couldScroll(AppBarLayout appBarLayout) {
+ static boolean couldScroll(AppBarLayout appBarLayout) {
if (appBarLayout == null) return false;
boolean couldScroll = false;
for (int i = 0; i < appBarLayout.getChildCount(); i++) {
@@ -69,4 +75,38 @@ private static boolean couldScrollChild(View v) {
return false;
}
}
+
+ //检查当前View是否是可以滚动的View(限定垂直方向)
+ static boolean isViewCouldScroll(View view) {
+ //如果是以下的view ,则直接返回true
+ if (view instanceof ListView
+ || view instanceof ScrollView
+ || view instanceof ScrollingView
+ || view instanceof WebView) {
+ return true;
+ } else {
+ //否则,检查view是否可以在垂直方向上滚动
+ return view.canScrollVertically(-1) || view.canScrollVertically(1);
+ }
+ }
+
+ //递归查找当前view和所有子view,找到可滚动的view,找不到则返回null
+ static View findViewCouldScroll(View view) {
+ if (isViewCouldScroll(view)) {
+ return view;
+ } else if (view instanceof ViewGroup) {
+ //view group
+ ViewGroup viewGroup = (ViewGroup) view;
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
+ View findView = findViewCouldScroll(viewGroup.getChildAt(i));
+ if (findView != null) {
+ return findView;
+ }
+ }
+ return null;
+ } else {
+ //view
+ return null;
+ }
+ }
}
diff --git a/library/src/main/java/com/liaoinstan/springview/widget/SpringView.java b/library/src/main/java/com/liaoinstan/springview/widget/SpringView.java
index 886a581..1e2f5f0 100644
--- a/library/src/main/java/com/liaoinstan/springview/widget/SpringView.java
+++ b/library/src/main/java/com/liaoinstan/springview/widget/SpringView.java
@@ -2,59 +2,61 @@
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.support.design.widget.AppBarLayout;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.ViewCompat;
+import android.os.Build;
+import android.os.Handler;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.animation.Animation;
-import android.view.animation.TranslateAnimation;
-import android.widget.ListView;
import android.widget.OverScroller;
+import android.widget.ScrollView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.widget.NestedScrollView;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.appbar.AppBarLayout;
import com.liaoinstan.springview.R;
+import com.liaoinstan.springview.container.InnerFooter;
+import com.liaoinstan.springview.container.InnerHeader;
import com.liaoinstan.springview.listener.AppBarStateChangeListener;
/**
* Created by liaoinstan on 2016/3/11.
*/
+@SuppressWarnings("unused ")
public class SpringView extends ViewGroup {
- private Context context;
- private LayoutInflater inflater;
- private OverScroller mScroller;
- private OnFreshListener listener; //监听回调
- private boolean isCallDown = false; //用于判断是否在下拉时到达临界点
- private boolean isCallUp = false; //用于判断是否在上拉时到达临界点
- private boolean isFirst = true; //用于判断是否是拖动动作的第一次move
- private boolean needChange = false; //是否需要改变样式
- private boolean needResetAnim = false; //是否需要弹回的动画
- private boolean isFullEnable = false; //是否超过一屏时才允许上拉,为false则不满一屏也可以上拉,注意样式为isOverlap时,无论如何也不允许在不满一屏时上拉
- private boolean isMoveNow = false; //当前是否正在拖动
- private long lastMoveTime;
- private boolean enableHeader = true; //是否禁止header下拉(默认可用)
- private boolean enableFooter = true; //是否禁止footer上拉(默认可用)
-
- private int MOVE_TIME = 400;
- private int MOVE_TIME_OVER = 200;
-
//是否需要回调接口:TOP 只回调刷新、BOTTOM 只回调加载更多、BOTH 都需要、NONE 都不
- public enum Give {
- BOTH, TOP, BOTTOM, NONE
- }
+ public enum Give {BOTH, TOP, BOTTOM, NONE}
- public enum Type {OVERLAP, FOLLOW}
-
- private Give give = Give.BOTH;
- private Type type = Type.FOLLOW;
- private Type _type;
+ //拖拽类型
+ //OVERLAP 重叠
+ //FOLLOW 跟随
+ //DRAG 拖拽
+ //SCROLL 滚动:目前该模式不支持ListView(建议使用RecyclerView代替,主要原因是由于ListView本身设计存在缺陷无法准确地计算出内容高度)
+ // ScrollView不支持6.0之前(建议使用NestedScrollView代替,主要原因是ScrollView的滚动接口只支持6.0之后的版本)
+ public enum Type {OVERLAP, FOLLOW, DRAG, SCROLL}
+ private Context context;
+ private LayoutInflater inflater;
+ private OverScroller mScroller;
+ private Handler handler = new Handler();
+ private Flag flag = new Flag(); //Spring使用的标志位
+ private OnFreshListener listener; //监听回调
+ private int lastScrollY; //上一次滚动时的Y轴滚动距离
+ private boolean needChangeType = false; //是否需要改变样式
+ private boolean needResetAnim = false; //是否需要弹回的动画
+ private boolean isMoveNow = false; //当前是否正在拖动
+ private boolean enableHeader = true; //是否禁止header下拉(默认可用)
+ private boolean enableFooter = true; //是否禁止footer上拉(默认可用)
+ private boolean isCallFresh = false; //是否手动调用callFresh()进行刷新操作
+
+ private int MOVE_TIME = 600; //统一弹动动画时间
//移动参数:计算手指移动量的时候会用到这个值,值越大,移动量越小,若值为1则手指移动多少就滑动多少px
- private final double MOVE_PARA = 2;
+ private float MOVE_PARA = 2;
//最大拉动距离(px),拉动距离越靠近这个值拉动就越缓慢
private int MAX_HEADER_PULL_HEIGHT = 600;
private int MAX_FOOTER_PULL_HEIGHT = 600;
@@ -63,31 +65,46 @@ public enum Type {OVERLAP, FOLLOW}
private int FOOTER_LIMIT_HEIGHT;
private int HEADER_SPRING_HEIGHT;
private int FOOTER_SPRING_HEIGHT;
- //储存上次的Y坐标
+ private int HEADER_ENDING_HEIGHT;
+ private int FOOTER_ENDING_HEIGHT;
+ //储存手指拉动上次的Y坐标
private float mLastY;
private float mLastX;
- //储存第一次的Y坐标
- private float mfirstY;
//储存手指拉动的总距离
private float dsY;
+ //储存当前的Y轴滚动距离
+ private float scrollY;
+ //储存上一次的Y轴滚动距离
+ private float oldScrollY;
//滑动事件目前是否在本控件的控制中(用于过渡滑动事件:比如正在滚动recyclerView到顶部后自动切换到SpringView处理后续事件进行下拉)
private boolean isInControl = false;
- //存储拉动前的位置
- private Rect mRect = new Rect();
+
+ private Give give = Give.BOTH;
+ private Type type = Type.FOLLOW;
+ private Type _type;
//头尾视图
private View header;
private View footer;
//目标View,即被SpringView包裹的View
+ private View contentLay;
private View contentView;
//头尾布局资源id
- private int headerResoureId;
- private int footerResoureId;
+ private int headerResourceId;
+ private int footerResourceId;
//记录AppBarLayout的当前状态(展开或折叠)
private AppBarStateChangeListener.State appbarState = AppBarStateChangeListener.State.EXPANDED;
private boolean appBarCouldScroll = false;
+ //保存上次ContentView的paddingTop和paddingBottom
+ private int paddingTopContent;
+ private int paddingBottomContent;
+
+ private RecyclerView.OnScrollListener onRecyclerScrollListener;
+ private NestedScrollView.OnScrollChangeListener onNestedScrollChangeListener;
+ private OnScrollChangeListener onScrollChangeListener;
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -104,13 +121,24 @@ public void onStateChanged(AppBarLayout appBarLayout, State state) {
}
}
- public SpringView(Context context, AttributeSet attrs) {
- super(context, attrs);
+ public SpringView(Context context) {
+ this(context, null);
+ }
+
+ public SpringView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SpringView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
this.context = context;
inflater = LayoutInflater.from(context);
-
mScroller = new OverScroller(context);
+ initAttr(attrs);
+ }
+ private void initAttr(AttributeSet attrs) {
+ if (attrs == null) return;
//获取自定义属性
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SpringView);
if (ta.hasValue(R.styleable.SpringView_type)) {
@@ -122,37 +150,184 @@ public SpringView(Context context, AttributeSet attrs) {
give = Give.values()[give_int];
}
if (ta.hasValue(R.styleable.SpringView_header)) {
- headerResoureId = ta.getResourceId(R.styleable.SpringView_header, 0);
+ headerResourceId = ta.getResourceId(R.styleable.SpringView_header, 0);
}
if (ta.hasValue(R.styleable.SpringView_footer)) {
- footerResoureId = ta.getResourceId(R.styleable.SpringView_footer, 0);
+ footerResourceId = ta.getResourceId(R.styleable.SpringView_footer, 0);
}
ta.recycle();
}
@Override
protected void onFinishInflate() {
- contentView = getChildAt(0);
- if (contentView == null) {
+ View content = getChildAt(0);
+ if (content == null) {
return;
}
setPadding(0, 0, 0, 0);
- contentView.setPadding(0, contentView.getPaddingTop(), 0, contentView.getPaddingBottom());
- if (headerResoureId != 0) {
- inflater.inflate(headerResoureId, this, true);
- header = getChildAt(getChildCount() - 1);
+ //找到当前布局下可以滚动的view
+ if (SpringHelper.isViewCouldScroll(content)) {
+ //如果内容是可以滚动的view,则直接设置contentLay,contentView
+ contentLay = content;
+ contentView = content;
+ } else {
+ //如果内容布局不能滚动,则遍历找到可以滚动的view
+ View viewCouldScroll = SpringHelper.findViewCouldScroll(content);
+ if (viewCouldScroll != null) {
+ //找到了则,把它设置为contentView
+ contentView = viewCouldScroll;
+ } else {
+ //没有查找到可滚动布局,则把原布局设置为contentView
+ contentView = content;
+ }
+ contentLay = content;
}
- if (footerResoureId != 0) {
- inflater.inflate(footerResoureId, this, true);
- footer = getChildAt(getChildCount() - 1);
- footer.setVisibility(INVISIBLE);
+
+ //保存padding
+ paddingTopContent = contentView.getPaddingTop();
+ paddingBottomContent = contentView.getPaddingBottom();
+
+ //初始化ScrollListener
+ onRecyclerScrollListener = new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+ paddingScroll();
+ }
+ };
+ onNestedScrollChangeListener = new NestedScrollView.OnScrollChangeListener() {
+ @Override
+ public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
+ paddingScroll();
+ }
+ };
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ onScrollChangeListener = new OnScrollChangeListener() {
+ @Override
+ public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
+ paddingScroll();
+ }
+ };
+ } else {
+ onScrollChangeListener = null;
}
- contentView.bringToFront(); //把内容放在最前端
+ //如果在布局中设置了header/footer则开始初始化
+ if (headerResourceId != 0) {
+ _setHeader(new InnerHeader(headerResourceId));
+ }
+ if (footerResourceId != 0) {
+ _setFooter(new InnerFooter(footerResourceId));
+ }
super.onFinishInflate();
}
+ private void setScrollChangeListener() {
+ if (judgeType(headerHander) == Type.SCROLL || judgeType(footerHander) == Type.SCROLL) {
+ if (contentView instanceof RecyclerView) {
+ //recyclerView在设置这个监听器的时候就会自动调用一次回调函数,ScrollView不会
+ ((RecyclerView) contentView).removeOnScrollListener(onRecyclerScrollListener);
+ ((RecyclerView) contentView).addOnScrollListener(onRecyclerScrollListener);
+ } else if (contentView instanceof NestedScrollView) {
+ ((NestedScrollView) contentView).setOnScrollChangeListener(onNestedScrollChangeListener);
+ } else {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ contentView.setOnScrollChangeListener(onScrollChangeListener);
+ }
+ }
+ }
+ }
+
+ private void paddingScroll() {
+ if (judgeType(headerHander) != Type.SCROLL && judgeType(footerHander) != Type.SCROLL) {
+ return;
+ }
+ //计算内容高度和滚动距离
+ int contentHeight;
+ int contentViewHeight;
+ int scrollY;
+ if (contentView instanceof RecyclerView) {
+ contentHeight = ((RecyclerView) contentView).computeVerticalScrollRange();
+ scrollY = ((RecyclerView) contentView).computeVerticalScrollOffset();
+ contentViewHeight = ((RecyclerView) contentView).computeVerticalScrollExtent();
+ } else if (contentView instanceof NestedScrollView) {
+ //不知道为何,NestedScrollView获取computeVerticalScrollRange的结果会莫名奇妙多出一个paddingTop的高度?
+ contentHeight = ((NestedScrollView) contentView).computeVerticalScrollRange() - contentView.getPaddingTop();
+ scrollY = ((NestedScrollView) contentView).computeVerticalScrollOffset();
+ contentViewHeight = ((NestedScrollView) contentView).computeVerticalScrollExtent() - contentView.getPaddingBottom() - contentView.getPaddingTop();
+ } else if (contentView instanceof ScrollView) {
+ contentHeight = ((ViewGroup) contentView).getChildAt(0).getMeasuredHeight();
+ scrollY = contentView.getScrollY();
+ contentViewHeight = contentView.getMeasuredHeight() - contentView.getPaddingBottom() - contentView.getPaddingTop();
+ } else {
+ //TODO:contentView是其他的情况,做默认处理
+ contentHeight = contentView.getMeasuredHeight();
+ scrollY = contentView.getScrollY();
+ contentViewHeight = contentView.getMeasuredHeight() - contentView.getPaddingBottom() - contentView.getPaddingTop();
+ }
+
+ //可滚动最大高度
+ int maxY = contentHeight - contentViewHeight;
+ if (maxY < 0) maxY = 0;
+ //上下部分滚动距离
+ //int offsetBottom = footer.getMeasuredHeight() - (maxY - scrollY);
+ //int offsetTop = -(header.getMeasuredHeight() - scrollY);
+ int offsetBottom = FOOTER_SPRING_HEIGHT - (maxY - scrollY);
+ int offsetTop = HEADER_SPRING_HEIGHT - scrollY;
+
+ if (judgeType(headerHander) == Type.SCROLL) {
+ if (offsetTop > 0) {
+ header.setVisibility(VISIBLE);
+ header.setTranslationY(offsetTop);
+ scrollCallbackHelper.onHeaderMove(headerHander, header, offsetTop);
+ } else {
+ header.setTranslationY(0);
+ scrollCallbackHelper.onHeaderMove(headerHander, header, 0);
+ }
+ }
+ if (judgeType(footerHander) == Type.SCROLL) {
+ if (offsetBottom > 0) {
+ footer.setVisibility(VISIBLE);
+ footer.setTranslationY(-offsetBottom);
+ scrollCallbackHelper.onFooterMove(footerHander, footer, offsetBottom);
+ } else {
+ footer.setTranslationY(0);
+ scrollCallbackHelper.onFooterMove(footerHander, footer, 0);
+ }
+ }
+
+ //Log.e("paddingScroll", "maxY:" + maxY + " scrollY:" + scrollY + " contentHeight:" + contentHeight + " offsetBottom:" + offsetBottom + " offsetTop:" + offsetTop);
+
+ //准备回调事件
+ if (scrollY == 0) {
+ //滚动到了顶部
+ if (judgeType(headerHander) == Type.SCROLL) {
+ scrollCallbackHelper.onScrollTop(headerHander, listener);
+ }
+ }
+ if (scrollY >= maxY) {
+ //滚动到了底部
+ //如果maxY=0则内部高度不足一屏,此时scrollY也为0,可以认为此时既在顶部同时也在底部
+ if (judgeType(footerHander) == Type.SCROLL) {
+ scrollCallbackHelper.onScrollBottom(footerHander, listener);
+ }
+ }
+ if (maxY <= FOOTER_SPRING_HEIGHT) {
+ //除去footer的高度已经不足一屏了
+ if (judgeType(footerHander) == Type.SCROLL) {
+ scrollCallbackHelper.onScreenFull(footerHander, footer, false);
+ }
+ } else {
+ //超过一屏
+ if (judgeType(footerHander) == Type.SCROLL) {
+ scrollCallbackHelper.onScreenFull(footerHander, footer, true);
+ }
+ }
+ }
+
+ //回调器:SCROLL
+ private ScrollCallbackHelper scrollCallbackHelper = new ScrollCallbackHelper();
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (getChildCount() > 0) {
@@ -172,6 +347,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//设置下拉弹动高度,只有在>0时才生效,否则默认和临界高度一致
int sh = headerHander.getDragSpringHeight(header);
HEADER_SPRING_HEIGHT = sh > 0 ? sh : HEADER_LIMIT_HEIGHT;
+ HEADER_ENDING_HEIGHT = headerHander.getEndingAnimHeight(header);
} else {
//不是动态设置的头部,设置默认值
if (header != null) HEADER_LIMIT_HEIGHT = header.getMeasuredHeight();
@@ -185,32 +361,66 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
FOOTER_LIMIT_HEIGHT = h > 0 ? h : footer.getMeasuredHeight();
int sh = footerHander.getDragSpringHeight(footer);
FOOTER_SPRING_HEIGHT = sh > 0 ? sh : FOOTER_LIMIT_HEIGHT;
+ FOOTER_ENDING_HEIGHT = footerHander.getEndingAnimHeight(footer);
} else {
if (footer != null) FOOTER_LIMIT_HEIGHT = footer.getMeasuredHeight();
FOOTER_SPRING_HEIGHT = FOOTER_LIMIT_HEIGHT;
}
+ boolean needHeaderScroll = headerHander != null && judgeType(headerHander) == Type.SCROLL;
+ boolean needFooterScroll = footerHander != null && judgeType(footerHander) == Type.SCROLL;
+ if (needHeaderScroll || needFooterScroll) {
+ //设置padding
+ int paddingTopAdd = needHeaderScroll ? HEADER_SPRING_HEIGHT : 0;
+ int paddingBottomAdd = needFooterScroll ? FOOTER_SPRING_HEIGHT : 0;
+ contentView.setPadding(contentView.getPaddingLeft(), paddingTopContent + paddingTopAdd, contentView.getPaddingRight(), paddingBottomContent + paddingBottomAdd);
+ if (contentView instanceof ViewGroup) {
+ ((ViewGroup) contentView).setClipToPadding(false);
+ }
+ } else {
+ contentView.setPadding(contentView.getPaddingLeft(), paddingTopContent, contentView.getPaddingRight(), paddingBottomContent);
+ ((ViewGroup) contentView).setClipToPadding(false);
+ }
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
- if (contentView != null) {
- if (type == Type.OVERLAP) {
- if (header != null) {
- header.layout(0, 0, getWidth(), header.getMeasuredHeight());
- }
- if (footer != null) {
- footer.layout(0, getHeight() - footer.getMeasuredHeight(), getWidth(), getHeight());
- }
- } else if (type == Type.FOLLOW) {
- if (header != null) {
- header.layout(0, -header.getMeasuredHeight(), getWidth(), 0);
+ if (contentLay != null) {
+ if (header != null) {
+ header.layout(0, -header.getMeasuredHeight(), getWidth(), 0);
+ }
+ if (footer != null) {
+ footer.layout(0, getHeight(), getWidth(), getHeight() + footer.getMeasuredHeight());
+ }
+ contentLay.layout(0, 0, contentLay.getMeasuredWidth(), contentLay.getMeasuredHeight());
+
+ //设置层级关系
+ if (judgeType(headerHander) == Type.OVERLAP) {
+ if (judgeType(footerHander) == Type.OVERLAP) {
+ //content:3 header: x footer x
+ contentLay.bringToFront();
+ } else {
+ //content:3 header: 1 footer 2
+ if (header != null) header.bringToFront();
+ if (footer != null) footer.bringToFront();
+ contentLay.bringToFront();
}
- if (footer != null) {
- footer.layout(0, getHeight(), getWidth(), getHeight() + footer.getMeasuredHeight());
+ } else {
+ if (judgeType(footerHander) == Type.OVERLAP) {
+ //content:2 header: 3 footer 1
+ if (footer != null) footer.bringToFront();
+ contentLay.bringToFront();
+ if (header != null) header.bringToFront();
+ } else {
+ //content:1 header: x footer x
+ if (header != null) header.bringToFront();
+ if (footer != null) footer.bringToFront();
}
}
- contentView.layout(0, 0, contentView.getMeasuredWidth(), contentView.getMeasuredHeight());
+ if (judgeType(headerHander) == Type.SCROLL || judgeType(footerHander) == Type.SCROLL) {
+ //scroll模式在layout好之后就要刷新一下header/footer位置,因为scroll模式的header/footer默认就是展示的
+ paddingScroll();
+ }
}
}
@@ -220,18 +430,23 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) {
//记录当前滚动事件是否需要SpringView进行处理,如果需要则SpringView拦截事件(比如已经滚动到顶部了还继续下拉)
private boolean isNeedMyMove;
+ private int downAction;
+ private boolean isContentDown;
+
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
dealMulTouchEvent(event);
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN: {
- hasCallFull = false;
- hasCallRefresh = false;
- mfirstY = event.getY();
- boolean isTop = isChildScrollToTop();
- boolean isBottom = isChildScrollToBottom();
- if (isTop || isBottom) isNeedMyMove = false;
+ flag.hasCallFull = false;
+ flag.hasCallRefresh = false;
+ flag.hasCallEnding = false;
+ //手指拉动第一次的Y坐标
+ float firstY = event.getY();
+ isNeedMyMove = false;
+ //判断当前down事件是否发生在content区域(当前正在拖拽header && 点击区域在ScrollY内)
+ isContentDown = !(isTop() && firstY < -getScrollY());
break;
}
case MotionEvent.ACTION_MOVE:
@@ -253,11 +468,14 @@ public boolean dispatchTouchEvent(MotionEvent event) {
break;
}
}
- /////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////appBar end//////////////////////////////////
dsY += dy;
isMoveNow = true;
- isNeedMyMove = isNeedMyMove();
- if (isNeedMyMove && !isInControl) {
+ isNeedMyMove = isNeedMyMove(event);
+ //如果内部控件既在顶部又在最底部,那说明该控件内容不足一屏,还不足以滚动
+ boolean isNotFull = isTop && isBottom;
+ //如果down事件不是在content区域发生的(比如说header内),则不对这个事件进行跟踪转发
+ if (isNeedMyMove && !isInControl && !isNotFull && isContentDown) {
//把内部控件的事件转发给本控件处理
isInControl = true;
event.setAction(MotionEvent.ACTION_CANCEL);
@@ -268,9 +486,6 @@ public boolean dispatchTouchEvent(MotionEvent event) {
}
break;
case MotionEvent.ACTION_UP:
- isMoveNow = false;
- lastMoveTime = System.currentTimeMillis();
- break;
case MotionEvent.ACTION_CANCEL:
isMoveNow = false;
break;
@@ -285,40 +500,33 @@ public boolean onInterceptTouchEvent(MotionEvent event) {
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (contentView == null) {
+ if (contentLay == null) {
return false;
}
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
- isFirst = true;
- //if (!mScroller.isFinished()) mScroller.abortAnimation();//不需要处理
+ performClick();
break;
case MotionEvent.ACTION_MOVE:
- //getParent().requestDisallowInterceptTouchEvent(true);
if (isNeedMyMove) {
- needResetAnim = false; //按下的时候关闭回弹
- //执行位移操作
- doMove();
+ //按下的时候关闭回弹
+ needResetAnim = false;
//下拉的时候显示header并隐藏footer,上拉的时候相反
if (isTop()) {
- if (header != null && header.getVisibility() != View.VISIBLE)
- header.setVisibility(View.VISIBLE);
- if (footer != null && footer.getVisibility() != View.INVISIBLE)
- footer.setVisibility(View.INVISIBLE);
+ boolean showFooter = judgeType(footerHander) == Type.SCROLL;
+ showHeaderAndFooter(true, showFooter);
} else if (isBottom()) {
- if (header != null && header.getVisibility() != View.INVISIBLE)
- header.setVisibility(View.INVISIBLE);
- if (footer != null && footer.getVisibility() != View.VISIBLE)
- footer.setVisibility(View.VISIBLE);
+ boolean showHeader = judgeType(headerHander) == Type.SCROLL;
+ showHeaderAndFooter(showHeader, true);
}
- //回调onDropAnim接口
- callOnDropAnim();
//回调callOnPreDrag接口
- callOnPreDrag();
+ callbackOnPreDrag();
+ //执行位移操作
+ doMove();
//回调onLimitDes接口
- callOnLimitDes();
- isFirst = false;
+ callbackOnLimitDes();
+ isInControl = true;
} else {
//手指在产生移动的时候(dy!=0)才重置位置
if (dy != 0 && isFlow()) {
@@ -330,18 +538,16 @@ public boolean onTouchEvent(MotionEvent event) {
}
}
break;
+ case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
needResetAnim = true; //松开的时候打开回弹
- isFirst = true;
- _firstDrag = true;
+ flag.hasCallPreDrag = false;
restSmartPosition();
dsY = 0;
dy = 0;
break;
- case MotionEvent.ACTION_CANCEL:
- break;
}
- return true;
+ return false;
}
/**
@@ -351,21 +557,22 @@ public boolean onTouchEvent(MotionEvent event) {
private int mActivePointerId = MotionEvent.INVALID_POINTER_ID;
public void dealMulTouchEvent(MotionEvent ev) {
- final int action = MotionEventCompat.getActionMasked(ev);
+ final int action = ev.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN: {
- final int pointerIndex = MotionEventCompat.getActionIndex(ev);
- final float x = MotionEventCompat.getX(ev, pointerIndex);
- final float y = MotionEventCompat.getY(ev, pointerIndex);
+ downAction = action;
+ final int pointerIndex = ev.getActionIndex();
+ final float x = ev.getX(pointerIndex);
+ final float y = ev.getY(pointerIndex);
mLastX = x;
mLastY = y;
- mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
+ mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
- final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
- final float x = MotionEventCompat.getX(ev, pointerIndex);
- final float y = MotionEventCompat.getY(ev, pointerIndex);
+ final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ final float x = ev.getX(pointerIndex);
+ final float y = ev.getY(pointerIndex);
dx = x - mLastX;
dy = y - mLastY;
mLastY = y;
@@ -377,406 +584,391 @@ public void dealMulTouchEvent(MotionEvent ev) {
mActivePointerId = MotionEvent.INVALID_POINTER_ID;
break;
case MotionEvent.ACTION_POINTER_DOWN: {
- final int pointerIndex = MotionEventCompat.getActionIndex(ev);
- final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
+ downAction = action;
+ final int pointerIndex = ev.getActionIndex();
+ final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId != mActivePointerId) {
- mLastX = MotionEventCompat.getX(ev, pointerIndex);
- mLastY = MotionEventCompat.getY(ev, pointerIndex);
- mActivePointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
+ mLastX = ev.getX(pointerIndex);
+ mLastY = ev.getY(pointerIndex);
+ mActivePointerId = ev.getPointerId(pointerIndex);
}
break;
}
case MotionEvent.ACTION_POINTER_UP: {
- final int pointerIndex = MotionEventCompat.getActionIndex(ev);
- final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
+ final int pointerIndex = ev.getActionIndex();
+ final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
- mLastX = MotionEventCompat.getX(ev, newPointerIndex);
- mLastY = MotionEventCompat.getY(ev, newPointerIndex);
- mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
+ mLastX = ev.getX(newPointerIndex);
+ mLastY = ev.getY(newPointerIndex);
+ mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
}
+ @Override
+ public boolean performClick() {
+ return super.performClick();
+ }
+
//执行位移操作
private void doMove() {
- if (type == Type.OVERLAP) {
- //记录移动前的位置
- if (mRect.isEmpty()) {
- mRect.set(contentView.getLeft(), contentView.getTop(), contentView.getRight(), contentView.getBottom());
+ if (!mScroller.isFinished()) mScroller.forceFinished(true);
+ //根据下拉高度计算位移距离,(越拉越慢)
+ float movePara;
+ if (dy > 0 && headerHander != null && headerHander.getMovePara() > 0) {
+ movePara = headerHander.getMovePara();
+ } else if (dy < 0 && footerHander != null && footerHander.getMovePara() > 0) {
+ movePara = footerHander.getMovePara();
+ } else {
+ movePara = MOVE_PARA;
+ }
+ int movedy;
+ if (dy > 0) {
+ movedy = (int) (((MAX_HEADER_PULL_HEIGHT + getScrollY()) / (float) MAX_HEADER_PULL_HEIGHT) * dy / movePara);
+ } else {
+ movedy = (int) (((MAX_FOOTER_PULL_HEIGHT - getScrollY()) / (float) MAX_FOOTER_PULL_HEIGHT) * dy / movePara);
+ }
+ scrollBy(0, -movedy);
+
+ callOnScrollAndDrag();
+ }
+
+ //滚动回调
+ @Override
+ public void computeScroll() {
+ if (mScroller.computeScrollOffset()) {
+ scrollTo(0, mScroller.getCurrY());
+ lastScrollY = getScrollY();
+ callOnScrollAndDrag();
+ invalidate();
+ }
+ //在滚动动画完全结束后回调接口
+ //滚动回调过程中mScroller.isFinished会多次返回true,导致判断条件被多次进入,设置标志位保证只调用一次
+ if (!isMoveNow && mScroller.isFinished()) {
+ if (flag.scrollAnimType == 0) {
+ if (!flag.hasCallFull) {
+ flag.hasCallFull = true;
+ callOnAfterFullAnim();
+ }
+ } else if (flag.scrollAnimType == 1) {
+ if (!flag.hasCallRefresh) {
+ flag.hasCallRefresh = true;
+ callOnAfterRefreshAnim();
+ }
+ } else if (flag.scrollAnimType == 2) {
+ if (!flag.hasCallEnding) {
+ flag.hasCallEnding = true;
+ callOnAfterEndingAnim();
+ }
}
- //根据下拉高度计算位移距离,(越拉越慢)
- int movedy;
- if (dy > 0) {
- movedy = (int) ((float) ((MAX_HEADER_PULL_HEIGHT - contentView.getTop()) / (float) MAX_HEADER_PULL_HEIGHT) * dy / MOVE_PARA);
- } else {
- movedy = (int) ((float) ((MAX_FOOTER_PULL_HEIGHT - (getHeight() - contentView.getBottom())) / (float) MAX_FOOTER_PULL_HEIGHT) * dy / MOVE_PARA);
+ }
+ }
+
+ //springView滚动或者拖拽的时候会执行该方法
+ private void callOnScrollAndDrag() {
+ //回调onDropAnim接口
+ callbackOnDropAnim();
+ //回调onFinishAnim接口
+ callbackOnFinishAnim();
+ //获取type,优先使用header/footer的返回值
+ boolean top = isTop();
+ boolean bottom = isBottom();
+ Type typeIn;
+ if (top) {
+ typeIn = judgeType(headerHander);
+ } else if (bottom) {
+ typeIn = judgeType(footerHander);
+ } else {
+ return;
+ }
+ //只在需要变更布局方式的时候才发起requestLayout()
+ if (isChildScrollToBottom() && oldScrollY <= 0 && scrollY > 0) {
+ //从下拉状态回到上拉时
+ requestLayout();
+ } else if (isChildScrollToTop() && oldScrollY >= 0 && scrollY < 0) {
+ //从上拉状态回到下拉时
+ requestLayout();
+ }
+ //更新位置
+ if (typeIn == Type.OVERLAP) {
+ if (header != null) header.setTranslationY(header.getHeight() + getScrollY());
+ if (footer != null) footer.setTranslationY(-footer.getHeight() + getScrollY());
+ //重置状态
+ if (contentLay != null) contentLay.setTranslationY(0);
+ } else if (typeIn == Type.DRAG) {
+ if (contentLay != null) contentLay.setTranslationY(getScrollY());
+ //重置状态
+ if (header != null) header.setTranslationY(0);
+ if (footer != null) footer.setTranslationY(0);
+ } else if (typeIn == Type.FOLLOW || typeIn == Type.SCROLL) {
+ //重置状态
+ if (contentLay != null) contentLay.setTranslationY(0);
+ if (header != null) header.setTranslationY(0);
+ if (footer != null) footer.setTranslationY(0);
+ }
+ //只在onScrollChanged回调会有闪烁
+ paddingScroll();
+ }
+
+ @Override
+ protected void onScrollChanged(int x, int y, int oldX, int oldY) {
+ super.onScrollChanged(x, y, oldX, oldY);
+ scrollY = y;
+ oldScrollY = oldY;
+ //重置位置
+ if (y == 0) {
+ if (contentLay != null) contentLay.setTranslationY(0);
+ if (header != null) header.setTranslationY(0);
+ if (footer != null) footer.setTranslationY(0);
+ }
+ paddingScroll();
+ }
+
+ //在完成全部动作后会执行此方法
+ private void callOnAfterFullAnim() {
+ //回调onResetAnim()
+ if (flag.callFreshOrLoad == 1 || flag.callFreshOrLoad == 3) {
+ if (headerHander != null) {
+ if (flag.callHeaderAnimFlag == 2) {
+ headerHander.onResetAnim();
+ flag.callHeaderAnimFlag = 0;
+ }
}
- int top = contentView.getTop() + movedy;
- contentView.layout(contentView.getLeft(), top, contentView.getRight(), top + contentView.getMeasuredHeight());
- } else if (type == Type.FOLLOW) {
- //根据下拉高度计算位移距离,(越拉越慢)
- int movedx;
- if (dy > 0) {
- movedx = (int) ((float) ((MAX_HEADER_PULL_HEIGHT + getScrollY()) / (float) MAX_HEADER_PULL_HEIGHT) * dy / MOVE_PARA);
- } else {
- movedx = (int) ((float) ((MAX_FOOTER_PULL_HEIGHT - getScrollY()) / (float) MAX_FOOTER_PULL_HEIGHT) * dy / MOVE_PARA);
+ } else if (flag.callFreshOrLoad == 2 || flag.callFreshOrLoad == 4) {
+ if (footerHander != null) {
+ if (flag.callFooterAnimFlag == 2) {
+ footerHander.onResetAnim();
+ flag.callFooterAnimFlag = 0;
+ }
+ }
+ }
+ //回调onFinishDrag()
+ if (flag.callFreshOrLoad == 1) {
+ if (headerHander != null) headerHander.onFinishDrag(header);
+ //如果在回弹动作中没有进行回调(说明是设置为不可弹动状态),那么在这里进行(但是callFresh例外)
+ if (give == Give.BOTTOM || give == Give.NONE && !isCallFresh) {
+ listener.onRefresh();
+ }
+ isCallFresh = false;
+ } else if (flag.callFreshOrLoad == 2) {
+ if (footerHander != null) footerHander.onFinishDrag(footer);
+ if (give == Give.TOP || give == Give.NONE) {
+ listener.onLoadmore();
}
- scrollBy(0, (int) (-movedx));
+ } else if (flag.callFreshOrLoad == 3) {
+ if (headerHander != null) headerHander.onFinishDrag(header);
+ } else if (flag.callFreshOrLoad == 4) {
+ if (footerHander != null) footerHander.onFinishDrag(footer);
+ }
+ flag.callFreshOrLoad = 0;
+ //动画完成后检查是否需要切换header/footer,是则切换
+ if (_headerHander != null) {
+ _setHeader(_headerHander);
+ _headerHander = null;
+ }
+ if (_footerHander != null) {
+ _setFooter(_footerHander);
+ _footerHander = null;
+ }
+ //动画完成后检查是否需要切换type,是则切换
+ if (needChangeType) {
+ _setType(_type);
}
}
- //回调自定义header/footer OnDropAnim接口
- private void callOnDropAnim() {
- if (type == Type.OVERLAP) {
- if (contentView.getTop() > 0)
- if (headerHander != null) headerHander.onDropAnim(header, contentView.getTop());
- if (contentView.getTop() < 0)
- if (footerHander != null) footerHander.onDropAnim(footer, contentView.getTop());
- } else if (type == Type.FOLLOW) {
- if (getScrollY() < 0)
- if (headerHander != null) headerHander.onDropAnim(header, -getScrollY());
- if (getScrollY() > 0)
- if (footerHander != null) footerHander.onDropAnim(footer, -getScrollY());
+ //在回弹到刷新位置的时候会执行此方法
+
+ /**
+ * 只有设置可弹动或者手动调用callFresh才会执行到这个回调,不弹动状态直接执行{@link #callOnAfterFullAnim()}
+ */
+ private void callOnAfterRefreshAnim() {
+ if (isTop()) {
+ listener.onRefresh();
+ } else if (isBottom()) {
+ listener.onLoadmore();
}
}
- private boolean _firstDrag = true;
+ //在开始退场动画时会执行此方法
+ private void callOnAfterEndingAnim() {
+ final DragHander dragHander = isTop() ? headerHander : footerHander;
+ if (dragHander == null) return;
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ dragHander.onEndingAnimEnd();
+ resetPosition();
+ }
+ }, dragHander.getEndingAnimTime());
+ }
+
+ //回调自定义header/footer OnDropAnim接口
+ private void callbackOnDropAnim() {
+ if (getScrollY() < 0)
+ if (headerHander != null) headerHander.onDropAnim(header, -getScrollY());
+ if (getScrollY() > 0)
+ if (footerHander != null) footerHander.onDropAnim(footer, -getScrollY());
+ }
+
+ //回调自定义header/footer onFinishAnim接口
+ private void callbackOnFinishAnim() {
+ if (getScrollY() < 0 && getScrollY() > -10 && headerHander != null)
+ if (flag.callHeaderAnimFlag == 1) {
+ headerHander.onFinishAnim();
+ flag.callHeaderAnimFlag = 2;
+ }
+ if (getScrollY() > 0 && getScrollY() < 10 && footerHander != null)
+ if (flag.callFooterAnimFlag == 1) {
+ footerHander.onFinishAnim();
+ flag.callFooterAnimFlag = 2;
+ }
+ }
//回调自定义header/footer OnPreDrag接口
- private void callOnPreDrag() {
- if (_firstDrag) {
+ private void callbackOnPreDrag() {
+ if (!flag.hasCallPreDrag) {
if (isTop()) {
if (headerHander != null) headerHander.onPreDrag(header);
- _firstDrag = false;
+ flag.hasCallPreDrag = true;
} else if (isBottom()) {
if (footerHander != null) footerHander.onPreDrag(footer);
- _firstDrag = false;
+ flag.hasCallPreDrag = true;
}
}
}
//回调自定义header/footer OnLimitDes接口
- private void callOnLimitDes() {
- boolean topORbottom = false;
- if (type == Type.OVERLAP) {
- topORbottom = contentView.getTop() >= 0 && isChildScrollToTop();
- } else if (type == Type.FOLLOW) {
- topORbottom = getScrollY() <= 0 && isChildScrollToTop();
- }
- if (isFirst) {
- if (topORbottom) {
- isCallUp = true;
- isCallDown = false;
- } else {
- isCallUp = false;
- isCallDown = true;
- }
- }
- if (dy == 0) return;
- boolean upORdown = dy < 0;
- if (topORbottom) {
- if (!upORdown) {
- if ((isTopOverFarm()) && !isCallDown) {
- isCallDown = true;
- if (headerHander != null) headerHander.onLimitDes(header, upORdown);
- isCallUp = false;
- }
- } else {
- if (!isTopOverFarm() && !isCallUp) {
- isCallUp = true;
- if (headerHander != null) headerHander.onLimitDes(header, upORdown);
- isCallDown = false;
- }
+ private void callbackOnLimitDes() {
+ int scrollY = getScrollY();
+ if (scrollY < 0) {
+ //下拉
+ if (Math.abs(scrollY) >= HEADER_LIMIT_HEIGHT && Math.abs(lastScrollY) < HEADER_LIMIT_HEIGHT) {
+ if (headerHander != null) headerHander.onLimitDes(header, false);
+ } else if (Math.abs(scrollY) <= HEADER_LIMIT_HEIGHT && Math.abs(lastScrollY) > HEADER_LIMIT_HEIGHT) {
+ if (headerHander != null) headerHander.onLimitDes(header, true);
}
} else {
- if (upORdown) {
- if (isBottomOverFarm() && !isCallUp) {
- isCallUp = true;
- if (footerHander != null) footerHander.onLimitDes(footer, upORdown);
- isCallDown = false;
- }
- } else {
- if (!isBottomOverFarm() && !isCallDown) {
- isCallDown = true;
- if (footerHander != null) footerHander.onLimitDes(footer, upORdown);
- isCallUp = false;
- }
+ //上拉
+ if (Math.abs(scrollY) >= FOOTER_LIMIT_HEIGHT && Math.abs(lastScrollY) < FOOTER_LIMIT_HEIGHT) {
+ if (footerHander != null) footerHander.onLimitDes(header, true);
+ } else if (Math.abs(scrollY) <= FOOTER_LIMIT_HEIGHT && Math.abs(lastScrollY) > FOOTER_LIMIT_HEIGHT) {
+ if (footerHander != null) footerHander.onLimitDes(header, false);
}
}
+ lastScrollY = scrollY;
}
- @Override
- public void computeScroll() {
- if (mScroller.computeScrollOffset()) {
- scrollTo(0, mScroller.getCurrY());
- invalidate();
- }
- //在滚动动画完全结束后回调接口
- //滚动回调过程中mScroller.isFinished会多次返回true,导致判断条件被多次进入,设置标志位保证只调用一次
- if (!isMoveNow && type == Type.FOLLOW && mScroller.isFinished()) {
- if (isFullAnim) {
- if (!hasCallFull) {
- hasCallFull = true;
- callOnAfterFullAnim();
+ private void callbackOnStartAnim() {
+ if (isTop()) { //下拉
+ flag.callFreshOrLoad = 1;
+ if (headerHander != null) {
+ if (flag.callHeaderAnimFlag == 0 || flag.callHeaderAnimFlag == 2) {
+ headerHander.onStartAnim();
+ flag.callHeaderAnimFlag = 1;
}
- } else {
- if (!hasCallRefresh) {
- hasCallRefresh = true;
- callOnAfterRefreshAnim();
+ }
+ } else if (isBottom()) {
+ flag.callFreshOrLoad = 2;
+ if (footerHander != null) {
+ if (flag.callFooterAnimFlag == 0 || flag.callFooterAnimFlag == 2) {
+ footerHander.onStartAnim();
+ flag.callFooterAnimFlag = 1;
}
}
}
}
- private int callFreshORload = 0;
- private boolean isFullAnim;
- private boolean hasCallFull = false;
- private boolean hasCallRefresh = false;
-
/**
* 判断是否需要由该控件来控制滑动事件
*/
- private boolean isNeedMyMove() {
- if (contentView == null) {
+ @SuppressWarnings("all")
+ private boolean isNeedMyMove(MotionEvent event) {
+ if (contentLay == null) {
return false;
}
- if (Math.abs(dy) < Math.abs(dx)) {
+ //如果手指的拖拽范围在header内则不拦截(微信header等,需要把事件传递给header内部自行处理,SpringView不干涉)
+ if (downAction == MotionEvent.ACTION_DOWN) {
+ if (getScrollY() < 0 && event.getY() < -getScrollY()) {
+ return false;
+ }
+ }
+ //横向拖拽距离大于竖直距离则不拦截(侧滑删除)
+ if (Math.abs(dy) <= Math.abs(dx)) {
return false;
}
- boolean isTop = isChildScrollToTop();
- boolean isBottom = isChildScrollToBottom();
+ boolean isNowTop = isChildScrollToTop();
+ boolean isNowBottom = isChildScrollToBottom();
//用户禁止了下拉操作,则不控制
- if (!enableHeader && isTop && dy > 0) {
+ if (!enableHeader && isNowTop && dy > 0) {
return false;
}
//用户禁止了上拉操作,则不控制
- if (!enableFooter && isBottom && dy < 0) {
+ if (!enableFooter && isNowBottom && dy < 0) {
return false;
}
- if (type == Type.OVERLAP) {
- if (header != null) {
- if (isTop && dy > 0 || contentView.getTop() > 0 + 20) {
- return true;
- }
- }
- if (footer != null) {
- if (isBottom && dy < 0 || contentView.getBottom() < mRect.bottom - 20) {
- return true;
- }
- }
- } else if (type == Type.FOLLOW) {
- if (header != null) {
- //其中的20是一个防止触摸误差的偏移量
- if (isTop && dy > 0 || getScrollY() < 0 - 20) {
- return true;
- }
- }
- if (footer != null) {
- if (isBottom && dy < 0 || getScrollY() > 0 + 20) {
- return true;
+ if (header != null) {
+ //其中的20是一个防止触摸误差的偏移量
+ if (isNowTop) {
+ if (judgeType(headerHander) != Type.SCROLL) {
+ if (dy > 0 || getScrollY() < 0 - 20) {
+ return true;
+ }
}
}
}
- return false;
- }
-
- private void callOnAfterFullAnim() {
- if (callFreshORload != 0) {
- callOnFinishAnim();
- }
- if (needChangeHeader) {
- needChangeHeader = false;
- setHeaderIn(_headerHander);
- }
- if (needChangeFooter) {
- needChangeFooter = false;
- setFooterIn(_footerHander);
- }
- //动画完成后检查是否需要切换type,是则切换
- if (needChange) {
- changeType(_type);
- }
- }
-
- private void callOnAfterRefreshAnim() {
- if (type == Type.FOLLOW) {
- if (isTop()) {
- listener.onRefresh();
- } else if (isBottom()) {
- listener.onLoadmore();
- }
- } else if (type == Type.OVERLAP) {
- if (!isMoveNow) {
- long nowtime = System.currentTimeMillis();
- if (nowtime - lastMoveTime >= MOVE_TIME_OVER) {
- if (callFreshORload == 1)
- listener.onRefresh();
- if (callFreshORload == 2)
- listener.onLoadmore();
+ if (footer != null) {
+ if (isNowBottom) {
+ if (judgeType(footerHander) != Type.SCROLL) {
+ if (dy < 0 || getScrollY() > 0 + 20) {
+ return true;
+ }
}
}
}
+ return false;
}
/**
* 重置控件位置到初始状态
*/
private void resetPosition() {
- isFullAnim = true;
+ flag.scrollAnimType = 0;
isInControl = false; //重置位置的时候,滑动事件已经不在控件的控制中了
- if (type == Type.OVERLAP) {
- if (mRect.bottom == 0 || mRect.right == 0) return;
- //根据下拉高度计算弹回时间,时间最小100,最大400
- int time = 0;
- if (contentView.getHeight() > 0) {
- time = Math.abs(400 * contentView.getTop() / contentView.getHeight());
- }
- if (time < 100) time = 100;
-
- Animation animation = new TranslateAnimation(0, 0, contentView.getTop(), mRect.top);
- animation.setDuration(time);
- animation.setFillAfter(true);
- animation.setAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- callOnAfterFullAnim();
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- }
- });
- contentView.startAnimation(animation);
- contentView.layout(mRect.left, mRect.top, mRect.right, mRect.bottom);
- } else if (type == Type.FOLLOW) {
- mScroller.startScroll(0, getScrollY(), 0, -getScrollY(), MOVE_TIME);
- invalidate();
- }
- //mRect.setEmpty();
- }
-
- private void callOnFinishAnim() {
- if (callFreshORload != 0) {
- if (callFreshORload == 1) {
- if (headerHander != null) headerHander.onFinishAnim();
- if (give == Give.BOTTOM || give == Give.NONE) {
- listener.onRefresh();
- }
- } else if (callFreshORload == 2) {
- if (footerHander != null) footerHander.onFinishAnim();
- if (give == Give.TOP || give == Give.NONE) {
- listener.onLoadmore();
- }
- }
- callFreshORload = 0;
- }
+ mScroller.startScroll(0, getScrollY(), 0, -getScrollY(), MOVE_TIME);
+ invalidate();
}
/**
* 重置控件位置到刷新状态(或加载状态)
*/
private void resetRefreshPosition() {
- isFullAnim = false;
+ flag.scrollAnimType = 1;
isInControl = false; //重置位置的时候,滑动事件已经不在控件的控制中了
- if (type == Type.OVERLAP) {
- if (mRect.bottom == 0 || mRect.right == 0) return;
- if (contentView.getTop() > mRect.top) { //下拉
- Animation animation = new TranslateAnimation(0, 0, contentView.getTop() - HEADER_SPRING_HEIGHT, mRect.top);
- animation.setDuration(MOVE_TIME_OVER);
- animation.setFillAfter(true);
- animation.setAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- callOnAfterRefreshAnim();
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- }
- });
- contentView.startAnimation(animation);
- contentView.layout(mRect.left, mRect.top + HEADER_SPRING_HEIGHT, mRect.right, mRect.bottom + HEADER_SPRING_HEIGHT);
- } else { //上拉
- Animation animation = new TranslateAnimation(0, 0, contentView.getTop() + FOOTER_SPRING_HEIGHT, mRect.top);
- animation.setDuration(MOVE_TIME_OVER);
- animation.setFillAfter(true);
- animation.setAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- callOnAfterRefreshAnim();
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- }
- });
- contentView.startAnimation(animation);
- contentView.layout(mRect.left, mRect.top - FOOTER_SPRING_HEIGHT, mRect.right, mRect.bottom - FOOTER_SPRING_HEIGHT);
- }
- } else if (type == Type.FOLLOW) {
- if (getScrollY() < 0) { //下拉
- mScroller.startScroll(0, getScrollY(), 0, -getScrollY() - HEADER_SPRING_HEIGHT, MOVE_TIME);
- invalidate();
- } else { //上拉
- mScroller.startScroll(0, getScrollY(), 0, -getScrollY() + FOOTER_SPRING_HEIGHT, MOVE_TIME);
- invalidate();
- }
+ if (getScrollY() < 0) { //下拉
+ mScroller.startScroll(0, getScrollY(), 0, -getScrollY() - HEADER_SPRING_HEIGHT, MOVE_TIME);
+ invalidate();
+ } else { //上拉
+ mScroller.startScroll(0, getScrollY(), 0, -getScrollY() + FOOTER_SPRING_HEIGHT, MOVE_TIME);
+ invalidate();
}
}
/**
- * {@link #callFresh()}的执行方法,不要暴露在外部
+ * 重置到收场动画状态
*/
- private void _callFresh() {
- header.setVisibility(VISIBLE);
- if (type == Type.OVERLAP) {
- if (mRect.isEmpty()) {
- mRect.set(contentView.getLeft(), contentView.getTop(), contentView.getRight(), contentView.getBottom());
- }
- Animation animation = new TranslateAnimation(0, 0, contentView.getTop() - HEADER_SPRING_HEIGHT, mRect.top);
- animation.setDuration(MOVE_TIME_OVER);
- animation.setFillAfter(true);
- animation.setAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {
- if (headerHander != null) headerHander.onStartAnim();
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- callFreshORload = 1;
- needResetAnim = true;
- listener.onRefresh();
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- }
- });
- contentView.startAnimation(animation);
- contentView.layout(mRect.left, mRect.top + HEADER_SPRING_HEIGHT, mRect.right, mRect.bottom + HEADER_SPRING_HEIGHT);
- } else if (type == Type.FOLLOW) {
- isFullAnim = false;
- hasCallRefresh = false;
- callFreshORload = 1;
- needResetAnim = true;
- if (headerHander != null) headerHander.onStartAnim();
- mScroller.startScroll(0, getScrollY(), 0, -getScrollY() - HEADER_SPRING_HEIGHT, MOVE_TIME);
+ private void resetEndingPosition() {
+ flag.scrollAnimType = 2;
+ isInControl = false; //重置位置的时候,滑动事件已经不在控件的控制中了
+ if (getScrollY() < 0) { //下拉
+ if (headerHander != null) headerHander.onEndingAnimStart();
+ mScroller.startScroll(0, getScrollY(), 0, -getScrollY() - HEADER_ENDING_HEIGHT, MOVE_TIME);
+ invalidate();
+ } else { //上拉
+ if (footerHander != null) footerHander.onEndingAnimStart();
+ mScroller.startScroll(0, getScrollY(), 0, -getScrollY() + FOOTER_ENDING_HEIGHT, MOVE_TIME);
invalidate();
}
}
@@ -786,45 +978,78 @@ public void onAnimationRepeat(Animation animation) {
*/
private void restSmartPosition() {
if (listener == null) {
+ //没有设置监听器直接复原
resetPosition();
} else {
- if (isTopOverFarm()) {
- callFreshORload();
- if (give == Give.BOTH || give == Give.TOP)
- resetRefreshPosition();
- else
+ if (isTop()) {
+ //顶部被拉动
+ if (isTopOverLimit()) {
+ //顶部拉动超过limit位置
+ callbackOnStartAnim();
+ if (give == Give.BOTH || give == Give.TOP)
+ resetRefreshPosition();
+ else
+ resetPosition();
+ } else {
+ //顶部拉动未超过limit位置
+ flag.callFreshOrLoad = 3;
resetPosition();
- } else if (isBottomOverFarm()) {
- callFreshORload();
- if (give == Give.BOTH || give == Give.BOTTOM)
- resetRefreshPosition();
- else
+ }
+ } else if (isBottom()) {
+ //底部被拉动
+ if (isBottomOverLimit()) {
+ //底部拉动超过limit位置
+ callbackOnStartAnim();
+ if (give == Give.BOTH || give == Give.BOTTOM)
+ resetRefreshPosition();
+ else
+ resetPosition();
+ } else {
+ //底部拉动未超过limit位置
+ flag.callFreshOrLoad = 4;
resetPosition();
- } else {
- resetPosition();
+ }
}
}
}
- private void callFreshORload() {
- if (isTop()) { //下拉
- callFreshORload = 1;
- if (type == Type.OVERLAP) {
- if (dsY > 200 || HEADER_LIMIT_HEIGHT >= HEADER_SPRING_HEIGHT) {
- if (headerHander != null) headerHander.onStartAnim();
- }
- } else if (type == Type.FOLLOW) {
- if (headerHander != null) headerHander.onStartAnim();
- }
- } else if (isBottom()) {
- callFreshORload = 2;
- if (type == Type.OVERLAP) {
- if (dsY < -200 || FOOTER_LIMIT_HEIGHT >= FOOTER_SPRING_HEIGHT) {
- if (footerHander != null) footerHander.onStartAnim();
- }
- } else if (type == Type.FOLLOW) {
- if (footerHander != null) footerHander.onStartAnim();
+ /**
+ * {@link #callFresh()}的执行方法,不要暴露在外部
+ */
+ private void _callFresh() {
+ if (!isTop()) {
+ isCallFresh = true;
+ flag.scrollAnimType = 1; //不是全部回弹动画(半回弹到limit位置)
+ needResetAnim = true; //允许执行回弹动画
+ flag.hasCallRefresh = false;
+ flag.hasCallFull = false;
+ flag.callFreshOrLoad = 1;
+ if (headerHander != null) {
+ headerHander.onPreDrag(header);
+ headerHander.onStartAnim();
}
+ showHeaderAndFooter(true, false);
+ mScroller.startScroll(0, getScrollY(), 0, -getScrollY() - HEADER_SPRING_HEIGHT, MOVE_TIME);
+ invalidate();
+ }
+ }
+
+ private void showHeaderAndFooter(Boolean showHeader, Boolean showFooter) {
+ if (header != null && showHeader != null)
+ header.setVisibility(showHeader ? View.VISIBLE : View.INVISIBLE);
+ if (footer != null && showFooter != null)
+ footer.setVisibility(showFooter ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ private Type judgeType(DragHander dragHander) {
+ if (dragHander == null) {
+ return null;
+ } else if (dragHander.getType() != null) {
+ return dragHander.getType();
+ } else if (type != null) {
+ return type;
+ } else {
+ return Type.FOLLOW;
}
}
@@ -832,86 +1057,62 @@ private void callFreshORload() {
* 判断目标View是否滑动到顶部 还能否继续滑动
*/
private boolean isChildScrollToTop() {
- return !ViewCompat.canScrollVertically(contentView, -1);
+ return !contentView.canScrollVertically(-1);
}
/**
* 是否滑动到底部
*/
private boolean isChildScrollToBottom() {
- return !ViewCompat.canScrollVertically(contentView, 1);
+ return !contentView.canScrollVertically(1);
}
/**
* 判断顶部拉动是否超过临界值
*/
- private boolean isTopOverFarm() {
- if (type == Type.OVERLAP) {
- return contentView.getTop() > HEADER_LIMIT_HEIGHT;
- } else if (type == Type.FOLLOW) {
- return -getScrollY() > HEADER_LIMIT_HEIGHT;
- } else
- return false;
+ private boolean isTopOverLimit() {
+ return -getScrollY() > HEADER_LIMIT_HEIGHT;
}
/**
* 判断底部拉动是否超过临界值
*/
- private boolean isBottomOverFarm() {
- if (type == Type.OVERLAP) {
- return getHeight() - contentView.getBottom() > FOOTER_LIMIT_HEIGHT;
- } else if (type == Type.FOLLOW) {
- return getScrollY() > FOOTER_LIMIT_HEIGHT;
- } else
- return false;
+ private boolean isBottomOverLimit() {
+ return getScrollY() > FOOTER_LIMIT_HEIGHT;
}
/**
- * 判断当前状态是否拉动到顶部
+ * 判断当前状态是否正在拉动顶部
*/
private boolean isTop() {
- if (type == Type.OVERLAP) {
- return contentView.getTop() > 0;
- } else if (type == Type.FOLLOW) {
- return getScrollY() < 0;
- } else
- return false;
+ return getScrollY() < 0;
}
/**
- * 判断当前状态是否拉动到底部
+ * 判断当前状态是否正在拉动底部
*/
private boolean isBottom() {
- if (type == Type.OVERLAP) {
- return contentView.getTop() < 0;
- } else if (type == Type.FOLLOW) {
- return getScrollY() > 0;
- } else
- return false;
+ return getScrollY() > 0;
}
/**
* 判断当前滚动位置是否已经进入可折叠范围了
*/
private boolean isFlow() {
- if (type == Type.OVERLAP) {
- return contentView.getTop() < 30 && contentView.getTop() > -30;
- } else if (type == Type.FOLLOW) {
- return getScrollY() > -30 && getScrollY() < 30;
- } else
- return false;
+ return getScrollY() > -30 && getScrollY() < 30;
}
/**
* 切换Type的执行方法,之所以不暴露在外部,是防止用户在拖动过程中调用造成布局错乱,虽然并不会有人这样做
* 所以在外部方法{@link #setType(Type)}中设置标志,然后在拖动完毕后判断是否需要调用,是则调用本方法执行切换
*/
- private void changeType(Type type) {
+ private void _setType(Type type) {
this.type = type;
- if (header != null && header.getVisibility() != INVISIBLE) header.setVisibility(INVISIBLE);
- if (footer != null && footer.getVisibility() != INVISIBLE) footer.setVisibility(INVISIBLE);
+ setScrollChangeListener();
requestLayout();
- needChange = false;
+ needChangeType = false;
+ if (header != null) header.setTranslationY(0);
+ if (footer != null) footer.setTranslationY(0);
}
//#############################################
@@ -923,33 +1124,88 @@ private void changeType(Type type) {
*/
public void onFinishFreshAndLoad() {
if (!isMoveNow && needResetAnim) {
- boolean needTop = isTop() && (give == Give.TOP || give == Give.BOTH);
- boolean needBottom = isBottom() && (give == Give.BOTTOM || give == Give.BOTH);
- if (needTop || needBottom) {
- if (contentView instanceof ListView) {
- //((ListView) contentView).smoothScrollByOffset(1);
- //刷新后调用,才能正确显示刷新的item,如果调用上面的方法,listview会被固定在底部
- //在版本更新中,这个问题已经被修复,保留注释
- //((ListView) contentView).smoothScrollBy(-1,0);
+ if (isCallFresh) {
+ //手动调用callFresh进行刷新
+ if (isTop()) {
+ //检查是否需要执行收场动画
+ if (headerHander != null && headerHander.getEndingAnimTime() > 0) {
+ //需要则,回弹到收场高度
+ resetEndingPosition();
+ } else {
+ //不需要则,直接跳过
+ resetPosition();
+ }
+ } else {
+ resetPosition();
+ }
+ } else {
+ //拉动回弹刷新
+ boolean needTop = isTop() && (give == Give.TOP || give == Give.BOTH);
+ boolean needBottom = isBottom() && (give == Give.BOTTOM || give == Give.BOTH);
+ if (needTop || needBottom) {
+ //检查是否需要执行收场动画
+ if (headerHander != null && headerHander.getEndingAnimTime() > 0) {
+ //需要则,回弹到收场高度
+ resetEndingPosition();
+ } else {
+ //不需要则,直接跳过
+ resetPosition();
+ }
}
- resetPosition();
}
}
}
+ public void onFinishFreshAndLoadDelay(int delay) {
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ onFinishFreshAndLoad();
+ }
+ }, delay);
+ }
+
+ public void onFinishFreshAndLoadDelay() {
+ onFinishFreshAndLoadDelay(100);
+ }
+
/**
* 手动调用该方法使SpringView进入拉动更新的状态
*/
public void callFresh() {
- _callFresh();
+ if (judgeType(headerHander) != Type.SCROLL) {
+ _callFresh();
+ }
+ }
+
+ public void callFreshDelay(int delay) {
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ callFresh();
+ }
+ }, delay);
+ }
+
+ public void callFreshDelay() {
+ callFreshDelay(100);
}
public void setMoveTime(int time) {
this.MOVE_TIME = time;
}
- public void setMoveTimeOver(int time) {
- this.MOVE_TIME_OVER = time;
+ //过时,不再使用double类型了,请调用下面的重载方法
+ @Deprecated
+ public void setMovePara(double movePara) {
+ setMovePara((float) movePara);
+ }
+
+ public void setMovePara(float movePara) {
+ //移动参数:计算手指移动量的时候会用到这个值,值越大,移动量越小,若值为1则手指移动多少就滑动多少px (默认为2,建议在1-2之间)
+ //如果 header 或者 footer 实现了 getMovePara() 接口,则会优先使用接口提供的值,建议在自定义header/footer中设置移动参数,而不是通过该方法
+ //自1.6.0起,header 和 footer 的移动参数相互独立,不再是一个值同时决定两头的移动参数了
+ this.MOVE_PARA = movePara;
}
/**
@@ -1001,11 +1257,11 @@ public void setType(Type type) {
if (isTop() || isBottom()) {
//如果当前用户正在拖动,直接调用changeType()会造成布局错乱
//设置needChange标志,在执行完拖动后再调用changeType()
- needChange = true;
+ needChangeType = true;
//把参数保持起来
_type = type;
} else {
- changeType(type);
+ _setType(type);
}
}
@@ -1016,6 +1272,14 @@ public Type getType() {
return type;
}
+ public View getContentLay() {
+ return contentLay;
+ }
+
+ public View getContentView() {
+ return contentView;
+ }
+
/**
* 回调接口
*/
@@ -1039,8 +1303,6 @@ public View getFooterView() {
return footer;
}
- private boolean needChangeHeader = false;
- private boolean needChangeFooter = false;
private DragHander _headerHander;
private DragHander _footerHander;
private DragHander headerHander;
@@ -1054,49 +1316,85 @@ public DragHander getFooter() {
return footerHander;
}
+ @SuppressWarnings("unchecked")
+ public TH getHeader(Class | c) {
+ return (TH) headerHander;
+ }
+
+ @SuppressWarnings("unchecked")
+ public TF getFooter(Class c) {
+ return (TF) footerHander;
+ }
+
public void setHeader(DragHander headerHander) {
if (this.headerHander != null && isTop()) {
- needChangeHeader = true;
_headerHander = headerHander;
resetPosition();
} else {
- setHeaderIn(headerHander);
+ _setHeader(headerHander);
}
}
- private void setHeaderIn(DragHander headerHander) {
+ /**
+ * {@link #setHeader(DragHander)} 的执行方法,不要暴露在外部
+ */
+ private void _setHeader(DragHander headerHander) {
this.headerHander = headerHander;
if (header != null) {
removeView(this.header);
}
- headerHander.getView(inflater, this);
- this.header = getChildAt(getChildCount() - 1);
- contentView.bringToFront(); //把内容放在最前端
+ View tempView = headerHander.getView(inflater, this);
+ //建议在自定义Header中getView方法中调用inflate(R.layout.header_view, viewGroup, false);时,第三个参数'attachToRoot'传false
+ //这里进行这个判断是为了兼容,无论attachToRoot用的什么参数都能够正常运行
+ //_setFooter(.)同理
+ if (tempView instanceof SpringView) {
+ //如果tempView就是SpringView,则说明自定义Header的getView方法中inflate传入了true,此时header已经被添加到SpringView中了,无需addView
+ //获取最后一个view,即是header view
+ this.header = getChildAt(getChildCount() - 1);
+ } else {
+ //否则,则说明getView方法中inflate传入的是false,需要在这里添加
+ //此时tempView即是header view
+ addView(tempView);
+ this.header = tempView;
+ }
+ setScrollChangeListener();
requestLayout();
}
public void setFooter(DragHander footerHander) {
if (this.footerHander != null && isBottom()) {
- needChangeFooter = true;
_footerHander = footerHander;
resetPosition();
} else {
- setFooterIn(footerHander);
+ _setFooter(footerHander);
}
}
- private void setFooterIn(DragHander footerHander) {
+ /**
+ * {@link #setFooter(DragHander)} 的执行方法,不要暴露在外部
+ */
+ private void _setFooter(DragHander footerHander) {
this.footerHander = footerHander;
if (footer != null) {
removeView(footer);
}
- footerHander.getView(inflater, this);
- this.footer = getChildAt(getChildCount() - 1);
- contentView.bringToFront(); //把内容放在最前端
+ View tempView = footerHander.getView(inflater, this);
+ //同_setHeader(.),注释详见_setHeader(.)方法
+ if (tempView instanceof SpringView) {
+ this.footer = getChildAt(getChildCount() - 1);
+ } else {
+ addView(tempView);
+ this.footer = tempView;
+ }
+ setScrollChangeListener();
requestLayout();
}
+ /**
+ * header/footer核心接口
+ */
public interface DragHander {
+
View getView(LayoutInflater inflater, ViewGroup viewGroup);
int getDragLimitHeight(View rootView);
@@ -1105,8 +1403,20 @@ public interface DragHander {
int getDragSpringHeight(View rootView);
+ float getMovePara();
+
+ Type getType();
+
+ /**
+ * 即将开始拖拽时的回调,可进行初始化操作
+ */
void onPreDrag(View rootView);
+ /**
+ * 拖拽结束时回调,不管是否拖拽超过limit阈值
+ */
+ void onFinishDrag(View rootView);
+
/**
* 手指拖动控件过程中的回调,用户可以根据拖动的距离添加拖动过程动画
*
@@ -1117,18 +1427,52 @@ public interface DragHander {
/**
* 手指拖动控件过程中每次抵达临界点时的回调,用户可以根据手指方向设置临界动画
*
- * @param upORdown 是上拉还是下拉
+ * @param upORdown 是上拉还是下拉 true(上),false(下)
*/
void onLimitDes(View rootView, boolean upORdown);
/**
- * 拉动超过临界点后松开时回调
+ * 开始动画回调:拉动超过临界点后松开时回调
+ * 和{@link #onFinishAnim()}总是成对调用
*/
void onStartAnim();
/**
- * 头(尾)已经全部弹回时回调
+ * 结束动画回调:头(尾)即将全部弹回时回调
+ * 和{@link #onStartAnim()}总是成对调用
*/
void onFinishAnim();
+
+ /**
+ * 头(尾)弹回已经彻底结束时回调
+ * onStartAnim在有效回弹开始时回调,onFinishAnim会在有效回弹结束时回调,但此时回弹动画并未完全完成
+ * 如果此项继续下拉则会继续触发onStartAnim -> onFinishAnim,而onResetAnim则会在回弹动画彻底结束后回调,可以在这个回调中进行释放动画资源等操作
+ * 增加这个接口主要是为了处理用户不断上下拉动疯狂拖拽header/footer时的手势兼容。
+ */
+ void onResetAnim();
+
+ //###########################################
+ //########### 收场动画相关接口 ##############
+ //###########################################
+
+ /**
+ * 收场动画执行时间
+ */
+ int getEndingAnimTime();
+
+ /**
+ * 收场动画回弹高度
+ */
+ int getEndingAnimHeight(View rootView);
+
+ /**
+ * 收场动画开始执行
+ */
+ void onEndingAnimStart();
+
+ /**
+ * 收场动画结束执行
+ */
+ void onEndingAnimEnd();
}
}
diff --git a/library/src/main/res/drawable-hdpi/ic_launcher.png b/library/src/main/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index cde69bc..0000000
Binary files a/library/src/main/res/drawable-hdpi/ic_launcher.png and /dev/null differ
diff --git a/library/src/main/res/drawable-hdpi/progress.png b/library/src/main/res/drawable-hdpi/progress.png
deleted file mode 100644
index 84995b0..0000000
Binary files a/library/src/main/res/drawable-hdpi/progress.png and /dev/null differ
diff --git a/library/src/main/res/drawable/progress.xml b/library/src/main/res/drawable/progress.xml
new file mode 100644
index 0000000..8083918
--- /dev/null
+++ b/library/src/main/res/drawable/progress.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/library/src/main/res/drawable/view_post_comment_bg.xml b/library/src/main/res/drawable/view_post_comment_bg.xml
deleted file mode 100644
index 896c65e..0000000
--- a/library/src/main/res/drawable/view_post_comment_bg.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/library/src/main/res/layout/auto_footer.xml b/library/src/main/res/layout/auto_footer.xml
new file mode 100644
index 0000000..369c234
--- /dev/null
+++ b/library/src/main/res/layout/auto_footer.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/library/src/main/res/layout/default_header.xml b/library/src/main/res/layout/default_header.xml
index 3b92888..91ee948 100644
--- a/library/src/main/res/layout/default_header.xml
+++ b/library/src/main/res/layout/default_header.xml
@@ -1,32 +1,33 @@
+ android:layout_height="70dp">
+ android:orientation="vertical">
+ android:gravity="center"
+ android:text="下拉刷新"
+ android:textColor="#777777" />
+ android:layout_marginTop="3dp">
@@ -51,7 +52,7 @@
android:layout_height="40dp"
android:layout_alignLeft="@id/default_header_text"
android:layout_centerVertical="true"
- android:layout_marginLeft="-35dp"/>
+ android:layout_marginLeft="-35dp" />
+ android:visibility="invisible"
+ tools:visibility="visible" />
\ No newline at end of file
diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml
index eb96317..d927a34 100644
--- a/library/src/main/res/values/attrs.xml
+++ b/library/src/main/res/values/attrs.xml
@@ -4,6 +4,7 @@
+
diff --git a/screenshot/auto_footer.gif b/screenshot/auto_footer.gif
new file mode 100644
index 0000000..49f4604
Binary files /dev/null and b/screenshot/auto_footer.gif differ
diff --git a/screenshot/auto_footer_s.gif b/screenshot/auto_footer_s.gif
new file mode 100644
index 0000000..04b782e
Binary files /dev/null and b/screenshot/auto_footer_s.gif differ
diff --git a/screenshot/du_header.gif b/screenshot/du_header.gif
new file mode 100644
index 0000000..2cd2da1
Binary files /dev/null and b/screenshot/du_header.gif differ
diff --git a/screenshot/du_header_s.gif b/screenshot/du_header_s.gif
new file mode 100644
index 0000000..2c05287
Binary files /dev/null and b/screenshot/du_header_s.gif differ
diff --git a/screenshot/weixin_header.gif b/screenshot/weixin_header.gif
new file mode 100644
index 0000000..243650b
Binary files /dev/null and b/screenshot/weixin_header.gif differ
diff --git a/screenshot/weixin_header_s.gif b/screenshot/weixin_header_s.gif
new file mode 100644
index 0000000..621263b
Binary files /dev/null and b/screenshot/weixin_header_s.gif differ
diff --git a/screenshot/weixin_header_v2.gif b/screenshot/weixin_header_v2.gif
new file mode 100644
index 0000000..ec3c894
Binary files /dev/null and b/screenshot/weixin_header_v2.gif differ
diff --git a/screenshot/weixin_header_v2_s.gif b/screenshot/weixin_header_v2_s.gif
new file mode 100644
index 0000000..4ab0987
Binary files /dev/null and b/screenshot/weixin_header_v2_s.gif differ
diff --git a/settings.gradle b/settings.gradle
index 2150f22..f1a54a7 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':library', ':demo'
+include ':library', ':demo', ':header_ali', ':header_meituan', ':header_rotation', ':header_acfun', ':header_weixin', ':header_wangyi', ':header_du'
|