├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── README_ZH.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── rouchuan │ │ └── viewpagerlayoutmanager │ │ ├── BaseActivity.java │ │ ├── DataAdapter.java │ │ ├── MainActivity.java │ │ ├── SettingPopUpWindow.java │ │ ├── Util.java │ │ ├── carousel │ │ ├── CarouselLayoutActivity.java │ │ └── CarouselPopUpWindow.java │ │ ├── circle │ │ ├── CircleLayoutActivity.java │ │ └── CirclePopUpWindow.java │ │ ├── circlescale │ │ ├── CircleScaleLayoutActivity.java │ │ └── CircleScalePopUpWindow.java │ │ ├── gallery │ │ ├── GalleryLayoutActivity.java │ │ └── GalleryPopUpWindow.java │ │ ├── rotate │ │ ├── RotateLayoutActivity.java │ │ └── RotatePopUpWindow.java │ │ └── scale │ │ ├── ScaleLayoutActivity.java │ │ └── ScalePopUpWindow.java │ └── res │ ├── drawable-xhdpi │ ├── item1.jpg │ ├── item10.jpg │ ├── item2.jpg │ ├── item3.jpg │ ├── item4.jpg │ ├── item5.jpg │ ├── item6.jpg │ ├── item7.jpg │ ├── item8.jpg │ ├── item9.jpg │ └── logo.png │ ├── drawable │ └── ic_settings_white_48px.xml │ ├── layout │ ├── activity_base.xml │ ├── activity_main.xml │ ├── dialog_carousel_setting.xml │ ├── dialog_circle_scale_setting.xml │ ├── dialog_circle_setting.xml │ ├── dialog_gallery_setting.xml │ ├── dialog_rotate_setting.xml │ ├── dialog_scale_setting.xml │ └── item_image.xml │ ├── menu │ └── settings.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-zh │ └── strings.xml │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── static ├── carousel.jpg ├── circle.jpg ├── circle_scale.jpg ├── customize.gif ├── gallery.jpg ├── infinite.gif ├── logo.png ├── rotate.jpg └── scale.jpg └── viewpagerlayoutmanager ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── java └── com │ └── leochuan │ ├── AutoPlayRecyclerView.java │ ├── AutoPlaySnapHelper.java │ ├── CarouselLayoutManager.java │ ├── CenterSnapHelper.java │ ├── CircleLayoutManager.java │ ├── CircleScaleLayoutManager.java │ ├── GalleryLayoutManager.java │ ├── OrientationHelper.java │ ├── PageSnapHelper.java │ ├── RotateLayoutManager.java │ ├── ScaleLayoutManager.java │ ├── ScrollHelper.java │ └── ViewPagerLayoutManager.java └── res └── values ├── attrs.xml └── strings.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | 15 | # Local configuration file (sdk path, etc) 16 | local.properties 17 | 18 | # Eclipse project files 19 | .classpath 20 | .project 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # Android Studio 26 | .idea 27 | *.iml 28 | 29 | #.idea/workspace.xml - remove # and delete .idea if it better suit your needs. 30 | .gradle 31 | build/ 32 | 33 | # Windows thumbnail db 34 | Thumbs.db 35 | 36 | # OSX files 37 | .DS_Store 38 | 39 | .settings/ 40 | 41 | /*/out 42 | /*/*/build 43 | **/build 44 | /*/*/production 45 | *.iws 46 | *.ipr 47 | *~ 48 | *.swp 49 | *.hprof 50 | javatest.iml 51 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: oraclejdk8 3 | sudo: false 4 | android: 5 | components: 6 | - build-tools-26.0.2 7 | - android-26 8 | script: 9 | - "./gradlew build" 10 | deploy: 11 | - provider: releases 12 | skip_cleanup: true 13 | file: "app/build/outputs/apk/debug/app-debug.apk" 14 | on: 15 | tags: true 16 | api_key: 17 | secure: VEFF5IWyypoYuu2s4fYDG8HujOFdD8mTKBa8FmJJOyOQxZXorhcrRRyoJUNno/cpVmJWmevS0AXcBLAlolh5R51YVnAIeb0AVcKNW3C4CCiy1F6J744PNAe59AivSTwY9ZwBDMKhm/u4gmVEmXPkomHT87mBuvFwrVxLDo6nmKb6hEX9zCx9ju/Qa+mhO7PyuYOhcAO1hA1I1kAdZALvzlCvl3rHsF6a5hrKuUfPlyeFy53B3GcW6Zv7UAmS0ZWaA91WgINofQblk06UatTNz911ih6cPdTnbbNePNHzOOWaXC9bAQ7Cs6IQVoNc/1CYK72mO/zD2w+uge5W7VzLX1yuSyOIKKDKz2sulH6oQP6q1QeVPqqBa2qhEfS82V7LXhJOIMSzKFd597S94bOO/Y+YFoqS1A8DAFXwNAJv9go39/k7IXu+gmGFTic8E9Gfc/z7t1VHJjNRuyS1uZRcj9hfd7kGJz/oz4Ps8UuePX6CRwxxwRrZeLU200mCZ0KmSqM1bBzPiWANT+d/zPhPfDjYBqNDIRvuNHFkRaR3Mw8HeF4b5R3FI1aInih7XkFc3t58AZYtuCsbSy3y8hG8aQiOYrHV3V3ixpV3kODocmChXzrHy7inHSr00rDPJWwssfop31T1dOTqlilg4ZpNFHzAN3jgZp+BeNZL7sKjHeI= 18 | - provider: bintray 19 | skip_cleanup: true 20 | file: "descriptor.json" 21 | on: 22 | tags: true 23 | user: leochuan 24 | key: 25 | secure: qHfqyFaQXqxA46ZaHtxzZ811tV+2sd+JWRZd2crzg43lLZH8NMBvBwiuSiP5a8/MYhnrviaw/LkZHso5+6SXyTbn3ET0N1Ksd2lI3FgZH4U35eLUwf6i/uNw9bqJ7TYBSO+o9sDaXciYjLkGdfVPQ7ihs/TTROr5Lag1tG1FccDXuVY6oGH2mfDCE8n38xyrjeL3oZKVk3PaUYeFlboergMrLwrVuk3vOy1o2i301uWq8YqcBynROxTObRPVd5hgF4cX5MOYhKUCqXrK6mwWTmRgCVVHe5+Dll6bnd5STCiR/0ymwbQXjPBdLXqqBrCxCWWkf/8aNIRaajDHaauDRMTQPW81NUeKNp76fvAqzq+CCzQDf4qa+jky1OsOntoah3yFvcAQlodCYWS+mAB4obilE3p8K2Z/joeCzb8uH+LlXOZ7asjK+FJ9jlCgiuXzienAbwQnU+Bm5fu4pxq+LgzvSovQVbC0e4oD+BL3eU5bGGc8QxkXFWvmIoQ5d2Uhw6epLleaxL2EEDPbPgakUzs3lp8b2qJ4afhDAiGs5W8knOFn7sxcRhpMzuOHxTJSVJBOj6Kkb7apHMzZa+ezQzdNwaw+Ad0YBkPRBQ2VCDzzv5wKtitTteRxWBJFsF63jLdsdkDOvt78+Svjtg5mNTGVft2YkGw70/gOE1ZXO0g= -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright (c) 2016 RuoChuan Shen 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ViewPager-LayoutManager [![Download](https://api.bintray.com/packages/leochuan/maven/viewpager-layout-manager/images/download.svg) ](https://bintray.com/leochuan/maven/viewpager-layout-manager/_latestVersion) ![build](https://travis-ci.org/leochuan/ViewPagerLayoutManager.svg?branch=master) 2 | 3 | **English** | [中文](README_ZH.md) 4 | 5 | ![logo](static/logo.png) 6 | 7 | VPLM is a `ViewPager` like `LayoutManager` which implements some common animations. If you need some other effects feel free to raise an issue or PR. 8 | 9 | ![circle](static/circle.jpg) ![circle_scale](static/circle_scale.jpg) ![carousel](static/carousel.jpg) ![gallery](static/gallery.jpg) ![rotate](static/rotate.jpg) ![scale](static/scale.jpg) 10 | 11 | ## Customzie 12 | 13 | ![customize](static/customize.gif) 14 | 15 | Each `layoutmanager` has bunch of different properties to customize. 16 | 17 | Such as: 18 | * radius 19 | * scroll speed 20 | * space 21 | * orientation 22 | 23 | Run the demo to see more details. 24 | 25 | ## Infinite Scroll 26 | 27 | ![infinite](static/infinite.gif) 28 | 29 | ## Auto Center 30 | 31 | You can make the target view stop right at center every time you swipe or drag by: 32 | ```java 33 | // work exactly same as LinearSnapHelper. 34 | new CenterSnapHelper().attachToRecyclerView(recyclerView); 35 | ``` 36 | 37 | ## Set Max Visible Item Count 38 | ```java 39 | layoutmanager.setMaxVisibleItemCount(count); 40 | ``` 41 | 42 | ## Get Position Of Center Item 43 | ```java 44 | layoutmanager.getCurrentPosition() 45 | ``` 46 | 47 | ## Smooth Scroll To Position 48 | Normally you can just use `RecyclerView`'s `SmoothScrollToPosition` method, 49 | but when infinite scroll enabled, using method below to avoid some unexpected errors. 50 | ```java 51 | ScrollHelper.smoothScrollToTargetView(recyclerView, itemViewYouWantScrollTo); 52 | ``` 53 | 54 | ## Auto Play 55 | 56 | You can make it auto play by using `AutoPlayRecyclerView` 57 | 58 | ```xml 59 | 65 | ``` 66 | 67 | ## Download 68 | 69 | Gradle: 70 | 71 | ```groovy 72 | repositories { 73 | jcenter() 74 | } 75 | 76 | dependencies { 77 | compile 'rouchuan.viewpagerlayoutmanager:viewpagerlayoutmanager:2.x.y' 78 | } 79 | ``` 80 | 81 | Maven: 82 | 83 | ```xml 84 | 85 | rouchuan.viewpagerlayoutmanager 86 | viewpagerlayoutmanager 87 | 2.x.y 88 | pom 89 | 90 | ``` 91 | 92 | ## Quick Start 93 | Make sure that each item has the same size, or something unpredictable may happen. 94 | 95 | You can warm up your layoutmanager by `Builder`. 96 | 97 | ```java 98 | new CircleLayoutManager.Builder(context) 99 | .setAngleInterval(mAngle) 100 | .setMaxRemoveAngle(mMaxRemoveAngle) 101 | .setMinRemoveAngle(mMinRemoveAngle) 102 | .setMoveSpeed(mSpeed) 103 | .setRadius(mRadius) 104 | .setReverseLayout(true) 105 | .build(); 106 | ``` 107 | 108 | Or just simply call the construct. 109 | 110 | ```java 111 | new CircleLayoutManager(context); 112 | ``` 113 | 114 | ## License 115 | 116 | Apache-2.0. See [LICENSE](LICENSE) file for detail -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 | # ViewPager-LayoutManager [![Download](https://api.bintray.com/packages/leochuan/maven/viewpager-layout-manager/images/download.svg) ](https://bintray.com/leochuan/maven/viewpager-layout-manager/_latestVersion) ![build](https://travis-ci.org/leochuan/ViewPagerLayoutManager.svg?branch=master) 2 | 3 | [English](README.md) | **中文** 4 | 5 | ![logo](static/logo.png) 6 | 7 | VPLM 实现了一些常见的动画效果,如果你有什么别的想要的效果欢迎给我提ISSUE以及PR 8 | 9 | ![circle](static/circle.jpg) ![circle_scale](static/circle_scale.jpg) ![carousel](static/carousel.jpg) ![gallery](static/gallery.jpg) ![rotate](static/rotate.jpg) ![scale](static/scale.jpg) 10 | 11 | ## 自定义属性 12 | ![customize](static/customize.gif) 13 | 14 | 各个`layoutmanager`都有各自的一些属性可以设置 15 | 比如: 16 | * 半径 17 | * 滚动速度 18 | * 间隔 19 | * 排列方向 20 | 21 | 可以运行下demo看下具体有哪些属性可以设置 22 | 23 | ## 循环列表 24 | 25 | ![infinite](static/infinite.gif) 26 | 27 | ## 自动滚动到中心 28 | 29 | 在每次拖动或者快速滑动的时候,你可以通过设置 `CenterSnapHelper` 让目标 view 自动停在屏幕中央 30 | ```java 31 | // work exactly same as LinearSnapHelper. 32 | new CenterSnapHelper().attachToRecyclerView(recyclerView); 33 | ``` 34 | 35 | ## 设置可见个数 36 | ```java 37 | layoutmanager.setMaxVisibleItemCount(count); 38 | ``` 39 | 40 | ## 获取中间item的位置 41 | ```java 42 | layoutmanager.getCurrentPosition() 43 | ``` 44 | 45 | ## 滚动到特定位置 46 | 一般情况下,直接使用`RecyclerView`自带的`smoothScrollToPosition`就可以了, 47 | 但是当无限滚动开启的时候,如果能获取到要滚动到的view建议使用下面的方法。 48 | ```java 49 | ScrollHelper.smoothScrollToTargetView(recyclerView, itemViewYouWantScrollTo); 50 | ``` 51 | 52 | ## 自动轮播 53 | 54 | 请使用 `AutoPlayRecyclerView` 55 | 56 | ```xml 57 | 63 | ``` 64 | 65 | ## 安装 66 | 67 | Gradle: 68 | 69 | ```groovy 70 | repositories { 71 | jcenter() 72 | } 73 | 74 | dependencies { 75 | compile 'rouchuan.viewpagerlayoutmanager:viewpagerlayoutmanager:2.x.y' 76 | } 77 | ``` 78 | 79 | Maven: 80 | 81 | ```xml 82 | 83 | rouchuan.viewpagerlayoutmanager 84 | viewpagerlayoutmanager 85 | 2.x.y 86 | pom 87 | 88 | ``` 89 | 90 | ## 快速开始 91 | 92 | 使用前请确保每一个`view`的大小都相同,不然可能会发生不可预料的错误。 93 | 94 | 95 | 96 | 你可以通过新建一个`Builder`来设置各种属性: 97 | 98 | ```java 99 | new CircleLayoutManager.Builder(context) 100 | .setAngleInterval(mAngle) 101 | .setMaxRemoveAngle(mMaxRemoveAngle) 102 | .setMinRemoveAngle(mMinRemoveAngle) 103 | .setMoveSpeed(mSpeed) 104 | .setRadius(mRadius) 105 | .setReverseLayout(true) 106 | .build(); 107 | ``` 108 | 109 | 或者只是简单的调用一下预设的构造方法: 110 | 111 | ```java 112 | new CircleLayoutManager(context); 113 | ``` 114 | 115 | ## License 116 | 117 | Apache-2.0. 详情见 [LICENSE](LICENSE) -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion '26.0.2' 6 | 7 | defaultConfig { 8 | applicationId "rouchuan.circlelayoutmanager" 9 | minSdkVersion 16 10 | targetSdkVersion 26 11 | versionCode 1 12 | versionName "1.0" 13 | vectorDrawables.useSupportLibrary = true 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | compileOptions { 22 | sourceCompatibility JavaVersion.VERSION_1_7 23 | targetCompatibility JavaVersion.VERSION_1_7 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation 'com.android.support:appcompat-v7:26.1.0' 29 | implementation 'com.android.support:cardview-v7:26.1.0' 30 | implementation 'com.android.support:recyclerview-v7:26.1.0' 31 | implementation 'com.android.support:design:26.1.0' 32 | implementation project(path: ':viewpagerlayoutmanager') 33 | // implementation 'rouchuan.viewpagerlayoutmanager:viewpagerlayoutmanager:2.0.2' 34 | } 35 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Dajavu/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.graphics.drawable.VectorDrawableCompat; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.support.v7.widget.RecyclerView; 8 | import android.view.Gravity; 9 | import android.view.Menu; 10 | import android.view.MenuItem; 11 | import android.view.View; 12 | import android.widget.Toast; 13 | 14 | import com.leochuan.ScrollHelper; 15 | import com.leochuan.ViewPagerLayoutManager; 16 | 17 | 18 | /** 19 | * Created by Dajavu on 26/10/2017. 20 | */ 21 | 22 | public abstract class BaseActivity 23 | extends AppCompatActivity { 24 | private RecyclerView recyclerView; 25 | private V viewPagerLayoutManager; 26 | private S settingPopUpWindow; 27 | 28 | protected abstract V createLayoutManager(); 29 | 30 | protected abstract S createSettingPopUpWindow(); 31 | 32 | @Override 33 | protected void onCreate(@Nullable Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | setContentView(R.layout.activity_base); 36 | setTitle(getIntent().getCharSequenceExtra(MainActivity.INTENT_TITLE)); 37 | recyclerView = findViewById(R.id.recycler); 38 | viewPagerLayoutManager = createLayoutManager(); 39 | DataAdapter dataAdapter = new DataAdapter(); 40 | dataAdapter.setOnItemClickListener(new DataAdapter.OnItemClickListener() { 41 | @Override 42 | public void onItemClick(View v, int pos) { 43 | Toast.makeText(v.getContext(), "clicked:" + pos, Toast.LENGTH_SHORT).show(); 44 | ScrollHelper.smoothScrollToTargetView(recyclerView, v); 45 | } 46 | }); 47 | recyclerView.setAdapter(dataAdapter); 48 | recyclerView.setLayoutManager(viewPagerLayoutManager); 49 | } 50 | 51 | @Override 52 | public boolean onCreateOptionsMenu(Menu menu) { 53 | getMenuInflater().inflate(R.menu.settings, menu); 54 | MenuItem settings = menu.findItem(R.id.setting); 55 | VectorDrawableCompat settingIcon = 56 | VectorDrawableCompat.create(getResources(), R.drawable.ic_settings_white_48px, null); 57 | settings.setIcon(settingIcon); 58 | return super.onCreateOptionsMenu(menu); 59 | } 60 | 61 | @Override 62 | public boolean onOptionsItemSelected(MenuItem item) { 63 | switch (item.getItemId()) { 64 | case R.id.setting: 65 | showDialog(); 66 | return true; 67 | case android.R.id.home: 68 | onBackPressed(); 69 | return true; 70 | default: 71 | return super.onOptionsItemSelected(item); 72 | } 73 | } 74 | 75 | private void showDialog() { 76 | if (settingPopUpWindow == null) { 77 | settingPopUpWindow = createSettingPopUpWindow(); 78 | } 79 | settingPopUpWindow.showAtLocation(recyclerView, Gravity.CENTER, 0, 0); 80 | } 81 | 82 | public V getViewPagerLayoutManager() { 83 | return viewPagerLayoutManager; 84 | } 85 | 86 | public S getSettingPopUpWindow() { 87 | return settingPopUpWindow; 88 | } 89 | 90 | public RecyclerView getRecyclerView() { 91 | return recyclerView; 92 | } 93 | 94 | @Override 95 | protected void onDestroy() { 96 | super.onDestroy(); 97 | if (settingPopUpWindow != null && settingPopUpWindow.isShowing()) 98 | settingPopUpWindow.dismiss(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/DataAdapter.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ImageView; 8 | 9 | /** 10 | * Created by Dajavu on 25/10/2017. 11 | */ 12 | 13 | public class DataAdapter extends RecyclerView.Adapter { 14 | public interface OnItemClickListener { 15 | void onItemClick(View v, int pos); 16 | } 17 | 18 | private int[] images = {R.drawable.item1, R.drawable.item2, R.drawable.item3, 19 | R.drawable.item4, R.drawable.item5, R.drawable.item6, R.drawable.item7, 20 | R.drawable.item8, R.drawable.item9, R.drawable.item10}; 21 | 22 | public OnItemClickListener onItemClickListener; 23 | 24 | @Override 25 | public DataAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 26 | return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false)); 27 | } 28 | 29 | @Override 30 | public void onBindViewHolder(DataAdapter.ViewHolder holder, int position) { 31 | holder.imageView.setImageResource(images[position]); 32 | holder.imageView.setTag(position); 33 | } 34 | 35 | @Override 36 | public int getItemCount() { 37 | return images.length; 38 | } 39 | 40 | class ViewHolder extends RecyclerView.ViewHolder { 41 | ImageView imageView; 42 | 43 | ViewHolder(View itemView) { 44 | super(itemView); 45 | imageView = itemView.findViewById(R.id.image); 46 | itemView.setOnClickListener(new View.OnClickListener() { 47 | @Override 48 | public void onClick(View v) { 49 | if (onItemClickListener != null) { 50 | onItemClickListener.onItemClick(v, getAdapterPosition()); 51 | } 52 | } 53 | }); 54 | } 55 | } 56 | 57 | public void setOnItemClickListener(OnItemClickListener onItemClickListener) { 58 | this.onItemClickListener = onItemClickListener; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/MainActivity.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.support.v7.widget.AppCompatButton; 7 | import android.view.View; 8 | 9 | import rouchuan.viewpagerlayoutmanager.carousel.CarouselLayoutActivity; 10 | import rouchuan.viewpagerlayoutmanager.circle.CircleLayoutActivity; 11 | import rouchuan.viewpagerlayoutmanager.circlescale.CircleScaleLayoutActivity; 12 | import rouchuan.viewpagerlayoutmanager.gallery.GalleryLayoutActivity; 13 | import rouchuan.viewpagerlayoutmanager.rotate.RotateLayoutActivity; 14 | import rouchuan.viewpagerlayoutmanager.scale.ScaleLayoutActivity; 15 | 16 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { 17 | public final static String INTENT_TITLE = "title"; 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_main); 23 | findViewById(R.id.bt_circle).setOnClickListener(this); 24 | findViewById(R.id.bt_circle_scale).setOnClickListener(this); 25 | findViewById(R.id.bt_elevate_scale).setOnClickListener(this); 26 | findViewById(R.id.bt_gallery).setOnClickListener(this); 27 | findViewById(R.id.bt_rotate).setOnClickListener(this); 28 | findViewById(R.id.bt_scale).setOnClickListener(this); 29 | 30 | } 31 | 32 | @Override 33 | public void onClick(View v) { 34 | switch (v.getId()) { 35 | case R.id.bt_circle: 36 | startActivity(CircleLayoutActivity.class, v); 37 | break; 38 | case R.id.bt_circle_scale: 39 | startActivity(CircleScaleLayoutActivity.class, v); 40 | break; 41 | case R.id.bt_elevate_scale: 42 | startActivity(CarouselLayoutActivity.class, v); 43 | break; 44 | case R.id.bt_gallery: 45 | startActivity(GalleryLayoutActivity.class, v); 46 | break; 47 | case R.id.bt_rotate: 48 | startActivity(RotateLayoutActivity.class, v); 49 | break; 50 | case R.id.bt_scale: 51 | startActivity(ScaleLayoutActivity.class, v); 52 | break; 53 | } 54 | } 55 | 56 | private void startActivity(Class clz, View view) { 57 | Intent intent = new Intent(this, clz); 58 | if (view instanceof AppCompatButton) { 59 | intent.putExtra(INTENT_TITLE, ((AppCompatButton) view).getText()); 60 | } 61 | startActivity(intent); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/SettingPopUpWindow.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager; 2 | 3 | import android.content.Context; 4 | import android.view.WindowManager; 5 | import android.widget.PopupWindow; 6 | 7 | /** 8 | * Created by Dajavu on 26/10/2017. 9 | */ 10 | 11 | public abstract class SettingPopUpWindow extends PopupWindow { 12 | public SettingPopUpWindow(Context context) { 13 | super(context); 14 | setOutsideTouchable(true); 15 | setWidth(Util.Dp2px(context, 320)); 16 | setHeight(WindowManager.LayoutParams.WRAP_CONTENT); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/Util.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager; 2 | 3 | import android.content.Context; 4 | 5 | import java.util.Locale; 6 | 7 | /** 8 | * Created by Dajavu on 25/10/2017. 9 | */ 10 | 11 | public class Util { 12 | public static int Dp2px(Context context, float dp) { 13 | final float scale = context.getResources().getDisplayMetrics().density; 14 | return (int) (dp * scale + 0.5f); 15 | } 16 | 17 | public static String formatFloat(float value) { 18 | return String.format(Locale.getDefault(), "%.3f", value); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/carousel/CarouselLayoutActivity.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager.carousel; 2 | 3 | import com.leochuan.CarouselLayoutManager; 4 | 5 | import rouchuan.viewpagerlayoutmanager.BaseActivity; 6 | import rouchuan.viewpagerlayoutmanager.Util; 7 | 8 | /** 9 | * Created by Dajavu on 27/10/2017. 10 | */ 11 | 12 | public class CarouselLayoutActivity extends BaseActivity { 13 | 14 | @Override 15 | protected CarouselLayoutManager createLayoutManager() { 16 | return new CarouselLayoutManager(this, Util.Dp2px(this, 100)); 17 | } 18 | 19 | @Override 20 | protected CarouselPopUpWindow createSettingPopUpWindow() { 21 | return new CarouselPopUpWindow(this, getViewPagerLayoutManager(), getRecyclerView()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/carousel/CarouselPopUpWindow.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager.carousel; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.support.v7.widget.SwitchCompat; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.widget.CompoundButton; 10 | import android.widget.SeekBar; 11 | import android.widget.TextView; 12 | 13 | import com.leochuan.CarouselLayoutManager; 14 | import com.leochuan.CenterSnapHelper; 15 | import com.leochuan.ViewPagerLayoutManager; 16 | 17 | import rouchuan.viewpagerlayoutmanager.R; 18 | import rouchuan.viewpagerlayoutmanager.SettingPopUpWindow; 19 | import rouchuan.viewpagerlayoutmanager.Util; 20 | 21 | /** 22 | * Created by Dajavu on 27/10/2017. 23 | */ 24 | 25 | @SuppressLint("InflateParams") 26 | @SuppressWarnings("FieldCanBeLocal") 27 | public class CarouselPopUpWindow extends SettingPopUpWindow 28 | implements SeekBar.OnSeekBarChangeListener, CompoundButton.OnCheckedChangeListener { 29 | 30 | private CarouselLayoutManager carouselLayoutManager; 31 | private RecyclerView recyclerView; 32 | private TextView itemSpaceValue; 33 | private TextView speedValue; 34 | private TextView minScaleValue; 35 | private SwitchCompat changeOrientation; 36 | private SwitchCompat autoCenter; 37 | private SwitchCompat infinite; 38 | private SwitchCompat reverse; 39 | private CenterSnapHelper centerSnapHelper; 40 | 41 | CarouselPopUpWindow(Context context, CarouselLayoutManager carouselLayoutManager, RecyclerView recyclerView) { 42 | super(context); 43 | this.carouselLayoutManager = carouselLayoutManager; 44 | this.recyclerView = recyclerView; 45 | View view = LayoutInflater.from(context).inflate(R.layout.dialog_carousel_setting, null); 46 | setContentView(view); 47 | 48 | centerSnapHelper = new CenterSnapHelper(); 49 | 50 | SeekBar itemSpace = view.findViewById(R.id.sb_item_space); 51 | SeekBar speed = view.findViewById(R.id.sb_speed); 52 | SeekBar minScale = view.findViewById(R.id.sb_min_scale); 53 | 54 | itemSpaceValue = view.findViewById(R.id.item_space); 55 | speedValue = view.findViewById(R.id.speed_value); 56 | minScaleValue = view.findViewById(R.id.min_scale_value); 57 | 58 | changeOrientation = view.findViewById(R.id.s_change_orientation); 59 | autoCenter = view.findViewById(R.id.s_auto_center); 60 | infinite = view.findViewById(R.id.s_infinite); 61 | reverse = view.findViewById(R.id.s_reverse); 62 | 63 | itemSpace.setOnSeekBarChangeListener(this); 64 | speed.setOnSeekBarChangeListener(this); 65 | minScale.setOnSeekBarChangeListener(this); 66 | 67 | itemSpace.setProgress(carouselLayoutManager.getItemSpace() / 5); 68 | speed.setProgress(Math.round(carouselLayoutManager.getMoveSpeed() / 0.05f)); 69 | minScale.setProgress(Math.round(carouselLayoutManager.getMinScale() * 100)); 70 | 71 | itemSpaceValue.setText(String.valueOf(carouselLayoutManager.getItemSpace())); 72 | speedValue.setText(Util.formatFloat(carouselLayoutManager.getMoveSpeed())); 73 | minScaleValue.setText(Util.formatFloat(carouselLayoutManager.getMinScale())); 74 | 75 | changeOrientation.setChecked(carouselLayoutManager.getOrientation() == ViewPagerLayoutManager.VERTICAL); 76 | reverse.setChecked(carouselLayoutManager.getReverseLayout()); 77 | infinite.setChecked(carouselLayoutManager.getInfinite()); 78 | 79 | changeOrientation.setOnCheckedChangeListener(this); 80 | autoCenter.setOnCheckedChangeListener(this); 81 | reverse.setOnCheckedChangeListener(this); 82 | infinite.setOnCheckedChangeListener(this); 83 | } 84 | 85 | @Override 86 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 87 | switch (seekBar.getId()) { 88 | case R.id.sb_item_space: 89 | int itemSpace = progress * 5; 90 | carouselLayoutManager.setItemSpace(itemSpace); 91 | itemSpaceValue.setText(String.valueOf(itemSpace)); 92 | break; 93 | case R.id.sb_min_scale: 94 | final float scale = progress / 100f; 95 | carouselLayoutManager.setMinScale(scale); 96 | minScaleValue.setText(Util.formatFloat(scale)); 97 | break; 98 | case R.id.sb_speed: 99 | final float speed = progress * 0.05f; 100 | carouselLayoutManager.setMoveSpeed(speed); 101 | speedValue.setText(Util.formatFloat(speed)); 102 | break; 103 | } 104 | } 105 | 106 | @Override 107 | public void onStartTrackingTouch(SeekBar seekBar) { 108 | 109 | } 110 | 111 | @Override 112 | public void onStopTrackingTouch(SeekBar seekBar) { 113 | 114 | } 115 | 116 | @Override 117 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 118 | switch (buttonView.getId()) { 119 | case R.id.s_infinite: 120 | recyclerView.scrollToPosition(0); 121 | carouselLayoutManager.setInfinite(isChecked); 122 | break; 123 | case R.id.s_change_orientation: 124 | carouselLayoutManager.scrollToPosition(0); 125 | carouselLayoutManager.setOrientation(isChecked ? 126 | ViewPagerLayoutManager.VERTICAL : ViewPagerLayoutManager.HORIZONTAL); 127 | break; 128 | case R.id.s_auto_center: 129 | if (isChecked) { 130 | centerSnapHelper.attachToRecyclerView(recyclerView); 131 | } else { 132 | centerSnapHelper.attachToRecyclerView(null); 133 | } 134 | break; 135 | case R.id.s_reverse: 136 | carouselLayoutManager.scrollToPosition(0); 137 | carouselLayoutManager.setReverseLayout(isChecked); 138 | break; 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/circle/CircleLayoutActivity.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager.circle; 2 | 3 | import com.leochuan.CircleLayoutManager; 4 | 5 | import rouchuan.viewpagerlayoutmanager.BaseActivity; 6 | 7 | /** 8 | * Created by Dajavu on 25/10/2017. 9 | */ 10 | 11 | public class CircleLayoutActivity extends BaseActivity { 12 | 13 | @Override 14 | protected CircleLayoutManager createLayoutManager() { 15 | return new CircleLayoutManager(this); 16 | } 17 | 18 | @Override 19 | protected CirclePopUpWindow createSettingPopUpWindow() { 20 | return new CirclePopUpWindow(this, getViewPagerLayoutManager(), getRecyclerView()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/circle/CirclePopUpWindow.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager.circle; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.support.v7.widget.SwitchCompat; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.widget.CompoundButton; 10 | import android.widget.RadioGroup; 11 | import android.widget.SeekBar; 12 | import android.widget.TextView; 13 | 14 | import com.leochuan.CenterSnapHelper; 15 | import com.leochuan.CircleLayoutManager; 16 | 17 | import rouchuan.viewpagerlayoutmanager.R; 18 | import rouchuan.viewpagerlayoutmanager.SettingPopUpWindow; 19 | import rouchuan.viewpagerlayoutmanager.Util; 20 | 21 | /** 22 | * Created by Dajavu on 25/10/2017. 23 | */ 24 | 25 | @SuppressLint("InflateParams") 26 | @SuppressWarnings("FieldCanBeLocal") 27 | class CirclePopUpWindow extends SettingPopUpWindow 28 | implements SeekBar.OnSeekBarChangeListener, CompoundButton.OnCheckedChangeListener, 29 | RadioGroup.OnCheckedChangeListener { 30 | 31 | private CircleLayoutManager circleLayoutManager; 32 | private RecyclerView recyclerView; 33 | private TextView radiusValue; 34 | private TextView intervalValue; 35 | private TextView speedValue; 36 | private TextView distanceToBottomValue; 37 | private SwitchCompat infinite; 38 | private SwitchCompat autoCenter; 39 | private SwitchCompat reverse; 40 | private SwitchCompat flipRotate; 41 | private CenterSnapHelper centerSnapHelper; 42 | private RadioGroup gravity; 43 | private RadioGroup zAlignment; 44 | 45 | CirclePopUpWindow(Context context, CircleLayoutManager circleLayoutManager, RecyclerView recyclerView) { 46 | super(context); 47 | this.circleLayoutManager = circleLayoutManager; 48 | this.recyclerView = recyclerView; 49 | View view = LayoutInflater.from(context).inflate(R.layout.dialog_circle_setting, null); 50 | setContentView(view); 51 | 52 | centerSnapHelper = new CenterSnapHelper(); 53 | 54 | SeekBar radius = view.findViewById(R.id.sb_radius); 55 | SeekBar interval = view.findViewById(R.id.sb_interval); 56 | SeekBar speed = view.findViewById(R.id.sb_speed); 57 | SeekBar distanceToBottom = view.findViewById(R.id.sb_distance_to_bottom); 58 | 59 | radiusValue = view.findViewById(R.id.radius_value); 60 | intervalValue = view.findViewById(R.id.interval_value); 61 | speedValue = view.findViewById(R.id.speed_value); 62 | distanceToBottomValue = view.findViewById(R.id.distance_to_bottom_value); 63 | 64 | infinite = view.findViewById(R.id.s_infinite); 65 | autoCenter = view.findViewById(R.id.s_auto_center); 66 | reverse = view.findViewById(R.id.s_reverse); 67 | flipRotate = view.findViewById(R.id.s_flip); 68 | 69 | gravity = view.findViewById(R.id.rg_gravity); 70 | zAlignment = view.findViewById(R.id.rg_z_alignment); 71 | 72 | radius.setOnSeekBarChangeListener(this); 73 | interval.setOnSeekBarChangeListener(this); 74 | speed.setOnSeekBarChangeListener(this); 75 | distanceToBottom.setOnSeekBarChangeListener(this); 76 | 77 | final int maxRadius = Util.Dp2px(radius.getContext(), 400); 78 | radius.setProgress(Math.round(circleLayoutManager.getRadius() * 1f / maxRadius * 100)); 79 | interval.setProgress(Math.round(circleLayoutManager.getAngleInterval() / 0.9f)); 80 | speed.setProgress(Math.round(circleLayoutManager.getMoveSpeed() / 0.005f)); 81 | distanceToBottom.setProgress(circleLayoutManager.getDistanceToBottom() / 10); 82 | 83 | radiusValue.setText(String.valueOf(circleLayoutManager.getRadius())); 84 | intervalValue.setText(String.valueOf(circleLayoutManager.getAngleInterval())); 85 | speedValue.setText(Util.formatFloat(circleLayoutManager.getMoveSpeed())); 86 | distanceToBottomValue.setText(String.valueOf(circleLayoutManager.getDistanceToBottom())); 87 | 88 | infinite.setChecked(circleLayoutManager.getInfinite()); 89 | reverse.setChecked(circleLayoutManager.getReverseLayout()); 90 | flipRotate.setChecked(circleLayoutManager.getFlipRotate()); 91 | 92 | infinite.setOnCheckedChangeListener(this); 93 | autoCenter.setOnCheckedChangeListener(this); 94 | reverse.setOnCheckedChangeListener(this); 95 | flipRotate.setOnCheckedChangeListener(this); 96 | 97 | switch (circleLayoutManager.getGravity()) { 98 | case CircleLayoutManager.LEFT: 99 | gravity.check(R.id.rb_left); 100 | break; 101 | case CircleLayoutManager.RIGHT: 102 | gravity.check(R.id.rb_right); 103 | break; 104 | case CircleLayoutManager.TOP: 105 | gravity.check(R.id.rb_top); 106 | break; 107 | case CircleLayoutManager.BOTTOM: 108 | gravity.check(R.id.rb_bottom); 109 | break; 110 | } 111 | 112 | switch (circleLayoutManager.getZAlignment()) { 113 | case CircleLayoutManager.LEFT_ON_TOP: 114 | zAlignment.check(R.id.rb_left_on_top); 115 | break; 116 | case CircleLayoutManager.RIGHT_ON_TOP: 117 | zAlignment.check(R.id.rb_right_on_top); 118 | break; 119 | case CircleLayoutManager.CENTER_ON_TOP: 120 | zAlignment.check(R.id.rb_center_on_top); 121 | break; 122 | } 123 | 124 | gravity.setOnCheckedChangeListener(this); 125 | zAlignment.setOnCheckedChangeListener(this); 126 | } 127 | 128 | @Override 129 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 130 | switch (seekBar.getId()) { 131 | case R.id.sb_radius: 132 | final int maxRadius = Util.Dp2px(seekBar.getContext(), 400); 133 | final int radius = Math.round(progress / 100f * maxRadius); 134 | circleLayoutManager.setRadius(radius); 135 | radiusValue.setText(String.valueOf(radius)); 136 | break; 137 | case R.id.sb_interval: 138 | final int interval = Math.round(progress * 0.9f); 139 | circleLayoutManager.setAngleInterval(interval); 140 | intervalValue.setText(String.valueOf(interval)); 141 | break; 142 | case R.id.sb_speed: 143 | final float speed = progress * 0.005f; 144 | circleLayoutManager.setMoveSpeed(speed); 145 | speedValue.setText(Util.formatFloat(speed)); 146 | break; 147 | case R.id.sb_distance_to_bottom: 148 | final int distance = progress * 10; 149 | circleLayoutManager.setDistanceToBottom(distance); 150 | distanceToBottomValue.setText(String.valueOf(distance)); 151 | break; 152 | } 153 | } 154 | 155 | @Override 156 | public void onStartTrackingTouch(SeekBar seekBar) { 157 | 158 | } 159 | 160 | @Override 161 | public void onStopTrackingTouch(SeekBar seekBar) { 162 | 163 | } 164 | 165 | @Override 166 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 167 | switch (buttonView.getId()) { 168 | case R.id.s_infinite: 169 | recyclerView.scrollToPosition(0); 170 | circleLayoutManager.setInfinite(isChecked); 171 | break; 172 | case R.id.s_auto_center: 173 | if (isChecked) { 174 | centerSnapHelper.attachToRecyclerView(recyclerView); 175 | } else { 176 | centerSnapHelper.attachToRecyclerView(null); 177 | } 178 | break; 179 | case R.id.s_reverse: 180 | circleLayoutManager.scrollToPosition(0); 181 | circleLayoutManager.setReverseLayout(isChecked); 182 | break; 183 | case R.id.s_flip: 184 | circleLayoutManager.setFlipRotate(isChecked); 185 | break; 186 | } 187 | } 188 | 189 | @Override 190 | public void onCheckedChanged(RadioGroup group, int checkedId) { 191 | switch (checkedId) { 192 | case R.id.rb_left: 193 | circleLayoutManager.setGravity(CircleLayoutManager.LEFT); 194 | break; 195 | case R.id.rb_right: 196 | circleLayoutManager.setGravity(CircleLayoutManager.RIGHT); 197 | break; 198 | case R.id.rb_top: 199 | circleLayoutManager.setGravity(CircleLayoutManager.TOP); 200 | break; 201 | case R.id.rb_bottom: 202 | circleLayoutManager.setGravity(CircleLayoutManager.BOTTOM); 203 | break; 204 | case R.id.rb_left_on_top: 205 | circleLayoutManager.setZAlignment(CircleLayoutManager.LEFT_ON_TOP); 206 | break; 207 | case R.id.rb_right_on_top: 208 | circleLayoutManager.setZAlignment(CircleLayoutManager.RIGHT_ON_TOP); 209 | break; 210 | case R.id.rb_center_on_top: 211 | circleLayoutManager.setZAlignment(CircleLayoutManager.CENTER_ON_TOP); 212 | break; 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/circlescale/CircleScaleLayoutActivity.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager.circlescale; 2 | 3 | import com.leochuan.CircleScaleLayoutManager; 4 | 5 | import rouchuan.viewpagerlayoutmanager.BaseActivity; 6 | 7 | /** 8 | * Created by Dajavu on 27/10/2017. 9 | */ 10 | 11 | public class CircleScaleLayoutActivity extends BaseActivity { 12 | 13 | @Override 14 | protected CircleScaleLayoutManager createLayoutManager() { 15 | return new CircleScaleLayoutManager(this); 16 | } 17 | 18 | @Override 19 | protected CircleScalePopUpWindow createSettingPopUpWindow() { 20 | return new CircleScalePopUpWindow(this, getViewPagerLayoutManager(), getRecyclerView()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/circlescale/CircleScalePopUpWindow.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager.circlescale; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.support.v7.widget.SwitchCompat; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.widget.CompoundButton; 10 | import android.widget.RadioGroup; 11 | import android.widget.SeekBar; 12 | import android.widget.TextView; 13 | 14 | import com.leochuan.CenterSnapHelper; 15 | import com.leochuan.CircleScaleLayoutManager; 16 | 17 | import rouchuan.viewpagerlayoutmanager.R; 18 | import rouchuan.viewpagerlayoutmanager.SettingPopUpWindow; 19 | import rouchuan.viewpagerlayoutmanager.Util; 20 | 21 | /** 22 | * Created by Dajavu on 27/10/2017. 23 | */ 24 | 25 | @SuppressLint("InflateParams") 26 | @SuppressWarnings("FieldCanBeLocal") 27 | public class CircleScalePopUpWindow extends SettingPopUpWindow 28 | implements SeekBar.OnSeekBarChangeListener, CompoundButton.OnCheckedChangeListener, 29 | RadioGroup.OnCheckedChangeListener { 30 | 31 | private CircleScaleLayoutManager circleScaleLayoutManager; 32 | private RecyclerView recyclerView; 33 | private TextView radiusValue; 34 | private TextView intervalValue; 35 | private TextView speedValue; 36 | private TextView centerScaleValue; 37 | private SwitchCompat infinite; 38 | private SwitchCompat autoCenter; 39 | private SwitchCompat reverse; 40 | private SwitchCompat flipRotate; 41 | private CenterSnapHelper centerSnapHelper; 42 | private RadioGroup gravity; 43 | private RadioGroup zAlignment; 44 | 45 | CircleScalePopUpWindow(Context context, CircleScaleLayoutManager circleScaleLayoutManager, RecyclerView recyclerView) { 46 | super(context); 47 | this.circleScaleLayoutManager = circleScaleLayoutManager; 48 | this.recyclerView = recyclerView; 49 | View view = LayoutInflater.from(context).inflate(R.layout.dialog_circle_scale_setting, null); 50 | setContentView(view); 51 | 52 | centerSnapHelper = new CenterSnapHelper(); 53 | 54 | SeekBar radius = view.findViewById(R.id.sb_radius); 55 | SeekBar interval = view.findViewById(R.id.sb_interval); 56 | SeekBar speed = view.findViewById(R.id.sb_speed); 57 | SeekBar centerScale = view.findViewById(R.id.sb_center_scale); 58 | 59 | radiusValue = view.findViewById(R.id.radius_value); 60 | intervalValue = view.findViewById(R.id.interval_value); 61 | speedValue = view.findViewById(R.id.speed_value); 62 | centerScaleValue = view.findViewById(R.id.center_scale_value); 63 | 64 | infinite = view.findViewById(R.id.s_infinite); 65 | autoCenter = view.findViewById(R.id.s_auto_center); 66 | reverse = view.findViewById(R.id.s_reverse); 67 | flipRotate = view.findViewById(R.id.s_flip); 68 | 69 | gravity = view.findViewById(R.id.rg_gravity); 70 | zAlignment = view.findViewById(R.id.rg_z_alignment); 71 | 72 | radius.setOnSeekBarChangeListener(this); 73 | interval.setOnSeekBarChangeListener(this); 74 | speed.setOnSeekBarChangeListener(this); 75 | centerScale.setOnSeekBarChangeListener(this); 76 | 77 | final int maxRadius = Util.Dp2px(radius.getContext(), 400); 78 | radius.setProgress(Math.round(circleScaleLayoutManager.getRadius() * 1f / maxRadius * 100)); 79 | interval.setProgress(Math.round(circleScaleLayoutManager.getAngleInterval() / 0.9f)); 80 | speed.setProgress(Math.round(circleScaleLayoutManager.getMoveSpeed() / 0.005f)); 81 | centerScale.setProgress(Math.round(circleScaleLayoutManager.getCenterScale() * 200f / 3 - 100f / 3)); 82 | 83 | radiusValue.setText(String.valueOf(circleScaleLayoutManager.getRadius())); 84 | intervalValue.setText(String.valueOf(circleScaleLayoutManager.getAngleInterval())); 85 | speedValue.setText(Util.formatFloat(circleScaleLayoutManager.getMoveSpeed())); 86 | centerScaleValue.setText(Util.formatFloat(circleScaleLayoutManager.getCenterScale())); 87 | 88 | infinite.setChecked(circleScaleLayoutManager.getInfinite()); 89 | reverse.setChecked(circleScaleLayoutManager.getReverseLayout()); 90 | flipRotate.setChecked(circleScaleLayoutManager.getFlipRotate()); 91 | 92 | infinite.setOnCheckedChangeListener(this); 93 | autoCenter.setOnCheckedChangeListener(this); 94 | reverse.setOnCheckedChangeListener(this); 95 | flipRotate.setOnCheckedChangeListener(this); 96 | 97 | switch (circleScaleLayoutManager.getGravity()) { 98 | case CircleScaleLayoutManager.LEFT: 99 | gravity.check(R.id.rb_left); 100 | break; 101 | case CircleScaleLayoutManager.RIGHT: 102 | gravity.check(R.id.rb_right); 103 | break; 104 | case CircleScaleLayoutManager.TOP: 105 | gravity.check(R.id.rb_top); 106 | break; 107 | case CircleScaleLayoutManager.BOTTOM: 108 | gravity.check(R.id.rb_bottom); 109 | break; 110 | } 111 | 112 | switch (circleScaleLayoutManager.getZAlignment()) { 113 | case CircleScaleLayoutManager.LEFT_ON_TOP: 114 | zAlignment.check(R.id.rb_left_on_top); 115 | break; 116 | case CircleScaleLayoutManager.RIGHT_ON_TOP: 117 | zAlignment.check(R.id.rb_right_on_top); 118 | break; 119 | case CircleScaleLayoutManager.CENTER_ON_TOP: 120 | zAlignment.check(R.id.rb_center_on_top); 121 | break; 122 | } 123 | 124 | gravity.setOnCheckedChangeListener(this); 125 | zAlignment.setOnCheckedChangeListener(this); 126 | } 127 | 128 | @Override 129 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 130 | switch (seekBar.getId()) { 131 | case R.id.sb_radius: 132 | final int maxRadius = Util.Dp2px(seekBar.getContext(), 400); 133 | final int radius = Math.round(progress / 100f * maxRadius); 134 | circleScaleLayoutManager.setRadius(radius); 135 | radiusValue.setText(String.valueOf(radius)); 136 | break; 137 | case R.id.sb_interval: 138 | final int interval = Math.round(progress * 0.9f); 139 | circleScaleLayoutManager.setAngleInterval(interval); 140 | intervalValue.setText(String.valueOf(interval)); 141 | break; 142 | case R.id.sb_center_scale: 143 | final float scale = (progress + 100f / 3) * 3 / 200; 144 | circleScaleLayoutManager.setCenterScale(scale); 145 | centerScaleValue.setText(Util.formatFloat(scale)); 146 | break; 147 | case R.id.sb_speed: 148 | final float speed = progress * 0.005f; 149 | circleScaleLayoutManager.setMoveSpeed(speed); 150 | speedValue.setText(Util.formatFloat(speed)); 151 | break; 152 | } 153 | } 154 | 155 | @Override 156 | public void onStartTrackingTouch(SeekBar seekBar) { 157 | 158 | } 159 | 160 | @Override 161 | public void onStopTrackingTouch(SeekBar seekBar) { 162 | 163 | } 164 | 165 | @Override 166 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 167 | switch (buttonView.getId()) { 168 | case R.id.s_infinite: 169 | recyclerView.scrollToPosition(0); 170 | circleScaleLayoutManager.setInfinite(isChecked); 171 | break; 172 | case R.id.s_auto_center: 173 | if (isChecked) { 174 | centerSnapHelper.attachToRecyclerView(recyclerView); 175 | } else { 176 | centerSnapHelper.attachToRecyclerView(null); 177 | } 178 | break; 179 | case R.id.s_reverse: 180 | circleScaleLayoutManager.scrollToPosition(0); 181 | circleScaleLayoutManager.setReverseLayout(isChecked); 182 | break; 183 | case R.id.s_flip: 184 | circleScaleLayoutManager.setFlipRotate(isChecked); 185 | break; 186 | } 187 | } 188 | 189 | @Override 190 | public void onCheckedChanged(RadioGroup group, int checkedId) { 191 | switch (checkedId) { 192 | case R.id.rb_left: 193 | circleScaleLayoutManager.setGravity(CircleScaleLayoutManager.LEFT); 194 | break; 195 | case R.id.rb_right: 196 | circleScaleLayoutManager.setGravity(CircleScaleLayoutManager.RIGHT); 197 | break; 198 | case R.id.rb_top: 199 | circleScaleLayoutManager.setGravity(CircleScaleLayoutManager.TOP); 200 | break; 201 | case R.id.rb_bottom: 202 | circleScaleLayoutManager.setGravity(CircleScaleLayoutManager.BOTTOM); 203 | break; 204 | case R.id.rb_left_on_top: 205 | circleScaleLayoutManager.setZAlignment(CircleScaleLayoutManager.LEFT_ON_TOP); 206 | break; 207 | case R.id.rb_right_on_top: 208 | circleScaleLayoutManager.setZAlignment(CircleScaleLayoutManager.RIGHT_ON_TOP); 209 | break; 210 | case R.id.rb_center_on_top: 211 | circleScaleLayoutManager.setZAlignment(CircleScaleLayoutManager.CENTER_ON_TOP); 212 | break; 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/gallery/GalleryLayoutActivity.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager.gallery; 2 | 3 | import com.leochuan.GalleryLayoutManager; 4 | 5 | import rouchuan.viewpagerlayoutmanager.BaseActivity; 6 | import rouchuan.viewpagerlayoutmanager.Util; 7 | 8 | /** 9 | * Created by Dajavu on 27/10/2017. 10 | */ 11 | 12 | public class GalleryLayoutActivity extends BaseActivity { 13 | 14 | @Override 15 | protected GalleryLayoutManager createLayoutManager() { 16 | return new GalleryLayoutManager(this, Util.Dp2px(this, 10)); 17 | } 18 | 19 | @Override 20 | protected GalleryPopUpWindow createSettingPopUpWindow() { 21 | return new GalleryPopUpWindow(this, getViewPagerLayoutManager(), getRecyclerView()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/gallery/GalleryPopUpWindow.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager.gallery; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.support.v7.widget.SwitchCompat; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.widget.CompoundButton; 10 | import android.widget.SeekBar; 11 | import android.widget.TextView; 12 | 13 | import com.leochuan.CenterSnapHelper; 14 | import com.leochuan.GalleryLayoutManager; 15 | import com.leochuan.ViewPagerLayoutManager; 16 | 17 | import rouchuan.viewpagerlayoutmanager.R; 18 | import rouchuan.viewpagerlayoutmanager.SettingPopUpWindow; 19 | import rouchuan.viewpagerlayoutmanager.Util; 20 | 21 | /** 22 | * Created by Dajavu on 27/10/2017. 23 | */ 24 | 25 | @SuppressLint("InflateParams") 26 | @SuppressWarnings("FieldCanBeLocal") 27 | public class GalleryPopUpWindow extends SettingPopUpWindow 28 | implements SeekBar.OnSeekBarChangeListener, CompoundButton.OnCheckedChangeListener { 29 | 30 | private GalleryLayoutManager galleryLayoutManager; 31 | private RecyclerView recyclerView; 32 | private TextView itemSpaceValue; 33 | private TextView speedValue; 34 | private TextView minAlphaValue; 35 | private TextView maxAlphaValue; 36 | private TextView angleValue; 37 | private SwitchCompat centerInFront; 38 | private SwitchCompat changeOrientation; 39 | private SwitchCompat autoCenter; 40 | private SwitchCompat infinite; 41 | private SwitchCompat reverse; 42 | private SwitchCompat flipRotate; 43 | private SwitchCompat rotateFromEdge; 44 | private CenterSnapHelper centerSnapHelper; 45 | 46 | GalleryPopUpWindow(Context context, GalleryLayoutManager galleryLayoutManager, RecyclerView recyclerView) { 47 | super(context); 48 | this.galleryLayoutManager = galleryLayoutManager; 49 | this.recyclerView = recyclerView; 50 | View view = LayoutInflater.from(context).inflate(R.layout.dialog_gallery_setting, null); 51 | setContentView(view); 52 | 53 | centerSnapHelper = new CenterSnapHelper(); 54 | 55 | SeekBar itemSpace = view.findViewById(R.id.sb_item_space); 56 | SeekBar speed = view.findViewById(R.id.sb_speed); 57 | SeekBar minAlpha = view.findViewById(R.id.sb_min_alpha); 58 | SeekBar maxAlpha = view.findViewById(R.id.sb_max_alpha); 59 | SeekBar angle = view.findViewById(R.id.sb_interval); 60 | 61 | itemSpaceValue = view.findViewById(R.id.item_space); 62 | speedValue = view.findViewById(R.id.speed_value); 63 | minAlphaValue = view.findViewById(R.id.min_alpha_value); 64 | maxAlphaValue = view.findViewById(R.id.max_alpha_value); 65 | angleValue = view.findViewById(R.id.angle_value); 66 | 67 | centerInFront = view.findViewById(R.id.s_center_in_front); 68 | changeOrientation = view.findViewById(R.id.s_change_orientation); 69 | autoCenter = view.findViewById(R.id.s_auto_center); 70 | infinite = view.findViewById(R.id.s_infinite); 71 | reverse = view.findViewById(R.id.s_reverse); 72 | flipRotate = view.findViewById(R.id.s_flip); 73 | rotateFromEdge = view.findViewById(R.id.s_rotate_from_edge); 74 | 75 | itemSpace.setOnSeekBarChangeListener(this); 76 | speed.setOnSeekBarChangeListener(this); 77 | minAlpha.setOnSeekBarChangeListener(this); 78 | maxAlpha.setOnSeekBarChangeListener(this); 79 | angle.setOnSeekBarChangeListener(this); 80 | 81 | itemSpace.setProgress(galleryLayoutManager.getItemSpace() / 8 + 50); 82 | speed.setProgress(Math.round(galleryLayoutManager.getMoveSpeed() / 0.05f)); 83 | maxAlpha.setProgress(Math.round(galleryLayoutManager.getMaxAlpha() * 100)); 84 | minAlpha.setProgress(Math.round(galleryLayoutManager.getMinAlpha() * 100)); 85 | angle.setProgress(Math.round(galleryLayoutManager.getAngle() / 0.9f)); 86 | 87 | itemSpaceValue.setText(String.valueOf(galleryLayoutManager.getItemSpace())); 88 | speedValue.setText(Util.formatFloat(galleryLayoutManager.getMoveSpeed())); 89 | minAlphaValue.setText(Util.formatFloat(galleryLayoutManager.getMinAlpha())); 90 | maxAlphaValue.setText(Util.formatFloat(galleryLayoutManager.getMaxAlpha())); 91 | angleValue.setText(Util.formatFloat(galleryLayoutManager.getAngle())); 92 | 93 | centerInFront.setChecked(galleryLayoutManager.getEnableBringCenterToFront()); 94 | changeOrientation.setChecked(galleryLayoutManager.getOrientation() == ViewPagerLayoutManager.VERTICAL); 95 | reverse.setChecked(galleryLayoutManager.getReverseLayout()); 96 | flipRotate.setChecked(galleryLayoutManager.getFlipRotate()); 97 | rotateFromEdge.setChecked(galleryLayoutManager.getRotateFromEdge()); 98 | infinite.setChecked(galleryLayoutManager.getInfinite()); 99 | 100 | centerInFront.setOnCheckedChangeListener(this); 101 | changeOrientation.setOnCheckedChangeListener(this); 102 | autoCenter.setOnCheckedChangeListener(this); 103 | reverse.setOnCheckedChangeListener(this); 104 | flipRotate.setOnCheckedChangeListener(this); 105 | rotateFromEdge.setOnCheckedChangeListener(this); 106 | infinite.setOnCheckedChangeListener(this); 107 | } 108 | 109 | @Override 110 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 111 | switch (seekBar.getId()) { 112 | case R.id.sb_item_space: 113 | int itemSpace = (progress - 50) * 8; 114 | galleryLayoutManager.setItemSpace(itemSpace); 115 | itemSpaceValue.setText(String.valueOf(itemSpace)); 116 | break; 117 | case R.id.sb_speed: 118 | final float speed = progress * 0.05f; 119 | galleryLayoutManager.setMoveSpeed(speed); 120 | speedValue.setText(Util.formatFloat(speed)); 121 | break; 122 | case R.id.sb_interval: 123 | final int angle = Math.round(progress * 0.9f); 124 | galleryLayoutManager.setAngle(angle); 125 | angleValue.setText(String.valueOf(angle)); 126 | break; 127 | case R.id.sb_max_alpha: 128 | final float maxAlpha = progress / 100f; 129 | galleryLayoutManager.setMaxAlpha(maxAlpha); 130 | maxAlphaValue.setText(Util.formatFloat(maxAlpha)); 131 | break; 132 | case R.id.sb_min_alpha: 133 | final float minAlpha = progress / 100f; 134 | galleryLayoutManager.setMinAlpha(minAlpha); 135 | minAlphaValue.setText(Util.formatFloat(minAlpha)); 136 | break; 137 | } 138 | } 139 | 140 | @Override 141 | public void onStartTrackingTouch(SeekBar seekBar) { 142 | 143 | } 144 | 145 | @Override 146 | public void onStopTrackingTouch(SeekBar seekBar) { 147 | 148 | } 149 | 150 | @Override 151 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 152 | switch (buttonView.getId()) { 153 | case R.id.s_infinite: 154 | recyclerView.scrollToPosition(0); 155 | galleryLayoutManager.setInfinite(isChecked); 156 | break; 157 | case R.id.s_change_orientation: 158 | galleryLayoutManager.scrollToPosition(0); 159 | galleryLayoutManager.setOrientation(isChecked ? 160 | ViewPagerLayoutManager.VERTICAL : ViewPagerLayoutManager.HORIZONTAL); 161 | break; 162 | case R.id.s_auto_center: 163 | if (isChecked) { 164 | centerSnapHelper.attachToRecyclerView(recyclerView); 165 | } else { 166 | centerSnapHelper.attachToRecyclerView(null); 167 | } 168 | break; 169 | case R.id.s_reverse: 170 | galleryLayoutManager.scrollToPosition(0); 171 | galleryLayoutManager.setReverseLayout(isChecked); 172 | break; 173 | case R.id.s_flip: 174 | galleryLayoutManager.setFlipRotate(isChecked); 175 | case R.id.s_center_in_front: 176 | galleryLayoutManager.setEnableBringCenterToFront(isChecked); 177 | break; 178 | case R.id.s_rotate_from_edge: 179 | galleryLayoutManager.setRotateFromEdge(isChecked); 180 | break; 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/rotate/RotateLayoutActivity.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager.rotate; 2 | 3 | import com.leochuan.RotateLayoutManager; 4 | 5 | import rouchuan.viewpagerlayoutmanager.BaseActivity; 6 | import rouchuan.viewpagerlayoutmanager.Util; 7 | 8 | /** 9 | * Created by Dajavu on 27/10/2017. 10 | */ 11 | 12 | public class RotateLayoutActivity extends BaseActivity { 13 | 14 | @Override 15 | protected RotateLayoutManager createLayoutManager() { 16 | return new RotateLayoutManager(this, Util.Dp2px(this, 10)); 17 | } 18 | 19 | @Override 20 | protected RotatePopUpWindow createSettingPopUpWindow() { 21 | return new RotatePopUpWindow(this, getViewPagerLayoutManager(), getRecyclerView()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/rotate/RotatePopUpWindow.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager.rotate; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.support.v7.widget.SwitchCompat; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.widget.CompoundButton; 10 | import android.widget.SeekBar; 11 | import android.widget.TextView; 12 | 13 | import com.leochuan.CenterSnapHelper; 14 | import com.leochuan.RotateLayoutManager; 15 | import com.leochuan.ViewPagerLayoutManager; 16 | 17 | import rouchuan.viewpagerlayoutmanager.R; 18 | import rouchuan.viewpagerlayoutmanager.SettingPopUpWindow; 19 | import rouchuan.viewpagerlayoutmanager.Util; 20 | 21 | /** 22 | * Created by Dajavu on 27/10/2017. 23 | */ 24 | 25 | @SuppressLint("InflateParams") 26 | @SuppressWarnings("FieldCanBeLocal") 27 | public class RotatePopUpWindow extends SettingPopUpWindow 28 | implements SeekBar.OnSeekBarChangeListener, CompoundButton.OnCheckedChangeListener { 29 | 30 | private RotateLayoutManager rotateLayoutManager; 31 | private RecyclerView recyclerView; 32 | private TextView itemSpaceValue; 33 | private TextView speedValue; 34 | private TextView angleValue; 35 | private SwitchCompat changeOrientation; 36 | private SwitchCompat autoCenter; 37 | private SwitchCompat infinite; 38 | private SwitchCompat reverseRotate; 39 | private SwitchCompat reverse; 40 | private CenterSnapHelper centerSnapHelper; 41 | 42 | RotatePopUpWindow(Context context, RotateLayoutManager rotateLayoutManager, RecyclerView recyclerView) { 43 | super(context); 44 | this.rotateLayoutManager = rotateLayoutManager; 45 | this.recyclerView = recyclerView; 46 | View view = LayoutInflater.from(context).inflate(R.layout.dialog_rotate_setting, null); 47 | setContentView(view); 48 | 49 | centerSnapHelper = new CenterSnapHelper(); 50 | 51 | SeekBar itemSpace = view.findViewById(R.id.sb_item_space); 52 | SeekBar speed = view.findViewById(R.id.sb_speed); 53 | SeekBar angle = view.findViewById(R.id.sb_angle); 54 | 55 | itemSpaceValue = view.findViewById(R.id.item_space); 56 | speedValue = view.findViewById(R.id.speed_value); 57 | angleValue = view.findViewById(R.id.angle_value); 58 | 59 | reverseRotate = view.findViewById(R.id.s_reverse_rotate); 60 | changeOrientation = view.findViewById(R.id.s_change_orientation); 61 | autoCenter = view.findViewById(R.id.s_auto_center); 62 | infinite = view.findViewById(R.id.s_infinite); 63 | reverse = view.findViewById(R.id.s_reverse); 64 | 65 | itemSpace.setOnSeekBarChangeListener(this); 66 | speed.setOnSeekBarChangeListener(this); 67 | angle.setOnSeekBarChangeListener(this); 68 | 69 | itemSpace.setProgress(rotateLayoutManager.getItemSpace() / 2); 70 | speed.setProgress(Math.round(rotateLayoutManager.getMoveSpeed() / 0.05f)); 71 | angle.setProgress(Math.round(rotateLayoutManager.getAngle() / 360 * 100)); 72 | 73 | itemSpaceValue.setText(String.valueOf(rotateLayoutManager.getItemSpace())); 74 | speedValue.setText(Util.formatFloat(rotateLayoutManager.getMoveSpeed())); 75 | angleValue.setText(Util.formatFloat(rotateLayoutManager.getAngle())); 76 | 77 | reverseRotate.setChecked(rotateLayoutManager.getEnableBringCenterToFront()); 78 | changeOrientation.setChecked(rotateLayoutManager.getOrientation() == ViewPagerLayoutManager.VERTICAL); 79 | reverse.setChecked(rotateLayoutManager.getReverseLayout()); 80 | infinite.setChecked(rotateLayoutManager.getInfinite()); 81 | 82 | reverseRotate.setOnCheckedChangeListener(this); 83 | changeOrientation.setOnCheckedChangeListener(this); 84 | autoCenter.setOnCheckedChangeListener(this); 85 | reverse.setOnCheckedChangeListener(this); 86 | infinite.setOnCheckedChangeListener(this); 87 | } 88 | 89 | @Override 90 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 91 | switch (seekBar.getId()) { 92 | case R.id.sb_item_space: 93 | int itemSpace = progress * 2; 94 | rotateLayoutManager.setItemSpace(itemSpace); 95 | itemSpaceValue.setText(String.valueOf(itemSpace)); 96 | break; 97 | case R.id.sb_angle: 98 | final float angle = progress / 100f * 360; 99 | rotateLayoutManager.setAngle(angle); 100 | angleValue.setText(Util.formatFloat(angle)); 101 | break; 102 | case R.id.sb_speed: 103 | final float speed = progress * 0.05f; 104 | rotateLayoutManager.setMoveSpeed(speed); 105 | speedValue.setText(Util.formatFloat(speed)); 106 | break; 107 | } 108 | } 109 | 110 | @Override 111 | public void onStartTrackingTouch(SeekBar seekBar) { 112 | 113 | } 114 | 115 | @Override 116 | public void onStopTrackingTouch(SeekBar seekBar) { 117 | 118 | } 119 | 120 | @Override 121 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 122 | switch (buttonView.getId()) { 123 | case R.id.s_infinite: 124 | recyclerView.scrollToPosition(0); 125 | rotateLayoutManager.setInfinite(isChecked); 126 | break; 127 | case R.id.s_change_orientation: 128 | rotateLayoutManager.scrollToPosition(0); 129 | rotateLayoutManager.setOrientation(isChecked ? 130 | ViewPagerLayoutManager.VERTICAL : ViewPagerLayoutManager.HORIZONTAL); 131 | break; 132 | case R.id.s_auto_center: 133 | if (isChecked) { 134 | centerSnapHelper.attachToRecyclerView(recyclerView); 135 | } else { 136 | centerSnapHelper.attachToRecyclerView(null); 137 | } 138 | break; 139 | case R.id.s_reverse_rotate: 140 | rotateLayoutManager.setReverseRotate(isChecked); 141 | break; 142 | case R.id.s_reverse: 143 | rotateLayoutManager.scrollToPosition(0); 144 | rotateLayoutManager.setReverseLayout(isChecked); 145 | break; 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/scale/ScaleLayoutActivity.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager.scale; 2 | 3 | import com.leochuan.ScaleLayoutManager; 4 | 5 | import rouchuan.viewpagerlayoutmanager.BaseActivity; 6 | import rouchuan.viewpagerlayoutmanager.Util; 7 | 8 | /** 9 | * Created by Dajavu on 27/10/2017. 10 | */ 11 | 12 | public class ScaleLayoutActivity extends BaseActivity { 13 | 14 | @Override 15 | protected ScaleLayoutManager createLayoutManager() { 16 | return new ScaleLayoutManager(this, Util.Dp2px(this, 10)); 17 | } 18 | 19 | @Override 20 | protected ScalePopUpWindow createSettingPopUpWindow() { 21 | return new ScalePopUpWindow(this, getViewPagerLayoutManager(), getRecyclerView()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/rouchuan/viewpagerlayoutmanager/scale/ScalePopUpWindow.java: -------------------------------------------------------------------------------- 1 | package rouchuan.viewpagerlayoutmanager.scale; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.support.v7.widget.SwitchCompat; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.widget.CompoundButton; 10 | import android.widget.SeekBar; 11 | import android.widget.TextView; 12 | 13 | import com.leochuan.CenterSnapHelper; 14 | import com.leochuan.ScaleLayoutManager; 15 | import com.leochuan.ViewPagerLayoutManager; 16 | 17 | import rouchuan.viewpagerlayoutmanager.R; 18 | import rouchuan.viewpagerlayoutmanager.SettingPopUpWindow; 19 | import rouchuan.viewpagerlayoutmanager.Util; 20 | 21 | /** 22 | * Created by Dajavu on 27/10/2017. 23 | */ 24 | 25 | @SuppressLint("InflateParams") 26 | @SuppressWarnings("FieldCanBeLocal") 27 | public class ScalePopUpWindow extends SettingPopUpWindow 28 | implements SeekBar.OnSeekBarChangeListener, CompoundButton.OnCheckedChangeListener { 29 | 30 | private ScaleLayoutManager scaleLayoutManager; 31 | private RecyclerView recyclerView; 32 | private TextView itemSpaceValue; 33 | private TextView speedValue; 34 | private TextView minScaleValue; 35 | private TextView minAlphaValue; 36 | private TextView maxAlphaValue; 37 | private SwitchCompat changeOrientation; 38 | private SwitchCompat autoCenter; 39 | private SwitchCompat infinite; 40 | private SwitchCompat reverse; 41 | private CenterSnapHelper centerSnapHelper; 42 | 43 | ScalePopUpWindow(Context context, ScaleLayoutManager scaleLayoutManager, RecyclerView recyclerView) { 44 | super(context); 45 | this.scaleLayoutManager = scaleLayoutManager; 46 | this.recyclerView = recyclerView; 47 | View view = LayoutInflater.from(context).inflate(R.layout.dialog_scale_setting, null); 48 | setContentView(view); 49 | 50 | centerSnapHelper = new CenterSnapHelper(); 51 | 52 | SeekBar itemSpace = view.findViewById(R.id.sb_item_space); 53 | SeekBar speed = view.findViewById(R.id.sb_speed); 54 | SeekBar minScale = view.findViewById(R.id.sb_min_scale); 55 | SeekBar minAlpha = view.findViewById(R.id.sb_min_alpha); 56 | SeekBar maxAlpha = view.findViewById(R.id.sb_max_alpha); 57 | 58 | itemSpaceValue = view.findViewById(R.id.item_space); 59 | speedValue = view.findViewById(R.id.speed_value); 60 | minScaleValue = view.findViewById(R.id.min_scale_value); 61 | minAlphaValue = view.findViewById(R.id.min_alpha_value); 62 | maxAlphaValue = view.findViewById(R.id.max_alpha_value); 63 | 64 | changeOrientation = view.findViewById(R.id.s_change_orientation); 65 | autoCenter = view.findViewById(R.id.s_auto_center); 66 | infinite = view.findViewById(R.id.s_infinite); 67 | reverse = view.findViewById(R.id.s_reverse); 68 | 69 | itemSpace.setOnSeekBarChangeListener(this); 70 | speed.setOnSeekBarChangeListener(this); 71 | minScale.setOnSeekBarChangeListener(this); 72 | minAlpha.setOnSeekBarChangeListener(this); 73 | maxAlpha.setOnSeekBarChangeListener(this); 74 | 75 | itemSpace.setProgress(scaleLayoutManager.getItemSpace() / 2); 76 | speed.setProgress(Math.round(scaleLayoutManager.getMoveSpeed() / 0.05f)); 77 | minScale.setProgress(Math.round((scaleLayoutManager.getMinScale() - 0.5f) * 200)); 78 | maxAlpha.setProgress(Math.round(scaleLayoutManager.getMaxAlpha() * 100)); 79 | minAlpha.setProgress(Math.round(scaleLayoutManager.getMinAlpha() * 100)); 80 | 81 | itemSpaceValue.setText(String.valueOf(scaleLayoutManager.getItemSpace())); 82 | speedValue.setText(Util.formatFloat(scaleLayoutManager.getMoveSpeed())); 83 | minScaleValue.setText(Util.formatFloat(scaleLayoutManager.getMinScale())); 84 | minAlphaValue.setText(Util.formatFloat(scaleLayoutManager.getMinAlpha())); 85 | maxAlphaValue.setText(Util.formatFloat(scaleLayoutManager.getMaxAlpha())); 86 | 87 | changeOrientation.setChecked(scaleLayoutManager.getOrientation() == ViewPagerLayoutManager.VERTICAL); 88 | reverse.setChecked(scaleLayoutManager.getReverseLayout()); 89 | infinite.setChecked(scaleLayoutManager.getInfinite()); 90 | 91 | changeOrientation.setOnCheckedChangeListener(this); 92 | autoCenter.setOnCheckedChangeListener(this); 93 | reverse.setOnCheckedChangeListener(this); 94 | infinite.setOnCheckedChangeListener(this); 95 | } 96 | 97 | @Override 98 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 99 | switch (seekBar.getId()) { 100 | case R.id.sb_item_space: 101 | int itemSpace = progress * 2; 102 | scaleLayoutManager.setItemSpace(itemSpace); 103 | itemSpaceValue.setText(String.valueOf(itemSpace)); 104 | break; 105 | case R.id.sb_min_scale: 106 | final float scale = 0.5f + (progress / 200f); 107 | scaleLayoutManager.setMinScale(scale); 108 | minScaleValue.setText(Util.formatFloat(scale)); 109 | break; 110 | case R.id.sb_speed: 111 | final float speed = progress * 0.05f; 112 | scaleLayoutManager.setMoveSpeed(speed); 113 | speedValue.setText(Util.formatFloat(speed)); 114 | break; 115 | case R.id.sb_max_alpha: 116 | final float maxAlpha = progress / 100f; 117 | scaleLayoutManager.setMaxAlpha(maxAlpha); 118 | maxAlphaValue.setText(Util.formatFloat(maxAlpha)); 119 | break; 120 | case R.id.sb_min_alpha: 121 | final float minAlpha = progress / 100f; 122 | scaleLayoutManager.setMinAlpha(minAlpha); 123 | minAlphaValue.setText(Util.formatFloat(minAlpha)); 124 | break; 125 | } 126 | } 127 | 128 | @Override 129 | public void onStartTrackingTouch(SeekBar seekBar) { 130 | 131 | } 132 | 133 | @Override 134 | public void onStopTrackingTouch(SeekBar seekBar) { 135 | 136 | } 137 | 138 | @Override 139 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 140 | switch (buttonView.getId()) { 141 | case R.id.s_infinite: 142 | recyclerView.scrollToPosition(0); 143 | scaleLayoutManager.setInfinite(isChecked); 144 | break; 145 | case R.id.s_change_orientation: 146 | scaleLayoutManager.scrollToPosition(0); 147 | scaleLayoutManager.setOrientation(isChecked ? 148 | ViewPagerLayoutManager.VERTICAL : ViewPagerLayoutManager.HORIZONTAL); 149 | break; 150 | case R.id.s_auto_center: 151 | if (isChecked) { 152 | centerSnapHelper.attachToRecyclerView(recyclerView); 153 | } else { 154 | centerSnapHelper.attachToRecyclerView(null); 155 | } 156 | break; 157 | case R.id.s_reverse: 158 | scaleLayoutManager.scrollToPosition(0); 159 | scaleLayoutManager.setReverseLayout(isChecked); 160 | break; 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/item1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/drawable-xhdpi/item1.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/item10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/drawable-xhdpi/item10.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/item2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/drawable-xhdpi/item2.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/item3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/drawable-xhdpi/item3.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/item4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/drawable-xhdpi/item4.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/item5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/drawable-xhdpi/item5.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/item6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/drawable-xhdpi/item6.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/item7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/drawable-xhdpi/item7.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/item8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/drawable-xhdpi/item8.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/item9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/drawable-xhdpi/item9.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/drawable-xhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings_white_48px.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_base.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 19 | 20 | 27 | 28 | 35 | 36 | 43 | 44 | 51 | 52 | 59 | 60 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_carousel_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 21 | 22 | 28 | 29 | 30 | 34 | 35 | 39 | 40 | 45 | 46 | 52 | 53 | 54 | 58 | 59 | 63 | 64 | 69 | 70 | 76 | 77 | 78 | 82 | 83 | 87 | 88 | 93 | 94 | 99 | 100 | 101 | 105 | 106 | 111 | 112 | 117 | 118 | 119 | 123 | 124 | 129 | 130 | 135 | 136 | 137 | 141 | 142 | 147 | 148 | 153 | 154 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_circle_scale_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 16 | 17 | 22 | 23 | 29 | 30 | 31 | 35 | 36 | 40 | 41 | 46 | 47 | 53 | 54 | 55 | 59 | 60 | 64 | 65 | 70 | 71 | 77 | 78 | 79 | 83 | 84 | 88 | 89 | 94 | 95 | 101 | 102 | 103 | 107 | 108 | 112 | 113 | 118 | 119 | 124 | 125 | 126 | 130 | 131 | 136 | 137 | 142 | 143 | 144 | 148 | 149 | 154 | 155 | 160 | 161 | 162 | 166 | 167 | 172 | 173 | 178 | 179 | 180 | 186 | 187 | 192 | 193 | 201 | 202 | 210 | 211 | 219 | 220 | 228 | 229 | 230 | 236 | 237 | 242 | 243 | 251 | 252 | 260 | 261 | 269 | 270 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_circle_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 16 | 17 | 22 | 23 | 29 | 30 | 31 | 35 | 36 | 40 | 41 | 46 | 47 | 53 | 54 | 55 | 59 | 60 | 64 | 65 | 70 | 71 | 77 | 78 | 79 | 83 | 84 | 88 | 89 | 94 | 95 | 101 | 102 | 103 | 107 | 108 | 112 | 113 | 118 | 119 | 124 | 125 | 126 | 130 | 131 | 136 | 137 | 142 | 143 | 144 | 148 | 149 | 154 | 155 | 160 | 161 | 162 | 166 | 167 | 172 | 173 | 178 | 179 | 180 | 186 | 187 | 192 | 193 | 201 | 202 | 210 | 211 | 219 | 220 | 228 | 229 | 230 | 236 | 237 | 242 | 243 | 251 | 252 | 260 | 261 | 269 | 270 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_gallery_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 15 | 16 | 19 | 20 | 25 | 26 | 32 | 33 | 34 | 38 | 39 | 43 | 44 | 49 | 50 | 56 | 57 | 58 | 62 | 63 | 67 | 68 | 73 | 74 | 80 | 81 | 82 | 86 | 87 | 91 | 92 | 97 | 98 | 104 | 105 | 106 | 110 | 111 | 115 | 116 | 121 | 122 | 128 | 129 | 130 | 134 | 135 | 139 | 140 | 145 | 146 | 151 | 152 | 153 | 157 | 158 | 163 | 164 | 169 | 170 | 171 | 175 | 176 | 181 | 182 | 187 | 188 | 189 | 193 | 194 | 199 | 200 | 205 | 206 | 207 | 211 | 212 | 217 | 218 | 223 | 224 | 225 | 229 | 230 | 235 | 236 | 241 | 242 | 243 | 247 | 248 | 253 | 254 | 259 | 260 | 261 | 262 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_rotate_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 21 | 22 | 28 | 29 | 30 | 34 | 35 | 39 | 40 | 45 | 46 | 52 | 53 | 54 | 58 | 59 | 63 | 64 | 69 | 70 | 76 | 77 | 78 | 82 | 83 | 87 | 88 | 93 | 94 | 99 | 100 | 101 | 105 | 106 | 111 | 112 | 117 | 118 | 119 | 123 | 124 | 129 | 130 | 135 | 136 | 137 | 141 | 142 | 147 | 148 | 153 | 154 | 155 | 159 | 160 | 165 | 166 | 171 | 172 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_scale_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 21 | 22 | 28 | 29 | 30 | 34 | 35 | 39 | 40 | 45 | 46 | 52 | 53 | 54 | 58 | 59 | 63 | 64 | 69 | 70 | 76 | 77 | 78 | 82 | 83 | 87 | 88 | 93 | 94 | 100 | 101 | 102 | 106 | 107 | 111 | 112 | 117 | 118 | 124 | 125 | 126 | 130 | 131 | 135 | 136 | 141 | 142 | 147 | 148 | 149 | 153 | 154 | 159 | 160 | 165 | 166 | 167 | 171 | 172 | 177 | 178 | 183 | 184 | 185 | 189 | 190 | 195 | 196 | 201 | 202 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/menu/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | VPLM 3 | 设置 4 | Circle LayoutManager 5 | Circle Scale LayoutManager 6 | Carousel LayoutManager 7 | Gallery LayoutManager 8 | Rotate LayoutManager 9 | Scale LayoutManager 10 | 11 | 半径 12 | 间距 13 | 滚动速度 14 | 间隔角度 15 | 到底部距离 16 | 中心缩放比 17 | 旋转角度 18 | 最小缩放比 19 | 最大透明度 20 | 最小透明度 21 | 无限滚动 22 | 中心靠前排列 23 | 逆向旋转 24 | 自动归位 25 | 翻转排列 26 | 交换翻转方向 27 | 改变朝向 28 | 方向 29 | 靠左 30 | 靠右 31 | 靠上 32 | 靠下 33 | Z轴对齐方式 34 | 靠左在上 35 | 靠右在上 36 | 靠中间在上 37 | 绕底边旋转 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #0f88eb 4 | #0d79d1 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | VPLM 3 | Setting 4 | Circle LayoutManager 5 | Circle Scale LayoutManager 6 | Carousel LayoutManager 7 | Gallery LayoutManager 8 | Rotate LayoutManager 9 | Scale LayoutManager 10 | 11 | Radius 12 | Item Space 13 | Move Speed 14 | Distance To Bottom 15 | Interval Angle 16 | Center Scale 17 | Rotate Angle 18 | Min Scale 19 | Max Alpha 20 | Min Alpha 21 | Toggle Infinite 22 | Toggle Bring Center In Front 23 | Toggle Reverse Rotate 24 | Toggle Auto Center 25 | Toggle Reverse 26 | Flip Rotate 27 | Rotate From Edge 28 | Change Orientation 29 | Gravity 30 | Left 31 | Right 32 | Top 33 | Bottom 34 | Z-Alignment 35 | Left On Top 36 | Right On Top 37 | Center On Top 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.0.1' 10 | } 11 | } 12 | 13 | allprojects { 14 | repositories { 15 | jcenter() 16 | google() 17 | } 18 | } 19 | 20 | task clean(type: Delete) { 21 | delete rootProject.buildDir 22 | } 23 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | org.gradle.parallel=true 19 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Nov 03 15:58:04 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':viewpagerlayoutmanager' 2 | -------------------------------------------------------------------------------- /static/carousel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/static/carousel.jpg -------------------------------------------------------------------------------- /static/circle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/static/circle.jpg -------------------------------------------------------------------------------- /static/circle_scale.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/static/circle_scale.jpg -------------------------------------------------------------------------------- /static/customize.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/static/customize.gif -------------------------------------------------------------------------------- /static/gallery.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/static/gallery.jpg -------------------------------------------------------------------------------- /static/infinite.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/static/infinite.gif -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/static/logo.png -------------------------------------------------------------------------------- /static/rotate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/static/rotate.jpg -------------------------------------------------------------------------------- /static/scale.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leochuan/ViewPagerLayoutManager/0daaebd2bf2339168d05e60f529da114d532eead/static/scale.jpg -------------------------------------------------------------------------------- /viewpagerlayoutmanager/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /viewpagerlayoutmanager/build.gradle: -------------------------------------------------------------------------------- 1 | import groovy.json.JsonBuilder 2 | 3 | apply plugin: 'com.android.library' 4 | 5 | android { 6 | compileSdkVersion 26 7 | buildToolsVersion '26.0.2' 8 | 9 | defaultConfig { 10 | minSdkVersion 11 11 | targetSdkVersion 26 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | } 25 | 26 | dependencies { 27 | compileOnly 'com.android.support:recyclerview-v7:26.1.0' 28 | } 29 | 30 | apply plugin: 'maven' 31 | 32 | def currentVersion = '2.0.22' 33 | def versionDes = 'Fix bugs' 34 | def websiteUrl = 'https://github.com/leochuan/ViewPagerLayoutManager' 35 | def date = new Date() 36 | def projectName = 'viewpager-layout-manager' 37 | 38 | task createPom << { 39 | pom { 40 | project { 41 | groupId 'rouchuan.viewpagerlayoutmanager' 42 | artifactId 'viewpagerlayoutmanager' 43 | version currentVersion 44 | packaging 'aar' 45 | name projectName 46 | url websiteUrl 47 | licenses { 48 | license { 49 | name 'The Apache Software License, Version 2.0' 50 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 51 | } 52 | } 53 | developers { 54 | developer { 55 | id 'leochuan' 56 | name 'Shen Ruo Chuan' 57 | email 'ruochuan.leo@gmail.com' 58 | } 59 | } 60 | scm { 61 | connection "${websiteUrl}.git" 62 | developerConnection "${websiteUrl}.git" 63 | url websiteUrl 64 | } 65 | } 66 | }.writeTo("build/viewpagerlayoutmanager-${currentVersion}.pom") 67 | 68 | def builder = new JsonBuilder() 69 | def root = builder { 70 | 'package' { 71 | name projectName 72 | repo 'maven' 73 | subject 'leochuan' 74 | desc 'ViewPager like LayoutManager which supports some awesome animations and infinite scroll.' 75 | website_url websiteUrl 76 | issue_tracker_url "$websiteUrl/issues" 77 | vcs_url "${websiteUrl}.git" 78 | github_use_tag_release_notes true 79 | github_release_notes_file 'RELEASE_NOTE.md' 80 | labels "android", "layoutManager" 81 | public_download_numbers false 82 | public_stats false 83 | } 84 | 'version' { 85 | name currentVersion 86 | desc versionDes 87 | released date.format('yyyy-MM-dd') 88 | vcs_tag "v${currentVersion}" 89 | gpgSign false 90 | } 91 | publish true 92 | } 93 | root.files = [] 94 | root.files.add([ 95 | 'includePattern': 'viewpagerlayoutmanager/build/libs/(.*\\.jar)', 96 | 'uploadPattern' : "rouchuan/viewpagerlayoutmanager/viewpagerlayoutmanager/$currentVersion/\$1", 97 | 'matrixParams' : ['override': 1] 98 | ]) 99 | root.files.add([ 100 | 'includePattern': 'viewpagerlayoutmanager/build/(.*\\.pom)', 101 | 'uploadPattern' : "rouchuan/viewpagerlayoutmanager/viewpagerlayoutmanager/$currentVersion/\$1", 102 | 'matrixParams' : ['override': 1] 103 | ]) 104 | root.files.add([ 105 | 'includePattern': 'viewpagerlayoutmanager/build/outputs/aar/(.*release\\.aar)', 106 | 'uploadPattern' : "rouchuan/viewpagerlayoutmanager/viewpagerlayoutmanager/$currentVersion/viewpagerlayoutmanager-${currentVersion}.aar", 107 | 'matrixParams' : ['override': 1] 108 | ]) 109 | 110 | def jsonFile = new File("descriptor.json") 111 | jsonFile.write(builder.toPrettyString()) 112 | } 113 | 114 | task sourcesJar(type: Jar) { 115 | from android.sourceSets.main.java.srcDirs 116 | classifier = currentVersion 117 | } 118 | 119 | artifacts { 120 | archives sourcesJar 121 | } 122 | 123 | build.dependsOn('createPom') -------------------------------------------------------------------------------- /viewpagerlayoutmanager/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Dajavu/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /viewpagerlayoutmanager/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /viewpagerlayoutmanager/src/main/java/com/leochuan/AutoPlayRecyclerView.java: -------------------------------------------------------------------------------- 1 | package com.leochuan; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.util.AttributeSet; 8 | import android.view.MotionEvent; 9 | 10 | /** 11 | * An implement of {@link RecyclerView} which support auto play. 12 | */ 13 | 14 | public class AutoPlayRecyclerView extends RecyclerView { 15 | private AutoPlaySnapHelper autoPlaySnapHelper; 16 | 17 | public AutoPlayRecyclerView(Context context) { 18 | this(context, null); 19 | } 20 | 21 | public AutoPlayRecyclerView(Context context, @Nullable AttributeSet attrs) { 22 | this(context, attrs, 0); 23 | } 24 | 25 | public AutoPlayRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { 26 | super(context, attrs, defStyle); 27 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AutoPlayRecyclerView); 28 | final int timeInterval = typedArray.getInt(R.styleable.AutoPlayRecyclerView_timeInterval, AutoPlaySnapHelper.TIME_INTERVAL); 29 | final int direction = typedArray.getInt(R.styleable.AutoPlayRecyclerView_direction, AutoPlaySnapHelper.RIGHT); 30 | typedArray.recycle(); 31 | autoPlaySnapHelper = new AutoPlaySnapHelper(timeInterval, direction); 32 | } 33 | 34 | @Override 35 | public boolean dispatchTouchEvent(MotionEvent ev) { 36 | boolean result = super.dispatchTouchEvent(ev); 37 | switch (ev.getAction()) { 38 | case MotionEvent.ACTION_DOWN: 39 | if (autoPlaySnapHelper != null) { 40 | autoPlaySnapHelper.pause(); 41 | } 42 | break; 43 | case MotionEvent.ACTION_UP: 44 | if (autoPlaySnapHelper != null) { 45 | autoPlaySnapHelper.start(); 46 | } 47 | } 48 | return result; 49 | } 50 | 51 | public void start() { 52 | autoPlaySnapHelper.start(); 53 | } 54 | 55 | public void pause() { 56 | autoPlaySnapHelper.pause(); 57 | } 58 | 59 | @Override 60 | public void setLayoutManager(LayoutManager layout) { 61 | super.setLayoutManager(layout); 62 | autoPlaySnapHelper.attachToRecyclerView(this); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /viewpagerlayoutmanager/src/main/java/com/leochuan/AutoPlaySnapHelper.java: -------------------------------------------------------------------------------- 1 | package com.leochuan; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.view.animation.DecelerateInterpolator; 8 | import android.widget.Scroller; 9 | 10 | 11 | /** 12 | * Used by {@link AutoPlayRecyclerView} to implement auto play effect 13 | */ 14 | 15 | class AutoPlaySnapHelper extends CenterSnapHelper { 16 | final static int TIME_INTERVAL = 2000; 17 | 18 | final static int LEFT = 1; 19 | final static int RIGHT = 2; 20 | 21 | private Handler handler; 22 | private int timeInterval; 23 | private Runnable autoPlayRunnable; 24 | private boolean runnableAdded; 25 | private int direction; 26 | 27 | AutoPlaySnapHelper(int timeInterval, int direction) { 28 | checkTimeInterval(timeInterval); 29 | checkDirection(direction); 30 | handler = new Handler(Looper.getMainLooper()); 31 | this.timeInterval = timeInterval; 32 | this.direction = direction; 33 | } 34 | 35 | @Override 36 | public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws IllegalStateException { 37 | if (mRecyclerView == recyclerView) { 38 | return; // nothing to do 39 | } 40 | if (mRecyclerView != null) { 41 | destroyCallbacks(); 42 | } 43 | mRecyclerView = recyclerView; 44 | if (mRecyclerView != null) { 45 | final RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager(); 46 | if (!(layoutManager instanceof ViewPagerLayoutManager)) return; 47 | 48 | setupCallbacks(); 49 | mGravityScroller = new Scroller(mRecyclerView.getContext(), 50 | new DecelerateInterpolator()); 51 | 52 | snapToCenterView((ViewPagerLayoutManager) layoutManager, 53 | ((ViewPagerLayoutManager) layoutManager).onPageChangeListener); 54 | 55 | ((ViewPagerLayoutManager) layoutManager).setInfinite(true); 56 | 57 | autoPlayRunnable = new Runnable() { 58 | @Override 59 | public void run() { 60 | final int currentPosition = 61 | ((ViewPagerLayoutManager) layoutManager).getCurrentPositionOffset() * 62 | (((ViewPagerLayoutManager) layoutManager).getReverseLayout() ? -1 : 1); 63 | ScrollHelper.smoothScrollToPosition(mRecyclerView, 64 | (ViewPagerLayoutManager) layoutManager, direction == RIGHT ? currentPosition + 1 : currentPosition - 1); 65 | handler.postDelayed(autoPlayRunnable, timeInterval); 66 | } 67 | }; 68 | handler.postDelayed(autoPlayRunnable, timeInterval); 69 | runnableAdded = true; 70 | } 71 | } 72 | 73 | @Override 74 | void destroyCallbacks() { 75 | super.destroyCallbacks(); 76 | if (runnableAdded) { 77 | handler.removeCallbacks(autoPlayRunnable); 78 | runnableAdded = false; 79 | } 80 | } 81 | 82 | void pause() { 83 | if (runnableAdded) { 84 | handler.removeCallbacks(autoPlayRunnable); 85 | runnableAdded = false; 86 | } 87 | } 88 | 89 | void start() { 90 | if (!runnableAdded) { 91 | handler.postDelayed(autoPlayRunnable, timeInterval); 92 | runnableAdded = true; 93 | } 94 | } 95 | 96 | void setTimeInterval(int timeInterval) { 97 | checkTimeInterval(timeInterval); 98 | this.timeInterval = timeInterval; 99 | } 100 | 101 | void setDirection(int direction) { 102 | checkDirection(direction); 103 | this.direction = direction; 104 | } 105 | 106 | private void checkDirection(int direction) { 107 | if (direction != LEFT && direction != RIGHT) 108 | throw new IllegalArgumentException("direction should be one of left or right"); 109 | } 110 | 111 | private void checkTimeInterval(int timeInterval) { 112 | if (timeInterval <= 0) 113 | throw new IllegalArgumentException("time interval should greater than 0"); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /viewpagerlayoutmanager/src/main/java/com/leochuan/CarouselLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.leochuan; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | /** 7 | * An implementation of {@link ViewPagerLayoutManager} 8 | * which layouts items like carousel 9 | */ 10 | 11 | @SuppressWarnings({"WeakerAccess", "unused"}) 12 | public class CarouselLayoutManager extends ViewPagerLayoutManager { 13 | 14 | private int itemSpace; 15 | private float minScale; 16 | private float moveSpeed; 17 | 18 | public CarouselLayoutManager(Context context, int itemSpace) { 19 | this(new Builder(context, itemSpace)); 20 | } 21 | 22 | public CarouselLayoutManager(Context context, int itemSpace, int orientation) { 23 | this(new Builder(context, itemSpace).setOrientation(orientation)); 24 | } 25 | 26 | public CarouselLayoutManager(Context context, int itemSpace, int orientation, boolean reverseLayout) { 27 | this(new Builder(context, itemSpace).setOrientation(orientation).setReverseLayout(reverseLayout)); 28 | } 29 | 30 | public CarouselLayoutManager(Builder builder) { 31 | this(builder.context, builder.itemSpace, builder.minScale, builder.orientation, 32 | builder.maxVisibleItemCount, builder.moveSpeed, builder.distanceToBottom, 33 | builder.reverseLayout); 34 | } 35 | 36 | private CarouselLayoutManager(Context context, int itemSpace, float minScale, int orientation, 37 | int maxVisibleItemCount, float moveSpeed, int distanceToBottom, 38 | boolean reverseLayout) { 39 | super(context, orientation, reverseLayout); 40 | setEnableBringCenterToFront(true); 41 | setDistanceToBottom(distanceToBottom); 42 | setMaxVisibleItemCount(maxVisibleItemCount); 43 | this.itemSpace = itemSpace; 44 | this.minScale = minScale; 45 | this.moveSpeed = moveSpeed; 46 | } 47 | 48 | public int getItemSpace() { 49 | return itemSpace; 50 | } 51 | 52 | public float getMinScale() { 53 | return minScale; 54 | } 55 | 56 | public float getMoveSpeed() { 57 | return moveSpeed; 58 | } 59 | 60 | public void setItemSpace(int itemSpace) { 61 | assertNotInLayoutOrScroll(null); 62 | if (this.itemSpace == itemSpace) return; 63 | this.itemSpace = itemSpace; 64 | removeAllViews(); 65 | } 66 | 67 | public void setMinScale(float minScale) { 68 | assertNotInLayoutOrScroll(null); 69 | if (minScale > 1f) minScale = 1f; 70 | if (this.minScale == minScale) return; 71 | this.minScale = minScale; 72 | requestLayout(); 73 | } 74 | 75 | public void setMoveSpeed(float moveSpeed) { 76 | assertNotInLayoutOrScroll(null); 77 | if (this.moveSpeed == moveSpeed) return; 78 | this.moveSpeed = moveSpeed; 79 | } 80 | 81 | @Override 82 | protected float setInterval() { 83 | return (mDecoratedMeasurement - itemSpace); 84 | } 85 | 86 | @Override 87 | protected void setItemViewProperty(View itemView, float targetOffset) { 88 | float scale = calculateScale(targetOffset + mSpaceMain); 89 | itemView.setScaleX(scale); 90 | itemView.setScaleY(scale); 91 | } 92 | 93 | @Override 94 | protected float getDistanceRatio() { 95 | if (moveSpeed == 0) return Float.MAX_VALUE; 96 | return 1 / moveSpeed; 97 | } 98 | 99 | @Override 100 | protected float setViewElevation(View itemView, float targetOffset) { 101 | return itemView.getScaleX() * 5; 102 | } 103 | 104 | private float calculateScale(float x) { 105 | float deltaX = Math.abs(x - (mOrientationHelper.getTotalSpace() - mDecoratedMeasurement) / 2f); 106 | return (minScale - 1) * deltaX / (mOrientationHelper.getTotalSpace() / 2f) + 1f; 107 | } 108 | 109 | public static class Builder { 110 | private static final float DEFAULT_SPEED = 1f; 111 | private static final float MIN_SCALE = 0.5f; 112 | 113 | private Context context; 114 | private int itemSpace; 115 | private int orientation; 116 | private float minScale; 117 | private float moveSpeed; 118 | private int maxVisibleItemCount; 119 | private boolean reverseLayout; 120 | private int distanceToBottom; 121 | 122 | public Builder(Context context, int itemSpace) { 123 | this.itemSpace = itemSpace; 124 | this.context = context; 125 | orientation = HORIZONTAL; 126 | minScale = MIN_SCALE; 127 | this.moveSpeed = DEFAULT_SPEED; 128 | reverseLayout = false; 129 | maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN; 130 | distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE; 131 | } 132 | 133 | public Builder setOrientation(int orientation) { 134 | this.orientation = orientation; 135 | return this; 136 | } 137 | 138 | public Builder setMinScale(float minScale) { 139 | this.minScale = minScale; 140 | return this; 141 | } 142 | 143 | public Builder setReverseLayout(boolean reverseLayout) { 144 | this.reverseLayout = reverseLayout; 145 | return this; 146 | } 147 | 148 | public Builder setMoveSpeed(float moveSpeed) { 149 | this.moveSpeed = moveSpeed; 150 | return this; 151 | } 152 | 153 | public Builder setMaxVisibleItemCount(int maxVisibleItemCount) { 154 | this.maxVisibleItemCount = maxVisibleItemCount; 155 | return this; 156 | } 157 | 158 | public Builder setDistanceToBottom(int distanceToBottom) { 159 | this.distanceToBottom = distanceToBottom; 160 | return this; 161 | } 162 | 163 | public CarouselLayoutManager build() { 164 | return new CarouselLayoutManager(this); 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /viewpagerlayoutmanager/src/main/java/com/leochuan/CenterSnapHelper.java: -------------------------------------------------------------------------------- 1 | package com.leochuan; 2 | 3 | import android.support.annotation.Nullable; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.support.v7.widget.RecyclerView.LayoutManager; 6 | import android.view.animation.DecelerateInterpolator; 7 | import android.widget.Scroller; 8 | 9 | /** 10 | * Class intended to support snapping for a {@link RecyclerView} 11 | * which use {@link ViewPagerLayoutManager} as its {@link LayoutManager}. 12 | *

