├── .gitignore
├── README.md
├── app
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── net
│ │ └── lucode
│ │ └── hackware
│ │ └── magicindicatordemo
│ │ ├── example
│ │ ├── BadgeTabExampleActivity.java
│ │ ├── CustomNavigatorExampleActivity.java
│ │ ├── DynamicTabExampleActivity.java
│ │ ├── ExampleMainActivity.java
│ │ ├── ExamplePagerAdapter.java
│ │ ├── FixedTabExampleActivity.java
│ │ ├── FragmentContainerExampleActivity.java
│ │ ├── LoadCustomLayoutExampleActivity.java
│ │ ├── NoTabOnlyIndicatorExampleActivity.java
│ │ ├── ScrollableTabExampleActivity.java
│ │ └── TestFragment.java
│ │ └── ext
│ │ ├── indicators
│ │ ├── CommonPagerIndicator.java
│ │ └── DotPagerIndicator.java
│ │ ├── navigator
│ │ ├── DummyCircleNavigator.java
│ │ └── ScaleCircleNavigator.java
│ │ └── titles
│ │ ├── ColorFlipPagerTitleView.java
│ │ └── ScaleTransitionPagerTitleView.java
│ └── res
│ ├── drawable
│ ├── round_indicator_bg.xml
│ ├── simple_count_badge_bg.xml
│ ├── simple_red_dot.xml
│ └── simple_splitter.xml
│ ├── layout
│ ├── activity_badge_tab_example_layout.xml
│ ├── activity_custom_navigator_example_layout.xml
│ ├── activity_dynamic_tab_example_layout.xml
│ ├── activity_example_main_layout.xml
│ ├── activity_fixed_tab_example_layout.xml
│ ├── activity_fragment_container_example_layout.xml
│ ├── activity_load_custom_layout_example.xml
│ ├── activity_no_tab_only_indicator_example_layout.xml
│ ├── activity_scrollable_indicator_example_layout.xml
│ ├── simple_count_badge_layout.xml
│ ├── simple_fragment_layout.xml
│ ├── simple_pager_title_layout.xml
│ └── simple_red_dot_badge_layout.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── demo.apk
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── local.properties
├── magicindicator.gif
├── magicindicator
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── net
│ │ └── lucode
│ │ └── hackware
│ │ └── magicindicator
│ │ ├── FragmentContainerHelper.java
│ │ ├── MagicIndicator.java
│ │ ├── NavigatorHelper.java
│ │ ├── ScrollState.java
│ │ ├── ViewPagerHelper.java
│ │ ├── abs
│ │ └── IPagerNavigator.java
│ │ └── buildins
│ │ ├── ArgbEvaluatorHolder.java
│ │ ├── UIUtil.java
│ │ ├── circlenavigator
│ │ └── CircleNavigator.java
│ │ └── commonnavigator
│ │ ├── CommonNavigator.java
│ │ ├── abs
│ │ ├── CommonNavigatorAdapter.java
│ │ ├── IMeasurablePagerTitleView.java
│ │ ├── IPagerIndicator.java
│ │ └── IPagerTitleView.java
│ │ ├── indicators
│ │ ├── BezierPagerIndicator.java
│ │ ├── LinePagerIndicator.java
│ │ ├── TestPagerIndicator.java
│ │ ├── TriangularPagerIndicator.java
│ │ └── WrapPagerIndicator.java
│ │ ├── model
│ │ └── PositionData.java
│ │ └── titles
│ │ ├── ClipPagerTitleView.java
│ │ ├── ColorTransitionPagerTitleView.java
│ │ ├── CommonPagerTitleView.java
│ │ ├── DummyPagerTitleView.java
│ │ ├── SimplePagerTitleView.java
│ │ └── badge
│ │ ├── BadgeAnchor.java
│ │ ├── BadgePagerTitleView.java
│ │ └── BadgeRule.java
│ └── res
│ └── layout
│ ├── pager_navigator_layout.xml
│ └── pager_navigator_layout_no_scroll.xml
├── magicindicatordemo.iml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/java,macos,android,androidstudio
3 | # Edit at https://www.gitignore.io/?templates=java,macos,android,androidstudio
4 |
5 | ### Android ###
6 | # Built application files
7 | *.apk
8 | *.ap_
9 | *.aab
10 |
11 | # Files for the ART/Dalvik VM
12 | *.dex
13 |
14 | # Java class files
15 | *.class
16 |
17 | # Generated files
18 | bin/
19 | gen/
20 | out/
21 |
22 | # Gradle files
23 | .gradle/
24 | build/
25 |
26 | # Local configuration file (sdk path, etc)
27 | local.properties
28 |
29 | # Proguard folder generated by Eclipse
30 | proguard/
31 |
32 | # Log Files
33 | *.log
34 |
35 | # Android Studio Navigation editor temp files
36 | .navigation/
37 |
38 | # Android Studio captures folder
39 | captures/
40 |
41 | # IntelliJ
42 | *.iml
43 | .idea/*
44 | .idea/workspace.xml
45 | .idea/tasks.xml
46 | .idea/gradle.xml
47 | .idea/assetWizardSettings.xml
48 | .idea/dictionaries
49 | .idea/libraries
50 | .idea/caches
51 | # Android Studio 3 in .gitignore file.
52 | .idea/caches/build_file_checksums.ser
53 | .idea/modules.xml
54 |
55 | # Keystore files
56 | # Uncomment the following lines if you do not want to check your keystore files in.
57 | #*.jks
58 | #*.keystore
59 |
60 | # External native build folder generated in Android Studio 2.2 and later
61 | .externalNativeBuild
62 |
63 | # Google Services (e.g. APIs or Firebase)
64 | # google-services.json
65 |
66 | # Freeline
67 | freeline.py
68 | freeline/
69 | freeline_project_description.json
70 |
71 | # fastlane
72 | fastlane/report.xml
73 | fastlane/Preview.html
74 | fastlane/screenshots
75 | fastlane/test_output
76 | fastlane/readme.md
77 |
78 | # Version control
79 | vcs.xml
80 |
81 | # lint
82 | lint/intermediates/
83 | lint/generated/
84 | lint/outputs/
85 | lint/tmp/
86 | # lint/reports/
87 |
88 | ### Android Patch ###
89 | gen-external-apklibs
90 | output.json
91 |
92 | ### AndroidStudio ###
93 | # Covers files to be ignored for android development using Android Studio.
94 |
95 | # Built application files
96 |
97 | # Files for the ART/Dalvik VM
98 |
99 | # Java class files
100 |
101 | # Generated files
102 |
103 | # Gradle files
104 | .gradle
105 |
106 | # Signing files
107 | .signing/
108 |
109 | # Local configuration file (sdk path, etc)
110 |
111 | # Proguard folder generated by Eclipse
112 |
113 | # Log Files
114 |
115 | # Android Studio
116 | /*/build/
117 | /*/local.properties
118 | /*/out
119 | /*/*/build
120 | /*/*/production
121 | *.ipr
122 | *~
123 | *.swp
124 |
125 | # Android Patch
126 |
127 | # External native build folder generated in Android Studio 2.2 and later
128 |
129 | # NDK
130 | obj/
131 |
132 | # IntelliJ IDEA
133 | *.iws
134 | /out/
135 |
136 | # User-specific configurations
137 | .idea/caches/
138 | .idea/libraries/
139 | .idea/shelf/
140 | .idea/.name
141 | .idea/compiler.xml
142 | .idea/copyright/profiles_settings.xml
143 | .idea/encodings.xml
144 | .idea/misc.xml
145 | .idea/scopes/scope_settings.xml
146 | .idea/vcs.xml
147 | .idea/jsLibraryMappings.xml
148 | .idea/datasources.xml
149 | .idea/dataSources.ids
150 | .idea/sqlDataSources.xml
151 | .idea/dynamic.xml
152 | .idea/uiDesigner.xml
153 |
154 | # OS-specific files
155 | .DS_Store
156 | .DS_Store?
157 | ._*
158 | .Spotlight-V100
159 | .Trashes
160 | ehthumbs.db
161 | Thumbs.db
162 |
163 | # Legacy Eclipse project files
164 | .classpath
165 | .project
166 | .cproject
167 | .settings/
168 |
169 | # Mobile Tools for Java (J2ME)
170 | .mtj.tmp/
171 |
172 | # Package Files #
173 | *.war
174 | *.ear
175 |
176 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
177 | hs_err_pid*
178 |
179 | ## Plugin-specific files:
180 |
181 | # mpeltonen/sbt-idea plugin
182 | .idea_modules/
183 |
184 | # JIRA plugin
185 | atlassian-ide-plugin.xml
186 |
187 | # Mongo Explorer plugin
188 | .idea/mongoSettings.xml
189 |
190 | # Crashlytics plugin (for Android Studio and IntelliJ)
191 | com_crashlytics_export_strings.xml
192 | crashlytics.properties
193 | crashlytics-build.properties
194 | fabric.properties
195 |
196 | ### AndroidStudio Patch ###
197 |
198 | !/gradle/wrapper/gradle-wrapper.jar
199 |
200 | ### Java ###
201 | # Compiled class file
202 |
203 | # Log file
204 |
205 | # BlueJ files
206 | *.ctxt
207 |
208 | # Mobile Tools for Java (J2ME)
209 |
210 | # Package Files #
211 | *.jar
212 | *.nar
213 | *.zip
214 | *.tar.gz
215 | *.rar
216 |
217 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
218 |
219 | ### macOS ###
220 | # General
221 | .AppleDouble
222 | .LSOverride
223 |
224 | # Icon must end with two \r
225 | Icon
226 |
227 | # Thumbnails
228 |
229 | # Files that might appear in the root of a volume
230 | .DocumentRevisions-V100
231 | .fseventsd
232 | .TemporaryItems
233 | .VolumeIcon.icns
234 | .com.apple.timemachine.donotpresent
235 |
236 | # Directories potentially created on remote AFP share
237 | .AppleDB
238 | .AppleDesktop
239 | Network Trash Folder
240 | Temporary Items
241 | .apdisk
242 |
243 | # End of https://www.gitignore.io/api/java,macos,android,androidstudio
244 | .idea
245 | app.iml
246 | magicindicator.iml
247 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MagicIndicator
2 |
3 | A powerful, customizable and extensible ViewPager indicator framework. As the best alternative of ViewPagerIndicator, TabLayout and PagerSlidingTabStrip.
4 |
5 | [Flutter_ConstraintLayout](https://github.com/hackware1993/Flutter_ConstraintLayout) Another very good open source project of mine.
6 |
7 | **I have developed the world's fastest general purpose sorting algorithm, which is on average 3 times faster than Quicksort and up to 20 times faster**, [ChenSort](https://github.com/hackware1993/ChenSort)
8 |
9 | [](https://jitpack.io/#hackware1993/MagicIndicator)
10 | [](https://android-arsenal.com/details/1/4252)
11 | [](https://www.codewake.com/p/magicindicator)
12 |
13 | 
14 |
15 | # Usage
16 |
17 | Simple steps, you can integrate **MagicIndicator**:
18 |
19 | 1. checkout out **MagicIndicator**, which contains source code and demo
20 | 2. import module **magicindicator** and add dependency:
21 |
22 | ```groovy
23 | implementation project(':magicindicator')
24 | ```
25 |
26 | **or**
27 |
28 | ```groovy
29 | repositories {
30 | ...
31 | maven {
32 | url "https://jitpack.io"
33 | }
34 | }
35 |
36 | dependencies {
37 | ...
38 | implementation 'com.github.hackware1993:MagicIndicator:1.6.0' // for support lib
39 | implementation 'com.github.hackware1993:MagicIndicator:1.7.0' // for androidx
40 | }
41 | ```
42 |
43 | 3. add **MagicIndicator** to your layout xml:
44 |
45 | ```xml
46 |
47 |
53 |
54 |
58 |
59 |
64 |
65 |
66 | ```
67 |
68 | 4. find **MagicIndicator** through code, initialize it:
69 |
70 | ```java
71 | MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator);
72 | CommonNavigator commonNavigator = new CommonNavigator(this);
73 | commonNavigator.setAdapter(new CommonNavigatorAdapter() {
74 |
75 | @Override
76 | public int getCount() {
77 | return mTitleDataList == null ? 0 : mTitleDataList.size();
78 | }
79 |
80 | @Override
81 | public IPagerTitleView getTitleView(Context context, final int index) {
82 | ColorTransitionPagerTitleView colorTransitionPagerTitleView = new ColorTransitionPagerTitleView(context);
83 | colorTransitionPagerTitleView.setNormalColor(Color.GRAY);
84 | colorTransitionPagerTitleView.setSelectedColor(Color.BLACK);
85 | colorTransitionPagerTitleView.setText(mTitleDataList.get(index));
86 | colorTransitionPagerTitleView.setOnClickListener(new View.OnClickListener() {
87 | @Override
88 | public void onClick(View view) {
89 | mViewPager.setCurrentItem(index);
90 | }
91 | });
92 | return colorTransitionPagerTitleView;
93 | }
94 |
95 | @Override
96 | public IPagerIndicator getIndicator(Context context) {
97 | LinePagerIndicator indicator = new LinePagerIndicator(context);
98 | indicator.setMode(LinePagerIndicator.MODE_WRAP_CONTENT);
99 | return indicator;
100 | }
101 | });
102 | magicIndicator.setNavigator(commonNavigator);
103 | ```
104 |
105 | 5. work with ViewPager:
106 |
107 | ```java
108 | ViewPagerHelper.bind(magicIndicator, mViewPager);
109 | ```
110 |
111 | **or**
112 |
113 | work with Fragment Container(switch Fragment by hide()、show()):
114 | ```java
115 | mFramentContainerHelper = new FragmentContainerHelper(magicIndicator);
116 |
117 | // ...
118 |
119 | mFragmentContainerHelper.handlePageSelected(pageIndex); // invoke when switch Fragment
120 | ```
121 |
122 | # Extend
123 |
124 | **MagicIndicator** can be easily extended:
125 |
126 | 1. implement **IPagerTitleView** to customize tab:
127 |
128 | ```java
129 | public class MyPagerTitleView extends View implements IPagerTitleView {
130 |
131 | public MyPagerTitleView(Context context) {
132 | super(context);
133 | }
134 |
135 | @Override
136 | public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
137 | }
138 |
139 | @Override
140 | public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
141 | }
142 |
143 | @Override
144 | public void onSelected(int index, int totalCount) {
145 | }
146 |
147 | @Override
148 | public void onDeselected(int index, int totalCount) {
149 | }
150 | }
151 | ```
152 |
153 | 2. implement **IPagerIndicator** to customize indicator:
154 |
155 | ```java
156 | public class MyPagerIndicator extends View implements IPagerIndicator {
157 |
158 | public MyPagerIndicator(Context context) {
159 | super(context);
160 | }
161 |
162 | @Override
163 | public void onPageSelected(int position) {
164 | }
165 |
166 | @Override
167 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
168 | }
169 |
170 | @Override
171 | public void onPageScrollStateChanged(int state) {
172 | }
173 |
174 | @Override
175 | public void onPositionDataProvide(List dataList) {
176 | }
177 | }
178 | ```
179 |
180 | 3. use **CommonPagerTitleView** to load custom layout xml.
181 |
182 | Now, enjoy yourself!
183 |
184 | See extensions in [*app/src/main/java/net/lucode/hackware/magicindicatordemo/ext*](https://github.com/hackware1993/MagicIndicator/tree/master/app/src/main/java/net/lucode/hackware/magicindicatordemo/ext),more extensions adding...
185 |
186 | # Who developed?
187 |
188 | hackware1993@gmail.com
189 |
190 | cfb1993@163.com
191 |
192 | Q&A
193 |
194 | An intermittent perfectionist.
195 |
196 | Visit [My Blog](http://hackware.lucode.net) for more articles about MagicIndicator.
197 |
198 | 订阅我的微信公众号以及时获取 MagicIndicator 的最新动态。后续也会分享一些高质量的、独特的、有思想的 Flutter 和 Android 技术文章。
199 |
200 | 
201 |
202 | # License
203 |
204 | ```
205 | MIT License
206 |
207 | Copyright (c) 2016 hackware1993
208 |
209 | Permission is hereby granted, free of charge, to any person obtaining a copy
210 | of this software and associated documentation files (the "Software"), to deal
211 | in the Software without restriction, including without limitation the rights
212 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
213 | copies of the Software, and to permit persons to whom the Software is
214 | furnished to do so, subject to the following conditions:
215 |
216 | The above copyright notice and this permission notice shall be included in all
217 | copies or substantial portions of the Software.
218 |
219 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
220 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
221 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
222 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
223 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
224 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
225 | SOFTWARE.
226 | ```
227 |
228 | # More
229 |
230 | Have seen here, give a star?(都看到这儿了,何不给个...,哎,别走啊,star还没...)
231 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | buildToolsVersion "28.0.3"
6 |
7 | defaultConfig {
8 | applicationId "net.lucode.hackware.magicindicatordemo"
9 | minSdkVersion 14
10 | targetSdkVersion 28
11 | }
12 | buildTypes {
13 | release {
14 | minifyEnabled false
15 | }
16 | }
17 | }
18 |
19 | dependencies {
20 | implementation fileTree(dir: 'libs', include: ['*.jar'])
21 | implementation project(':magicindicator')
22 | implementation 'androidx.appcompat:appcompat:1.2.0'
23 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
20 |
21 |
24 |
25 |
28 |
29 |
32 |
33 |
36 |
37 |
40 |
41 |
44 |
45 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/app/src/main/java/net/lucode/hackware/magicindicatordemo/example/CustomNavigatorExampleActivity.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicatordemo.example;
2 |
3 | import android.graphics.Color;
4 | import android.os.Bundle;
5 |
6 | import androidx.appcompat.app.AppCompatActivity;
7 | import androidx.viewpager.widget.ViewPager;
8 |
9 | import net.lucode.hackware.magicindicator.MagicIndicator;
10 | import net.lucode.hackware.magicindicator.ViewPagerHelper;
11 | import net.lucode.hackware.magicindicator.buildins.circlenavigator.CircleNavigator;
12 | import net.lucode.hackware.magicindicatordemo.R;
13 | import net.lucode.hackware.magicindicatordemo.ext.navigator.ScaleCircleNavigator;
14 |
15 | import java.util.Arrays;
16 | import java.util.List;
17 |
18 | public class CustomNavigatorExampleActivity extends AppCompatActivity {
19 | private static final String[] CHANNELS = new String[]{"CUPCAKE", "DONUT", "ECLAIR", "GINGERBREAD", "HONEYCOMB", "ICE_CREAM_SANDWICH", "JELLY_BEAN", "KITKAT", "LOLLIPOP", "M", "NOUGAT"};
20 | private List mDataList = Arrays.asList(CHANNELS);
21 | private ExamplePagerAdapter mExamplePagerAdapter = new ExamplePagerAdapter(mDataList);
22 |
23 | private ViewPager mViewPager;
24 |
25 | @Override
26 | protected void onCreate(Bundle savedInstanceState) {
27 | super.onCreate(savedInstanceState);
28 | setContentView(R.layout.activity_custom_navigator_example_layout);
29 |
30 | mViewPager = (ViewPager) findViewById(R.id.view_pager);
31 | mViewPager.setAdapter(mExamplePagerAdapter);
32 |
33 | initMagicIndicator1();
34 | initMagicIndicator2();
35 | initMagicIndicator3();
36 | }
37 |
38 | private void initMagicIndicator1() {
39 | MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator1);
40 | CircleNavigator circleNavigator = new CircleNavigator(this);
41 | circleNavigator.setCircleCount(CHANNELS.length);
42 | circleNavigator.setCircleColor(Color.RED);
43 | circleNavigator.setCircleClickListener(new CircleNavigator.OnCircleClickListener() {
44 | @Override
45 | public void onClick(int index) {
46 | mViewPager.setCurrentItem(index);
47 | }
48 | });
49 | magicIndicator.setNavigator(circleNavigator);
50 | ViewPagerHelper.bind(magicIndicator, mViewPager);
51 | }
52 |
53 | private void initMagicIndicator2() {
54 | MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator2);
55 | CircleNavigator circleNavigator = new CircleNavigator(this);
56 | circleNavigator.setFollowTouch(false);
57 | circleNavigator.setCircleCount(CHANNELS.length);
58 | circleNavigator.setCircleColor(Color.RED);
59 | circleNavigator.setCircleClickListener(new CircleNavigator.OnCircleClickListener() {
60 | @Override
61 | public void onClick(int index) {
62 | mViewPager.setCurrentItem(index);
63 | }
64 | });
65 | magicIndicator.setNavigator(circleNavigator);
66 | ViewPagerHelper.bind(magicIndicator, mViewPager);
67 | }
68 |
69 | private void initMagicIndicator3() {
70 | MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator3);
71 | ScaleCircleNavigator scaleCircleNavigator = new ScaleCircleNavigator(this);
72 | scaleCircleNavigator.setCircleCount(CHANNELS.length);
73 | scaleCircleNavigator.setNormalCircleColor(Color.LTGRAY);
74 | scaleCircleNavigator.setSelectedCircleColor(Color.DKGRAY);
75 | scaleCircleNavigator.setCircleClickListener(new ScaleCircleNavigator.OnCircleClickListener() {
76 | @Override
77 | public void onClick(int index) {
78 | mViewPager.setCurrentItem(index);
79 | }
80 | });
81 | magicIndicator.setNavigator(scaleCircleNavigator);
82 | ViewPagerHelper.bind(magicIndicator, mViewPager);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/net/lucode/hackware/magicindicatordemo/example/DynamicTabExampleActivity.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicatordemo.example;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.os.Bundle;
6 | import android.view.View;
7 | import android.widget.Toast;
8 |
9 | import androidx.appcompat.app.AppCompatActivity;
10 | import androidx.viewpager.widget.ViewPager;
11 |
12 | import net.lucode.hackware.magicindicator.MagicIndicator;
13 | import net.lucode.hackware.magicindicator.ViewPagerHelper;
14 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator;
15 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.CommonNavigatorAdapter;
16 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
17 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView;
18 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.ClipPagerTitleView;
19 | import net.lucode.hackware.magicindicatordemo.R;
20 |
21 | import java.util.ArrayList;
22 | import java.util.Arrays;
23 | import java.util.List;
24 | import java.util.Random;
25 |
26 | public class DynamicTabExampleActivity extends AppCompatActivity {
27 | private static final String[] CHANNELS = new String[]{"CUPCAKE", "DONUT", "ECLAIR", "GINGERBREAD", "HONEYCOMB", "ICE_CREAM_SANDWICH", "JELLY_BEAN", "KITKAT", "LOLLIPOP", "M", "NOUGAT"};
28 | private List mDataList = new ArrayList(Arrays.asList(CHANNELS));
29 | private ExamplePagerAdapter mExamplePagerAdapter = new ExamplePagerAdapter(mDataList);
30 |
31 | private ViewPager mViewPager;
32 | private MagicIndicator mMagicIndicator;
33 | private CommonNavigator mCommonNavigator;
34 |
35 | private Toast mToast;
36 |
37 | @Override
38 | protected void onCreate(Bundle savedInstanceState) {
39 | super.onCreate(savedInstanceState);
40 | setContentView(R.layout.activity_dynamic_tab_example_layout);
41 |
42 | mViewPager = (ViewPager) findViewById(R.id.view_pager);
43 | mViewPager.setAdapter(mExamplePagerAdapter);
44 |
45 | mMagicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator1);
46 | mMagicIndicator.setBackgroundColor(Color.parseColor("#d43d3d"));
47 | mCommonNavigator = new CommonNavigator(this);
48 | mCommonNavigator.setSkimOver(true);
49 | mCommonNavigator.setAdapter(new CommonNavigatorAdapter() {
50 |
51 | @Override
52 | public int getCount() {
53 | return mDataList.size();
54 | }
55 |
56 | @Override
57 | public IPagerTitleView getTitleView(Context context, final int index) {
58 | ClipPagerTitleView clipPagerTitleView = new ClipPagerTitleView(context);
59 | clipPagerTitleView.setText(mDataList.get(index));
60 | clipPagerTitleView.setTextColor(Color.parseColor("#f2c4c4"));
61 | clipPagerTitleView.setClipColor(Color.WHITE);
62 | clipPagerTitleView.setOnClickListener(new View.OnClickListener() {
63 | @Override
64 | public void onClick(View v) {
65 | mViewPager.setCurrentItem(index);
66 | }
67 | });
68 | return clipPagerTitleView;
69 | }
70 |
71 | @Override
72 | public IPagerIndicator getIndicator(Context context) {
73 | return null;
74 | }
75 | });
76 | mMagicIndicator.setNavigator(mCommonNavigator);
77 | ViewPagerHelper.bind(mMagicIndicator, mViewPager);
78 |
79 | mToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
80 | }
81 |
82 | public void randomPage(View view) {
83 | mDataList.clear();
84 | int total = new Random().nextInt(CHANNELS.length);
85 | for (int i = 0; i <= total; i++) {
86 | mDataList.add(CHANNELS[i]);
87 | }
88 |
89 | mCommonNavigator.notifyDataSetChanged(); // must call firstly
90 | mExamplePagerAdapter.notifyDataSetChanged();
91 |
92 | mToast.setText("" + mDataList.size() + " page");
93 | mToast.show();
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/java/net/lucode/hackware/magicindicatordemo/example/ExampleMainActivity.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicatordemo.example;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.view.View;
6 |
7 | import androidx.appcompat.app.AppCompatActivity;
8 |
9 | import net.lucode.hackware.magicindicatordemo.R;
10 |
11 | public class ExampleMainActivity extends AppCompatActivity {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_example_main_layout);
17 | }
18 |
19 | public void onClick(View view) {
20 | switch (view.getId()) {
21 | case R.id.scrollable_tab:
22 | startActivity(new Intent(this, ScrollableTabExampleActivity.class));
23 | break;
24 | case R.id.fixed_tab:
25 | startActivity(new Intent(this, FixedTabExampleActivity.class));
26 | break;
27 | case R.id.dynamic_tab:
28 | startActivity(new Intent(this, DynamicTabExampleActivity.class));
29 | break;
30 | case R.id.no_tab_only_indicator:
31 | startActivity(new Intent(this, NoTabOnlyIndicatorExampleActivity.class));
32 | break;
33 | case R.id.tab_with_badge_view:
34 | startActivity(new Intent(this, BadgeTabExampleActivity.class));
35 | break;
36 | case R.id.work_with_fragment_container:
37 | startActivity(new Intent(this, FragmentContainerExampleActivity.class));
38 | break;
39 | case R.id.load_custom_layout:
40 | startActivity(new Intent(this, LoadCustomLayoutExampleActivity.class));
41 | break;
42 | case R.id.custom_navigator:
43 | startActivity(new Intent(this, CustomNavigatorExampleActivity.class));
44 | break;
45 | default:
46 | break;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/net/lucode/hackware/magicindicatordemo/example/ExamplePagerAdapter.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicatordemo.example;
2 |
3 | import android.graphics.Color;
4 | import android.view.Gravity;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.TextView;
8 |
9 | import androidx.viewpager.widget.PagerAdapter;
10 |
11 | import java.util.List;
12 |
13 | /**
14 | * Created by hackware on 2016/9/10.
15 | */
16 |
17 | public class ExamplePagerAdapter extends PagerAdapter {
18 | private List mDataList;
19 |
20 | public ExamplePagerAdapter(List dataList) {
21 | mDataList = dataList;
22 | }
23 |
24 | @Override
25 | public int getCount() {
26 | return mDataList == null ? 0 : mDataList.size();
27 | }
28 |
29 | @Override
30 | public boolean isViewFromObject(View view, Object object) {
31 | return view == object;
32 | }
33 |
34 | @Override
35 | public Object instantiateItem(ViewGroup container, int position) {
36 | TextView textView = new TextView(container.getContext());
37 | textView.setText(mDataList.get(position));
38 | textView.setGravity(Gravity.CENTER);
39 | textView.setTextColor(Color.BLACK);
40 | textView.setTextSize(24);
41 | container.addView(textView);
42 | return textView;
43 | }
44 |
45 | @Override
46 | public void destroyItem(ViewGroup container, int position, Object object) {
47 | container.removeView((View) object);
48 | }
49 |
50 | @Override
51 | public int getItemPosition(Object object) {
52 | TextView textView = (TextView) object;
53 | String text = textView.getText().toString();
54 | int index = mDataList.indexOf(text);
55 | if (index >= 0) {
56 | return index;
57 | }
58 | return POSITION_NONE;
59 | }
60 |
61 | @Override
62 | public CharSequence getPageTitle(int position) {
63 | return mDataList.get(position);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/net/lucode/hackware/magicindicatordemo/example/FixedTabExampleActivity.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicatordemo.example;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.graphics.drawable.ColorDrawable;
6 | import android.os.Bundle;
7 | import android.view.View;
8 | import android.view.animation.AccelerateInterpolator;
9 | import android.view.animation.DecelerateInterpolator;
10 | import android.view.animation.OvershootInterpolator;
11 | import android.widget.LinearLayout;
12 |
13 | import androidx.appcompat.app.AppCompatActivity;
14 | import androidx.viewpager.widget.ViewPager;
15 |
16 | import net.lucode.hackware.magicindicator.FragmentContainerHelper;
17 | import net.lucode.hackware.magicindicator.MagicIndicator;
18 | import net.lucode.hackware.magicindicator.ViewPagerHelper;
19 | import net.lucode.hackware.magicindicator.buildins.UIUtil;
20 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator;
21 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.CommonNavigatorAdapter;
22 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
23 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView;
24 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators.LinePagerIndicator;
25 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.ClipPagerTitleView;
26 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.ColorTransitionPagerTitleView;
27 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.SimplePagerTitleView;
28 | import net.lucode.hackware.magicindicatordemo.R;
29 | import net.lucode.hackware.magicindicatordemo.ext.titles.ScaleTransitionPagerTitleView;
30 |
31 | import java.util.Arrays;
32 | import java.util.List;
33 |
34 | public class FixedTabExampleActivity extends AppCompatActivity {
35 | private static final String[] CHANNELS = new String[]{"KITKAT", "NOUGAT", "DONUT"};
36 | private List mDataList = Arrays.asList(CHANNELS);
37 | private ExamplePagerAdapter mExamplePagerAdapter = new ExamplePagerAdapter(mDataList);
38 |
39 | private ViewPager mViewPager;
40 |
41 | @Override
42 | protected void onCreate(Bundle savedInstanceState) {
43 | super.onCreate(savedInstanceState);
44 | setContentView(R.layout.activity_fixed_tab_example_layout);
45 |
46 | mViewPager = (ViewPager) findViewById(R.id.view_pager);
47 | mViewPager.setAdapter(mExamplePagerAdapter);
48 |
49 | initMagicIndicator1();
50 | initMagicIndicator2();
51 | initMagicIndicator3();
52 | initMagicIndicator4();
53 | }
54 |
55 | private void initMagicIndicator1() {
56 | MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator1);
57 | CommonNavigator commonNavigator = new CommonNavigator(this);
58 | commonNavigator.setAdapter(new CommonNavigatorAdapter() {
59 | @Override
60 | public int getCount() {
61 | return mDataList == null ? 0 : mDataList.size();
62 | }
63 |
64 | @Override
65 | public IPagerTitleView getTitleView(Context context, final int index) {
66 | SimplePagerTitleView simplePagerTitleView = new ColorTransitionPagerTitleView(context);
67 | simplePagerTitleView.setText(mDataList.get(index));
68 | simplePagerTitleView.setNormalColor(Color.parseColor("#88ffffff"));
69 | simplePagerTitleView.setSelectedColor(Color.WHITE);
70 | simplePagerTitleView.setOnClickListener(new View.OnClickListener() {
71 | @Override
72 | public void onClick(View v) {
73 | mViewPager.setCurrentItem(index);
74 | }
75 | });
76 | return simplePagerTitleView;
77 | }
78 |
79 | @Override
80 | public IPagerIndicator getIndicator(Context context) {
81 | LinePagerIndicator indicator = new LinePagerIndicator(context);
82 | indicator.setColors(Color.parseColor("#40c4ff"));
83 | return indicator;
84 | }
85 | });
86 | magicIndicator.setNavigator(commonNavigator);
87 | LinearLayout titleContainer = commonNavigator.getTitleContainer(); // must after setNavigator
88 | titleContainer.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
89 | titleContainer.setDividerPadding(UIUtil.dip2px(this, 15));
90 | titleContainer.setDividerDrawable(getResources().getDrawable(R.drawable.simple_splitter));
91 | ViewPagerHelper.bind(magicIndicator, mViewPager);
92 | }
93 |
94 | private void initMagicIndicator2() {
95 | MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator2);
96 | magicIndicator.setBackgroundColor(Color.WHITE);
97 | CommonNavigator commonNavigator = new CommonNavigator(this);
98 | commonNavigator.setAdjustMode(true);
99 | commonNavigator.setAdapter(new CommonNavigatorAdapter() {
100 | @Override
101 | public int getCount() {
102 | return mDataList == null ? 0 : mDataList.size();
103 | }
104 |
105 | @Override
106 | public IPagerTitleView getTitleView(Context context, final int index) {
107 | SimplePagerTitleView simplePagerTitleView = new ScaleTransitionPagerTitleView(context);
108 | simplePagerTitleView.setText(mDataList.get(index));
109 | simplePagerTitleView.setTextSize(18);
110 | simplePagerTitleView.setNormalColor(Color.parseColor("#616161"));
111 | simplePagerTitleView.setSelectedColor(Color.parseColor("#f57c00"));
112 | simplePagerTitleView.setOnClickListener(new View.OnClickListener() {
113 | @Override
114 | public void onClick(View v) {
115 | mViewPager.setCurrentItem(index);
116 | }
117 | });
118 | return simplePagerTitleView;
119 | }
120 |
121 | @Override
122 | public IPagerIndicator getIndicator(Context context) {
123 | LinePagerIndicator indicator = new LinePagerIndicator(context);
124 | indicator.setStartInterpolator(new AccelerateInterpolator());
125 | indicator.setEndInterpolator(new DecelerateInterpolator(1.6f));
126 | indicator.setYOffset(UIUtil.dip2px(context, 39));
127 | indicator.setLineHeight(UIUtil.dip2px(context, 1));
128 | indicator.setColors(Color.parseColor("#f57c00"));
129 | return indicator;
130 | }
131 |
132 | @Override
133 | public float getTitleWeight(Context context, int index) {
134 | if (index == 0) {
135 | return 2.0f;
136 | } else if (index == 1) {
137 | return 1.2f;
138 | } else {
139 | return 1.0f;
140 | }
141 | }
142 | });
143 | magicIndicator.setNavigator(commonNavigator);
144 | ViewPagerHelper.bind(magicIndicator, mViewPager);
145 | }
146 |
147 | private void initMagicIndicator3() {
148 | MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator3);
149 | magicIndicator.setBackgroundResource(R.drawable.round_indicator_bg);
150 | CommonNavigator commonNavigator = new CommonNavigator(this);
151 | commonNavigator.setAdapter(new CommonNavigatorAdapter() {
152 | @Override
153 | public int getCount() {
154 | return mDataList == null ? 0 : mDataList.size();
155 | }
156 |
157 | @Override
158 | public IPagerTitleView getTitleView(Context context, final int index) {
159 | ClipPagerTitleView clipPagerTitleView = new ClipPagerTitleView(context);
160 | clipPagerTitleView.setText(mDataList.get(index));
161 | clipPagerTitleView.setTextColor(Color.parseColor("#e94220"));
162 | clipPagerTitleView.setClipColor(Color.WHITE);
163 | clipPagerTitleView.setOnClickListener(new View.OnClickListener() {
164 | @Override
165 | public void onClick(View v) {
166 | mViewPager.setCurrentItem(index);
167 | }
168 | });
169 | return clipPagerTitleView;
170 | }
171 |
172 | @Override
173 | public IPagerIndicator getIndicator(Context context) {
174 | LinePagerIndicator indicator = new LinePagerIndicator(context);
175 | float navigatorHeight = context.getResources().getDimension(R.dimen.common_navigator_height);
176 | float borderWidth = UIUtil.dip2px(context, 1);
177 | float lineHeight = navigatorHeight - 2 * borderWidth;
178 | indicator.setLineHeight(lineHeight);
179 | indicator.setRoundRadius(lineHeight / 2);
180 | indicator.setYOffset(borderWidth);
181 | indicator.setColors(Color.parseColor("#bc2a2a"));
182 | return indicator;
183 | }
184 | });
185 | magicIndicator.setNavigator(commonNavigator);
186 | ViewPagerHelper.bind(magicIndicator, mViewPager);
187 | }
188 |
189 | private void initMagicIndicator4() {
190 | MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator4);
191 | CommonNavigator commonNavigator = new CommonNavigator(this);
192 | commonNavigator.setAdapter(new CommonNavigatorAdapter() {
193 |
194 | @Override
195 | public int getCount() {
196 | return mDataList.size();
197 | }
198 |
199 | @Override
200 | public IPagerTitleView getTitleView(Context context, final int index) {
201 | SimplePagerTitleView simplePagerTitleView = new ColorTransitionPagerTitleView(context);
202 | simplePagerTitleView.setNormalColor(Color.GRAY);
203 | simplePagerTitleView.setSelectedColor(Color.WHITE);
204 | simplePagerTitleView.setText(mDataList.get(index));
205 | simplePagerTitleView.setOnClickListener(new View.OnClickListener() {
206 | @Override
207 | public void onClick(View v) {
208 | mViewPager.setCurrentItem(index);
209 | }
210 | });
211 | return simplePagerTitleView;
212 | }
213 |
214 | @Override
215 | public IPagerIndicator getIndicator(Context context) {
216 | LinePagerIndicator linePagerIndicator = new LinePagerIndicator(context);
217 | linePagerIndicator.setMode(LinePagerIndicator.MODE_EXACTLY);
218 | linePagerIndicator.setLineWidth(UIUtil.dip2px(context, 10));
219 | linePagerIndicator.setColors(Color.WHITE);
220 | return linePagerIndicator;
221 | }
222 | });
223 | magicIndicator.setNavigator(commonNavigator);
224 | LinearLayout titleContainer = commonNavigator.getTitleContainer(); // must after setNavigator
225 | titleContainer.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
226 | titleContainer.setDividerDrawable(new ColorDrawable() {
227 | @Override
228 | public int getIntrinsicWidth() {
229 | return UIUtil.dip2px(FixedTabExampleActivity.this, 15);
230 | }
231 | });
232 |
233 | final FragmentContainerHelper fragmentContainerHelper = new FragmentContainerHelper(magicIndicator);
234 | fragmentContainerHelper.setInterpolator(new OvershootInterpolator(2.0f));
235 | fragmentContainerHelper.setDuration(300);
236 | mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
237 | @Override
238 | public void onPageSelected(int position) {
239 | fragmentContainerHelper.handlePageSelected(position);
240 | }
241 | });
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/app/src/main/java/net/lucode/hackware/magicindicatordemo/example/FragmentContainerExampleActivity.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicatordemo.example;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.os.Bundle;
6 | import android.view.View;
7 |
8 | import androidx.appcompat.app.AppCompatActivity;
9 | import androidx.fragment.app.Fragment;
10 | import androidx.fragment.app.FragmentManager;
11 | import androidx.fragment.app.FragmentTransaction;
12 |
13 | import net.lucode.hackware.magicindicator.FragmentContainerHelper;
14 | import net.lucode.hackware.magicindicator.MagicIndicator;
15 | import net.lucode.hackware.magicindicator.buildins.UIUtil;
16 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator;
17 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.CommonNavigatorAdapter;
18 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
19 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView;
20 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators.LinePagerIndicator;
21 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.ClipPagerTitleView;
22 | import net.lucode.hackware.magicindicatordemo.R;
23 |
24 | import java.util.ArrayList;
25 | import java.util.List;
26 |
27 | public class FragmentContainerExampleActivity extends AppCompatActivity {
28 | private static final String[] CHANNELS = new String[]{"KITKAT", "NOUGAT", "DONUT"};
29 | private List mFragments = new ArrayList();
30 | private FragmentContainerHelper mFragmentContainerHelper = new FragmentContainerHelper();
31 |
32 | @Override
33 | protected void onCreate(Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 | setContentView(R.layout.activity_fragment_container_example_layout);
36 |
37 | initFragments();
38 | initMagicIndicator1();
39 |
40 | mFragmentContainerHelper.handlePageSelected(1, false);
41 | switchPages(1);
42 | }
43 |
44 | private void switchPages(int index) {
45 | FragmentManager fragmentManager = getSupportFragmentManager();
46 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
47 | Fragment fragment;
48 | for (int i = 0, j = mFragments.size(); i < j; i++) {
49 | if (i == index) {
50 | continue;
51 | }
52 | fragment = mFragments.get(i);
53 | if (fragment.isAdded()) {
54 | fragmentTransaction.hide(fragment);
55 | }
56 | }
57 | fragment = mFragments.get(index);
58 | if (fragment.isAdded()) {
59 | fragmentTransaction.show(fragment);
60 | } else {
61 | fragmentTransaction.add(R.id.fragment_container, fragment);
62 | }
63 | fragmentTransaction.commitAllowingStateLoss();
64 | }
65 |
66 | private void initFragments() {
67 | for (int i = 0; i < CHANNELS.length; i++) {
68 | TestFragment testFragment = new TestFragment();
69 | Bundle bundle = new Bundle();
70 | bundle.putString(TestFragment.EXTRA_TEXT, CHANNELS[i]);
71 | testFragment.setArguments(bundle);
72 | mFragments.add(testFragment);
73 | }
74 | }
75 |
76 | private void initMagicIndicator1() {
77 | MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator1);
78 | magicIndicator.setBackgroundResource(R.drawable.round_indicator_bg);
79 | CommonNavigator commonNavigator = new CommonNavigator(this);
80 | commonNavigator.setAdapter(new CommonNavigatorAdapter() {
81 | @Override
82 | public int getCount() {
83 | return CHANNELS.length;
84 | }
85 |
86 | @Override
87 | public IPagerTitleView getTitleView(Context context, final int index) {
88 | ClipPagerTitleView clipPagerTitleView = new ClipPagerTitleView(context);
89 | clipPagerTitleView.setText(CHANNELS[index]);
90 | clipPagerTitleView.setTextColor(Color.parseColor("#e94220"));
91 | clipPagerTitleView.setClipColor(Color.WHITE);
92 | clipPagerTitleView.setOnClickListener(new View.OnClickListener() {
93 | @Override
94 | public void onClick(View v) {
95 | mFragmentContainerHelper.handlePageSelected(index);
96 | switchPages(index);
97 | }
98 | });
99 | return clipPagerTitleView;
100 | }
101 |
102 | @Override
103 | public IPagerIndicator getIndicator(Context context) {
104 | LinePagerIndicator indicator = new LinePagerIndicator(context);
105 | float navigatorHeight = context.getResources().getDimension(R.dimen.common_navigator_height);
106 | float borderWidth = UIUtil.dip2px(context, 1);
107 | float lineHeight = navigatorHeight - 2 * borderWidth;
108 | indicator.setLineHeight(lineHeight);
109 | indicator.setRoundRadius(lineHeight / 2);
110 | indicator.setYOffset(borderWidth);
111 | indicator.setColors(Color.parseColor("#bc2a2a"));
112 | return indicator;
113 | }
114 | });
115 | magicIndicator.setNavigator(commonNavigator);
116 | mFragmentContainerHelper.attachMagicIndicator(magicIndicator);
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/app/src/main/java/net/lucode/hackware/magicindicatordemo/example/LoadCustomLayoutExampleActivity.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicatordemo.example;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.os.Bundle;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 |
11 | import androidx.appcompat.app.AppCompatActivity;
12 | import androidx.viewpager.widget.ViewPager;
13 |
14 | import net.lucode.hackware.magicindicator.MagicIndicator;
15 | import net.lucode.hackware.magicindicator.ViewPagerHelper;
16 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator;
17 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.CommonNavigatorAdapter;
18 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
19 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView;
20 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.CommonPagerTitleView;
21 | import net.lucode.hackware.magicindicatordemo.R;
22 |
23 | import java.util.Arrays;
24 | import java.util.List;
25 |
26 | public class LoadCustomLayoutExampleActivity extends AppCompatActivity {
27 | private static final String[] CHANNELS = new String[]{"NOUGAT", "DONUT", "ECLAIR", "KITKAT"};
28 | private List mDataList = Arrays.asList(CHANNELS);
29 | private ExamplePagerAdapter mExamplePagerAdapter = new ExamplePagerAdapter(mDataList);
30 |
31 | private ViewPager mViewPager;
32 |
33 | @Override
34 | protected void onCreate(Bundle savedInstanceState) {
35 | super.onCreate(savedInstanceState);
36 | setContentView(R.layout.activity_load_custom_layout_example);
37 |
38 | mViewPager = (ViewPager) findViewById(R.id.view_pager);
39 | mViewPager.setAdapter(mExamplePagerAdapter);
40 |
41 | initMagicIndicator1();
42 | }
43 |
44 | private void initMagicIndicator1() {
45 | MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator1);
46 | magicIndicator.setBackgroundColor(Color.BLACK);
47 | CommonNavigator commonNavigator = new CommonNavigator(this);
48 | commonNavigator.setAdjustMode(true);
49 | commonNavigator.setAdapter(new CommonNavigatorAdapter() {
50 |
51 | @Override
52 | public int getCount() {
53 | return mDataList.size();
54 | }
55 |
56 | @Override
57 | public IPagerTitleView getTitleView(Context context, final int index) {
58 | CommonPagerTitleView commonPagerTitleView = new CommonPagerTitleView(context);
59 |
60 | // load custom layout
61 | View customLayout = LayoutInflater.from(context).inflate(R.layout.simple_pager_title_layout, null);
62 | final ImageView titleImg = (ImageView) customLayout.findViewById(R.id.title_img);
63 | final TextView titleText = (TextView) customLayout.findViewById(R.id.title_text);
64 | titleImg.setImageResource(R.mipmap.ic_launcher);
65 | titleText.setText(mDataList.get(index));
66 | commonPagerTitleView.setContentView(customLayout);
67 |
68 | commonPagerTitleView.setOnPagerTitleChangeListener(new CommonPagerTitleView.OnPagerTitleChangeListener() {
69 |
70 | @Override
71 | public void onSelected(int index, int totalCount) {
72 | titleText.setTextColor(Color.WHITE);
73 | }
74 |
75 | @Override
76 | public void onDeselected(int index, int totalCount) {
77 | titleText.setTextColor(Color.LTGRAY);
78 | }
79 |
80 | @Override
81 | public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
82 | titleImg.setScaleX(1.3f + (0.8f - 1.3f) * leavePercent);
83 | titleImg.setScaleY(1.3f + (0.8f - 1.3f) * leavePercent);
84 | }
85 |
86 | @Override
87 | public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
88 | titleImg.setScaleX(0.8f + (1.3f - 0.8f) * enterPercent);
89 | titleImg.setScaleY(0.8f + (1.3f - 0.8f) * enterPercent);
90 | }
91 | });
92 |
93 | commonPagerTitleView.setOnClickListener(new View.OnClickListener() {
94 | @Override
95 | public void onClick(View v) {
96 | mViewPager.setCurrentItem(index);
97 | }
98 | });
99 |
100 | return commonPagerTitleView;
101 | }
102 |
103 | @Override
104 | public IPagerIndicator getIndicator(Context context) {
105 | return null;
106 | }
107 | });
108 | magicIndicator.setNavigator(commonNavigator);
109 | ViewPagerHelper.bind(magicIndicator, mViewPager);
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/app/src/main/java/net/lucode/hackware/magicindicatordemo/example/NoTabOnlyIndicatorExampleActivity.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicatordemo.example;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.os.Bundle;
6 |
7 | import androidx.appcompat.app.AppCompatActivity;
8 | import androidx.viewpager.widget.ViewPager;
9 |
10 | import net.lucode.hackware.magicindicator.MagicIndicator;
11 | import net.lucode.hackware.magicindicator.ViewPagerHelper;
12 | import net.lucode.hackware.magicindicator.buildins.UIUtil;
13 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator;
14 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.CommonNavigatorAdapter;
15 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
16 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView;
17 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators.LinePagerIndicator;
18 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators.TriangularPagerIndicator;
19 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.DummyPagerTitleView;
20 | import net.lucode.hackware.magicindicatordemo.R;
21 |
22 | import java.util.Arrays;
23 | import java.util.List;
24 |
25 | public class NoTabOnlyIndicatorExampleActivity extends AppCompatActivity {
26 | private static final String[] CHANNELS = new String[]{"CUPCAKE", "DONUT", "ECLAIR", "GINGERBREAD", "NOUGAT", "DONUT"};
27 | private List mDataList = Arrays.asList(CHANNELS);
28 | private ExamplePagerAdapter mExamplePagerAdapter = new ExamplePagerAdapter(mDataList);
29 |
30 | private ViewPager mViewPager;
31 |
32 | @Override
33 | protected void onCreate(Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 | setContentView(R.layout.activity_no_tab_only_indicator_example_layout);
36 |
37 | mViewPager = (ViewPager) findViewById(R.id.view_pager);
38 | mViewPager.setAdapter(mExamplePagerAdapter);
39 |
40 | initMagicIndicator1();
41 | initMagicIndicator2();
42 | }
43 |
44 | private void initMagicIndicator1() {
45 | MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator1);
46 | magicIndicator.setBackgroundColor(Color.LTGRAY);
47 | CommonNavigator commonNavigator = new CommonNavigator(this);
48 | commonNavigator.setAdjustMode(true);
49 | commonNavigator.setAdapter(new CommonNavigatorAdapter() {
50 | @Override
51 | public int getCount() {
52 | return mDataList == null ? 0 : mDataList.size();
53 | }
54 |
55 | @Override
56 | public IPagerTitleView getTitleView(Context context, final int index) {
57 | return new DummyPagerTitleView(context);
58 | }
59 |
60 | @Override
61 | public IPagerIndicator getIndicator(Context context) {
62 | LinePagerIndicator indicator = new LinePagerIndicator(context);
63 | float lineHeight = context.getResources().getDimension(R.dimen.small_navigator_height);
64 | indicator.setLineHeight(lineHeight);
65 | indicator.setColors(Color.parseColor("#40c4ff"));
66 | return indicator;
67 | }
68 | });
69 | magicIndicator.setNavigator(commonNavigator);
70 | ViewPagerHelper.bind(magicIndicator, mViewPager);
71 | }
72 |
73 | private void initMagicIndicator2() {
74 | MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator2);
75 | CommonNavigator commonNavigator = new CommonNavigator(this);
76 | commonNavigator.setAdjustMode(true);
77 | commonNavigator.setAdapter(new CommonNavigatorAdapter() {
78 | @Override
79 | public int getCount() {
80 | return mDataList == null ? 0 : mDataList.size();
81 | }
82 |
83 | @Override
84 | public IPagerTitleView getTitleView(Context context, final int index) {
85 | return new DummyPagerTitleView(context);
86 | }
87 |
88 | @Override
89 | public IPagerIndicator getIndicator(Context context) {
90 | TriangularPagerIndicator indicator = new TriangularPagerIndicator(context);
91 | indicator.setReverse(true);
92 | float smallNavigatorHeight = context.getResources().getDimension(R.dimen.small_navigator_height);
93 | indicator.setLineHeight(UIUtil.dip2px(context, 2));
94 | indicator.setTriangleHeight((int) smallNavigatorHeight);
95 | indicator.setLineColor(Color.parseColor("#e94220"));
96 | return indicator;
97 | }
98 | });
99 | magicIndicator.setNavigator(commonNavigator);
100 | ViewPagerHelper.bind(magicIndicator, mViewPager);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/app/src/main/java/net/lucode/hackware/magicindicatordemo/example/TestFragment.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicatordemo.example;
2 |
3 | import android.os.Bundle;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.TextView;
8 |
9 | import androidx.annotation.Nullable;
10 | import androidx.fragment.app.Fragment;
11 |
12 | import net.lucode.hackware.magicindicatordemo.R;
13 |
14 | /**
15 | * Created by hackware on 2016/9/13.
16 | */
17 |
18 | public class TestFragment extends Fragment {
19 | public static final String EXTRA_TEXT = "extra_text";
20 |
21 | @Nullable
22 | @Override
23 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
24 | return inflater.inflate(R.layout.simple_fragment_layout, container, false);
25 | }
26 |
27 | @Override
28 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
29 | TextView textView = (TextView) view.findViewById(R.id.text_view);
30 | Bundle bundle = getArguments();
31 | if (bundle != null) {
32 | textView.setText(bundle.getString(EXTRA_TEXT));
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/net/lucode/hackware/magicindicatordemo/ext/indicators/CommonPagerIndicator.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicatordemo.ext.indicators;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Rect;
6 | import android.graphics.drawable.Drawable;
7 | import android.view.View;
8 | import android.view.animation.Interpolator;
9 | import android.view.animation.LinearInterpolator;
10 |
11 | import net.lucode.hackware.magicindicator.FragmentContainerHelper;
12 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
13 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData;
14 |
15 | import java.util.List;
16 |
17 | /**
18 | * 通用的indicator,支持外面设置Drawable
19 | * Created by hackware on 2016/11/14.
20 | */
21 |
22 | public class CommonPagerIndicator extends View implements IPagerIndicator {
23 | public static final int MODE_MATCH_EDGE = 0; // drawable宽度 == title宽度 - 2 * mXOffset
24 | public static final int MODE_WRAP_CONTENT = 1; // drawable宽度 == title内容宽度 - 2 * mXOffset
25 | public static final int MODE_EXACTLY = 2;
26 |
27 | private int mMode; // 默认为MODE_MATCH_EDGE模式
28 | private Drawable mIndicatorDrawable;
29 |
30 | // 控制动画
31 | private Interpolator mStartInterpolator = new LinearInterpolator();
32 | private Interpolator mEndInterpolator = new LinearInterpolator();
33 |
34 | private float mDrawableHeight;
35 | private float mDrawableWidth;
36 | private float mYOffset;
37 | private float mXOffset;
38 |
39 | private List mPositionDataList;
40 | private Rect mDrawableRect = new Rect();
41 |
42 | public CommonPagerIndicator(Context context) {
43 | super(context);
44 | }
45 |
46 | @Override
47 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
48 | if (mIndicatorDrawable == null) {
49 | return;
50 | }
51 |
52 | if (mPositionDataList == null || mPositionDataList.isEmpty()) {
53 | return;
54 | }
55 |
56 | // 计算锚点位置
57 | PositionData current = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position);
58 | PositionData next = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position + 1);
59 |
60 | float leftX;
61 | float nextLeftX;
62 | float rightX;
63 | float nextRightX;
64 | if (mMode == MODE_MATCH_EDGE) {
65 | leftX = current.mLeft + mXOffset;
66 | nextLeftX = next.mLeft + mXOffset;
67 | rightX = current.mRight - mXOffset;
68 | nextRightX = next.mRight - mXOffset;
69 | mDrawableRect.top = (int) mYOffset;
70 | mDrawableRect.bottom = (int) (getHeight() - mYOffset);
71 | } else if (mMode == MODE_WRAP_CONTENT) {
72 | leftX = current.mContentLeft + mXOffset;
73 | nextLeftX = next.mContentLeft + mXOffset;
74 | rightX = current.mContentRight - mXOffset;
75 | nextRightX = next.mContentRight - mXOffset;
76 | mDrawableRect.top = (int) (current.mContentTop - mYOffset);
77 | mDrawableRect.bottom = (int) (current.mContentBottom + mYOffset);
78 | } else { // MODE_EXACTLY
79 | leftX = current.mLeft + (current.width() - mDrawableWidth) / 2;
80 | nextLeftX = next.mLeft + (next.width() - mDrawableWidth) / 2;
81 | rightX = current.mLeft + (current.width() + mDrawableWidth) / 2;
82 | nextRightX = next.mLeft + (next.width() + mDrawableWidth) / 2;
83 | mDrawableRect.top = (int) (getHeight() - mDrawableHeight - mYOffset);
84 | mDrawableRect.bottom = (int) (getHeight() - mYOffset);
85 | }
86 |
87 | mDrawableRect.left = (int) (leftX + (nextLeftX - leftX) * mStartInterpolator.getInterpolation(positionOffset));
88 | mDrawableRect.right = (int) (rightX + (nextRightX - rightX) * mEndInterpolator.getInterpolation(positionOffset));
89 | mIndicatorDrawable.setBounds(mDrawableRect);
90 |
91 | invalidate();
92 | }
93 |
94 | @Override
95 | public void onPageSelected(int position) {
96 | }
97 |
98 | @Override
99 | public void onPageScrollStateChanged(int state) {
100 | }
101 |
102 | @Override
103 | protected void onDraw(Canvas canvas) {
104 | if (mIndicatorDrawable != null) {
105 | mIndicatorDrawable.draw(canvas);
106 | }
107 | }
108 |
109 | @Override
110 | public void onPositionDataProvide(List dataList) {
111 | mPositionDataList = dataList;
112 | }
113 |
114 | public Drawable getIndicatorDrawable() {
115 | return mIndicatorDrawable;
116 | }
117 |
118 | public void setIndicatorDrawable(Drawable indicatorDrawable) {
119 | mIndicatorDrawable = indicatorDrawable;
120 | }
121 |
122 | public Interpolator getStartInterpolator() {
123 | return mStartInterpolator;
124 | }
125 |
126 | public void setStartInterpolator(Interpolator startInterpolator) {
127 | mStartInterpolator = startInterpolator;
128 | }
129 |
130 | public Interpolator getEndInterpolator() {
131 | return mEndInterpolator;
132 | }
133 |
134 | public void setEndInterpolator(Interpolator endInterpolator) {
135 | mEndInterpolator = endInterpolator;
136 | }
137 |
138 | public int getMode() {
139 | return mMode;
140 | }
141 |
142 | public void setMode(int mode) {
143 | if (mode == MODE_EXACTLY || mode == MODE_MATCH_EDGE || mode == MODE_WRAP_CONTENT) {
144 | mMode = mode;
145 | } else {
146 | throw new IllegalArgumentException("mode " + mode + " not supported.");
147 | }
148 | }
149 |
150 | public float getDrawableHeight() {
151 | return mDrawableHeight;
152 | }
153 |
154 | public void setDrawableHeight(float drawableHeight) {
155 | mDrawableHeight = drawableHeight;
156 | }
157 |
158 | public float getDrawableWidth() {
159 | return mDrawableWidth;
160 | }
161 |
162 | public void setDrawableWidth(float drawableWidth) {
163 | mDrawableWidth = drawableWidth;
164 | }
165 |
166 | public float getYOffset() {
167 | return mYOffset;
168 | }
169 |
170 | public void setYOffset(float yOffset) {
171 | mYOffset = yOffset;
172 | }
173 |
174 | public float getXOffset() {
175 | return mXOffset;
176 | }
177 |
178 | public void setXOffset(float xOffset) {
179 | mXOffset = xOffset;
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/app/src/main/java/net/lucode/hackware/magicindicatordemo/ext/indicators/DotPagerIndicator.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicatordemo.ext.indicators;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.view.View;
8 |
9 | import net.lucode.hackware.magicindicator.buildins.UIUtil;
10 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
11 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData;
12 |
13 | import java.util.List;
14 |
15 | /**
16 | * 非手指跟随的小圆点指示器
17 | * Created by hackware on 2016/7/13.
18 | */
19 | public class DotPagerIndicator extends View implements IPagerIndicator {
20 | private List mDataList;
21 | private float mRadius;
22 | private float mYOffset;
23 | private int mDotColor;
24 |
25 | private float mCircleCenterX;
26 | private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
27 |
28 | public DotPagerIndicator(Context context) {
29 | super(context);
30 | mRadius = UIUtil.dip2px(context, 3);
31 | mYOffset = UIUtil.dip2px(context, 3);
32 | mDotColor = Color.WHITE;
33 | }
34 |
35 | @Override
36 | public void onPageSelected(int position) {
37 | if (mDataList == null || mDataList.isEmpty()) {
38 | return;
39 | }
40 | PositionData data = mDataList.get(position);
41 | mCircleCenterX = data.mLeft + data.width() / 2;
42 | invalidate();
43 | }
44 |
45 | @Override
46 | public void onPositionDataProvide(List dataList) {
47 | mDataList = dataList;
48 | }
49 |
50 | @Override
51 | protected void onDraw(Canvas canvas) {
52 | super.onDraw(canvas);
53 | mPaint.setColor(mDotColor);
54 | canvas.drawCircle(mCircleCenterX, getHeight() - mYOffset - mRadius, mRadius, mPaint);
55 | }
56 |
57 | @Override
58 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
59 | }
60 |
61 | @Override
62 | public void onPageScrollStateChanged(int state) {
63 | }
64 |
65 | public float getRadius() {
66 | return mRadius;
67 | }
68 |
69 | public void setRadius(float radius) {
70 | mRadius = radius;
71 | invalidate();
72 | }
73 |
74 | public float getYOffset() {
75 | return mYOffset;
76 | }
77 |
78 | public void setYOffset(float yOffset) {
79 | mYOffset = yOffset;
80 | invalidate();
81 | }
82 |
83 | public int getDotColor() {
84 | return mDotColor;
85 | }
86 |
87 | public void setDotColor(int dotColor) {
88 | mDotColor = dotColor;
89 | invalidate();
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/app/src/main/java/net/lucode/hackware/magicindicatordemo/ext/navigator/DummyCircleNavigator.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicatordemo.ext.navigator;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.PointF;
7 | import android.view.View;
8 |
9 | import net.lucode.hackware.magicindicator.abs.IPagerNavigator;
10 | import net.lucode.hackware.magicindicator.buildins.UIUtil;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | /**
16 | * Created by hackware on 2016/7/24.
17 | */
18 |
19 | public class DummyCircleNavigator extends View implements IPagerNavigator {
20 | private int mRadius;
21 | private int mCircleColor;
22 | private int mStrokeWidth;
23 | private int mCircleSpacing;
24 | private int mCircleCount;
25 |
26 | private int mCurrentIndex;
27 | private List mCirclePoints = new ArrayList();
28 | private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
29 |
30 | public DummyCircleNavigator(Context context) {
31 | super(context);
32 | mRadius = UIUtil.dip2px(context, 3);
33 | mCircleSpacing = UIUtil.dip2px(context, 8);
34 | mStrokeWidth = UIUtil.dip2px(context, 1);
35 | }
36 |
37 | @Override
38 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
39 | }
40 |
41 | @Override
42 | public void onPageScrollStateChanged(int state) {
43 | }
44 |
45 | @Override
46 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
47 | prepareCirclePoints();
48 | }
49 |
50 | private void prepareCirclePoints() {
51 | mCirclePoints.clear();
52 | if (mCircleCount > 0) {
53 | int y = getHeight() / 2;
54 | int measureWidth = mCircleCount * mRadius * 2 + (mCircleCount - 1) * mCircleSpacing;
55 | int centerSpacing = mRadius * 2 + mCircleSpacing;
56 | int startX = (getWidth() - measureWidth) / 2 + mRadius;
57 | for (int i = 0; i < mCircleCount; i++) {
58 | PointF pointF = new PointF(startX, y);
59 | mCirclePoints.add(pointF);
60 | startX += centerSpacing;
61 | }
62 | }
63 | }
64 |
65 | @Override
66 | protected void onDraw(Canvas canvas) {
67 | drawDeselectedCircles(canvas);
68 | drawSelectedCircle(canvas);
69 | }
70 |
71 | private void drawDeselectedCircles(Canvas canvas) {
72 | mPaint.setStyle(Paint.Style.STROKE);
73 | mPaint.setStrokeWidth(mStrokeWidth);
74 | mPaint.setColor(mCircleColor);
75 | for (int i = 0, j = mCirclePoints.size(); i < j; i++) {
76 | PointF pointF = mCirclePoints.get(i);
77 | canvas.drawCircle(pointF.x, pointF.y, mRadius, mPaint);
78 | }
79 | }
80 |
81 | private void drawSelectedCircle(Canvas canvas) {
82 | mPaint.setStyle(Paint.Style.FILL);
83 | if (mCirclePoints.size() > 0) {
84 | float selectedCircleX = mCirclePoints.get(mCurrentIndex).x;
85 | canvas.drawCircle(selectedCircleX, getHeight() / 2, mRadius, mPaint);
86 | }
87 | }
88 |
89 | // 被添加到 magicindicator 时调用
90 | @Override
91 | public void onAttachToMagicIndicator() {
92 | }
93 |
94 | // 从 magicindicator 上移除时调用
95 | @Override
96 | public void onDetachFromMagicIndicator() {
97 | }
98 |
99 | // 当指示数目改变时调用
100 | @Override
101 | public void notifyDataSetChanged() {
102 | prepareCirclePoints();
103 | invalidate();
104 | }
105 |
106 | @Override
107 | public void onPageSelected(int position) {
108 | mCurrentIndex = position;
109 | invalidate();
110 | }
111 |
112 | public int getCircleColor() {
113 | return mCircleColor;
114 | }
115 |
116 | public void setCircleColor(int circleColor) {
117 | mCircleColor = circleColor;
118 | invalidate();
119 | }
120 |
121 | public int getStrokeWidth() {
122 | return mStrokeWidth;
123 | }
124 |
125 | public void setStrokeWidth(int strokeWidth) {
126 | mStrokeWidth = strokeWidth;
127 | invalidate();
128 | }
129 |
130 | public int getRadius() {
131 | return mRadius;
132 | }
133 |
134 | public void setRadius(int radius) {
135 | mRadius = radius;
136 | prepareCirclePoints();
137 | invalidate();
138 | }
139 |
140 | public int getCircleSpacing() {
141 | return mCircleSpacing;
142 | }
143 |
144 | public void setCircleSpacing(int circleSpacing) {
145 | mCircleSpacing = circleSpacing;
146 | prepareCirclePoints();
147 | invalidate();
148 | }
149 |
150 | public int getCurrentIndex() {
151 | return mCurrentIndex;
152 | }
153 |
154 | public int getCircleCount() {
155 | return mCircleCount;
156 | }
157 |
158 | /**
159 | * notifyDataSetChanged应该紧随其后调用
160 | *
161 | * @param circleCount
162 | */
163 | public void setCircleCount(int circleCount) {
164 | mCircleCount = circleCount;
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/app/src/main/java/net/lucode/hackware/magicindicatordemo/ext/navigator/ScaleCircleNavigator.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicatordemo.ext.navigator;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.PointF;
8 | import android.util.SparseArray;
9 | import android.view.MotionEvent;
10 | import android.view.View;
11 | import android.view.ViewConfiguration;
12 | import android.view.animation.Interpolator;
13 | import android.view.animation.LinearInterpolator;
14 |
15 | import net.lucode.hackware.magicindicator.NavigatorHelper;
16 | import net.lucode.hackware.magicindicator.abs.IPagerNavigator;
17 | import net.lucode.hackware.magicindicator.buildins.ArgbEvaluatorHolder;
18 | import net.lucode.hackware.magicindicator.buildins.UIUtil;
19 |
20 | import java.util.ArrayList;
21 | import java.util.List;
22 |
23 | // _oo0oo_
24 | // o8888888o
25 | // 88" . "88
26 | // (| -_- |)
27 | // 0\ = /0
28 | // ___/`---'\___
29 | // .' \\| |// '.
30 | // / \\||| : |||// \
31 | // / _||||| -:- |||||- \
32 | // | | \\\ - /// | |
33 | // | \_| ''\---/'' |_/ |
34 | // \ .-\__ '-' ___/-. /
35 | // ___'. .' /--.--\ `. .'___
36 | // ."" '< `.___\_<|>_/___.' >' "".
37 | // | | : `- \`.;`\ _ /`;.`/ - ` : | |
38 | // \ \ `_. \_ __\ /__ _/ .-` / /
39 | // =====`-.____`.___ \_____/___.-`___.-'=====
40 | // `=---='
41 | //
42 | //
43 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
44 | //
45 | // 佛祖保佑 永无BUG
46 |
47 | /**
48 | * 类似CircleIndicator的效果
49 | * Created by hackware on 2016/9/3.
50 | */
51 |
52 | public class ScaleCircleNavigator extends View implements IPagerNavigator, NavigatorHelper.OnNavigatorScrollListener {
53 | private int mMinRadius;
54 | private int mMaxRadius;
55 | private int mNormalCircleColor = Color.LTGRAY;
56 | private int mSelectedCircleColor = Color.GRAY;
57 | private int mCircleSpacing;
58 | private int mCircleCount;
59 |
60 | private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
61 | private List mCirclePoints = new ArrayList();
62 | private SparseArray mCircleRadiusArray = new SparseArray();
63 |
64 | // 事件回调
65 | private boolean mTouchable;
66 | private ScaleCircleNavigator.OnCircleClickListener mCircleClickListener;
67 | private float mDownX;
68 | private float mDownY;
69 | private int mTouchSlop;
70 |
71 | private boolean mFollowTouch = true; // 是否跟随手指滑动
72 | private NavigatorHelper mNavigatorHelper = new NavigatorHelper();
73 | private Interpolator mStartInterpolator = new LinearInterpolator();
74 |
75 | public ScaleCircleNavigator(Context context) {
76 | super(context);
77 | init(context);
78 | }
79 |
80 | private void init(Context context) {
81 | mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
82 | mMinRadius = UIUtil.dip2px(context, 3);
83 | mMaxRadius = UIUtil.dip2px(context, 5);
84 | mCircleSpacing = UIUtil.dip2px(context, 8);
85 | mNavigatorHelper.setNavigatorScrollListener(this);
86 | mNavigatorHelper.setSkimOver(true);
87 | }
88 |
89 | @Override
90 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
91 | setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
92 | }
93 |
94 | private int measureWidth(int widthMeasureSpec) {
95 | int mode = MeasureSpec.getMode(widthMeasureSpec);
96 | int width = MeasureSpec.getSize(widthMeasureSpec);
97 | int result = 0;
98 | switch (mode) {
99 | case MeasureSpec.EXACTLY:
100 | result = width;
101 | break;
102 | case MeasureSpec.AT_MOST:
103 | case MeasureSpec.UNSPECIFIED:
104 | if (mCircleCount <= 0) {
105 | result = getPaddingLeft() + getPaddingRight();
106 | } else {
107 | result = (mCircleCount - 1) * mMinRadius * 2 + mMaxRadius * 2 + (mCircleCount - 1) * mCircleSpacing + getPaddingLeft() + getPaddingRight();
108 | }
109 | break;
110 | default:
111 | break;
112 | }
113 | return result;
114 | }
115 |
116 | private int measureHeight(int heightMeasureSpec) {
117 | int mode = MeasureSpec.getMode(heightMeasureSpec);
118 | int height = MeasureSpec.getSize(heightMeasureSpec);
119 | int result = 0;
120 | switch (mode) {
121 | case MeasureSpec.EXACTLY:
122 | result = height;
123 | break;
124 | case MeasureSpec.AT_MOST:
125 | case MeasureSpec.UNSPECIFIED:
126 | result = mMaxRadius * 2 + getPaddingTop() + getPaddingBottom();
127 | break;
128 | default:
129 | break;
130 | }
131 | return result;
132 | }
133 |
134 | @Override
135 | protected void onDraw(Canvas canvas) {
136 | for (int i = 0, j = mCirclePoints.size(); i < j; i++) {
137 | PointF point = mCirclePoints.get(i);
138 | float radius = mCircleRadiusArray.get(i, (float) mMinRadius);
139 | mPaint.setColor(ArgbEvaluatorHolder.eval((radius - mMinRadius) / (mMaxRadius - mMinRadius), mNormalCircleColor, mSelectedCircleColor));
140 | canvas.drawCircle(point.x, getHeight() / 2.0f, radius, mPaint);
141 | }
142 | }
143 |
144 | private void prepareCirclePoints() {
145 | mCirclePoints.clear();
146 | if (mCircleCount > 0) {
147 | int y = Math.round(getHeight() / 2.0f);
148 | int centerSpacing = mMinRadius * 2 + mCircleSpacing;
149 | int startX = mMaxRadius + getPaddingLeft();
150 | for (int i = 0; i < mCircleCount; i++) {
151 | PointF pointF = new PointF(startX, y);
152 | mCirclePoints.add(pointF);
153 | startX += centerSpacing;
154 | }
155 | }
156 | }
157 |
158 | @Override
159 | public boolean onTouchEvent(MotionEvent event) {
160 | float x = event.getX();
161 | float y = event.getY();
162 | switch (event.getAction()) {
163 | case MotionEvent.ACTION_DOWN:
164 | if (mTouchable) {
165 | mDownX = x;
166 | mDownY = y;
167 | return true;
168 | }
169 | break;
170 | case MotionEvent.ACTION_UP:
171 | if (mCircleClickListener != null) {
172 | if (Math.abs(x - mDownX) <= mTouchSlop && Math.abs(y - mDownY) <= mTouchSlop) {
173 | float max = Float.MAX_VALUE;
174 | int index = 0;
175 | for (int i = 0; i < mCirclePoints.size(); i++) {
176 | PointF pointF = mCirclePoints.get(i);
177 | float offset = Math.abs(pointF.x - x);
178 | if (offset < max) {
179 | max = offset;
180 | index = i;
181 | }
182 | }
183 | mCircleClickListener.onClick(index);
184 | }
185 | }
186 | break;
187 | default:
188 | break;
189 | }
190 | return super.onTouchEvent(event);
191 | }
192 |
193 | @Override
194 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
195 | mNavigatorHelper.onPageScrolled(position, positionOffset, positionOffsetPixels);
196 | }
197 |
198 | @Override
199 | public void onPageSelected(int position) {
200 | mNavigatorHelper.onPageSelected(position);
201 | }
202 |
203 | @Override
204 | public void onPageScrollStateChanged(int state) {
205 | mNavigatorHelper.onPageScrollStateChanged(state);
206 | }
207 |
208 | @Override
209 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
210 | prepareCirclePoints();
211 | }
212 |
213 | @Override
214 | public void notifyDataSetChanged() {
215 | prepareCirclePoints();
216 | requestLayout();
217 | }
218 |
219 | @Override
220 | public void onAttachToMagicIndicator() {
221 | }
222 |
223 | @Override
224 | public void onDetachFromMagicIndicator() {
225 | }
226 |
227 | public void setMinRadius(int minRadius) {
228 | mMinRadius = minRadius;
229 | prepareCirclePoints();
230 | invalidate();
231 | }
232 |
233 | public void setMaxRadius(int maxRadius) {
234 | mMaxRadius = maxRadius;
235 | prepareCirclePoints();
236 | invalidate();
237 | }
238 |
239 | public void setNormalCircleColor(int normalCircleColor) {
240 | mNormalCircleColor = normalCircleColor;
241 | invalidate();
242 | }
243 |
244 | public void setSelectedCircleColor(int selectedCircleColor) {
245 | mSelectedCircleColor = selectedCircleColor;
246 | invalidate();
247 | }
248 |
249 | public void setCircleSpacing(int circleSpacing) {
250 | mCircleSpacing = circleSpacing;
251 | prepareCirclePoints();
252 | invalidate();
253 | }
254 |
255 | public void setStartInterpolator(Interpolator startInterpolator) {
256 | mStartInterpolator = startInterpolator;
257 | if (mStartInterpolator == null) {
258 | mStartInterpolator = new LinearInterpolator();
259 | }
260 | }
261 |
262 | public void setCircleCount(int count) {
263 | mCircleCount = count; // 此处不调用invalidate,让外部调用notifyDataSetChanged
264 | mNavigatorHelper.setTotalCount(mCircleCount);
265 | }
266 |
267 | public void setTouchable(boolean touchable) {
268 | mTouchable = touchable;
269 | }
270 |
271 | public void setFollowTouch(boolean followTouch) {
272 | mFollowTouch = followTouch;
273 | }
274 |
275 | public void setSkimOver(boolean skimOver) {
276 | mNavigatorHelper.setSkimOver(skimOver);
277 | }
278 |
279 | public void setCircleClickListener(OnCircleClickListener circleClickListener) {
280 | if (!mTouchable) {
281 | mTouchable = true;
282 | }
283 | mCircleClickListener = circleClickListener;
284 | }
285 |
286 | @Override
287 | public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
288 | if (mFollowTouch) {
289 | float radius = mMinRadius + (mMaxRadius - mMinRadius) * mStartInterpolator.getInterpolation(enterPercent);
290 | mCircleRadiusArray.put(index, radius);
291 | invalidate();
292 | }
293 | }
294 |
295 | @Override
296 | public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
297 | if (mFollowTouch) {
298 | float radius = mMaxRadius + (mMinRadius - mMaxRadius) * mStartInterpolator.getInterpolation(leavePercent);
299 | mCircleRadiusArray.put(index, radius);
300 | invalidate();
301 | }
302 | }
303 |
304 | @Override
305 | public void onSelected(int index, int totalCount) {
306 | if (!mFollowTouch) {
307 | mCircleRadiusArray.put(index, (float) mMaxRadius);
308 | invalidate();
309 | }
310 | }
311 |
312 | @Override
313 | public void onDeselected(int index, int totalCount) {
314 | if (!mFollowTouch) {
315 | mCircleRadiusArray.put(index, (float) mMinRadius);
316 | invalidate();
317 | }
318 | }
319 |
320 | public interface OnCircleClickListener {
321 | void onClick(int index);
322 | }
323 | }
324 |
--------------------------------------------------------------------------------
/app/src/main/java/net/lucode/hackware/magicindicatordemo/ext/titles/ColorFlipPagerTitleView.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicatordemo.ext.titles;
2 |
3 | import android.content.Context;
4 |
5 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.SimplePagerTitleView;
6 |
7 | /**
8 | * Created by hackware on 2016/7/24.
9 | */
10 |
11 | public class ColorFlipPagerTitleView extends SimplePagerTitleView {
12 | private float mChangePercent = 0.5f;
13 |
14 | public ColorFlipPagerTitleView(Context context) {
15 | super(context);
16 | }
17 |
18 | @Override
19 | public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
20 | if (leavePercent >= mChangePercent) {
21 | setTextColor(mNormalColor);
22 | } else {
23 | setTextColor(mSelectedColor);
24 | }
25 | }
26 |
27 | @Override
28 | public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
29 | if (enterPercent >= mChangePercent) {
30 | setTextColor(mSelectedColor);
31 | } else {
32 | setTextColor(mNormalColor);
33 | }
34 | }
35 |
36 | @Override
37 | public void onSelected(int index, int totalCount) {
38 | }
39 |
40 | @Override
41 | public void onDeselected(int index, int totalCount) {
42 | }
43 |
44 | public float getChangePercent() {
45 | return mChangePercent;
46 | }
47 |
48 | public void setChangePercent(float changePercent) {
49 | mChangePercent = changePercent;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/net/lucode/hackware/magicindicatordemo/ext/titles/ScaleTransitionPagerTitleView.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicatordemo.ext.titles;
2 |
3 | import android.content.Context;
4 |
5 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.ColorTransitionPagerTitleView;
6 |
7 | /**
8 | * 带颜色渐变和缩放的指示器标题
9 | * 博客: http://hackware.lucode.net
10 | * Created by hackware on 2016/6/26.
11 | */
12 | public class ScaleTransitionPagerTitleView extends ColorTransitionPagerTitleView {
13 | private float mMinScale = 0.75f;
14 |
15 | public ScaleTransitionPagerTitleView(Context context) {
16 | super(context);
17 | }
18 |
19 | @Override
20 | public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
21 | super.onEnter(index, totalCount, enterPercent, leftToRight); // 实现颜色渐变
22 | setScaleX(mMinScale + (1.0f - mMinScale) * enterPercent);
23 | setScaleY(mMinScale + (1.0f - mMinScale) * enterPercent);
24 | }
25 |
26 | @Override
27 | public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
28 | super.onLeave(index, totalCount, leavePercent, leftToRight); // 实现颜色渐变
29 | setScaleX(1.0f + (mMinScale - 1.0f) * leavePercent);
30 | setScaleY(1.0f + (mMinScale - 1.0f) * leavePercent);
31 | }
32 |
33 | public float getMinScale() {
34 | return mMinScale;
35 | }
36 |
37 | public void setMinScale(float minScale) {
38 | mMinScale = minScale;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/round_indicator_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/simple_count_badge_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/simple_red_dot.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/simple_splitter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_badge_tab_example_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
21 |
22 |
23 |
24 |
29 |
30 |
35 |
36 |
41 |
42 |
43 |
44 |
50 |
51 |
56 |
57 |
58 |
59 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_custom_navigator_example_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
19 |
20 |
26 |
27 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_dynamic_tab_example_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
19 |
20 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_example_main_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
17 |
23 |
24 |
30 |
31 |
37 |
38 |
44 |
45 |
51 |
52 |
58 |
59 |
65 |
66 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_fixed_tab_example_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
21 |
22 |
23 |
24 |
29 |
30 |
35 |
36 |
41 |
42 |
43 |
44 |
50 |
51 |
56 |
57 |
58 |
59 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_fragment_container_example_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
20 |
21 |
22 |
23 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_load_custom_layout_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_no_tab_only_indicator_example_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
20 |
21 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_scrollable_indicator_example_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
17 |
18 |
23 |
24 |
29 |
30 |
35 |
36 |
41 |
42 |
47 |
48 |
53 |
54 |
59 |
60 |
65 |
66 |
71 |
72 |
73 |
74 |
75 |
76 |
80 |
81 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/simple_count_badge_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/simple_fragment_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/simple_pager_title_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/simple_red_dot_badge_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackware1993/MagicIndicator/fe22c7dc45444312bf1e8c71d1ced8e7c1aa3c9c/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackware1993/MagicIndicator/fe22c7dc45444312bf1e8c71d1ced8e7c1aa3c9c/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackware1993/MagicIndicator/fe22c7dc45444312bf1e8c71d1ced8e7c1aa3c9c/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackware1993/MagicIndicator/fe22c7dc45444312bf1e8c71d1ced8e7c1aa3c9c/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackware1993/MagicIndicator/fe22c7dc45444312bf1e8c71d1ced8e7c1aa3c9c/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3f51b5
4 | #303f9f
5 | #ff4081
6 | #dedede
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 | 16dp
4 | 45dp
5 | 5dp
6 | 5dp
7 | 45dp
8 | 65dp
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | magicindicatordemo
3 | scrollable tab
4 | fixed tab
5 | dynamic tab
6 | no tab, only indicator
7 | work with fragment container
8 | tab with badge view
9 | custom navigator
10 | random page
11 | load custom layout
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/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 | google()
6 | maven {
7 | url 'http://maven.aliyun.com/nexus/content/groups/public'
8 | }
9 | maven {
10 | url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'
11 | }
12 | jcenter()
13 | mavenCentral()
14 | }
15 | dependencies {
16 | classpath 'com.android.tools.build:gradle:3.6.4'
17 |
18 | // NOTE: Do not place your application dependencies here; they belong
19 | // in the individual module build.gradle files
20 | }
21 | }
22 |
23 | allprojects {
24 | repositories {
25 | google()
26 | maven {
27 | url 'http://maven.aliyun.com/nexus/content/groups/public'
28 | }
29 | maven {
30 | url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'
31 | }
32 | jcenter()
33 | mavenCentral()
34 | maven {
35 | url 'https://jitpack.io'
36 | }
37 | }
38 | }
39 |
40 | task clean(type: Delete) {
41 | delete rootProject.buildDir
42 | }
--------------------------------------------------------------------------------
/demo.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackware1993/MagicIndicator/fe22c7dc45444312bf1e8c71d1ced8e7c1aa3c9c/demo.apk
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 | android.useAndroidX=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackware1993/MagicIndicator/fe22c7dc45444312bf1e8c71d1ced8e7c1aa3c9c/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Sep 29 13:32:52 CST 2020
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-5.6.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/local.properties:
--------------------------------------------------------------------------------
1 | ## This file must *NOT* be checked into Version Control Systems,
2 | # as it contains information specific to your local configuration.
3 | #
4 | # Location of the SDK. This is only used by Gradle.
5 | # For customization when using a Version Control System, please read the
6 | # header note.
7 | #Sun Jun 14 12:46:25 CST 2020
8 | ndk.dir=/Users/SherlockHolmes/Library/Android/sdk/ndk-bundle
9 | sdk.dir=D\:\\Android\\Sdk
10 |
--------------------------------------------------------------------------------
/magicindicator.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackware1993/MagicIndicator/fe22c7dc45444312bf1e8c71d1ced8e7c1aa3c9c/magicindicator.gif
--------------------------------------------------------------------------------
/magicindicator/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 28
5 | buildToolsVersion "28.0.3"
6 |
7 | defaultConfig {
8 | minSdkVersion 14
9 | targetSdkVersion 28
10 | }
11 | buildTypes {
12 | release {
13 | minifyEnabled false
14 | }
15 | }
16 | }
17 |
18 | dependencies {
19 | implementation fileTree(dir: 'libs', include: ['*.jar'])
20 | implementation 'androidx.viewpager:viewpager:1.0.0'
21 | }
--------------------------------------------------------------------------------
/magicindicator/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/FragmentContainerHelper.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ValueAnimator;
6 | import android.annotation.TargetApi;
7 | import android.os.Build;
8 | import android.view.animation.AccelerateDecelerateInterpolator;
9 | import android.view.animation.Interpolator;
10 |
11 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 |
17 | /**
18 | * 使得MagicIndicator在FragmentContainer中使用
19 | * Created by hackware on 2016/9/4.
20 | */
21 |
22 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
23 | public class FragmentContainerHelper {
24 | private List mMagicIndicators = new ArrayList();
25 | private ValueAnimator mScrollAnimator;
26 | private int mLastSelectedIndex;
27 | private int mDuration = 150;
28 | private Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
29 |
30 | private Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
31 | @Override
32 | public void onAnimationEnd(Animator animation) {
33 | dispatchPageScrollStateChanged(ScrollState.SCROLL_STATE_IDLE);
34 | mScrollAnimator = null;
35 | }
36 | };
37 |
38 | private ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
39 | @Override
40 | public void onAnimationUpdate(ValueAnimator animation) {
41 | float positionOffsetSum = (Float) animation.getAnimatedValue();
42 | int position = (int) positionOffsetSum;
43 | float positionOffset = positionOffsetSum - position;
44 | if (positionOffsetSum < 0) {
45 | position = position - 1;
46 | positionOffset = 1.0f + positionOffset;
47 | }
48 | dispatchPageScrolled(position, positionOffset, 0);
49 | }
50 | };
51 |
52 | public FragmentContainerHelper() {
53 | }
54 |
55 | public FragmentContainerHelper(MagicIndicator magicIndicator) {
56 | mMagicIndicators.add(magicIndicator);
57 | }
58 |
59 | /**
60 | * IPagerIndicator支持弹性效果的辅助方法
61 | *
62 | * @param positionDataList
63 | * @param index
64 | * @return
65 | */
66 | public static PositionData getImitativePositionData(List positionDataList, int index) {
67 | if (index >= 0 && index <= positionDataList.size() - 1) { // 越界后,返回假的PositionData
68 | return positionDataList.get(index);
69 | } else {
70 | PositionData result = new PositionData();
71 | PositionData referenceData;
72 | int offset;
73 | if (index < 0) {
74 | offset = index;
75 | referenceData = positionDataList.get(0);
76 | } else {
77 | offset = index - positionDataList.size() + 1;
78 | referenceData = positionDataList.get(positionDataList.size() - 1);
79 | }
80 | result.mLeft = referenceData.mLeft + offset * referenceData.width();
81 | result.mTop = referenceData.mTop;
82 | result.mRight = referenceData.mRight + offset * referenceData.width();
83 | result.mBottom = referenceData.mBottom;
84 | result.mContentLeft = referenceData.mContentLeft + offset * referenceData.width();
85 | result.mContentTop = referenceData.mContentTop;
86 | result.mContentRight = referenceData.mContentRight + offset * referenceData.width();
87 | result.mContentBottom = referenceData.mContentBottom;
88 | return result;
89 | }
90 | }
91 |
92 | public void handlePageSelected(int selectedIndex) {
93 | handlePageSelected(selectedIndex, true);
94 | }
95 |
96 | public void handlePageSelected(int selectedIndex, boolean smooth) {
97 | if (mLastSelectedIndex == selectedIndex) {
98 | return;
99 | }
100 | if (smooth) {
101 | if (mScrollAnimator == null || !mScrollAnimator.isRunning()) {
102 | dispatchPageScrollStateChanged(ScrollState.SCROLL_STATE_SETTLING);
103 | }
104 | dispatchPageSelected(selectedIndex);
105 | float currentPositionOffsetSum = mLastSelectedIndex;
106 | if (mScrollAnimator != null) {
107 | currentPositionOffsetSum = (Float) mScrollAnimator.getAnimatedValue();
108 | mScrollAnimator.cancel();
109 | mScrollAnimator = null;
110 | }
111 | mScrollAnimator = new ValueAnimator();
112 | mScrollAnimator.setFloatValues(currentPositionOffsetSum, selectedIndex); // position = selectedIndex, positionOffset = 0.0f
113 | mScrollAnimator.addUpdateListener(mAnimatorUpdateListener);
114 | mScrollAnimator.addListener(mAnimatorListener);
115 | mScrollAnimator.setInterpolator(mInterpolator);
116 | mScrollAnimator.setDuration(mDuration);
117 | mScrollAnimator.start();
118 | } else {
119 | dispatchPageSelected(selectedIndex);
120 | if (mScrollAnimator != null && mScrollAnimator.isRunning()) {
121 | dispatchPageScrolled(mLastSelectedIndex, 0.0f, 0);
122 | }
123 | dispatchPageScrollStateChanged(ScrollState.SCROLL_STATE_IDLE);
124 | dispatchPageScrolled(selectedIndex, 0.0f, 0);
125 | }
126 | mLastSelectedIndex = selectedIndex;
127 | }
128 |
129 | public void setDuration(int duration) {
130 | mDuration = duration;
131 | }
132 |
133 | public void setInterpolator(Interpolator interpolator) {
134 | if (interpolator == null) {
135 | mInterpolator = new AccelerateDecelerateInterpolator();
136 | } else {
137 | mInterpolator = interpolator;
138 | }
139 | }
140 |
141 | public void attachMagicIndicator(MagicIndicator magicIndicator) {
142 | mMagicIndicators.add(magicIndicator);
143 | }
144 |
145 | private void dispatchPageSelected(int pageIndex) {
146 | for (MagicIndicator magicIndicator : mMagicIndicators) {
147 | magicIndicator.onPageSelected(pageIndex);
148 | }
149 | }
150 |
151 | private void dispatchPageScrollStateChanged(int state) {
152 | for (MagicIndicator magicIndicator : mMagicIndicators) {
153 | magicIndicator.onPageScrollStateChanged(state);
154 | }
155 | }
156 |
157 | private void dispatchPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
158 | for (MagicIndicator magicIndicator : mMagicIndicators) {
159 | magicIndicator.onPageScrolled(position, positionOffset, positionOffsetPixels);
160 | }
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/MagicIndicator.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.FrameLayout;
7 |
8 | import net.lucode.hackware.magicindicator.abs.IPagerNavigator;
9 |
10 | /**
11 | * 整个框架的入口,核心
12 | * 博客: http://hackware.lucode.net
13 | * Created by hackware on 2016/6/26.
14 | */
15 | public class MagicIndicator extends FrameLayout {
16 | private IPagerNavigator mNavigator;
17 |
18 | public MagicIndicator(Context context) {
19 | super(context);
20 | }
21 |
22 | public MagicIndicator(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | }
25 |
26 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
27 | if (mNavigator != null) {
28 | mNavigator.onPageScrolled(position, positionOffset, positionOffsetPixels);
29 | }
30 | }
31 |
32 | public void onPageSelected(int position) {
33 | if (mNavigator != null) {
34 | mNavigator.onPageSelected(position);
35 | }
36 | }
37 |
38 | public void onPageScrollStateChanged(int state) {
39 | if (mNavigator != null) {
40 | mNavigator.onPageScrollStateChanged(state);
41 | }
42 | }
43 |
44 | public IPagerNavigator getNavigator() {
45 | return mNavigator;
46 | }
47 |
48 | public void setNavigator(IPagerNavigator navigator) {
49 | if (mNavigator == navigator) {
50 | return;
51 | }
52 | if (mNavigator != null) {
53 | mNavigator.onDetachFromMagicIndicator();
54 | }
55 | mNavigator = navigator;
56 | removeAllViews();
57 | if (mNavigator instanceof View) {
58 | LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
59 | addView((View) mNavigator, lp);
60 | mNavigator.onAttachToMagicIndicator();
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/NavigatorHelper.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator;
2 |
3 | import android.util.SparseArray;
4 | import android.util.SparseBooleanArray;
5 |
6 | /**
7 | * 方便扩展IPagerNavigator的帮助类,将ViewPager的3个回调方法转换成
8 | * onSelected、onDeselected、onEnter等回调,方便扩展
9 | * 博客: http://hackware.lucode.net
10 | * Created by hackware on 2016/6/26.
11 | */
12 | public class NavigatorHelper {
13 | private SparseBooleanArray mDeselectedItems = new SparseBooleanArray();
14 | private SparseArray mLeavedPercents = new SparseArray();
15 |
16 | private int mTotalCount;
17 | private int mCurrentIndex;
18 | private int mLastIndex;
19 | private float mLastPositionOffsetSum;
20 | private int mScrollState;
21 |
22 | private boolean mSkimOver;
23 | private NavigatorHelper.OnNavigatorScrollListener mNavigatorScrollListener;
24 |
25 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
26 | float currentPositionOffsetSum = position + positionOffset;
27 | boolean leftToRight = false;
28 | if (mLastPositionOffsetSum <= currentPositionOffsetSum) {
29 | leftToRight = true;
30 | }
31 | if (mScrollState != ScrollState.SCROLL_STATE_IDLE) {
32 | if (currentPositionOffsetSum == mLastPositionOffsetSum) {
33 | return;
34 | }
35 | int nextPosition = position + 1;
36 | boolean normalDispatch = true;
37 | if (positionOffset == 0.0f) {
38 | if (leftToRight) {
39 | nextPosition = position - 1;
40 | normalDispatch = false;
41 | }
42 | }
43 | for (int i = 0; i < mTotalCount; i++) {
44 | if (i == position || i == nextPosition) {
45 | continue;
46 | }
47 | Float leavedPercent = mLeavedPercents.get(i, 0.0f);
48 | if (leavedPercent != 1.0f) {
49 | dispatchOnLeave(i, 1.0f, leftToRight, true);
50 | }
51 | }
52 | if (normalDispatch) {
53 | if (leftToRight) {
54 | dispatchOnLeave(position, positionOffset, true, false);
55 | dispatchOnEnter(nextPosition, positionOffset, true, false);
56 | } else {
57 | dispatchOnLeave(nextPosition, 1.0f - positionOffset, false, false);
58 | dispatchOnEnter(position, 1.0f - positionOffset, false, false);
59 | }
60 | } else {
61 | dispatchOnLeave(nextPosition, 1.0f - positionOffset, true, false);
62 | dispatchOnEnter(position, 1.0f - positionOffset, true, false);
63 | }
64 | } else {
65 | for (int i = 0; i < mTotalCount; i++) {
66 | if (i == mCurrentIndex) {
67 | continue;
68 | }
69 | boolean deselected = mDeselectedItems.get(i);
70 | if (!deselected) {
71 | dispatchOnDeselected(i);
72 | }
73 | Float leavedPercent = mLeavedPercents.get(i, 0.0f);
74 | if (leavedPercent != 1.0f) {
75 | dispatchOnLeave(i, 1.0f, false, true);
76 | }
77 | }
78 | dispatchOnEnter(mCurrentIndex, 1.0f, false, true);
79 | dispatchOnSelected(mCurrentIndex);
80 | }
81 | mLastPositionOffsetSum = currentPositionOffsetSum;
82 | }
83 |
84 | private void dispatchOnEnter(int index, float enterPercent, boolean leftToRight, boolean force) {
85 | if (mSkimOver || index == mCurrentIndex || mScrollState == ScrollState.SCROLL_STATE_DRAGGING || force) {
86 | if (mNavigatorScrollListener != null) {
87 | mNavigatorScrollListener.onEnter(index, mTotalCount, enterPercent, leftToRight);
88 | }
89 | mLeavedPercents.put(index, 1.0f - enterPercent);
90 | }
91 | }
92 |
93 | private void dispatchOnLeave(int index, float leavePercent, boolean leftToRight, boolean force) {
94 | if (mSkimOver || index == mLastIndex || mScrollState == ScrollState.SCROLL_STATE_DRAGGING || ((index == mCurrentIndex - 1 || index == mCurrentIndex + 1) && mLeavedPercents.get(index, 0.0f) != 1.0f) || force) {
95 | if (mNavigatorScrollListener != null) {
96 | mNavigatorScrollListener.onLeave(index, mTotalCount, leavePercent, leftToRight);
97 | }
98 | mLeavedPercents.put(index, leavePercent);
99 | }
100 | }
101 |
102 | private void dispatchOnSelected(int index) {
103 | if (mNavigatorScrollListener != null) {
104 | mNavigatorScrollListener.onSelected(index, mTotalCount);
105 | }
106 | mDeselectedItems.put(index, false);
107 | }
108 |
109 | private void dispatchOnDeselected(int index) {
110 | if (mNavigatorScrollListener != null) {
111 | mNavigatorScrollListener.onDeselected(index, mTotalCount);
112 | }
113 | mDeselectedItems.put(index, true);
114 | }
115 |
116 | public void onPageSelected(int position) {
117 | mLastIndex = mCurrentIndex;
118 | mCurrentIndex = position;
119 | dispatchOnSelected(mCurrentIndex);
120 | for (int i = 0; i < mTotalCount; i++) {
121 | if (i == mCurrentIndex) {
122 | continue;
123 | }
124 | boolean deselected = mDeselectedItems.get(i);
125 | if (!deselected) {
126 | dispatchOnDeselected(i);
127 | }
128 | }
129 | }
130 |
131 | public void onPageScrollStateChanged(int state) {
132 | mScrollState = state;
133 | }
134 |
135 | public void setNavigatorScrollListener(NavigatorHelper.OnNavigatorScrollListener navigatorScrollListener) {
136 | mNavigatorScrollListener = navigatorScrollListener;
137 | }
138 |
139 | public void setSkimOver(boolean skimOver) {
140 | mSkimOver = skimOver;
141 | }
142 |
143 | public int getTotalCount() {
144 | return mTotalCount;
145 | }
146 |
147 | public void setTotalCount(int totalCount) {
148 | mTotalCount = totalCount;
149 | mDeselectedItems.clear();
150 | mLeavedPercents.clear();
151 | }
152 |
153 | public int getCurrentIndex() {
154 | return mCurrentIndex;
155 | }
156 |
157 | public int getScrollState() {
158 | return mScrollState;
159 | }
160 |
161 | public interface OnNavigatorScrollListener {
162 | void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight);
163 |
164 | void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight);
165 |
166 | void onSelected(int index, int totalCount);
167 |
168 | void onDeselected(int index, int totalCount);
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/ScrollState.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator;
2 |
3 | /**
4 | * 自定义滚动状态,消除对ViewPager的依赖
5 | * Created by hackware on 2016/8/27.
6 | */
7 |
8 | public interface ScrollState {
9 | int SCROLL_STATE_IDLE = 0;
10 | int SCROLL_STATE_DRAGGING = 1;
11 | int SCROLL_STATE_SETTLING = 2;
12 | }
13 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/ViewPagerHelper.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator;
2 |
3 | import androidx.viewpager.widget.ViewPager;
4 |
5 | /**
6 | * 简化和ViewPager绑定
7 | * Created by hackware on 2016/8/17.
8 | */
9 |
10 | public class ViewPagerHelper {
11 | public static void bind(final MagicIndicator magicIndicator, ViewPager viewPager) {
12 | viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
13 |
14 | @Override
15 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
16 | magicIndicator.onPageScrolled(position, positionOffset, positionOffsetPixels);
17 | }
18 |
19 | @Override
20 | public void onPageSelected(int position) {
21 | magicIndicator.onPageSelected(position);
22 | }
23 |
24 | @Override
25 | public void onPageScrollStateChanged(int state) {
26 | magicIndicator.onPageScrollStateChanged(state);
27 | }
28 | });
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/abs/IPagerNavigator.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.abs;
2 |
3 | /**
4 | * 抽象的ViewPager导航器
5 | * 博客: http://hackware.lucode.net
6 | * Created by hackware on 2016/6/26.
7 | */
8 | public interface IPagerNavigator {
9 |
10 | ///////////////////////// ViewPager的3个回调
11 | void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
12 |
13 | void onPageSelected(int position);
14 |
15 | void onPageScrollStateChanged(int state);
16 | /////////////////////////
17 |
18 | /**
19 | * 当IPagerNavigator被添加到MagicIndicator时调用
20 | */
21 | void onAttachToMagicIndicator();
22 |
23 | /**
24 | * 当IPagerNavigator从MagicIndicator上移除时调用
25 | */
26 | void onDetachFromMagicIndicator();
27 |
28 | /**
29 | * ViewPager内容改变时需要先调用此方法,自定义的IPagerNavigator应当遵守此约定
30 | */
31 | void notifyDataSetChanged();
32 | }
33 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/ArgbEvaluatorHolder.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins;
2 |
3 |
4 | /**
5 | * 实现颜色渐变,考虑到兼容性,不使用内置的ArgbEvaluator
6 | * 博客: http://hackware.lucode.net
7 | * Created by hackware on 2016/6/26.
8 | */
9 | public class ArgbEvaluatorHolder {
10 | public static int eval(float fraction, int startValue, int endValue) {
11 | int startA = (startValue >> 24) & 0xff;
12 | int startR = (startValue >> 16) & 0xff;
13 | int startG = (startValue >> 8) & 0xff;
14 | int startB = startValue & 0xff;
15 |
16 | int endA = (endValue >> 24) & 0xff;
17 | int endR = (endValue >> 16) & 0xff;
18 | int endG = (endValue >> 8) & 0xff;
19 | int endB = endValue & 0xff;
20 |
21 | int currentA = (startA + (int) (fraction * (endA - startA))) << 24;
22 | int currentR = (startR + (int) (fraction * (endR - startR))) << 16;
23 | int currentG = (startG + (int) (fraction * (endG - startG))) << 8;
24 | int currentB = startB + (int) (fraction * (endB - startB));
25 |
26 | return currentA | currentR | currentG | currentB;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/UIUtil.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | * 博客: http://hackware.lucode.net
7 | * Created by hackware on 2016/6/26.
8 | */
9 | public final class UIUtil {
10 |
11 | public static int dip2px(Context context, double dpValue) {
12 | float density = context.getResources().getDisplayMetrics().density;
13 | return (int) (dpValue * density + 0.5);
14 | }
15 |
16 | public static int getScreenWidth(Context context) {
17 | return context.getResources().getDisplayMetrics().widthPixels;
18 | }
19 | }
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/circlenavigator/CircleNavigator.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.circlenavigator;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.PointF;
7 | import android.view.MotionEvent;
8 | import android.view.View;
9 | import android.view.ViewConfiguration;
10 | import android.view.animation.Interpolator;
11 | import android.view.animation.LinearInterpolator;
12 |
13 | import net.lucode.hackware.magicindicator.abs.IPagerNavigator;
14 | import net.lucode.hackware.magicindicator.buildins.UIUtil;
15 |
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 | /**
20 | * 圆圈式的指示器
21 | * 博客: http://hackware.lucode.net
22 | * Created by hackware on 2016/6/26.
23 | */
24 | public class CircleNavigator extends View implements IPagerNavigator {
25 | private int mRadius;
26 | private int mCircleColor;
27 | private int mStrokeWidth;
28 | private int mCircleSpacing;
29 | private int mCurrentIndex;
30 | private int mTotalCount;
31 | private Interpolator mStartInterpolator = new LinearInterpolator();
32 |
33 | private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
34 | private List mCirclePoints = new ArrayList();
35 | private float mIndicatorX;
36 |
37 | // 事件回调
38 | private boolean mTouchable;
39 | private OnCircleClickListener mCircleClickListener;
40 | private float mDownX;
41 | private float mDownY;
42 | private int mTouchSlop;
43 |
44 | private boolean mFollowTouch = true; // 是否跟随手指滑动
45 |
46 | public CircleNavigator(Context context) {
47 | super(context);
48 | init(context);
49 | }
50 |
51 | private void init(Context context) {
52 | mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
53 | mRadius = UIUtil.dip2px(context, 3);
54 | mCircleSpacing = UIUtil.dip2px(context, 8);
55 | mStrokeWidth = UIUtil.dip2px(context, 1);
56 | }
57 |
58 | @Override
59 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
60 | setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
61 | }
62 |
63 | private int measureWidth(int widthMeasureSpec) {
64 | int mode = MeasureSpec.getMode(widthMeasureSpec);
65 | int width = MeasureSpec.getSize(widthMeasureSpec);
66 | int result = 0;
67 | switch (mode) {
68 | case MeasureSpec.EXACTLY:
69 | result = width;
70 | break;
71 | case MeasureSpec.AT_MOST:
72 | case MeasureSpec.UNSPECIFIED:
73 | result = mTotalCount * mRadius * 2 + (mTotalCount - 1) * mCircleSpacing + getPaddingLeft() + getPaddingRight() + mStrokeWidth * 2;
74 | break;
75 | default:
76 | break;
77 | }
78 | return result;
79 | }
80 |
81 | private int measureHeight(int heightMeasureSpec) {
82 | int mode = MeasureSpec.getMode(heightMeasureSpec);
83 | int height = MeasureSpec.getSize(heightMeasureSpec);
84 | int result = 0;
85 | switch (mode) {
86 | case MeasureSpec.EXACTLY:
87 | result = height;
88 | break;
89 | case MeasureSpec.AT_MOST:
90 | case MeasureSpec.UNSPECIFIED:
91 | result = mRadius * 2 + mStrokeWidth * 2 + getPaddingTop() + getPaddingBottom();
92 | break;
93 | default:
94 | break;
95 | }
96 | return result;
97 | }
98 |
99 | @Override
100 | protected void onDraw(Canvas canvas) {
101 | mPaint.setColor(mCircleColor);
102 | drawCircles(canvas);
103 | drawIndicator(canvas);
104 | }
105 |
106 | private void drawCircles(Canvas canvas) {
107 | mPaint.setStyle(Paint.Style.STROKE);
108 | mPaint.setStrokeWidth(mStrokeWidth);
109 | for (int i = 0, j = mCirclePoints.size(); i < j; i++) {
110 | PointF pointF = mCirclePoints.get(i);
111 | canvas.drawCircle(pointF.x, pointF.y, mRadius, mPaint);
112 | }
113 | }
114 |
115 | private void drawIndicator(Canvas canvas) {
116 | mPaint.setStyle(Paint.Style.FILL);
117 | if (mCirclePoints.size() > 0) {
118 | canvas.drawCircle(mIndicatorX, (int) (getHeight() / 2.0f + 0.5f), mRadius, mPaint);
119 | }
120 | }
121 |
122 | private void prepareCirclePoints() {
123 | mCirclePoints.clear();
124 | if (mTotalCount > 0) {
125 | int y = (int) (getHeight() / 2.0f + 0.5f);
126 | int centerSpacing = mRadius * 2 + mCircleSpacing;
127 | int startX = mRadius + (int) (mStrokeWidth / 2.0f + 0.5f) + getPaddingLeft();
128 | for (int i = 0; i < mTotalCount; i++) {
129 | PointF pointF = new PointF(startX, y);
130 | mCirclePoints.add(pointF);
131 | startX += centerSpacing;
132 | }
133 | mIndicatorX = mCirclePoints.get(mCurrentIndex).x;
134 | }
135 | }
136 |
137 | @Override
138 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
139 | if (mFollowTouch) {
140 | if (mCirclePoints.isEmpty()) {
141 | return;
142 | }
143 |
144 | int currentPosition = Math.min(mCirclePoints.size() - 1, position);
145 | int nextPosition = Math.min(mCirclePoints.size() - 1, position + 1);
146 | PointF current = mCirclePoints.get(currentPosition);
147 | PointF next = mCirclePoints.get(nextPosition);
148 |
149 | mIndicatorX = current.x + (next.x - current.x) * mStartInterpolator.getInterpolation(positionOffset);
150 |
151 | invalidate();
152 | }
153 | }
154 |
155 | @Override
156 | public boolean onTouchEvent(MotionEvent event) {
157 | float x = event.getX();
158 | float y = event.getY();
159 | switch (event.getAction()) {
160 | case MotionEvent.ACTION_DOWN:
161 | if (mTouchable) {
162 | mDownX = x;
163 | mDownY = y;
164 | return true;
165 | }
166 | break;
167 | case MotionEvent.ACTION_UP:
168 | if (mCircleClickListener != null) {
169 | if (Math.abs(x - mDownX) <= mTouchSlop && Math.abs(y - mDownY) <= mTouchSlop) {
170 | float max = Float.MAX_VALUE;
171 | int index = 0;
172 | for (int i = 0; i < mCirclePoints.size(); i++) {
173 | PointF pointF = mCirclePoints.get(i);
174 | float offset = Math.abs(pointF.x - x);
175 | if (offset < max) {
176 | max = offset;
177 | index = i;
178 | }
179 | }
180 | mCircleClickListener.onClick(index);
181 | }
182 | }
183 | break;
184 | default:
185 | break;
186 | }
187 | return super.onTouchEvent(event);
188 | }
189 |
190 | @Override
191 | public void onPageSelected(int position) {
192 | mCurrentIndex = position;
193 | if (!mFollowTouch) {
194 | mIndicatorX = mCirclePoints.get(mCurrentIndex).x;
195 | invalidate();
196 | }
197 | }
198 |
199 | @Override
200 | public void onPageScrollStateChanged(int state) {
201 | }
202 |
203 | @Override
204 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
205 | prepareCirclePoints();
206 | }
207 |
208 | @Override
209 | public void onAttachToMagicIndicator() {
210 | }
211 |
212 | @Override
213 | public void notifyDataSetChanged() {
214 | prepareCirclePoints();
215 | invalidate();
216 | }
217 |
218 | @Override
219 | public void onDetachFromMagicIndicator() {
220 | }
221 |
222 | public int getRadius() {
223 | return mRadius;
224 | }
225 |
226 | public void setRadius(int radius) {
227 | mRadius = radius;
228 | prepareCirclePoints();
229 | invalidate();
230 | }
231 |
232 | public int getCircleColor() {
233 | return mCircleColor;
234 | }
235 |
236 | public void setCircleColor(int circleColor) {
237 | mCircleColor = circleColor;
238 | invalidate();
239 | }
240 |
241 | public int getStrokeWidth() {
242 | return mStrokeWidth;
243 | }
244 |
245 | public void setStrokeWidth(int strokeWidth) {
246 | mStrokeWidth = strokeWidth;
247 | invalidate();
248 | }
249 |
250 | public int getCircleSpacing() {
251 | return mCircleSpacing;
252 | }
253 |
254 | public void setCircleSpacing(int circleSpacing) {
255 | mCircleSpacing = circleSpacing;
256 | prepareCirclePoints();
257 | invalidate();
258 | }
259 |
260 | public Interpolator getStartInterpolator() {
261 | return mStartInterpolator;
262 | }
263 |
264 | public void setStartInterpolator(Interpolator startInterpolator) {
265 | mStartInterpolator = startInterpolator;
266 | if (mStartInterpolator == null) {
267 | mStartInterpolator = new LinearInterpolator();
268 | }
269 | }
270 |
271 | public int getCircleCount() {
272 | return mTotalCount;
273 | }
274 |
275 | public void setCircleCount(int count) {
276 | mTotalCount = count; // 此处不调用invalidate,让外部调用notifyDataSetChanged
277 | }
278 |
279 | public boolean isTouchable() {
280 | return mTouchable;
281 | }
282 |
283 | public void setTouchable(boolean touchable) {
284 | mTouchable = touchable;
285 | }
286 |
287 | public boolean isFollowTouch() {
288 | return mFollowTouch;
289 | }
290 |
291 | public void setFollowTouch(boolean followTouch) {
292 | mFollowTouch = followTouch;
293 | }
294 |
295 | public OnCircleClickListener getCircleClickListener() {
296 | return mCircleClickListener;
297 | }
298 |
299 | public void setCircleClickListener(OnCircleClickListener circleClickListener) {
300 | if (!mTouchable) {
301 | mTouchable = true;
302 | }
303 | mCircleClickListener = circleClickListener;
304 | }
305 |
306 | public interface OnCircleClickListener {
307 | void onClick(int index);
308 | }
309 | }
310 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/CommonNavigatorAdapter.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.abs;
2 |
3 | import android.content.Context;
4 | import android.database.DataSetObservable;
5 | import android.database.DataSetObserver;
6 |
7 | /**
8 | * CommonNavigator适配器,通过它可轻松切换不同的指示器样式
9 | * 博客: http://hackware.lucode.net
10 | * Created by hackware on 2016/6/26.
11 | */
12 | public abstract class CommonNavigatorAdapter {
13 |
14 | private final DataSetObservable mDataSetObservable = new DataSetObservable();
15 |
16 | public abstract int getCount();
17 |
18 | public abstract IPagerTitleView getTitleView(Context context, int index);
19 |
20 | public abstract IPagerIndicator getIndicator(Context context);
21 |
22 | public float getTitleWeight(Context context, int index) {
23 | return 1;
24 | }
25 |
26 | public final void registerDataSetObserver(DataSetObserver observer) {
27 | mDataSetObservable.registerObserver(observer);
28 | }
29 |
30 | public final void unregisterDataSetObserver(DataSetObserver observer) {
31 | mDataSetObservable.unregisterObserver(observer);
32 | }
33 |
34 | public final void notifyDataSetChanged() {
35 | mDataSetObservable.notifyChanged();
36 | }
37 |
38 | public final void notifyDataSetInvalidated() {
39 | mDataSetObservable.notifyInvalidated();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/IMeasurablePagerTitleView.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.abs;
2 |
3 | /**
4 | * 可测量内容区域的指示器标题
5 | * 博客: http://hackware.lucode.net
6 | * Created by hackware on 2016/6/26.
7 | */
8 | public interface IMeasurablePagerTitleView extends IPagerTitleView {
9 | int getContentLeft();
10 |
11 | int getContentTop();
12 |
13 | int getContentRight();
14 |
15 | int getContentBottom();
16 | }
17 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/IPagerIndicator.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.abs;
2 |
3 |
4 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * 抽象的viewpager指示器,适用于CommonNavigator
10 | * 博客: http://hackware.lucode.net
11 | * Created by hackware on 2016/6/26.
12 | */
13 | public interface IPagerIndicator {
14 | void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
15 |
16 | void onPageSelected(int position);
17 |
18 | void onPageScrollStateChanged(int state);
19 |
20 | void onPositionDataProvide(List dataList);
21 | }
22 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/IPagerTitleView.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.abs;
2 |
3 | /**
4 | * 抽象的指示器标题,适用于CommonNavigator
5 | * 博客: http://hackware.lucode.net
6 | * Created by hackware on 2016/6/26.
7 | */
8 | public interface IPagerTitleView {
9 | /**
10 | * 被选中
11 | */
12 | void onSelected(int index, int totalCount);
13 |
14 | /**
15 | * 未被选中
16 | */
17 | void onDeselected(int index, int totalCount);
18 |
19 | /**
20 | * 离开
21 | *
22 | * @param leavePercent 离开的百分比, 0.0f - 1.0f
23 | * @param leftToRight 从左至右离开
24 | */
25 | void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight);
26 |
27 | /**
28 | * 进入
29 | *
30 | * @param enterPercent 进入的百分比, 0.0f - 1.0f
31 | * @param leftToRight 从左至右离开
32 | */
33 | void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight);
34 | }
35 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/BezierPagerIndicator.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.Path;
7 | import android.view.View;
8 | import android.view.animation.AccelerateInterpolator;
9 | import android.view.animation.DecelerateInterpolator;
10 | import android.view.animation.Interpolator;
11 |
12 | import net.lucode.hackware.magicindicator.FragmentContainerHelper;
13 | import net.lucode.hackware.magicindicator.buildins.ArgbEvaluatorHolder;
14 | import net.lucode.hackware.magicindicator.buildins.UIUtil;
15 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
16 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData;
17 |
18 | import java.util.Arrays;
19 | import java.util.List;
20 |
21 | /**
22 | * 贝塞尔曲线ViewPager指示器,带颜色渐变
23 | * 博客: http://hackware.lucode.net
24 | * Created by hackware on 2016/6/26.
25 | */
26 | public class BezierPagerIndicator extends View implements IPagerIndicator {
27 | private List mPositionDataList;
28 |
29 | private float mLeftCircleRadius;
30 | private float mLeftCircleX;
31 | private float mRightCircleRadius;
32 | private float mRightCircleX;
33 |
34 | private float mYOffset;
35 | private float mMaxCircleRadius;
36 | private float mMinCircleRadius;
37 |
38 | private Paint mPaint;
39 | private Path mPath = new Path();
40 |
41 | private List mColors;
42 | private Interpolator mStartInterpolator = new AccelerateInterpolator();
43 | private Interpolator mEndInterpolator = new DecelerateInterpolator();
44 |
45 | public BezierPagerIndicator(Context context) {
46 | super(context);
47 | init(context);
48 | }
49 |
50 | private void init(Context context) {
51 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
52 | mPaint.setStyle(Paint.Style.FILL);
53 | mMaxCircleRadius = UIUtil.dip2px(context, 3.5);
54 | mMinCircleRadius = UIUtil.dip2px(context, 2);
55 | mYOffset = UIUtil.dip2px(context, 1.5);
56 | }
57 |
58 | @Override
59 | protected void onDraw(Canvas canvas) {
60 | canvas.drawCircle(mLeftCircleX, getHeight() - mYOffset - mMaxCircleRadius, mLeftCircleRadius, mPaint);
61 | canvas.drawCircle(mRightCircleX, getHeight() - mYOffset - mMaxCircleRadius, mRightCircleRadius, mPaint);
62 | drawBezierCurve(canvas);
63 | }
64 |
65 | /**
66 | * 绘制贝塞尔曲线
67 | *
68 | * @param canvas
69 | */
70 | private void drawBezierCurve(Canvas canvas) {
71 | mPath.reset();
72 | float y = getHeight() - mYOffset - mMaxCircleRadius;
73 | mPath.moveTo(mRightCircleX, y);
74 | mPath.lineTo(mRightCircleX, y - mRightCircleRadius);
75 | mPath.quadTo(mRightCircleX + (mLeftCircleX - mRightCircleX) / 2.0f, y, mLeftCircleX, y - mLeftCircleRadius);
76 | mPath.lineTo(mLeftCircleX, y + mLeftCircleRadius);
77 | mPath.quadTo(mRightCircleX + (mLeftCircleX - mRightCircleX) / 2.0f, y, mRightCircleX, y + mRightCircleRadius);
78 | mPath.close(); // 闭合
79 | canvas.drawPath(mPath, mPaint);
80 | }
81 |
82 | @Override
83 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
84 | if (mPositionDataList == null || mPositionDataList.isEmpty()) {
85 | return;
86 | }
87 |
88 | // 计算颜色
89 | if (mColors != null && mColors.size() > 0) {
90 | int currentColor = mColors.get(Math.abs(position) % mColors.size());
91 | int nextColor = mColors.get(Math.abs(position + 1) % mColors.size());
92 | int color = ArgbEvaluatorHolder.eval(positionOffset, currentColor, nextColor);
93 | mPaint.setColor(color);
94 | }
95 |
96 | // 计算锚点位置
97 | PositionData current = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position);
98 | PositionData next = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position + 1);
99 |
100 | float leftX = current.mLeft + (current.mRight - current.mLeft) / 2;
101 | float rightX = next.mLeft + (next.mRight - next.mLeft) / 2;
102 |
103 | mLeftCircleX = leftX + (rightX - leftX) * mStartInterpolator.getInterpolation(positionOffset);
104 | mRightCircleX = leftX + (rightX - leftX) * mEndInterpolator.getInterpolation(positionOffset);
105 | mLeftCircleRadius = mMaxCircleRadius + (mMinCircleRadius - mMaxCircleRadius) * mEndInterpolator.getInterpolation(positionOffset);
106 | mRightCircleRadius = mMinCircleRadius + (mMaxCircleRadius - mMinCircleRadius) * mStartInterpolator.getInterpolation(positionOffset);
107 |
108 | invalidate();
109 | }
110 |
111 | @Override
112 | public void onPageSelected(int position) {
113 | }
114 |
115 | @Override
116 | public void onPageScrollStateChanged(int state) {
117 | }
118 |
119 | @Override
120 | public void onPositionDataProvide(List dataList) {
121 | mPositionDataList = dataList;
122 | }
123 |
124 | public float getMaxCircleRadius() {
125 | return mMaxCircleRadius;
126 | }
127 |
128 | public void setMaxCircleRadius(float maxCircleRadius) {
129 | mMaxCircleRadius = maxCircleRadius;
130 | }
131 |
132 | public float getMinCircleRadius() {
133 | return mMinCircleRadius;
134 | }
135 |
136 | public void setMinCircleRadius(float minCircleRadius) {
137 | mMinCircleRadius = minCircleRadius;
138 | }
139 |
140 | public float getYOffset() {
141 | return mYOffset;
142 | }
143 |
144 | public void setYOffset(float yOffset) {
145 | mYOffset = yOffset;
146 | }
147 |
148 | public void setColors(Integer... colors) {
149 | mColors = Arrays.asList(colors);
150 | }
151 |
152 | public void setStartInterpolator(Interpolator startInterpolator) {
153 | mStartInterpolator = startInterpolator;
154 | if (mStartInterpolator == null) {
155 | mStartInterpolator = new AccelerateInterpolator();
156 | }
157 | }
158 |
159 | public void setEndInterpolator(Interpolator endInterpolator) {
160 | mEndInterpolator = endInterpolator;
161 | if (mEndInterpolator == null) {
162 | mEndInterpolator = new DecelerateInterpolator();
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/LinePagerIndicator.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.RectF;
7 | import android.view.View;
8 | import android.view.animation.Interpolator;
9 | import android.view.animation.LinearInterpolator;
10 |
11 | import net.lucode.hackware.magicindicator.FragmentContainerHelper;
12 | import net.lucode.hackware.magicindicator.buildins.ArgbEvaluatorHolder;
13 | import net.lucode.hackware.magicindicator.buildins.UIUtil;
14 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
15 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData;
16 |
17 | import java.util.Arrays;
18 | import java.util.List;
19 |
20 | /**
21 | * 直线viewpager指示器,带颜色渐变
22 | * 博客: http://hackware.lucode.net
23 | * Created by hackware on 2016/6/26.
24 | */
25 | public class LinePagerIndicator extends View implements IPagerIndicator {
26 | public static final int MODE_MATCH_EDGE = 0; // 直线宽度 == title宽度 - 2 * mXOffset
27 | public static final int MODE_WRAP_CONTENT = 1; // 直线宽度 == title内容宽度 - 2 * mXOffset
28 | public static final int MODE_EXACTLY = 2; // 直线宽度 == mLineWidth
29 |
30 | private int mMode; // 默认为MODE_MATCH_EDGE模式
31 |
32 | // 控制动画
33 | private Interpolator mStartInterpolator = new LinearInterpolator();
34 | private Interpolator mEndInterpolator = new LinearInterpolator();
35 |
36 | private float mYOffset; // 相对于底部的偏移量,如果你想让直线位于title上方,设置它即可
37 | private float mLineHeight;
38 | private float mXOffset;
39 | private float mLineWidth;
40 | private float mRoundRadius;
41 |
42 | private Paint mPaint;
43 | private List mPositionDataList;
44 | private List mColors;
45 |
46 | private RectF mLineRect = new RectF();
47 |
48 | public LinePagerIndicator(Context context) {
49 | super(context);
50 | init(context);
51 | }
52 |
53 | private void init(Context context) {
54 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
55 | mPaint.setStyle(Paint.Style.FILL);
56 | mLineHeight = UIUtil.dip2px(context, 3);
57 | mLineWidth = UIUtil.dip2px(context, 10);
58 | }
59 |
60 | @Override
61 | protected void onDraw(Canvas canvas) {
62 | canvas.drawRoundRect(mLineRect, mRoundRadius, mRoundRadius, mPaint);
63 | }
64 |
65 | @Override
66 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
67 | if (mPositionDataList == null || mPositionDataList.isEmpty()) {
68 | return;
69 | }
70 |
71 | // 计算颜色
72 | if (mColors != null && mColors.size() > 0) {
73 | int currentColor = mColors.get(Math.abs(position) % mColors.size());
74 | int nextColor = mColors.get(Math.abs(position + 1) % mColors.size());
75 | int color = ArgbEvaluatorHolder.eval(positionOffset, currentColor, nextColor);
76 | mPaint.setColor(color);
77 | }
78 |
79 | // 计算锚点位置
80 | PositionData current = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position);
81 | PositionData next = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position + 1);
82 |
83 | float leftX;
84 | float nextLeftX;
85 | float rightX;
86 | float nextRightX;
87 | if (mMode == MODE_MATCH_EDGE) {
88 | leftX = current.mLeft + mXOffset;
89 | nextLeftX = next.mLeft + mXOffset;
90 | rightX = current.mRight - mXOffset;
91 | nextRightX = next.mRight - mXOffset;
92 | } else if (mMode == MODE_WRAP_CONTENT) {
93 | leftX = current.mContentLeft + mXOffset;
94 | nextLeftX = next.mContentLeft + mXOffset;
95 | rightX = current.mContentRight - mXOffset;
96 | nextRightX = next.mContentRight - mXOffset;
97 | } else { // MODE_EXACTLY
98 | leftX = current.mLeft + (current.width() - mLineWidth) / 2;
99 | nextLeftX = next.mLeft + (next.width() - mLineWidth) / 2;
100 | rightX = current.mLeft + (current.width() + mLineWidth) / 2;
101 | nextRightX = next.mLeft + (next.width() + mLineWidth) / 2;
102 | }
103 |
104 | mLineRect.left = leftX + (nextLeftX - leftX) * mStartInterpolator.getInterpolation(positionOffset);
105 | mLineRect.right = rightX + (nextRightX - rightX) * mEndInterpolator.getInterpolation(positionOffset);
106 | mLineRect.top = getHeight() - mLineHeight - mYOffset;
107 | mLineRect.bottom = getHeight() - mYOffset;
108 |
109 | invalidate();
110 | }
111 |
112 | @Override
113 | public void onPageSelected(int position) {
114 | }
115 |
116 | @Override
117 | public void onPageScrollStateChanged(int state) {
118 | }
119 |
120 | @Override
121 | public void onPositionDataProvide(List dataList) {
122 | mPositionDataList = dataList;
123 | }
124 |
125 | public float getYOffset() {
126 | return mYOffset;
127 | }
128 |
129 | public void setYOffset(float yOffset) {
130 | mYOffset = yOffset;
131 | }
132 |
133 | public float getXOffset() {
134 | return mXOffset;
135 | }
136 |
137 | public void setXOffset(float xOffset) {
138 | mXOffset = xOffset;
139 | }
140 |
141 | public float getLineHeight() {
142 | return mLineHeight;
143 | }
144 |
145 | public void setLineHeight(float lineHeight) {
146 | mLineHeight = lineHeight;
147 | }
148 |
149 | public float getLineWidth() {
150 | return mLineWidth;
151 | }
152 |
153 | public void setLineWidth(float lineWidth) {
154 | mLineWidth = lineWidth;
155 | }
156 |
157 | public float getRoundRadius() {
158 | return mRoundRadius;
159 | }
160 |
161 | public void setRoundRadius(float roundRadius) {
162 | mRoundRadius = roundRadius;
163 | }
164 |
165 | public int getMode() {
166 | return mMode;
167 | }
168 |
169 | public void setMode(int mode) {
170 | if (mode == MODE_EXACTLY || mode == MODE_MATCH_EDGE || mode == MODE_WRAP_CONTENT) {
171 | mMode = mode;
172 | } else {
173 | throw new IllegalArgumentException("mode " + mode + " not supported.");
174 | }
175 | }
176 |
177 | public Paint getPaint() {
178 | return mPaint;
179 | }
180 |
181 | public List getColors() {
182 | return mColors;
183 | }
184 |
185 | public void setColors(Integer... colors) {
186 | mColors = Arrays.asList(colors);
187 | }
188 |
189 | public Interpolator getStartInterpolator() {
190 | return mStartInterpolator;
191 | }
192 |
193 | public void setStartInterpolator(Interpolator startInterpolator) {
194 | mStartInterpolator = startInterpolator;
195 | if (mStartInterpolator == null) {
196 | mStartInterpolator = new LinearInterpolator();
197 | }
198 | }
199 |
200 | public Interpolator getEndInterpolator() {
201 | return mEndInterpolator;
202 | }
203 |
204 | public void setEndInterpolator(Interpolator endInterpolator) {
205 | mEndInterpolator = endInterpolator;
206 | if (mEndInterpolator == null) {
207 | mEndInterpolator = new LinearInterpolator();
208 | }
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/TestPagerIndicator.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.RectF;
8 | import android.view.View;
9 |
10 | import net.lucode.hackware.magicindicator.FragmentContainerHelper;
11 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
12 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData;
13 |
14 | import java.util.List;
15 |
16 |
17 | /**
18 | * 用于测试的指示器,可用来检测自定义的IMeasurablePagerTitleView是否正确测量内容区域
19 | * 博客: http://hackware.lucode.net
20 | * Created by hackware on 2016/6/26.
21 | */
22 | public class TestPagerIndicator extends View implements IPagerIndicator {
23 | private Paint mPaint;
24 | private int mOutRectColor;
25 | private int mInnerRectColor;
26 | private RectF mOutRect = new RectF();
27 | private RectF mInnerRect = new RectF();
28 |
29 | private List mPositionDataList;
30 |
31 | public TestPagerIndicator(Context context) {
32 | super(context);
33 | init(context);
34 | }
35 |
36 | private void init(Context context) {
37 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
38 | mPaint.setStyle(Paint.Style.STROKE);
39 | mOutRectColor = Color.RED;
40 | mInnerRectColor = Color.GREEN;
41 | }
42 |
43 | @Override
44 | protected void onDraw(Canvas canvas) {
45 | mPaint.setColor(mOutRectColor);
46 | canvas.drawRect(mOutRect, mPaint);
47 | mPaint.setColor(mInnerRectColor);
48 | canvas.drawRect(mInnerRect, mPaint);
49 | }
50 |
51 | @Override
52 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
53 | if (mPositionDataList == null || mPositionDataList.isEmpty()) {
54 | return;
55 | }
56 |
57 | // 计算锚点位置
58 | PositionData current = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position);
59 | PositionData next = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position + 1);
60 |
61 | mOutRect.left = current.mLeft + (next.mLeft - current.mLeft) * positionOffset;
62 | mOutRect.top = current.mTop + (next.mTop - current.mTop) * positionOffset;
63 | mOutRect.right = current.mRight + (next.mRight - current.mRight) * positionOffset;
64 | mOutRect.bottom = current.mBottom + (next.mBottom - current.mBottom) * positionOffset;
65 |
66 | mInnerRect.left = current.mContentLeft + (next.mContentLeft - current.mContentLeft) * positionOffset;
67 | mInnerRect.top = current.mContentTop + (next.mContentTop - current.mContentTop) * positionOffset;
68 | mInnerRect.right = current.mContentRight + (next.mContentRight - current.mContentRight) * positionOffset;
69 | mInnerRect.bottom = current.mContentBottom + (next.mContentBottom - current.mContentBottom) * positionOffset;
70 |
71 | invalidate();
72 | }
73 |
74 | @Override
75 | public void onPageSelected(int position) {
76 | }
77 |
78 | @Override
79 | public void onPageScrollStateChanged(int state) {
80 | }
81 |
82 | @Override
83 | public void onPositionDataProvide(List dataList) {
84 | mPositionDataList = dataList;
85 | }
86 |
87 | public int getOutRectColor() {
88 | return mOutRectColor;
89 | }
90 |
91 | public void setOutRectColor(int outRectColor) {
92 | mOutRectColor = outRectColor;
93 | }
94 |
95 | public int getInnerRectColor() {
96 | return mInnerRectColor;
97 | }
98 |
99 | public void setInnerRectColor(int innerRectColor) {
100 | mInnerRectColor = innerRectColor;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/TriangularPagerIndicator.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.Path;
7 | import android.view.View;
8 | import android.view.animation.Interpolator;
9 | import android.view.animation.LinearInterpolator;
10 |
11 | import net.lucode.hackware.magicindicator.FragmentContainerHelper;
12 | import net.lucode.hackware.magicindicator.buildins.UIUtil;
13 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
14 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData;
15 |
16 | import java.util.List;
17 |
18 | /**
19 | * 带有小尖角的直线指示器
20 | * 博客: http://hackware.lucode.net
21 | * Created by hackware on 2016/6/26.
22 | */
23 | public class TriangularPagerIndicator extends View implements IPagerIndicator {
24 | private List mPositionDataList;
25 | private Paint mPaint;
26 | private int mLineHeight;
27 | private int mLineColor;
28 | private int mTriangleHeight;
29 | private int mTriangleWidth;
30 | private boolean mReverse;
31 | private float mYOffset;
32 |
33 | private Path mPath = new Path();
34 | private Interpolator mStartInterpolator = new LinearInterpolator();
35 | private float mAnchorX;
36 |
37 | public TriangularPagerIndicator(Context context) {
38 | super(context);
39 | init(context);
40 | }
41 |
42 | private void init(Context context) {
43 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
44 | mPaint.setStyle(Paint.Style.FILL);
45 | mLineHeight = UIUtil.dip2px(context, 3);
46 | mTriangleWidth = UIUtil.dip2px(context, 14);
47 | mTriangleHeight = UIUtil.dip2px(context, 8);
48 | }
49 |
50 | @Override
51 | protected void onDraw(Canvas canvas) {
52 | mPaint.setColor(mLineColor);
53 | if (mReverse) {
54 | canvas.drawRect(0, getHeight() - mYOffset - mTriangleHeight, getWidth(), getHeight() - mYOffset - mTriangleHeight + mLineHeight, mPaint);
55 | } else {
56 | canvas.drawRect(0, getHeight() - mLineHeight - mYOffset, getWidth(), getHeight() - mYOffset, mPaint);
57 | }
58 | mPath.reset();
59 | if (mReverse) {
60 | mPath.moveTo(mAnchorX - mTriangleWidth / 2, getHeight() - mYOffset - mTriangleHeight);
61 | mPath.lineTo(mAnchorX, getHeight() - mYOffset);
62 | mPath.lineTo(mAnchorX + mTriangleWidth / 2, getHeight() - mYOffset - mTriangleHeight);
63 | } else {
64 | mPath.moveTo(mAnchorX - mTriangleWidth / 2, getHeight() - mYOffset);
65 | mPath.lineTo(mAnchorX, getHeight() - mTriangleHeight - mYOffset);
66 | mPath.lineTo(mAnchorX + mTriangleWidth / 2, getHeight() - mYOffset);
67 | }
68 | mPath.close();
69 | canvas.drawPath(mPath, mPaint);
70 | }
71 |
72 | @Override
73 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
74 | if (mPositionDataList == null || mPositionDataList.isEmpty()) {
75 | return;
76 | }
77 |
78 | // 计算锚点位置
79 | PositionData current = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position);
80 | PositionData next = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position + 1);
81 |
82 | float leftX = current.mLeft + (current.mRight - current.mLeft) / 2;
83 | float rightX = next.mLeft + (next.mRight - next.mLeft) / 2;
84 |
85 | mAnchorX = leftX + (rightX - leftX) * mStartInterpolator.getInterpolation(positionOffset);
86 |
87 | invalidate();
88 | }
89 |
90 | @Override
91 | public void onPageSelected(int position) {
92 | }
93 |
94 | @Override
95 | public void onPageScrollStateChanged(int state) {
96 | }
97 |
98 | @Override
99 | public void onPositionDataProvide(List dataList) {
100 | mPositionDataList = dataList;
101 | }
102 |
103 | public int getLineHeight() {
104 | return mLineHeight;
105 | }
106 |
107 | public void setLineHeight(int lineHeight) {
108 | mLineHeight = lineHeight;
109 | }
110 |
111 | public int getLineColor() {
112 | return mLineColor;
113 | }
114 |
115 | public void setLineColor(int lineColor) {
116 | mLineColor = lineColor;
117 | }
118 |
119 | public int getTriangleHeight() {
120 | return mTriangleHeight;
121 | }
122 |
123 | public void setTriangleHeight(int triangleHeight) {
124 | mTriangleHeight = triangleHeight;
125 | }
126 |
127 | public int getTriangleWidth() {
128 | return mTriangleWidth;
129 | }
130 |
131 | public void setTriangleWidth(int triangleWidth) {
132 | mTriangleWidth = triangleWidth;
133 | }
134 |
135 | public Interpolator getStartInterpolator() {
136 | return mStartInterpolator;
137 | }
138 |
139 | public void setStartInterpolator(Interpolator startInterpolator) {
140 | mStartInterpolator = startInterpolator;
141 | if (mStartInterpolator == null) {
142 | mStartInterpolator = new LinearInterpolator();
143 | }
144 | }
145 |
146 | public boolean isReverse() {
147 | return mReverse;
148 | }
149 |
150 | public void setReverse(boolean reverse) {
151 | mReverse = reverse;
152 | }
153 |
154 | public float getYOffset() {
155 | return mYOffset;
156 | }
157 |
158 | public void setYOffset(float yOffset) {
159 | mYOffset = yOffset;
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/WrapPagerIndicator.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.RectF;
7 | import android.view.View;
8 | import android.view.animation.Interpolator;
9 | import android.view.animation.LinearInterpolator;
10 |
11 | import net.lucode.hackware.magicindicator.FragmentContainerHelper;
12 | import net.lucode.hackware.magicindicator.buildins.UIUtil;
13 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
14 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData;
15 |
16 | import java.util.List;
17 |
18 |
19 | /**
20 | * 包裹住内容区域的指示器,类似天天快报的切换效果,需要和IMeasurablePagerTitleView配合使用
21 | * 博客: http://hackware.lucode.net
22 | * Created by hackware on 2016/6/26.
23 | */
24 | public class WrapPagerIndicator extends View implements IPagerIndicator {
25 | private int mVerticalPadding;
26 | private int mHorizontalPadding;
27 | private int mFillColor;
28 | private float mRoundRadius;
29 | private Interpolator mStartInterpolator = new LinearInterpolator();
30 | private Interpolator mEndInterpolator = new LinearInterpolator();
31 |
32 | private List mPositionDataList;
33 | private Paint mPaint;
34 |
35 | private RectF mRect = new RectF();
36 | private boolean mRoundRadiusSet;
37 |
38 | public WrapPagerIndicator(Context context) {
39 | super(context);
40 | init(context);
41 | }
42 |
43 | private void init(Context context) {
44 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
45 | mPaint.setStyle(Paint.Style.FILL);
46 | mVerticalPadding = UIUtil.dip2px(context, 6);
47 | mHorizontalPadding = UIUtil.dip2px(context, 10);
48 | }
49 |
50 | @Override
51 | protected void onDraw(Canvas canvas) {
52 | mPaint.setColor(mFillColor);
53 | canvas.drawRoundRect(mRect, mRoundRadius, mRoundRadius, mPaint);
54 | }
55 |
56 | @Override
57 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
58 | if (mPositionDataList == null || mPositionDataList.isEmpty()) {
59 | return;
60 | }
61 |
62 | // 计算锚点位置
63 | PositionData current = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position);
64 | PositionData next = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position + 1);
65 |
66 | mRect.left = current.mContentLeft - mHorizontalPadding + (next.mContentLeft - current.mContentLeft) * mEndInterpolator.getInterpolation(positionOffset);
67 | mRect.top = current.mContentTop - mVerticalPadding;
68 | mRect.right = current.mContentRight + mHorizontalPadding + (next.mContentRight - current.mContentRight) * mStartInterpolator.getInterpolation(positionOffset);
69 | mRect.bottom = current.mContentBottom + mVerticalPadding;
70 |
71 | if (!mRoundRadiusSet) {
72 | mRoundRadius = mRect.height() / 2;
73 | }
74 |
75 | invalidate();
76 | }
77 |
78 | @Override
79 | public void onPageSelected(int position) {
80 | }
81 |
82 | @Override
83 | public void onPageScrollStateChanged(int state) {
84 | }
85 |
86 | @Override
87 | public void onPositionDataProvide(List dataList) {
88 | mPositionDataList = dataList;
89 | }
90 |
91 | public Paint getPaint() {
92 | return mPaint;
93 | }
94 |
95 | public int getVerticalPadding() {
96 | return mVerticalPadding;
97 | }
98 |
99 | public void setVerticalPadding(int verticalPadding) {
100 | mVerticalPadding = verticalPadding;
101 | }
102 |
103 | public int getHorizontalPadding() {
104 | return mHorizontalPadding;
105 | }
106 |
107 | public void setHorizontalPadding(int horizontalPadding) {
108 | mHorizontalPadding = horizontalPadding;
109 | }
110 |
111 | public int getFillColor() {
112 | return mFillColor;
113 | }
114 |
115 | public void setFillColor(int fillColor) {
116 | mFillColor = fillColor;
117 | }
118 |
119 | public float getRoundRadius() {
120 | return mRoundRadius;
121 | }
122 |
123 | public void setRoundRadius(float roundRadius) {
124 | mRoundRadius = roundRadius;
125 | mRoundRadiusSet = true;
126 | }
127 |
128 | public Interpolator getStartInterpolator() {
129 | return mStartInterpolator;
130 | }
131 |
132 | public void setStartInterpolator(Interpolator startInterpolator) {
133 | mStartInterpolator = startInterpolator;
134 | if (mStartInterpolator == null) {
135 | mStartInterpolator = new LinearInterpolator();
136 | }
137 | }
138 |
139 | public Interpolator getEndInterpolator() {
140 | return mEndInterpolator;
141 | }
142 |
143 | public void setEndInterpolator(Interpolator endInterpolator) {
144 | mEndInterpolator = endInterpolator;
145 | if (mEndInterpolator == null) {
146 | mEndInterpolator = new LinearInterpolator();
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/model/PositionData.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.model;
2 |
3 | /**
4 | * 保存指示器标题的坐标
5 | * 博客: http://hackware.lucode.net
6 | * Created by hackware on 2016/6/26.
7 | */
8 | public class PositionData {
9 | public int mLeft;
10 | public int mTop;
11 | public int mRight;
12 | public int mBottom;
13 | public int mContentLeft;
14 | public int mContentTop;
15 | public int mContentRight;
16 | public int mContentBottom;
17 |
18 | public int width() {
19 | return mRight - mLeft;
20 | }
21 |
22 | public int height() {
23 | return mBottom - mTop;
24 | }
25 |
26 | public int contentWidth() {
27 | return mContentRight - mContentLeft;
28 | }
29 |
30 | public int contentHeight() {
31 | return mContentBottom - mContentTop;
32 | }
33 |
34 | public int horizontalCenter() {
35 | return mLeft + width() / 2;
36 | }
37 |
38 | public int verticalCenter() {
39 | return mTop + height() / 2;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/ClipPagerTitleView.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.Rect;
7 | import android.view.View;
8 |
9 | import net.lucode.hackware.magicindicator.buildins.UIUtil;
10 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IMeasurablePagerTitleView;
11 |
12 | /**
13 | * 类似今日头条切换效果的指示器标题
14 | * 博客: http://hackware.lucode.net
15 | * Created by hackware on 2016/6/26.
16 | */
17 | public class ClipPagerTitleView extends View implements IMeasurablePagerTitleView {
18 | private String mText;
19 | private int mTextColor;
20 | private int mClipColor;
21 | private boolean mLeftToRight;
22 | private float mClipPercent;
23 |
24 | private Paint mPaint;
25 | private Rect mTextBounds = new Rect();
26 |
27 | public ClipPagerTitleView(Context context) {
28 | super(context);
29 | init(context);
30 | }
31 |
32 | private void init(Context context) {
33 | int textSize = UIUtil.dip2px(context, 16);
34 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
35 | mPaint.setTextSize(textSize);
36 | int padding = UIUtil.dip2px(context, 10);
37 | setPadding(padding, 0, padding, 0);
38 | }
39 |
40 | @Override
41 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
42 | measureTextBounds();
43 | setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
44 | }
45 |
46 | private int measureWidth(int widthMeasureSpec) {
47 | int mode = MeasureSpec.getMode(widthMeasureSpec);
48 | int size = MeasureSpec.getSize(widthMeasureSpec);
49 | int result = size;
50 | switch (mode) {
51 | case MeasureSpec.AT_MOST:
52 | int width = mTextBounds.width() + getPaddingLeft() + getPaddingRight();
53 | result = Math.min(width, size);
54 | break;
55 | case MeasureSpec.UNSPECIFIED:
56 | result = mTextBounds.width() + getPaddingLeft() + getPaddingRight();
57 | break;
58 | default:
59 | break;
60 | }
61 | return result;
62 | }
63 |
64 | private int measureHeight(int heightMeasureSpec) {
65 | int mode = MeasureSpec.getMode(heightMeasureSpec);
66 | int size = MeasureSpec.getSize(heightMeasureSpec);
67 | int result = size;
68 | switch (mode) {
69 | case MeasureSpec.AT_MOST:
70 | int height = mTextBounds.height() + getPaddingTop() + getPaddingBottom();
71 | result = Math.min(height, size);
72 | break;
73 | case MeasureSpec.UNSPECIFIED:
74 | result = mTextBounds.height() + getPaddingTop() + getPaddingBottom();
75 | break;
76 | default:
77 | break;
78 | }
79 | return result;
80 | }
81 |
82 | @Override
83 | protected void onDraw(Canvas canvas) {
84 | int x = (getWidth() - mTextBounds.width()) / 2;
85 | Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
86 | int y = (int) ((getHeight() - fontMetrics.bottom - fontMetrics.top) / 2);
87 |
88 | // 画底层
89 | mPaint.setColor(mTextColor);
90 | canvas.drawText(mText, x, y, mPaint);
91 |
92 | // 画clip层
93 | canvas.save();
94 | if (mLeftToRight) {
95 | canvas.clipRect(0, 0, getWidth() * mClipPercent, getHeight());
96 | } else {
97 | canvas.clipRect(getWidth() * (1 - mClipPercent), 0, getWidth(), getHeight());
98 | }
99 | mPaint.setColor(mClipColor);
100 | canvas.drawText(mText, x, y, mPaint);
101 | canvas.restore();
102 | }
103 |
104 | @Override
105 | public void onSelected(int index, int totalCount) {
106 | }
107 |
108 | @Override
109 | public void onDeselected(int index, int totalCount) {
110 | }
111 |
112 | @Override
113 | public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
114 | mLeftToRight = !leftToRight;
115 | mClipPercent = 1.0f - leavePercent;
116 | invalidate();
117 | }
118 |
119 | @Override
120 | public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
121 | mLeftToRight = leftToRight;
122 | mClipPercent = enterPercent;
123 | invalidate();
124 | }
125 |
126 | private void measureTextBounds() {
127 | mPaint.getTextBounds(mText, 0, mText == null ? 0 : mText.length(), mTextBounds);
128 | }
129 |
130 | public String getText() {
131 | return mText;
132 | }
133 |
134 | public void setText(String text) {
135 | mText = text;
136 | requestLayout();
137 | }
138 |
139 | public float getTextSize() {
140 | return mPaint.getTextSize();
141 | }
142 |
143 | public void setTextSize(float textSize) {
144 | mPaint.setTextSize(textSize);
145 | requestLayout();
146 | }
147 |
148 | public int getTextColor() {
149 | return mTextColor;
150 | }
151 |
152 | public void setTextColor(int textColor) {
153 | mTextColor = textColor;
154 | invalidate();
155 | }
156 |
157 | public int getClipColor() {
158 | return mClipColor;
159 | }
160 |
161 | public void setClipColor(int clipColor) {
162 | mClipColor = clipColor;
163 | invalidate();
164 | }
165 |
166 | @Override
167 | public int getContentLeft() {
168 | int contentWidth = mTextBounds.width();
169 | return getLeft() + getWidth() / 2 - contentWidth / 2;
170 | }
171 |
172 | @Override
173 | public int getContentTop() {
174 | Paint.FontMetrics metrics = mPaint.getFontMetrics();
175 | float contentHeight = metrics.bottom - metrics.top;
176 | return (int) (getHeight() / 2 - contentHeight / 2);
177 | }
178 |
179 | @Override
180 | public int getContentRight() {
181 | int contentWidth = mTextBounds.width();
182 | return getLeft() + getWidth() / 2 + contentWidth / 2;
183 | }
184 |
185 | @Override
186 | public int getContentBottom() {
187 | Paint.FontMetrics metrics = mPaint.getFontMetrics();
188 | float contentHeight = metrics.bottom - metrics.top;
189 | return (int) (getHeight() / 2 + contentHeight / 2);
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/ColorTransitionPagerTitleView.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles;
2 |
3 | import android.content.Context;
4 |
5 | import net.lucode.hackware.magicindicator.buildins.ArgbEvaluatorHolder;
6 |
7 |
8 | /**
9 | * 两种颜色过渡的指示器标题
10 | * 博客: http://hackware.lucode.net
11 | * Created by hackware on 2016/6/26.
12 | */
13 | public class ColorTransitionPagerTitleView extends SimplePagerTitleView {
14 |
15 | public ColorTransitionPagerTitleView(Context context) {
16 | super(context);
17 | }
18 |
19 | @Override
20 | public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
21 | int color = ArgbEvaluatorHolder.eval(leavePercent, mSelectedColor, mNormalColor);
22 | setTextColor(color);
23 | }
24 |
25 | @Override
26 | public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
27 | int color = ArgbEvaluatorHolder.eval(enterPercent, mNormalColor, mSelectedColor);
28 | setTextColor(color);
29 | }
30 |
31 | @Override
32 | public void onSelected(int index, int totalCount) {
33 | }
34 |
35 | @Override
36 | public void onDeselected(int index, int totalCount) {
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/CommonPagerTitleView.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.FrameLayout;
8 |
9 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IMeasurablePagerTitleView;
10 |
11 | /**
12 | * 通用的指示器标题,子元素内容由外部提供,事件回传给外部
13 | * 博客: http://hackware.lucode.net
14 | * Created by hackware on 2016/7/3.
15 | */
16 | public class CommonPagerTitleView extends FrameLayout implements IMeasurablePagerTitleView {
17 | private OnPagerTitleChangeListener mOnPagerTitleChangeListener;
18 | private ContentPositionDataProvider mContentPositionDataProvider;
19 |
20 | public CommonPagerTitleView(Context context) {
21 | super(context);
22 | }
23 |
24 | @Override
25 | public void onSelected(int index, int totalCount) {
26 | if (mOnPagerTitleChangeListener != null) {
27 | mOnPagerTitleChangeListener.onSelected(index, totalCount);
28 | }
29 | }
30 |
31 | @Override
32 | public void onDeselected(int index, int totalCount) {
33 | if (mOnPagerTitleChangeListener != null) {
34 | mOnPagerTitleChangeListener.onDeselected(index, totalCount);
35 | }
36 | }
37 |
38 | @Override
39 | public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
40 | if (mOnPagerTitleChangeListener != null) {
41 | mOnPagerTitleChangeListener.onLeave(index, totalCount, leavePercent, leftToRight);
42 | }
43 | }
44 |
45 | @Override
46 | public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
47 | if (mOnPagerTitleChangeListener != null) {
48 | mOnPagerTitleChangeListener.onEnter(index, totalCount, enterPercent, leftToRight);
49 | }
50 | }
51 |
52 | @Override
53 | public int getContentLeft() {
54 | if (mContentPositionDataProvider != null) {
55 | return mContentPositionDataProvider.getContentLeft();
56 | }
57 | return getLeft();
58 | }
59 |
60 | @Override
61 | public int getContentTop() {
62 | if (mContentPositionDataProvider != null) {
63 | return mContentPositionDataProvider.getContentTop();
64 | }
65 | return getTop();
66 | }
67 |
68 | @Override
69 | public int getContentRight() {
70 | if (mContentPositionDataProvider != null) {
71 | return mContentPositionDataProvider.getContentRight();
72 | }
73 | return getRight();
74 | }
75 |
76 | @Override
77 | public int getContentBottom() {
78 | if (mContentPositionDataProvider != null) {
79 | return mContentPositionDataProvider.getContentBottom();
80 | }
81 | return getBottom();
82 | }
83 |
84 | /**
85 | * 外部直接将布局设置进来
86 | *
87 | * @param contentView
88 | */
89 | public void setContentView(View contentView) {
90 | setContentView(contentView, null);
91 | }
92 |
93 | public void setContentView(View contentView, FrameLayout.LayoutParams lp) {
94 | removeAllViews();
95 | if (contentView != null) {
96 | if (lp == null) {
97 | lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
98 | }
99 | addView(contentView, lp);
100 | }
101 | }
102 |
103 | public void setContentView(int layoutId) {
104 | View child = LayoutInflater.from(getContext()).inflate(layoutId, null);
105 | setContentView(child, null);
106 | }
107 |
108 | public OnPagerTitleChangeListener getOnPagerTitleChangeListener() {
109 | return mOnPagerTitleChangeListener;
110 | }
111 |
112 | public void setOnPagerTitleChangeListener(OnPagerTitleChangeListener onPagerTitleChangeListener) {
113 | mOnPagerTitleChangeListener = onPagerTitleChangeListener;
114 | }
115 |
116 | public ContentPositionDataProvider getContentPositionDataProvider() {
117 | return mContentPositionDataProvider;
118 | }
119 |
120 | public void setContentPositionDataProvider(ContentPositionDataProvider contentPositionDataProvider) {
121 | mContentPositionDataProvider = contentPositionDataProvider;
122 | }
123 |
124 | public interface OnPagerTitleChangeListener {
125 | void onSelected(int index, int totalCount);
126 |
127 | void onDeselected(int index, int totalCount);
128 |
129 | void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight);
130 |
131 | void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight);
132 | }
133 |
134 | public interface ContentPositionDataProvider {
135 | int getContentLeft();
136 |
137 | int getContentTop();
138 |
139 | int getContentRight();
140 |
141 | int getContentBottom();
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/DummyPagerTitleView.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 |
6 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView;
7 |
8 | /**
9 | * 空指示器标题,用于只需要指示器而不需要title的需求
10 | * 博客: http://hackware.lucode.net
11 | * Created by hackware on 2016/6/26.
12 | */
13 | public class DummyPagerTitleView extends View implements IPagerTitleView {
14 |
15 | public DummyPagerTitleView(Context context) {
16 | super(context);
17 | }
18 |
19 | @Override
20 | public void onSelected(int index, int totalCount) {
21 | }
22 |
23 | @Override
24 | public void onDeselected(int index, int totalCount) {
25 | }
26 |
27 | @Override
28 | public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
29 | }
30 |
31 | @Override
32 | public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/SimplePagerTitleView.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles;
2 |
3 | import android.content.Context;
4 | import android.graphics.Paint;
5 | import android.graphics.Rect;
6 | import android.text.TextUtils;
7 | import android.view.Gravity;
8 | import android.widget.TextView;
9 |
10 | import net.lucode.hackware.magicindicator.buildins.UIUtil;
11 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IMeasurablePagerTitleView;
12 |
13 |
14 | /**
15 | * 带文本的指示器标题
16 | * 博客: http://hackware.lucode.net
17 | * Created by hackware on 2016/6/26.
18 | */
19 | public class SimplePagerTitleView extends TextView implements IMeasurablePagerTitleView {
20 | protected int mSelectedColor;
21 | protected int mNormalColor;
22 |
23 | public SimplePagerTitleView(Context context) {
24 | super(context, null);
25 | init(context);
26 | }
27 |
28 | private void init(Context context) {
29 | setGravity(Gravity.CENTER);
30 | int padding = UIUtil.dip2px(context, 10);
31 | setPadding(padding, 0, padding, 0);
32 | setSingleLine();
33 | setEllipsize(TextUtils.TruncateAt.END);
34 | }
35 |
36 | @Override
37 | public void onSelected(int index, int totalCount) {
38 | setTextColor(mSelectedColor);
39 | }
40 |
41 | @Override
42 | public void onDeselected(int index, int totalCount) {
43 | setTextColor(mNormalColor);
44 | }
45 |
46 | @Override
47 | public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
48 | }
49 |
50 | @Override
51 | public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
52 | }
53 |
54 | @Override
55 | public int getContentLeft() {
56 | Rect bound = new Rect();
57 | String longestString = "";
58 | if (getText().toString().contains("\n")) {
59 | String[] brokenStrings = getText().toString().split("\\n");
60 | for (String each : brokenStrings) {
61 | if (each.length() > longestString.length()) longestString = each;
62 | }
63 | } else {
64 | longestString = getText().toString();
65 | }
66 | getPaint().getTextBounds(longestString, 0, longestString.length(), bound);
67 | int contentWidth = bound.width();
68 | return getLeft() + getWidth() / 2 - contentWidth / 2;
69 | }
70 |
71 | @Override
72 | public int getContentTop() {
73 | Paint.FontMetrics metrics = getPaint().getFontMetrics();
74 | float contentHeight = metrics.bottom - metrics.top;
75 | return (int) (getHeight() / 2 - contentHeight / 2);
76 | }
77 |
78 | @Override
79 | public int getContentRight() {
80 | Rect bound = new Rect();
81 | String longestString = "";
82 | if (getText().toString().contains("\n")) {
83 | String[] brokenStrings = getText().toString().split("\\n");
84 | for (String each : brokenStrings) {
85 | if (each.length() > longestString.length()) longestString = each;
86 | }
87 | } else {
88 | longestString = getText().toString();
89 | }
90 | getPaint().getTextBounds(longestString, 0, longestString.length(), bound);
91 | int contentWidth = bound.width();
92 | return getLeft() + getWidth() / 2 + contentWidth / 2;
93 | }
94 |
95 | @Override
96 | public int getContentBottom() {
97 | Paint.FontMetrics metrics = getPaint().getFontMetrics();
98 | float contentHeight = metrics.bottom - metrics.top;
99 | return (int) (getHeight() / 2 + contentHeight / 2);
100 | }
101 |
102 | public int getSelectedColor() {
103 | return mSelectedColor;
104 | }
105 |
106 | public void setSelectedColor(int selectedColor) {
107 | mSelectedColor = selectedColor;
108 | }
109 |
110 | public int getNormalColor() {
111 | return mNormalColor;
112 | }
113 |
114 | public void setNormalColor(int normalColor) {
115 | mNormalColor = normalColor;
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/badge/BadgeAnchor.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.badge;
2 |
3 | /**
4 | * 角标的锚点
5 | * Created by hackware on 2016/7/19.
6 | */
7 | public enum BadgeAnchor {
8 | LEFT,
9 | TOP,
10 | RIGHT,
11 | BOTTOM,
12 | CONTENT_LEFT,
13 | CONTENT_TOP,
14 | CONTENT_RIGHT,
15 | CONTENT_BOTTOM,
16 | CENTER_X,
17 | CENTER_Y,
18 | LEFT_EDGE_CENTER_X,
19 | TOP_EDGE_CENTER_Y,
20 | RIGHT_EDGE_CENTER_X,
21 | BOTTOM_EDGE_CENTER_Y
22 | }
23 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/badge/BadgePagerTitleView.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.badge;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 | import android.widget.FrameLayout;
6 |
7 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IMeasurablePagerTitleView;
8 | import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView;
9 |
10 | /**
11 | * 支持显示角标的title,角标布局可自定义
12 | * 博客: http://hackware.lucode.net
13 | * Created by hackware on 2016/7/18.
14 | */
15 | public class BadgePagerTitleView extends FrameLayout implements IMeasurablePagerTitleView {
16 | private IPagerTitleView mInnerPagerTitleView;
17 | private View mBadgeView;
18 | private boolean mAutoCancelBadge = true;
19 |
20 | private BadgeRule mXBadgeRule;
21 | private BadgeRule mYBadgeRule;
22 |
23 | public BadgePagerTitleView(Context context) {
24 | super(context);
25 | }
26 |
27 | @Override
28 | public void onSelected(int index, int totalCount) {
29 | if (mInnerPagerTitleView != null) {
30 | mInnerPagerTitleView.onSelected(index, totalCount);
31 | }
32 | if (mAutoCancelBadge) {
33 | setBadgeView(null);
34 | }
35 | }
36 |
37 | @Override
38 | public void onDeselected(int index, int totalCount) {
39 | if (mInnerPagerTitleView != null) {
40 | mInnerPagerTitleView.onDeselected(index, totalCount);
41 | }
42 | }
43 |
44 | @Override
45 | public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
46 | if (mInnerPagerTitleView != null) {
47 | mInnerPagerTitleView.onLeave(index, totalCount, leavePercent, leftToRight);
48 | }
49 | }
50 |
51 | @Override
52 | public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
53 | if (mInnerPagerTitleView != null) {
54 | mInnerPagerTitleView.onEnter(index, totalCount, enterPercent, leftToRight);
55 | }
56 | }
57 |
58 | public IPagerTitleView getInnerPagerTitleView() {
59 | return mInnerPagerTitleView;
60 | }
61 |
62 | public void setInnerPagerTitleView(IPagerTitleView innerPagerTitleView) {
63 | if (mInnerPagerTitleView == innerPagerTitleView) {
64 | return;
65 | }
66 | mInnerPagerTitleView = innerPagerTitleView;
67 | removeAllViews();
68 | if (mInnerPagerTitleView instanceof View) {
69 | FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
70 | addView((View) mInnerPagerTitleView, lp);
71 | }
72 | if (mBadgeView != null) {
73 | FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
74 | addView(mBadgeView, lp);
75 | }
76 | }
77 |
78 | public View getBadgeView() {
79 | return mBadgeView;
80 | }
81 |
82 | public void setBadgeView(View badgeView) {
83 | if (mBadgeView == badgeView) {
84 | return;
85 | }
86 | mBadgeView = badgeView;
87 | removeAllViews();
88 | if (mInnerPagerTitleView instanceof View) {
89 | FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
90 | addView((View) mInnerPagerTitleView, lp);
91 | }
92 | if (mBadgeView != null) {
93 | FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
94 | addView(mBadgeView, lp);
95 | }
96 | }
97 |
98 | @Override
99 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
100 | super.onLayout(changed, left, top, right, bottom);
101 | if (mInnerPagerTitleView instanceof View && mBadgeView != null) {
102 | int[] position = new int[14]; // 14种角标定位方式
103 | View v = (View) mInnerPagerTitleView;
104 | position[0] = v.getLeft();
105 | position[1] = v.getTop();
106 | position[2] = v.getRight();
107 | position[3] = v.getBottom();
108 | if (mInnerPagerTitleView instanceof IMeasurablePagerTitleView) {
109 | IMeasurablePagerTitleView view = (IMeasurablePagerTitleView) mInnerPagerTitleView;
110 | position[4] = view.getContentLeft();
111 | position[5] = view.getContentTop();
112 | position[6] = view.getContentRight();
113 | position[7] = view.getContentBottom();
114 | } else {
115 | for (int i = 4; i < 8; i++) {
116 | position[i] = position[i - 4];
117 | }
118 | }
119 | position[8] = v.getWidth() / 2;
120 | position[9] = v.getHeight() / 2;
121 | position[10] = position[4] / 2;
122 | position[11] = position[5] / 2;
123 | position[12] = position[6] + (position[2] - position[6]) / 2;
124 | position[13] = position[7] + (position[3] - position[7]) / 2;
125 |
126 | // 根据设置的BadgeRule调整角标的位置
127 | if (mXBadgeRule != null) {
128 | int x = position[mXBadgeRule.getAnchor().ordinal()];
129 | int offset = mXBadgeRule.getOffset();
130 | int newLeft = x + offset;
131 | mBadgeView.offsetLeftAndRight(newLeft - mBadgeView.getLeft());
132 | }
133 | if (mYBadgeRule != null) {
134 | int y = position[mYBadgeRule.getAnchor().ordinal()];
135 | int offset = mYBadgeRule.getOffset();
136 | int newTop = y + offset;
137 | mBadgeView.offsetTopAndBottom(newTop - mBadgeView.getTop());
138 | }
139 | }
140 | }
141 |
142 | @Override
143 | public int getContentLeft() {
144 | if (mInnerPagerTitleView instanceof IMeasurablePagerTitleView) {
145 | return getLeft() + ((IMeasurablePagerTitleView) mInnerPagerTitleView).getContentLeft();
146 | }
147 | return getLeft();
148 | }
149 |
150 | @Override
151 | public int getContentTop() {
152 | if (mInnerPagerTitleView instanceof IMeasurablePagerTitleView) {
153 | return ((IMeasurablePagerTitleView) mInnerPagerTitleView).getContentTop();
154 | }
155 | return getTop();
156 | }
157 |
158 | @Override
159 | public int getContentRight() {
160 | if (mInnerPagerTitleView instanceof IMeasurablePagerTitleView) {
161 | return getLeft() + ((IMeasurablePagerTitleView) mInnerPagerTitleView).getContentRight();
162 | }
163 | return getRight();
164 | }
165 |
166 | @Override
167 | public int getContentBottom() {
168 | if (mInnerPagerTitleView instanceof IMeasurablePagerTitleView) {
169 | return ((IMeasurablePagerTitleView) mInnerPagerTitleView).getContentBottom();
170 | }
171 | return getBottom();
172 | }
173 |
174 | public BadgeRule getXBadgeRule() {
175 | return mXBadgeRule;
176 | }
177 |
178 | public void setXBadgeRule(BadgeRule badgeRule) {
179 | if (badgeRule != null) {
180 | BadgeAnchor anchor = badgeRule.getAnchor();
181 | if (anchor != BadgeAnchor.LEFT
182 | && anchor != BadgeAnchor.RIGHT
183 | && anchor != BadgeAnchor.CONTENT_LEFT
184 | && anchor != BadgeAnchor.CONTENT_RIGHT
185 | && anchor != BadgeAnchor.CENTER_X
186 | && anchor != BadgeAnchor.LEFT_EDGE_CENTER_X
187 | && anchor != BadgeAnchor.RIGHT_EDGE_CENTER_X) {
188 | throw new IllegalArgumentException("x badge rule is wrong.");
189 | }
190 | }
191 | mXBadgeRule = badgeRule;
192 | }
193 |
194 | public BadgeRule getYBadgeRule() {
195 | return mYBadgeRule;
196 | }
197 |
198 | public void setYBadgeRule(BadgeRule badgeRule) {
199 | if (badgeRule != null) {
200 | BadgeAnchor anchor = badgeRule.getAnchor();
201 | if (anchor != BadgeAnchor.TOP
202 | && anchor != BadgeAnchor.BOTTOM
203 | && anchor != BadgeAnchor.CONTENT_TOP
204 | && anchor != BadgeAnchor.CONTENT_BOTTOM
205 | && anchor != BadgeAnchor.CENTER_Y
206 | && anchor != BadgeAnchor.TOP_EDGE_CENTER_Y
207 | && anchor != BadgeAnchor.BOTTOM_EDGE_CENTER_Y) {
208 | throw new IllegalArgumentException("y badge rule is wrong.");
209 | }
210 | }
211 | mYBadgeRule = badgeRule;
212 | }
213 |
214 | public boolean isAutoCancelBadge() {
215 | return mAutoCancelBadge;
216 | }
217 |
218 | public void setAutoCancelBadge(boolean autoCancelBadge) {
219 | mAutoCancelBadge = autoCancelBadge;
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/badge/BadgeRule.java:
--------------------------------------------------------------------------------
1 | package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.badge;
2 |
3 | /**
4 | * 角标的定位规则
5 | * Created by hackware on 2016/7/19.
6 | */
7 | public class BadgeRule {
8 | private BadgeAnchor mAnchor;
9 | private int mOffset;
10 |
11 | public BadgeRule(BadgeAnchor anchor, int offset) {
12 | mAnchor = anchor;
13 | mOffset = offset;
14 | }
15 |
16 | public BadgeAnchor getAnchor() {
17 | return mAnchor;
18 | }
19 |
20 | public void setAnchor(BadgeAnchor anchor) {
21 | mAnchor = anchor;
22 | }
23 |
24 | public int getOffset() {
25 | return mOffset;
26 | }
27 |
28 | public void setOffset(int offset) {
29 | mOffset = offset;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/magicindicator/src/main/res/layout/pager_navigator_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
18 |
19 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/magicindicator/src/main/res/layout/pager_navigator_layout_no_scroll.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/magicindicatordemo.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':magicindicator'
2 |
--------------------------------------------------------------------------------