├── .gitignore ├── LICENSE ├── README-ZH.md ├── README.md ├── apk └── app-debug.apk ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── ckr │ │ └── flexitemdecoration │ │ ├── MainActivity.java │ │ ├── adapter │ │ ├── HorizontalGridAdapter.java │ │ ├── MainAdapter.java │ │ └── MyFragmentPagerAdapter.java │ │ ├── model │ │ ├── Header.java │ │ └── UserInfo.java │ │ ├── view │ │ ├── BaseFragment.java │ │ ├── HorizontalGridFragment.java │ │ ├── LinearFragment.java │ │ └── VerticalGridFragment.java │ │ └── widget │ │ └── MyViewPager.java │ └── res │ ├── drawable │ ├── bg_divider_list.xml │ ├── bg_divider_offset.xml │ ├── bg_divider_offset_grid.xml │ ├── bg_divider_redraw.xml │ ├── bg_divider_redraw_grid.xml │ └── bg_tablayout.xml │ ├── layout │ ├── activity_main.xml │ ├── fragment_horizontal_grid.xml │ ├── fragment_main.xml │ ├── item_picture.xml │ └── item_picture_horizontal.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── banner2.png │ ├── ic_launcher.png │ ├── ic_launcher_round.png │ └── ic_person.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── raw │ └── user.json │ ├── values-zh │ └── strings.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── config.gradle ├── decoration ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── ckr │ │ └── decoration │ │ ├── BaseItemDecoration.java │ │ ├── DecorationLog.java │ │ ├── DividerGridItemDecoration.java │ │ ├── DividerLinearItemDecoration.java │ │ └── OnHeaderListener.java │ └── res │ ├── drawable │ └── bg_decoration.xml │ ├── layout │ └── item_header.xml │ └── values │ └── strings.xml ├── gradle.properties ├── gradlew ├── gradlew.bat ├── screenshot ├── Screenshot_1.png ├── Screenshot_10.png ├── Screenshot_2.png ├── Screenshot_3.png ├── Screenshot_4.png ├── Screenshot_5.png ├── Screenshot_6.png ├── Screenshot_7.png ├── Screenshot_8.png └── Screenshot_9.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /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 {yyyy} {name of copyright owner} 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-ZH.md: -------------------------------------------------------------------------------- 1 | # FlexItemDecoration 2 | 灵活的分割线,可绘制头部、底部、最左边、最右边分割线,还可以定制某一行的分割线和批量定制多行分割线。 3 | 4 | ## 效果图 5 | | vertical-grid | vertical-grid-2 | vertical-linear | 6 | | -------------------------------- | -------------------------------- | -------------------------------- | 7 | | ![](screenshot/Screenshot_1.png) | ![](screenshot/Screenshot_3.png) | ![](screenshot/Screenshot_4.png) | 8 | | custom-line-linear | custom-line-grid | stiky-header | 9 | | -------------------------------- | -------------------------------- | -------------------------------- | 10 | | ![](screenshot/Screenshot_6.png) | ![](screenshot/Screenshot_9.png) | ![](screenshot/Screenshot_10.png) | 11 | 12 | ## Demo 13 | [下载 APK](apk/app-debug.apk) 14 | 15 | ## 依赖 16 | #### 添加依赖: 17 | ``` 18 | dependencies { 19 | implementation 'ckrjfrog.FlexItemDecoration:Decoration:1.1.3'//gradle plugin 3.0(包含)以上使用 20 | //compile 'ckrjfrog.FlexItemDecoration:Decoration:1.1.3'//gradle plugin 3.0一下使用 21 | } 22 | ``` 23 | 24 | ## 功能及使用 25 | #### 1.网格分割线的使用 26 | ``` 27 | DividerGridItemDecoration.Builder builder = new DividerGridItemDecoration.Builder(context,orientation,SPAN_COUNT);//SPAN_COUNT:列数,orientation:水平或竖直方向 28 | builder.setDivider(R.drawable.bg_divider_list)//设置分割线的颜色及宽高 29 | .setShowOtherStyle(false)//另一种方式显示网格分割线 30 | .removeHeaderDivider(false)//是否移除头部分割线 31 | .removeFooterDivider(false)//是否移除底部分割线 32 | .removeLeftDivider(false)//是否移除最左边分割线 33 | .removeRightDivider(false)//是否移除最右边分割线 34 | .subDivider(1, 4)//分割线截取绘制,1:开始下标,4:结束下标 35 | .setSubDividerHeight(24)//设置截取分割线的高度,在竖直方向有效 36 | .setSubDividerWidth(24)//设置截取分割线的宽度,在水平方向有效 37 | .setSubDividerDrawable(R.drawable.bg_divider_offset_grid)//设置截取分割线的样式 38 | .redrawDivider(2)//分割线定制的下标 39 | .redrawDividerHeight(30)//定制分割线的高度,在竖直方向有效 40 | .redrawDividerWidth(30)//定制分割线的宽度,在水平方向有效 41 | .redrawDividerDrawable(R.drawable.bg_divider_redraw_grid)//定制分割线的样式 42 | .redrawHeaderDivider()//头部分割线的定制,在竖直方向有效 43 | .redrawHeaderDividerHeight(40)//定制头部分割线的高度 44 |                       .redrawHeaderDividerDrawable(R.drawable.bg_divider_offset_grid);//定制头部分割线的样式 45 | .redrawFooterDivider()//底部分割线的定制,在竖直方向有效 46 | .redrawFooterDividerHeight(40)//定制底部分割线的高度 47 | .redrawFooterDividerDrawable(R.drawable.bg_divider_offset_grid)//定制底部分割线的样式 48 | .redrawLeftDivider()//最左边分割线的定制,在水平方向有效 49 |                       .redrawLeftDividerWidth(40)//定制最左边分割线的宽度 50 |                       .redrawLeftDividerDrawable(R.drawable.bg_divider_list)//定制最左边分割线的样式 51 | .redrawRightDivider()//最右边分割线的定制,在水平方向有效 52 |                       .redrawRightDividerWidth(40)//定制最右边分割线的宽度 53 |                       .redrawRightDividerDrawable(R.drawable.bg_divider_list);//定制最右边分割线的样式 54 | recyclerView.addItemDecoration(builder.build()); 55 | ``` 56 | 57 | #### 2.线性分割线的使用 58 | ``` 59 | DividerLinearItemDecoration.Builder builder = new DividerLinearItemDecoration.Builder(context, orientation);//orientation:方向 60 | builder.setDivider(R.drawable.bg_divider_list)//设置分割线的颜色及宽高 61 | .removeHeaderDivider(false)//是否移除头部分割线,在竖直方向有效 62 | .removeFooterDivider(false)//是否移除底部分割线,在竖直方向有效 63 | .removeLeftDivider(false)//是否移除最左边分割线,在水平方向有效 64 | .removeRightDivider(false)//是否移除最右边分割线,在水平方向有效 65 | .subDivider(1, 4);//分割线截取绘制,1:开始下标,4:结束下标 66 | .setSubDividerHeight(24)//设置截取分割线的高度,在竖直方向有效 67 | .setSubDividerWidth(24)//设置截取分割线的宽度,在水平方向有效 68 | .setSubDividerDrawable(R.drawable.bg_divider_offset)//设置截取分割线的样式 69 | .redrawDivider(2)//分割线定制的下标 70 | .redrawDividerHeight(30)//定制分割线的高度,在竖直方向有效 71 | .redrawDividerWidth(30)//定制分割线的宽度,在水平方向有效 72 | .redrawDividerDrawable(R.drawable.bg_divider_redraw)//定制分割线的样式 73 | .redrawHeaderDivider()//头部分割线的定制,在竖直方向有效 74 | .redrawHeaderDividerHeight(40)//定制头部分割线的高度 75 | .redrawHeaderDividerDrawable(R.drawable.bg_divider_offset);//定制头部分割线的样式 76 | .redrawFooterDivider()//底部分割线的定制,在竖直方向有效 77 | .redrawFooterDividerHeight(40)//定制底部分割线的高度 78 | .redrawFooterDividerDrawable(R.drawable.bg_divider_offset)//定制底部分割线的样式 79 | .redrawLeftDivider()//最左边分割线的定制,在水平方向有效 80 |                       .redrawLeftDividerWidth(40)//定制最左边分割线的宽度 81 |                       .redrawLeftDividerDrawable(R.drawable.bg_divider_list)//定制最左边分割线的样式 82 | .redrawRightDivider()//最右边分割线的定制,在水平方向有效 83 |                       .redrawRightDividerWidth(40)//定制最右边分割线的宽度 84 |                       .redrawRightDividerDrawable(R.drawable.bg_divider_list);//定制最右边分割线的样式 85 | recyclerView.addItemDecoration(builder.build()); 86 | ``` 87 | ## 我的开源项目 88 | [PageRecyclerView](https://github.com/ckrgithub/PageRecyclerView):自定义RecyclerView实现翻页功能及无限轮播 89 | 90 | [CollapsingRefresh](https://github.com/ckrgithub/CollapsingRefresh):AppBarLayout+ViewPager+RecyclerView的刷新功能 91 | 92 | License 93 | ------- 94 | 95 | Copyright 2018 ckrgithub 96 | 97 | Licensed under the Apache License, Version 2.0 (the "License"); 98 | you may not use this file except in compliance with the License. 99 | You may obtain a copy of the License at 100 | 101 | http://www.apache.org/licenses/LICENSE-2.0 102 | 103 | Unless required by applicable law or agreed to in writing, software 104 | distributed under the License is distributed on an "AS IS" BASIS, 105 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 106 | See the License for the specific language governing permissions and 107 | limitations under the License. 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlexItemDecoration 2 | FlexItemDecoration, can customize the head, bottom, leftmost, rightmost dividing line, but also customize any one of the dividing lines and batch custom multiple dividing lines [中文文档](README-ZH.md). 3 | 4 | ## Effect 5 | | vertical-grid | vertical-grid-2 | vertical-linear | 6 | | -------------------------------- | -------------------------------- | -------------------------------- | 7 | | ![](screenshot/Screenshot_1.png) | ![](screenshot/Screenshot_3.png) | ![](screenshot/Screenshot_4.png) | 8 | 9 | | custom-line-linear | custom-line-grid | stiky-header | 10 | | -------------------------------- | -------------------------------- | -------------------------------- | 11 | | ![](screenshot/Screenshot_6.png) | ![](screenshot/Screenshot_9.png) | ![](screenshot/Screenshot_10.png) | 12 | 13 | ## Demo 14 | [Download APK](apk/app-debug.apk) 15 | 16 | ## Dependencies 17 | #### add dependencies: 18 | ``` 19 | dependencies { 20 | implementation 'ckrjfrog.FlexItemDecoration:Decoration:1.1.3'//gradle plugin 3.0(inclusive) above used 21 | //compile 'ckrjfrog.FlexItemDecoration:Decoration:1.1.3'//gradle plugin 3.0 below used 22 | } 23 | ``` 24 | 25 | ## Function And Use 26 | #### 1.DividerGridItemDecoration 27 | ``` 28 | DividerGridItemDecoration.Builder builder = new DividerGridItemDecoration.Builder(context,orientation,SPAN_COUNT); 29 | builder.setDivider(R.drawable.bg_divider_list)//set the drawable of the dividing line 30 | .setShowOtherStyle(false) 31 | .removeHeaderDivider(false) 32 | .removeFooterDivider(false) 33 | .removeLeftDivider(false) 34 | .removeRightDivider(false) 35 | .subDivider(1, 4)//custom multiple lines 36 | .setSubDividerHeight(24)//valid in vertical direction 37 | .setSubDividerWidth(24)//valid in horizontal direction 38 | .setSubDividerDrawable(R.drawable.bg_divider_offset_grid)//set the drawable of the multiple dividing lines 39 | .redrawDivider(2)//custom any one of the dividing lines(exclude the head,bottom,leftmost,rightmost dividing line) 40 | .redrawDividerHeight(30)//valid in vertical direction 41 | .redrawDividerWidth(30)//valid in horizontal direction 42 | .redrawDividerDrawable(R.drawable.bg_divider_redraw_grid)//set the drawable of the dividing line 43 | .redrawHeaderDivider()//custom the head dividing line,valid in vertical direction 44 | .redrawHeaderDividerHeight(40) 45 |                       .redrawHeaderDividerDrawable(R.drawable.bg_divider_offset_grid); 46 | .redrawFooterDivider()//custom the bottom dividing line,valid in vertical direction 47 | .redrawFooterDividerHeight(40) 48 | .redrawFooterDividerDrawable(R.drawable.bg_divider_offset_grid) 49 | .redrawLeftDivider()//custom the leftmost dividing line,valid in horizontal direction 50 |                       .redrawLeftDividerWidth(40) 51 |                       .redrawLeftDividerDrawable(R.drawable.bg_divider_list) 52 | .redrawRightDivider()//custom the rightmost dividing line,valid in horizontal direction 53 |                       .redrawRightDividerWidth(40) 54 |                       .redrawRightDividerDrawable(R.drawable.bg_divider_list); 55 | recyclerView.addItemDecoration(builder.build()); 56 | ``` 57 | 58 | #### 2.DividerLinearItemDecoration 59 | ``` 60 | DividerLinearItemDecoration.Builder builder = new DividerLinearItemDecoration.Builder(context, orientation);//orientation:方向 61 | builder.setDivider(R.drawable.bg_divider_list)//set the drawable of the dividing line 62 | .removeHeaderDivider(false)//valid in vertical direction 63 | .removeFooterDivider(false)//valid in vertical direction 64 | .removeLeftDivider(false)//valid in horizontal direction 65 | .removeRightDivider(false)//valid in horizontal direction 66 | .subDivider(1, 4);//custom multiple lines 67 | .setSubDividerHeight(24)//valid in vertical direction 68 | .setSubDividerWidth(24)//valid in horizontal direction 69 | .setSubDividerDrawable(R.drawable.bg_divider_offset) 70 | .redrawDivider(2)//custom any one of the dividing lines(exclude the head,bottom,leftmost,rightmost dividing line) 71 | .redrawDividerHeight(30)//valid in vertical direction 72 | .redrawDividerWidth(30)//valid in horizontal direction 73 | .redrawDividerDrawable(R.drawable.bg_divider_redraw) 74 | .redrawHeaderDivider()//custom the head dividing line,valid in vertical direction 75 | .redrawHeaderDividerHeight(40) 76 | .redrawHeaderDividerDrawable(R.drawable.bg_divider_offset); 77 | .redrawFooterDivider()//custom the bottom dividing line,valid in vertical direction 78 | .redrawFooterDividerHeight(40) 79 | .redrawFooterDividerDrawable(R.drawable.bg_divider_offset) 80 | .redrawLeftDivider()//custom the leftmost dividing line,valid in horizontal direction 81 |                       .redrawLeftDividerWidth(40) 82 |                       .redrawLeftDividerDrawable(R.drawable.bg_divider_list) 83 | .redrawRightDivider()custom the rightmost dividing line,valid in horizontal direction 84 |                       .redrawRightDividerWidth(40) 85 |                       .redrawRightDividerDrawable(R.drawable.bg_divider_list); 86 | recyclerView.addItemDecoration(builder.build()); 87 | ``` 88 | ## My Other Project 89 | [PageRecyclerView](https://github.com/ckrgithub/PageRecyclerView) 90 | 91 | [CollapsingRefresh](https://github.com/ckrgithub/CollapsingRefresh) 92 | 93 | License 94 | ------- 95 | 96 | Copyright 2018 ckrgithub 97 | 98 | Licensed under the Apache License, Version 2.0 (the "License"); 99 | you may not use this file except in compliance with the License. 100 | You may obtain a copy of the License at 101 | 102 | http://www.apache.org/licenses/LICENSE-2.0 103 | 104 | Unless required by applicable law or agreed to in writing, software 105 | distributed under the License is distributed on an "AS IS" BASIS, 106 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 107 | See the License for the specific language governing permissions and 108 | limitations under the License. 109 | -------------------------------------------------------------------------------- /apk/app-debug.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/apk/app-debug.apk -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'android-apt' 3 | 4 | android { 5 | compileSdkVersion rootProject.ext.androidConfig.compileSdkVersion 6 | buildToolsVersion rootProject.ext.androidConfig.buildToolsVersion 7 | defaultConfig { 8 | applicationId rootProject.ext.appConfig.applicationId 9 | minSdkVersion rootProject.ext.androidConfig.minSdkVersion 10 | targetSdkVersion rootProject.ext.androidConfig.targetSdkVersion 11 | versionCode rootProject.ext.appConfig.versionCode 12 | versionName rootProject.ext.appConfig.versionName 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | lintOptions { 21 | abortOnError false 22 | } 23 | } 24 | 25 | dependencies { 26 | compile fileTree(include: ['*.jar'], dir: 'libs') 27 | compile "com.android.support:appcompat-v7:$rootProject.supportVersion" 28 | compile "com.android.support:recyclerview-v7:$rootProject.supportVersion" 29 | compile "com.android.support:cardview-v7:$rootProject.supportVersion" 30 | compile "com.android.support:design:$rootProject.supportVersion" 31 | compile 'com.jakewharton:butterknife:8.8.1' 32 | apt 'com.jakewharton:butterknife-compiler:8.8.1' 33 | compile 'com.google.code.gson:gson:2.8.5' 34 | // compile project(':decoration') 35 | compile 'ckrjfrog.FlexItemDecoration:Decoration:1.1.3' 36 | } 37 | -------------------------------------------------------------------------------- /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 D:\Android\sdk1/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 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/ckr/flexitemdecoration/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.ckr.flexitemdecoration; 2 | 3 | import android.os.Bundle; 4 | import android.support.design.widget.TabLayout; 5 | import android.support.v4.app.FragmentManager; 6 | import android.support.v4.view.ViewPager; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.util.Log; 9 | import android.view.Menu; 10 | import android.view.MenuItem; 11 | 12 | import com.ckr.decoration.BaseItemDecoration; 13 | import com.ckr.decoration.DecorationLog; 14 | import com.ckr.flexitemdecoration.adapter.MyFragmentPagerAdapter; 15 | import com.ckr.flexitemdecoration.view.BaseFragment; 16 | import com.ckr.flexitemdecoration.view.HorizontalGridFragment; 17 | import com.ckr.flexitemdecoration.view.LinearFragment; 18 | import com.ckr.flexitemdecoration.view.VerticalGridFragment; 19 | 20 | import java.util.ArrayList; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | 24 | import butterknife.BindView; 25 | import butterknife.ButterKnife; 26 | import butterknife.Unbinder; 27 | 28 | public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener { 29 | private static final String TAG = "MainActivity"; 30 | private static final String PAGE = "page"; 31 | @BindView(R.id.viewPager) 32 | ViewPager viewPager; 33 | @BindView(R.id.tabLayout) 34 | TabLayout tabLayout; 35 | private Menu menu; 36 | private Unbinder unBinder; 37 | private ArrayList fragmentList; 38 | private int currentPage = 0; 39 | public Map map; 40 | public static final int[] MENU_ITEM_ID = {R.id.item1, R.id.item2, R.id.item3, R.id.item4, R.id.item5, R.id.item6, R.id.item7, R.id.item8, R.id.item9, R.id.item10, R.id.item11}; 41 | public boolean[] is_checked = {true, false, false, false, false, false, false, false, false, false, false}; 42 | public static final String[] TITLES = {"垂直网格", "水平网格", "垂直网格2", "垂直线性", "水平线性"}; 43 | private FragmentManager fragmentManager; 44 | 45 | @Override 46 | protected void onCreate(Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | DecorationLog.debug(); 49 | if (savedInstanceState == null) { 50 | } else { 51 | currentPage = savedInstanceState.getInt(PAGE); 52 | } 53 | setContentView(R.layout.activity_main); 54 | unBinder = ButterKnife.bind(this); 55 | initFragment(); 56 | initView(); 57 | map = new HashMap<>(MENU_ITEM_ID.length); 58 | for (int i = 0; i < MENU_ITEM_ID.length; i++) { 59 | map.put(MENU_ITEM_ID[i], i); 60 | } 61 | } 62 | 63 | @Override 64 | protected void onSaveInstanceState(Bundle outState) { 65 | super.onSaveInstanceState(outState); 66 | outState.putInt(PAGE, currentPage); 67 | } 68 | 69 | private static String makeFragmentName(int viewId, long id) { 70 | return "android:switcher:" + viewId + ":" + id; 71 | } 72 | 73 | private void initFragment() { 74 | fragmentManager = getSupportFragmentManager(); 75 | fragmentList = new ArrayList<>(); 76 | for (int i = 0; i < TITLES.length; i++) { 77 | String name = makeFragmentName(R.id.viewPager, i); 78 | BaseFragment fragment = (BaseFragment) fragmentManager.findFragmentByTag(name); 79 | if (fragment == null) { 80 | if (i == 0) { 81 | fragmentList.add(VerticalGridFragment.newInstance(false)); 82 | } else if (i == 1) { 83 | fragmentList.add(HorizontalGridFragment.newInstance()); 84 | } else if (i == 2) { 85 | fragmentList.add(VerticalGridFragment.newInstance(true)); 86 | } else if (i == 3) { 87 | fragmentList.add(LinearFragment.newInstance(BaseItemDecoration.VERTICAL)); 88 | } else if (i == 4) { 89 | fragmentList.add(LinearFragment.newInstance(BaseItemDecoration.HORIZONTAL)); 90 | } 91 | } else { 92 | fragmentList.add(fragment); 93 | } 94 | } 95 | } 96 | 97 | private void initView() { 98 | tabLayout.addTab(tabLayout.newTab().setText(TITLES[currentPage]), currentPage, true); 99 | viewPager.setAdapter(new MyFragmentPagerAdapter(fragmentManager, fragmentList, TITLES)); 100 | tabLayout.setupWithViewPager(viewPager); 101 | viewPager.addOnPageChangeListener(this); 102 | viewPager.setCurrentItem(currentPage,false); 103 | } 104 | 105 | @Override 106 | protected void onDestroy() { 107 | super.onDestroy(); 108 | unBinder.unbind(); 109 | } 110 | 111 | @Override 112 | public boolean onCreateOptionsMenu(Menu menu) { 113 | this.menu = menu; 114 | getMenuInflater().inflate(R.menu.menu_main, this.menu); 115 | return true; 116 | } 117 | 118 | @Override 119 | public boolean onOptionsItemSelected(MenuItem item) { 120 | Log.d(TAG, "onOptionsItemSelected: id:" + item.getItemId()); 121 | switch (item.getItemId()) { 122 | case R.id.item1: 123 | if (!item.isChecked()) { 124 | for (int i = 1; i < MENU_ITEM_ID.length; i++) { 125 | disableChecked(i); 126 | } 127 | break; 128 | } else { 129 | return true; 130 | } 131 | case R.id.item2: 132 | case R.id.item3: 133 | case R.id.item4: 134 | case R.id.item5: 135 | if (!item.isChecked()) { 136 | disableChecked(0); 137 | } 138 | break; 139 | case R.id.item6: 140 | case R.id.item7: 141 | case R.id.item8: 142 | case R.id.item9: 143 | case R.id.item10: 144 | case R.id.item11: 145 | break; 146 | default: 147 | return super.onOptionsItemSelected(item); 148 | } 149 | boolean checked = !item.isChecked(); 150 | item.setChecked(checked); 151 | Integer index = map.get(item.getItemId()); 152 | is_checked[index] = checked; 153 | fragmentList.get(currentPage).refreshFragment(is_checked); 154 | return true; 155 | } 156 | 157 | private void disableChecked(int pos) { 158 | MenuItem menuItem = menu.findItem(MENU_ITEM_ID[pos]); 159 | if (menuItem.isChecked()) { 160 | menuItem.setChecked(false); 161 | } 162 | is_checked[pos] = false; 163 | } 164 | 165 | @Override 166 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 167 | 168 | } 169 | 170 | @Override 171 | public void onPageSelected(int position) { 172 | currentPage = position; 173 | fragmentList.get(currentPage).refreshFragment(is_checked); 174 | } 175 | 176 | @Override 177 | public void onPageScrollStateChanged(int state) { 178 | 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /app/src/main/java/com/ckr/flexitemdecoration/adapter/HorizontalGridAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ckr.flexitemdecoration.adapter; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import com.ckr.flexitemdecoration.R; 10 | 11 | /** 12 | * Created by PC大佬 on 2018/1/4. 13 | */ 14 | 15 | public class HorizontalGridAdapter extends RecyclerView.Adapter { 16 | private Context mContext; 17 | 18 | public HorizontalGridAdapter(Context context) { 19 | mContext = context; 20 | } 21 | 22 | @Override 23 | public MainHolder onCreateViewHolder(ViewGroup parent, int viewType) { 24 | return new MainHolder(LayoutInflater.from(mContext).inflate(R.layout.item_picture_horizontal, parent, false)); 25 | } 26 | 27 | @Override 28 | public void onBindViewHolder(MainHolder holder, int position) { 29 | 30 | } 31 | 32 | @Override 33 | public int getItemCount() { 34 | return 13; 35 | } 36 | 37 | class MainHolder extends RecyclerView.ViewHolder { 38 | 39 | public MainHolder(View itemView) { 40 | super(itemView); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/ckr/flexitemdecoration/adapter/MainAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ckr.flexitemdecoration.adapter; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.TextView; 9 | 10 | import com.ckr.decoration.OnHeaderListener; 11 | import com.ckr.flexitemdecoration.R; 12 | import com.ckr.flexitemdecoration.model.Header; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | /** 18 | * Created by PC大佬 on 2018/1/4. 19 | */ 20 | 21 | public class MainAdapter extends RecyclerView.Adapter implements OnHeaderListener{ 22 | private Context mContext; 23 | private List
headerList; 24 | 25 | public MainAdapter(Context context) { 26 | mContext = context; 27 | } 28 | 29 | public void updateAll(List
headers){ 30 | if (headers == null) { 31 | return; 32 | } 33 | if (headerList == null) { 34 | headerList=new ArrayList<>(headers.size()); 35 | } 36 | headerList.clear(); 37 | headerList.addAll(headers); 38 | notifyDataSetChanged(); 39 | } 40 | 41 | @Override 42 | public MainHolder onCreateViewHolder(ViewGroup parent, int viewType) { 43 | return new MainHolder(LayoutInflater.from(mContext).inflate(R.layout.item_picture, parent, false)); 44 | } 45 | 46 | @Override 47 | public void onBindViewHolder(MainHolder holder, int position) { 48 | holder.titleView.setText("item " + position); 49 | if (headerList != null&&headerList.size()!=0) { 50 | Header header = headerList.get(position); 51 | holder.userName.setText(header.getUserName()); 52 | } 53 | } 54 | 55 | @Override 56 | public int getItemCount() { 57 | if (headerList == null||headerList.size()==0) { 58 | return 13; 59 | } 60 | return headerList.size(); 61 | } 62 | 63 | @Override 64 | public String getHeaderName(int position) { 65 | if (headerList == null||headerList.size()==0) { 66 | return null; 67 | } 68 | return headerList.get(position).getFirstLetter(); 69 | } 70 | 71 | class MainHolder extends RecyclerView.ViewHolder { 72 | 73 | private TextView titleView; 74 | private TextView userName; 75 | 76 | public MainHolder(View itemView) { 77 | super(itemView); 78 | titleView = (TextView) itemView.findViewById(R.id.title); 79 | userName = (TextView) itemView.findViewById(R.id.userName); 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/ckr/flexitemdecoration/adapter/MyFragmentPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ckr.flexitemdecoration.adapter; 2 | 3 | import android.support.v4.app.Fragment; 4 | import android.support.v4.app.FragmentManager; 5 | import android.support.v4.app.FragmentPagerAdapter; 6 | 7 | import com.ckr.flexitemdecoration.view.BaseFragment; 8 | 9 | import java.util.List; 10 | 11 | 12 | public class MyFragmentPagerAdapter extends FragmentPagerAdapter { 13 | List fragmentList; 14 | String[] titles; 15 | 16 | public MyFragmentPagerAdapter(FragmentManager fm, List fragmentList, String[] titles) { 17 | super(fm); 18 | this.fragmentList = fragmentList; 19 | this.titles = titles; 20 | } 21 | 22 | @Override 23 | public Fragment getItem(int position) { 24 | return fragmentList.get(position); 25 | } 26 | 27 | @Override 28 | public int getCount() { 29 | return fragmentList.size(); 30 | } 31 | 32 | @Override 33 | public CharSequence getPageTitle(int position) { 34 | return titles[position]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/ckr/flexitemdecoration/model/Header.java: -------------------------------------------------------------------------------- 1 | package com.ckr.flexitemdecoration.model; 2 | 3 | /** 4 | * Created by PC大佬 on 2018/6/9. 5 | */ 6 | 7 | public class Header implements Cloneable{ 8 | private String userName; 9 | private String firstLetter; 10 | 11 | public Header(String userName, String firstLetter) { 12 | this.userName = userName; 13 | this.firstLetter = firstLetter; 14 | } 15 | 16 | public String getUserName() { 17 | return userName; 18 | } 19 | 20 | public String getFirstLetter() { 21 | return firstLetter; 22 | } 23 | 24 | public void setUserName(String userName) { 25 | this.userName = userName; 26 | } 27 | 28 | public void setFirstLetter(String firstLetter) { 29 | this.firstLetter = firstLetter; 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "Header{" + 35 | "userName='" + userName + '\'' + 36 | ", firstLetter='" + firstLetter + '\'' + 37 | '}'; 38 | } 39 | 40 | @Override 41 | public Object clone() throws CloneNotSupportedException { 42 | return super.clone(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/ckr/flexitemdecoration/model/UserInfo.java: -------------------------------------------------------------------------------- 1 | package com.ckr.flexitemdecoration.model; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by PC大佬 on 2018/6/9. 7 | */ 8 | 9 | public class UserInfo { 10 | private int code; 11 | private String msg; 12 | private List
data; 13 | 14 | public int getCode() { 15 | return code; 16 | } 17 | 18 | public String getMsg() { 19 | return msg; 20 | } 21 | 22 | public List
getData() { 23 | return data; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/ckr/flexitemdecoration/view/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.ckr.flexitemdecoration.view; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v4.app.Fragment; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import butterknife.ButterKnife; 11 | import butterknife.Unbinder; 12 | 13 | /** 14 | * Created by Administrator on 2017/8/15. 15 | */ 16 | 17 | public abstract class BaseFragment extends Fragment { 18 | private static final String TAG = "BaseFragment"; 19 | private View view; 20 | private Unbinder unbinder; 21 | 22 | 23 | @Nullable 24 | @Override 25 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 26 | Bundle savedInstanceState) { 27 | view = inflater.inflate(getContentLayoutId(), container, false); 28 | unbinder = ButterKnife.bind(this, view); 29 | init(); 30 | return view; 31 | } 32 | 33 | @Override 34 | public void onDestroyView() { 35 | super.onDestroyView(); 36 | unbinder.unbind(); 37 | } 38 | 39 | protected abstract int getContentLayoutId(); 40 | 41 | protected abstract void init(); 42 | 43 | public abstract void refreshFragment(boolean... params); 44 | 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/ckr/flexitemdecoration/view/HorizontalGridFragment.java: -------------------------------------------------------------------------------- 1 | package com.ckr.flexitemdecoration.view; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import android.support.v7.widget.GridLayoutManager; 7 | import android.support.v7.widget.LinearLayoutManager; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.util.Log; 10 | 11 | import com.ckr.decoration.BaseItemDecoration; 12 | import com.ckr.decoration.DividerGridItemDecoration; 13 | import com.ckr.flexitemdecoration.R; 14 | import com.ckr.flexitemdecoration.adapter.HorizontalGridAdapter; 15 | 16 | import java.util.Arrays; 17 | 18 | import butterknife.BindDimen; 19 | import butterknife.BindView; 20 | 21 | /** 22 | * A simple {@link Fragment} subclass. 23 | */ 24 | public class HorizontalGridFragment extends BaseFragment { 25 | private static final String TAG = "HorizontalGridFragment"; 26 | @BindView(R.id.recyclerView) 27 | RecyclerView recyclerView; 28 | @BindDimen(R.dimen.size10) 29 | int padding; 30 | private HorizontalGridAdapter mainAdapter; 31 | public static final int SPAN_COUNT = 2; 32 | public static final int ORIENTATION = LinearLayoutManager.HORIZONTAL; 33 | private BaseItemDecoration itemDecoration; 34 | public boolean[] is_checked = {true, false, false, false, false, false, false, false, false, false, false}; 35 | private boolean isInit = false; 36 | 37 | public static HorizontalGridFragment newInstance() { 38 | Bundle args = new Bundle(); 39 | HorizontalGridFragment fragment = new HorizontalGridFragment(); 40 | fragment.setArguments(args); 41 | return fragment; 42 | } 43 | 44 | @Override 45 | protected int getContentLayoutId() { 46 | return R.layout.fragment_horizontal_grid; 47 | } 48 | 49 | @Override 50 | protected void init() { 51 | isInit = true; 52 | // int dimension = (int) getResources().getDimension(R.dimen.size12); 53 | setItemDecoration(); 54 | recyclerView.setLayoutManager(new GridLayoutManager(getContext(), SPAN_COUNT, ORIENTATION, false)); 55 | recyclerView.setPadding(padding, padding, padding, padding); 56 | mainAdapter = new HorizontalGridAdapter(getContext()); 57 | recyclerView.setAdapter(mainAdapter); 58 | } 59 | 60 | private void setItemDecoration() { 61 | if (!isInit) { 62 | return; 63 | } 64 | if (itemDecoration != null) { 65 | recyclerView.removeItemDecoration(itemDecoration); 66 | } 67 | DividerGridItemDecoration.Builder builder = new DividerGridItemDecoration.Builder(getContext(),ORIENTATION, SPAN_COUNT); 68 | // DividerGridItemDecoration.Builder builder = new DividerGridItemDecoration.Builder(getContext(), SPAN_COUNT); 69 | builder.setDivider(R.drawable.bg_divider_list); 70 | if (is_checked[0]) { 71 | } else { 72 | builder.removeHeaderDivider(is_checked[1]) 73 | .removeFooterDivider(is_checked[2]) 74 | .removeLeftDivider(is_checked[3]) 75 | .removeRightDivider(is_checked[4]) 76 | ; 77 | } 78 | if (is_checked[5]) { 79 | builder.subDivider(3, 7) 80 | .setSubDividerHeight(24) 81 | .setSubDividerWidth(24) 82 | .setSubDividerDrawable(R.drawable.bg_divider_offset_grid); 83 | } 84 | if (is_checked[6]) { 85 | builder.redrawDivider(1) 86 | .redrawDividerWidth(30) 87 | .redrawDividerDrawable(R.drawable.bg_divider_redraw_grid); 88 | } 89 | if (is_checked[7]) { 90 | builder.redrawHeaderDivider() 91 | .redrawHeaderDividerHeight(40) 92 | .redrawHeaderDividerDrawable(R.drawable.bg_divider_offset_grid); 93 | } 94 | if (is_checked[8]) { 95 | builder.redrawFooterDivider() 96 | .redrawFooterDividerHeight(40) 97 | .redrawFooterDividerDrawable(R.drawable.bg_divider_offset_grid); 98 | } 99 | if (is_checked[9]) { 100 | builder.redrawLeftDivider(). 101 | redrawLeftDividerWidth(40) 102 | .redrawLeftDividerDrawable(R.drawable.bg_divider_offset_grid); 103 | } 104 | if (is_checked[10]) { 105 | builder.redrawRightDivider() 106 | .redrawRightDividerWidth(40) 107 | .redrawRightDividerDrawable(R.drawable.bg_divider_offset_grid); 108 | } 109 | itemDecoration = builder.build(); 110 | recyclerView.addItemDecoration(itemDecoration); 111 | /* itemDecoration = new DividerGridItemDecoration(getContext(), BaseItemDecoration.HORIZONTAL,SPAN_COUNT); 112 | itemDecoration.setDivider(R.drawable.bg_divider_list); 113 | if (is_checked[0]) { 114 | } else { 115 | itemDecoration.removeHeaderDivider(is_checked[1]) 116 | .removeFooterDivider(is_checked[2]) 117 | .removeLeftDivider(is_checked[3]) 118 | .removeRightDivider(is_checked[4]) 119 | ; 120 | } 121 | recyclerView.addItemDecoration(itemDecoration);*/ 122 | } 123 | 124 | @Override 125 | public void refreshFragment(boolean... params) { 126 | Log.d(TAG, "refreshFragment: params:" + Arrays.toString(params)); 127 | if (!Arrays.equals(params, is_checked)) { 128 | System.arraycopy(params, 0, is_checked, 0, params.length); 129 | Log.d(TAG, "refreshFragment: is_checked:" + Arrays.toString(is_checked)); 130 | setItemDecoration(); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /app/src/main/java/com/ckr/flexitemdecoration/view/LinearFragment.java: -------------------------------------------------------------------------------- 1 | package com.ckr.flexitemdecoration.view; 2 | 3 | 4 | import android.content.Context; 5 | import android.os.AsyncTask; 6 | import android.os.Bundle; 7 | import android.support.v4.app.Fragment; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.util.Log; 11 | 12 | import com.ckr.decoration.DividerLinearItemDecoration; 13 | import com.ckr.flexitemdecoration.R; 14 | import com.ckr.flexitemdecoration.adapter.MainAdapter; 15 | import com.ckr.flexitemdecoration.model.Header; 16 | import com.ckr.flexitemdecoration.model.UserInfo; 17 | import com.google.gson.Gson; 18 | 19 | import java.io.InputStream; 20 | import java.io.InputStreamReader; 21 | import java.io.Reader; 22 | import java.util.Arrays; 23 | import java.util.List; 24 | 25 | import butterknife.BindDimen; 26 | import butterknife.BindView; 27 | 28 | import static com.ckr.decoration.BaseItemDecoration.HORIZONTAL; 29 | import static com.ckr.decoration.BaseItemDecoration.VERTICAL; 30 | 31 | /** 32 | * A simple {@link Fragment} subclass. 33 | */ 34 | public class LinearFragment extends BaseFragment { 35 | private static final String TAG = "LinearFragment"; 36 | public static final String ORIENTATION = "orientation"; 37 | @BindView(R.id.recyclerView) 38 | RecyclerView recyclerView; 39 | @BindDimen(R.dimen.size10) 40 | int padding; 41 | @BindDimen(R.dimen.size8) 42 | int padding8; 43 | @BindDimen(R.dimen.size5) 44 | int padding5; 45 | private MainAdapter mainAdapter; 46 | public int orientation = LinearLayoutManager.VERTICAL; 47 | public boolean[] is_checked = {true, false, false, false, false, false, false, false, false, false, false}; 48 | private DividerLinearItemDecoration itemDecoration; 49 | private boolean isInit = false; 50 | 51 | public static LinearFragment newInstance(int orientation) { 52 | if (orientation != HORIZONTAL && orientation != VERTICAL) { 53 | orientation = VERTICAL; 54 | } 55 | Bundle args = new Bundle(); 56 | args.putInt(ORIENTATION, orientation); 57 | LinearFragment fragment = new LinearFragment(); 58 | fragment.setArguments(args); 59 | return fragment; 60 | } 61 | 62 | @Override 63 | public void onAttach(Context context) { 64 | super.onAttach(context); 65 | Bundle arguments = getArguments(); 66 | if (arguments != null) { 67 | orientation = arguments.getInt(ORIENTATION, VERTICAL); 68 | } 69 | } 70 | 71 | @Override 72 | protected int getContentLayoutId() { 73 | return R.layout.fragment_main; 74 | } 75 | 76 | @Override 77 | protected void init() { 78 | isInit = true; 79 | setItemDecoration(); 80 | recyclerView.setClipToPadding(false); 81 | recyclerView.setLayoutManager(new LinearLayoutManager(getContext(), orientation, false)); 82 | recyclerView.setPadding(0, 0, 0, 0); 83 | mainAdapter = new MainAdapter(getContext()); 84 | recyclerView.setAdapter(mainAdapter); 85 | // if (orientation == LinearLayoutManager.VERTICAL) { 86 | new MyTask().execute(); 87 | // } 88 | } 89 | 90 | private void setItemDecoration() { 91 | if (!isInit) { 92 | return; 93 | } 94 | if (itemDecoration != null) { 95 | recyclerView.removeItemDecoration(itemDecoration); 96 | } 97 | DividerLinearItemDecoration.Builder builder = new DividerLinearItemDecoration.Builder(getContext(), orientation); 98 | builder.setDivider(R.drawable.bg_divider_list); 99 | builder.setDividerPadding(padding8, padding5, 0, padding5); 100 | // if (orientation == LinearLayoutManager.VERTICAL) { 101 | builder.setSticky(true) 102 | .setStickyDrawable(R.drawable.bg_decoration) 103 | .setStickyHeightOrWidth(90); 104 | // } 105 | if (is_checked[0]) { 106 | } else { 107 | builder.removeHeaderDivider(is_checked[1]) 108 | .removeFooterDivider(is_checked[2]) 109 | .removeLeftDivider(is_checked[3]) 110 | .removeRightDivider(is_checked[4]) 111 | ; 112 | } 113 | if (is_checked[5]) { 114 | builder.subDivider(1, 4) 115 | .setSubDividerHeight(24) 116 | .setSubDividerWidth(24) 117 | .setSubDividerDrawable(R.drawable.bg_divider_offset); 118 | } 119 | if (is_checked[6]) { 120 | builder.redrawDivider(2) 121 | .redrawDividerHeight(30) 122 | .redrawDividerWidth(30) 123 | .redrawDividerDrawable(R.drawable.bg_divider_redraw); 124 | } 125 | if (is_checked[7]) { 126 | builder.redrawHeaderDivider() 127 | .redrawHeaderDividerHeight(40) 128 | .redrawHeaderDividerDrawable(R.drawable.bg_divider_offset); 129 | } 130 | if (is_checked[8]) { 131 | builder.redrawFooterDivider() 132 | .redrawFooterDividerHeight(40) 133 | .redrawFooterDividerDrawable(R.drawable.bg_divider_offset); 134 | } 135 | if (is_checked[9]) { 136 | builder.redrawLeftDivider(). 137 | redrawLeftDividerWidth(40) 138 | .redrawLeftDividerDrawable(R.drawable.bg_divider_list); 139 | } 140 | if (is_checked[10]) { 141 | builder.redrawRightDivider() 142 | .redrawRightDividerWidth(40) 143 | .redrawRightDividerDrawable(R.drawable.bg_divider_list); 144 | } 145 | /* builder 146 | .redrawHeaderDivider(). 147 | redrawHeaderDividerHeight(40) 148 | .redrawHeaderDividerDrawable(R.drawable.bg_divider_offset) 149 | .redrawFooterDivider() 150 | .redrawFooterDividerHeight(40) 151 | .redrawFooterDividerDrawable(R.drawable.bg_divider_offset) 152 | .redrawLeftDivider(). 153 | redrawLeftDividerWidth(40) 154 | .redrawLeftDividerDrawable(R.drawable.bg_divider_list) 155 | .redrawRightDivider(). 156 | redrawRightDividerWidth(40) 157 | .redrawRightDividerDrawable(R.drawable.bg_divider_list) 158 | ;*/ 159 | itemDecoration = builder.build(); 160 | recyclerView.addItemDecoration(itemDecoration); 161 | /* itemDecoration = new DividerLinearItemDecoration(getContext(), orientation,R.drawable.bg_divider_list); 162 | if (is_checked[0]) { 163 | } else { 164 | itemDecoration.removeHeaderDivider(is_checked[1]) 165 | .removeFooterDivider(is_checked[2]) 166 | .removeLeftDivider(is_checked[3]) 167 | .removeRightDivider(is_checked[4]) 168 | ; 169 | } 170 | recyclerView.addItemDecoration(itemDecoration);*/ 171 | } 172 | 173 | 174 | @Override 175 | public void refreshFragment(boolean... params) { 176 | Log.d(TAG, "refreshFragment: params:" + Arrays.toString(params)); 177 | boolean forceRefresh = false; 178 | if (orientation == LinearLayoutManager.VERTICAL) { 179 | for (int i = 0; i < 3; i++) { 180 | if (params[i] != is_checked[i]) { 181 | forceRefresh = true; 182 | break; 183 | } 184 | } 185 | if (!forceRefresh) { 186 | for (int i = 5; i < params.length; i++) { 187 | if (params[i] != is_checked[i]) { 188 | forceRefresh = true; 189 | break; 190 | } 191 | } 192 | } 193 | if (forceRefresh) { 194 | System.arraycopy(params, 0, is_checked, 0, 3); 195 | System.arraycopy(params, 5, is_checked, 5, params.length - 5); 196 | Log.d(TAG, "refreshFragment: is_checked111:" + Arrays.toString(is_checked)); 197 | setItemDecoration(); 198 | } 199 | } else { 200 | if (params[0] != is_checked[0]) { 201 | forceRefresh = true; 202 | } else { 203 | for (int i = 3; i < params.length; i++) { 204 | if (params[i] != is_checked[i]) { 205 | forceRefresh = true; 206 | break; 207 | } 208 | } 209 | } 210 | if (forceRefresh) { 211 | is_checked[0] = params[0]; 212 | System.arraycopy(params, 3, is_checked, 3, params.length - 3); 213 | Log.d(TAG, "refreshFragment: is_checked222:" + Arrays.toString(is_checked)); 214 | setItemDecoration(); 215 | } 216 | 217 | } 218 | } 219 | 220 | class MyTask extends AsyncTask> { 221 | 222 | @Override 223 | protected List
doInBackground(Void... voids) { 224 | InputStream inputStream = getResources().openRawResource(R.raw.user); 225 | Reader reader = new InputStreamReader(inputStream); 226 | UserInfo userInfo = new Gson().fromJson(reader, UserInfo.class); 227 | List
data = userInfo.getData(); 228 | return data; 229 | } 230 | 231 | @Override 232 | protected void onPostExecute(List
headers) { 233 | if (mainAdapter != null) { 234 | mainAdapter.updateAll(headers); 235 | } 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /app/src/main/java/com/ckr/flexitemdecoration/view/VerticalGridFragment.java: -------------------------------------------------------------------------------- 1 | package com.ckr.flexitemdecoration.view; 2 | 3 | 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v7.widget.GridLayoutManager; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.util.Log; 11 | 12 | import com.ckr.decoration.DividerGridItemDecoration; 13 | import com.ckr.flexitemdecoration.R; 14 | import com.ckr.flexitemdecoration.adapter.MainAdapter; 15 | 16 | import java.util.Arrays; 17 | 18 | import butterknife.BindDimen; 19 | import butterknife.BindView; 20 | 21 | /** 22 | * A simple {@link Fragment} subclass. 23 | */ 24 | public class VerticalGridFragment extends BaseFragment { 25 | private static final String TAG = "VerticalGridFragment"; 26 | public static final String STYLE = "style"; 27 | @BindView(R.id.recyclerView) 28 | RecyclerView recyclerView; 29 | @BindDimen(R.dimen.size10) 30 | int padding; 31 | private MainAdapter mainAdapter; 32 | public static final int SPAN_COUNT = 2; 33 | public static final int ORIENTATION = LinearLayoutManager.VERTICAL; 34 | private DividerGridItemDecoration itemDecoration; 35 | public boolean[] is_checked = {true, false, false, false, false, false, false, false, false, false, false}; 36 | private boolean isInit = false; 37 | private boolean isShowOtherStyle; 38 | 39 | public static VerticalGridFragment newInstance(boolean b) { 40 | Bundle args = new Bundle(); 41 | args.putBoolean(STYLE, b); 42 | VerticalGridFragment fragment = new VerticalGridFragment(); 43 | fragment.setArguments(args); 44 | return fragment; 45 | } 46 | 47 | @Override 48 | public void onAttach(Context context) { 49 | super.onAttach(context); 50 | Bundle arguments = getArguments(); 51 | if (arguments != null) { 52 | isShowOtherStyle = arguments.getBoolean(STYLE, false); 53 | } 54 | } 55 | 56 | @Override 57 | protected int getContentLayoutId() { 58 | return R.layout.fragment_main; 59 | } 60 | 61 | @Override 62 | protected void init() { 63 | isInit = true; 64 | setItemDecoration(); 65 | recyclerView.setLayoutManager(new GridLayoutManager(getContext(), SPAN_COUNT, ORIENTATION, false)); 66 | recyclerView.setPadding(padding, padding, padding, padding); 67 | mainAdapter = new MainAdapter(getContext()); 68 | recyclerView.setAdapter(mainAdapter); 69 | } 70 | 71 | private void setItemDecoration() { 72 | if (!isInit) { 73 | return; 74 | } 75 | if (itemDecoration != null) { 76 | recyclerView.removeItemDecoration(itemDecoration); 77 | } 78 | DividerGridItemDecoration.Builder builder = new DividerGridItemDecoration.Builder(getContext(), ORIENTATION, SPAN_COUNT); 79 | // DividerGridItemDecoration.Builder builder = new DividerGridItemDecoration.Builder(getContext(), SPAN_COUNT); 80 | builder.setShowOtherStyle(isShowOtherStyle) 81 | .setDivider(R.drawable.bg_divider_list); 82 | if (is_checked[0]) { 83 | } else { 84 | builder.removeHeaderDivider(is_checked[1]) 85 | .removeFooterDivider(is_checked[2]) 86 | .removeLeftDivider(is_checked[3]) 87 | .removeRightDivider(is_checked[4]) 88 | ; 89 | } 90 | if (is_checked[5]) { 91 | builder.subDivider(1, 4) 92 | .setSubDividerHeight(24) 93 | .setSubDividerWidth(24) 94 | .setSubDividerDrawable(R.drawable.bg_divider_offset_grid); 95 | } 96 | if (is_checked[6]) { 97 | builder.redrawDivider(2) 98 | .redrawDividerHeight(30) 99 | .redrawDividerDrawable(R.drawable.bg_divider_redraw_grid); 100 | } 101 | if (is_checked[7]) { 102 | builder.redrawHeaderDivider() 103 | .redrawHeaderDividerHeight(40) 104 | .redrawHeaderDividerDrawable(R.drawable.bg_divider_offset_grid); 105 | } 106 | if (is_checked[8]) { 107 | builder.redrawFooterDivider() 108 | .redrawFooterDividerHeight(40) 109 | .redrawFooterDividerDrawable(R.drawable.bg_divider_offset_grid); 110 | } 111 | if (is_checked[9]) { 112 | builder.redrawLeftDivider(). 113 | redrawLeftDividerWidth(40) 114 | .redrawLeftDividerDrawable(R.drawable.bg_divider_list); 115 | } 116 | if (is_checked[10]) { 117 | builder.redrawRightDivider() 118 | .redrawRightDividerWidth(40) 119 | .redrawRightDividerDrawable(R.drawable.bg_divider_list); 120 | } 121 | itemDecoration = builder.build(); 122 | recyclerView.addItemDecoration(itemDecoration); 123 | /* itemDecoration = new DividerGridItemDecoration(getContext(), BaseItemDecoration.HORIZONTAL,SPAN_COUNT); 124 | itemDecoration.setDivider(R.drawable.bg_divider_list); 125 | if (is_checked[0]) { 126 | } else { 127 | itemDecoration.removeHeaderDivider(is_checked[1]) 128 | .removeFooterDivider(is_checked[2]) 129 | .removeLeftDivider(is_checked[3]) 130 | .removeRightDivider(is_checked[4]) 131 | ; 132 | } 133 | recyclerView.addItemDecoration(itemDecoration);*/ 134 | } 135 | 136 | @Override 137 | public void refreshFragment(boolean... params) { 138 | Log.d(TAG, "refreshFragment: params:" + Arrays.toString(params)); 139 | if (!Arrays.equals(params, is_checked)) { 140 | System.arraycopy(params, 0, is_checked, 0, params.length); 141 | Log.d(TAG, "refreshFragment: is_checked:" + Arrays.toString(is_checked)); 142 | setItemDecoration(); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /app/src/main/java/com/ckr/flexitemdecoration/widget/MyViewPager.java: -------------------------------------------------------------------------------- 1 | package com.ckr.flexitemdecoration.widget; 2 | 3 | import android.content.Context; 4 | import android.support.v4.view.ViewPager; 5 | import android.util.AttributeSet; 6 | import android.view.MotionEvent; 7 | 8 | 9 | public class MyViewPager extends ViewPager { 10 | private boolean isCanScroll = false; 11 | 12 | public MyViewPager(Context context) { 13 | super(context); 14 | } 15 | 16 | public MyViewPager(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | } 19 | 20 | public void setScanScroll(boolean isCanScroll) { 21 | this.isCanScroll = isCanScroll; 22 | } 23 | 24 | @Override 25 | public void scrollTo(int x, int y) { 26 | super.scrollTo(x, y); 27 | } 28 | 29 | @Override 30 | public void setCurrentItem(int item) { 31 | super.setCurrentItem(item, false); 32 | } 33 | 34 | @Override 35 | public void setCurrentItem(int item, boolean smoothScroll) { 36 | super.setCurrentItem(item, false); 37 | } 38 | 39 | @Override 40 | public boolean onTouchEvent(MotionEvent arg0) { 41 | if (isCanScroll) { 42 | return super.onTouchEvent(arg0); 43 | } else { 44 | return false; 45 | } 46 | } 47 | 48 | @Override 49 | public boolean onInterceptTouchEvent(MotionEvent arg0) { 50 | if (isCanScroll) { 51 | return super.onInterceptTouchEvent(arg0); 52 | } else { 53 | return false; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_divider_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_divider_offset.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_divider_offset_grid.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_divider_redraw.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_divider_redraw_grid.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_tablayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_horizontal_grid.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_main.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_picture.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 19 | 20 | 26 | 27 | 28 | 32 | 39 | 48 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_picture_horizontal.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 18 | 19 | 25 | 26 | 30 | 31 | 38 | 39 | 48 | 49 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 14 | 18 | 22 | 26 | 27 | 31 | 35 | 39 | 43 | 47 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/banner2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/app/src/main/res/mipmap-xxhdpi/banner2.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_person.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/app/src/main/res/mipmap-xxhdpi/ic_person.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/raw/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 200, 3 | "msg": "操作成功", 4 | "data": [ 5 | { 6 | "userName": "AI", 7 | "firstLetter": "A" 8 | }, 9 | { 10 | "userName": "爱迪生", 11 | "firstLetter": "A" 12 | }, 13 | { 14 | "userName": "爱我吧", 15 | "firstLetter": "A" 16 | }, 17 | { 18 | "userName": "Arm", 19 | "firstLetter": "A" 20 | }, 21 | { 22 | "userName": "安静", 23 | "firstLetter": "A" 24 | }, 25 | { 26 | "userName": "Animate", 27 | "firstLetter": "A" 28 | }, 29 | { 30 | "userName": "啊啊啊啊", 31 | "firstLetter": "A" 32 | }, 33 | { 34 | "userName": "爱好", 35 | "firstLetter": "A" 36 | }, 37 | { 38 | "userName": "百草", 39 | "firstLetter": "B" 40 | }, 41 | { 42 | "userName": "博尔特", 43 | "firstLetter": "B" 44 | }, 45 | { 46 | "userName": "办卡啦", 47 | "firstLetter": "B" 48 | }, 49 | { 50 | "userName": "背景", 51 | "firstLetter": "B" 52 | }, 53 | { 54 | "userName": "北京", 55 | "firstLetter": "B" 56 | }, 57 | { 58 | "userName": "冰天雪地", 59 | "firstLetter": "B" 60 | }, 61 | { 62 | "userName": "不行", 63 | "firstLetter": "B" 64 | }, 65 | { 66 | "userName": "菜鸟", 67 | "firstLetter": "C" 68 | }, 69 | { 70 | "userName": "曹操", 71 | "firstLetter": "C" 72 | }, 73 | { 74 | "userName": "长城", 75 | "firstLetter": "C" 76 | }, 77 | { 78 | "userName": "超人", 79 | "firstLetter": "C" 80 | }, 81 | { 82 | "userName": "超牛逼", 83 | "firstLetter": "C" 84 | }, 85 | { 86 | "userName": "C++", 87 | "firstLetter": "C" 88 | }, 89 | { 90 | "userName": "C#", 91 | "firstLetter": "C" 92 | }, 93 | { 94 | "userName": "C语言", 95 | "firstLetter": "C" 96 | }, 97 | { 98 | "userName": "大大", 99 | "firstLetter": "D" 100 | }, 101 | { 102 | "userName": "代码", 103 | "firstLetter": "D" 104 | }, 105 | { 106 | "userName": "代替", 107 | "firstLetter": "D" 108 | }, 109 | { 110 | "userName": "大桥", 111 | "firstLetter": "D" 112 | }, 113 | { 114 | "userName": "大元帅府", 115 | "firstLetter": "D" 116 | }, 117 | { 118 | "userName": "带我飞", 119 | "firstLetter": "D" 120 | }, 121 | { 122 | "userName": "大卫", 123 | "firstLetter": "D" 124 | }, 125 | { 126 | "userName": "嗯哼二将", 127 | "firstLetter": "E" 128 | }, 129 | { 130 | "userName": "文件", 131 | "firstLetter": "F" 132 | }, 133 | { 134 | "userName": "纷纷", 135 | "firstLetter": "F" 136 | }, 137 | { 138 | "userName": "分类", 139 | "firstLetter": "F" 140 | }, 141 | { 142 | "userName": "Game", 143 | "firstLetter": "G" 144 | }, 145 | { 146 | "userName": "GG", 147 | "firstLetter": "G" 148 | }, 149 | { 150 | "userName": "还没", 151 | "firstLetter": "H" 152 | }, 153 | { 154 | "userName": "还好", 155 | "firstLetter": "H" 156 | }, 157 | { 158 | "userName": "Happy", 159 | "firstLetter": "H" 160 | }, 161 | { 162 | "userName": "胡适", 163 | "firstLetter": "H" 164 | }, 165 | { 166 | "userName": "忽悠", 167 | "firstLetter": "H" 168 | }, 169 | { 170 | "userName": "红颜", 171 | "firstLetter": "H" 172 | }, 173 | { 174 | "userName": "红色革命", 175 | "firstLetter": "H" 176 | }, 177 | { 178 | "userName": "Json", 179 | "firstLetter": "J" 180 | }, 181 | { 182 | "userName": "Java", 183 | "firstLetter": "J" 184 | }, 185 | { 186 | "userName": "JJ", 187 | "firstLetter": "J" 188 | } 189 | ] 190 | } -------------------------------------------------------------------------------- /app/src/main/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 分割线 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2dp 4 | 5dp 5 | 8dp 6 | 10dp 7 | 12dp 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | FlexItemDecoration 3 | 4 | 5 | Hello blank fragment 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | apply from:"config.gradle" 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.3.2' 9 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' 10 | classpath 'com.novoda:bintray-release:0.6.1' 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | } 20 | } 21 | 22 | task clean(type: Delete) { 23 | delete rootProject.buildDir 24 | } 25 | -------------------------------------------------------------------------------- /config.gradle: -------------------------------------------------------------------------------- 1 | //全局参数 2 | ext { 3 | appConfig = [ 4 | versionCode : 12, 5 | versionName : "1.1.2", 6 | applicationId: "com.ckr.flexitemdecoration", 7 | ] 8 | 9 | androidConfig = [ 10 | compileSdkVersion: 25, 11 | buildToolsVersion: "25.0.3", 12 | minSdkVersion : 16, 13 | targetSdkVersion : 25, 14 | ] 15 | 16 | supportVersion = "25.3.1" 17 | } -------------------------------------------------------------------------------- /decoration/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /decoration/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.novoda.bintray-release' 3 | 4 | 5 | def siteUrl = 'https://github.com/ckrgithub/FlexItemDecoration' 6 | publish{ 7 | repoName = 'FlexItemDecoration' 8 | userOrg = 'ckrjfrog' 9 | groupId = 'ckrjfrog.FlexItemDecoration' 10 | artifactId = 'Decoration' 11 | publishVersion = '1.1.3' 12 | desc = '列表分割线' 13 | website = siteUrl 14 | } 15 | tasks.withType(Javadoc) { 16 | options.addStringOption('Xdoclint:none', '-quiet') 17 | options.addStringOption('encoding', 'UTF-8') 18 | options.addStringOption('charSet', 'UTF-8') 19 | } 20 | 21 | android { 22 | compileSdkVersion rootProject.ext.androidConfig.compileSdkVersion 23 | buildToolsVersion rootProject.ext.androidConfig.buildToolsVersion 24 | 25 | defaultConfig { 26 | minSdkVersion rootProject.ext.androidConfig.minSdkVersion 27 | targetSdkVersion rootProject.ext.androidConfig.targetSdkVersion 28 | versionCode 13 29 | versionName "1.1.3" 30 | } 31 | 32 | buildTypes { 33 | release { 34 | minifyEnabled false 35 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 36 | } 37 | } 38 | lintOptions { 39 | abortOnError false 40 | } 41 | } 42 | 43 | dependencies { 44 | compile fileTree(include: ['*.jar'], dir: 'libs') 45 | compile "com.android.support:appcompat-v7:$rootProject.supportVersion" 46 | provided "com.android.support:recyclerview-v7:$rootProject.supportVersion" 47 | provided "com.android.support:cardview-v7:$rootProject.supportVersion" 48 | provided "com.android.support:design:$rootProject.supportVersion" 49 | } 50 | -------------------------------------------------------------------------------- /decoration/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 22 | -------------------------------------------------------------------------------- /decoration/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /decoration/src/main/java/com/ckr/decoration/BaseItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.ckr.decoration; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.drawable.Drawable; 7 | import android.support.annotation.DrawableRes; 8 | import android.support.annotation.IntRange; 9 | import android.support.v4.content.ContextCompat; 10 | import android.support.v7.widget.LinearLayoutManager; 11 | import android.support.v7.widget.RecyclerView; 12 | 13 | /** 14 | * Created by PC大佬 on 2018/1/6. 15 | */ 16 | 17 | public abstract class BaseItemDecoration extends RecyclerView.ItemDecoration { 18 | private static final int[] ATTRS = new int[]{android.R.attr.listDivider}; 19 | public static final int HORIZONTAL = LinearLayoutManager.HORIZONTAL; 20 | public static final int VERTICAL = LinearLayoutManager.VERTICAL; 21 | protected static final int LINEAR = 1; 22 | protected static final int GRID = 2; 23 | protected int mFlag = 0; 24 | protected int mOrientation = VERTICAL; 25 | protected Drawable mDivider; 26 | protected boolean noDrawHeaderDivider; 27 | protected boolean noDrawFooterDivider; 28 | protected boolean noDrawLeftDivider; 29 | protected boolean noDrawRightDivider; 30 | protected int mDividerHeight; 31 | protected int mDividerWidth; 32 | protected Context mContext; 33 | 34 | protected boolean isSubDivider = false;//分割线截取绘制 35 | protected int mStartIndex;//分割线开始绘制的下标 36 | protected int mEndIndex;//分割线停止绘制的下标 37 | protected int mSubDividerHeight; 38 | protected int mSubDividerWidth; 39 | protected Drawable mSubDrawable; 40 | protected boolean isRedrawDivider = false; 41 | protected int mDividerIndex = -1;//分割线定制的下标,优先级高于分割线截取绘制 42 | protected Drawable mDividerDrawable; 43 | protected int mRedrawDividerHeight; 44 | protected int mRedrawDividerWidth; 45 | protected boolean isRedrawHeaderDivider = false;//头部分割线是否定制 46 | protected Drawable mHeaderDividerDrawable; 47 | protected int mHeaderDividerHeight; 48 | protected boolean isRedrawFooterDivider = false;//底部分割线是否定制 49 | protected Drawable mFooterDividerDrawable; 50 | protected int mFooterDividerHeight; 51 | protected boolean isRedrawLeftDivider = false;//最左边分割线是否定制 52 | protected Drawable mLeftDividerDrawable; 53 | protected int mLeftDividerWidth; 54 | protected boolean isRedrawRightDivider = false;//最右边分割线是否定制 55 | protected Drawable mRightDividerDrawable; 56 | protected int mRightDividerWidth; 57 | protected boolean isShowOtherStyle; 58 | 59 | 60 | protected BaseItemDecoration(Context context, int mFlag, int orientation) { 61 | if (orientation != HORIZONTAL && orientation != VERTICAL && orientation != GRID) { 62 | throw new IllegalArgumentException("invalid orientation"); 63 | } 64 | this.mContext = context; 65 | this.mFlag = mFlag; 66 | this.mOrientation = orientation; 67 | initDefaultDivider(context); 68 | } 69 | 70 | protected BaseItemDecoration(Context context, int mFlag, int orientation, int drawableId) { 71 | if (orientation != HORIZONTAL && orientation != VERTICAL && orientation != GRID) { 72 | throw new IllegalArgumentException("invalid orientation"); 73 | } 74 | this.mContext = context; 75 | this.mFlag = mFlag; 76 | this.mOrientation = orientation; 77 | mDivider = ContextCompat.getDrawable(context.getApplicationContext(), drawableId); 78 | mDividerHeight = mDivider.getIntrinsicHeight(); 79 | mDividerWidth = mDivider.getIntrinsicWidth(); 80 | } 81 | 82 | protected BaseItemDecoration(BaseBuilder baseBuilder) { 83 | this.mDivider = baseBuilder.mDivider; 84 | if (this.mDivider == null) { 85 | initDefaultDivider(baseBuilder.mContext); 86 | } else { 87 | mDividerHeight = mDivider.getIntrinsicHeight(); 88 | mDividerWidth = mDivider.getIntrinsicWidth(); 89 | } 90 | this.mContext = baseBuilder.mContext; 91 | this.mFlag = baseBuilder.mFlag; 92 | this.mOrientation = baseBuilder.mOrientation; 93 | this.noDrawHeaderDivider = baseBuilder.noDrawHeaderDivider; 94 | this.noDrawFooterDivider = baseBuilder.noDrawFooterDivider; 95 | this.noDrawLeftDivider = baseBuilder.noDrawLeftDivider; 96 | this.noDrawRightDivider = baseBuilder.noDrawRightDivider; 97 | this.isShowOtherStyle = baseBuilder.isShowOtherStyle; 98 | 99 | this.isSubDivider = baseBuilder.isSubDivider; 100 | this.mStartIndex = baseBuilder.mStartIndex; 101 | this.mEndIndex = baseBuilder.mEndIndex; 102 | this.mSubDividerHeight = baseBuilder.mSubDividerHeight; 103 | this.mSubDividerWidth = baseBuilder.mSubDividerWidth; 104 | this.mSubDrawable = baseBuilder.mSubDrawable; 105 | this.isRedrawDivider = baseBuilder.isRedrawDivider; 106 | this.mDividerIndex = baseBuilder.mDividerIndex; 107 | this.mDividerDrawable = baseBuilder.mDividerDrawable; 108 | this.mRedrawDividerHeight = baseBuilder.mRedrawDividerHeight; 109 | this.mRedrawDividerWidth = baseBuilder.mRedrawDividerWidth; 110 | this.isRedrawHeaderDivider = baseBuilder.isRedrawHeaderDivider; 111 | this.mHeaderDividerDrawable = baseBuilder.mHeaderDividerDrawable; 112 | this.mHeaderDividerHeight = baseBuilder.mHeaderDividerHeight; 113 | this.isRedrawFooterDivider = baseBuilder.isRedrawFooterDivider; 114 | this.mFooterDividerDrawable = baseBuilder.mFooterDividerDrawable; 115 | this.mFooterDividerHeight = baseBuilder.mFooterDividerHeight; 116 | this.isRedrawLeftDivider = baseBuilder.isRedrawLeftDivider; 117 | this.mLeftDividerDrawable = baseBuilder.mLeftDividerDrawable; 118 | this.mLeftDividerWidth = baseBuilder.mLeftDividerWidth; 119 | this.isRedrawRightDivider = baseBuilder.isRedrawRightDivider; 120 | this.mRightDividerDrawable = baseBuilder.mRightDividerDrawable; 121 | this.mRightDividerWidth = baseBuilder.mRightDividerWidth; 122 | } 123 | 124 | private void initDefaultDivider(Context context) { 125 | final TypedArray a = context.obtainStyledAttributes(ATTRS); 126 | mDivider = a.getDrawable(0); 127 | a.recycle(); 128 | mDividerHeight = mDivider.getIntrinsicHeight() * 5; 129 | mDividerWidth = mDivider.getIntrinsicWidth() * 5; 130 | } 131 | 132 | public BaseItemDecoration setDivider(@DrawableRes int drawableId) { 133 | this.mDivider = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 134 | mDividerHeight = mDivider.getIntrinsicHeight(); 135 | mDividerWidth = mDivider.getIntrinsicWidth(); 136 | return this; 137 | } 138 | 139 | public BaseItemDecoration removeHeaderDivider(boolean noDrawHeaderDivider) { 140 | this.noDrawHeaderDivider = noDrawHeaderDivider; 141 | return this; 142 | } 143 | 144 | public BaseItemDecoration removeFooterDivider(boolean noDrawFooterDivider) { 145 | this.noDrawFooterDivider = noDrawFooterDivider; 146 | return this; 147 | } 148 | 149 | public BaseItemDecoration removeLeftDivider(boolean noDrawLeftDivider) { 150 | this.noDrawLeftDivider = noDrawLeftDivider; 151 | return this; 152 | } 153 | 154 | public BaseItemDecoration removeRightDivider(boolean noDrawRightDivider) { 155 | this.noDrawRightDivider = noDrawRightDivider; 156 | return this; 157 | } 158 | 159 | public BaseItemDecoration redrawHeaderDivider() { 160 | this.isRedrawHeaderDivider = true; 161 | return this; 162 | } 163 | 164 | public BaseItemDecoration redrawHeaderDividerDrawable(@DrawableRes int drawableId) { 165 | this.mHeaderDividerDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 166 | if (this.mHeaderDividerHeight == 0) { 167 | this.mHeaderDividerHeight = mHeaderDividerDrawable.getIntrinsicHeight(); 168 | } 169 | return this; 170 | } 171 | 172 | public BaseItemDecoration redrawHeaderDividerHeight(@IntRange(from = 0) int height) { 173 | this.mHeaderDividerHeight = height; 174 | return this; 175 | } 176 | 177 | public BaseItemDecoration redrawFooterDivider() { 178 | this.isRedrawFooterDivider = true; 179 | return this; 180 | } 181 | 182 | public BaseItemDecoration redrawFooterDividerDrawable(@DrawableRes int drawableId) { 183 | this.mFooterDividerDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 184 | if (this.mFooterDividerHeight == 0) { 185 | this.mFooterDividerHeight = this.mFooterDividerDrawable.getIntrinsicHeight(); 186 | } 187 | return this; 188 | } 189 | 190 | public BaseItemDecoration redrawFooterDividerHeight(@IntRange(from = 0) int height) { 191 | this.mFooterDividerHeight = height; 192 | return this; 193 | } 194 | 195 | public BaseItemDecoration redrawLeftDivider() { 196 | this.isRedrawLeftDivider = true; 197 | return this; 198 | } 199 | 200 | public BaseItemDecoration redrawLeftDividerDrawable(@DrawableRes int drawableId) { 201 | this.mLeftDividerDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 202 | if (this.mLeftDividerWidth == 0) { 203 | this.mLeftDividerWidth = mLeftDividerDrawable.getIntrinsicWidth(); 204 | } 205 | return this; 206 | } 207 | 208 | public BaseItemDecoration redrawLeftDividerWidth(@IntRange(from = 0) int width) { 209 | this.mLeftDividerWidth = width; 210 | return this; 211 | } 212 | 213 | public BaseItemDecoration redrawRightDivider() { 214 | this.isRedrawRightDivider = true; 215 | return this; 216 | } 217 | 218 | public BaseItemDecoration redrawRightDividerDrawable(@DrawableRes int drawableId) { 219 | this.mRightDividerDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 220 | if (this.mRightDividerWidth == 0) { 221 | this.mRightDividerWidth = mRightDividerDrawable.getIntrinsicWidth(); 222 | } 223 | return this; 224 | } 225 | 226 | public BaseItemDecoration redrawRightDividerWidth(@IntRange(from = 0) int width) { 227 | this.mRightDividerWidth = width; 228 | return this; 229 | } 230 | 231 | public BaseItemDecoration redrawDivider(@IntRange(from = 0) int dividerLineIndex) { 232 | this.mDividerIndex = dividerLineIndex; 233 | this.isRedrawDivider = true; 234 | return this; 235 | } 236 | 237 | public BaseItemDecoration redrawDividerHeight(@IntRange(from = 0) int height) { 238 | this.mRedrawDividerHeight = height; 239 | return this; 240 | } 241 | 242 | public BaseItemDecoration redrawDividerWidth(@IntRange(from = 0) int width) { 243 | this.mRedrawDividerWidth = width; 244 | return this; 245 | } 246 | 247 | public BaseItemDecoration redrawDividerDrawable(@DrawableRes int drawableId) { 248 | this.mDividerDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 249 | if (mOrientation == VERTICAL) { 250 | if (this.mRedrawDividerHeight == 0) { 251 | this.mRedrawDividerHeight = mDividerDrawable.getIntrinsicHeight(); 252 | } 253 | } else { 254 | if (mRedrawDividerWidth == 0) { 255 | this.mRedrawDividerWidth = mDividerDrawable.getIntrinsicWidth(); 256 | } 257 | } 258 | return this; 259 | } 260 | 261 | public BaseItemDecoration subDivider(@IntRange(from = 0) int startIndex, @IntRange(from = 1) int endIndex) { 262 | int subLen = endIndex - startIndex; 263 | if (subLen < 0) { 264 | throw new IndexOutOfBoundsException(startIndex + ">=" + endIndex); 265 | } 266 | this.mStartIndex = startIndex; 267 | this.mEndIndex = endIndex; 268 | this.isSubDivider = true; 269 | return this; 270 | } 271 | 272 | public BaseItemDecoration setSubDividerHeight(@IntRange(from = 0) int height) { 273 | this.mSubDividerHeight = height; 274 | return this; 275 | } 276 | 277 | public BaseItemDecoration setSubDividerWidth(@IntRange(from = 0) int width) { 278 | this.mSubDividerWidth = width; 279 | return this; 280 | } 281 | 282 | public BaseItemDecoration setSubDividerDrawable(@DrawableRes int drawableId) { 283 | this.mSubDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 284 | if (mOrientation == VERTICAL) { 285 | if (this.mSubDividerHeight == 0) { 286 | this.mSubDividerHeight = mSubDrawable.getIntrinsicHeight(); 287 | } 288 | } else { 289 | if (this.mSubDividerWidth == 0) { 290 | this.mSubDividerWidth = mSubDrawable.getIntrinsicWidth(); 291 | } 292 | } 293 | return this; 294 | } 295 | 296 | @Override 297 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 298 | if (mFlag == LINEAR) { 299 | if (mOrientation == VERTICAL) { 300 | drawHorizontal(c, parent); 301 | } else { 302 | drawVertical(c, parent); 303 | } 304 | } else if (mFlag == GRID) { 305 | drawHorizontal(c, parent); 306 | drawVertical(c, parent); 307 | } 308 | } 309 | 310 | protected abstract void drawVertical(Canvas c, RecyclerView parent); 311 | 312 | protected abstract void drawHorizontal(Canvas c, RecyclerView parent); 313 | 314 | public static abstract class BaseBuilder { 315 | protected Context mContext; 316 | private Drawable mDivider; 317 | private int mFlag;//标记网格布局还是线性布局 318 | private int mOrientation = VERTICAL; 319 | private boolean noDrawHeaderDivider;//头部部分割线是否绘制 320 | private boolean noDrawFooterDivider;//底部分割线是否绘制 321 | protected boolean noDrawLeftDivider;//最左边分割线是否绘制 322 | protected boolean noDrawRightDivider;//最右边分割线是否绘制 323 | 324 | private boolean isSubDivider = false;//分割线截取绘制 325 | private int mStartIndex;//分割线开始绘制的下标 326 | private int mEndIndex;//分割线停止绘制的下标 327 | private int mSubDividerHeight;//分割线的高度,仅适用于竖直方向 328 | private int mSubDividerWidth;//分割线的宽带,仅适用于水平方向 329 | private Drawable mSubDrawable; 330 | 331 | private boolean isRedrawDivider = false;//分割线的定制(注:不包括头部、底部、最左边、最右边分割线定制) 332 | private int mDividerIndex = -1;//分割线定制的下标,优先级高于分割线截取绘制 333 | private Drawable mDividerDrawable; 334 | private int mRedrawDividerHeight; 335 | private int mRedrawDividerWidth; 336 | 337 | private boolean isRedrawHeaderDivider = false;//头部分割线是否定制,仅适用于竖直方向 338 | private Drawable mHeaderDividerDrawable; 339 | private int mHeaderDividerHeight; 340 | private boolean isRedrawFooterDivider = false;//底部分割线是否定制,仅适用于竖直方向 341 | private Drawable mFooterDividerDrawable; 342 | private int mFooterDividerHeight; 343 | private boolean isRedrawLeftDivider = false;//最左边分割线是否定制,仅适用于水平方向 344 | private Drawable mLeftDividerDrawable; 345 | private int mLeftDividerWidth; 346 | private boolean isRedrawRightDivider = false;//最右边分割线是否定制,仅适用于水平方向 347 | private Drawable mRightDividerDrawable; 348 | private int mRightDividerWidth; 349 | 350 | protected boolean isShowOtherStyle;//仅适用于网格分割线 351 | 352 | /** 353 | * @param context 用于资源文件访问 354 | * @param flag 布局方式,如:{@link #LINEAR} or {@link #GRID} 355 | */ 356 | protected BaseBuilder(Context context, int flag) { 357 | this.mContext = context; 358 | this.mFlag = flag; 359 | } 360 | 361 | /** 362 | * @param context 用于资源文件访问 363 | * @param flag 布局方式,如:{@link #LINEAR} or {@link #GRID} 364 | * @param mOrientation 布局方向,如:{@link #HORIZONTAL} or {@link #VERTICAL} 365 | */ 366 | protected BaseBuilder(Context context, int flag, int mOrientation) { 367 | if (mOrientation != HORIZONTAL && mOrientation != VERTICAL) { 368 | throw new IllegalArgumentException("invalid orientation"); 369 | } 370 | this.mContext = context; 371 | this.mFlag = flag; 372 | this.mOrientation = mOrientation; 373 | } 374 | 375 | /** 376 | * 设置分割线的样式 377 | * 378 | * @param drawableId drawable资源id 379 | * @return 380 | */ 381 | public BaseBuilder setDivider(@DrawableRes int drawableId) { 382 | this.mDivider = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 383 | return this; 384 | } 385 | 386 | /** 387 | * @param noDrawHeaderDivider 是否绘制头部分割线 388 | * @return 389 | */ 390 | public BaseBuilder removeHeaderDivider(boolean noDrawHeaderDivider) { 391 | this.noDrawHeaderDivider = noDrawHeaderDivider; 392 | return this; 393 | } 394 | 395 | /** 396 | * @param noDrawFooterDivider 是否绘制底部分割线 397 | * @return 398 | */ 399 | public BaseBuilder removeFooterDivider(boolean noDrawFooterDivider) { 400 | this.noDrawFooterDivider = noDrawFooterDivider; 401 | return this; 402 | } 403 | 404 | /** 405 | * @param noDrawLeftDivider 是否绘制最左边分割线 406 | * @return 407 | */ 408 | public BaseBuilder removeLeftDivider(boolean noDrawLeftDivider) { 409 | this.noDrawLeftDivider = noDrawLeftDivider; 410 | return this; 411 | } 412 | 413 | /** 414 | * @param noDrawRightDivider 是否绘制最右边分割线 415 | * @return 416 | */ 417 | public BaseBuilder removeRightDivider(boolean noDrawRightDivider) { 418 | this.noDrawRightDivider = noDrawRightDivider; 419 | return this; 420 | } 421 | 422 | /** 423 | * 头部分割线的定制 424 | * 425 | * @return 426 | */ 427 | public BaseBuilder redrawHeaderDivider() { 428 | this.isRedrawHeaderDivider = true; 429 | return this; 430 | } 431 | 432 | /** 433 | * @param drawableId 头部分割线drawable资源id 434 | * @return 435 | */ 436 | public BaseBuilder redrawHeaderDividerDrawable(@DrawableRes int drawableId) { 437 | this.mHeaderDividerDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 438 | if (this.mHeaderDividerHeight == 0) { 439 | this.mHeaderDividerHeight = mHeaderDividerDrawable.getIntrinsicHeight(); 440 | } 441 | return this; 442 | } 443 | 444 | /** 445 | * @param height 头部分割线高度 446 | * @return 447 | */ 448 | public BaseBuilder redrawHeaderDividerHeight(@IntRange(from = 0) int height) { 449 | this.mHeaderDividerHeight = height; 450 | return this; 451 | } 452 | 453 | /** 454 | * 底部分割线的定制 455 | * 456 | * @return 457 | */ 458 | public BaseBuilder redrawFooterDivider() { 459 | this.isRedrawFooterDivider = true; 460 | return this; 461 | } 462 | 463 | /** 464 | * @param drawableId 底部分割线的drawable资源id 465 | * @return 466 | */ 467 | public BaseBuilder redrawFooterDividerDrawable(@DrawableRes int drawableId) { 468 | this.mFooterDividerDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 469 | if (this.mFooterDividerHeight == 0) { 470 | this.mFooterDividerHeight = this.mFooterDividerDrawable.getIntrinsicHeight(); 471 | } 472 | return this; 473 | } 474 | 475 | /** 476 | * @param height 底部分割线的高度 477 | * @return 478 | */ 479 | public BaseBuilder redrawFooterDividerHeight(@IntRange(from = 0) int height) { 480 | this.mFooterDividerHeight = height; 481 | return this; 482 | } 483 | 484 | /** 485 | * 最左边分割线的定制 486 | * 487 | * @return 488 | */ 489 | public BaseBuilder redrawLeftDivider() { 490 | this.isRedrawLeftDivider = true; 491 | return this; 492 | } 493 | 494 | /** 495 | * @param drawableId 最左边分割线的drawable资源id 496 | * @return 497 | */ 498 | public BaseBuilder redrawLeftDividerDrawable(@DrawableRes int drawableId) { 499 | this.mLeftDividerDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 500 | if (this.mLeftDividerWidth == 0) { 501 | this.mLeftDividerWidth = mLeftDividerDrawable.getIntrinsicWidth(); 502 | } 503 | return this; 504 | } 505 | 506 | /** 507 | * @param width 最左边分割线的宽度 508 | * @return 509 | */ 510 | public BaseBuilder redrawLeftDividerWidth(@IntRange(from = 0) int width) { 511 | this.mLeftDividerWidth = width; 512 | return this; 513 | } 514 | 515 | /** 516 | * 最右边分割线的定制 517 | * 518 | * @return 519 | */ 520 | public BaseBuilder redrawRightDivider() { 521 | this.isRedrawRightDivider = true; 522 | return this; 523 | } 524 | 525 | /** 526 | * @param drawableId 最右边分割线的drawable资源id 527 | * @return 528 | */ 529 | public BaseBuilder redrawRightDividerDrawable(@DrawableRes int drawableId) { 530 | this.mRightDividerDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 531 | if (this.mRightDividerWidth == 0) { 532 | this.mRightDividerWidth = mRightDividerDrawable.getIntrinsicWidth(); 533 | } 534 | return this; 535 | } 536 | 537 | /** 538 | * @param width 最右边分割线的宽度 539 | * @return 540 | */ 541 | public BaseBuilder redrawRightDividerWidth(@IntRange(from = 0) int width) { 542 | this.mRightDividerWidth = width; 543 | return this; 544 | } 545 | 546 | /** 547 | * 分割线的定制 548 | * 549 | * @param dividerLineIndex 分割线的下标 550 | * @return 551 | */ 552 | public BaseBuilder redrawDivider(@IntRange(from = 0) int dividerLineIndex) { 553 | this.mDividerIndex = dividerLineIndex; 554 | this.isRedrawDivider = true; 555 | return this; 556 | } 557 | 558 | /** 559 | * @param height 定制分割线的高度 560 | * @return 561 | */ 562 | public BaseBuilder redrawDividerHeight(@IntRange(from = 0) int height) { 563 | this.mRedrawDividerHeight = height; 564 | return this; 565 | } 566 | 567 | /** 568 | * @param width 定制分割线的宽度 569 | * @return 570 | */ 571 | public BaseBuilder redrawDividerWidth(@IntRange(from = 0) int width) { 572 | this.mRedrawDividerWidth = width; 573 | return this; 574 | } 575 | 576 | /** 577 | * @param drawableId 定制分割线的drawable资源id 578 | * @return 579 | */ 580 | public BaseBuilder redrawDividerDrawable(@DrawableRes int drawableId) { 581 | this.mDividerDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 582 | if (mOrientation == VERTICAL) { 583 | if (this.mRedrawDividerHeight == 0) { 584 | this.mRedrawDividerHeight = mDividerDrawable.getIntrinsicHeight(); 585 | } 586 | } else { 587 | if (mRedrawDividerWidth == 0) { 588 | this.mRedrawDividerWidth = mDividerDrawable.getIntrinsicWidth(); 589 | } 590 | } 591 | return this; 592 | } 593 | 594 | /** 595 | * 分割线的批量定制 596 | * 597 | * @param startIndex 当mOrientation==Vertical时,startIndex代表起始行的下标;否则,startIndex代表起始列的下标 598 | * @param endIndex 当mOrientation==Vertical时,endIndex代表末尾行的下标;否则,endIndex代表末尾列的下标 599 | * @return 600 | */ 601 | public BaseBuilder subDivider(@IntRange(from = 0) int startIndex, @IntRange(from = 1) int endIndex) { 602 | int subLen = endIndex - startIndex; 603 | if (subLen <= 0) { 604 | throw new IndexOutOfBoundsException(startIndex + ">=" + endIndex); 605 | } 606 | this.mStartIndex = startIndex; 607 | this.mEndIndex = endIndex; 608 | this.isSubDivider = true; 609 | return this; 610 | } 611 | 612 | /** 613 | * @param height 分割线的高度 614 | * @return 615 | */ 616 | public BaseBuilder setSubDividerHeight(@IntRange(from = 0) int height) { 617 | this.mSubDividerHeight = height; 618 | return this; 619 | } 620 | 621 | /** 622 | * @param width 分割线的宽度 623 | * @return 624 | */ 625 | public BaseBuilder setSubDividerWidth(@IntRange(from = 0) int width) { 626 | this.mSubDividerWidth = width; 627 | return this; 628 | } 629 | 630 | /** 631 | * @param drawableId 分割线的drawable资源id 632 | * @return 633 | */ 634 | public BaseBuilder setSubDividerDrawable(@DrawableRes int drawableId) { 635 | this.mSubDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 636 | if (mOrientation == VERTICAL) { 637 | if (this.mSubDividerHeight == 0) { 638 | this.mSubDividerHeight = mSubDrawable.getIntrinsicHeight(); 639 | } 640 | } else { 641 | if (this.mSubDividerWidth == 0) { 642 | this.mSubDividerWidth = mSubDrawable.getIntrinsicWidth(); 643 | } 644 | } 645 | return this; 646 | } 647 | } 648 | 649 | } 650 | -------------------------------------------------------------------------------- /decoration/src/main/java/com/ckr/decoration/DecorationLog.java: -------------------------------------------------------------------------------- 1 | package com.ckr.decoration; 2 | 3 | import android.util.Log; 4 | 5 | /** 6 | * Created by PC大佬 on 2018/1/26. 7 | */ 8 | 9 | public class DecorationLog { 10 | private static final String TAG = "DecorationLog"; 11 | private static boolean isDebug = false; 12 | 13 | public static void debug() { 14 | DecorationLog.isDebug = true; 15 | } 16 | 17 | public static void Logd(String msg) { 18 | Logd("", msg); 19 | } 20 | 21 | public static void Logd(String tag, String msg) { 22 | if (isDebug) { 23 | Log.d(TAG, tag + "--->" + msg); 24 | } 25 | } 26 | 27 | public static void Logi(String msg) { 28 | Logi("", msg); 29 | } 30 | 31 | public static void Logi(String tag, String msg) { 32 | if (isDebug) { 33 | Log.i(TAG, tag + "--->" + msg); 34 | } 35 | } 36 | 37 | public static void Logw(String msg) { 38 | Logw("", msg); 39 | } 40 | 41 | public static void Logw(String tag, String msg) { 42 | if (isDebug) { 43 | Log.w(TAG, tag + "--->" + msg); 44 | } 45 | } 46 | 47 | public static void Loge(String msg) { 48 | Loge("", msg); 49 | } 50 | 51 | public static void Loge(String tag, String msg) { 52 | if (isDebug) { 53 | Log.e(TAG, tag + "--->" + msg); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /decoration/src/main/java/com/ckr/decoration/DividerGridItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.ckr.decoration; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.Rect; 8 | import android.graphics.drawable.Drawable; 9 | import android.support.annotation.DrawableRes; 10 | import android.support.annotation.IntRange; 11 | import android.support.v4.content.ContextCompat; 12 | import android.support.v7.widget.RecyclerView; 13 | import android.text.TextUtils; 14 | import android.view.View; 15 | 16 | import static com.ckr.decoration.DecorationLog.Logd; 17 | import static com.ckr.decoration.DecorationLog.Loge; 18 | 19 | /** 20 | * Created by PC大佬 on 2018/1/6. 21 | */ 22 | public class DividerGridItemDecoration extends BaseItemDecoration { 23 | private static final String TAG = "GridItemDecoration"; 24 | protected int mSpanCount = 1; 25 | private Drawable mTopDivider; //item上方分割线的drawable 26 | private Drawable mBottomDivider; //item下方分割线的drawable 27 | private Drawable mLeftDivider; //item左边方分割线的drawable 28 | private Drawable mRightDivider; //item右方分割线的drawable 29 | private boolean isSticky;//固定头部 30 | private int mStickyHeightOrWidth;//固定头部高度或宽度 31 | private Drawable mStickyDrawable;//固定头部样式 32 | private int mStickyTextPaddingLeft = 48;//固定头部的文本左边距 33 | private int mStickyTextPaddingTop = 60;//固定头部的文本上边距 34 | private int mStickyTextColor = Color.WHITE;//固定头部的文本的字体颜色 35 | private int mStickyTextSize = 42;//固定头部的文本的字体大小 36 | private Paint mStickyTextPaint; 37 | private float mOffsetY;//字体中间线到基准线baseline的偏移量 38 | private float mTextHeight; 39 | 40 | public DividerGridItemDecoration(Context context, int mSpanCount) { 41 | super(context, GRID, VERTICAL); 42 | this.mSpanCount = mSpanCount; 43 | } 44 | 45 | public DividerGridItemDecoration(Context context, int orientation, int mSpanCount) { 46 | super(context, GRID, orientation); 47 | this.mSpanCount = mSpanCount; 48 | } 49 | 50 | public DividerGridItemDecoration(Context context, int orientation, int mSpanCount, @DrawableRes int drawableId) { 51 | super(context, GRID, orientation, drawableId); 52 | this.mSpanCount = mSpanCount; 53 | } 54 | 55 | private DividerGridItemDecoration(Builder builder) { 56 | super(builder); 57 | this.mSpanCount = builder.mSpanCount; 58 | this.isSticky = builder.isSticky; 59 | this.mStickyHeightOrWidth = builder.mStickyHeightOrWidth; 60 | this.mStickyDrawable = builder.mStickyDrawable; 61 | this.mStickyTextPaddingLeft = builder.mStickyTextPaddingLeft; 62 | this.mStickyTextPaddingTop = builder.mStickyTextPaddingTop; 63 | this.mStickyTextColor = builder.mStickyTextColor; 64 | this.mStickyTextSize = builder.mStickyTextSize; 65 | if (isSticky) { 66 | mStickyTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 67 | mStickyTextPaint.setColor(mStickyTextColor);//注意:颜色需为argb,否则,绘制不出 68 | mStickyTextPaint.setTextSize(mStickyTextSize); 69 | if (mOrientation == HORIZONTAL) { 70 | mStickyTextPaint.setTextAlign(Paint.Align.CENTER); 71 | } 72 | Paint.FontMetricsInt mFontMetricsInt = mStickyTextPaint.getFontMetricsInt(); 73 | mTextHeight = (mFontMetricsInt.descent - mFontMetricsInt.ascent); 74 | float textCenter = mTextHeight / 2.0f; 75 | mOffsetY = -mFontMetricsInt.ascent - textCenter; 76 | } 77 | } 78 | 79 | public DividerGridItemDecoration setShowOtherStyle(boolean showOtherStyle) { 80 | isShowOtherStyle = showOtherStyle; 81 | return this; 82 | } 83 | 84 | public BaseItemDecoration setSticky(boolean sticky) { 85 | isSticky = sticky; 86 | return this; 87 | } 88 | 89 | public BaseItemDecoration setStickyHeightOrWidth(@IntRange(from = 0) int stickyHeaderHeight) { 90 | this.mStickyHeightOrWidth = stickyHeaderHeight; 91 | return this; 92 | } 93 | 94 | public BaseItemDecoration setStickyDrawable(@DrawableRes int drawableId) { 95 | this.mStickyDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 96 | ; 97 | if (this.mStickyHeightOrWidth == 0) { 98 | this.mStickyHeightOrWidth = mStickyDrawable.getIntrinsicHeight(); 99 | } 100 | return this; 101 | } 102 | 103 | public BaseItemDecoration setStickyTextPaddingLeft(int textPaddingLeft) { 104 | this.mStickyTextPaddingLeft = textPaddingLeft; 105 | return this; 106 | } 107 | 108 | public BaseItemDecoration setStickyTextPaddingTop(int textPaddingTop) { 109 | this.mStickyTextPaddingTop = textPaddingTop; 110 | return this; 111 | } 112 | 113 | /** 114 | * @param textColor 颜色需为argb,否则不生效 115 | * @return 116 | */ 117 | public BaseItemDecoration setStickyTextColor(int textColor) { 118 | this.mStickyTextColor = textColor; 119 | return this; 120 | } 121 | 122 | public BaseItemDecoration setStickyTextSize(int textSize) { 123 | this.mStickyTextSize = textSize; 124 | return this; 125 | } 126 | 127 | //绘制水平分割线 128 | @Override 129 | protected void drawHorizontal(Canvas c, RecyclerView parent) { 130 | int childCount = parent.getChildCount();//可视item的个数 131 | int itemCount = parent.getAdapter().getItemCount();//item总个数 132 | Logd(TAG, "drawHorizontal: childCount:" + childCount + ",itemCount:" + itemCount); 133 | int left = 0; 134 | int top = 0; 135 | int right = 0; 136 | int bottom = 0; 137 | boolean headerPosHandle = true; 138 | boolean footerPosHandle = true; 139 | boolean isSubDividerHandle = true; 140 | boolean isRedrawDividerHandle = true; 141 | for (int i = 0; i < childCount; i++) { 142 | final View child = parent.getChildAt(i); 143 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 144 | .getLayoutParams(); 145 | int topDividerHeight = mDividerHeight; 146 | int bottomDividerHeight = mDividerHeight; 147 | mTopDivider = mDivider; 148 | mBottomDivider = mDivider; 149 | if (mOrientation == VERTICAL) { 150 | int leftDividerWidth = mDividerWidth; 151 | int rightDividerWidth = mDividerWidth; 152 | if (noDrawLeftDivider) {//最左边分割线处理 153 | if (i % mSpanCount == 0) { 154 | leftDividerWidth = 0; 155 | } 156 | } 157 | if (noDrawRightDivider) {//最右边分割线处理 158 | if (i % mSpanCount == mSpanCount - 1) { 159 | rightDividerWidth = 0; 160 | } 161 | } 162 | left = child.getLeft() - params.leftMargin - leftDividerWidth;//计算分割线的左边 163 | right = child.getRight() + params.rightMargin + rightDividerWidth;//计算分割线的右边 164 | // 165 | if (noDrawHeaderDivider) {//顶部分割线处理 166 | if (headerPosHandle) { 167 | int adapterPosition = parent.getChildAdapterPosition(child); 168 | if (mSpanCount > adapterPosition) { 169 | Logd(TAG, "drawHorizontal: noDrawHeaderDivider:" + i + ",adapterPosition:" + adapterPosition); 170 | topDividerHeight = 0; 171 | if (adapterPosition == mSpanCount - 1) { 172 | headerPosHandle = false; 173 | } 174 | } else { 175 | headerPosHandle = false; 176 | } 177 | } 178 | } else { 179 | if (isRedrawHeaderDivider) {//顶部分割线的定制 180 | if (headerPosHandle) { 181 | int adapterPosition = parent.getChildAdapterPosition(child); 182 | if (mSpanCount > adapterPosition) { 183 | Logd(TAG, "drawHorizontal: isRedrawHeaderDivider:" + i + ",adapterPosition:" + adapterPosition); 184 | topDividerHeight = mHeaderDividerHeight; 185 | if (adapterPosition == mSpanCount - 1) { 186 | headerPosHandle = false; 187 | } 188 | if (mHeaderDividerDrawable != null) { 189 | mTopDivider = mHeaderDividerDrawable; 190 | } 191 | } else { 192 | headerPosHandle = false; 193 | } 194 | } 195 | } 196 | } 197 | // 198 | // 199 | if (noDrawFooterDivider) {//底部分割线处理 200 | if (footerPosHandle) { 201 | int rowNum = itemCount % mSpanCount == 0 ? itemCount / mSpanCount - 1 : itemCount / mSpanCount; 202 | int startNum = childCount - (itemCount - rowNum * mSpanCount); 203 | if (startNum <= i) { 204 | int adapterPosition = parent.getChildAdapterPosition(child); 205 | if (rowNum * mSpanCount <= adapterPosition) { 206 | Logd(TAG, "drawHorizontal: noDrawFooterDivider:" + i + ",adapterPosition:" + adapterPosition); 207 | bottomDividerHeight = 0; 208 | if (adapterPosition == itemCount - 1) { 209 | footerPosHandle = false; 210 | } 211 | } else { 212 | footerPosHandle = false; 213 | } 214 | } 215 | } 216 | } else { 217 | if (isRedrawFooterDivider) {//底部分割线的定制 218 | if (footerPosHandle) { 219 | int rowNum = itemCount % mSpanCount == 0 ? itemCount / mSpanCount - 1 : itemCount / mSpanCount; 220 | int startNum = childCount - (itemCount - rowNum * mSpanCount); 221 | if (startNum <= i) { 222 | int adapterPosition = parent.getChildAdapterPosition(child); 223 | if (rowNum * mSpanCount <= adapterPosition) { 224 | Logd(TAG, "drawHorizontal: isRedrawFooterDivider:" + i + ",adapterPosition:" + adapterPosition); 225 | bottomDividerHeight = mFooterDividerHeight; 226 | if (adapterPosition == itemCount - 1) { 227 | footerPosHandle = false; 228 | } 229 | if (mFooterDividerDrawable != null) { 230 | mBottomDivider = mFooterDividerDrawable; 231 | } 232 | } else { 233 | footerPosHandle = false; 234 | } 235 | } 236 | } 237 | } 238 | } 239 | // 240 | // 241 | if (isSubDivider) {//分割线的截取 242 | if (isSubDividerHandle) { 243 | int adapterPosition = parent.getChildAdapterPosition(child); 244 | int rowCount = itemCount % mSpanCount == 0 ? itemCount / mSpanCount : itemCount / mSpanCount + 1; 245 | int rowNum = (adapterPosition + 1) % mSpanCount == 0 ? (adapterPosition + 1) / mSpanCount - 1 : (adapterPosition + 1) / mSpanCount; 246 | if (rowNum > mStartIndex) { 247 | int maxRow = Math.min(mEndIndex, rowCount - 1); 248 | if (rowNum < maxRow) { 249 | bottomDividerHeight = mSubDividerHeight; 250 | topDividerHeight = mSubDividerHeight; 251 | if (mSubDrawable != null) { 252 | mTopDivider = mSubDrawable; 253 | mBottomDivider = mSubDrawable; 254 | } 255 | } else if (rowNum == maxRow) { 256 | topDividerHeight = mSubDividerHeight; 257 | if (mSubDrawable != null) { 258 | mTopDivider = mSubDrawable; 259 | } 260 | } else { 261 | isSubDividerHandle = false; 262 | } 263 | } else if (rowNum == mStartIndex) { 264 | bottomDividerHeight = mSubDividerHeight; 265 | if (mSubDrawable != null) { 266 | mBottomDivider = mSubDrawable; 267 | } 268 | } 269 | } 270 | } 271 | // 272 | // 273 | if (isRedrawDivider) {//分割线的定制 274 | if (isRedrawDividerHandle) { 275 | int adapterPosition = parent.getChildAdapterPosition(child); 276 | int rowNum = (adapterPosition + 1) % mSpanCount == 0 ? (adapterPosition + 1) / mSpanCount : (adapterPosition + 1) / mSpanCount + 1; 277 | if ((rowNum - 1) == mDividerIndex) { 278 | bottomDividerHeight = mRedrawDividerHeight; 279 | if (mDividerDrawable != null) { 280 | mBottomDivider = mDividerDrawable; 281 | } 282 | } else if (mDividerIndex + 1 == rowNum - 1) { 283 | topDividerHeight = mRedrawDividerHeight; 284 | if (mDividerDrawable != null) { 285 | mTopDivider = mDividerDrawable; 286 | } 287 | } 288 | } 289 | } 290 | // 291 | // 292 | bottom = child.getTop() - params.topMargin;//计算分割线的下边 293 | top = bottom - topDividerHeight;//计算分割线的上边 294 | mTopDivider.setBounds(left, top, right, bottom); 295 | mTopDivider.draw(c); 296 | // 297 | // 298 | top = child.getBottom() + params.bottomMargin;//计算分割线的上边 299 | bottom = top + bottomDividerHeight;//计算分割线的下边 300 | mBottomDivider.setBounds(left, top, right, bottom); 301 | mBottomDivider.draw(c); 302 | // 303 | } else { 304 | left = child.getLeft() - params.leftMargin;//计算分割线的左边 305 | right = child.getRight() + params.rightMargin;//计算分割线的右边 306 | if (noDrawHeaderDivider) {//顶部分割线处理 307 | if (i % mSpanCount == 0) { 308 | Loge(TAG, "drawHorizontal: noDrawHeaderDivider:" + i); 309 | topDividerHeight = 0; 310 | } 311 | } 312 | if (noDrawFooterDivider) {//底部分割线处理 313 | if (i % mSpanCount == mSpanCount - 1) { 314 | Loge(TAG, "drawHorizontal: noDrawFooterDivider:" + i); 315 | bottomDividerHeight = 0; 316 | } 317 | } 318 | // 319 | bottom = child.getTop() - params.topMargin;//计算分割线的下边 320 | top = bottom - topDividerHeight;//计算分割线的上边 321 | mDivider.setBounds(left, top, right, bottom); 322 | mDivider.draw(c); 323 | // 324 | // 325 | top = child.getBottom() + params.bottomMargin;//计算分割线的上边 326 | bottom = top + bottomDividerHeight;//计算分割线的下边 327 | mDivider.setBounds(left, top, right, bottom); 328 | mDivider.draw(c); 329 | // 330 | } 331 | } 332 | } 333 | 334 | //绘制竖直分割线 335 | @Override 336 | protected void drawVertical(Canvas c, RecyclerView parent) { 337 | final int childCount = parent.getChildCount();//可视item的个数 338 | int itemCount = parent.getAdapter().getItemCount();//item个数 339 | int left = 0; 340 | int top = 0; 341 | int right = 0; 342 | int bottom = 0; 343 | boolean leftPosHandle = true; 344 | boolean rightPosHandle = true; 345 | boolean isSubDividerHandle = true; 346 | boolean isRedrawDividerHandle = true; 347 | for (int i = 0; i < childCount; i++) { 348 | final View child = parent.getChildAt(i); 349 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 350 | .getLayoutParams(); 351 | int leftDividerWidth = mDividerWidth; 352 | int rightDividerWidth = mDividerWidth; 353 | mLeftDivider = mDivider; 354 | mRightDivider = mDivider; 355 | if (mOrientation == VERTICAL) { 356 | top = child.getTop() - params.topMargin; 357 | bottom = child.getBottom() + params.bottomMargin; 358 | if (noDrawLeftDivider) { 359 | if (i % mSpanCount == 0) { 360 | leftDividerWidth = 0; 361 | } 362 | } 363 | if (noDrawRightDivider) { 364 | if (i % mSpanCount == mSpanCount - 1) { 365 | rightDividerWidth = 0; 366 | } 367 | } 368 | // 369 | right = child.getLeft() - params.leftMargin; 370 | left = right - leftDividerWidth; 371 | mDivider.setBounds(left, top, right, bottom); 372 | mDivider.draw(c); 373 | // 374 | // 375 | left = child.getRight() + params.rightMargin; 376 | right = left + rightDividerWidth; 377 | mDivider.setBounds(left, top, right, bottom); 378 | mDivider.draw(c); 379 | // 380 | } else { 381 | int topDividerWidth = mDividerHeight; 382 | int bottomDividerWidth = mDividerHeight; 383 | if (noDrawHeaderDivider) {//头部分割线处理 384 | if (i % mSpanCount == 0) { 385 | topDividerWidth = 0; 386 | } 387 | } 388 | if (noDrawFooterDivider) {//底部分割线处理 389 | if (i % mSpanCount == mSpanCount - 1) { 390 | bottomDividerWidth = 0; 391 | } 392 | } 393 | top = child.getTop() - params.topMargin - topDividerWidth; 394 | bottom = child.getBottom() + params.bottomMargin + bottomDividerWidth; 395 | // 396 | if (noDrawLeftDivider) {//左边分割线处理 397 | if (leftPosHandle) { 398 | int adapterPosition = parent.getChildAdapterPosition(child); 399 | if (mSpanCount > adapterPosition) { 400 | Loge(TAG, "drawVertical: noDrawHeaderDivider:" + i + ",adapterPosition:" + adapterPosition); 401 | leftDividerWidth = 0; 402 | if (adapterPosition == mSpanCount - 1) { 403 | leftPosHandle = false; 404 | } 405 | } else { 406 | leftPosHandle = false; 407 | } 408 | } 409 | } else { 410 | if (isRedrawLeftDivider) {//左边分割线定制 411 | if (leftPosHandle) { 412 | int adapterPosition = parent.getChildAdapterPosition(child); 413 | if (mSpanCount > adapterPosition) { 414 | leftDividerWidth = mLeftDividerWidth; 415 | if (adapterPosition == mSpanCount - 1) { 416 | leftPosHandle = false; 417 | } 418 | if (mLeftDividerDrawable != null) { 419 | mLeftDivider = mLeftDividerDrawable; 420 | } 421 | } else { 422 | leftPosHandle = false; 423 | } 424 | } 425 | } 426 | } 427 | // 428 | // 429 | if (noDrawRightDivider) {//右边分割线处理 430 | if (rightPosHandle) { 431 | int columnNum = itemCount % mSpanCount == 0 ? itemCount / mSpanCount - 1 : itemCount / mSpanCount; 432 | int startNum = childCount - (itemCount - columnNum * mSpanCount); 433 | if (startNum <= i) { 434 | int adapterPosition = parent.getChildAdapterPosition(child); 435 | if (columnNum * mSpanCount <= adapterPosition) { 436 | Logd(TAG, "drawVertical: noDrawFooterDivider:" + i + ",adapterPosition:" + adapterPosition); 437 | rightDividerWidth = 0; 438 | if (adapterPosition == itemCount - 1) { 439 | rightPosHandle = false; 440 | } 441 | } else { 442 | rightPosHandle = false; 443 | } 444 | } 445 | } 446 | } else { 447 | if (isRedrawRightDivider) {//右边分割线定制 448 | if (rightPosHandle) { 449 | int columnNum = itemCount % mSpanCount == 0 ? itemCount / mSpanCount - 1 : itemCount / mSpanCount; 450 | int startNum = childCount - (itemCount - columnNum * mSpanCount); 451 | if (startNum <= i) { 452 | int adapterPosition = parent.getChildAdapterPosition(child); 453 | if (columnNum * mSpanCount <= adapterPosition) { 454 | Logd(TAG, "drawVertical: noDrawFooterDivider:" + i + ",adapterPosition:" + adapterPosition); 455 | rightDividerWidth = mRightDividerWidth; 456 | if (adapterPosition == itemCount - 1) { 457 | rightPosHandle = false; 458 | } 459 | if (mRightDividerDrawable != null) { 460 | mRightDivider = mRightDividerDrawable; 461 | } 462 | } else { 463 | rightPosHandle = false; 464 | } 465 | } 466 | } 467 | } 468 | } 469 | // 470 | // 471 | if (isSubDivider) {//分割线的截取 472 | if (isSubDividerHandle) { 473 | int adapterPosition = parent.getChildAdapterPosition(child); 474 | int rowCount = itemCount % mSpanCount == 0 ? itemCount / mSpanCount : itemCount / mSpanCount + 1; 475 | int rowNum = (adapterPosition + 1) % mSpanCount == 0 ? (adapterPosition + 1) / mSpanCount - 1 : (adapterPosition + 1) / mSpanCount; 476 | if (rowNum > mStartIndex) { 477 | Loge(TAG, "drawVertical: mStartIndex:" + mStartIndex + ",mEndIndex:" + mEndIndex + ",adapterPosition:" + adapterPosition); 478 | int maxRow = Math.min(mEndIndex, rowCount - 1); 479 | if (rowNum < maxRow) { 480 | leftDividerWidth = mSubDividerWidth; 481 | rightDividerWidth = mSubDividerWidth; 482 | if (mSubDrawable != null) { 483 | mLeftDivider = mSubDrawable; 484 | mRightDivider = mSubDrawable; 485 | } 486 | } else if (rowNum == maxRow) { 487 | leftDividerWidth = mSubDividerWidth; 488 | if (mSubDrawable != null) { 489 | mLeftDivider = mSubDrawable; 490 | } 491 | } else { 492 | isSubDividerHandle = false; 493 | } 494 | } else if (rowNum == mStartIndex) { 495 | rightDividerWidth = mSubDividerWidth; 496 | if (mSubDrawable != null) { 497 | mRightDivider = mSubDrawable; 498 | } 499 | } 500 | } 501 | } 502 | // 503 | // 504 | if (isRedrawDivider) {//分割线的定制 505 | if (isRedrawDividerHandle) { 506 | int adapterPosition = parent.getChildAdapterPosition(child); 507 | int rowNum = (adapterPosition + 1) % mSpanCount == 0 ? (adapterPosition + 1) / mSpanCount : (adapterPosition + 1) / mSpanCount + 1; 508 | if ((rowNum - 1) == mDividerIndex) { 509 | rightDividerWidth = mRedrawDividerWidth; 510 | if (mDividerDrawable != null) { 511 | mRightDivider = mDividerDrawable; 512 | } 513 | } else if (mDividerIndex + 1 == rowNum - 1) { 514 | leftDividerWidth = mRedrawDividerWidth; 515 | if (mDividerDrawable != null) { 516 | mLeftDivider = mDividerDrawable; 517 | } 518 | } 519 | } 520 | } 521 | // 522 | // 523 | right = child.getLeft() - params.leftMargin; 524 | left = right - leftDividerWidth; 525 | mLeftDivider.setBounds(left, top, right, bottom); 526 | mLeftDivider.draw(c); 527 | // 528 | // 529 | left = child.getRight() + params.rightMargin; 530 | right = left + rightDividerWidth; 531 | mRightDivider.setBounds(left, top, right, bottom); 532 | mRightDivider.draw(c); 533 | // 534 | } 535 | } 536 | } 537 | 538 | /** 539 | * 要想清楚outRect作用,请看{@link android.support.v7.widget.GridLayoutManager}源码,如:measureChild(). 540 | */ 541 | @Override 542 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 543 | int itemPosition = parent.getChildAdapterPosition(view); 544 | Loge(TAG, "getItemOffsets: pos:" + itemPosition); 545 | int left = mDividerWidth; 546 | int top = mDividerHeight; 547 | int right = mDividerWidth; 548 | int bottom = mDividerHeight; 549 | if (mOrientation == VERTICAL) { 550 | // 551 | if (isSticky) {//悬浮头部 552 | RecyclerView.Adapter adapter = parent.getAdapter(); 553 | if (adapter instanceof OnHeaderListener) { 554 | OnHeaderListener listener = ((OnHeaderListener) adapter); 555 | String headerName = listener.getHeaderName(itemPosition); 556 | if (!TextUtils.isEmpty(headerName)) { 557 | if (itemPosition == 0 || !headerName.equals(listener.getHeaderName(itemPosition - 1))) { 558 | Logd(TAG, "getItemOffsets: headerName:" + headerName + ",itemPosition:" + itemPosition); 559 | top = mStickyHeightOrWidth; 560 | outRect.set(0, top, 0, bottom); 561 | return; 562 | } 563 | } 564 | } 565 | } 566 | // 567 | // 568 | if (noDrawHeaderDivider) { 569 | if (mSpanCount > itemPosition) { 570 | Logd(TAG, "getItemOffsets: noDrawHeaderDivider:" + itemPosition); 571 | top = 0; 572 | } 573 | } else { 574 | if (isRedrawHeaderDivider) { 575 | if (mSpanCount > itemPosition) { 576 | Loge(TAG, "getItemOffsets: isRedrawHeaderDivider:" + itemPosition); 577 | top = mHeaderDividerHeight; 578 | } 579 | } 580 | } 581 | // 582 | // 583 | if (noDrawFooterDivider) { 584 | int itemCount = parent.getAdapter().getItemCount(); 585 | int rowNum = itemCount % mSpanCount == 0 ? itemCount / mSpanCount - 1 : itemCount / mSpanCount; 586 | if (rowNum * mSpanCount <= itemPosition) { 587 | Logd(TAG, "getItemOffsets: noDrawFooterDivider:" + itemPosition); 588 | bottom = 0; 589 | } 590 | } else { 591 | if (isRedrawFooterDivider) { 592 | int itemCount = parent.getAdapter().getItemCount(); 593 | int rowNum = itemCount % mSpanCount == 0 ? itemCount / mSpanCount - 1 : itemCount / mSpanCount; 594 | if (rowNum * mSpanCount <= itemPosition) { 595 | bottom = mFooterDividerHeight; 596 | } 597 | } 598 | } 599 | // 600 | if (noDrawLeftDivider) { 601 | if (itemPosition % mSpanCount == 0) { 602 | left = 0; 603 | } 604 | } 605 | if (noDrawRightDivider) { 606 | if (itemPosition % mSpanCount == mSpanCount - 1) { 607 | right = 0; 608 | } 609 | } 610 | // 611 | if (isSubDivider) { 612 | int itemCount = parent.getAdapter().getItemCount(); 613 | int rowCount = itemCount % mSpanCount == 0 ? itemCount / mSpanCount : itemCount / mSpanCount + 1; 614 | if (mStartIndex >= rowCount - 1) { 615 | isSubDivider = false; 616 | } else { 617 | int rowNum = (itemPosition + 1) % mSpanCount == 0 ? (itemPosition + 1) / mSpanCount - 1 : (itemPosition + 1) / mSpanCount;//计算第几行 618 | if (rowNum > mStartIndex) { 619 | int maxRow = Math.min(mEndIndex, rowCount - 1); 620 | if (rowNum < maxRow) { 621 | top = mSubDividerHeight; 622 | bottom = mSubDividerHeight; 623 | } else if (rowNum == maxRow) { 624 | top = mSubDividerHeight; 625 | } 626 | } else if (rowNum == mStartIndex) { 627 | bottom = mSubDividerHeight; 628 | } 629 | } 630 | } 631 | // 632 | // 633 | if (isRedrawDivider) { 634 | int itemCount = parent.getAdapter().getItemCount(); 635 | int rowCount = itemCount % mSpanCount == 0 ? itemCount / mSpanCount : itemCount / mSpanCount + 1;//计算总行数 636 | if (mDividerIndex >= rowCount - 1) { 637 | isRedrawDivider = false; 638 | } 639 | int rowNum = (itemPosition + 1) % mSpanCount == 0 ? (itemPosition + 1) / mSpanCount : (itemPosition + 1) / mSpanCount + 1;//计算第几行 640 | if ((rowNum - 1) == mDividerIndex) { 641 | bottom = mRedrawDividerHeight; 642 | } else if (mDividerIndex + 1 == rowNum - 1) { 643 | top = mRedrawDividerHeight; 644 | } 645 | } 646 | // 647 | } else { 648 | if (noDrawHeaderDivider) { 649 | if (itemPosition % mSpanCount == 0) { 650 | Logd(TAG, "getItemOffsets: noDrawHeaderDivider:" + itemPosition); 651 | top = 0; 652 | } 653 | } 654 | if (noDrawFooterDivider) { 655 | if (itemPosition % mSpanCount == mSpanCount - 1) { 656 | Loge(TAG, "getItemOffsets: noDrawFooterDivider:" + itemPosition); 657 | bottom = 0; 658 | } 659 | } 660 | // 661 | if (noDrawLeftDivider) { 662 | if (mSpanCount > itemPosition) { 663 | Loge(TAG, "getItemOffsets: noDrawLeftDivider:" + itemPosition); 664 | left = 0; 665 | } 666 | } else { 667 | if (isRedrawLeftDivider) { 668 | if (mSpanCount > itemPosition) { 669 | Loge(TAG, "getItemOffsets: isRedrawLeftDivider:" + itemPosition); 670 | left = mLeftDividerWidth; 671 | } 672 | } 673 | } 674 | // 675 | // 676 | if (noDrawRightDivider) { 677 | int itemCount = parent.getAdapter().getItemCount(); 678 | int columnCount = itemCount % mSpanCount == 0 ? itemCount / mSpanCount - 1 : itemCount / mSpanCount; 679 | if (columnCount * mSpanCount <= itemPosition) { 680 | right = 0; 681 | } 682 | } else { 683 | if (isRedrawRightDivider) { 684 | int itemCount = parent.getAdapter().getItemCount(); 685 | int columnCount = itemCount % mSpanCount == 0 ? itemCount / mSpanCount - 1 : itemCount / mSpanCount; 686 | if (columnCount * mSpanCount <= itemPosition) { 687 | right = mRightDividerWidth; 688 | } 689 | } 690 | } 691 | // 692 | // 693 | if (isSubDivider) { 694 | int itemCount = parent.getAdapter().getItemCount(); 695 | int rowCount = itemCount % mSpanCount == 0 ? itemCount / mSpanCount : itemCount / mSpanCount + 1; 696 | if (mStartIndex >= rowCount - 1) { 697 | isSubDivider = false; 698 | } else { 699 | int rowNum = (itemPosition + 1) % mSpanCount == 0 ? (itemPosition + 1) / mSpanCount - 1 : (itemPosition + 1) / mSpanCount;//计算第几行 700 | if (rowNum > mStartIndex) { 701 | int maxRow = Math.min(mEndIndex, rowCount - 1); 702 | if (rowNum < maxRow) { 703 | left = mSubDividerWidth; 704 | right = mSubDividerWidth; 705 | } else if (rowNum == maxRow) { 706 | left = mSubDividerWidth; 707 | } 708 | } else if (rowNum == mStartIndex) { 709 | right = mSubDividerWidth; 710 | } 711 | } 712 | } 713 | // 714 | // 715 | if (isRedrawDivider) { 716 | int itemCount = parent.getAdapter().getItemCount(); 717 | int rowCount = itemCount % mSpanCount == 0 ? itemCount / mSpanCount : itemCount / mSpanCount + 1;//计算总行数 718 | if (mDividerIndex >= rowCount - 1) { 719 | isRedrawDivider = false; 720 | } 721 | int rowNum = (itemPosition + 1) % mSpanCount == 0 ? (itemPosition + 1) / mSpanCount : (itemPosition + 1) / mSpanCount + 1;//计算第几行 722 | if ((rowNum - 1) == mDividerIndex) { 723 | right = mRedrawDividerWidth; 724 | } else if (mDividerIndex + 1 == rowNum - 1) { 725 | left = mRedrawDividerWidth; 726 | } 727 | } 728 | // 729 | } 730 | 731 | /* 732 | * left:代表item的左边分割线占有的x轴长度 733 | * top:代表item的顶部分割线占有的y轴长度 734 | * right:代表item的右边分割线占有的x轴长度 735 | * bottom:代表item的底部分割线占有的y轴长度 736 | * */ 737 | if (isShowOtherStyle) { 738 | outRect.set(0, top, right, 0); 739 | } else { 740 | outRect.set(left, top, right, bottom); 741 | } 742 | } 743 | 744 | public static class Builder extends BaseBuilder { 745 | private int mSpanCount = 1; 746 | private boolean isSticky;//固定头部 747 | private int mStickyHeightOrWidth;//固定头部高度或宽度 748 | private Drawable mStickyDrawable;//固定头部样式 749 | private int mStickyTextPaddingLeft = 48;//固定头部的文本左边距 750 | private int mStickyTextPaddingTop = 60;//固定头部的文本上边距 751 | private int mStickyTextColor = Color.WHITE;//固定头部的文本的字体颜色 752 | private int mStickyTextSize = 42;//固定头部的文本的字体大小 753 | 754 | public Builder(Context context, int mSpanCount) { 755 | super(context, GRID); 756 | this.mSpanCount = mSpanCount; 757 | if (this.mSpanCount == 1) { 758 | noDrawLeftDivider = true; 759 | noDrawRightDivider = true; 760 | } 761 | } 762 | 763 | public Builder(Context context, int mOrientation, int mSpanCount) { 764 | super(context, GRID, mOrientation); 765 | this.mSpanCount = mSpanCount; 766 | if (this.mSpanCount == 1) { 767 | noDrawLeftDivider = true; 768 | noDrawRightDivider = true; 769 | } 770 | } 771 | 772 | public BaseBuilder setShowOtherStyle(boolean showOtherStyle) { 773 | this.isShowOtherStyle = showOtherStyle; 774 | return this; 775 | } 776 | 777 | public Builder setSticky(boolean sticky) { 778 | isSticky = sticky; 779 | return this; 780 | } 781 | 782 | public Builder setStickyHeightOrWidth(@IntRange(from = 0) int stickyHeaderHeight) { 783 | this.mStickyHeightOrWidth = stickyHeaderHeight; 784 | return this; 785 | } 786 | 787 | public Builder setStickyDrawable(@DrawableRes int drawableId) { 788 | this.mStickyDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 789 | ; 790 | if (this.mStickyHeightOrWidth == 0) { 791 | this.mStickyHeightOrWidth = mStickyDrawable.getIntrinsicHeight(); 792 | } 793 | return this; 794 | } 795 | 796 | public Builder setStickyTextPaddingLeft(int textPaddingLeft) { 797 | this.mStickyTextPaddingLeft = textPaddingLeft; 798 | return this; 799 | } 800 | 801 | public Builder setStickyTextPaddingTop(int textPaddingTop) { 802 | this.mStickyTextPaddingTop = textPaddingTop; 803 | return this; 804 | } 805 | 806 | /** 807 | * @param textColor 颜色需为argb,否则不生效 808 | * @return 809 | */ 810 | public Builder setStickyTextColor(int textColor) { 811 | this.mStickyTextColor = textColor; 812 | return this; 813 | } 814 | 815 | public Builder setStickyTextSize(int textSize) { 816 | this.mStickyTextSize = textSize; 817 | return this; 818 | } 819 | 820 | public DividerGridItemDecoration build() { 821 | return new DividerGridItemDecoration(this); 822 | } 823 | } 824 | 825 | } 826 | -------------------------------------------------------------------------------- /decoration/src/main/java/com/ckr/decoration/DividerLinearItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.ckr.decoration; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.Rect; 8 | import android.graphics.drawable.Drawable; 9 | import android.support.annotation.DrawableRes; 10 | import android.support.annotation.IntRange; 11 | import android.support.v4.content.ContextCompat; 12 | import android.support.v7.widget.RecyclerView; 13 | import android.text.TextUtils; 14 | import android.view.View; 15 | 16 | import static com.ckr.decoration.DecorationLog.Logd; 17 | import static com.ckr.decoration.DecorationLog.Loge; 18 | 19 | 20 | /** 21 | * Created by PC大佬 on 2018/1/6. 22 | */ 23 | 24 | public class DividerLinearItemDecoration extends BaseItemDecoration { 25 | private static final String TAG = "LinearItemDecoration"; 26 | private int mDividerPaddingLeft;//分割线左边距,仅适用于竖直方向 27 | private int mDividerPaddingTop;//分割线上边距,仅适用于水平方向 28 | private int mDividerPaddingRight;//分割线右边距,仅适用于竖直方向 29 | private int mDividerPaddingBottom;//分割线下边距,仅适用于水平方向 30 | private boolean isSticky;//固定头部 31 | private int mStickyHeightOrWidth;//固定头部高度或宽度 32 | private Drawable mStickyDrawable;//固定头部样式 33 | private int mStickyTextPaddingLeft = 48;//固定头部的文本左边距 34 | private int mStickyTextPaddingTop = 60;//固定头部的文本上边距 35 | private int mStickyTextColor = Color.WHITE;//固定头部的文本的字体颜色 36 | private int mStickyTextSize = 42;//固定头部的文本的字体大小 37 | private Paint mStickyTextPaint; 38 | private float mOffsetY;//字体中间线到基准线baseline的偏移量 39 | private float mTextHeight; 40 | 41 | public DividerLinearItemDecoration(Context context) { 42 | super(context, LINEAR, VERTICAL); 43 | } 44 | 45 | public DividerLinearItemDecoration(Context context, int orientation) { 46 | super(context, LINEAR, orientation); 47 | } 48 | 49 | public DividerLinearItemDecoration(Context context, int orientation, @DrawableRes int drawableId) { 50 | super(context, LINEAR, orientation, drawableId); 51 | } 52 | 53 | private DividerLinearItemDecoration(Builder builder) { 54 | super(builder); 55 | this.mDividerPaddingLeft = builder.mDividerPaddingLeft; 56 | this.mDividerPaddingTop = builder.mDividerPaddingTop; 57 | this.mDividerPaddingRight = builder.mDividerPaddingRight; 58 | this.mDividerPaddingBottom = builder.mDividerPaddingBottom; 59 | this.isSticky = builder.isSticky; 60 | this.mStickyHeightOrWidth = builder.mStickyHeightOrWidth; 61 | this.mStickyDrawable = builder.mStickyDrawable; 62 | this.mStickyTextPaddingLeft = builder.mStickyTextPaddingLeft; 63 | this.mStickyTextPaddingTop = builder.mStickyTextPaddingTop; 64 | this.mStickyTextColor = builder.mStickyTextColor; 65 | this.mStickyTextSize = builder.mStickyTextSize; 66 | if (isSticky) { 67 | mStickyTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 68 | mStickyTextPaint.setColor(mStickyTextColor);//注意:颜色需为argb,否则,绘制不出 69 | mStickyTextPaint.setTextSize(mStickyTextSize); 70 | if (mOrientation == HORIZONTAL) { 71 | mStickyTextPaint.setTextAlign(Paint.Align.CENTER); 72 | } 73 | Paint.FontMetricsInt mFontMetricsInt = mStickyTextPaint.getFontMetricsInt(); 74 | mTextHeight = (mFontMetricsInt.descent - mFontMetricsInt.ascent); 75 | float textCenter = mTextHeight / 2.0f; 76 | mOffsetY = -mFontMetricsInt.ascent - textCenter; 77 | } 78 | } 79 | 80 | public BaseItemDecoration setDividerPaddingLeft(@IntRange(from = 0) int paddingLeft) { 81 | this.mDividerPaddingLeft = paddingLeft; 82 | return this; 83 | } 84 | 85 | public BaseItemDecoration setDividerPaddingRight(@IntRange(from = 0) int paddingRight) { 86 | this.mDividerPaddingRight = paddingRight; 87 | return this; 88 | } 89 | 90 | public BaseItemDecoration setDividerPaddingTop(@IntRange(from = 0) int paddingTop) { 91 | this.mDividerPaddingTop = paddingTop; 92 | return this; 93 | } 94 | 95 | public BaseItemDecoration setDividerPaddingBottom(@IntRange(from = 0) int paddingBottom) { 96 | this.mDividerPaddingBottom = paddingBottom; 97 | return this; 98 | } 99 | 100 | public BaseItemDecoration setDividerPadding(@IntRange(from = 0) int... padding) { 101 | for (int i = 0; i < padding.length; i++) { 102 | if (i == 0) { 103 | this.mDividerPaddingLeft = padding[i]; 104 | } else if (i == 1) { 105 | this.mDividerPaddingTop = padding[i]; 106 | } else if (i == 2) { 107 | this.mDividerPaddingRight = padding[i]; 108 | } else if (i == 3) { 109 | this.mDividerPaddingBottom = padding[i]; 110 | break; 111 | } 112 | } 113 | return this; 114 | } 115 | 116 | public BaseItemDecoration setSticky(boolean sticky) { 117 | isSticky = sticky; 118 | return this; 119 | } 120 | 121 | public BaseItemDecoration setStickyHeightOrWidth(@IntRange(from = 0) int stickyHeaderHeight) { 122 | this.mStickyHeightOrWidth = stickyHeaderHeight; 123 | return this; 124 | } 125 | 126 | public BaseItemDecoration setStickyDrawable(@DrawableRes int drawableId) { 127 | this.mStickyDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 128 | ; 129 | if (this.mStickyHeightOrWidth == 0) { 130 | this.mStickyHeightOrWidth = mStickyDrawable.getIntrinsicHeight(); 131 | } 132 | return this; 133 | } 134 | 135 | public BaseItemDecoration setStickyTextPaddingLeft(int textPaddingLeft) { 136 | this.mStickyTextPaddingLeft = textPaddingLeft; 137 | return this; 138 | } 139 | 140 | public BaseItemDecoration setStickyTextPaddingTop(int textPaddingTop) { 141 | this.mStickyTextPaddingTop = textPaddingTop; 142 | return this; 143 | } 144 | 145 | /** 146 | * @param textColor 颜色需为argb,否则不生效 147 | * @return 148 | */ 149 | public BaseItemDecoration setStickyTextColor(int textColor) { 150 | this.mStickyTextColor = textColor; 151 | return this; 152 | } 153 | 154 | public BaseItemDecoration setStickyTextSize(int textSize) { 155 | this.mStickyTextSize = textSize; 156 | return this; 157 | } 158 | 159 | //绘制竖直分割线 160 | @Override 161 | public void drawVertical(Canvas c, RecyclerView parent) { 162 | final int childCount = parent.getChildCount();//可视item的个数 163 | RecyclerView.Adapter adapter = parent.getAdapter(); 164 | int itemCount = adapter.getItemCount();//item个数 165 | boolean clipToPadding = isClipToPadding(parent); 166 | int left = 0, top = 0, right = 0, bottom = 0; 167 | top = parent.getPaddingTop() + mDividerPaddingTop; 168 | bottom = parent.getHeight() - parent.getPaddingBottom() - mDividerPaddingBottom; 169 | boolean leftPosHandle = true, rightPosHandle = true, isSubDividerHandle = true, isRedrawDividerHandle = true; 170 | for (int i = 0; i < childCount; i++) { 171 | final View child = parent.getChildAt(i); 172 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 173 | .getLayoutParams(); 174 | // 175 | if (isSticky) { 176 | if (adapter instanceof OnHeaderListener) { 177 | int adapterPosition = parent.getChildAdapterPosition(child); 178 | OnHeaderListener listener = ((OnHeaderListener) adapter); 179 | String headerName = listener.getHeaderName(adapterPosition); 180 | if (!TextUtils.isEmpty(headerName)) { 181 | if (adapterPosition != 0 && !headerName.equals(listener.getHeaderName(adapterPosition - 1))) { 182 | right = child.getLeft() - params.leftMargin;//计算分割线的右边 183 | left = right - mStickyHeightOrWidth;//计算分割线的左边 184 | int stickTop = parent.getPaddingTop(); 185 | int stickyBottom = parent.getHeight() - parent.getPaddingBottom(); 186 | if (clipToPadding) { 187 | int paddingLeft = parent.getPaddingLeft(); 188 | if (paddingLeft > left) { 189 | left = paddingLeft; 190 | } 191 | if (paddingLeft > right) { 192 | continue; 193 | } 194 | } 195 | if (mStickyDrawable != null) { 196 | mStickyDrawable.setBounds(left, stickTop, right, stickyBottom); 197 | mStickyDrawable.draw(c); 198 | } else { 199 | mDivider.setBounds(left, stickTop, right, stickyBottom); 200 | mDivider.draw(c); 201 | } 202 | int x = left + mStickyHeightOrWidth / 2; 203 | float baseline = stickTop + mStickyTextPaddingLeft; 204 | c.drawText(headerName, x, baseline, mStickyTextPaint); 205 | continue; 206 | } 207 | } 208 | } 209 | } 210 | // 211 | if (!noDrawLeftDivider && !isSticky) {//最左边分割线处理 212 | if (i == 0) { 213 | if (leftPosHandle) { 214 | leftPosHandle = false; 215 | int adapterPosition = parent.getChildAdapterPosition(child); 216 | if (0 == adapterPosition) { 217 | Logd(TAG, "drawVertical: adapterPosition:" + adapterPosition); 218 | right = child.getLeft() - params.leftMargin; 219 | if (isRedrawLeftDivider) {//最左边分割线的定制 220 | left = right - mLeftDividerWidth; 221 | if (clipToPadding) { 222 | int paddingLeft = parent.getPaddingLeft(); 223 | if (paddingLeft > left) { 224 | left = paddingLeft; 225 | } 226 | if (paddingLeft > right) { 227 | continue; 228 | } 229 | } 230 | if (mLeftDividerDrawable != null) { 231 | mLeftDividerDrawable.setBounds(left, top, right, bottom); 232 | mLeftDividerDrawable.draw(c); 233 | } else { 234 | mDivider.setBounds(left, top, right, bottom); 235 | mDivider.draw(c); 236 | } 237 | } else { 238 | left = right - mDividerWidth; 239 | if (clipToPadding) { 240 | int paddingLeft = parent.getPaddingLeft(); 241 | if (paddingLeft > left) { 242 | left = paddingLeft; 243 | } 244 | if (paddingLeft > right) { 245 | continue; 246 | } 247 | } 248 | mDivider.setBounds(left, top, right, bottom); 249 | mDivider.draw(c); 250 | } 251 | } 252 | continue; 253 | } 254 | } 255 | } else { 256 | if (i == 0) { 257 | int adapterPosition = parent.getChildAdapterPosition(child); 258 | if (0 == adapterPosition) { 259 | continue; 260 | } 261 | } 262 | } 263 | // 264 | // 265 | if (!noDrawRightDivider) {//最右边分割线处理 266 | if (childCount - 1 == i) { 267 | if (rightPosHandle) { 268 | int adapterPosition = parent.getChildAdapterPosition(child); 269 | rightPosHandle = false; 270 | if (itemCount - 1 == adapterPosition) { 271 | Logd(TAG, "drawVertical: adapterPosition:" + adapterPosition); 272 | left = child.getRight() + params.rightMargin; 273 | boolean isDraw = true; 274 | if (isRedrawRightDivider) {//最右边分割线的定制 275 | right = left + mRightDividerWidth; 276 | if (clipToPadding) {//兼容该属性的绘制方式 277 | int paddingRight = parent.getWidth() - parent.getPaddingRight(); 278 | if (left > paddingRight) { 279 | isDraw = false; 280 | } else { 281 | if (right > paddingRight) { 282 | right = paddingRight; 283 | } 284 | } 285 | } 286 | if (isDraw) { 287 | if (mRightDividerDrawable != null) { 288 | mRightDividerDrawable.setBounds(left, top, right, bottom); 289 | mRightDividerDrawable.draw(c); 290 | } else { 291 | mDivider.setBounds(left, top, right, bottom); 292 | mDivider.draw(c); 293 | } 294 | } 295 | } else { 296 | right = left + mDividerWidth; 297 | if (clipToPadding) {//兼容该属性的绘制方式 298 | int paddingRight = parent.getWidth() - parent.getPaddingRight(); 299 | if (left > paddingRight) { 300 | isDraw = false; 301 | } else { 302 | if (right > paddingRight) { 303 | right = paddingRight; 304 | } 305 | } 306 | } 307 | if (isDraw) { 308 | mDivider.setBounds(left, top, right, bottom); 309 | mDivider.draw(c); 310 | } 311 | } 312 | } 313 | } 314 | } 315 | } 316 | // 317 | // 318 | if (isRedrawDivider) {//分割线的定制 319 | if (isRedrawDividerHandle) { 320 | int adapterPosition = parent.getChildAdapterPosition(child); 321 | if (Math.min(mDividerIndex, itemCount - 1) == adapterPosition) { 322 | isRedrawDividerHandle = false; 323 | right = child.getLeft() - params.leftMargin; 324 | left = right - mRedrawDividerWidth; 325 | if (clipToPadding) { 326 | int paddingLeft = parent.getPaddingLeft(); 327 | if (paddingLeft > left) { 328 | left = paddingLeft; 329 | } 330 | if (paddingLeft > right) { 331 | continue; 332 | } 333 | } 334 | if (mDividerDrawable != null) { 335 | mDividerDrawable.setBounds(left, top, right, bottom); 336 | mDividerDrawable.draw(c); 337 | } else { 338 | mDivider.setBounds(left, top, right, bottom); 339 | mDivider.draw(c); 340 | } 341 | continue; 342 | } 343 | } 344 | } 345 | // 346 | // 347 | if (isSubDivider) {//分割线的截取 348 | if (isSubDividerHandle) { 349 | int adapterPosition = parent.getChildAdapterPosition(child); 350 | if (mStartIndex > itemCount - 1) { 351 | isSubDivider = false; 352 | } else { 353 | if (adapterPosition != 0 && adapterPosition >= mStartIndex) { 354 | Loge(TAG, "drawVertical: isSubDivider;" + adapterPosition); 355 | if (adapterPosition <= Math.min(mEndIndex - 1, itemCount - 1)) { 356 | if (adapterPosition == mEndIndex - 1) { 357 | isSubDividerHandle = false; 358 | } 359 | right = child.getLeft() - params.leftMargin; 360 | left = right - mSubDividerWidth; 361 | if (clipToPadding) { 362 | int paddingLeft = parent.getPaddingLeft(); 363 | if (paddingLeft > left) { 364 | left = paddingLeft; 365 | } 366 | if (paddingLeft > right) { 367 | continue; 368 | } 369 | } 370 | if (mSubDrawable != null) { 371 | mSubDrawable.setBounds(left, top, right, bottom); 372 | mSubDrawable.draw(c); 373 | } else { 374 | mDivider.setBounds(left, top, right, bottom); 375 | mDivider.draw(c); 376 | } 377 | continue; 378 | } else { 379 | isSubDividerHandle = false; 380 | } 381 | } 382 | } 383 | } 384 | } 385 | // 386 | right = child.getLeft() - params.leftMargin; 387 | left = right - mDividerWidth; 388 | if (clipToPadding) { 389 | int paddingLeft = parent.getPaddingLeft(); 390 | if (paddingLeft > left) { 391 | left = paddingLeft; 392 | } 393 | if (paddingLeft > right) { 394 | continue; 395 | } 396 | if (clipToPadding) {//兼容该属性的绘制方式 397 | int paddingRight = parent.getWidth() - parent.getPaddingRight(); 398 | if (left > paddingRight) { 399 | continue; 400 | } 401 | if (right > paddingRight) { 402 | right = paddingRight; 403 | } 404 | } 405 | } 406 | mDivider.setBounds(left, top, right, bottom); 407 | mDivider.draw(c); 408 | } 409 | } 410 | 411 | private boolean isClipToPadding(RecyclerView parent) { 412 | return parent.getClipToPadding(); 413 | } 414 | 415 | //绘制水平分割线 416 | @Override 417 | public void drawHorizontal(Canvas c, RecyclerView parent) { 418 | int childCount = parent.getChildCount();//可视item的个数 419 | RecyclerView.Adapter adapter = parent.getAdapter(); 420 | int itemCount = adapter.getItemCount();//item个数 421 | boolean clipToPadding = isClipToPadding(parent); 422 | int left = 0, top = 0, right = 0, bottom = 0; 423 | left = parent.getPaddingLeft() + mDividerPaddingLeft; 424 | right = parent.getWidth() - parent.getPaddingRight() - mDividerPaddingRight; 425 | boolean headerPosHandle = true, footerPosHandle = true, isSubDividerHandle = true, isRedrawDividerHandle = true; 426 | for (int i = 0; i < childCount; i++) { 427 | final View child = parent.getChildAt(i); 428 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 429 | .getLayoutParams(); 430 | // 431 | if (isSticky) { 432 | if (adapter instanceof OnHeaderListener) { 433 | int adapterPosition = parent.getChildAdapterPosition(child); 434 | OnHeaderListener listener = ((OnHeaderListener) adapter); 435 | String headerName = listener.getHeaderName(adapterPosition); 436 | if (!TextUtils.isEmpty(headerName)) { 437 | if (adapterPosition != 0 && !headerName.equals(listener.getHeaderName(adapterPosition - 1))) { 438 | bottom = child.getTop() - params.topMargin;//计算分割线的下边 439 | top = bottom - mStickyHeightOrWidth;//计算分割线的上边 440 | int stickyLeft = parent.getPaddingLeft(); 441 | int stickyRight = parent.getWidth() - parent.getPaddingRight(); 442 | if (clipToPadding) { 443 | int paddingTop = parent.getPaddingTop(); 444 | if (paddingTop > top) { 445 | top = paddingTop; 446 | } 447 | if (paddingTop > bottom) { 448 | continue; 449 | } 450 | } 451 | if (mStickyDrawable != null) { 452 | mStickyDrawable.setBounds(stickyLeft, top, stickyRight, bottom); 453 | mStickyDrawable.draw(c); 454 | } else { 455 | mDivider.setBounds(stickyLeft, top, stickyRight, bottom); 456 | mDivider.draw(c); 457 | } 458 | int x = stickyLeft + mStickyTextPaddingLeft; 459 | float baseline = top + mStickyHeightOrWidth / 2 + mOffsetY; 460 | c.drawText(headerName, x, baseline, mStickyTextPaint); 461 | continue; 462 | } 463 | } 464 | } 465 | } 466 | // 467 | // 468 | if (!noDrawHeaderDivider && !isSticky) {//顶部分割线处理 469 | if (i == 0) { 470 | if (headerPosHandle) { 471 | int adapterPosition = parent.getChildAdapterPosition(child); 472 | headerPosHandle = false; 473 | if (0 == adapterPosition) { 474 | Logd(TAG, "drawHorizontal: adapterPosition:" + adapterPosition); 475 | bottom = child.getTop() - params.topMargin; 476 | if (isRedrawHeaderDivider) {//顶部分割线定制 477 | top = bottom - mHeaderDividerHeight; 478 | if (clipToPadding) { 479 | int paddingTop = parent.getPaddingTop(); 480 | if (paddingTop > top) { 481 | top = paddingTop; 482 | } 483 | if (paddingTop > bottom) { 484 | continue; 485 | } 486 | } 487 | if (mHeaderDividerDrawable != null) { 488 | mHeaderDividerDrawable.setBounds(left, top, right, bottom); 489 | mHeaderDividerDrawable.draw(c); 490 | } else { 491 | mDivider.setBounds(left, top, right, bottom); 492 | mDivider.draw(c); 493 | } 494 | } else { 495 | top = bottom - mDividerHeight; 496 | if (clipToPadding) { 497 | int paddingTop = parent.getPaddingTop(); 498 | if (paddingTop > top) { 499 | top = paddingTop; 500 | } 501 | if (paddingTop > bottom) { 502 | continue; 503 | } 504 | } 505 | mDivider.setBounds(left, top, right, bottom); 506 | mDivider.draw(c); 507 | } 508 | } 509 | continue; 510 | } 511 | } 512 | } else { 513 | if (i == 0) { 514 | int adapterPosition = parent.getChildAdapterPosition(child); 515 | if (0 == adapterPosition) { 516 | continue; 517 | } 518 | } 519 | } 520 | // 521 | // 522 | if (!noDrawFooterDivider) { 523 | if (childCount - 1 == i) { 524 | if (footerPosHandle) { 525 | int adapterPosition = parent.getChildAdapterPosition(child); 526 | footerPosHandle = false; 527 | if (itemCount - 1 == adapterPosition) { 528 | top = child.getBottom() + params.bottomMargin; 529 | boolean isDraw = true; 530 | if (isRedrawFooterDivider) {//底部分割线定制 531 | bottom = top + mFooterDividerHeight; 532 | if (clipToPadding) {//兼容该属性的绘制方式 533 | int paddingBottom = parent.getHeight() - parent.getPaddingBottom(); 534 | if (top > paddingBottom) { 535 | isDraw = false; 536 | } else { 537 | if (bottom > paddingBottom) { 538 | bottom = paddingBottom; 539 | } 540 | } 541 | } 542 | if (isDraw) { 543 | if (mFooterDividerDrawable != null) { 544 | mFooterDividerDrawable.setBounds(left, top, right, bottom); 545 | mFooterDividerDrawable.draw(c); 546 | } else { 547 | mDivider.setBounds(left, top, right, bottom); 548 | mDivider.draw(c); 549 | } 550 | } 551 | } else { 552 | bottom = top + mDividerHeight; 553 | if (clipToPadding) { 554 | int paddingBottom = parent.getHeight() - parent.getPaddingBottom(); 555 | if (top > paddingBottom) { 556 | isDraw = false; 557 | } else { 558 | if (bottom > paddingBottom) { 559 | bottom = paddingBottom; 560 | } 561 | } 562 | } 563 | if (isDraw) { 564 | mDivider.setBounds(left, top, right, bottom); 565 | mDivider.draw(c); 566 | } 567 | } 568 | } 569 | } 570 | } 571 | } 572 | // 573 | // 574 | if (isRedrawDivider) {//分割线的定制 575 | if (isRedrawDividerHandle) { 576 | int adapterPosition = parent.getChildAdapterPosition(child); 577 | if (Math.min(mDividerIndex, itemCount - 1) == adapterPosition) { 578 | isRedrawDividerHandle = false; 579 | bottom = child.getTop() - params.topMargin; 580 | top = bottom - mRedrawDividerHeight; 581 | if (clipToPadding) { 582 | int paddingTop = parent.getPaddingTop(); 583 | if (paddingTop > top) { 584 | top = paddingTop; 585 | } 586 | if (paddingTop > bottom) { 587 | continue; 588 | } 589 | } 590 | if (mDividerDrawable != null) { 591 | mDividerDrawable.setBounds(left, top, right, bottom); 592 | mDividerDrawable.draw(c); 593 | } else { 594 | mDivider.setBounds(left, top, right, bottom); 595 | mDivider.draw(c); 596 | } 597 | continue; 598 | } 599 | } 600 | } 601 | // 602 | // 603 | if (isSubDivider) {//分割线的截取 604 | if (isSubDividerHandle) { 605 | int adapterPosition = parent.getChildAdapterPosition(child); 606 | if (mStartIndex > itemCount - 1) { 607 | isSubDivider = false; 608 | } else { 609 | if (adapterPosition != 0 && adapterPosition >= mStartIndex) { 610 | if (adapterPosition <= Math.min(mEndIndex - 1, itemCount - 1)) { 611 | Loge(TAG, "drawHorizontal: isSubDivider;" + adapterPosition); 612 | if (adapterPosition == mEndIndex - 1) { 613 | isSubDividerHandle = false; 614 | } 615 | bottom = child.getTop() - params.topMargin; 616 | top = bottom - mSubDividerHeight; 617 | if (clipToPadding) { 618 | int paddingTop = parent.getPaddingTop(); 619 | if (paddingTop > top) { 620 | top = paddingTop; 621 | } 622 | if (paddingTop > bottom) { 623 | continue; 624 | } 625 | } 626 | if (mSubDrawable != null) { 627 | mSubDrawable.setBounds(left, top, right, bottom); 628 | mSubDrawable.draw(c); 629 | } else { 630 | mDivider.setBounds(left, top, right, bottom); 631 | mDivider.draw(c); 632 | } 633 | continue; 634 | } else { 635 | isSubDividerHandle = false; 636 | } 637 | } 638 | } 639 | } 640 | } 641 | // 642 | bottom = child.getTop() - params.topMargin; 643 | top = bottom - mDividerHeight; 644 | if (clipToPadding) { 645 | int paddingTop = parent.getPaddingTop(); 646 | if (paddingTop > top) { 647 | top = paddingTop; 648 | } 649 | if (paddingTop > bottom) { 650 | continue; 651 | } 652 | int paddingBottom = parent.getHeight() - parent.getPaddingBottom(); 653 | if (top > paddingBottom) { 654 | continue; 655 | } 656 | if (bottom > paddingBottom) { 657 | bottom = paddingBottom; 658 | } 659 | } 660 | mDivider.setBounds(left, top, right, bottom); 661 | mDivider.draw(c); 662 | } 663 | } 664 | 665 | @Override 666 | public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { 667 | super.onDrawOver(c, parent, state); 668 | if (isSticky) { 669 | RecyclerView.Adapter adapter = parent.getAdapter(); 670 | if (adapter instanceof OnHeaderListener) { 671 | OnHeaderListener listener = ((OnHeaderListener) adapter); 672 | View child = parent.getChildAt(1);//得到第二个可视item 673 | int adapterPosition = parent.getChildAdapterPosition(child); 674 | adapterPosition = Math.max(0, adapterPosition); 675 | String headerName = listener.getHeaderName(adapterPosition); 676 | Loge(TAG, "onDrawOver: headerName:" + headerName + ",adapterPosition:" + adapterPosition); 677 | if (!TextUtils.isEmpty(headerName)) { 678 | boolean clipToPadding = isClipToPadding(parent); 679 | boolean flag = false; 680 | if (mOrientation == VERTICAL) { 681 | int stickyTop = !clipToPadding ? parent.getTop() : parent.getPaddingTop(); 682 | int stickyBottom = stickyTop + mStickyHeightOrWidth; 683 | int left = parent.getPaddingLeft(); 684 | int right = parent.getWidth() - parent.getPaddingRight(); 685 | if (!headerName.equals(listener.getHeaderName(Math.max(0, adapterPosition - 1)))) {//判断与上一个的头部文本是否不同 686 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 687 | .getLayoutParams(); 688 | int bottom = child.getTop() - params.topMargin;//分割线的底部 689 | int top = bottom - mStickyHeightOrWidth;//分割线的顶部 690 | Logd(TAG, "onDrawOver: stickyBottom:" + stickyBottom + ",stickyTop:" + stickyTop + ",top:" + top + ",bottom:" + bottom); 691 | if (stickyBottom >= top && top > stickyTop) {//分割线的顶部是否与悬浮的头部重叠 692 | bottom = top; 693 | top = top - mStickyHeightOrWidth; 694 | if (mStickyDrawable != null) { 695 | mStickyDrawable.setBounds(left, top, right, bottom); 696 | mStickyDrawable.draw(c); 697 | } else { 698 | mDivider.setBounds(left, top, right, bottom); 699 | mDivider.draw(c); 700 | } 701 | int x = left + mStickyTextPaddingLeft; 702 | float baseline = top + mStickyHeightOrWidth / 2 + mOffsetY; 703 | String lastHeaderName = listener.getHeaderName(Math.max(0, adapterPosition - 1)); 704 | if (!TextUtils.isEmpty(lastHeaderName)) {//得到上一个item的头部文本 705 | c.drawText(lastHeaderName, x, baseline, mStickyTextPaint); 706 | } 707 | return; 708 | } else { 709 | if (top <= stickyTop) { 710 | flag = true; 711 | } 712 | } 713 | } 714 | if (mStickyDrawable != null) { 715 | mStickyDrawable.setBounds(left, stickyTop, right, stickyBottom); 716 | mStickyDrawable.draw(c); 717 | } else { 718 | mDivider.setBounds(left, stickyTop, right, stickyBottom); 719 | mDivider.draw(c); 720 | } 721 | int x = left + mStickyTextPaddingLeft; 722 | float baseline = stickyTop + mStickyHeightOrWidth / 2 + mOffsetY; 723 | String lastHeaderName = listener.getHeaderName(flag ? adapterPosition : Math.max(0, adapterPosition - 1));//得到上一个item的头部文本 724 | if (!TextUtils.isEmpty(lastHeaderName)) { 725 | c.drawText(lastHeaderName, x, baseline, mStickyTextPaint); 726 | } 727 | } else { 728 | int top = parent.getPaddingTop(); 729 | int bottom = parent.getHeight() - parent.getPaddingBottom(); 730 | int stickyLeft = !clipToPadding ? parent.getLeft() : parent.getPaddingLeft(); 731 | int stickyRight = stickyLeft + mStickyHeightOrWidth; 732 | if (adapterPosition != 0 && !headerName.equals(listener.getHeaderName(Math.max(0, adapterPosition - 1)))) {//判断与上一个的头部文本是否不同 733 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 734 | .getLayoutParams(); 735 | int right = child.getLeft() - params.leftMargin; 736 | int left = right - mStickyHeightOrWidth; 737 | Logd(TAG, "onDrawOver: stickyRight:" + stickyRight + ",stickyLeft:" + stickyLeft + ",left:" + left + ",right:" + right); 738 | if (stickyRight >= left && left > stickyLeft) {//分割线的顶部是否与悬浮的头部重叠 739 | right = left; 740 | left = left - mStickyHeightOrWidth; 741 | if (mStickyDrawable != null) { 742 | mStickyDrawable.setBounds(left, top, right, bottom); 743 | mStickyDrawable.draw(c); 744 | } else { 745 | mDivider.setBounds(left, top, right, bottom); 746 | mDivider.draw(c); 747 | } 748 | int x = left + mStickyHeightOrWidth / 2; 749 | float baseline = top + mStickyTextPaddingLeft; 750 | String lastHeaderName = listener.getHeaderName(Math.max(0, adapterPosition - 1)); 751 | if (!TextUtils.isEmpty(lastHeaderName)) {//得到上一个item的头部文本' 752 | c.drawText(lastHeaderName, x, baseline, mStickyTextPaint); 753 | } 754 | return; 755 | } else { 756 | if (left <= stickyLeft) { 757 | flag = true; 758 | } 759 | } 760 | } 761 | if (mStickyDrawable != null) { 762 | mStickyDrawable.setBounds(stickyLeft, top, stickyRight, bottom); 763 | mStickyDrawable.draw(c); 764 | } else { 765 | mDivider.setBounds(stickyLeft, top, stickyRight, bottom); 766 | mDivider.draw(c); 767 | } 768 | int x = stickyLeft + mStickyHeightOrWidth / 2; 769 | float baseline = top + mStickyTextPaddingTop; 770 | String lastHeaderName = listener.getHeaderName(flag ? adapterPosition : Math.max(0, adapterPosition - 1));//得到上一个item的头部文本 771 | if (!TextUtils.isEmpty(lastHeaderName)) { 772 | int len = lastHeaderName.length(); 773 | if (len > 1) { 774 | char[] array = lastHeaderName.toCharArray(); 775 | int length = array.length; 776 | for (int i = 0; i < length; i++) { 777 | char c1 = array[i]; 778 | c.drawText(String.valueOf(c1), x, baseline, mStickyTextPaint); 779 | baseline += mTextHeight; 780 | } 781 | } else { 782 | c.drawText(lastHeaderName, x, baseline, mStickyTextPaint); 783 | } 784 | 785 | } 786 | } 787 | } 788 | } 789 | } 790 | } 791 | 792 | /** 793 | * 要想清楚outRect作用,请看{@link android.support.v7.widget.LinearLayoutManager}源码,如:measureChild(). 794 | */ 795 | @Override 796 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 797 | int itemPosition = parent.getChildAdapterPosition(view); 798 | Loge(TAG, "getItemOffsets: itemPosition:" + itemPosition); 799 | if (mOrientation == VERTICAL) { 800 | int top = mDividerHeight; 801 | int bottom = 0; 802 | // 803 | if (isSticky) {//悬浮头部 804 | RecyclerView.Adapter adapter = parent.getAdapter(); 805 | if (adapter instanceof OnHeaderListener) { 806 | OnHeaderListener listener = ((OnHeaderListener) adapter); 807 | String headerName = listener.getHeaderName(itemPosition); 808 | if (!TextUtils.isEmpty(headerName)) { 809 | if (itemPosition == 0 || !headerName.equals(listener.getHeaderName(itemPosition - 1))) { 810 | Logd(TAG, "getItemOffsets: headerName:" + headerName + ",itemPosition:" + itemPosition); 811 | top = mStickyHeightOrWidth; 812 | outRect.set(0, top, 0, bottom); 813 | return; 814 | } 815 | } 816 | } 817 | } 818 | // 819 | // 820 | if (noDrawHeaderDivider) { 821 | if (itemPosition == 0) { 822 | top = 0; 823 | } 824 | } else { 825 | if (itemPosition == 0) { 826 | if (isRedrawHeaderDivider) { 827 | top = mHeaderDividerHeight; 828 | } 829 | } 830 | } 831 | // 832 | // 833 | if (!noDrawFooterDivider) { 834 | int itemCount = parent.getAdapter().getItemCount(); 835 | if (itemPosition == itemCount - 1) { 836 | Logd(TAG, "getItemOffsets: mFooterDividerHeight" + mFooterDividerHeight); 837 | if (isRedrawFooterDivider) { 838 | bottom = mFooterDividerHeight; 839 | } else { 840 | bottom = mDividerHeight; 841 | } 842 | } 843 | } 844 | // 845 | // 846 | if (isSubDivider) { 847 | int itemCount = parent.getAdapter().getItemCount(); 848 | if (mStartIndex > itemCount - 1) { 849 | isSubDivider = false; 850 | } else { 851 | if (itemPosition != 0 && itemPosition >= mStartIndex && itemPosition <= Math.min(mEndIndex - 1, itemCount - 1)) { 852 | Logd(TAG, "getItemOffsets: mStartIndex:" + mStartIndex + ",mEndIndex:" + mEndIndex + ",itemPosition:" + itemPosition); 853 | top = mSubDividerHeight; 854 | } 855 | } 856 | } 857 | // 858 | // 859 | if (isRedrawDivider) { 860 | int itemCount = parent.getAdapter().getItemCount(); 861 | if (itemPosition != 0 && Math.min(mDividerIndex, itemCount - 1) == itemPosition) { 862 | top = mRedrawDividerHeight; 863 | } 864 | } 865 | // 866 | outRect.set(0, top, 0, bottom); 867 | } else { 868 | int left = mDividerWidth; 869 | int right = 0; 870 | // 871 | if (isSticky) {//悬浮左边 872 | RecyclerView.Adapter adapter = parent.getAdapter(); 873 | if (adapter instanceof OnHeaderListener) { 874 | OnHeaderListener listener = ((OnHeaderListener) adapter); 875 | String headerName = listener.getHeaderName(itemPosition); 876 | if (!TextUtils.isEmpty(headerName)) { 877 | if (itemPosition == 0 || !headerName.equals(listener.getHeaderName(itemPosition - 1))) { 878 | Logd(TAG, "getItemOffsets: headerName:" + headerName + ",itemPosition:" + itemPosition); 879 | left = mStickyHeightOrWidth; 880 | outRect.set(left, 0, right, 0); 881 | return; 882 | } 883 | } 884 | } 885 | } 886 | // 887 | // 888 | if (noDrawLeftDivider) { 889 | if (itemPosition == 0) { 890 | left = 0; 891 | } 892 | } else { 893 | if (itemPosition == 0) { 894 | if (isRedrawLeftDivider) { 895 | left = mLeftDividerWidth; 896 | } 897 | } 898 | } 899 | // 900 | // 901 | if (!noDrawRightDivider) { 902 | int itemCount = parent.getAdapter().getItemCount(); 903 | if (itemPosition == itemCount - 1) { 904 | if (isRedrawRightDivider) { 905 | right = mRightDividerWidth; 906 | } else { 907 | right = mDividerWidth; 908 | } 909 | } 910 | } 911 | // 912 | // 913 | if (isSubDivider) { 914 | int itemCount = parent.getAdapter().getItemCount(); 915 | if (mStartIndex > itemCount - 1) { 916 | isSubDivider = false; 917 | } else { 918 | if (itemPosition != 0 && itemPosition >= mStartIndex && itemPosition <= Math.min(mEndIndex - 1, itemCount - 1)) { 919 | Logd(TAG, "getItemOffsets: mStartIndex:" + mStartIndex + ",mEndIndex:" + mEndIndex + ",itemPosition:" + itemPosition); 920 | left = mSubDividerWidth; 921 | } 922 | } 923 | } 924 | // 925 | // 926 | if (isRedrawDivider) { 927 | int itemCount = parent.getAdapter().getItemCount(); 928 | if (itemPosition != 0 && Math.min(mDividerIndex, itemCount - 1) == itemPosition) { 929 | left = mRedrawDividerWidth; 930 | } 931 | } 932 | // 933 | outRect.set(left, 0, right, 0); 934 | } 935 | } 936 | 937 | public static class Builder extends BaseBuilder { 938 | private int mDividerPaddingLeft;//分割线左边距,仅适用于竖直方向 939 | private int mDividerPaddingTop;//分割线上边距,仅适用于水平方向 940 | private int mDividerPaddingRight;//分割线右边距,仅适用于竖直方向 941 | private int mDividerPaddingBottom;//分割线下边距,仅适用于水平方向 942 | private boolean isSticky;//固定头部 943 | private int mStickyHeightOrWidth;//固定头部高度或宽度 944 | private Drawable mStickyDrawable;//固定头部样式 945 | private int mStickyTextPaddingLeft = 48;//固定头部的文本左边距 946 | private int mStickyTextPaddingTop = 60;//固定头部的文本上边距 947 | private int mStickyTextColor = Color.WHITE;//固定头部的文本的字体颜色 948 | private int mStickyTextSize = 42;//固定头部的文本的字体大小 949 | 950 | public Builder(Context context) { 951 | super(context, LINEAR); 952 | } 953 | 954 | public Builder(Context context, int mOrientation) { 955 | super(context, LINEAR, mOrientation); 956 | } 957 | 958 | public Builder setDividerPaddingLeft(@IntRange(from = 0) int paddingLeft) { 959 | this.mDividerPaddingLeft = paddingLeft; 960 | return this; 961 | } 962 | 963 | public Builder setDividerPaddingRight(@IntRange(from = 0) int paddingRight) { 964 | this.mDividerPaddingRight = paddingRight; 965 | return this; 966 | } 967 | 968 | public Builder setDividerPaddingTop(@IntRange(from = 0) int paddingTop) { 969 | this.mDividerPaddingTop = paddingTop; 970 | return this; 971 | } 972 | 973 | public Builder setDividerPaddingBottom(@IntRange(from = 0) int paddingBottom) { 974 | this.mDividerPaddingBottom = paddingBottom; 975 | return this; 976 | } 977 | 978 | public Builder setDividerPadding(@IntRange(from = 0) int... padding) { 979 | for (int i = 0; i < padding.length; i++) { 980 | if (i == 0) { 981 | this.mDividerPaddingLeft = padding[i]; 982 | } else if (i == 1) { 983 | this.mDividerPaddingTop = padding[i]; 984 | } else if (i == 2) { 985 | this.mDividerPaddingRight = padding[i]; 986 | } else if (i == 3) { 987 | this.mDividerPaddingBottom = padding[i]; 988 | break; 989 | } 990 | } 991 | return this; 992 | } 993 | 994 | public Builder setSticky(boolean sticky) { 995 | isSticky = sticky; 996 | return this; 997 | } 998 | 999 | public Builder setStickyHeightOrWidth(@IntRange(from = 0) int stickyHeaderHeight) { 1000 | this.mStickyHeightOrWidth = stickyHeaderHeight; 1001 | return this; 1002 | } 1003 | 1004 | public Builder setStickyDrawable(@DrawableRes int drawableId) { 1005 | this.mStickyDrawable = ContextCompat.getDrawable(mContext.getApplicationContext(), drawableId); 1006 | ; 1007 | if (this.mStickyHeightOrWidth == 0) { 1008 | this.mStickyHeightOrWidth = mStickyDrawable.getIntrinsicHeight(); 1009 | } 1010 | return this; 1011 | } 1012 | 1013 | public Builder setStickyTextPaddingLeft(int textPaddingLeft) { 1014 | this.mStickyTextPaddingLeft = textPaddingLeft; 1015 | return this; 1016 | } 1017 | 1018 | public Builder setStickyTextPaddingTop(int textPaddingTop) { 1019 | this.mStickyTextPaddingTop = textPaddingTop; 1020 | return this; 1021 | } 1022 | 1023 | /** 1024 | * @param textColor 颜色需为argb,否则不生效 1025 | * @return 1026 | */ 1027 | public Builder setStickyTextColor(int textColor) { 1028 | this.mStickyTextColor = textColor; 1029 | return this; 1030 | } 1031 | 1032 | public Builder setStickyTextSize(int textSize) { 1033 | this.mStickyTextSize = textSize; 1034 | return this; 1035 | } 1036 | 1037 | public DividerLinearItemDecoration build() { 1038 | return new DividerLinearItemDecoration(this); 1039 | } 1040 | } 1041 | } 1042 | -------------------------------------------------------------------------------- /decoration/src/main/java/com/ckr/decoration/OnHeaderListener.java: -------------------------------------------------------------------------------- 1 | package com.ckr.decoration; 2 | 3 | /** 4 | * Created by PC大佬 on 2018/6/8. 5 | */ 6 | 7 | public interface OnHeaderListener { 8 | String getHeaderName(int position); 9 | } 10 | -------------------------------------------------------------------------------- /decoration/src/main/res/drawable/bg_decoration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | -------------------------------------------------------------------------------- /decoration/src/main/res/layout/item_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /decoration/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Decoration 3 | 4 | -------------------------------------------------------------------------------- /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 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /screenshot/Screenshot_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/screenshot/Screenshot_1.png -------------------------------------------------------------------------------- /screenshot/Screenshot_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/screenshot/Screenshot_10.png -------------------------------------------------------------------------------- /screenshot/Screenshot_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/screenshot/Screenshot_2.png -------------------------------------------------------------------------------- /screenshot/Screenshot_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/screenshot/Screenshot_3.png -------------------------------------------------------------------------------- /screenshot/Screenshot_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/screenshot/Screenshot_4.png -------------------------------------------------------------------------------- /screenshot/Screenshot_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/screenshot/Screenshot_5.png -------------------------------------------------------------------------------- /screenshot/Screenshot_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/screenshot/Screenshot_6.png -------------------------------------------------------------------------------- /screenshot/Screenshot_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/screenshot/Screenshot_7.png -------------------------------------------------------------------------------- /screenshot/Screenshot_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/screenshot/Screenshot_8.png -------------------------------------------------------------------------------- /screenshot/Screenshot_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ckrgithub/FlexItemDecoration/0a0b4baf17f619a6bd3e8e116585464e2071f68b/screenshot/Screenshot_9.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':decoration' 2 | --------------------------------------------------------------------------------