├── .gitignore
├── LICENSE
├── PagerLayoutManager
├── .gitignore
├── .idea
│ ├── compiler.xml
│ ├── copyright
│ │ ├── GcsCopyright.xml
│ │ ├── mchang.xml
│ │ └── profiles_settings.xml
│ ├── gradle.xml
│ ├── misc.xml
│ ├── modules.xml
│ └── runConfigurations.xml
├── app
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── gcssloop
│ │ │ └── pagelayoutmanager
│ │ │ ├── MainActivity.java
│ │ │ └── MyAdapter.java
│ │ └── res
│ │ ├── drawable-xhdpi
│ │ └── pic.jpg
│ │ ├── drawable-xxxhdpi
│ │ └── ic_launcher.jpg
│ │ ├── drawable
│ │ ├── bg_button.xml
│ │ ├── bg_item.xml
│ │ └── divider_bg.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ └── layout_item.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── pagerlayoutmanager
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── gcssloop
│ │ └── widget
│ │ ├── PagerConfig.java
│ │ ├── PagerGridLayoutManager.java
│ │ ├── PagerGridSmoothScroller.java
│ │ └── PagerGridSnapHelper.java
└── settings.gradle
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Android Studio Navigation editor temp files
30 | .navigation/
31 |
32 | # Android Studio captures folder
33 | captures/
34 |
35 | # Intellij
36 | *.iml
37 | .idea/workspace.xml
38 | .idea/tasks.xml
39 | .idea/gradle.xml
40 | .idea/dictionaries
41 | .idea/libraries
42 |
43 | # Keystore files
44 | *.jks
45 |
46 | # External native build folder generated in Android Studio 2.2 and later
47 | .externalNativeBuild
48 |
49 | # Google Services (e.g. APIs or Firebase)
50 | google-services.json
51 |
52 | # Freeline
53 | freeline.py
54 | freeline/
55 | freeline_project_description.json
56 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/PagerLayoutManager/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Android Studio Navigation editor temp files
30 | .navigation/
31 |
32 | # Android Studio captures folder
33 | captures/
34 |
35 | # Intellij
36 | *.iml
37 | .idea/*
38 | .idea/workspace.xml
39 | .idea/tasks.xml
40 | .idea/gradle.xml
41 | .idea/dictionaries
42 | .idea/libraries
43 | .idea/*
44 |
45 | # Keystore files
46 | *.jks
47 |
48 | # External native build folder generated in Android Studio 2.2 and later
49 | .externalNativeBuild
50 |
51 | # Google Services (e.g. APIs or Firebase)
52 | google-services.json
53 |
54 | # Freeline
55 | freeline.py
56 | freeline/
57 | freeline_project_description.json
--------------------------------------------------------------------------------
/PagerLayoutManager/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/PagerLayoutManager/.idea/copyright/GcsCopyright.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/PagerLayoutManager/.idea/copyright/mchang.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/PagerLayoutManager/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/PagerLayoutManager/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/PagerLayoutManager/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | Android
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | GcsCopyright
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/PagerLayoutManager/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/PagerLayoutManager/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/PagerLayoutManager/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/PagerLayoutManager/app/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 GcsSloop
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * Last modified 2017-09-18 23:40:20
17 | *
18 | * GitHub: https://github.com/GcsSloop
19 | * WeiBo: http://weibo.com/GcsSloop
20 | * WebSite: http://www.gcssloop.com
21 | */
22 |
23 | apply plugin: 'com.android.application'
24 |
25 | android {
26 | compileSdkVersion 25
27 | buildToolsVersion "25.0.3"
28 | defaultConfig {
29 | applicationId "com.gcssloop.pagelayoutmanager"
30 | minSdkVersion 14
31 | targetSdkVersion 25
32 | versionCode 1
33 | versionName "1.0"
34 | }
35 | }
36 |
37 | dependencies {
38 | compile fileTree(include: ['*.jar'], dir: 'libs')
39 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
40 | exclude group: 'com.android.support', module: 'support-annotations'
41 | })
42 | compile 'com.android.support:appcompat-v7:25.3.1'
43 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
44 | testCompile 'junit:junit:4.12'
45 | compile project(':pagerlayoutmanager')
46 | }
47 |
--------------------------------------------------------------------------------
/PagerLayoutManager/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/GcsSloop/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/PagerLayoutManager/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
26 |
27 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/PagerLayoutManager/app/src/main/java/com/gcssloop/pagelayoutmanager/MainActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 GcsSloop
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * Last modified 2017-09-18 23:47:01
17 | *
18 | * GitHub: https://github.com/GcsSloop
19 | * WeiBo: http://weibo.com/GcsSloop
20 | * WebSite: http://www.gcssloop.com
21 | */
22 |
23 | package com.gcssloop.pagelayoutmanager;
24 |
25 | import android.annotation.SuppressLint;
26 | import android.os.Bundle;
27 | import android.support.annotation.IdRes;
28 | import android.support.v7.app.AppCompatActivity;
29 | import android.support.v7.widget.RecyclerView;
30 | import android.util.Log;
31 | import android.view.View;
32 | import android.widget.RadioGroup;
33 | import android.widget.TextView;
34 |
35 | import com.gcssloop.widget.PagerConfig;
36 | import com.gcssloop.widget.PagerGridLayoutManager;
37 | import com.gcssloop.widget.PagerGridSnapHelper;
38 |
39 | import java.util.ArrayList;
40 | import java.util.List;
41 |
42 | public class MainActivity extends AppCompatActivity implements PagerGridLayoutManager
43 | .PageListener, RadioGroup.OnCheckedChangeListener {
44 |
45 | private int mRows = 2;
46 | private int mColumns = 3;
47 | private RecyclerView mRecyclerView;
48 | private MyAdapter mAdapter;
49 | private PagerGridLayoutManager mLayoutManager;
50 | private RadioGroup mRadioGroup;
51 | private TextView mPageTotal; // 总页数
52 | private TextView mPageCurrent; // 当前页数
53 |
54 | private int mTotal = 0;
55 | private int mCurrent = 0;
56 |
57 | @Override
58 | protected void onCreate(Bundle savedInstanceState) {
59 | super.onCreate(savedInstanceState);
60 | setContentView(R.layout.activity_main);
61 | Log.i("GCS", "onCreate");
62 |
63 | mRadioGroup = (RadioGroup) findViewById(R.id.orientation_type);
64 | mRadioGroup.setOnCheckedChangeListener(this);
65 |
66 | mPageTotal = (TextView) findViewById(R.id.page_total);
67 | mPageCurrent = (TextView) findViewById(R.id.page_current);
68 |
69 | mLayoutManager = new PagerGridLayoutManager(mRows, mColumns, PagerGridLayoutManager
70 | .HORIZONTAL);
71 |
72 |
73 | // 系统带的 RecyclerView,无需自定义
74 | mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
75 |
76 | // 水平分页布局管理器
77 | mLayoutManager.setPageListener(this); // 设置页面变化监听器
78 | mRecyclerView.setLayoutManager(mLayoutManager);
79 |
80 | // 设置滚动辅助工具
81 | PagerGridSnapHelper pageSnapHelper = new PagerGridSnapHelper();
82 | pageSnapHelper.attachToRecyclerView(mRecyclerView);
83 |
84 | // 如果需要查看调试日志可以设置为true,一般情况忽略即可
85 | PagerConfig.setShowLog(true);
86 |
87 | // 使用原生的 Adapter 即可
88 | mAdapter = new MyAdapter();
89 | mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
90 | @Override public void onChanged() {
91 | super.onChanged();
92 | int count = mAdapter.getItemCount();
93 |
94 | }
95 | });
96 | mRecyclerView.setAdapter(mAdapter);
97 | }
98 |
99 | @Override public void onPageSizeChanged(int pageSize) {
100 | mTotal = pageSize;
101 | Log.e("TAG", "总页数 = " + pageSize);
102 | mPageTotal.setText("共 " + pageSize + " 页");
103 | }
104 |
105 | @Override public void onPageSelect(int pageIndex) {
106 | mCurrent = pageIndex;
107 | Log.e("TAG", "选中页码 = " + pageIndex);
108 | mPageCurrent.setText("第 " + (pageIndex + 1) + " 页");
109 | }
110 |
111 | public void addOne(View view) {
112 | mAdapter.data.add(0, "add");
113 | mAdapter.notifyDataSetChanged();
114 | }
115 |
116 | public void removeOne(View view) {
117 | if (mAdapter.data.size() > 0) {
118 | mAdapter.data.remove(0);
119 | mAdapter.notifyDataSetChanged();
120 | }
121 | }
122 |
123 | public void addMore(View view) {
124 | List data = new ArrayList<>();
125 | for (int i = 1; i <= 5; i++) {
126 | data.add(i + "a");
127 | }
128 | mAdapter.data.addAll(data);
129 | mAdapter.notifyDataSetChanged();
130 | }
131 |
132 | @SuppressLint("WrongConstant")
133 | @Override public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
134 | int type = -1;
135 | if (checkedId == R.id.type_horizontal) {
136 | type = mLayoutManager.setOrientationType(PagerGridLayoutManager.HORIZONTAL);
137 | } else if (checkedId == R.id.type_vertical) {
138 | type = mLayoutManager.setOrientationType(PagerGridLayoutManager.VERTICAL);
139 | } else {
140 | throw new RuntimeException("不支持的方向类型");
141 | }
142 |
143 | Log.i("GCST", "type == " + type);
144 | }
145 |
146 | public void prePage(View view) {
147 | mLayoutManager.prePage();
148 | }
149 |
150 | public void nextPage(View view) {
151 | mLayoutManager.nextPage();
152 | }
153 |
154 | public void smoothPrePage(View view) {
155 | mLayoutManager.smoothPrePage();
156 | }
157 |
158 | public void smoothNextPage(View view) {
159 | mLayoutManager.smoothNextPage();
160 | }
161 |
162 | public void firstPage(View view) {
163 | mRecyclerView.smoothScrollToPosition(0);
164 | }
165 |
166 | public void lastPage(View view) {
167 | mRecyclerView.smoothScrollToPosition(mAdapter.getItemCount()-1);
168 | }
169 |
170 | }
171 |
--------------------------------------------------------------------------------
/PagerLayoutManager/app/src/main/java/com/gcssloop/pagelayoutmanager/MyAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 GcsSloop
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * Last modified 2017-09-18 23:47:01
17 | *
18 | * GitHub: https://github.com/GcsSloop
19 | * WeiBo: http://weibo.com/GcsSloop
20 | * WebSite: http://www.gcssloop.com
21 | */
22 |
23 | package com.gcssloop.pagelayoutmanager;
24 |
25 | import android.support.v7.widget.RecyclerView;
26 | import android.view.LayoutInflater;
27 | import android.view.View;
28 | import android.view.ViewGroup;
29 | import android.widget.TextView;
30 | import android.widget.Toast;
31 |
32 | import java.util.ArrayList;
33 | import java.util.List;
34 |
35 |
36 | public class MyAdapter extends RecyclerView.Adapter {
37 |
38 | public static List data = new ArrayList<>();
39 |
40 | static {
41 | for (int i = 1; i <= 15; i++) {
42 | data.add(i + "");
43 | }
44 | }
45 |
46 |
47 | @Override
48 | public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
49 | // Log.i("GCS", "onCreateViewHolder");
50 | LayoutInflater inflater = LayoutInflater.from(parent.getContext());
51 | View view = inflater.inflate(R.layout.layout_item, parent, false);
52 | return new MyViewHolder(view);
53 | }
54 |
55 | @Override
56 | public void onBindViewHolder(final MyViewHolder holder, final int position) {
57 | // Log.i("GCS","onBindViewHolder = "+position);
58 | final String title = data.get(position);
59 | holder.tv_title.setText(title);
60 | holder.itemView.setOnClickListener(new View.OnClickListener() {
61 | @Override
62 | public void onClick(View v) {
63 | Toast.makeText(v.getContext(), "item" + title + " 被点击了", Toast.LENGTH_SHORT).show();
64 | holder.tv_title.setText("G "+title);
65 | notifyItemChanged(position);
66 | }
67 | });
68 | }
69 |
70 | @Override
71 | public int getItemCount() {
72 | return data.size();
73 | }
74 |
75 | class MyViewHolder extends RecyclerView.ViewHolder {
76 | TextView tv_title;
77 |
78 | public MyViewHolder(View itemView) {
79 | super(itemView);
80 | tv_title = (TextView) itemView.findViewById(R.id.tv_title);
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/PagerLayoutManager/app/src/main/res/drawable-xhdpi/pic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GcsSloop/pager-layoutmanager/06654ad01dc0839929515171efde32d10a91fb6d/PagerLayoutManager/app/src/main/res/drawable-xhdpi/pic.jpg
--------------------------------------------------------------------------------
/PagerLayoutManager/app/src/main/res/drawable-xxxhdpi/ic_launcher.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GcsSloop/pager-layoutmanager/06654ad01dc0839929515171efde32d10a91fb6d/PagerLayoutManager/app/src/main/res/drawable-xxxhdpi/ic_launcher.jpg
--------------------------------------------------------------------------------
/PagerLayoutManager/app/src/main/res/drawable/bg_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
25 | -
26 |
27 |
31 |
32 |
33 |
34 |
35 | -
36 |
37 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/PagerLayoutManager/app/src/main/res/drawable/bg_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
9 |
10 |
11 | -
12 |
13 |
14 |
17 |
18 |
19 | -
20 |
21 |
22 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/PagerLayoutManager/app/src/main/res/drawable/divider_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
--------------------------------------------------------------------------------
/PagerLayoutManager/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
22 |
23 |
31 |
32 |
36 |
37 |
42 |
43 |
44 |
45 |
49 |
50 |
54 |
55 |
64 |
65 |
69 |
70 |
79 |
80 |
81 |
85 |
86 |
90 |
91 |
95 |
96 |
104 |
105 |
112 |
113 |
120 |
121 |
128 |
129 |
130 |
131 |
135 |
136 |
140 |
141 |
148 |
149 |
156 |
157 |
158 |
164 |
165 |
169 |
170 |
177 |
178 |
185 |
186 |
187 |
191 |
192 |
199 |
200 |
207 |
208 |
209 |
213 |
214 |
221 |
222 |
229 |
230 |
231 |
232 |
--------------------------------------------------------------------------------
/PagerLayoutManager/app/src/main/res/layout/layout_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
30 |
31 |
37 |
38 |
46 |
--------------------------------------------------------------------------------
/PagerLayoutManager/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #376A81
6 |
7 |
--------------------------------------------------------------------------------
/PagerLayoutManager/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PagerLayoutManager
3 |
4 |
--------------------------------------------------------------------------------
/PagerLayoutManager/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/PagerLayoutManager/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.3.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/PagerLayoutManager/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 |
--------------------------------------------------------------------------------
/PagerLayoutManager/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GcsSloop/pager-layoutmanager/06654ad01dc0839929515171efde32d10a91fb6d/PagerLayoutManager/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/PagerLayoutManager/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Sep 18 23:36:55 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/PagerLayoutManager/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 |
--------------------------------------------------------------------------------
/PagerLayoutManager/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 |
--------------------------------------------------------------------------------
/PagerLayoutManager/pagerlayoutmanager/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/PagerLayoutManager/pagerlayoutmanager/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 GcsSloop
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * Last modified 2017-09-18 23:38:56
17 | *
18 | * GitHub: https://github.com/GcsSloop
19 | * WeiBo: http://weibo.com/GcsSloop
20 | * WebSite: http://www.gcssloop.com
21 | */
22 |
23 | apply plugin: 'com.android.library'
24 |
25 | android {
26 | compileSdkVersion 25
27 | buildToolsVersion "25.0.3"
28 |
29 | defaultConfig {
30 | minSdkVersion 7
31 | targetSdkVersion 25
32 | versionCode 1
33 | versionName "1.0"
34 | }
35 | }
36 |
37 | dependencies {
38 | compile 'com.android.support:recyclerview-v7:25.3.1'
39 | }
40 |
41 | // 上传信息
42 | ext {
43 | GROUP_ID = "com.gcssloop.recyclerview"
44 | ARTIFACT_ID = "pagerlayoutmanager"
45 | VERSION = "2.3.8"
46 | }
47 |
48 | apply from: "/Applications/nexus/pack/pack-upload.gradle"
--------------------------------------------------------------------------------
/PagerLayoutManager/pagerlayoutmanager/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/GcsSloop/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/PagerLayoutManager/pagerlayoutmanager/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/PagerLayoutManager/pagerlayoutmanager/src/main/java/com/gcssloop/widget/PagerConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 GcsSloop
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * Last modified 2017-09-20 16:32:43
17 | *
18 | * GitHub: https://github.com/GcsSloop
19 | * WeiBo: http://weibo.com/GcsSloop
20 | * WebSite: http://www.gcssloop.com
21 | */
22 |
23 | package com.gcssloop.widget;
24 |
25 | import android.util.Log;
26 |
27 | /**
28 | * 作用:Pager配置
29 | * 作者:GcsSloop
30 | * 摘要:主要用于Log的显示与关闭
31 | */
32 | public class PagerConfig {
33 | private static final String TAG = "PagerGrid";
34 | private static boolean sShowLog = false;
35 | private static int sFlingThreshold = 1000; // Fling 阀值,滚动速度超过该阀值才会触发滚动
36 | private static float sMillisecondsPreInch = 60f; // 每一个英寸滚动需要的微秒数,数值越大,速度越慢
37 |
38 | /**
39 | * 判断是否输出日志
40 | *
41 | * @return true 输出,false 不输出
42 | */
43 | public static boolean isShowLog() {
44 | return sShowLog;
45 | }
46 |
47 | /**
48 | * 设置是否输出日志
49 | *
50 | * @param showLog 是否输出
51 | */
52 | public static void setShowLog(boolean showLog) {
53 | sShowLog = showLog;
54 | }
55 |
56 | /**
57 | * 获取当前滚动速度阀值
58 | *
59 | * @return 当前滚动速度阀值
60 | */
61 | public static int getFlingThreshold() {
62 | return sFlingThreshold;
63 | }
64 |
65 | /**
66 | * 设置当前滚动速度阀值
67 | *
68 | * @param flingThreshold 滚动速度阀值
69 | */
70 | public static void setFlingThreshold(int flingThreshold) {
71 | sFlingThreshold = flingThreshold;
72 | }
73 |
74 | /**
75 | * 获取滚动速度 英寸/微秒
76 | *
77 | * @return 英寸滚动速度
78 | */
79 | public static float getMillisecondsPreInch() {
80 | return sMillisecondsPreInch;
81 | }
82 |
83 | /**
84 | * 设置像素滚动速度 英寸/微秒
85 | *
86 | * @param millisecondsPreInch 英寸滚动速度
87 | */
88 | public static void setMillisecondsPreInch(float millisecondsPreInch) {
89 | sMillisecondsPreInch = millisecondsPreInch;
90 | }
91 |
92 | //--- 日志 -------------------------------------------------------------------------------------
93 |
94 | public static void Logi(String msg) {
95 | if (!PagerConfig.isShowLog()) return;
96 | Log.i(TAG, msg);
97 | }
98 |
99 | public static void Loge(String msg) {
100 | if (!PagerConfig.isShowLog()) return;
101 | Log.e(TAG, msg);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/PagerLayoutManager/pagerlayoutmanager/src/main/java/com/gcssloop/widget/PagerGridLayoutManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 GcsSloop
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * Last modified 2017-09-20 16:32:43
17 | *
18 | * GitHub: https://github.com/GcsSloop
19 | * WeiBo: http://weibo.com/GcsSloop
20 | * WebSite: http://www.gcssloop.com
21 | */
22 |
23 | package com.gcssloop.widget;
24 |
25 | import android.annotation.SuppressLint;
26 | import android.graphics.PointF;
27 | import android.graphics.Rect;
28 | import android.support.annotation.IntDef;
29 | import android.support.annotation.IntRange;
30 | import android.support.v7.widget.LinearSmoothScroller;
31 | import android.support.v7.widget.RecyclerView;
32 | import android.util.Log;
33 | import android.util.SparseArray;
34 | import android.view.View;
35 | import android.view.ViewGroup;
36 |
37 | import static android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE;
38 | import static android.view.View.MeasureSpec.EXACTLY;
39 | import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
40 | import static com.gcssloop.widget.PagerConfig.Loge;
41 | import static com.gcssloop.widget.PagerConfig.Logi;
42 |
43 |
44 | /**
45 | * 作用:分页的网格布局管理器
46 | * 作者:GcsSloop
47 | * 摘要:
48 | * 1. 网格布局
49 | * 2. 支持水平分页和垂直分页
50 | * 3. 杜绝高内存占用
51 | */
52 | public class PagerGridLayoutManager extends RecyclerView.LayoutManager
53 | implements RecyclerView.SmoothScroller.ScrollVectorProvider {
54 | private static final String TAG = PagerGridLayoutManager.class.getSimpleName();
55 |
56 | public static final int VERTICAL = 0; // 垂直滚动
57 | public static final int HORIZONTAL = 1; // 水平滚动
58 |
59 | @IntDef({VERTICAL, HORIZONTAL})
60 | public @interface OrientationType {} // 滚动类型
61 |
62 | @OrientationType
63 | private int mOrientation; // 默认水平滚动
64 |
65 | private int mOffsetX = 0; // 水平滚动距离(偏移量)
66 | private int mOffsetY = 0; // 垂直滚动距离(偏移量)
67 |
68 | private int mRows; // 行数
69 | private int mColumns; // 列数
70 | private int mOnePageSize; // 一页的条目数量
71 |
72 | private SparseArray mItemFrames; // 条目的显示区域
73 |
74 | private int mItemWidth = 0; // 条目宽度
75 | private int mItemHeight = 0; // 条目高度
76 |
77 | private int mWidthUsed = 0; // 已经使用空间,用于测量View
78 | private int mHeightUsed = 0; // 已经使用空间,用于测量View
79 |
80 | private int mMaxScrollX; // 最大允许滑动的宽度
81 | private int mMaxScrollY; // 最大允许滑动的高度
82 | private int mScrollState = SCROLL_STATE_IDLE; // 滚动状态
83 |
84 | private boolean mAllowContinuousScroll = true; // 是否允许连续滚动
85 |
86 | private RecyclerView mRecyclerView;
87 |
88 | /**
89 | * 构造函数
90 | *
91 | * @param rows 行数
92 | * @param columns 列数
93 | * @param orientation 方向
94 | */
95 | public PagerGridLayoutManager(@IntRange(from = 1, to = 100) int rows,
96 | @IntRange(from = 1, to = 100) int columns,
97 | @OrientationType int orientation) {
98 | mItemFrames = new SparseArray<>();
99 | mOrientation = orientation;
100 | mRows = rows;
101 | mColumns = columns;
102 | mOnePageSize = mRows * mColumns;
103 | }
104 |
105 | @Override
106 | public void onAttachedToWindow(RecyclerView view) {
107 | super.onAttachedToWindow(view);
108 | mRecyclerView = view;
109 | }
110 |
111 | //--- 处理布局 ----------------------------------------------------------------------------------
112 |
113 | /**
114 | * 布局子View
115 | *
116 | * @param recycler Recycler
117 | * @param state State
118 | */
119 | @Override
120 | public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
121 | Logi("Item onLayoutChildren");
122 | Logi("Item onLayoutChildren isPreLayout = " + state.isPreLayout());
123 | Logi("Item onLayoutChildren isMeasuring = " + state.isMeasuring());
124 | Loge("Item onLayoutChildren state = " + state);
125 |
126 | // 如果是 preLayout 则不重新布局
127 | if (state.isPreLayout() || !state.didStructureChange()) {
128 | return;
129 | }
130 |
131 | if (getItemCount() == 0) {
132 | removeAndRecycleAllViews(recycler);
133 | // 页面变化回调
134 | setPageCount(0);
135 | setPageIndex(0, false);
136 | return;
137 | } else {
138 | setPageCount(getTotalPageCount());
139 | setPageIndex(getPageIndexByOffset(), false);
140 | }
141 |
142 | // 计算页面数量
143 | int mPageCount = getItemCount() / mOnePageSize;
144 | if (getItemCount() % mOnePageSize != 0) {
145 | mPageCount++;
146 | }
147 |
148 | // 计算可以滚动的最大数值,并对滚动距离进行修正
149 | if (canScrollHorizontally()) {
150 | mMaxScrollX = (mPageCount - 1) * getUsableWidth();
151 | mMaxScrollY = 0;
152 | if (mOffsetX > mMaxScrollX) {
153 | mOffsetX = mMaxScrollX;
154 | }
155 | } else {
156 | mMaxScrollX = 0;
157 | mMaxScrollY = (mPageCount - 1) * getUsableHeight();
158 | if (mOffsetY > mMaxScrollY) {
159 | mOffsetY = mMaxScrollY;
160 | }
161 | }
162 |
163 | // 接口回调
164 | // setPageCount(mPageCount);
165 | // setPageIndex(mCurrentPageIndex, false);
166 |
167 | Logi("count = " + getItemCount());
168 |
169 | if (mItemWidth <= 0) {
170 | mItemWidth = getUsableWidth() / mColumns;
171 | }
172 | if (mItemHeight <= 0) {
173 | mItemHeight = getUsableHeight() / mRows;
174 | }
175 |
176 | mWidthUsed = getUsableWidth() - mItemWidth;
177 | mHeightUsed = getUsableHeight() - mItemHeight;
178 |
179 | // 预存储两页的View显示区域
180 | for (int i = 0; i < mOnePageSize * 2; i++) {
181 | getItemFrameByPosition(i);
182 | }
183 |
184 | if (mOffsetX == 0 && mOffsetY == 0) {
185 | // 预存储View
186 | for (int i = 0; i < mOnePageSize; i++) {
187 | if (i >= getItemCount()) break; // 防止数据过少时导致数组越界异常
188 | View view = recycler.getViewForPosition(i);
189 | addView(view);
190 | measureChildWithMargins(view, mWidthUsed, mHeightUsed);
191 | }
192 | }
193 |
194 | // 回收和填充布局
195 | recycleAndFillItems(recycler, state, true);
196 | }
197 |
198 | /**
199 | * 布局结束
200 | *
201 | * @param state State
202 | */
203 | @Override
204 | public void onLayoutCompleted(RecyclerView.State state) {
205 | super.onLayoutCompleted(state);
206 | if (state.isPreLayout()) return;
207 | // 页面状态回调
208 | setPageCount(getTotalPageCount());
209 | setPageIndex(getPageIndexByOffset(), false);
210 | }
211 |
212 | /**
213 | * 回收和填充布局
214 | *
215 | * @param recycler Recycler
216 | * @param state State
217 | * @param isStart 是否从头开始,用于控制View遍历方向,true 为从头到尾,false 为从尾到头
218 | */
219 | @SuppressLint("CheckResult")
220 | private void recycleAndFillItems(RecyclerView.Recycler recycler, RecyclerView.State state,
221 | boolean isStart) {
222 | if (state.isPreLayout()) {
223 | return;
224 | }
225 |
226 | Logi("mOffsetX = " + mOffsetX);
227 | Logi("mOffsetY = " + mOffsetY);
228 |
229 | // 计算显示区域区前后多存储一列或则一行
230 | Rect displayRect = new Rect(mOffsetX - mItemWidth, mOffsetY - mItemHeight,
231 | getUsableWidth() + mOffsetX + mItemWidth, getUsableHeight() + mOffsetY + mItemHeight);
232 | // 对显显示区域进行修正(计算当前显示区域和最大显示区域对交集)
233 | displayRect.intersect(0, 0, mMaxScrollX + getUsableWidth(), mMaxScrollY + getUsableHeight());
234 | Loge("displayRect = " + displayRect.toString());
235 |
236 | int startPos = 0; // 获取第一个条目的Pos
237 | int pageIndex = getPageIndexByOffset();
238 | startPos = pageIndex * mOnePageSize;
239 | Logi("startPos = " + startPos);
240 | startPos = startPos - mOnePageSize * 2;
241 | if (startPos < 0) {
242 | startPos = 0;
243 | }
244 | int stopPos = startPos + mOnePageSize * 4;
245 | if (stopPos > getItemCount()) {
246 | stopPos = getItemCount();
247 | }
248 |
249 | Loge("startPos = " + startPos);
250 | Loge("stopPos = " + stopPos);
251 |
252 | detachAndScrapAttachedViews(recycler); // 移除所有View
253 |
254 | if (isStart) {
255 | for (int i = startPos; i < stopPos; i++) {
256 | addOrRemove(recycler, displayRect, i);
257 | }
258 | } else {
259 | for (int i = stopPos - 1; i >= startPos; i--) {
260 | addOrRemove(recycler, displayRect, i);
261 | }
262 | }
263 | Loge("child count = " + getChildCount());
264 | }
265 |
266 | /**
267 | * 添加或者移除条目
268 | *
269 | * @param recycler RecyclerView
270 | * @param displayRect 显示区域
271 | * @param i 条目下标
272 | */
273 | private void addOrRemove(RecyclerView.Recycler recycler, Rect displayRect, int i) {
274 | View child = recycler.getViewForPosition(i);
275 | Rect rect = getItemFrameByPosition(i);
276 | if (!Rect.intersects(displayRect, rect)) {
277 | removeAndRecycleView(child, recycler); // 回收入暂存区
278 | } else {
279 | addView(child);
280 | measureChildWithMargins(child, mWidthUsed, mHeightUsed);
281 | RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
282 | layoutDecorated(child,
283 | rect.left - mOffsetX + lp.leftMargin + getPaddingLeft(),
284 | rect.top - mOffsetY + lp.topMargin + getPaddingTop(),
285 | rect.right - mOffsetX - lp.rightMargin + getPaddingLeft(),
286 | rect.bottom - mOffsetY - lp.bottomMargin + getPaddingTop());
287 | }
288 | }
289 |
290 |
291 | //--- 处理滚动 ----------------------------------------------------------------------------------
292 |
293 | /**
294 | * 水平滚动
295 | *
296 | * @param dx 滚动距离
297 | * @param recycler 回收器
298 | * @param state 滚动状态
299 | * @return 实际滚动距离
300 | */
301 | @Override
302 | public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State
303 | state) {
304 | int newX = mOffsetX + dx;
305 | int result = dx;
306 | if (newX > mMaxScrollX) {
307 | result = mMaxScrollX - mOffsetX;
308 | } else if (newX < 0) {
309 | result = 0 - mOffsetX;
310 | }
311 | mOffsetX += result;
312 | setPageIndex(getPageIndexByOffset(), true);
313 | offsetChildrenHorizontal(-result);
314 | if (result > 0) {
315 | recycleAndFillItems(recycler, state, true);
316 | } else {
317 | recycleAndFillItems(recycler, state, false);
318 | }
319 | return result;
320 | }
321 |
322 | /**
323 | * 垂直滚动
324 | *
325 | * @param dy 滚动距离
326 | * @param recycler 回收器
327 | * @param state 滚动状态
328 | * @return 实际滚动距离
329 | */
330 | @Override
331 | public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State
332 | state) {
333 | int newY = mOffsetY + dy;
334 | int result = dy;
335 | if (newY > mMaxScrollY) {
336 | result = mMaxScrollY - mOffsetY;
337 | } else if (newY < 0) {
338 | result = 0 - mOffsetY;
339 | }
340 | mOffsetY += result;
341 | setPageIndex(getPageIndexByOffset(), true);
342 | offsetChildrenVertical(-result);
343 | if (result > 0) {
344 | recycleAndFillItems(recycler, state, true);
345 | } else {
346 | recycleAndFillItems(recycler, state, false);
347 | }
348 | return result;
349 | }
350 |
351 | /**
352 | * 监听滚动状态,滚动结束后通知当前选中的页面
353 | *
354 | * @param state 滚动状态
355 | */
356 | @Override
357 | public void onScrollStateChanged(int state) {
358 | Logi("onScrollStateChanged = " + state);
359 | mScrollState = state;
360 | super.onScrollStateChanged(state);
361 | if (state == SCROLL_STATE_IDLE) {
362 | setPageIndex(getPageIndexByOffset(), false);
363 | }
364 | }
365 |
366 |
367 | //--- 私有方法 ----------------------------------------------------------------------------------
368 |
369 | /**
370 | * 获取条目显示区域
371 | *
372 | * @param pos 位置下标
373 | * @return 显示区域
374 | */
375 | private Rect getItemFrameByPosition(int pos) {
376 | Rect rect = mItemFrames.get(pos);
377 | if (null == rect) {
378 | rect = new Rect();
379 | // 计算显示区域 Rect
380 |
381 | // 1. 获取当前View所在页数
382 | int page = pos / mOnePageSize;
383 |
384 | // 2. 计算当前页数左上角的总偏移量
385 | int offsetX = 0;
386 | int offsetY = 0;
387 | if (canScrollHorizontally()) {
388 | offsetX += getUsableWidth() * page;
389 | } else {
390 | offsetY += getUsableHeight() * page;
391 | }
392 |
393 | // 3. 根据在当前页面中的位置确定具体偏移量
394 | int pagePos = pos % mOnePageSize; // 在当前页面中是第几个
395 | int row = pagePos / mColumns; // 获取所在行
396 | int col = pagePos - (row * mColumns); // 获取所在列
397 |
398 | offsetX += col * mItemWidth;
399 | offsetY += row * mItemHeight;
400 |
401 | // 状态输出,用于调试
402 | Logi("pagePos = " + pagePos);
403 | Logi("行 = " + row);
404 | Logi("列 = " + col);
405 |
406 | Logi("offsetX = " + offsetX);
407 | Logi("offsetY = " + offsetY);
408 |
409 | rect.left = offsetX;
410 | rect.top = offsetY;
411 | rect.right = offsetX + mItemWidth;
412 | rect.bottom = offsetY + mItemHeight;
413 |
414 | // 存储
415 | mItemFrames.put(pos, rect);
416 | }
417 | return rect;
418 | }
419 |
420 | /**
421 | * 获取可用的宽度
422 | *
423 | * @return 宽度 - padding
424 | */
425 | private int getUsableWidth() {
426 | return getWidth() - getPaddingLeft() - getPaddingRight();
427 | }
428 |
429 | /**
430 | * 获取可用的高度
431 | *
432 | * @return 高度 - padding
433 | */
434 | private int getUsableHeight() {
435 | return getHeight() - getPaddingTop() - getPaddingBottom();
436 | }
437 |
438 |
439 | //--- 页面相关(私有) -----------------------------------------------------------------------------
440 |
441 | /**
442 | * 获取总页数
443 | */
444 | private int getTotalPageCount() {
445 | if (getItemCount() <= 0) return 0;
446 | int totalCount = getItemCount() / mOnePageSize;
447 | if (getItemCount() % mOnePageSize != 0) {
448 | totalCount++;
449 | }
450 | return totalCount;
451 | }
452 |
453 | /**
454 | * 根据pos,获取该View所在的页面
455 | *
456 | * @param pos position
457 | * @return 页面的页码
458 | */
459 | private int getPageIndexByPos(int pos) {
460 | return pos / mOnePageSize;
461 | }
462 |
463 | /**
464 | * 根据 offset 获取页面Index
465 | *
466 | * @return 页面 Index
467 | */
468 | private int getPageIndexByOffset() {
469 | int pageIndex;
470 | if (canScrollVertically()) {
471 | int pageHeight = getUsableHeight();
472 | if (mOffsetY <= 0 || pageHeight <= 0) {
473 | pageIndex = 0;
474 | } else {
475 | pageIndex = mOffsetY / pageHeight;
476 | if (mOffsetY % pageHeight > pageHeight / 2) {
477 | pageIndex++;
478 | }
479 | }
480 | } else {
481 | int pageWidth = getUsableWidth();
482 | if (mOffsetX <= 0 || pageWidth <= 0) {
483 | pageIndex = 0;
484 | } else {
485 | pageIndex = mOffsetX / pageWidth;
486 | if (mOffsetX % pageWidth > pageWidth / 2) {
487 | pageIndex++;
488 | }
489 | }
490 | }
491 | Logi("getPageIndexByOffset pageIndex = " + pageIndex);
492 | return pageIndex;
493 | }
494 |
495 |
496 | //--- 公开方法 ----------------------------------------------------------------------------------
497 |
498 | /**
499 | * 创建默认布局参数
500 | *
501 | * @return 默认布局参数
502 | */
503 | @Override
504 | public RecyclerView.LayoutParams generateDefaultLayoutParams() {
505 | return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
506 | ViewGroup.LayoutParams.WRAP_CONTENT);
507 | }
508 |
509 | /**
510 | * 处理测量逻辑
511 | *
512 | * @param recycler RecyclerView
513 | * @param state 状态
514 | * @param widthMeasureSpec 宽度属性
515 | * @param heightMeasureSpec 高估属性
516 | */
517 | @Override
518 | public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthMeasureSpec, int heightMeasureSpec) {
519 | super.onMeasure(recycler, state, widthMeasureSpec, heightMeasureSpec);
520 | int widthsize = View.MeasureSpec.getSize(widthMeasureSpec); //取出宽度的确切数值
521 | int widthmode = View.MeasureSpec.getMode(widthMeasureSpec); //取出宽度的测量模式
522 |
523 | int heightsize = View.MeasureSpec.getSize(heightMeasureSpec); //取出高度的确切数值
524 | int heightmode = View.MeasureSpec.getMode(heightMeasureSpec); //取出高度的测量模式
525 |
526 | // 将 wrap_content 转换为 match_parent
527 | if (widthmode != EXACTLY && widthsize > 0) {
528 | widthmode = EXACTLY;
529 | }
530 | if (heightmode != EXACTLY && heightsize > 0) {
531 | heightmode = EXACTLY;
532 | }
533 | setMeasuredDimension(View.MeasureSpec.makeMeasureSpec(widthsize, widthmode),
534 | View.MeasureSpec.makeMeasureSpec(heightsize, heightmode));
535 | }
536 |
537 | /**
538 | * 是否可以水平滚动
539 | *
540 | * @return true 是,false 不是。
541 | */
542 | @Override
543 | public boolean canScrollHorizontally() {
544 | return mOrientation == HORIZONTAL;
545 | }
546 |
547 | /**
548 | * 是否可以垂直滚动
549 | *
550 | * @return true 是,false 不是。
551 | */
552 | @Override
553 | public boolean canScrollVertically() {
554 | return mOrientation == VERTICAL;
555 | }
556 |
557 | /**
558 | * 找到下一页第一个条目的位置
559 | *
560 | * @return 第一个搞条目的位置
561 | */
562 | int findNextPageFirstPos() {
563 | int page = mLastPageIndex;
564 | page++;
565 | if (page >= getTotalPageCount()) {
566 | page = getTotalPageCount() - 1;
567 | }
568 | Loge("computeScrollVectorForPosition next = " + page);
569 | return page * mOnePageSize;
570 | }
571 |
572 | /**
573 | * 找到上一页的第一个条目的位置
574 | *
575 | * @return 第一个条目的位置
576 | */
577 | int findPrePageFirstPos() {
578 | // 在获取时由于前一页的View预加载出来了,所以获取到的直接就是前一页
579 | int page = mLastPageIndex;
580 | page--;
581 | Loge("computeScrollVectorForPosition pre = " + page);
582 | if (page < 0) {
583 | page = 0;
584 | }
585 | Loge("computeScrollVectorForPosition pre = " + page);
586 | return page * mOnePageSize;
587 | }
588 |
589 | /**
590 | * 获取当前 X 轴偏移量
591 | *
592 | * @return X 轴偏移量
593 | */
594 | public int getOffsetX() {
595 | return mOffsetX;
596 | }
597 |
598 | /**
599 | * 获取当前 Y 轴偏移量
600 | *
601 | * @return Y 轴偏移量
602 | */
603 | public int getOffsetY() {
604 | return mOffsetY;
605 | }
606 |
607 |
608 | //--- 页面对齐 ----------------------------------------------------------------------------------
609 |
610 | /**
611 | * 计算到目标位置需要滚动的距离{@link RecyclerView.SmoothScroller.ScrollVectorProvider}
612 | *
613 | * @param targetPosition 目标控件
614 | * @return 需要滚动的距离
615 | */
616 | @Override
617 | public PointF computeScrollVectorForPosition(int targetPosition) {
618 | PointF vector = new PointF();
619 | int[] pos = getSnapOffset(targetPosition);
620 | vector.x = pos[0];
621 | vector.y = pos[1];
622 | return vector;
623 | }
624 |
625 | /**
626 | * 获取偏移量(为PagerGridSnapHelper准备)
627 | * 用于分页滚动,确定需要滚动的距离。
628 | * {@link PagerGridSnapHelper}
629 | *
630 | * @param targetPosition 条目下标
631 | */
632 | int[] getSnapOffset(int targetPosition) {
633 | int[] offset = new int[2];
634 | int[] pos = getPageLeftTopByPosition(targetPosition);
635 | offset[0] = pos[0] - mOffsetX;
636 | offset[1] = pos[1] - mOffsetY;
637 | return offset;
638 | }
639 |
640 | /**
641 | * 根据条目下标获取该条目所在页面的左上角位置
642 | *
643 | * @param pos 条目下标
644 | * @return 左上角位置
645 | */
646 | private int[] getPageLeftTopByPosition(int pos) {
647 | int[] leftTop = new int[2];
648 | int page = getPageIndexByPos(pos);
649 | if (canScrollHorizontally()) {
650 | leftTop[0] = page * getUsableWidth();
651 | leftTop[1] = 0;
652 | } else {
653 | leftTop[0] = 0;
654 | leftTop[1] = page * getUsableHeight();
655 | }
656 | return leftTop;
657 | }
658 |
659 | /**
660 | * 获取需要对齐的View
661 | *
662 | * @return 需要对齐的View
663 | */
664 | public View findSnapView() {
665 | if (null != getFocusedChild()) {
666 | return getFocusedChild();
667 | }
668 | if (getChildCount() <= 0) {
669 | return null;
670 | }
671 | int targetPos = getPageIndexByOffset() * mOnePageSize; // 目标Pos
672 | for (int i = 0; i < getChildCount(); i++) {
673 | int childPos = getPosition(getChildAt(i));
674 | if (childPos == targetPos) {
675 | return getChildAt(i);
676 | }
677 | }
678 | return getChildAt(0);
679 | }
680 |
681 |
682 | //--- 处理页码变化 -------------------------------------------------------------------------------
683 |
684 | private boolean mChangeSelectInScrolling = true; // 是否在滚动过程中对页面变化回调
685 | private int mLastPageCount = -1; // 上次页面总数
686 | private int mLastPageIndex = -1; // 上次页面下标
687 |
688 | /**
689 | * 设置页面总数
690 | *
691 | * @param pageCount 页面总数
692 | */
693 | private void setPageCount(int pageCount) {
694 | if (pageCount >= 0) {
695 | if (mPageListener != null && pageCount != mLastPageCount) {
696 | mPageListener.onPageSizeChanged(pageCount);
697 | }
698 | mLastPageCount = pageCount;
699 | }
700 | }
701 |
702 | /**
703 | * 设置当前选中页面
704 | *
705 | * @param pageIndex 页面下标
706 | * @param isScrolling 是否处于滚动状态
707 | */
708 | private void setPageIndex(int pageIndex, boolean isScrolling) {
709 | Loge("setPageIndex = " + pageIndex + ":" + isScrolling);
710 | if (pageIndex == mLastPageIndex) return;
711 | // 如果允许连续滚动,那么在滚动过程中就会更新页码记录
712 | if (isAllowContinuousScroll()) {
713 | mLastPageIndex = pageIndex;
714 | } else {
715 | // 否则,只有等滚动停下时才会更新页码记录
716 | if (!isScrolling) {
717 | mLastPageIndex = pageIndex;
718 | }
719 | }
720 | if (isScrolling && !mChangeSelectInScrolling) return;
721 | if (pageIndex >= 0) {
722 | if (null != mPageListener) {
723 | mPageListener.onPageSelect(pageIndex);
724 | }
725 | }
726 | }
727 |
728 | /**
729 | * 设置是否在滚动状态更新选中页码
730 | *
731 | * @param changeSelectInScrolling true:更新、false:不更新
732 | */
733 | public void setChangeSelectInScrolling(boolean changeSelectInScrolling) {
734 | mChangeSelectInScrolling = changeSelectInScrolling;
735 | }
736 |
737 | /**
738 | * 设置滚动方向
739 | *
740 | * @param orientation 滚动方向
741 | * @return 最终的滚动方向
742 | */
743 | @OrientationType
744 | public int setOrientationType(@OrientationType int orientation) {
745 | if (mOrientation == orientation || mScrollState != SCROLL_STATE_IDLE) return mOrientation;
746 | mOrientation = orientation;
747 | mItemFrames.clear();
748 | int x = mOffsetX;
749 | int y = mOffsetY;
750 | mOffsetX = y / getUsableHeight() * getUsableWidth();
751 | mOffsetY = x / getUsableWidth() * getUsableHeight();
752 | int mx = mMaxScrollX;
753 | int my = mMaxScrollY;
754 | mMaxScrollX = my / getUsableHeight() * getUsableWidth();
755 | mMaxScrollY = mx / getUsableWidth() * getUsableHeight();
756 | return mOrientation;
757 | }
758 |
759 | //--- 滚动到指定位置 -----------------------------------------------------------------------------
760 |
761 | @Override
762 | public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
763 | int targetPageIndex = getPageIndexByPos(position);
764 | smoothScrollToPage(targetPageIndex);
765 | }
766 |
767 | /**
768 | * 平滑滚动到上一页
769 | */
770 | public void smoothPrePage() {
771 | smoothScrollToPage(getPageIndexByOffset() - 1);
772 | }
773 |
774 | /**
775 | * 平滑滚动到下一页
776 | */
777 | public void smoothNextPage() {
778 | smoothScrollToPage(getPageIndexByOffset() + 1);
779 | }
780 |
781 | /**
782 | * 平滑滚动到指定页面
783 | *
784 | * @param pageIndex 页面下标
785 | */
786 | public void smoothScrollToPage(int pageIndex) {
787 | if (pageIndex < 0 || pageIndex >= mLastPageCount) {
788 | Log.e(TAG, "pageIndex is outOfIndex, must in [0, " + mLastPageCount + ").");
789 | return;
790 | }
791 | if (null == mRecyclerView) {
792 | Log.e(TAG, "RecyclerView Not Found!");
793 | return;
794 | }
795 |
796 | // 如果滚动到页面之间距离过大,先直接滚动到目标页面到临近页面,在使用 smoothScroll 最终滚动到目标
797 | // 否则在滚动距离很大时,会导致滚动耗费的时间非常长
798 | int currentPageIndex = getPageIndexByOffset();
799 | if (Math.abs(pageIndex - currentPageIndex) > 3) {
800 | if (pageIndex > currentPageIndex) {
801 | scrollToPage(pageIndex - 3);
802 | } else if (pageIndex < currentPageIndex) {
803 | scrollToPage(pageIndex + 3);
804 | }
805 | }
806 |
807 | // 具体执行滚动
808 | LinearSmoothScroller smoothScroller = new PagerGridSmoothScroller(mRecyclerView);
809 | int position = pageIndex * mOnePageSize;
810 | smoothScroller.setTargetPosition(position);
811 | startSmoothScroll(smoothScroller);
812 | }
813 |
814 | //=== 直接滚动 ===
815 |
816 | @Override
817 | public void scrollToPosition(int position) {
818 | int pageIndex = getPageIndexByPos(position);
819 | scrollToPage(pageIndex);
820 | }
821 |
822 | /**
823 | * 上一页
824 | */
825 | public void prePage() {
826 | scrollToPage(getPageIndexByOffset() - 1);
827 | }
828 |
829 | /**
830 | * 下一页
831 | */
832 | public void nextPage() {
833 | scrollToPage(getPageIndexByOffset() + 1);
834 | }
835 |
836 | /**
837 | * 滚动到指定页面
838 | *
839 | * @param pageIndex 页面下标
840 | */
841 | public void scrollToPage(int pageIndex) {
842 | if (pageIndex < 0 || pageIndex >= mLastPageCount) {
843 | Log.e(TAG, "pageIndex = " + pageIndex + " is out of bounds, mast in [0, " + mLastPageCount + ")");
844 | return;
845 | }
846 |
847 | if (null == mRecyclerView) {
848 | Log.e(TAG, "RecyclerView Not Found!");
849 | return;
850 | }
851 |
852 | int mTargetOffsetXBy = 0;
853 | int mTargetOffsetYBy = 0;
854 | if (canScrollVertically()) {
855 | mTargetOffsetXBy = 0;
856 | mTargetOffsetYBy = pageIndex * getUsableHeight() - mOffsetY;
857 | } else {
858 | mTargetOffsetXBy = pageIndex * getUsableWidth() - mOffsetX;
859 | mTargetOffsetYBy = 0;
860 | }
861 | Loge("mTargetOffsetXBy = " + mTargetOffsetXBy);
862 | Loge("mTargetOffsetYBy = " + mTargetOffsetYBy);
863 | mRecyclerView.scrollBy(mTargetOffsetXBy, mTargetOffsetYBy);
864 | setPageIndex(pageIndex, false);
865 | }
866 |
867 | /**
868 | * 是否允许连续滚动,默认为允许
869 | *
870 | * @return true 允许, false 不允许
871 | */
872 | public boolean isAllowContinuousScroll() {
873 | return mAllowContinuousScroll;
874 | }
875 |
876 | /**
877 | * 设置是否允许连续滚动
878 | *
879 | * @param allowContinuousScroll true 允许,false 不允许
880 | */
881 | public void setAllowContinuousScroll(boolean allowContinuousScroll) {
882 | mAllowContinuousScroll = allowContinuousScroll;
883 | }
884 |
885 | //--- 对外接口 ----------------------------------------------------------------------------------
886 |
887 | private PageListener mPageListener = null;
888 |
889 | public void setPageListener(PageListener pageListener) {
890 | mPageListener = pageListener;
891 | }
892 |
893 | public interface PageListener {
894 | /**
895 | * 页面总数量变化
896 | *
897 | * @param pageSize 页面总数
898 | */
899 | void onPageSizeChanged(int pageSize);
900 |
901 | /**
902 | * 页面被选中
903 | *
904 | * @param pageIndex 选中的页面
905 | */
906 | void onPageSelect(int pageIndex);
907 | }
908 | }
--------------------------------------------------------------------------------
/PagerLayoutManager/pagerlayoutmanager/src/main/java/com/gcssloop/widget/PagerGridSmoothScroller.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 GcsSloop
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * Last modified 2018-04-09 23:56:59
17 | *
18 | * GitHub: https://github.com/GcsSloop
19 | * WeiBo: http://weibo.com/GcsSloop
20 | * WebSite: http://www.gcssloop.com
21 | */
22 |
23 | package com.gcssloop.widget;
24 |
25 | import android.support.annotation.NonNull;
26 | import android.support.v7.widget.LinearSmoothScroller;
27 | import android.support.v7.widget.RecyclerView;
28 | import android.util.DisplayMetrics;
29 | import android.view.View;
30 |
31 | import static com.gcssloop.widget.PagerConfig.Logi;
32 |
33 | /**
34 | * 作用:用于处理平滑滚动
35 | * 作者:GcsSloop
36 | * 摘要:用于用户手指抬起后页面对齐或者 Fling 事件。
37 | */
38 | public class PagerGridSmoothScroller extends LinearSmoothScroller {
39 | private RecyclerView mRecyclerView;
40 |
41 | public PagerGridSmoothScroller(@NonNull RecyclerView recyclerView) {
42 | super(recyclerView.getContext());
43 | mRecyclerView = recyclerView;
44 | }
45 |
46 | @Override
47 | protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
48 | RecyclerView.LayoutManager manager = mRecyclerView.getLayoutManager();
49 | if (null == manager) return;
50 | if (manager instanceof PagerGridLayoutManager) {
51 | PagerGridLayoutManager layoutManager = (PagerGridLayoutManager) manager;
52 | int pos = mRecyclerView.getChildAdapterPosition(targetView);
53 | int[] snapDistances = layoutManager.getSnapOffset(pos);
54 | final int dx = snapDistances[0];
55 | final int dy = snapDistances[1];
56 | Logi("dx = " + dx);
57 | Logi("dy = " + dy);
58 | final int time = calculateTimeForScrolling(Math.max(Math.abs(dx), Math.abs(dy)));
59 | if (time > 0) {
60 | action.update(dx, dy, time, mDecelerateInterpolator);
61 | }
62 | }
63 | }
64 |
65 | @Override
66 | protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
67 | return PagerConfig.getMillisecondsPreInch() / displayMetrics.densityDpi;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/PagerLayoutManager/pagerlayoutmanager/src/main/java/com/gcssloop/widget/PagerGridSnapHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 GcsSloop
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * Last modified 2017-09-20 16:32:43
17 | *
18 | * GitHub: https://github.com/GcsSloop
19 | * WeiBo: http://weibo.com/GcsSloop
20 | * WebSite: http://www.gcssloop.com
21 | */
22 |
23 | package com.gcssloop.widget;
24 |
25 | import android.support.annotation.NonNull;
26 | import android.support.annotation.Nullable;
27 | import android.support.v7.widget.LinearSmoothScroller;
28 | import android.support.v7.widget.RecyclerView;
29 | import android.support.v7.widget.SnapHelper;
30 | import android.view.View;
31 |
32 | import static com.gcssloop.widget.PagerConfig.Loge;
33 |
34 | /**
35 | * 作用:分页居中工具
36 | * 作者:GcsSloop
37 | * 摘要:每次只滚动一个页面
38 | */
39 | public class PagerGridSnapHelper extends SnapHelper {
40 | private RecyclerView mRecyclerView; // RecyclerView
41 |
42 | /**
43 | * 用于将滚动工具和 Recycler 绑定
44 | *
45 | * @param recyclerView RecyclerView
46 | * @throws IllegalStateException 状态异常
47 | */
48 | @Override
49 | public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws
50 | IllegalStateException {
51 | super.attachToRecyclerView(recyclerView);
52 | mRecyclerView = recyclerView;
53 | }
54 |
55 | /**
56 | * 计算需要滚动的向量,用于页面自动回滚对齐
57 | *
58 | * @param layoutManager 布局管理器
59 | * @param targetView 目标控件
60 | * @return 需要滚动的距离
61 | */
62 | @Nullable
63 | @Override
64 | public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,
65 | @NonNull View targetView) {
66 | int pos = layoutManager.getPosition(targetView);
67 | Loge("findTargetSnapPosition, pos = " + pos);
68 | int[] offset = new int[2];
69 | if (layoutManager instanceof PagerGridLayoutManager) {
70 | PagerGridLayoutManager manager = (PagerGridLayoutManager) layoutManager;
71 | offset = manager.getSnapOffset(pos);
72 | }
73 | return offset;
74 | }
75 |
76 | /**
77 | * 获得需要对齐的View,对于分页布局来说,就是页面第一个
78 | *
79 | * @param layoutManager 布局管理器
80 | * @return 目标控件
81 | */
82 | @Nullable
83 | @Override
84 | public View findSnapView(RecyclerView.LayoutManager layoutManager) {
85 | if (layoutManager instanceof PagerGridLayoutManager) {
86 | PagerGridLayoutManager manager = (PagerGridLayoutManager) layoutManager;
87 | return manager.findSnapView();
88 | }
89 | return null;
90 | }
91 |
92 | /**
93 | * 获取目标控件的位置下标
94 | * (获取滚动后第一个View的下标)
95 | *
96 | * @param layoutManager 布局管理器
97 | * @param velocityX X 轴滚动速率
98 | * @param velocityY Y 轴滚动速率
99 | * @return 目标控件的下标
100 | */
101 | @Override
102 | public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager,
103 | int velocityX, int velocityY) {
104 | int target = RecyclerView.NO_POSITION;
105 | Loge("findTargetSnapPosition, velocityX = " + velocityX + ", velocityY" + velocityY);
106 | if (null != layoutManager && layoutManager instanceof PagerGridLayoutManager) {
107 | PagerGridLayoutManager manager = (PagerGridLayoutManager) layoutManager;
108 | if (manager.canScrollHorizontally()) {
109 | if (velocityX > PagerConfig.getFlingThreshold()) {
110 | target = manager.findNextPageFirstPos();
111 | } else if (velocityX < -PagerConfig.getFlingThreshold()) {
112 | target = manager.findPrePageFirstPos();
113 | }
114 | } else if (manager.canScrollVertically()) {
115 | if (velocityY > PagerConfig.getFlingThreshold()) {
116 | target = manager.findNextPageFirstPos();
117 | } else if (velocityY < -PagerConfig.getFlingThreshold()) {
118 | target = manager.findPrePageFirstPos();
119 | }
120 | }
121 | }
122 | Loge("findTargetSnapPosition, target = " + target);
123 | return target;
124 | }
125 |
126 | /**
127 | * 一扔(快速滚动)
128 | *
129 | * @param velocityX X 轴滚动速率
130 | * @param velocityY Y 轴滚动速率
131 | * @return 是否消费该事件
132 | */
133 | @Override
134 | public boolean onFling(int velocityX, int velocityY) {
135 | RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
136 | if (layoutManager == null) {
137 | return false;
138 | }
139 | RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
140 | if (adapter == null) {
141 | return false;
142 | }
143 | int minFlingVelocity = PagerConfig.getFlingThreshold();
144 | Loge("minFlingVelocity = " + minFlingVelocity);
145 | return (Math.abs(velocityY) > minFlingVelocity || Math.abs(velocityX) > minFlingVelocity)
146 | && snapFromFling(layoutManager, velocityX, velocityY);
147 | }
148 |
149 | /**
150 | * 快速滚动的具体处理方案
151 | *
152 | * @param layoutManager 布局管理器
153 | * @param velocityX X 轴滚动速率
154 | * @param velocityY Y 轴滚动速率
155 | * @return 是否消费该事件
156 | */
157 | private boolean snapFromFling(@NonNull RecyclerView.LayoutManager layoutManager, int velocityX,
158 | int velocityY) {
159 | if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
160 | return false;
161 | }
162 |
163 | RecyclerView.SmoothScroller smoothScroller = createSnapScroller(layoutManager);
164 | if (smoothScroller == null) {
165 | return false;
166 | }
167 |
168 | int targetPosition = findTargetSnapPosition(layoutManager, velocityX, velocityY);
169 | if (targetPosition == RecyclerView.NO_POSITION) {
170 | return false;
171 | }
172 |
173 | smoothScroller.setTargetPosition(targetPosition);
174 | layoutManager.startSmoothScroll(smoothScroller);
175 | return true;
176 | }
177 |
178 | /**
179 | * 通过自定义 LinearSmoothScroller 来控制速度
180 | *
181 | * @param layoutManager 布局故哪里去
182 | * @return 自定义 LinearSmoothScroller
183 | */
184 | protected LinearSmoothScroller createSnapScroller(RecyclerView.LayoutManager layoutManager) {
185 | if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
186 | return null;
187 | }
188 | return new PagerGridSmoothScroller(mRecyclerView);
189 | }
190 |
191 | //--- 公开方法 ----------------------------------------------------------------------------------
192 |
193 | /**
194 | * 设置滚动阀值
195 | * @param threshold 滚动阀值
196 | */
197 | public void setFlingThreshold(int threshold) {
198 | PagerConfig.setFlingThreshold(threshold);
199 | }
200 | }
--------------------------------------------------------------------------------
/PagerLayoutManager/settings.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 GcsSloop
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * Last modified 2017-09-18 23:38:56
17 | *
18 | * GitHub: https://github.com/GcsSloop
19 | * WeiBo: http://weibo.com/GcsSloop
20 | * WebSite: http://www.gcssloop.com
21 | */
22 |
23 | include ':app', ':pagerlayoutmanager'
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PagerLayoutManager
2 |
3 | 具有分页功能的 Recyclerview 布局管理器,主打分页,可以替代部分场景下的网格布局,线性布局,以及一些简单的ViewPager,但也有一定的局限性,请选择性使用。
4 |
5 | - [网格分页布局源码解析(上)](https://xiaozhuanlan.com/topic/5841730926)
6 | - [网格分页布局源码解析(下)](https://xiaozhuanlan.com/topic/1456397082)
7 |
8 | ## 1. 效果预览
9 |
10 |  
11 |
12 |  
13 |
14 |
15 |
16 | ## 2. 支持的特性
17 |
18 | - [x] 网格布局,设置合适的行列数可以模拟线性布局或者ViewPager。
19 | - [x] 分页效果,支持自动回调页码的变化(总页数和当前页数)。
20 | - [x] 布局方向,支持横向滚动和垂直滚动。
21 | - [x] 方向切换,支持切换滚动方向。
22 | - [x] 支持电视,支持TV上按键翻页。
23 | - [x] 内存稳定,不会因为一次性添加大量的数据导致内存飙升或者严重卡顿。
24 | - [x] 使用简单,无侵入性,可以快速的将其他布局替换为该布局,也可以快速的移除该布局。
25 | - [x] 自动修正,当滚动到第3页,删除数据后内容不足2页,会自动修正当前页面为第2页。
26 |
27 | ## 3. 主要文件
28 |
29 | | 名称 | 作用 |
30 | | --------------------------------- | ----------------------------- |
31 | | /java/PagerGridLayoutManager.java | 水平分页网格布局管理器。 |
32 | | /java/PagerGridSnapHelper.java | 滚动辅助工具。 |
33 | | /java/PagerConfig.java | Pager配置,用于开关调试日志。 |
34 |
35 |
36 |
37 | ## 4. 使用方法
38 |
39 | ### 4.1 基本用法
40 |
41 | #### 4.1.1 基本设置
42 |
43 | ```java
44 | // 1.水平分页布局管理器
45 | PagerGridLayoutManager layoutManager = new PagerGridLayoutManager(
46 | 2, 3, PagerGridLayoutManager.VERTICAL);
47 | recyclerView.setLayoutManager(layoutManager);
48 |
49 | // 2.设置滚动辅助工具
50 | PagerGridSnapHelper pageSnapHelper = new PagerGridSnapHelper();
51 | pageSnapHelper.attachToRecyclerView(recyclerView);
52 | ```
53 |
54 | **注意:**
55 | **1. 一定要在先设置 PagerGridLayoutManager, 之后再设置 PagerGridSnapHelper。**
56 | **2. 注意名称是 PagerGridSnapHelper 不是 PagerSnapHelper。**
57 |
58 | #### 4.1.2 设置页面变化监听器
59 |
60 | ```java
61 | layoutManager.setPageListener(this); // 设置页面变化监听器
62 |
63 | // 当总页数确定时的回调
64 | @Override public void onPageSizeChanged(int pageSize) {
65 | Log.e("TAG", "总页数 = " + pageSize);
66 | }
67 |
68 | // 当页面被选中时的回调(从 0 开始)
69 | @Override public void onPageSelect(int pageIndex) {
70 | Log.e("TAG", "选中页码 = " + (pageIndex+1));
71 | }
72 | ```
73 |
74 | #### 4.1.3 直接滚动
75 |
76 | ```java
77 | // 滚动到指定条目
78 | public void scrollToPosition(int position);
79 | // 切换页面
80 | public void scrollToPage(int pageIndex); // 滚动到指定页面
81 | public void prePage(); // 上一页
82 | public void nextPage(); // 下一页
83 |
84 | // 使用示例
85 | recyclerView.scrollToPosition(0);
86 | layoutManager.scrollToPage(0);
87 | layoutManager.prePage();
88 | layoutManager.nextPage();
89 | ```
90 |
91 | #### 4.1.4 平滑滚动
92 |
93 | ```java
94 | // 平滑滚动到指定条目
95 | mRecyclerView.smoothScrollToPosition(pos);
96 | // 平滑切换页面
97 | mPagerLyoutManager.smoothScrollToPage(pageIndex); // 平滑滚动到指定页
98 | mPagerLyoutManager.smoothPrePage(); // 平滑滚动到上一页
99 | mPagerLyoutManager.smoothNextPage(); // 平滑滚动到下一页
100 |
101 | // 使用示例
102 | recyclerView.smoothScrollToPosition(0);
103 | layoutManager.smoothScrollToPage(0);
104 | layoutManager.smoothPrePage();
105 | layoutManager.smoothNextPage();
106 | ```
107 |
108 | ### 4.2 其他设置
109 |
110 | #### 4.2.1 是否在滚动过程中回调页码变化
111 |
112 | ```java
113 | // 设置是否在滚动过程中回调页码变化
114 | void setChangeSelectInScrolling(boolean changeSelectInScrolling);
115 |
116 | // 使用示例
117 | layoutManager.setChangeSelectInScrolling(false);
118 | ```
119 |
120 | #### 4.2.2 是否允许连续滚动
121 |
122 | 默认允许连续滚动。
123 |
124 | ```java
125 | // 是否允许连续滚动
126 | boolean isAllowContinuousScroll();
127 | // 设置是否允许连续滚动
128 | void setAllowContinuousScroll(boolean allowContinuousScroll);
129 |
130 | // 使用示例
131 | layoutManager.isAllowContinuousScroll();
132 | layoutManager.setAllowContinuousScroll(false);
133 | ```
134 |
135 | #### 4.2.3 设置滚动方向
136 |
137 | 注意:滚动过程中不可切换方向,设置无效。
138 |
139 | ```java
140 | // 设置滚动方向
141 | int setOrientationType(@OrientationType int orientation);
142 |
143 | // 使用示例
144 | layoutManager.setOrientationType(PagerGridLayoutManager.HORIZONTAL);
145 | ```
146 |
147 | #### 4.2.4 设置 Fling 阀值
148 |
149 | ```java
150 | // 设置 Fling 阀值
151 | void setFlingThreshold(int flingThreshold);
152 |
153 | // 使用示例
154 | PagerConfig.setFlingThreshold(1000);
155 | ```
156 |
157 | #### 4.2.5 设置滚动速度
158 |
159 | ```java
160 | // 设置滚动速度(滚动一英寸所耗费的微秒数,数值越大,速度越慢,默认为 60f)
161 | void setMillisecondsPreInch(float millisecondsPreInch);
162 |
163 | // 使用示例
164 | PagerConfig.setMillisecondsPreInch(60f);
165 | ```
166 |
167 | #### 4.2.6 打开调试日志
168 |
169 | ```java
170 | // 打开调试用日志输出,一般情况忽略即可
171 | void setShowLog(boolean showLog);
172 |
173 | // 使用示例
174 | PagerConfig.setShowLog(true);
175 | ```
176 |
177 |
178 |
179 | ## 5. 注意事项:
180 |
181 | 目前存在一个问题,使用的时候请务必给 RecyclerView 设置固定大小或者match_parent,如果不设置默认高度为 0 ,则任何内容都不会显示出来。
182 |
183 | ## 6. 添加方式
184 |
185 | #### 6.1 添加仓库
186 |
187 | 在项目的 `build.gradle` 文件中配置仓库地址。
188 |
189 | ```groovy
190 | allprojects {
191 | repositories {
192 | jcenter()
193 | // 私有仓库地址
194 | maven { url "http://lib.gcssloop.com/repository/gcssloop-central/" }
195 | }
196 | }
197 | ```
198 |
199 | #### 6.2 添加项目依赖
200 |
201 | 在需要添加依赖的 Module 下添加以下信息,使用方式和普通的远程仓库一样。
202 |
203 | ```groovy
204 | implementation 'com.gcssloop.recyclerview:pagerlayoutmanager:2.3.8'
205 | ```
206 |
207 |
208 |
209 | ## 作者简介
210 |
211 | #### 作者微博: [@GcsSloop](http://weibo.com/GcsSloop)
212 |
213 | #### 个人网站: http://www.gcssloop.com
214 |
215 |
216 |
217 | ## 更新日志
218 |
219 | #### v2.3.8
220 |
221 | 修复 RecyclerView 设置为 wrap_content 时不显示(当属性为 wrap_content 时让其填充父窗体)。
222 |
223 | #### v2.3.7
224 |
225 | 完善代码细节,移除冗余逻辑。
226 |
227 | #### v2.3.6
228 |
229 | 降低最低版本兼容。
230 | 完善文档注释,精简项目结构。
231 |
232 | #### v2.3.5
233 |
234 | 优化代码结构,抽取 PagerGridSmoothScroller。
235 |
236 | #### v2.3.4
237 |
238 | 修复 padding 问题。
239 | 优化计算显示区域的代码逻辑。
240 |
241 | #### v2.3.3
242 |
243 | 优化代码结构。
244 | 移除冗余代码。
245 |
246 | #### v2.3.2
247 |
248 | 调整平滑滚动对外接口。
249 |
250 | ```java
251 | // 平滑滚动到上一页
252 | mPagerLyoutManager.smoothPrePage();
253 | // 平滑滚动到下一页
254 | mPagerLyoutManager.smoothNextPage();
255 | // 平滑滚动到指定页
256 | mPagerLyoutManager.smoothScrollToPage(pageIndex);
257 | ```
258 |
259 | #### v2.3.1
260 |
261 | 优化直接滚动。
262 | 修复更新单个条目导致整个界面刷新的情况。
263 |
264 | #### v2.3.0
265 |
266 | 添加平滑滚动,优化超长距离平滑滚动。
267 |
268 | ```java
269 | // 平滑滚动到指定条目,通过 RecyclerView 调用
270 | mRecyclerView.smoothScrollToPosition(pos);
271 |
272 | // 平滑滚动到上一页
273 | mPagerLyoutManager.smoothPrePage(recyclerView);
274 | // 平滑滚动到下一页
275 | mPagerLyoutManager.smoothNextPage(recyclerView);
276 | // 平滑滚动到指定页
277 | mPagerLyoutManager.smoothScrollToPage(recyclerView, pageIndex);
278 | ```
279 |
280 | #### V2.2.4
281 |
282 | 优化滚动体验。
283 | 新增配置选项:
284 |
285 | ```java
286 | // 是否允许连续滚动
287 | boolean isAllowContinuousScroll();
288 | // 设置是否允许连续滚动
289 | void setAllowContinuousScroll(boolean allowContinuousScroll);
290 | ```
291 |
292 | #### v2.2.3
293 |
294 | 优化滚动过程中页面回调逻辑。
295 |
296 | #### v2.2.2
297 |
298 | 修复除零异常。
299 |
300 | #### v2.2.1
301 |
302 | 修复直接滚动到指定页面的Bug。
303 | 添加设置快速滚动阀值的方法 `snapHelper.setFlingThreshold(1000);`
304 | 修复不设置页面监听器会导致显示不正常的问题。
305 | 修复行列为1时滚动出现的问题。
306 |
307 | #### v2.2.0(该版本不稳定)
308 |
309 | 新增方法:
310 |
311 | ```java
312 | // 滚动到指定条目
313 | public void scrollToPosition(int position);
314 | // 滚动到指定页面
315 | public void scrollToPage(int pageIndex);
316 | // 上一页
317 | public void prePage();
318 | // 下一页
319 | public void nextPage();
320 | ```
321 |
322 | #### v2.1.1
323 |
324 | 修复使用Glide或者Fresco可能导致布局重复刷新的问题。
325 |
326 | (判断RecyclerView.State,如果结构状态没有变化则不重新布局)
327 |
328 | #### v2.1.0
329 |
330 | 修复从后向前滚动时内存占用变大的问题。
331 |
332 | (产生原因时先添加了新的View,最后再回收废弃的View,导致被回收的View一直难以被复用,持续占用内存,正确的应该是先将废弃的View放入回收区,这样新View创建时直接从回收区取View,就避免了回收区堆积大量的废弃View。)
333 |
334 | #### v2.0.0
335 |
336 | 统一包名(功能没有变化)。
337 |
338 | #### v1.3.1
339 |
340 | 修复特殊情况下删除条目导致显示不完整的问题。
341 |
342 | #### v1.3.0
343 |
344 | 支持动态切换滚动方向。
345 |
346 | #### v1.2.2
347 |
348 | 修复条目Margin问题。
349 |
350 | #### v1.2.1
351 |
352 | 修复慢速滚动偶尔会停止在两个页面之间的问题。
353 |
354 | #### v1.2.0
355 |
356 | 修复页面相关的Bug。
357 | 慢速滑动超多半个View时自动滚动到下一页。
358 | 调整布局逻辑和滚动辅助器逻辑。
359 | 移除部分无效方法。
360 | 代码结构调整。
361 | 支持按键翻页(TV)。
362 |
363 | **在TV上快速滚动时存在焦点问题:**
364 |
365 | 前置条件:竖向滚动,当下方有可以获取焦点控件。
366 | 预期结果:当有内容时向下滚动,当滚动到最后一页时,焦点才能移动到下面控件上。
367 | 实际结果:当慢速滚动时符合预期,当快速滚动时有可能在移动到最后一页之前就将焦点移动到下面的控件上。
368 |
369 | #### v1.1.3
370 |
371 | 修正页面变化回调位置,将回调修正到布局之后。
372 | 修正页面数据变化回调逻辑,允许多次回调同一个页面数据。
373 |
374 | #### v1.1.2
375 |
376 | 修复内容删除完毕时没有页面变化回调问题。
377 |
378 | #### v1.1.1
379 |
380 | 修复删除完毕最后一页作品,页面显示空白,滑动卡顿的bug。
381 |
382 | #### v1.1.0
383 |
384 | beta测试通过。
385 | 整理代码结构。
386 |
387 | #### v1.1.0-beta
388 |
389 | 添加在滚动过程中回调页码变化。
390 |
391 | #### v1.0.1
392 |
393 | 修复数据过少导致的数组越界异常。
394 |
395 | #### v1.0.0
396 |
397 | 移除部分日志。
398 | 代码结构整理。
399 |
400 | #### v1.0.0-beta1
401 |
402 | 修复错误回滚问题。`例如:本应滚动到第二页,却在滚动结束后回滚到第一页。`
403 |
404 | #### v1.0.0-beta
405 |
406 | 完成基本的分页功能。
407 | 完成分页滚动辅助工具。
408 | 滑动结束自动锁定到最近页面。
409 | 每次滚动仅允许滚动一页,防止连续滚动多页。
410 | 低内存占用。
411 | 支持水平滚动和垂直分页。
412 | 允许开关调试日志。
413 |
414 | ## 版权信息
415 |
416 | ```
417 | Copyright (c) 2017 GcsSloop
418 |
419 | Licensed under the Apache License, Version 2.0 (the "License");
420 | you may not use this file except in compliance with the License.
421 | You may obtain a copy of the License at
422 |
423 | http://www.apache.org/licenses/LICENSE-2.0
424 |
425 | Unless required by applicable law or agreed to in writing, software
426 | distributed under the License is distributed on an "AS IS" BASIS,
427 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
428 | See the License for the specific language governing permissions and
429 | limitations under the License.
430 | ```
431 |
432 |
--------------------------------------------------------------------------------