├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── dependencies.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── image └── layout_manager.gif ├── layout-manager ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── arc │ └── fast │ └── layoutmanager │ └── FastRightSlideLayoutManager.kt ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ └── com │ │ └── arc │ │ └── fast │ │ └── layoutmanager │ │ └── sample │ │ ├── MainActivity.kt │ │ └── extension │ │ ├── BindingAdapter.kt │ │ └── ViewExtensions.kt │ └── res │ ├── layout │ ├── activity_main.xml │ └── item_layout.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher_foreground.png │ ├── ic_launcher_round.png │ ├── s1.webp │ ├── s2.webp │ ├── s3.webp │ └── s4.webp │ ├── mipmap-xxxhdpi │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── ic_launcher_background.xml │ ├── strings.xml │ ├── styles.xml │ └── themes.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | /.idea/caches 6 | /.idea/libraries 7 | /.idea/modules.xml 8 | /.idea/workspace.xml 9 | /.idea/navEditor.xml 10 | /.idea/assetWizardSettings.xml 11 | .DS_Store 12 | /build 13 | /captures 14 | .externalNativeBuild 15 | .cxx 16 | local.properties 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 木兰宽松许可证, 第2版 2 | 3 | 木兰宽松许可证, 第2版 4 | 2020年1月 http://license.coscl.org.cn/MulanPSL2 5 | 6 | 7 | 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: 8 | 9 | 0. 定义 10 | 11 | “软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 12 | 13 | “贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 14 | 15 | “贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 16 | 17 | “法人实体”是指提交贡献的机构及其“关联实体”。 18 | 19 | “关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 20 | 21 | 1. 授予版权许可 22 | 23 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 24 | 25 | 2. 授予专利许可 26 | 27 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 28 | 29 | 3. 无商标许可 30 | 31 | “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 32 | 33 | 4. 分发限制 34 | 35 | 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 36 | 37 | 5. 免责声明与责任限制 38 | 39 | “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 40 | 41 | 6. 语言 42 | “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 43 | 44 | 条款结束 45 | 46 | 如何将木兰宽松许可证,第2版,应用到您的软件 47 | 48 | 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: 49 | 50 | 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; 51 | 52 | 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; 53 | 54 | 3, 请将如下声明文本放入每个源文件的头部注释中。 55 | 56 | Copyright (c) [Year] [name of copyright holder] 57 | [Software Name] is licensed under Mulan PSL v2. 58 | You can use this software according to the terms and conditions of the Mulan PSL v2. 59 | You may obtain a copy of Mulan PSL v2 at: 60 | http://license.coscl.org.cn/MulanPSL2 61 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 62 | See the Mulan PSL v2 for more details. 63 | 64 | 65 | Mulan Permissive Software License,Version 2 66 | 67 | Mulan Permissive Software License,Version 2 (Mulan PSL v2) 68 | January 2020 http://license.coscl.org.cn/MulanPSL2 69 | 70 | Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: 71 | 72 | 0. Definition 73 | 74 | Software means the program and related documents which are licensed under this License and comprise all Contribution(s). 75 | 76 | Contribution means the copyrightable work licensed by a particular Contributor under this License. 77 | 78 | Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. 79 | 80 | Legal Entity means the entity making a Contribution and all its Affiliates. 81 | 82 | Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. 83 | 84 | 1. Grant of Copyright License 85 | 86 | Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. 87 | 88 | 2. Grant of Patent License 89 | 90 | Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. 91 | 92 | 3. No Trademark License 93 | 94 | No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4. 95 | 96 | 4. Distribution Restriction 97 | 98 | You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. 99 | 100 | 5. Disclaimer of Warranty and Limitation of Liability 101 | 102 | THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 103 | 104 | 6. Language 105 | 106 | THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. 107 | 108 | END OF THE TERMS AND CONDITIONS 109 | 110 | How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software 111 | 112 | To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: 113 | 114 | i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; 115 | 116 | ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; 117 | 118 | iii Attach the statement to the appropriate annotated syntax at the beginning of each source file. 119 | 120 | 121 | Copyright (c) [Year] [name of copyright holder] 122 | [Software Name] is licensed under Mulan PSL v2. 123 | You can use this software according to the terms and conditions of the Mulan PSL v2. 124 | You may obtain a copy of Mulan PSL v2 at: 125 | http://license.coscl.org.cn/MulanPSL2 126 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 127 | See the Mulan PSL v2 for more details. 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fast-flow-layout 2 | 3 | [![](https://jitpack.io/v/Arcns/fast-layout-manager.svg)](https://jitpack.io/#Arcns/fast-layout-manager) 4 | 5 | > 自定义LayoutManager,实现从右向左快速层叠滑动的LayoutManager效果,提供更多灵活的配置项。 6 | 7 | ![集成效果](./image/layout_manager.gif) 8 | 9 | #### 1.集成方式: 10 | 11 | ``` 12 | allprojects { 13 | repositories { 14 | ... 15 | maven { url 'https://www.jitpack.io' } 16 | } 17 | } 18 | ``` 19 | 20 | ``` 21 | implementation 'com.github.Arcns:fast-layout-manager:latest.release' 22 | ``` 23 | 24 | #### 2.使用方式 25 | ``` 26 | // 使用从右向左快速层叠滑动的LayoutManager 27 | recyclerView.layoutManager = FastRightSlideLayoutManager( 28 | // 可选配置项:是否启用循环模式,默认false 29 | enableLoop = true, 30 | // 可选配置项:是否启用ViewPager模式,默认false 31 | enablePagerMode = false, 32 | // 可选配置项:是否启用自动翻页,默认false 33 | enableAutoSlide = true, 34 | // 可选配置项:自动翻页时间,默认3000L 35 | autoSlideTime = 3000L, 36 | // 可选配置项:默认显示出来的候选item数量,默认2 37 | candidateCount = 2, 38 | // 可选配置项:默认显示出来的最后一个候选item的缩放比例,默认0.8f 39 | candidateLastScale = 0.8f, 40 | // 可选配置项:是否启用离开时透明效果,默认false 41 | enableExitAlpha = true, 42 | // 可选配置项:离开达到宽度的多少比例时开始启用透明效果,默认0.1f 43 | exitAlphaStartSign = 0.1f, 44 | // 可选配置项:离开时透明效果的最终值,默认0.6f 45 | exitAlphaEndValue = 0.6f, 46 | // 可选配置项:是否启用离开时缩放效果,默认true 47 | enableExitScale = true, 48 | // 可选配置项:横向的总空间偏移量,默认0 49 | totalHorizontallySpaceOffset = 0, 50 | // 可选配置项:当前Position更新事件,默认null 51 | onCurrentPositionChangeListener = null 52 | ) 53 | // 测试数据 54 | val itemWidth = (screenWidth * 0.8f).roundToInt() 55 | val data = arrayListOf(R.mipmap.s1, R.mipmap.s2, R.mipmap.s3, R.mipmap.s4) 56 | // 57 | recyclerView.adapter = 58 | object : BaseQuickAdapter(R.layout.item_layout, data) { 59 | override fun convert(holder: BaseViewHolder, item: Int) { 60 | // 注意:item的宽度必须小于RecyclerView的宽度,否则无法计算出其他候选item的位置 61 | holder.getView(R.id.cl_root).layoutParams.width = itemWidth 62 | holder.getView(R.id.iv_image).setImageResource(item) 63 | } 64 | } 65 | ``` -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply from: rootProject.file("dependencies.gradle") 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.3.1' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | maven { url "https://jitpack.io" } 19 | } 20 | } 21 | 22 | task clean(type: Delete) { 23 | delete rootProject.buildDir 24 | } -------------------------------------------------------------------------------- /dependencies.gradle: -------------------------------------------------------------------------------- 1 | ext.versions = [ 2 | min_sdk : 21, 3 | compile_sdk : 33, 4 | publish_code: 1, 5 | publish_name: "1.0.0" 6 | ] -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Aug 12 10:14:18 CST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | Please set the JAVA_HOME variable in your environment to match the 131 | location of your Java installation." 132 | fi 133 | else 134 | JAVACMD=java 135 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 136 | Please set the JAVA_HOME variable in your environment to match the 137 | location of your Java installation." 138 | fi 139 | 140 | # Increase the maximum file descriptors if we can. 141 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 142 | case $MAX_FD in #( 143 | max*) 144 | MAX_FD=$( ulimit -H -n ) || 145 | warn "Could not query maximum file descriptor limit" 146 | esac 147 | case $MAX_FD in #( 148 | '' | soft) :;; #( 149 | *) 150 | ulimit -n "$MAX_FD" || 151 | warn "Could not set maximum file descriptor limit to $MAX_FD" 152 | esac 153 | fi 154 | 155 | # Collect all arguments for the java command, stacking in reverse order: 156 | # * args from the command line 157 | # * the main class name 158 | # * -classpath 159 | # * -D...appname settings 160 | # * --module-path (only if needed) 161 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 162 | 163 | # For Cygwin or MSYS, switch paths to Windows format before running java 164 | if "$cygwin" || "$msys" ; then 165 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 166 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 167 | 168 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 169 | 170 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 171 | for arg do 172 | if 173 | case $arg in #( 174 | -*) false ;; # don't mess with options #( 175 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 176 | [ -e "$t" ] ;; #( 177 | *) false ;; 178 | esac 179 | then 180 | arg=$( cygpath --path --ignore --mixed "$arg" ) 181 | fi 182 | # Roll the args list around exactly as many times as the number of 183 | # args, so each arg winds up back in the position where it started, but 184 | # possibly modified. 185 | # 186 | # NB: a `for` loop captures its iteration list before it begins, so 187 | # changing the positional parameters here affects neither the number of 188 | # iterations, nor the values presented in `arg`. 189 | shift # remove old arg 190 | set -- "$@" "$arg" # push replacement arg 191 | done 192 | fi 193 | 194 | # Collect all arguments for the java command; 195 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 196 | # shell script including quotes and variable substitutions, so put them in 197 | # double quotes to make sure that they get re-expanded; and 198 | # * put everything else in single quotes, so that it's not re-expanded. 199 | 200 | set -- \ 201 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 202 | -classpath "$CLASSPATH" \ 203 | org.gradle.wrapper.GradleWrapperMain \ 204 | "$@" 205 | 206 | # Use "xargs" to parse quoted args. 207 | # 208 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 209 | # 210 | # In Bash we could simply go: 211 | # 212 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 213 | # set -- "${ARGS[@]}" "$@" 214 | # 215 | # but POSIX shell has neither arrays nor command substitution, so instead we 216 | # post-process each arg (as a line of input to sed) to backslash-escape any 217 | # character that might be a shell metacharacter, then use eval to reverse 218 | # that process (while maintaining the separation between arguments), and wrap 219 | # the whole thing up as a single "set" statement. 220 | # 221 | # This will of course break if any of these variables contains a newline or 222 | # an unmatched quote. 223 | # 224 | 225 | eval "set -- $( 226 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 227 | xargs -n1 | 228 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 229 | tr '\n' ' ' 230 | )" '"$@"' 231 | 232 | exec "$JAVACMD" "$@" -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /image/layout_manager.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/image/layout_manager.gif -------------------------------------------------------------------------------- /layout-manager/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /layout-manager/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-android' 4 | id 'maven-publish' 5 | id 'kotlin-parcelize' 6 | } 7 | android { 8 | namespace 'com.arc.fast.layoutmanager' 9 | compileSdk versions.compile_sdk 10 | defaultConfig { 11 | minSdk versions.min_sdk 12 | targetSdk versions.compile_sdk 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | compileOptions { 21 | sourceCompatibility JavaVersion.VERSION_11 22 | targetCompatibility JavaVersion.VERSION_11 23 | } 24 | kotlinOptions { 25 | jvmTarget = JavaVersion.VERSION_11 26 | } 27 | } 28 | dependencies { 29 | compileOnly 'androidx.recyclerview:recyclerview:1.2.1' 30 | } 31 | afterEvaluate { 32 | publishing { 33 | publications { 34 | release(MavenPublication) { 35 | from components.release 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /layout-manager/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /layout-manager/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /layout-manager/src/main/java/com/arc/fast/layoutmanager/FastRightSlideLayoutManager.kt: -------------------------------------------------------------------------------- 1 | package com.arc.fast.layoutmanager 2 | 3 | import android.graphics.PointF 4 | import android.util.Log 5 | import android.view.MotionEvent 6 | import android.view.View 7 | import android.view.View.OnTouchListener 8 | import android.view.ViewGroup 9 | import androidx.recyclerview.widget.RecyclerView 10 | import androidx.recyclerview.widget.SnapHelper 11 | import kotlin.math.abs 12 | import kotlin.math.ceil 13 | 14 | /** 15 | * 右滑LayoutManager 16 | */ 17 | class FastRightSlideLayoutManager( 18 | // 是否启用循环模式 19 | val enableLoop: Boolean = false, 20 | // 是否启用ViewPager模式 21 | val enablePagerMode: Boolean = false, 22 | // 是否启用自动翻页 23 | val enableAutoSlide: Boolean = false, 24 | // 自动翻页时间 25 | val autoSlideTime: Long = 3000L, 26 | // 默认显示出来的候选item数量 27 | val candidateCount: Int = 2, 28 | // 默认显示出来的最后一个候选item的缩放比例 29 | val candidateLastScale: Float = 0.8f, 30 | // 是否启用离开时透明效果 31 | val enableExitAlpha: Boolean = false, 32 | // 离开达到宽度的多少比例时开始启用透明效果 33 | val exitAlphaStartSign: Float = 0.1f, 34 | // 离开时透明效果的最终值 35 | val exitAlphaEndValue: Float = 0.6f, 36 | // 是否启用离开时缩放效果 37 | val enableExitScale: Boolean = true, 38 | // 横向的总空间偏移量 39 | val totalHorizontallySpaceOffset: Int = 0, 40 | // 当前Position更新事件 41 | var onCurrentPositionChangeListener: ((Int) -> Unit)? = null 42 | ) : RecyclerView.LayoutManager(), RecyclerView.SmoothScroller.ScrollVectorProvider { 43 | 44 | private var mRecyclerView: RecyclerView? = null 45 | 46 | // 是否已初始化 47 | private var mAttached = false 48 | private var mInit = false 49 | 50 | // 每个item的宽度 51 | private var mChildWidth = 0 52 | 53 | // 每个item的高度 54 | private var mChildHeight = 0 55 | 56 | // 横向的总空间 57 | private val mTotalHorizontallySpace: Int by lazy { width + totalHorizontallySpaceOffset } 58 | 59 | // 总偏移量 60 | private var mTotalOffset: Int = 0 61 | 62 | // 最大可偏移量 63 | private val mOffsetMax: Int get() = mChildWidth * (itemCount - 1) 64 | 65 | // 候选item的参数(第2个item开始为候选item) 66 | // 默认显示出来的最后一个候选item的高度 67 | private var mCandidateLastHeight = 0f 68 | 69 | // 候选item的缩放速度 70 | private var mCandidateScaleSpeed = 0f 71 | 72 | // 每个候选item显示的空间 73 | private var mCandidateEachDisplaySpace: Int = 0 74 | 75 | // 候选item的速度 76 | private var mCandidateSpeed: Float = 0f 77 | 78 | // 候选item的基础left 79 | private var mCandidateBaseLeft: Int = 0 80 | 81 | // item离开时的透明速度 82 | private var mExitAlphaSpeed = 1f 83 | 84 | // 离开达到left时开始启用透明效果 85 | private var mExitAlphaStartLeft = 0f 86 | 87 | // item离开时的缩放速度 88 | private var mExitScaleSpeed = 0f 89 | 90 | // 是否启用缩放功能 91 | private val mEnableScale by lazy { 92 | candidateLastScale < 1f 93 | } 94 | 95 | // 最后一个dx 96 | private var mLastDx: Int = 0 97 | 98 | // 当前第一个显示的item的position 99 | private val firstItemPosition: Int 100 | get() = if (mTotalOffset < 0) { 101 | (itemCount - ceil(abs(mTotalOffset).toDouble() / mChildWidth).toInt() % itemCount).let { 102 | if (it >= itemCount) 0 103 | else it 104 | } 105 | } else (mTotalOffset / mChildWidth) % itemCount 106 | 107 | // 当前第一个显示的item的left 108 | val firstItemLeft: Int 109 | get() = if (mTotalOffset % mChildWidth == 0) 0 110 | else if (mTotalOffset < 0) -mTotalOffset % mChildWidth - mChildWidth 111 | else -mTotalOffset % mChildWidth 112 | 113 | // 等待切换的位置 114 | private var mPendingPosition = RecyclerView.NO_POSITION 115 | 116 | // 自动翻页 117 | private val mAutoSlideRunnable by lazy { 118 | Runnable { 119 | if (enableAutoSlide) { 120 | mRecyclerView?.smoothScrollBy( 121 | calculateDistanceToPosition((firstItemPosition + 1).let { if (it > itemCount - 1) 0 else it }), 122 | 0 123 | ) 124 | startAutoSlide() 125 | } 126 | } 127 | } 128 | private var mAutoSlideInit = false 129 | private var mSmoothScrollToPosition = RecyclerView.NO_POSITION 130 | 131 | // 当前坐标 132 | var currentPosition: Int = RecyclerView.NO_POSITION 133 | private set 134 | 135 | // 触摸事件 136 | var onTouchListener: OnTouchListener? = null 137 | 138 | override fun onAttachedToWindow(view: RecyclerView?) { 139 | super.onAttachedToWindow(view) 140 | mRecyclerView = view 141 | if (!mAttached) { 142 | mAttached = true 143 | // 监听触摸事件 144 | view?.setOnTouchListener { v, event -> 145 | when (event.action) { 146 | MotionEvent.ACTION_DOWN -> { 147 | stopAutoSlide() 148 | } 149 | MotionEvent.ACTION_MOVE -> { 150 | stopAutoSlide() 151 | } 152 | MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_OUTSIDE -> { 153 | if (v.isPressed) { 154 | v.performClick() 155 | } 156 | startAutoSlide() 157 | } 158 | } 159 | onTouchListener?.onTouch(view, event) ?: false 160 | } 161 | // 设置惯性显示完整的item 162 | object : SnapHelper() { 163 | private var mDirection = 0 164 | override fun calculateDistanceToFinalSnap( 165 | layoutManager: RecyclerView.LayoutManager, targetView: View 166 | ): IntArray = 167 | intArrayOf( 168 | calculateDistanceToPosition(layoutManager.getPosition(targetView)), 169 | 0 170 | ) 171 | 172 | override fun findTargetSnapPosition( 173 | layoutManager: RecyclerView.LayoutManager, velocityX: Int, 174 | velocityY: Int 175 | ): Int { 176 | mDirection = velocityX 177 | return RecyclerView.NO_POSITION 178 | } 179 | 180 | override fun findSnapView(layoutManager: RecyclerView.LayoutManager): View? { 181 | val pos = getFixedScrollPosition(mDirection) 182 | mDirection = 0 183 | return if (pos != RecyclerView.NO_POSITION) layoutManager.findViewByPosition(pos) else null 184 | } 185 | }.attachToRecyclerView(view) 186 | } 187 | } 188 | 189 | override fun onLayoutCompleted(state: RecyclerView.State?) { 190 | super.onLayoutCompleted(state) 191 | if (itemCount <= 0 || mAutoSlideInit) { 192 | return 193 | } 194 | mAutoSlideInit = true 195 | startAutoSlide() 196 | } 197 | 198 | override fun onAdapterChanged( 199 | oldAdapter: RecyclerView.Adapter<*>?, 200 | newAdapter: RecyclerView.Adapter<*>? 201 | ) { 202 | mAutoSlideInit = false 203 | } 204 | 205 | 206 | override fun requestLayout() { 207 | super.requestLayout() 208 | mAutoSlideInit = false 209 | } 210 | 211 | override fun onDetachedFromWindow(view: RecyclerView?, recycler: RecyclerView.Recycler?) { 212 | super.onDetachedFromWindow(view, recycler) 213 | stopAutoSlide() 214 | } 215 | 216 | override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams { 217 | return RecyclerView.LayoutParams( 218 | ViewGroup.LayoutParams.WRAP_CONTENT, 219 | ViewGroup.LayoutParams.WRAP_CONTENT 220 | ) 221 | } 222 | 223 | override fun isAutoMeasureEnabled(): Boolean = true 224 | 225 | override fun canScrollHorizontally(): Boolean = true 226 | 227 | override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) { 228 | if (state.itemCount == 0) { 229 | removeAndRecycleAllViews(recycler) 230 | return 231 | } 232 | // 不支持预测动画,可以直接return 233 | if (state.isPreLayout) return 234 | // 跳转 235 | if (mPendingPosition != RecyclerView.NO_POSITION) { 236 | mTotalOffset = mPendingPosition * mChildWidth 237 | mPendingPosition = RecyclerView.NO_POSITION 238 | } 239 | //轻量级的将view移除屏幕 240 | detachAndScrapAttachedViews(recycler) 241 | // 填充 242 | if (!mInit) fill(recycler, false, 0, 0) 243 | else fill(recycler) 244 | } 245 | 246 | 247 | override fun scrollToPosition(position: Int) { 248 | if (position < 0 || position >= itemCount) return 249 | mPendingPosition = position 250 | requestLayout() 251 | } 252 | 253 | override fun smoothScrollToPosition( 254 | recyclerView: RecyclerView, 255 | state: RecyclerView.State, 256 | position: Int 257 | ) { 258 | mSmoothScrollToPosition = position 259 | stopAutoSlide() 260 | recyclerView.smoothScrollBy(calculateDistanceToPosition(position), 0) 261 | } 262 | 263 | 264 | override fun scrollHorizontallyBy( 265 | dx: Int, 266 | recycler: RecyclerView.Recycler, 267 | state: RecyclerView.State 268 | ): Int { 269 | if (childCount == 0 || dx == 0) return 0 270 | 271 | // dx超过边界时进行重新计算 272 | var actualDx = dx 273 | mTotalOffset += dx 274 | if (!enableLoop) { 275 | if (mTotalOffset < 0) { 276 | actualDx = mTotalOffset 277 | mTotalOffset = 0 278 | } else if (mTotalOffset > mOffsetMax) { 279 | actualDx = mTotalOffset - mOffsetMax 280 | mTotalOffset = mOffsetMax 281 | } 282 | } 283 | mLastDx = actualDx 284 | 285 | fill(recycler) 286 | 287 | return if (enablePagerMode) 1 else actualDx 288 | } 289 | 290 | 291 | private fun fill( 292 | recycler: RecyclerView.Recycler, 293 | recycle: Boolean = true, 294 | firstPosition: Int = firstItemPosition, 295 | firstLeft: Int = firstItemLeft 296 | ) { 297 | var fillPosition = firstPosition 298 | var fillLeft = firstLeft 299 | // 剩余空间 300 | var freeSpace = 0 301 | // 本次填充的相对position 302 | var relativePosition = 0 303 | 304 | //根据限定条件,不停地填充View进来 305 | val fillChilds = ArrayList() 306 | while (fillPosition in 0 until itemCount) { 307 | val itemView = recycler.getViewForPosition(fillPosition) 308 | fillChilds.add(itemView) 309 | addView(itemView, 0) 310 | // addView(itemView) 311 | measureChild(itemView, 0, 0) 312 | // 设置间距 313 | itemView.layoutParams = (itemView.layoutParams as ViewGroup.MarginLayoutParams).apply { 314 | leftMargin = paddingLeft 315 | topMargin = paddingTop 316 | rightMargin = paddingRight 317 | bottomMargin = paddingBottom 318 | } 319 | if (relativePosition == 0) { 320 | // 初始化公共参数 321 | if (!mInit) { 322 | mInit = true 323 | val child = getChildAt(0)!! 324 | mChildWidth = getDecoratedMeasuredWidth(child) + paddingLeft + paddingRight 325 | mChildHeight = getDecoratedMeasuredHeight(child) + paddingTop + paddingBottom 326 | mCandidateEachDisplaySpace = 327 | (mTotalHorizontallySpace - mChildWidth) / candidateCount 328 | mCandidateSpeed = mCandidateEachDisplaySpace.toFloat() / mChildWidth 329 | mCandidateBaseLeft = mCandidateEachDisplaySpace 330 | mCandidateLastHeight = mChildHeight * candidateLastScale 331 | mCandidateScaleSpeed = 332 | (1 - candidateLastScale) / (mTotalHorizontallySpace - mChildWidth) 333 | if (enableExitAlpha) { 334 | mExitAlphaSpeed = 335 | (1f - exitAlphaEndValue) / (mChildWidth * (1f - exitAlphaStartSign)) 336 | mExitAlphaStartLeft = -mChildWidth * exitAlphaStartSign 337 | } 338 | if (enableExitScale) { 339 | mExitScaleSpeed = (1 - candidateLastScale) / candidateCount / mChildWidth 340 | } 341 | } 342 | // 计算剩余空间 343 | freeSpace = mTotalHorizontallySpace - (fillLeft + mChildWidth) 344 | } 345 | 346 | //将要填充的View的左上右下 347 | var left: Int = 0 348 | var right: Int = 0 349 | var top = 0 350 | var bottom = top + mChildHeight 351 | var scale = 1f 352 | var alpha = 1f 353 | 354 | // 本次填充的第1个item 355 | if (relativePosition == 0) { 356 | left = fillLeft 357 | right = left + mChildWidth 358 | if (enableExitAlpha && left < mExitAlphaStartLeft) { 359 | alpha = 360 | exitAlphaEndValue + (mChildWidth - abs(left - mExitAlphaStartLeft)) * mExitAlphaSpeed 361 | } 362 | if (mEnableScale && enableExitScale && left < 0) { 363 | scale = 1f - abs(left) * mExitScaleSpeed 364 | } 365 | if (fillPosition != currentPosition) { 366 | currentPosition = fillPosition 367 | onCurrentPositionChangeListener?.invoke(currentPosition) 368 | } 369 | } 370 | // 本次填充的第2个item(第一个候选者) 371 | else if (relativePosition == 1) { 372 | val firstRigth = fillLeft + mChildWidth 373 | left = (mCandidateBaseLeft + fillLeft * mCandidateSpeed).toInt() 374 | right = left + mChildWidth 375 | scale = 376 | candidateLastScale + (mTotalHorizontallySpace - right) * mCandidateScaleSpeed 377 | fillLeft = left + mCandidateEachDisplaySpace 378 | freeSpace -= (right - firstRigth) 379 | // 根据缩放后会位移,需要重新记录left和right 380 | if (mEnableScale) { 381 | left += (mChildWidth * (1 - scale) / 2).toInt() 382 | right = left + mChildWidth 383 | } 384 | } 385 | // 本次填充的其他候选者 386 | else { 387 | left = fillLeft 388 | right = left + mChildWidth 389 | scale = 390 | candidateLastScale + (mTotalHorizontallySpace - right) * mCandidateScaleSpeed 391 | fillLeft = left + mCandidateEachDisplaySpace 392 | freeSpace -= mCandidateEachDisplaySpace 393 | // 根据缩放后会位移,需要重新记录left和right 394 | if (mEnableScale) { 395 | left += (mChildWidth * (1 - scale) / 2).toInt() 396 | right = left + mChildWidth 397 | } 398 | } 399 | 400 | // 填充到指定位置 401 | // layoutDecorated(itemView, left, top, right, bottom) 402 | layoutDecoratedWithMargins(itemView, left, top, right, bottom) 403 | if (mEnableScale && itemView.scaleX != scale) { 404 | itemView.scaleX = scale 405 | itemView.scaleY = scale 406 | // if (fillPosition == 2) { 407 | // Log.e("滑动", "$mLastDx scale:新:$scale 旧:$lastscale ") 408 | // if (firstPosition!=2 && scale != 1f && mLastDx > 0 && scale < lastscale) { 409 | // Log.e("滑动抖动", "scale:新:$scale 旧:$lastscale ") 410 | // } 411 | // lastscale = scale 412 | // } 413 | } 414 | itemView.alpha = alpha 415 | 416 | // 判断还有没有剩余空间 417 | if (freeSpace <= 0 || mCandidateEachDisplaySpace <= 0) break 418 | fillPosition++ 419 | // 无限循环 420 | if (enableLoop && fillPosition >= itemCount) { 421 | fillPosition = 0 422 | } 423 | relativePosition++ 424 | } 425 | 426 | if (recycle) { 427 | val recycleChilds = ArrayList() 428 | for (i in 0 until childCount) { 429 | val child = getChildAt(i) ?: return 430 | if (!fillChilds.contains(child)) { 431 | recycleChilds.add(child) 432 | } 433 | } 434 | recycleChilds.forEach { 435 | removeAndRecycleView(it, recycler) 436 | } 437 | recycleChilds.clear() 438 | } 439 | 440 | fillChilds.clear() 441 | } 442 | 443 | /** 444 | * 计算滚动到指定位置所需要的最近距离 445 | */ 446 | fun calculateDistanceToPosition(targetPos: Int): Int { 447 | val currentPos = firstItemPosition 448 | val distance = if (currentPos == targetPos) { 449 | if (mTotalOffset % mChildWidth == 0) 0 450 | else if (mTotalOffset >= 0) -mTotalOffset % mChildWidth 451 | else abs(mTotalOffset % mChildWidth) - mChildWidth 452 | } else if ((targetPos - currentPos).let { it == 1 || it == itemCount - 1 }) { 453 | if (mTotalOffset >= 0) mChildWidth - mTotalOffset % mChildWidth 454 | else abs(mTotalOffset % mChildWidth) 455 | } else if ((targetPos - currentPos).let { it == -1 || it == -itemCount + 1 }) { 456 | if (mTotalOffset >= 0) mChildWidth - mTotalOffset % mChildWidth 457 | else abs(mTotalOffset % mChildWidth) 458 | } 459 | // 向前循环的场景 460 | else if (mTotalOffset > mOffsetMax) { 461 | // <--计算假如向左边滚动所需的距离 462 | val leftPosDistance = 463 | if (targetPos >= currentPos) targetPos - currentPos else itemCount - currentPos + targetPos 464 | // -->计算假如向右边滚动所需的距离 465 | val rightPosDistance = 466 | if (targetPos <= currentPos) currentPos - targetPos else currentPos + (itemCount - targetPos) 467 | if (leftPosDistance < rightPosDistance) { 468 | // <--向左边滚动 + 469 | mChildWidth * (mTotalOffset / mChildWidth + leftPosDistance) - mTotalOffset 470 | } else { 471 | // -->向右边滚动 - 472 | mChildWidth * (mTotalOffset / mChildWidth - rightPosDistance) - mTotalOffset 473 | } 474 | } 475 | // 向后循环的场景 476 | else if (mTotalOffset < 0) { 477 | // <--计算假如向左边滚动所需的距离 478 | val leftPosDistance = 479 | if (targetPos >= currentPos) targetPos - currentPos else itemCount - currentPos + targetPos 480 | // -->计算假如向右边滚动所需的距离 481 | val rightPosDistance = 482 | if (targetPos <= currentPos) currentPos - targetPos else currentPos + (itemCount - targetPos) 483 | 484 | if (leftPosDistance < rightPosDistance) { 485 | // <--向左边滚动 + 486 | mChildWidth * (mTotalOffset / mChildWidth + leftPosDistance - 1) - mTotalOffset 487 | } else { 488 | // -->向右边滚动 - 489 | mChildWidth * (mTotalOffset / mChildWidth - rightPosDistance - 1) - mTotalOffset 490 | } 491 | } else mChildWidth * targetPos - mTotalOffset 492 | return distance 493 | } 494 | 495 | /** 496 | * 计算并返回滚动结束后停留的坐标 497 | */ 498 | fun getFixedScrollPosition(direction: Int): Int { 499 | var targetPosition = RecyclerView.NO_POSITION 500 | if (mInit) { 501 | if (mTotalOffset % mChildWidth != 0) { 502 | val itemPositionF = 503 | abs( 504 | if (mTotalOffset < 0) itemCount - abs(mTotalOffset.toFloat()) / mChildWidth % itemCount 505 | else if (mTotalOffset > mOffsetMax) mTotalOffset.toFloat() / mChildWidth % itemCount 506 | else mTotalOffset.toFloat() / mChildWidth 507 | ) 508 | val itemPosition = itemPositionF.toInt() 509 | Log.e( 510 | "getFixedScrollPosition", 511 | "itemPositionF:" + itemPositionF + ",itemPosition:" + itemPosition + ",direction:" + direction 512 | ) 513 | targetPosition = if (enablePagerMode) { 514 | if (mLastDx > 0) itemPosition + 1 else itemPosition 515 | } else { 516 | val offset = itemPositionF - itemPosition 517 | (if (direction > 0 && offset >= 0.3) itemPosition + 1 518 | else if (direction < 0 && offset >= 0.7) itemPosition + 1 519 | else if (direction == 0 && offset >= 0.4) itemPosition + 1 520 | else itemPosition) 521 | }.let { if (it >= itemCount) 0 else it } 522 | } else { 523 | val currentPosition = firstItemPosition 524 | if (mSmoothScrollToPosition != RecyclerView.NO_POSITION && mSmoothScrollToPosition == currentPosition) { 525 | startAutoSlide() 526 | } 527 | mSmoothScrollToPosition = RecyclerView.NO_POSITION 528 | } 529 | } 530 | Log.e("getFixedScrollPosition", "targetPosition:" + targetPosition) 531 | return targetPosition 532 | } 533 | 534 | /** 535 | * 停止定期自动滑动 536 | */ 537 | private fun stopAutoSlide() { 538 | mRecyclerView?.removeCallbacks(mAutoSlideRunnable) 539 | } 540 | 541 | /** 542 | * 开始定期自动滑动 543 | */ 544 | private fun startAutoSlide() { 545 | if (enableAutoSlide && itemCount > 1 && autoSlideTime > 0 && mRecyclerView != null) { 546 | stopAutoSlide() 547 | mRecyclerView?.postDelayed( 548 | mAutoSlideRunnable, 549 | autoSlideTime 550 | ) 551 | } 552 | } 553 | 554 | // 这里必须实现ScrollVectorProvider,才能使自定义SnapHelper接收到findTargetSnapPosition回调,从而获取到velocityX的值 555 | override fun computeScrollVectorForPosition(targetPosition: Int): PointF? { 556 | if (childCount == 0) { 557 | return null 558 | } 559 | val firstChildPos = getPosition(getChildAt(0)!!) 560 | val direction = if (targetPosition < firstChildPos) -1 else 1 561 | return PointF(direction.toFloat(), 0f) 562 | } 563 | 564 | } 565 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | id 'kotlin-kapt' 5 | id 'kotlin-parcelize' 6 | } 7 | android { 8 | compileSdk versions.compile_sdk 9 | namespace 'com.arc.fast.layoutmanager.sample' 10 | defaultConfig { 11 | applicationId "com.arc.fast.layoutmanager.sample" 12 | minSdk versions.min_sdk 13 | targetSdk versions.compile_sdk 14 | versionCode versions.publish_code 15 | versionName versions.publish_name 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | // signingConfigs { 19 | // release { 20 | // keyAlias 'sample' 21 | // keyPassword 'sample' 22 | // storeFile file('../sample.jks') 23 | // storePassword 'sample' 24 | // } 25 | // } 26 | buildTypes { 27 | release { 28 | minifyEnabled true 29 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 30 | // signingConfig signingConfigs.release 31 | } 32 | debug { 33 | minifyEnabled true 34 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 35 | // signingConfig signingConfigs.release 36 | } 37 | } 38 | compileOptions { 39 | sourceCompatibility JavaVersion.VERSION_11 40 | targetCompatibility JavaVersion.VERSION_11 41 | } 42 | kotlinOptions { 43 | jvmTarget = JavaVersion.VERSION_11 44 | } 45 | // dataBinding 46 | buildFeatures { 47 | dataBinding true 48 | } 49 | 50 | // 配置打包APK的名称 51 | // android.applicationVariants.all { 52 | // variant -> 53 | // def name = "" 54 | // if (variant.buildType.name.contains('release')) { 55 | // name = "release" 56 | // } else { 57 | // name = "debug" 58 | // } 59 | // variant.outputs.all { 60 | // outputFileName = "${defaultConfig.applicationId}_${name}_${variant.versionName}(${variant.versionCode}).apk" 61 | // } 62 | // } 63 | } 64 | dependencies { 65 | implementation 'androidx.core:core-ktx:1.9.0' 66 | implementation 'androidx.appcompat:appcompat:1.6.0' 67 | implementation 'com.google.android.material:material:1.8.0' 68 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 69 | implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.7' 70 | implementation 'com.github.Arcns.arc-fast:core:1.23.1' 71 | implementation 'com.github.Arcns.arc-fast:immersive:1.23.1' 72 | // fast-layout-manager 73 | implementation project(path: ':layout-manager') 74 | // implementation 'com.github.Arcns:fast-layout-manager:1.0.0' 75 | 76 | } -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /sample/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/sample/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /sample/src/main/java/com/arc/fast/layoutmanager/sample/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.arc.fast.layoutmanager.sample 2 | 3 | import android.os.Bundle 4 | import android.widget.ImageView 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.constraintlayout.widget.ConstraintLayout 7 | import androidx.databinding.DataBindingUtil 8 | import com.arc.fast.core.screenWidth 9 | import com.arc.fast.layoutmanager.FastRightSlideLayoutManager 10 | import com.arc.fast.layoutmanager.sample.databinding.ActivityMainBinding 11 | import com.arc.fast.layoutmanager.sample.extension.applyFullScreen 12 | import com.arc.fast.layoutmanager.sample.extension.setLightSystemBar 13 | import com.chad.library.adapter.base.BaseQuickAdapter 14 | import com.chad.library.adapter.base.viewholder.BaseViewHolder 15 | import kotlin.math.roundToInt 16 | 17 | class MainActivity : AppCompatActivity() { 18 | 19 | lateinit var binding: ActivityMainBinding 20 | 21 | override fun onCreate(savedInstanceState: Bundle?) { 22 | super.onCreate(savedInstanceState) 23 | applyFullScreen() 24 | setLightSystemBar(true) 25 | binding = DataBindingUtil.setContentView(this, R.layout.activity_main) 26 | binding.rv.layoutManager = FastRightSlideLayoutManager( 27 | // 是否启用循环模式 28 | enableLoop = true, 29 | // 是否启用ViewPager模式 30 | enablePagerMode = false, 31 | // 是否启用自动翻页 32 | enableAutoSlide = true, 33 | // 自动翻页时间 34 | autoSlideTime = 3000L, 35 | // 默认显示出来的候选item数量 36 | candidateCount = 2, 37 | // 默认显示出来的最后一个候选item的缩放比例 38 | candidateLastScale = 0.8f, 39 | // 是否启用离开时透明效果 40 | enableExitAlpha = true, 41 | // 离开达到宽度的多少比例时开始启用透明效果 42 | exitAlphaStartSign = 0.1f, 43 | // 离开时透明效果的最终值 44 | exitAlphaEndValue = 0.6f, 45 | // 是否启用离开时缩放效果 46 | enableExitScale = true, 47 | // 横向的总空间偏移量 48 | totalHorizontallySpaceOffset = 0, 49 | // 当前Position更新事件 50 | onCurrentPositionChangeListener = null 51 | ) 52 | val itemWidth = (screenWidth * 0.8f).roundToInt() 53 | val data = arrayListOf(R.mipmap.s1, R.mipmap.s2, R.mipmap.s3, R.mipmap.s4) 54 | binding.rv.adapter = 55 | object : BaseQuickAdapter(R.layout.item_layout, data) { 56 | override fun convert(holder: BaseViewHolder, item: Int) { 57 | // 注意:item的宽度必须小于RecyclerView的宽度,否则无法计算出其他候选item的位置 58 | holder.getView(R.id.cl_root).layoutParams.width = itemWidth 59 | holder.getView(R.id.iv_image).setImageResource(item) 60 | } 61 | } 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/arc/fast/layoutmanager/sample/extension/BindingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.arc.fast.layoutmanager.sample.extension 2 | 3 | import android.view.View 4 | import androidx.databinding.BindingAdapter 5 | import com.arc.fast.immersive.applySystemWindowsInsetsMargin 6 | import com.arc.fast.immersive.applySystemWindowsInsetsPadding 7 | 8 | 9 | @BindingAdapter( 10 | "paddingLeftSystemWindowInsets", 11 | "paddingTopSystemWindowInsets", 12 | "paddingRightSystemWindowInsets", 13 | "paddingBottomSystemWindowInsets", 14 | requireAll = false 15 | ) 16 | fun applySystemWindowsInsetsPadding( 17 | view: View, 18 | applyLeft: Boolean, 19 | applyTop: Boolean, 20 | applyRight: Boolean, 21 | applyBottom: Boolean 22 | ) { 23 | view.applySystemWindowsInsetsPadding(applyLeft, applyTop, applyRight, applyBottom) 24 | } 25 | 26 | 27 | @BindingAdapter( 28 | "marginLeftSystemWindowInsets", 29 | "marginTopSystemWindowInsets", 30 | "marginRightSystemWindowInsets", 31 | "marginBottomSystemWindowInsets", 32 | requireAll = false 33 | ) 34 | fun applySystemWindowsInsetsMargin( 35 | view: View, 36 | applyLeft: Boolean, 37 | applyTop: Boolean, 38 | applyRight: Boolean, 39 | applyBottom: Boolean 40 | ) { 41 | view.applySystemWindowsInsetsMargin(applyLeft, applyTop, applyRight, applyBottom) 42 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/arc/fast/layoutmanager/sample/extension/ViewExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.arc.fast.layoutmanager.sample.extension 2 | 3 | import android.app.Activity 4 | import android.graphics.Color 5 | import android.os.Build 6 | import android.view.WindowManager 7 | import android.widget.TextView 8 | import androidx.appcompat.widget.Toolbar 9 | import androidx.core.view.WindowCompat 10 | import androidx.core.view.WindowInsetsControllerCompat 11 | 12 | 13 | fun Activity.applyFullScreen() { 14 | WindowCompat.setDecorFitsSystemWindows(window, false) 15 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 16 | window.attributes = window.attributes.apply { 17 | layoutInDisplayCutoutMode = 18 | WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 19 | } 20 | } 21 | window.navigationBarColor = Color.TRANSPARENT 22 | window.statusBarColor = Color.TRANSPARENT 23 | } 24 | 25 | fun Activity.setLightSystemBar(value: Boolean) { 26 | WindowInsetsControllerCompat(window, window.decorView).isAppearanceLightStatusBars = value 27 | WindowInsetsControllerCompat(window, window.decorView).isAppearanceLightNavigationBars = value 28 | } 29 | 30 | val Toolbar.titleTextView: TextView? 31 | get() { 32 | (0..childCount).forEach { 33 | val view = getChildAt(it) 34 | if (view is TextView && (title.isNullOrBlank() || view.text.toString() == title.toString())) { 35 | return view 36 | } 37 | } 38 | return null 39 | } -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 15 | 23 | 24 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/item_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/sample/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/sample/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/sample/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/sample/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/s1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/sample/src/main/res/mipmap-xxhdpi/s1.webp -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/s2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/sample/src/main/res/mipmap-xxhdpi/s2.webp -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/s3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/sample/src/main/res/mipmap-xxhdpi/s3.webp -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/s4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/sample/src/main/res/mipmap-xxhdpi/s4.webp -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcns/fast-layout-manager/978c1a3c4b46189b0b088aa6a8beeb1c2cd9774d/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | #7b7c7d 3 | #006780 4 | #FFFFFF 5 | #B8EAFF 6 | #001F28 7 | #006C46 8 | #FFFFFF 9 | #8FF7BF 10 | #002112 11 | #6A5F00 12 | #FFFFFF 13 | #F7E467 14 | #201C00 15 | #BA1A1A 16 | #FFDAD6 17 | #FFFFFF 18 | #410002 19 | #FDFCFF 20 | #001C3A 21 | #FDFCFF 22 | #001C3A 23 | #DCE4E8 24 | #40484C 25 | #70787C 26 | #EBF1FF 27 | #00315E 28 | #5ED4FC 29 | #000000 30 | #006780 31 | #006780 32 | #5ED4FC 33 | #003544 34 | #004D61 35 | #B8EAFF 36 | #73DAA5 37 | #003822 38 | #005234 39 | #8FF7BF 40 | #DAC84E 41 | #373100 42 | #504700 43 | #F7E467 44 | #FFB4AB 45 | #93000A 46 | #690005 47 | #FFDAD6 48 | #001C3A 49 | #D4E3FF 50 | #001C3A 51 | #D4E3FF 52 | #40484C 53 | #BFC8CC 54 | #8A9296 55 | #001C3A 56 | #D4E3FF 57 | #006780 58 | #000000 59 | #5ED4FC 60 | #5ED4FC 61 | 62 | #42006780 63 | #FFF73173 64 | #FFFFFF 65 | #F7F7F7 66 | #000000 67 | #66000000 68 | #99000000 69 | #4D000000 70 | #FFFFFF 71 | #66FFFFFF 72 | #99FFFFFF 73 | #4DFFFFFF 74 | -------------------------------------------------------------------------------- /sample/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #686868 4 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Fast Layout Manager 3 | -------------------------------------------------------------------------------- /sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /sample/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 42 | 43 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "fast-layout-manager" 2 | include ':layout-manager' 3 | include ':sample' 4 | --------------------------------------------------------------------------------