13 | * The implementation will snap the center of the target child view to the center of 14 | * the attached {@link RecyclerView}. 15 | */ 16 | public class CenterSnapHelper extends RecyclerView.OnFlingListener { 17 | 18 | RecyclerView mRecyclerView; 19 | Scroller mGravityScroller; 20 | 21 | /** 22 | * when the dataSet is extremely large 23 | * {@link #snapToCenterView(ViewPagerLayoutManager, ViewPagerLayoutManager.OnPageChangeListener)} 24 | * may keep calling itself because the accuracy of float 25 | */ 26 | private boolean snapToCenter = false; 27 | 28 | // Handles the snap on scroll case. 29 | private final RecyclerView.OnScrollListener mScrollListener = 30 | new RecyclerView.OnScrollListener() { 31 | 32 | boolean mScrolled = false; 33 | 34 | @Override 35 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 36 | super.onScrollStateChanged(recyclerView, newState); 37 | 38 | final ViewPagerLayoutManager layoutManager = 39 | (ViewPagerLayoutManager) recyclerView.getLayoutManager(); 40 | final ViewPagerLayoutManager.OnPageChangeListener onPageChangeListener = 41 | layoutManager.onPageChangeListener; 42 | if (onPageChangeListener != null) { 43 | onPageChangeListener.onPageScrollStateChanged(newState); 44 | } 45 | 46 | if (newState == RecyclerView.SCROLL_STATE_IDLE && mScrolled) { 47 | mScrolled = false; 48 | if (!snapToCenter) { 49 | snapToCenter = true; 50 | snapToCenterView(layoutManager, onPageChangeListener); 51 | } else { 52 | snapToCenter = false; 53 | } 54 | } 55 | } 56 | 57 | @Override 58 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 59 | if (dx != 0 || dy != 0) { 60 | mScrolled = true; 61 | } 62 | } 63 | }; 64 | 65 | @Override 66 | public boolean onFling(int velocityX, int velocityY) { 67 | ViewPagerLayoutManager layoutManager = (ViewPagerLayoutManager) mRecyclerView.getLayoutManager(); 68 | if (layoutManager == null) { 69 | return false; 70 | } 71 | RecyclerView.Adapter adapter = mRecyclerView.getAdapter(); 72 | if (adapter == null) { 73 | return false; 74 | } 75 | 76 | if (!layoutManager.getInfinite() && 77 | (layoutManager.mOffset == layoutManager.getMaxOffset() 78 | || layoutManager.mOffset == layoutManager.getMinOffset())) { 79 | return false; 80 | } 81 | 82 | final int minFlingVelocity = mRecyclerView.getMinFlingVelocity(); 83 | mGravityScroller.fling(0, 0, velocityX, velocityY, 84 | Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE); 85 | 86 | if (layoutManager.mOrientation == ViewPagerLayoutManager.VERTICAL 87 | && Math.abs(velocityY) > minFlingVelocity) { 88 | final int currentPosition = layoutManager.getCurrentPositionOffset(); 89 | final int offsetPosition = (int) (mGravityScroller.getFinalY() / 90 | layoutManager.mInterval / layoutManager.getDistanceRatio()); 91 | ScrollHelper.smoothScrollToPosition(mRecyclerView, layoutManager, layoutManager.getReverseLayout() ? 92 | -currentPosition - offsetPosition : currentPosition + offsetPosition); 93 | return true; 94 | } else if (layoutManager.mOrientation == ViewPagerLayoutManager.HORIZONTAL 95 | && Math.abs(velocityX) > minFlingVelocity) { 96 | final int currentPosition = layoutManager.getCurrentPositionOffset(); 97 | final int offsetPosition = (int) (mGravityScroller.getFinalX() / 98 | layoutManager.mInterval / layoutManager.getDistanceRatio()); 99 | ScrollHelper.smoothScrollToPosition(mRecyclerView, layoutManager, layoutManager.getReverseLayout() ? 100 | -currentPosition - offsetPosition : currentPosition + offsetPosition); 101 | return true; 102 | } 103 | 104 | return true; 105 | } 106 | 107 | /** 108 | * Please attach after {{@link LayoutManager} is setting} 109 | * Attaches the {@link CenterSnapHelper} to the provided RecyclerView, by calling 110 | * {@link RecyclerView#setOnFlingListener(RecyclerView.OnFlingListener)}. 111 | * You can call this method with {@code null} to detach it from the current RecyclerView. 112 | * 113 | * @param recyclerView The RecyclerView instance to which you want to add this helper or 114 | * {@code null} if you want to remove CenterSnapHelper from the current 115 | * RecyclerView. 116 | * @throws IllegalArgumentException if there is already a {@link RecyclerView.OnFlingListener} 117 | * attached to the provided {@link RecyclerView}. 118 | */ 119 | public void attachToRecyclerView(@Nullable RecyclerView recyclerView) 120 | throws IllegalStateException { 121 | if (mRecyclerView == recyclerView) { 122 | return; // nothing to do 123 | } 124 | if (mRecyclerView != null) { 125 | destroyCallbacks(); 126 | } 127 | mRecyclerView = recyclerView; 128 | if (mRecyclerView != null) { 129 | final LayoutManager layoutManager = mRecyclerView.getLayoutManager(); 130 | if (!(layoutManager instanceof ViewPagerLayoutManager)) return; 131 | 132 | setupCallbacks(); 133 | mGravityScroller = new Scroller(mRecyclerView.getContext(), 134 | new DecelerateInterpolator()); 135 | 136 | snapToCenterView((ViewPagerLayoutManager) layoutManager, 137 | ((ViewPagerLayoutManager) layoutManager).onPageChangeListener); 138 | } 139 | } 140 | 141 | void snapToCenterView(ViewPagerLayoutManager layoutManager, 142 | ViewPagerLayoutManager.OnPageChangeListener listener) { 143 | final int delta = layoutManager.getOffsetToCenter(); 144 | if (delta != 0) { 145 | if (layoutManager.getOrientation() 146 | == ViewPagerLayoutManager.VERTICAL) 147 | mRecyclerView.smoothScrollBy(0, delta); 148 | else 149 | mRecyclerView.smoothScrollBy(delta, 0); 150 | } else { 151 | // set it false to make smoothScrollToPosition keep trigger the listener 152 | snapToCenter = false; 153 | } 154 | 155 | if (listener != null) 156 | listener.onPageSelected(layoutManager.getCurrentPosition()); 157 | } 158 | 159 | /** 160 | * Called when an instance of a {@link RecyclerView} is attached. 161 | */ 162 | void setupCallbacks() throws IllegalStateException { 163 | if (mRecyclerView.getOnFlingListener() != null) { 164 | throw new IllegalStateException("An instance of OnFlingListener already set."); 165 | } 166 | mRecyclerView.addOnScrollListener(mScrollListener); 167 | mRecyclerView.setOnFlingListener(this); 168 | } 169 | 170 | /** 171 | * Called when the instance of a {@link RecyclerView} is detached. 172 | */ 173 | void destroyCallbacks() { 174 | mRecyclerView.removeOnScrollListener(mScrollListener); 175 | mRecyclerView.setOnFlingListener(null); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /viewpagerlayoutmanager/src/main/java/com/leochuan/GalleryLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.leochuan; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | /** 7 | * An implementation of {@link ViewPagerLayoutManager} 8 | * which will change rotate x or rotate y 9 | */ 10 | 11 | @SuppressWarnings({"WeakerAccess", "unused"}) 12 | public class GalleryLayoutManager extends ViewPagerLayoutManager { 13 | private final float MAX_ELEVATION = 5F; 14 | 15 | private int itemSpace; 16 | private float moveSpeed; 17 | private float maxAlpha; 18 | private float minAlpha; 19 | private float angle; 20 | private boolean flipRotate; 21 | private boolean rotateFromEdge; 22 | 23 | public GalleryLayoutManager(Context context, int itemSpace) { 24 | this(new Builder(context, itemSpace)); 25 | } 26 | 27 | public GalleryLayoutManager(Context context, int itemSpace, int orientation) { 28 | this(new Builder(context, itemSpace).setOrientation(orientation)); 29 | } 30 | 31 | public GalleryLayoutManager(Context context, int itemSpace, int orientation, boolean reverseLayout) { 32 | this(new Builder(context, itemSpace).setOrientation(orientation).setReverseLayout(reverseLayout)); 33 | } 34 | 35 | public GalleryLayoutManager(Builder builder) { 36 | this(builder.context, builder.itemSpace, builder.angle, builder.maxAlpha, builder.minAlpha, 37 | builder.orientation, builder.moveSpeed, builder.flipRotate, builder.rotateFromEdge, 38 | builder.maxVisibleItemCount, builder.distanceToBottom, builder.reverseLayout); 39 | } 40 | 41 | private GalleryLayoutManager(Context context, int itemSpace, float angle, float maxAlpha, float minAlpha, 42 | int orientation, float moveSpeed, boolean flipRotate, boolean rotateFromEdge, 43 | int maxVisibleItemCount, int distanceToBottom, boolean reverseLayout) { 44 | super(context, orientation, reverseLayout); 45 | setDistanceToBottom(distanceToBottom); 46 | setMaxVisibleItemCount(maxVisibleItemCount); 47 | this.itemSpace = itemSpace; 48 | this.moveSpeed = moveSpeed; 49 | this.angle = angle; 50 | this.maxAlpha = maxAlpha; 51 | this.minAlpha = minAlpha; 52 | this.flipRotate = flipRotate; 53 | this.rotateFromEdge = rotateFromEdge; 54 | } 55 | 56 | public int getItemSpace() { 57 | return itemSpace; 58 | } 59 | 60 | public float getMaxAlpha() { 61 | return maxAlpha; 62 | } 63 | 64 | public float getMinAlpha() { 65 | return minAlpha; 66 | } 67 | 68 | public float getAngle() { 69 | return angle; 70 | } 71 | 72 | public float getMoveSpeed() { 73 | return moveSpeed; 74 | } 75 | 76 | public boolean getFlipRotate() { 77 | return flipRotate; 78 | } 79 | 80 | public boolean getRotateFromEdge() { 81 | return rotateFromEdge; 82 | } 83 | 84 | public void setItemSpace(int itemSpace) { 85 | assertNotInLayoutOrScroll(null); 86 | if (this.itemSpace == itemSpace) return; 87 | this.itemSpace = itemSpace; 88 | removeAllViews(); 89 | } 90 | 91 | public void setMoveSpeed(float moveSpeed) { 92 | assertNotInLayoutOrScroll(null); 93 | if (this.moveSpeed == moveSpeed) return; 94 | this.moveSpeed = moveSpeed; 95 | } 96 | 97 | public void setMaxAlpha(float maxAlpha) { 98 | assertNotInLayoutOrScroll(null); 99 | if (maxAlpha > 1) maxAlpha = 1; 100 | if (this.maxAlpha == maxAlpha) return; 101 | this.maxAlpha = maxAlpha; 102 | requestLayout(); 103 | } 104 | 105 | public void setMinAlpha(float minAlpha) { 106 | assertNotInLayoutOrScroll(null); 107 | if (minAlpha < 0) minAlpha = 0; 108 | if (this.minAlpha == minAlpha) return; 109 | this.minAlpha = minAlpha; 110 | requestLayout(); 111 | } 112 | 113 | public void setAngle(float angle) { 114 | assertNotInLayoutOrScroll(null); 115 | if (this.angle == angle) return; 116 | this.angle = angle; 117 | requestLayout(); 118 | } 119 | 120 | public void setFlipRotate(boolean flipRotate) { 121 | assertNotInLayoutOrScroll(null); 122 | if (this.flipRotate == flipRotate) return; 123 | this.flipRotate = flipRotate; 124 | requestLayout(); 125 | } 126 | 127 | public void setRotateFromEdge(boolean rotateFromEdge) { 128 | assertNotInLayoutOrScroll(null); 129 | if (this.rotateFromEdge == rotateFromEdge) return; 130 | this.rotateFromEdge = rotateFromEdge; 131 | removeAllViews(); 132 | } 133 | 134 | @Override 135 | protected float setInterval() { 136 | return mDecoratedMeasurement + itemSpace; 137 | } 138 | 139 | @Override 140 | protected void setItemViewProperty(View itemView, float targetOffset) { 141 | final float rotation = calRotation(targetOffset); 142 | if (getOrientation() == HORIZONTAL) { 143 | if (rotateFromEdge) { 144 | itemView.setPivotX(rotation > 0 ? 0 : mDecoratedMeasurement); 145 | itemView.setPivotY(mDecoratedMeasurementInOther * 0.5f); 146 | } 147 | if (flipRotate) { 148 | itemView.setRotationX(rotation); 149 | } else { 150 | itemView.setRotationY(rotation); 151 | } 152 | } else { 153 | if (rotateFromEdge) { 154 | itemView.setPivotY(rotation > 0 ? 0 : mDecoratedMeasurement); 155 | itemView.setPivotX(mDecoratedMeasurementInOther * 0.5f); 156 | 157 | } 158 | if (flipRotate) { 159 | itemView.setRotationY(-rotation); 160 | } else { 161 | itemView.setRotationX(-rotation); 162 | } 163 | } 164 | final float alpha = calAlpha(targetOffset); 165 | itemView.setAlpha(alpha); 166 | } 167 | 168 | @Override 169 | protected float setViewElevation(View itemView, float targetOffset) { 170 | final float ele = Math.max(Math.abs(itemView.getRotationX()), Math.abs(itemView.getRotationY())) * MAX_ELEVATION / 360; 171 | return MAX_ELEVATION - ele; 172 | } 173 | 174 | @Override 175 | protected float getDistanceRatio() { 176 | if (moveSpeed == 0) return Float.MAX_VALUE; 177 | return 1 / moveSpeed; 178 | } 179 | 180 | private float calRotation(float targetOffset) { 181 | return -angle / mInterval * targetOffset; 182 | } 183 | 184 | private float calAlpha(float targetOffset) { 185 | final float offset = Math.abs(targetOffset); 186 | float alpha = (minAlpha - maxAlpha) / mInterval * offset + maxAlpha; 187 | if (offset >= mInterval) alpha = minAlpha; 188 | return alpha; 189 | } 190 | 191 | public static class Builder { 192 | private static float INTERVAL_ANGLE = 30f; 193 | private static final float DEFAULT_SPEED = 1f; 194 | private static float MIN_ALPHA = 0.5f; 195 | private static float MAX_ALPHA = 1f; 196 | 197 | private int itemSpace; 198 | private float moveSpeed; 199 | private int orientation; 200 | private float maxAlpha; 201 | private float minAlpha; 202 | private float angle; 203 | private boolean flipRotate; 204 | private boolean reverseLayout; 205 | private Context context; 206 | private int maxVisibleItemCount; 207 | private int distanceToBottom; 208 | private boolean rotateFromEdge; 209 | 210 | public Builder(Context context, int itemSpace) { 211 | this.itemSpace = itemSpace; 212 | this.context = context; 213 | orientation = HORIZONTAL; 214 | angle = INTERVAL_ANGLE; 215 | maxAlpha = MAX_ALPHA; 216 | minAlpha = MIN_ALPHA; 217 | this.moveSpeed = DEFAULT_SPEED; 218 | reverseLayout = false; 219 | flipRotate = false; 220 | rotateFromEdge = false; 221 | distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE; 222 | maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN; 223 | } 224 | 225 | public Builder setItemSpace(int itemSpace) { 226 | this.itemSpace = itemSpace; 227 | return this; 228 | } 229 | 230 | public Builder setMoveSpeed(float moveSpeed) { 231 | this.moveSpeed = moveSpeed; 232 | return this; 233 | } 234 | 235 | public Builder setOrientation(int orientation) { 236 | this.orientation = orientation; 237 | return this; 238 | } 239 | 240 | public Builder setMaxAlpha(float maxAlpha) { 241 | if (maxAlpha > 1) maxAlpha = 1; 242 | this.maxAlpha = maxAlpha; 243 | return this; 244 | } 245 | 246 | public Builder setMinAlpha(float minAlpha) { 247 | if (minAlpha < 0) minAlpha = 0; 248 | this.minAlpha = minAlpha; 249 | return this; 250 | } 251 | 252 | public Builder setAngle(float angle) { 253 | this.angle = angle; 254 | return this; 255 | } 256 | 257 | public Builder setFlipRotate(boolean flipRotate) { 258 | this.flipRotate = flipRotate; 259 | return this; 260 | } 261 | 262 | public Builder setReverseLayout(boolean reverseLayout) { 263 | this.reverseLayout = reverseLayout; 264 | return this; 265 | } 266 | 267 | public Builder setMaxVisibleItemCount(int maxVisibleItemCount) { 268 | this.maxVisibleItemCount = maxVisibleItemCount; 269 | return this; 270 | } 271 | 272 | public Builder setDistanceToBottom(int distanceToBottom) { 273 | this.distanceToBottom = distanceToBottom; 274 | return this; 275 | } 276 | 277 | public Builder setRotateFromEdge(boolean rotateFromEdge) { 278 | this.rotateFromEdge = rotateFromEdge; 279 | return this; 280 | } 281 | 282 | public GalleryLayoutManager build() { 283 | return new GalleryLayoutManager(this); 284 | } 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /viewpagerlayoutmanager/src/main/java/com/leochuan/PageSnapHelper.java: -------------------------------------------------------------------------------- 1 | package com.leochuan; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | 5 | /** 6 | * The implementation will snap the center of the target child view to the center of 7 | * the attached {@link RecyclerView}. And per Child per fling. 8 | */ 9 | 10 | public class PageSnapHelper extends CenterSnapHelper { 11 | 12 | @Override 13 | public boolean onFling(int velocityX, int velocityY) { 14 | ViewPagerLayoutManager layoutManager = (ViewPagerLayoutManager) mRecyclerView.getLayoutManager(); 15 | if (layoutManager == null) { 16 | return false; 17 | } 18 | RecyclerView.Adapter adapter = mRecyclerView.getAdapter(); 19 | if (adapter == null) { 20 | return false; 21 | } 22 | 23 | if (!layoutManager.getInfinite() && 24 | (layoutManager.mOffset == layoutManager.getMaxOffset() 25 | || layoutManager.mOffset == layoutManager.getMinOffset())) { 26 | return false; 27 | } 28 | 29 | final int minFlingVelocity = mRecyclerView.getMinFlingVelocity(); 30 | mGravityScroller.fling(0, 0, velocityX, velocityY, 31 | Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE); 32 | 33 | if (layoutManager.mOrientation == ViewPagerLayoutManager.VERTICAL 34 | && Math.abs(velocityY) > minFlingVelocity) { 35 | final int currentPosition = layoutManager.getCurrentPositionOffset(); 36 | final int offsetPosition = mGravityScroller.getFinalY() * layoutManager.getDistanceRatio() > layoutManager.mInterval ? 1 : 0; 37 | ScrollHelper.smoothScrollToPosition(mRecyclerView, layoutManager, layoutManager.getReverseLayout() ? 38 | -currentPosition - offsetPosition : currentPosition + offsetPosition); 39 | return true; 40 | } else if (layoutManager.mOrientation == ViewPagerLayoutManager.HORIZONTAL 41 | && Math.abs(velocityX) > minFlingVelocity) { 42 | final int currentPosition = layoutManager.getCurrentPositionOffset(); 43 | final int offsetPosition = mGravityScroller.getFinalX() * layoutManager.getDistanceRatio() > layoutManager.mInterval ? 1 : 0; 44 | ScrollHelper.smoothScrollToPosition(mRecyclerView, layoutManager, layoutManager.getReverseLayout() ? 45 | -currentPosition - offsetPosition : currentPosition + offsetPosition); 46 | return true; 47 | } 48 | 49 | return true; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /viewpagerlayoutmanager/src/main/java/com/leochuan/RotateLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.leochuan; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | /** 7 | * An implementation of {@link ViewPagerLayoutManager} 8 | * which rotates items 9 | */ 10 | 11 | @SuppressWarnings({"WeakerAccess", "unused"}) 12 | public class RotateLayoutManager extends ViewPagerLayoutManager { 13 | 14 | private int itemSpace; 15 | private float angle; 16 | private float moveSpeed; 17 | private boolean reverseRotate; 18 | 19 | public RotateLayoutManager(Context context, int itemSpace) { 20 | this(new Builder(context, itemSpace)); 21 | } 22 | 23 | public RotateLayoutManager(Context context, int itemSpace, int orientation) { 24 | this(new Builder(context, itemSpace).setOrientation(orientation)); 25 | } 26 | 27 | public RotateLayoutManager(Context context, int itemSpace, int orientation, boolean reverseLayout) { 28 | this(new Builder(context, itemSpace).setOrientation(orientation).setReverseLayout(reverseLayout)); 29 | } 30 | 31 | public RotateLayoutManager(Builder builder) { 32 | this(builder.context, builder.itemSpace, builder.angle, builder.orientation, builder.moveSpeed, 33 | builder.reverseRotate, builder.maxVisibleItemCount, builder.distanceToBottom, 34 | builder.reverseLayout); 35 | } 36 | 37 | private RotateLayoutManager(Context context, int itemSpace, float angle, int orientation, float moveSpeed, 38 | boolean reverseRotate, int maxVisibleItemCount, int distanceToBottom, 39 | boolean reverseLayout) { 40 | super(context, orientation, reverseLayout); 41 | setDistanceToBottom(distanceToBottom); 42 | setMaxVisibleItemCount(maxVisibleItemCount); 43 | this.itemSpace = itemSpace; 44 | this.angle = angle; 45 | this.moveSpeed = moveSpeed; 46 | this.reverseRotate = reverseRotate; 47 | } 48 | 49 | public int getItemSpace() { 50 | return itemSpace; 51 | } 52 | 53 | public float getAngle() { 54 | return angle; 55 | } 56 | 57 | public float getMoveSpeed() { 58 | return moveSpeed; 59 | } 60 | 61 | public boolean getReverseRotate() { 62 | return reverseRotate; 63 | } 64 | 65 | public void setItemSpace(int itemSpace) { 66 | assertNotInLayoutOrScroll(null); 67 | if (this.itemSpace == itemSpace) return; 68 | this.itemSpace = itemSpace; 69 | removeAllViews(); 70 | } 71 | 72 | public void setAngle(float centerScale) { 73 | assertNotInLayoutOrScroll(null); 74 | if (this.angle == centerScale) return; 75 | this.angle = centerScale; 76 | requestLayout(); 77 | } 78 | 79 | public void setMoveSpeed(float moveSpeed) { 80 | assertNotInLayoutOrScroll(null); 81 | if (this.moveSpeed == moveSpeed) return; 82 | this.moveSpeed = moveSpeed; 83 | } 84 | 85 | public void setReverseRotate(boolean reverseRotate) { 86 | assertNotInLayoutOrScroll(null); 87 | if (this.reverseRotate == reverseRotate) return; 88 | this.reverseRotate = reverseRotate; 89 | requestLayout(); 90 | } 91 | 92 | @Override 93 | protected float setInterval() { 94 | return mDecoratedMeasurement + itemSpace; 95 | } 96 | 97 | @Override 98 | protected void setItemViewProperty(View itemView, float targetOffset) { 99 | itemView.setRotation(calRotation(targetOffset)); 100 | } 101 | 102 | @Override 103 | protected float getDistanceRatio() { 104 | if (moveSpeed == 0) return Float.MAX_VALUE; 105 | return 1 / moveSpeed; 106 | } 107 | 108 | private float calRotation(float targetOffset) { 109 | final float realAngle = reverseRotate ? angle : -angle; 110 | return realAngle / mInterval * targetOffset; 111 | } 112 | 113 | public static class Builder { 114 | private static float INTERVAL_ANGLE = 360f; 115 | private static final float DEFAULT_SPEED = 1f; 116 | 117 | private int itemSpace; 118 | private int orientation; 119 | private float angle; 120 | private float moveSpeed; 121 | private boolean reverseRotate; 122 | private boolean reverseLayout; 123 | private Context context; 124 | private int maxVisibleItemCount; 125 | private int distanceToBottom; 126 | 127 | public Builder(Context context, int itemSpace) { 128 | this.context = context; 129 | this.itemSpace = itemSpace; 130 | orientation = HORIZONTAL; 131 | angle = INTERVAL_ANGLE; 132 | this.moveSpeed = DEFAULT_SPEED; 133 | reverseRotate = false; 134 | reverseLayout = false; 135 | distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE; 136 | maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN; 137 | } 138 | 139 | public Builder setOrientation(int orientation) { 140 | this.orientation = orientation; 141 | return this; 142 | } 143 | 144 | public Builder setAngle(float angle) { 145 | this.angle = angle; 146 | return this; 147 | } 148 | 149 | public Builder setReverseLayout(boolean reverseLayout) { 150 | this.reverseLayout = reverseLayout; 151 | return this; 152 | } 153 | 154 | public Builder setMoveSpeed(float moveSpeed) { 155 | this.moveSpeed = moveSpeed; 156 | return this; 157 | } 158 | 159 | public Builder setReverseRotate(boolean reverseRotate) { 160 | this.reverseRotate = reverseRotate; 161 | return this; 162 | } 163 | 164 | public Builder setMaxVisibleItemCount(int maxVisibleItemCount) { 165 | this.maxVisibleItemCount = maxVisibleItemCount; 166 | return this; 167 | } 168 | 169 | public Builder setDistanceToBottom(int distanceToBottom) { 170 | this.distanceToBottom = distanceToBottom; 171 | return this; 172 | } 173 | 174 | public RotateLayoutManager build() { 175 | return new RotateLayoutManager(this); 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /viewpagerlayoutmanager/src/main/java/com/leochuan/ScaleLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.leochuan; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | /** 7 | * An implementation of {@link ViewPagerLayoutManager} 8 | * which zooms the center item 9 | */ 10 | 11 | @SuppressWarnings({"WeakerAccess", "unused"}) 12 | public class ScaleLayoutManager extends ViewPagerLayoutManager { 13 | 14 | private int itemSpace; 15 | private float minScale; 16 | private float moveSpeed; 17 | private float maxAlpha; 18 | private float minAlpha; 19 | 20 | public ScaleLayoutManager(Context context, int itemSpace) { 21 | this(new Builder(context, itemSpace)); 22 | } 23 | 24 | public ScaleLayoutManager(Context context, int itemSpace, int orientation) { 25 | this(new Builder(context, itemSpace).setOrientation(orientation)); 26 | } 27 | 28 | public ScaleLayoutManager(Context context, int itemSpace, int orientation, boolean reverseLayout) { 29 | this(new Builder(context, itemSpace).setOrientation(orientation).setReverseLayout(reverseLayout)); 30 | } 31 | 32 | public ScaleLayoutManager(Builder builder) { 33 | this(builder.context, builder.itemSpace, builder.minScale, builder.maxAlpha, builder.minAlpha, 34 | builder.orientation, builder.moveSpeed, builder.maxVisibleItemCount, builder.distanceToBottom, 35 | builder.reverseLayout); 36 | } 37 | 38 | private ScaleLayoutManager(Context context, int itemSpace, float minScale, float maxAlpha, float minAlpha, 39 | int orientation, float moveSpeed, int maxVisibleItemCount, int distanceToBottom, 40 | boolean reverseLayout) { 41 | super(context, orientation, reverseLayout); 42 | setDistanceToBottom(distanceToBottom); 43 | setMaxVisibleItemCount(maxVisibleItemCount); 44 | this.itemSpace = itemSpace; 45 | this.minScale = minScale; 46 | this.moveSpeed = moveSpeed; 47 | this.maxAlpha = maxAlpha; 48 | this.minAlpha = minAlpha; 49 | } 50 | 51 | public int getItemSpace() { 52 | return itemSpace; 53 | } 54 | 55 | public float getMinScale() { 56 | return minScale; 57 | } 58 | 59 | public float getMoveSpeed() { 60 | return moveSpeed; 61 | } 62 | 63 | public float getMaxAlpha() { 64 | return maxAlpha; 65 | } 66 | 67 | public float getMinAlpha() { 68 | return minAlpha; 69 | } 70 | 71 | public void setItemSpace(int itemSpace) { 72 | assertNotInLayoutOrScroll(null); 73 | if (this.itemSpace == itemSpace) return; 74 | this.itemSpace = itemSpace; 75 | removeAllViews(); 76 | } 77 | 78 | public void setMinScale(float minScale) { 79 | assertNotInLayoutOrScroll(null); 80 | if (this.minScale == minScale) return; 81 | this.minScale = minScale; 82 | removeAllViews(); 83 | } 84 | 85 | public void setMaxAlpha(float maxAlpha) { 86 | assertNotInLayoutOrScroll(null); 87 | if (maxAlpha > 1) maxAlpha = 1; 88 | if (this.maxAlpha == maxAlpha) return; 89 | this.maxAlpha = maxAlpha; 90 | requestLayout(); 91 | } 92 | 93 | public void setMinAlpha(float minAlpha) { 94 | assertNotInLayoutOrScroll(null); 95 | if (minAlpha < 0) minAlpha = 0; 96 | if (this.minAlpha == minAlpha) return; 97 | this.minAlpha = minAlpha; 98 | requestLayout(); 99 | } 100 | 101 | public void setMoveSpeed(float moveSpeed) { 102 | assertNotInLayoutOrScroll(null); 103 | if (this.moveSpeed == moveSpeed) return; 104 | this.moveSpeed = moveSpeed; 105 | } 106 | 107 | @Override 108 | protected float setInterval() { 109 | return itemSpace + mDecoratedMeasurement; 110 | } 111 | 112 | @Override 113 | protected void setItemViewProperty(View itemView, float targetOffset) { 114 | float scale = calculateScale(targetOffset + mSpaceMain); 115 | itemView.setScaleX(scale); 116 | itemView.setScaleY(scale); 117 | final float alpha = calAlpha(targetOffset); 118 | itemView.setAlpha(alpha); 119 | } 120 | 121 | private float calAlpha(float targetOffset) { 122 | final float offset = Math.abs(targetOffset); 123 | float alpha = (minAlpha - maxAlpha) / mInterval * offset + maxAlpha; 124 | if (offset >= mInterval) alpha = minAlpha; 125 | return alpha; 126 | } 127 | 128 | @Override 129 | protected float getDistanceRatio() { 130 | if (moveSpeed == 0) return Float.MAX_VALUE; 131 | return 1 / moveSpeed; 132 | } 133 | 134 | /** 135 | * @param x start positon of the view you want scale 136 | * @return the scale rate of current scroll mOffset 137 | */ 138 | private float calculateScale(float x) { 139 | float deltaX = Math.abs(x - mSpaceMain); 140 | if (deltaX - mDecoratedMeasurement > 0) deltaX = mDecoratedMeasurement; 141 | return 1f - deltaX / mDecoratedMeasurement * (1f - minScale); 142 | } 143 | 144 | public static class Builder { 145 | private static final float SCALE_RATE = 0.8f; 146 | private static final float DEFAULT_SPEED = 1f; 147 | private static float MIN_ALPHA = 1f; 148 | private static float MAX_ALPHA = 1f; 149 | 150 | private int itemSpace; 151 | private int orientation; 152 | private float minScale; 153 | private float moveSpeed; 154 | private float maxAlpha; 155 | private float minAlpha; 156 | private boolean reverseLayout; 157 | private Context context; 158 | private int maxVisibleItemCount; 159 | private int distanceToBottom; 160 | 161 | public Builder(Context context, int itemSpace) { 162 | this.itemSpace = itemSpace; 163 | this.context = context; 164 | orientation = HORIZONTAL; 165 | minScale = SCALE_RATE; 166 | this.moveSpeed = DEFAULT_SPEED; 167 | maxAlpha = MAX_ALPHA; 168 | minAlpha = MIN_ALPHA; 169 | reverseLayout = false; 170 | distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE; 171 | maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN; 172 | } 173 | 174 | public Builder setOrientation(int orientation) { 175 | this.orientation = orientation; 176 | return this; 177 | } 178 | 179 | public Builder setMinScale(float minScale) { 180 | this.minScale = minScale; 181 | return this; 182 | } 183 | 184 | public Builder setReverseLayout(boolean reverseLayout) { 185 | this.reverseLayout = reverseLayout; 186 | return this; 187 | } 188 | 189 | public Builder setMaxAlpha(float maxAlpha) { 190 | if (maxAlpha > 1) maxAlpha = 1; 191 | this.maxAlpha = maxAlpha; 192 | return this; 193 | } 194 | 195 | public Builder setMinAlpha(float minAlpha) { 196 | if (minAlpha < 0) minAlpha = 0; 197 | this.minAlpha = minAlpha; 198 | return this; 199 | } 200 | 201 | public Builder setMoveSpeed(float moveSpeed) { 202 | this.moveSpeed = moveSpeed; 203 | return this; 204 | } 205 | 206 | public Builder setMaxVisibleItemCount(int maxVisibleItemCount) { 207 | this.maxVisibleItemCount = maxVisibleItemCount; 208 | return this; 209 | } 210 | 211 | public Builder setDistanceToBottom(int distanceToBottom) { 212 | this.distanceToBottom = distanceToBottom; 213 | return this; 214 | } 215 | 216 | public ScaleLayoutManager build() { 217 | return new ScaleLayoutManager(this); 218 | } 219 | } 220 | } 221 | 222 | -------------------------------------------------------------------------------- /viewpagerlayoutmanager/src/main/java/com/leochuan/ScrollHelper.java: -------------------------------------------------------------------------------- 1 | package com.leochuan; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.View; 5 | 6 | public class ScrollHelper { 7 | /* package */ static void smoothScrollToPosition(RecyclerView recyclerView, ViewPagerLayoutManager viewPagerLayoutManager, int targetPosition) { 8 | final int delta = viewPagerLayoutManager.getOffsetToPosition(targetPosition); 9 | if (viewPagerLayoutManager.getOrientation() == ViewPagerLayoutManager.VERTICAL) { 10 | recyclerView.smoothScrollBy(0, delta); 11 | } else { 12 | recyclerView.smoothScrollBy(delta, 0); 13 | } 14 | } 15 | 16 | public static void smoothScrollToTargetView(RecyclerView recyclerView, View targetView) { 17 | final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); 18 | if (!(layoutManager instanceof ViewPagerLayoutManager)) return; 19 | final int targetPosition = ((ViewPagerLayoutManager) layoutManager).getLayoutPositionOfView(targetView); 20 | smoothScrollToPosition(recyclerView, (ViewPagerLayoutManager) layoutManager, targetPosition); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /viewpagerlayoutmanager/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /viewpagerlayoutmanager/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | viewpagerlayoutmanager 3 | 4 | --------------------------------------------------------------------------------