├── .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/v/hackware1993/MagicIndicator.svg)](https://jitpack.io/#hackware1993/MagicIndicator) 10 | [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-MagicIndicator-green.svg?style=true)](https://android-arsenal.com/details/1/4252) 11 | [![Codewake](https://www.codewake.com/badges/ask_question.svg)](https://www.codewake.com/p/magicindicator) 12 | 13 | ![magicindicaotor.gif](https://github.com/hackware1993/MagicIndicator/blob/main/magicindicator.gif) 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 MagicIndicator交流群 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 | ![official_account.webp](https://github.com/hackware1993/weiV/blob/master/official_account.webp?raw=true) 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 |