Repository: ogaclejapan/SmartTabLayout
Branch: master
Commit: 712e81a92f1e
Files: 95
Total size: 171.7 KB
Directory structure:
gitextract_8nyfmi02/
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.txt
├── README.md
├── build.gradle
├── demo/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── ogaclejapan/
│ │ └── smarttablayout/
│ │ └── demo/
│ │ ├── Demo.java
│ │ ├── DemoActivity.java
│ │ ├── DemoFragment.java
│ │ ├── DemoLikeMediumActivity.java
│ │ ├── DemoRtlActivity.java
│ │ ├── DemoTabWithNotificationMarkActivity.java
│ │ ├── MainActivity.java
│ │ └── TintableImageView.java
│ └── res/
│ ├── color/
│ │ ├── custom_tab.xml
│ │ ├── custom_tab_icon.xml
│ │ └── custom_tab_like_a_medium.xml
│ ├── drawable/
│ │ ├── custom_circle.xml
│ │ ├── custom_icon.xml
│ │ ├── custom_tab.xml
│ │ └── shape_notification_mark.xml
│ ├── layout/
│ │ ├── activity_demo.xml
│ │ ├── activity_demo_tab_with_notification_mark.xml
│ │ ├── activity_like_a_medium.xml
│ │ ├── activity_main.xml
│ │ ├── activity_rtl.xml
│ │ ├── custom_tab.xml
│ │ ├── custom_tab_circle.xml
│ │ ├── custom_tab_icon1.xml
│ │ ├── custom_tab_icon2.xml
│ │ ├── custom_tab_icon_and_notification_mark.xml
│ │ ├── custom_tab_icon_and_text.xml
│ │ ├── custom_tab_like_a_medium.xml
│ │ ├── custom_tab_margin.xml
│ │ ├── demo_always_in_center.xml
│ │ ├── demo_basic.xml
│ │ ├── demo_basic_title_offset_auto_center.xml
│ │ ├── demo_custom_tab_colors.xml
│ │ ├── demo_custom_tab_icon_and_notification_mark.xml
│ │ ├── demo_custom_tab_icon_and_text.xml
│ │ ├── demo_custom_tab_icons1.xml
│ │ ├── demo_custom_tab_icons2.xml
│ │ ├── demo_custom_tab_margin.xml
│ │ ├── demo_custom_tab_text.xml
│ │ ├── demo_distribute_evenly.xml
│ │ ├── demo_indicator_trick1.xml
│ │ ├── demo_indicator_trick2.xml
│ │ ├── demo_like_a_medium_tag.xml
│ │ ├── demo_rtl.xml
│ │ ├── demo_smart_indicator.xml
│ │ └── fragment_demo.xml
│ ├── menu/
│ │ ├── menu_demo.xml
│ │ └── menu_main.xml
│ └── values/
│ ├── attrs.xml
│ ├── colors.xml
│ ├── dimens.xml
│ ├── ids.xml
│ ├── strings.xml
│ └── styles.xml
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── library/
│ ├── .gitignore
│ ├── LICENSE
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── ogaclejapan/
│ │ └── smarttablayout/
│ │ ├── SmartTabIndicationInterpolator.java
│ │ ├── SmartTabLayout.java
│ │ ├── SmartTabStrip.java
│ │ └── Utils.java
│ └── res/
│ └── values/
│ └── attrs.xml
├── publish.gradle
├── settings.gradle
└── utils-v4/
├── .gitignore
├── LICENSE
├── build.gradle
├── proguard-rules.pro
└── src/
└── main/
├── AndroidManifest.xml
└── java/
└── com/
└── ogaclejapan/
└── smarttablayout/
└── utils/
├── PagerItem.java
├── PagerItems.java
├── ViewPagerItem.java
├── ViewPagerItemAdapter.java
├── ViewPagerItems.java
└── v4/
├── Bundler.java
├── FragmentPagerItem.java
├── FragmentPagerItemAdapter.java
├── FragmentPagerItems.java
└── FragmentStatePagerItemAdapter.java
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# https://github.com/github/gitignore
# https://github.com/hsz/idea-gitignore
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Android template
# Built application files
*.apk
*.ap_
# Files for the Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
# Gradle files
.gradle/
build/
/*/build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
================================================
FILE: CHANGELOG.md
================================================
# Version 2.0.0
* Migrate to androidx 1.0.0
* Remove util-v13 library
# Version 1.7.0
* Update support library version to 28.0.0
* Change target sdk version to 28
* Change min sdk version to 14
* Deprecated util-v13 library
# Version 1.6.1
* Fixed issue with center last item in always in center tablayout #160
# Version 1.6.0
* Add `stl_indicatorWidth` to custom indicator's width #106
# Version 1.5.1
* Fixed indicator position of auto_center #100
# Version 1.5.0
* Add `stl_drawDecorationAfterTab` attribute for change the drawing order #58
* Add `stl_titleOffset` attribute for adjust the slide position #89
* Fixed a condition code of onSizeChanged for always in center
# Version 1.4.2
* Change the call order of OnTabClickListener when press the tab #74
# Version 1.4.1
* Update android support library version to 22.2.1
# Version 1.4.0
* Add TabClickListener interface #68
# Version 1.3.0
* RTL support #48
* Add `stl_clickable` attribute.
# Version 1.2.3
* Modify to ensure the first scroll #54
# Version 1.2.2
* Fix bug when indicatorAlwaysInCenter is true and tabHost has only two tabs #50
# Version 1.2.1
* Add custom ScrollListener interface #46
# Version 1.2.0
* Support the margin of each tab.
* Add `stl_indicatorWithoutPadding` and `stl_indicatorGravity` attributes.
* Add `stl_overlineColor` and `stl_overlineThickness` attributes.
# Version 1.1.3
* Allow to set the background on default tab #13
# Version 1.1.2
* Added setter for tab text colors #10
* Allow to set a String title dynamically on PagerItems. #7
# Version 1.1.1
* Enable the format of ‘reference’ for defaultTextColor to support ColorStateList #3
# Version 1.1.0
* Supported Icon Tab. #1
# Version 1.0.0
* Initial release.
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
If you would like to contribute code to SmartTabLayout you can do so through GitHub by
forking the repository and sending a pull request.
When submitting code, please make every effort to follow existing conventions
and style in order to keep the code as readable as possible.
## Coding Style
* Use the AndroidModernStyle of [Android Code Styles](https://github.com/ogaclejapan/android-code-styles) repository
================================================
FILE: LICENSE.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (C) 2015 ogaclejapan
Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# SmartTabLayout
[![Maven Central][maven_central_badge_svg]][maven_central_badge_app] [![Android Arsenal][android_arsenal_badge_svg]][android_arsenal_badge_link] [![Android Weekly][android_weekly_badge_svg]][android_weekly_badge_link]
![icon][demo_icon]
A custom ViewPager title strip which gives continuous feedback to the user when scrolling.
This library has been added some features and utilities based on [android-SlidingTabBasic][google_slidingtabbasic] project of Google Samples.
![SmartTabLayout Demo1][demo1_gif] ![SmartTabLayout Demo2][demo2_gif]
![SmartTabLayout Demo3][demo3_gif] ![SmartTabLayout Demo4][demo4_gif]
![SmartTabLayout Demo5][demo5_gif] ![SmartTabLayout Demo6][demo6_gif]
![SmartTabLayout Demo7][demo7_gif]
Try out the sample application on the Play Store.
[![Get it on Google Play][googleplay_store_badge]][demo_app]
# Usage
_(For a working implementation of this project see the demo/ folder.)_
Add the dependency to your build.gradle.
```
// For androidx (1.0.0)
dependencies {
compile 'com.ogaclejapan.smarttablayout:library:2.0.0@aar'
//Optional: see how to use the utility.
compile 'com.ogaclejapan.smarttablayout:utils-v4:2.0.0@aar'
}
// For legacy android support library (28.0.0)
dependencies {
compile 'com.ogaclejapan.smarttablayout:library:1.7.0@aar'
//Optional: see how to use the utility.
compile 'com.ogaclejapan.smarttablayout:utils-v4:1.7.0@aar'
//Deprecated since 1.7.0
compile 'com.ogaclejapan.smarttablayout:utils-v13:1.7.0@aar'
}
```
Include the SmartTabLayout widget in your layout.
This should usually be placed above the ViewPager it represents.
```xml
```
In your onCreate method (or onCreateView for a fragment), bind the widget to the ViewPager.
(If you use a utility together, you can easily add items to PagerAdapter)
e.g. ViewPager of v4.Fragment
```java
FragmentPagerItemAdapter adapter = new FragmentPagerItemAdapter(
getSupportFragmentManager(), FragmentPagerItems.with(this)
.add(R.string.titleA, PageFragment.class)
.add(R.string.titleB, PageFragment.class)
.create());
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
viewPager.setAdapter(adapter);
SmartTabLayout viewPagerTab = (SmartTabLayout) findViewById(R.id.viewpagertab);
viewPagerTab.setViewPager(viewPager);
```
(Optional) If you use an OnPageChangeListener with your view pager you should set it in the widget rather than on the pager directly.
```java
viewPagerTab.setOnPageChangeListener(mPageChangeListener);
```
(Optional) Using the FragmentPagerItemAdapter of utility, you will be able to get a position in the Fragment side.
```java
int position = FragmentPagerItem.getPosition(getArguments());
```
This position will help to implement the parallax scrolling header that contains the ViewPager :P
# Attributes
There are several attributes you can set:
| attr | description |
|:---|:---|
| stl_indicatorAlwaysInCenter | If set to true, active tab is always displayed in center (Like Newsstand google app), default false |
| stl_indicatorWithoutPadding | If set to true, draw the indicator without padding of tab, default false |
| stl_indicatorInFront | Draw the indicator in front of the underline, default false |
| stl_indicatorInterpolation | Behavior of the indicator: 'linear' or 'smart' |
| stl_indicatorGravity | Drawing position of the indicator: 'bottom' or 'top' or 'center', default 'bottom' |
| stl_indicatorColor | Color of the indicator |
| stl_indicatorColors | Multiple colors of the indicator, can set the color for each tab |
| stl_indicatorThickness | Thickness of the indicator |
| stl_indicatorWidth | Width of the indicator, default 'auto' |
| stl_indicatorCornerRadius | Radius of rounded corner the indicator |
| stl_overlineColor | Color of the top line |
| stl_overlineThickness | Thickness of the top line |
| stl_underlineColor | Color of the bottom line |
| stl_underlineThickness | Thickness of the bottom line |
| stl_dividerColor | Color of the dividers between tabs |
| stl_dividerColors | Multiple colors of the dividers between tabs, can set the color for each tab |
| stl_dividerThickness | Thickness of the divider |
| stl_defaultTabBackground | Background drawable of each tab. In general it set the StateListDrawable |
| stl_defaultTabTextAllCaps | If set to true, all tab titles will be upper case, default true |
| stl_defaultTabTextColor | Text color of the tab that was included by default |
| stl_defaultTabTextSize | Text size of the tab that was included by default |
| stl_defaultTabTextHorizontalPadding | Text layout padding of the tab that was included by default |
| stl_defaultTabTextMinWidth | Minimum width of tab |
| stl_customTabTextLayoutId | Layout ID defined custom tab. If you do not specify a layout, use the default tab |
| stl_customTabTextViewId | Text view ID in a custom tab layout. If you do not define with customTabTextLayoutId, does not work |
| stl_distributeEvenly | If set to true, each tab is given the same weight, default false |
| stl_clickable | If set to false, disable the selection of a tab click, default true |
| stl_titleOffset | If set to 'auto_center', the slide position of the tab in the middle it will keep to the center. If specify a dimension it will be offset from the left edge, default 24dp |
| stl_drawDecorationAfterTab | Draw the decoration(indicator and lines) after drawing of tab, default false |
*__Notes:__ Both 'stl_indicatorAlwaysInCenter' and 'stl_distributeEvenly' if it is set to true, it will throw UnsupportedOperationException.*
# How to customize the tab
The customization of tab There are three ways.
* Customize the attribute
* SmartTabLayout#setCustomTabView(int layoutResId, int textViewId)
* SmartTabLayout#setCustomTabView(TabProvider provider)
If set the TabProvider, can build a view for each tab.
```java
public class SmartTabLayout extends HorizontalScrollView {
//...
/**
* Create the custom tabs in the tab layout. Set with
* {@link #setCustomTabView(com.ogaclejapan.smarttablayout.SmartTabLayout.TabProvider)}
*/
public interface TabProvider {
/**
* @return Return the View of {@code position} for the Tabs
*/
View createTabView(ViewGroup container, int position, PagerAdapter adapter);
}
//...
}
```
# How to use the utility
Utility has two types available to suit the Android support library.
* utils-v4 library contains the PagerAdapter implementation class for _androidx.fragment.app.Fragment_
* (Deprecated) utils-v13 library contains the PagerAdapter implementation class for _android.app.Fragment_
The two libraries have different Android support libraries that depend,
but implemented functionality is the same.
## PagerAdapter for View-based Page
```java
ViewPagerItemAdapter adapter = new ViewPagerItemAdapter(ViewPagerItems.with(this)
.add(R.string.title, R.layout.page)
.add("title", R.layout.page)
.create());
viewPager.setAdapter(adapter);
//...
public void onPageSelected(int position) {
//.instantiateItem() from until .destroyItem() is called it will be able to get the View of page.
View page = adapter.getPage(position);
}
```
## PagerAdapter for Fragment-based Page
Fragment-based PagerAdapter There are two implementations.
Please differences refer to the library documentation for Android.
* FragmentPagerItemAdapter extends FragmentPagerAdapter
* FragmentStatePagerItemAdapter extends FragmentStatePagerAdapter
```java
FragmentPagerItemAdapter adapter = new FragmentPagerItemAdapter(
getSupportFragmentManager(), FragmentPagerItems.with(this)
.add(R.string.title, PageFragment.class),
.add(R.string.title, WithArgumentsPageFragment.class, new Bundler().putString("key", "value").get()),
.add("title", PageFragment.class)
.create());
viewPager.setAdapter(adapter);
//...
public void onPageSelected(int position) {
//.instantiateItem() from until .destoryItem() is called it will be able to get the Fragment of page.
Fragment page = adapter.getPage(position);
}
```
*__Notes:__ If using fragment inside a ViewPager, Must be use [Fragment#getChildFragmentManager()](https://developer.android.com/reference/androidx/fragment/app/Fragment.html#getChildFragmentManager).*
# Looking for iOS ?
Check [WormTabStrip](https://github.com/EzimetYusup/WormTabStrip) out.
# LICENSE
```
Copyright (C) 2015 ogaclejapan
Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
[demo1_gif]: https://raw.githubusercontent.com/ogaclejapan/SmartTabLayout/master/art/demo1.gif
[demo2_gif]: https://raw.githubusercontent.com/ogaclejapan/SmartTabLayout/master/art/demo2.gif
[demo3_gif]: https://raw.githubusercontent.com/ogaclejapan/SmartTabLayout/master/art/demo3.gif
[demo4_gif]: https://raw.githubusercontent.com/ogaclejapan/SmartTabLayout/master/art/demo4.gif
[demo5_gif]: https://raw.githubusercontent.com/ogaclejapan/SmartTabLayout/master/art/demo5.gif
[demo6_gif]: https://raw.githubusercontent.com/ogaclejapan/SmartTabLayout/master/art/demo6.gif
[demo7_gif]: https://raw.githubusercontent.com/ogaclejapan/SmartTabLayout/master/art/demo7.gif
[demo_app]: https://play.google.com/store/apps/details?id=com.ogaclejapan.smarttablayout.demo&referrer=utm_source%3Dgithub
[demo_icon]: https://raw.githubusercontent.com/ogaclejapan/SmartTabLayout/master/art/icon.png
[googleplay_store_badge]: http://www.android.com/images/brand/get_it_on_play_logo_large.png
[maven_central_badge_svg]: https://maven-badges.herokuapp.com/maven-central/com.ogaclejapan.smarttablayout/library/badge.svg?style=flat
[maven_central_badge_app]: https://maven-badges.herokuapp.com/maven-central/com.ogaclejapan.smarttablayout/library
[android_arsenal_badge_svg]: https://img.shields.io/badge/Android%20Arsenal-SmartTabLayout-brightgreen.svg?style=flat
[android_arsenal_badge_link]: http://android-arsenal.com/details/1/1683
[android_weekly_badge_svg]: https://img.shields.io/badge/AndroidWeekly-%23148-blue.svg
[android_weekly_badge_link]: http://androidweekly.net/issues/issue-148
[qiitanium]: https://github.com/ogaclejapan/Qiitanium
[google_slidingtabbasic]: https://github.com/googlesamples/android-SlidingTabsBasic
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
ext {
// Package information for bintray
pkginfo = [name : ARTIFACT_NAME,
description: ARTIFACT_DESCRIPTION,
site : SITE_URL,
issue : ISSUE_URL,
vcs : SCM_URL]
}
buildscript {
repositories {
google()
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.1'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0'
classpath 'gradle.plugin.com.hierynomus.gradle.plugins:license-gradle-plugin:0.15.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
version = VERSION_NAME
group = GROUP
repositories {
google()
mavenCentral()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: demo/.gitignore
================================================
/build
================================================
FILE: demo/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion COMPILE_SDK_VERSION as int
defaultConfig {
minSdkVersion 14
targetSdkVersion COMPILE_SDK_VERSION as int
versionCode VERSION_CODE as int
versionName VERSION_NAME
}
def secretFile = file("${rootDir}/secret.gradle")
if (secretFile.exists()) {
apply from: secretFile.absolutePath
signingConfigs {
release {
storeFile project.ext.storeFile
storePassword project.ext.storePassword
keyAlias project.ext.keyAlias
keyPassword project.ext.keyPassword
}
}
}
buildTypes {
release {
if (secretFile.exists()) {
signingConfig signingConfigs.release
}
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation project(':library')
implementation project(':utils-v4')
implementation "androidx.appcompat:appcompat:${ANDROIDX_APPCOMPAT_VERSION}"
}
================================================
FILE: demo/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/msk/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: demo/src/main/AndroidManifest.xml
================================================
================================================
FILE: demo/src/main/java/com/ogaclejapan/smarttablayout/demo/Demo.java
================================================
package com.ogaclejapan.smarttablayout.demo;
import android.content.Context;
import android.content.res.Resources;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.ogaclejapan.smarttablayout.SmartTabLayout;
import androidx.viewpager.widget.PagerAdapter;
public enum Demo {
BASIC(R.string.demo_title_basic, R.layout.demo_basic),
BASIC2(R.string.demo_title_basic2, R.layout.demo_basic_title_offset_auto_center),
SMART_INDICATOR(R.string.demo_title_smart_indicator, R.layout.demo_smart_indicator),
DISTRIBUTE_EVENLY(R.string.demo_title_distribute_evenly, R.layout.demo_distribute_evenly) {
@Override
public int[] tabs() {
return tab3();
}
},
ALWAYS_IN_CENTER(R.string.demo_title_always_in_center, R.layout.demo_always_in_center),
CUSTOM_TAB(R.string.demo_title_custom_tab_text, R.layout.demo_custom_tab_text),
CUSTOM_TAB_COLORS(R.string.demo_title_custom_tab_colors, R.layout.demo_custom_tab_colors),
CUSTOM_TAB_ICONS1(R.string.demo_title_custom_tab_icons1, R.layout.demo_custom_tab_icons1) {
@Override
public int[] tabs() {
return new int[] {
R.string.demo_tab_no_title,
R.string.demo_tab_no_title,
R.string.demo_tab_no_title,
R.string.demo_tab_no_title
};
}
@Override
public void setup(SmartTabLayout layout) {
super.setup(layout);
final LayoutInflater inflater = LayoutInflater.from(layout.getContext());
final Resources res = layout.getContext().getResources();
layout.setCustomTabView(new SmartTabLayout.TabProvider() {
@Override
public View createTabView(ViewGroup container, int position, PagerAdapter adapter) {
ImageView icon = (ImageView) inflater.inflate(R.layout.custom_tab_icon1, container,
false);
switch (position) {
case 0:
icon.setImageDrawable(res.getDrawable(R.drawable.ic_home_white_24dp));
break;
case 1:
icon.setImageDrawable(res.getDrawable(R.drawable.ic_search_white_24dp));
break;
case 2:
icon.setImageDrawable(res.getDrawable(R.drawable.ic_person_white_24dp));
break;
case 3:
icon.setImageDrawable(res.getDrawable(R.drawable.ic_flash_on_white_24dp));
break;
default:
throw new IllegalStateException("Invalid position: " + position);
}
return icon;
}
});
}
},
CUSTOM_TAB_ICONS2(R.string.demo_title_custom_tab_icons2, R.layout.demo_custom_tab_icons2) {
@Override
public int[] tabs() {
return new int[] {
R.string.demo_tab_no_title,
R.string.demo_tab_no_title,
R.string.demo_tab_no_title,
R.string.demo_tab_no_title
};
}
@Override
public void setup(SmartTabLayout layout) {
super.setup(layout);
final LayoutInflater inflater = LayoutInflater.from(layout.getContext());
final Resources res = layout.getContext().getResources();
layout.setCustomTabView(new SmartTabLayout.TabProvider() {
@Override
public View createTabView(ViewGroup container, int position, PagerAdapter adapter) {
ImageView icon = (ImageView) inflater.inflate(R.layout.custom_tab_icon2, container,
false);
switch (position) {
case 0:
icon.setImageDrawable(res.getDrawable(R.drawable.ic_home_white_24dp));
break;
case 1:
icon.setImageDrawable(res.getDrawable(R.drawable.ic_search_white_24dp));
break;
case 2:
icon.setImageDrawable(res.getDrawable(R.drawable.ic_person_white_24dp));
break;
case 3:
icon.setImageDrawable(res.getDrawable(R.drawable.ic_flash_on_white_24dp));
break;
default:
throw new IllegalStateException("Invalid position: " + position);
}
return icon;
}
});
}
},
CUSTOM_TAB_ICON_AND_TEXT(R.string.demo_title_custom_tab_icon_and_text,
R.layout.demo_custom_tab_icon_and_text) {
@Override
public int[] tabs() {
return tab3();
}
},
CUSTOM_TAB_ICON_AND_NOTIFICATION_MARK(R.string.demo_title_custom_tab_icon_and_notification_mark,
R.layout.demo_custom_tab_icon_and_notification_mark) {
@Override
public int[] tabs() {
return tab3();
}
@Override
public void startActivity(Context context) {
DemoTabWithNotificationMarkActivity.startActivity(context, this);
}
},
CUSTOM_TAB_MARGIN(R.string.demo_title_custom_tab_margin, R.layout.demo_custom_tab_margin),
INDICATOR_TRICK1(R.string.demo_title_indicator_trick1, R.layout.demo_indicator_trick1),
INDICATOR_TRICK2(R.string.demo_title_indicator_trick2, R.layout.demo_indicator_trick2),
RIGHT_TO_LEFT(R.string.demo_title_right_to_left, R.layout.demo_rtl) {
@Override
public void startActivity(Context context) {
DemoRtlActivity.startActivity(context, this);
}
},
LIKE_MEDIUM_TAG(R.string.demo_title_advanced_medium, R.layout.demo_like_a_medium_tag) {
@Override
public int[] tabs() {
return new int[] {
R.string.demo_tab_like_a_medium_top,
R.string.demo_tab_like_a_medium_latest
};
}
@Override
public void startActivity(Context context) {
DemoLikeMediumActivity.startActivity(context, this);
}
};
public final int titleResId;
public final int layoutResId;
Demo(int titleResId, int layoutResId) {
this.titleResId = titleResId;
this.layoutResId = layoutResId;
}
public static int[] tab10() {
return new int[] {
R.string.demo_tab_1,
R.string.demo_tab_2,
R.string.demo_tab_3,
R.string.demo_tab_4,
R.string.demo_tab_5,
R.string.demo_tab_6,
R.string.demo_tab_7,
R.string.demo_tab_8,
R.string.demo_tab_9,
R.string.demo_tab_10
};
}
public static int[] tab3() {
return new int[] {
R.string.demo_tab_8,
R.string.demo_tab_9,
R.string.demo_tab_10
};
}
public void startActivity(Context context) {
DemoActivity.startActivity(context, this);
}
public void setup(final SmartTabLayout layout) {
//Do nothing.
}
public int[] tabs() {
return tab10();
}
}
================================================
FILE: demo/src/main/java/com/ogaclejapan/smarttablayout/demo/DemoActivity.java
================================================
package com.ogaclejapan.smarttablayout.demo;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import com.ogaclejapan.smarttablayout.SmartTabLayout;
import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItem;
import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItemAdapter;
import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItems;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.viewpager.widget.ViewPager;
public class DemoActivity extends AppCompatActivity {
private static final String KEY_DEMO = "demo";
public static void startActivity(Context context, Demo demo) {
Intent intent = new Intent(context, DemoActivity.class);
intent.putExtra(KEY_DEMO, demo.name());
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
Demo demo = getDemo();
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(demo.titleResId);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
ViewGroup tab = (ViewGroup) findViewById(R.id.tab);
tab.addView(LayoutInflater.from(this).inflate(demo.layoutResId, tab, false));
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
SmartTabLayout viewPagerTab = (SmartTabLayout) findViewById(R.id.viewpagertab);
demo.setup(viewPagerTab);
FragmentPagerItems pages = new FragmentPagerItems(this);
for (int titleResId : demo.tabs()) {
pages.add(FragmentPagerItem.of(getString(titleResId), DemoFragment.class));
}
FragmentPagerItemAdapter adapter = new FragmentPagerItemAdapter(
getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);
viewPagerTab.setViewPager(viewPager);
}
private Demo getDemo() {
return Demo.valueOf(getIntent().getStringExtra(KEY_DEMO));
}
}
================================================
FILE: demo/src/main/java/com/ogaclejapan/smarttablayout/demo/DemoFragment.java
================================================
package com.ogaclejapan.smarttablayout.demo;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItem;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
public class DemoFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_demo, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
int position = FragmentPagerItem.getPosition(getArguments());
TextView title = (TextView) view.findViewById(R.id.item_title);
title.setText(String.valueOf(position));
}
}
================================================
FILE: demo/src/main/java/com/ogaclejapan/smarttablayout/demo/DemoLikeMediumActivity.java
================================================
package com.ogaclejapan.smarttablayout.demo;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import com.ogaclejapan.smarttablayout.SmartTabLayout;
import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItem;
import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItemAdapter;
import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItems;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.viewpager.widget.ViewPager;
public class DemoLikeMediumActivity extends AppCompatActivity {
private static final String KEY_DEMO = "demo";
public static void startActivity(Context context, Demo demo) {
Intent intent = new Intent(context, DemoLikeMediumActivity.class);
intent.putExtra(KEY_DEMO, demo.name());
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_like_a_medium);
Demo demo = getDemo();
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(demo.titleResId);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
ViewGroup tab = (ViewGroup) findViewById(R.id.tab);
tab.addView(LayoutInflater.from(this).inflate(demo.layoutResId, tab, false));
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
SmartTabLayout viewPagerTab = (SmartTabLayout) findViewById(R.id.viewpagertab);
demo.setup(viewPagerTab);
FragmentPagerItems pages = new FragmentPagerItems(this);
for (int titleResId : demo.tabs()) {
pages.add(FragmentPagerItem.of(getString(titleResId), DemoFragment.class));
}
FragmentPagerItemAdapter adapter = new FragmentPagerItemAdapter(
getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);
viewPagerTab.setViewPager(viewPager);
}
private Demo getDemo() {
return Demo.valueOf(getIntent().getStringExtra(KEY_DEMO));
}
}
================================================
FILE: demo/src/main/java/com/ogaclejapan/smarttablayout/demo/DemoRtlActivity.java
================================================
package com.ogaclejapan.smarttablayout.demo;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import com.ogaclejapan.smarttablayout.SmartTabLayout;
import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItem;
import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItemAdapter;
import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItems;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.viewpager.widget.ViewPager;
public class DemoRtlActivity extends AppCompatActivity {
private static final String KEY_DEMO = "demo";
public static void startActivity(Context context, Demo demo) {
Intent intent = new Intent(context, DemoRtlActivity.class);
intent.putExtra(KEY_DEMO, demo.name());
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rtl);
Demo demo = getDemo();
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(demo.titleResId);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
ViewGroup tab = (ViewGroup) findViewById(R.id.tab);
tab.addView(LayoutInflater.from(this).inflate(demo.layoutResId, tab, false));
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
final SmartTabLayout viewPagerTab = (SmartTabLayout) findViewById(R.id.viewpagertab);
demo.setup(viewPagerTab);
FragmentPagerItems pages = new FragmentPagerItems(this);
for (int titleResId : demo.tabs()) {
pages.add(FragmentPagerItem.of(getString(titleResId), DemoFragment.class));
}
FragmentPagerItemAdapter adapter = new FragmentPagerItemAdapter(
getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);
viewPagerTab.setViewPager(viewPager);
}
private Demo getDemo() {
return Demo.valueOf(getIntent().getStringExtra(KEY_DEMO));
}
}
================================================
FILE: demo/src/main/java/com/ogaclejapan/smarttablayout/demo/DemoTabWithNotificationMarkActivity.java
================================================
package com.ogaclejapan.smarttablayout.demo;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.ogaclejapan.smarttablayout.SmartTabLayout;
import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItem;
import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItemAdapter;
import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItems;
import java.util.Random;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
public class DemoTabWithNotificationMarkActivity extends AppCompatActivity implements
SmartTabLayout.TabProvider {
private static final String KEY_DEMO = "demo";
public static void startActivity(Context context, Demo demo) {
Intent intent = new Intent(context, DemoTabWithNotificationMarkActivity.class);
intent.putExtra(KEY_DEMO, demo.name());
context.startActivity(intent);
}
private Random random = new Random(System.currentTimeMillis());
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo_tab_with_notification_mark);
final Demo demo = getDemo();
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(demo.titleResId);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
ViewGroup tab = (ViewGroup) findViewById(R.id.tab);
tab.addView(LayoutInflater.from(this).inflate(demo.layoutResId, tab, false));
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
final SmartTabLayout viewPagerTab = (SmartTabLayout) findViewById(R.id.viewpagertab);
viewPagerTab.setCustomTabView(this);
FragmentPagerItems pages = new FragmentPagerItems(this);
for (int titleResId : demo.tabs()) {
pages.add(FragmentPagerItem.of(getString(titleResId), DemoFragment.class));
}
FragmentPagerItemAdapter adapter = new FragmentPagerItemAdapter(
getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);
viewPagerTab.setViewPager(viewPager);
viewPagerTab.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
View tab = viewPagerTab.getTabAt(position);
View mark = tab.findViewById(R.id.custom_tab_notification_mark);
mark.setVisibility(View.GONE);
}
});
findViewById(R.id.test).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = Math.abs(random.nextInt()) % demo.tabs().length;
View tab = viewPagerTab.getTabAt(position);
View mark = tab.findViewById(R.id.custom_tab_notification_mark);
mark.setVisibility(View.VISIBLE);
}
});
}
@Override
public View createTabView(ViewGroup container, int position, PagerAdapter adapter) {
LayoutInflater inflater = LayoutInflater.from(container.getContext());
Resources res = container.getContext().getResources();
View tab = inflater.inflate(R.layout.custom_tab_icon_and_notification_mark, container, false);
View mark = tab.findViewById(R.id.custom_tab_notification_mark);
mark.setVisibility(View.GONE);
ImageView icon = (ImageView) tab.findViewById(R.id.custom_tab_icon);
switch (position) {
case 0:
icon.setImageDrawable(res.getDrawable(R.drawable.ic_home_white_24dp));
break;
case 1:
icon.setImageDrawable(res.getDrawable(R.drawable.ic_search_white_24dp));
break;
case 2:
icon.setImageDrawable(res.getDrawable(R.drawable.ic_person_white_24dp));
break;
default:
throw new IllegalStateException("Invalid position: " + position);
}
return tab;
}
private Demo getDemo() {
return Demo.valueOf(getIntent().getStringExtra(KEY_DEMO));
}
}
================================================
FILE: demo/src/main/java/com/ogaclejapan/smarttablayout/demo/MainActivity.java
================================================
package com.ogaclejapan.smarttablayout.demo;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity implements AbsListView.OnItemClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(R.id.list);
listView.setOnItemClickListener(this);
ArrayAdapter demoAdapter = new ArrayAdapter(this,
android.R.layout.simple_list_item_1);
for (Demo demo : Demo.values()) {
demoAdapter.add(getString(demo.titleResId));
}
listView.setAdapter(demoAdapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_github:
openGitHub();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
Demo demo = Demo.values()[position];
demo.startActivity(this);
}
private void openGitHub() {
Uri uri = Uri.parse(getString(R.string.app_github_url));
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
}
================================================
FILE: demo/src/main/java/com/ogaclejapan/smarttablayout/demo/TintableImageView.java
================================================
package com.ogaclejapan.smarttablayout.demo;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import androidx.appcompat.widget.AppCompatImageView;
/**
* https://gist.github.com/tylerchesley/5d15d859be4f3ce31213
*/
public class TintableImageView extends AppCompatImageView {
private ColorStateList tint;
public TintableImageView(Context context) {
super(context);
}
public TintableImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public TintableImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.TintableImageView, defStyle, 0);
tint = a.getColorStateList(
R.styleable.TintableImageView_tint);
a.recycle();
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
if (tint != null && tint.isStateful()) {
updateTintColor();
}
}
public void setColorFilter(ColorStateList tint) {
this.tint = tint;
super.setColorFilter(tint.getColorForState(getDrawableState(), 0));
}
private void updateTintColor() {
int color = tint.getColorForState(getDrawableState(), 0);
setColorFilter(color);
}
}
================================================
FILE: demo/src/main/res/color/custom_tab.xml
================================================
================================================
FILE: demo/src/main/res/color/custom_tab_icon.xml
================================================
================================================
FILE: demo/src/main/res/color/custom_tab_like_a_medium.xml
================================================
================================================
FILE: demo/src/main/res/drawable/custom_circle.xml
================================================
================================================
FILE: demo/src/main/res/drawable/custom_icon.xml
================================================
================================================
FILE: demo/src/main/res/drawable/custom_tab.xml
================================================
================================================
FILE: demo/src/main/res/drawable/shape_notification_mark.xml
================================================
================================================
FILE: demo/src/main/res/layout/activity_demo.xml
================================================
================================================
FILE: demo/src/main/res/layout/activity_demo_tab_with_notification_mark.xml
================================================
================================================
FILE: demo/src/main/res/layout/activity_like_a_medium.xml
================================================
================================================
FILE: demo/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: demo/src/main/res/layout/activity_rtl.xml
================================================
================================================
FILE: demo/src/main/res/layout/custom_tab.xml
================================================
================================================
FILE: demo/src/main/res/layout/custom_tab_circle.xml
================================================
================================================
FILE: demo/src/main/res/layout/custom_tab_icon1.xml
================================================
================================================
FILE: demo/src/main/res/layout/custom_tab_icon2.xml
================================================
================================================
FILE: demo/src/main/res/layout/custom_tab_icon_and_notification_mark.xml
================================================
================================================
FILE: demo/src/main/res/layout/custom_tab_icon_and_text.xml
================================================
================================================
FILE: demo/src/main/res/layout/custom_tab_like_a_medium.xml
================================================
================================================
FILE: demo/src/main/res/layout/custom_tab_margin.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_always_in_center.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_basic.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_basic_title_offset_auto_center.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_custom_tab_colors.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_custom_tab_icon_and_notification_mark.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_custom_tab_icon_and_text.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_custom_tab_icons1.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_custom_tab_icons2.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_custom_tab_margin.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_custom_tab_text.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_distribute_evenly.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_indicator_trick1.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_indicator_trick2.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_like_a_medium_tag.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_rtl.xml
================================================
================================================
FILE: demo/src/main/res/layout/demo_smart_indicator.xml
================================================
================================================
FILE: demo/src/main/res/layout/fragment_demo.xml
================================================
================================================
FILE: demo/src/main/res/menu/menu_demo.xml
================================================
================================================
FILE: demo/src/main/res/menu/menu_main.xml
================================================
================================================
FILE: demo/src/main/res/values/attrs.xml
================================================
================================================
FILE: demo/src/main/res/values/colors.xml
================================================
#3C515C#142E3C#40C4FF#FFFFFFFF#FF000000#33000000#33FFFFFF#00000000#FFFFFFFF#4DFFFFFF#FFFFFFFF#FF63727B#FF000000#4D000000#03A9F4#00BCD4#009688#4CAF50#8BC34A@color/light_blue_500@color/cyan_500@color/teal_500@color/green_500@color/light_green_500
================================================
FILE: demo/src/main/res/values/dimens.xml
================================================
48dp72dp
================================================
FILE: demo/src/main/res/values/ids.xml
================================================
================================================
FILE: demo/src/main/res/values/strings.xml
================================================
SmartTabLayoutGitHubSettingsBasicBasic(Title Offset Auto Center)Smart IndicatorDistribute EvenlyAlways In CenterCustom Tab TextCustom Tab Margin (since 1.2~)Custom Tab ColorsCustom Tab Icons 1Custom Tab Icons 2 (since 1.2~)Custom Tab Icon and TextCustom Tab Icon and Notification MarkIndicator Thickness Trick 1Indicator Thickness Trick 2RTL supportAdvanced: Like a Medium Tag (since 1.2~)CupcakeDonutEclairFroyoGingerbreadHoneycombIce Cream SandwichJelly BeanKitKatLollipopNo TitleTopLatest
================================================
FILE: demo/src/main/res/values/styles.xml
================================================
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Sun Nov 27 17:23:04 JST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
================================================
FILE: gradle.properties
================================================
android.useAndroidX=true
VERSION_NAME=2.0.0
VERSION_CODE=18
COMPILE_SDK_VERSION=28
ANDROIDX_APPCOMPAT_VERSION=1.0.2
ANDROIDX_BASE_VERSION=1.0.0
GROUP=com.ogaclejapan.smarttablayout
ARTIFACT_NAME=SmartTabLayout
ARTIFACT_DESCRIPTION=A custom ViewPager title strip which gives continuous feedback to the user when scrolling
SITE_URL=https://github.com/ogaclejapan/SmartTabLayout
ISSUE_SYSTEM=github
ISSUE_URL=https://github.com/ogaclejapan/SmartTabLayout/issues
SCM_URL=https://github.com/ogaclejapan/SmartTabLayout
SCM_CONNECTION=scm:git@github.com:ogaclejapan/SmartTabLayout.git
SCM_DEV_CONNECTION=scm:git@github.com:ogaclejapan/SmartTabLayout.git
LICENCE_NAME=The Apache Software License, Version 2.0
LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
LICENCE_DIST=repo
DEVELOPER_ID=ogaclejapan
DEVELOPER_NAME=Masaki Ogata
DEVELOPER_EMAIL=ogaclejapan@gmail.com
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: library/.gitignore
================================================
/build
================================================
FILE: library/LICENSE
================================================
Copyright (C) 2015 ogaclejapan
Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: library/build.gradle
================================================
apply plugin: 'com.android.library'
apply plugin: 'com.github.ben-manes.versions'
apply plugin: 'com.github.hierynomus.license'
android {
compileSdkVersion COMPILE_SDK_VERSION as int
resourcePrefix 'stl_'
defaultConfig {
minSdkVersion 14
targetSdkVersion COMPILE_SDK_VERSION as int
versionCode VERSION_CODE as int
versionName VERSION_NAME
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation "androidx.viewpager:viewpager:${ANDROIDX_BASE_VERSION}"
implementation "androidx.fragment:fragment:${ANDROIDX_BASE_VERSION}"
}
license {
sourceSets {
main.java.srcDirs = android.sourceSets.main.java.srcDirs
main.resources.srcDirs = android.sourceSets.main.resources.srcDirs
}
excludes(["**/*.xml"])
}
pkginfo.name = ARTIFACT_NAME
apply from: "${project.rootDir}/publish.gradle"
================================================
FILE: library/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/msk/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: library/src/main/AndroidManifest.xml
================================================
================================================
FILE: library/src/main/java/com/ogaclejapan/smarttablayout/SmartTabIndicationInterpolator.java
================================================
/**
* Copyright (C) 2015 ogaclejapan
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ogaclejapan.smarttablayout;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
public abstract class SmartTabIndicationInterpolator {
public static final SmartTabIndicationInterpolator SMART = new SmartIndicationInterpolator();
public static final SmartTabIndicationInterpolator LINEAR = new LinearIndicationInterpolator();
static final int ID_SMART = 0;
static final int ID_LINEAR = 1;
public static SmartTabIndicationInterpolator of(int id) {
switch (id) {
case ID_SMART:
return SMART;
case ID_LINEAR:
return LINEAR;
default:
throw new IllegalArgumentException("Unknown id: " + id);
}
}
public abstract float getLeftEdge(float offset);
public abstract float getRightEdge(float offset);
public float getThickness(float offset) {
return 1f; //Always the same thickness by default
}
public static class SmartIndicationInterpolator extends SmartTabIndicationInterpolator {
private static final float DEFAULT_INDICATOR_INTERPOLATION_FACTOR = 3.0f;
private final Interpolator leftEdgeInterpolator;
private final Interpolator rightEdgeInterpolator;
public SmartIndicationInterpolator() {
this(DEFAULT_INDICATOR_INTERPOLATION_FACTOR);
}
public SmartIndicationInterpolator(float factor) {
leftEdgeInterpolator = new AccelerateInterpolator(factor);
rightEdgeInterpolator = new DecelerateInterpolator(factor);
}
@Override
public float getLeftEdge(float offset) {
return leftEdgeInterpolator.getInterpolation(offset);
}
@Override
public float getRightEdge(float offset) {
return rightEdgeInterpolator.getInterpolation(offset);
}
@Override
public float getThickness(float offset) {
return 1f / (1.0f - getLeftEdge(offset) + getRightEdge(offset));
}
}
public static class LinearIndicationInterpolator extends SmartTabIndicationInterpolator {
@Override
public float getLeftEdge(float offset) {
return offset;
}
@Override
public float getRightEdge(float offset) {
return offset;
}
}
}
================================================
FILE: library/src/main/java/com/ogaclejapan/smarttablayout/SmartTabLayout.java
================================================
/**
* Copyright (C) 2015 ogaclejapan
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ogaclejapan.smarttablayout;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.os.Build;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.view.ViewCompat;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
/**
* To be used with ViewPager to provide a tab indicator component which give constant feedback as
* to
* the user's scroll progress.
*
* To use the component, simply add it to your view hierarchy. Then in your
* {@link android.app.Activity} or {@link androidx.fragment.app.Fragment} call
* {@link #setViewPager(ViewPager)} providing it the ViewPager this
* layout
* is being used for.
*
* The colors can be customized in two ways. The first and simplest is to provide an array of
* colors
* via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
* alternative is via the {@link TabColorizer} interface which provides you complete control over
* which color is used for any individual position.
*
* The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
* providing the layout ID of your custom layout.
*
* Forked from Google Samples > SlidingTabsBasic >
* SlidingTabLayout
*/
public class SmartTabLayout extends HorizontalScrollView {
private static final boolean DEFAULT_DISTRIBUTE_EVENLY = false;
private static final int TITLE_OFFSET_DIPS = 24;
private static final int TITLE_OFFSET_AUTO_CENTER = -1;
private static final int TAB_VIEW_PADDING_DIPS = 16;
private static final boolean TAB_VIEW_TEXT_ALL_CAPS = true;
private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
private static final int TAB_VIEW_TEXT_COLOR = 0xFC000000;
private static final int TAB_VIEW_TEXT_MIN_WIDTH = 0;
private static final boolean TAB_CLICKABLE = true;
protected final SmartTabStrip tabStrip;
private int titleOffset;
private int tabViewBackgroundResId;
private boolean tabViewTextAllCaps;
private ColorStateList tabViewTextColors;
private float tabViewTextSize;
private int tabViewTextHorizontalPadding;
private int tabViewTextMinWidth;
private ViewPager viewPager;
private ViewPager.OnPageChangeListener viewPagerPageChangeListener;
private OnScrollChangeListener onScrollChangeListener;
private TabProvider tabProvider;
private InternalTabClickListener internalTabClickListener;
private OnTabClickListener onTabClickListener;
private boolean distributeEvenly;
public SmartTabLayout(Context context) {
this(context, null);
}
public SmartTabLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SmartTabLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Disable the Scroll Bar
setHorizontalScrollBarEnabled(false);
final DisplayMetrics dm = getResources().getDisplayMetrics();
final float density = dm.density;
int tabBackgroundResId = NO_ID;
boolean textAllCaps = TAB_VIEW_TEXT_ALL_CAPS;
ColorStateList textColors;
float textSize = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP, dm);
int textHorizontalPadding = (int) (TAB_VIEW_PADDING_DIPS * density);
int textMinWidth = (int) (TAB_VIEW_TEXT_MIN_WIDTH * density);
boolean distributeEvenly = DEFAULT_DISTRIBUTE_EVENLY;
int customTabLayoutId = NO_ID;
int customTabTextViewId = NO_ID;
boolean clickable = TAB_CLICKABLE;
int titleOffset = (int) (TITLE_OFFSET_DIPS * density);
TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.stl_SmartTabLayout, defStyle, 0);
tabBackgroundResId = a.getResourceId(
R.styleable.stl_SmartTabLayout_stl_defaultTabBackground, tabBackgroundResId);
textAllCaps = a.getBoolean(
R.styleable.stl_SmartTabLayout_stl_defaultTabTextAllCaps, textAllCaps);
textColors = a.getColorStateList(
R.styleable.stl_SmartTabLayout_stl_defaultTabTextColor);
textSize = a.getDimension(
R.styleable.stl_SmartTabLayout_stl_defaultTabTextSize, textSize);
textHorizontalPadding = a.getDimensionPixelSize(
R.styleable.stl_SmartTabLayout_stl_defaultTabTextHorizontalPadding, textHorizontalPadding);
textMinWidth = a.getDimensionPixelSize(
R.styleable.stl_SmartTabLayout_stl_defaultTabTextMinWidth, textMinWidth);
customTabLayoutId = a.getResourceId(
R.styleable.stl_SmartTabLayout_stl_customTabTextLayoutId, customTabLayoutId);
customTabTextViewId = a.getResourceId(
R.styleable.stl_SmartTabLayout_stl_customTabTextViewId, customTabTextViewId);
distributeEvenly = a.getBoolean(
R.styleable.stl_SmartTabLayout_stl_distributeEvenly, distributeEvenly);
clickable = a.getBoolean(
R.styleable.stl_SmartTabLayout_stl_clickable, clickable);
titleOffset = a.getLayoutDimension(
R.styleable.stl_SmartTabLayout_stl_titleOffset, titleOffset);
a.recycle();
this.titleOffset = titleOffset;
this.tabViewBackgroundResId = tabBackgroundResId;
this.tabViewTextAllCaps = textAllCaps;
this.tabViewTextColors = (textColors != null)
? textColors
: ColorStateList.valueOf(TAB_VIEW_TEXT_COLOR);
this.tabViewTextSize = textSize;
this.tabViewTextHorizontalPadding = textHorizontalPadding;
this.tabViewTextMinWidth = textMinWidth;
this.internalTabClickListener = clickable ? new InternalTabClickListener() : null;
this.distributeEvenly = distributeEvenly;
if (customTabLayoutId != NO_ID) {
setCustomTabView(customTabLayoutId, customTabTextViewId);
}
this.tabStrip = new SmartTabStrip(context, attrs);
if (distributeEvenly && tabStrip.isIndicatorAlwaysInCenter()) {
throw new UnsupportedOperationException(
"'distributeEvenly' and 'indicatorAlwaysInCenter' both use does not support");
}
// Make sure that the Tab Strips fills this View
setFillViewport(!tabStrip.isIndicatorAlwaysInCenter());
addView(tabStrip, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (onScrollChangeListener != null) {
onScrollChangeListener.onScrollChanged(l, oldl);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (tabStrip.isIndicatorAlwaysInCenter() && tabStrip.getChildCount() > 0) {
View firstTab = tabStrip.getChildAt(0);
View lastTab = tabStrip.getChildAt(tabStrip.getChildCount() - 1);
int start = (w - Utils.getMeasuredWidth(firstTab)) / 2 - Utils.getMarginStart(firstTab);
int end = (w - Utils.getMeasuredWidth(lastTab)) / 2 - Utils.getMarginEnd(lastTab);
tabStrip.setMinimumWidth(tabStrip.getMeasuredWidth());
ViewCompat.setPaddingRelative(this, start, getPaddingTop(), end, getPaddingBottom());
setClipToPadding(false);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
// Ensure first scroll
if (changed && viewPager != null) {
scrollToTab(viewPager.getCurrentItem(), 0);
}
}
/**
* Set the behavior of the Indicator scrolling feedback.
*
* @param interpolator {@link com.ogaclejapan.smarttablayout.SmartTabIndicationInterpolator}
*/
public void setIndicationInterpolator(SmartTabIndicationInterpolator interpolator) {
tabStrip.setIndicationInterpolator(interpolator);
}
/**
* Set the custom {@link TabColorizer} to be used.
*
* If you only require simple customisation then you can use
* {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
* similar effects.
*/
public void setCustomTabColorizer(TabColorizer tabColorizer) {
tabStrip.setCustomTabColorizer(tabColorizer);
}
/**
* Set the color used for styling the tab text. This will need to be called prior to calling
* {@link #setViewPager(ViewPager)} otherwise it will not get set
*
* @param color to use for tab text
*/
public void setDefaultTabTextColor(int color) {
tabViewTextColors = ColorStateList.valueOf(color);
}
/**
* Sets the colors used for styling the tab text. This will need to be called prior to calling
* {@link #setViewPager(ViewPager)} otherwise it will not get set
*
* @param colors ColorStateList to use for tab text
*/
public void setDefaultTabTextColor(ColorStateList colors) {
tabViewTextColors = colors;
}
/**
* Set the same weight for tab
*/
public void setDistributeEvenly(boolean distributeEvenly) {
this.distributeEvenly = distributeEvenly;
}
/**
* Sets the colors to be used for indicating the selected tab. These colors are treated as a
* circular array. Providing one color will mean that all tabs are indicated with the same color.
*/
public void setSelectedIndicatorColors(int... colors) {
tabStrip.setSelectedIndicatorColors(colors);
}
/**
* Sets the colors to be used for tab dividers. These colors are treated as a circular array.
* Providing one color will mean that all tabs are indicated with the same color.
*/
public void setDividerColors(int... colors) {
tabStrip.setDividerColors(colors);
}
/**
* Set the {@link ViewPager.OnPageChangeListener}. When using {@link SmartTabLayout} you are
* required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
* that the layout can update it's scroll position correctly.
*
* @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
*/
public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
viewPagerPageChangeListener = listener;
}
/**
* Set {@link OnScrollChangeListener} for obtaining values of scrolling.
*
* @param listener the {@link OnScrollChangeListener} to set
*/
public void setOnScrollChangeListener(OnScrollChangeListener listener) {
onScrollChangeListener = listener;
}
/**
* Set {@link OnTabClickListener} for obtaining click event.
*
* @param listener the {@link OnTabClickListener} to set
*/
public void setOnTabClickListener(OnTabClickListener listener) {
onTabClickListener = listener;
}
/**
* Set the custom layout to be inflated for the tab views.
*
* @param layoutResId Layout id to be inflated
* @param textViewId id of the {@link android.widget.TextView} in the inflated view
*/
public void setCustomTabView(int layoutResId, int textViewId) {
tabProvider = new SimpleTabProvider(getContext(), layoutResId, textViewId);
}
/**
* Set the custom layout to be inflated for the tab views.
*
* @param provider {@link TabProvider}
*/
public void setCustomTabView(TabProvider provider) {
tabProvider = provider;
}
/**
* Sets the associated view pager. Note that the assumption here is that the pager content
* (number of tabs and tab titles) does not change after this call has been made.
*/
public void setViewPager(ViewPager viewPager) {
tabStrip.removeAllViews();
this.viewPager = viewPager;
if (viewPager != null && viewPager.getAdapter() != null) {
viewPager.addOnPageChangeListener(new InternalViewPagerListener());
populateTabStrip();
}
}
/**
* Returns the view at the specified position in the tabs.
*
* @param position the position at which to get the view from
* @return the view at the specified position or null if the position does not exist within the
* tabs
*/
public View getTabAt(int position) {
return tabStrip.getChildAt(position);
}
/**
* Create a default view to be used for tabs. This is called if a custom tab view is not set via
* {@link #setCustomTabView(int, int)}.
*/
protected TextView createDefaultTabView(CharSequence title) {
TextView textView = new TextView(getContext());
textView.setGravity(Gravity.CENTER);
textView.setText(title);
textView.setTextColor(tabViewTextColors);
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabViewTextSize);
textView.setTypeface(Typeface.DEFAULT_BOLD);
textView.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT));
if (tabViewBackgroundResId != NO_ID) {
textView.setBackgroundResource(tabViewBackgroundResId);
} else {
// If we're running on Honeycomb or newer, then we can use the Theme's
// selectableItemBackground to ensure that the View has a pressed state
TypedValue outValue = new TypedValue();
getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
outValue, true);
textView.setBackgroundResource(outValue.resourceId);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
textView.setAllCaps(tabViewTextAllCaps);
}
textView.setPadding(
tabViewTextHorizontalPadding, 0,
tabViewTextHorizontalPadding, 0);
if (tabViewTextMinWidth > 0) {
textView.setMinWidth(tabViewTextMinWidth);
}
return textView;
}
private void populateTabStrip() {
final PagerAdapter adapter = viewPager.getAdapter();
for (int i = 0; i < adapter.getCount(); i++) {
final View tabView = (tabProvider == null)
? createDefaultTabView(adapter.getPageTitle(i))
: tabProvider.createTabView(tabStrip, i, adapter);
if (tabView == null) {
throw new IllegalStateException("tabView is null.");
}
if (distributeEvenly) {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) tabView.getLayoutParams();
lp.width = 0;
lp.weight = 1;
}
if (internalTabClickListener != null) {
tabView.setOnClickListener(internalTabClickListener);
}
tabStrip.addView(tabView);
if (i == viewPager.getCurrentItem()) {
tabView.setSelected(true);
}
}
}
private void scrollToTab(int tabIndex, float positionOffset) {
final int tabStripChildCount = tabStrip.getChildCount();
if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
return;
}
final boolean isLayoutRtl = Utils.isLayoutRtl(this);
View selectedTab = tabStrip.getChildAt(tabIndex);
int widthPlusMargin = Utils.getWidth(selectedTab) + Utils.getMarginHorizontally(selectedTab);
int extraOffset = (int) (positionOffset * widthPlusMargin);
if (tabStrip.isIndicatorAlwaysInCenter()) {
if (0f < positionOffset && positionOffset < 1f) {
View nextTab = tabStrip.getChildAt(tabIndex + 1);
int selectHalfWidth = Utils.getWidth(selectedTab) / 2 + Utils.getMarginEnd(selectedTab);
int nextHalfWidth = Utils.getWidth(nextTab) / 2 + Utils.getMarginStart(nextTab);
extraOffset = Math.round(positionOffset * (selectHalfWidth + nextHalfWidth));
}
View firstTab = tabStrip.getChildAt(0);
int x;
if (isLayoutRtl) {
int first = Utils.getWidth(firstTab) + Utils.getMarginEnd(firstTab);
int selected = Utils.getWidth(selectedTab) + Utils.getMarginEnd(selectedTab);
x = Utils.getEnd(selectedTab) - Utils.getMarginEnd(selectedTab) - extraOffset;
x -= (first - selected) / 2;
} else {
int first = Utils.getWidth(firstTab) + Utils.getMarginStart(firstTab);
int selected = Utils.getWidth(selectedTab) + Utils.getMarginStart(selectedTab);
x = Utils.getStart(selectedTab) - Utils.getMarginStart(selectedTab) + extraOffset;
x -= (first - selected) / 2;
}
scrollTo(x, 0);
return;
}
int x;
if (titleOffset == TITLE_OFFSET_AUTO_CENTER) {
if (0f < positionOffset && positionOffset < 1f) {
View nextTab = tabStrip.getChildAt(tabIndex + 1);
int selectHalfWidth = Utils.getWidth(selectedTab) / 2 + Utils.getMarginEnd(selectedTab);
int nextHalfWidth = Utils.getWidth(nextTab) / 2 + Utils.getMarginStart(nextTab);
extraOffset = Math.round(positionOffset * (selectHalfWidth + nextHalfWidth));
}
if (isLayoutRtl) {
x = -Utils.getWidthWithMargin(selectedTab) / 2 + getWidth() / 2;
x -= Utils.getPaddingStart(this);
} else {
x = Utils.getWidthWithMargin(selectedTab) / 2 - getWidth() / 2;
x += Utils.getPaddingStart(this);
}
} else {
if (isLayoutRtl) {
x = (tabIndex > 0 || positionOffset > 0) ? titleOffset : 0;
} else {
x = (tabIndex > 0 || positionOffset > 0) ? -titleOffset : 0;
}
}
int start = Utils.getStart(selectedTab);
int startMargin = Utils.getMarginStart(selectedTab);
if (isLayoutRtl) {
x += start + startMargin - extraOffset - getWidth() + Utils.getPaddingHorizontally(this);
} else {
x += start - startMargin + extraOffset;
}
scrollTo(x, 0);
}
/**
* Allows complete control over the colors drawn in the tab layout. Set with
* {@link #setCustomTabColorizer(TabColorizer)}.
*/
public interface TabColorizer {
/**
* @return return the color of the indicator used when {@code position} is selected.
*/
int getIndicatorColor(int position);
/**
* @return return the color of the divider drawn to the right of {@code position}.
*/
int getDividerColor(int position);
}
/**
* Interface definition for a callback to be invoked when the scroll position of a view changes.
*/
public interface OnScrollChangeListener {
/**
* Called when the scroll position of a view changes.
*
* @param scrollX Current horizontal scroll origin.
* @param oldScrollX Previous horizontal scroll origin.
*/
void onScrollChanged(int scrollX, int oldScrollX);
}
/**
* Interface definition for a callback to be invoked when a tab is clicked.
*/
public interface OnTabClickListener {
/**
* Called when a tab is clicked.
*
* @param position tab's position
*/
void onTabClicked(int position);
}
/**
* Create the custom tabs in the tab layout. Set with
* {@link #setCustomTabView(com.ogaclejapan.smarttablayout.SmartTabLayout.TabProvider)}
*/
public interface TabProvider {
/**
* @return Return the View of {@code position} for the Tabs
*/
View createTabView(ViewGroup container, int position, PagerAdapter adapter);
}
private static class SimpleTabProvider implements TabProvider {
private final LayoutInflater inflater;
private final int tabViewLayoutId;
private final int tabViewTextViewId;
private SimpleTabProvider(Context context, int layoutResId, int textViewId) {
inflater = LayoutInflater.from(context);
tabViewLayoutId = layoutResId;
tabViewTextViewId = textViewId;
}
@Override
public View createTabView(ViewGroup container, int position, PagerAdapter adapter) {
View tabView = null;
TextView tabTitleView = null;
if (tabViewLayoutId != NO_ID) {
tabView = inflater.inflate(tabViewLayoutId, container, false);
}
if (tabViewTextViewId != NO_ID && tabView != null) {
tabTitleView = (TextView) tabView.findViewById(tabViewTextViewId);
}
if (tabTitleView == null && TextView.class.isInstance(tabView)) {
tabTitleView = (TextView) tabView;
}
if (tabTitleView != null) {
tabTitleView.setText(adapter.getPageTitle(position));
}
return tabView;
}
}
private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
private int scrollState;
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
int tabStripChildCount = tabStrip.getChildCount();
if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
return;
}
tabStrip.onViewPagerPageChanged(position, positionOffset);
scrollToTab(position, positionOffset);
if (viewPagerPageChangeListener != null) {
viewPagerPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
@Override
public void onPageScrollStateChanged(int state) {
scrollState = state;
if (viewPagerPageChangeListener != null) {
viewPagerPageChangeListener.onPageScrollStateChanged(state);
}
}
@Override
public void onPageSelected(int position) {
if (scrollState == ViewPager.SCROLL_STATE_IDLE) {
tabStrip.onViewPagerPageChanged(position, 0f);
scrollToTab(position, 0);
}
for (int i = 0, size = tabStrip.getChildCount(); i < size; i++) {
tabStrip.getChildAt(i).setSelected(position == i);
}
if (viewPagerPageChangeListener != null) {
viewPagerPageChangeListener.onPageSelected(position);
}
}
}
private class InternalTabClickListener implements OnClickListener {
@Override
public void onClick(View v) {
for (int i = 0; i < tabStrip.getChildCount(); i++) {
if (v == tabStrip.getChildAt(i)) {
if (onTabClickListener != null) {
onTabClickListener.onTabClicked(i);
}
viewPager.setCurrentItem(i);
return;
}
}
}
}
}
================================================
FILE: library/src/main/java/com/ogaclejapan/smarttablayout/SmartTabStrip.java
================================================
/**
* Copyright (C) 2015 ogaclejapan
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ogaclejapan.smarttablayout;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.widget.LinearLayout;
/**
*
* Forked from Google Samples > SlidingTabsBasic >
* SlidingTabStrip
*/
class SmartTabStrip extends LinearLayout {
private static final int GRAVITY_BOTTOM = 0;
private static final int GRAVITY_TOP = 1;
private static final int GRAVITY_CENTER = 2;
private static final int AUTO_WIDTH = -1;
private static final int DEFAULT_TOP_BORDER_THICKNESS_DIPS = 0;
private static final byte DEFAULT_TOP_BORDER_COLOR_ALPHA = 0x26;
private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
private static final float DEFAULT_INDICATOR_CORNER_RADIUS = 0f;
private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
private static final boolean DEFAULT_INDICATOR_IN_CENTER = false;
private static final boolean DEFAULT_INDICATOR_IN_FRONT = false;
private static final boolean DEFAULT_INDICATOR_WITHOUT_PADDING = false;
private static final int DEFAULT_INDICATOR_GRAVITY = GRAVITY_BOTTOM;
private static final boolean DEFAULT_DRAW_DECORATION_AFTER_TAB = false;
private final int topBorderThickness;
private final int topBorderColor;
private final int bottomBorderThickness;
private final int bottomBorderColor;
private final Paint borderPaint;
private final RectF indicatorRectF = new RectF();
private final boolean indicatorWithoutPadding;
private final boolean indicatorAlwaysInCenter;
private final boolean indicatorInFront;
private final int indicatorThickness;
private final int indicatorWidth;
private final int indicatorGravity;
private final float indicatorCornerRadius;
private final Paint indicatorPaint;
private final int dividerThickness;
private final Paint dividerPaint;
private final float dividerHeight;
private final SimpleTabColorizer defaultTabColorizer;
private final boolean drawDecorationAfterTab;
private int lastPosition;
private int selectedPosition;
private float selectionOffset;
private SmartTabIndicationInterpolator indicationInterpolator;
private SmartTabLayout.TabColorizer customTabColorizer;
SmartTabStrip(Context context, AttributeSet attrs) {
super(context);
setWillNotDraw(false);
final float density = getResources().getDisplayMetrics().density;
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.colorForeground, outValue, true);
final int themeForegroundColor = outValue.data;
boolean indicatorWithoutPadding = DEFAULT_INDICATOR_WITHOUT_PADDING;
boolean indicatorInFront = DEFAULT_INDICATOR_IN_FRONT;
boolean indicatorAlwaysInCenter = DEFAULT_INDICATOR_IN_CENTER;
int indicationInterpolatorId = SmartTabIndicationInterpolator.ID_SMART;
int indicatorGravity = DEFAULT_INDICATOR_GRAVITY;
int indicatorColor = DEFAULT_SELECTED_INDICATOR_COLOR;
int indicatorColorsId = NO_ID;
int indicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
int indicatorWidth = AUTO_WIDTH;
float indicatorCornerRadius = DEFAULT_INDICATOR_CORNER_RADIUS * density;
int overlineColor = setColorAlpha(themeForegroundColor, DEFAULT_TOP_BORDER_COLOR_ALPHA);
int overlineThickness = (int) (DEFAULT_TOP_BORDER_THICKNESS_DIPS * density);
int underlineColor = setColorAlpha(themeForegroundColor, DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
int underlineThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
int dividerColor = setColorAlpha(themeForegroundColor, DEFAULT_DIVIDER_COLOR_ALPHA);
int dividerColorsId = NO_ID;
int dividerThickness = (int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density);
boolean drawDecorationAfterTab = DEFAULT_DRAW_DECORATION_AFTER_TAB;
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.stl_SmartTabLayout);
indicatorAlwaysInCenter = a.getBoolean(
R.styleable.stl_SmartTabLayout_stl_indicatorAlwaysInCenter, indicatorAlwaysInCenter);
indicatorWithoutPadding = a.getBoolean(
R.styleable.stl_SmartTabLayout_stl_indicatorWithoutPadding, indicatorWithoutPadding);
indicatorInFront = a.getBoolean(
R.styleable.stl_SmartTabLayout_stl_indicatorInFront, indicatorInFront);
indicationInterpolatorId = a.getInt(
R.styleable.stl_SmartTabLayout_stl_indicatorInterpolation, indicationInterpolatorId);
indicatorGravity = a.getInt(
R.styleable.stl_SmartTabLayout_stl_indicatorGravity, indicatorGravity);
indicatorColor = a.getColor(
R.styleable.stl_SmartTabLayout_stl_indicatorColor, indicatorColor);
indicatorColorsId = a.getResourceId(
R.styleable.stl_SmartTabLayout_stl_indicatorColors, indicatorColorsId);
indicatorThickness = a.getDimensionPixelSize(
R.styleable.stl_SmartTabLayout_stl_indicatorThickness, indicatorThickness);
indicatorWidth = a.getLayoutDimension(
R.styleable.stl_SmartTabLayout_stl_indicatorWidth, indicatorWidth);
indicatorCornerRadius = a.getDimension(
R.styleable.stl_SmartTabLayout_stl_indicatorCornerRadius, indicatorCornerRadius);
overlineColor = a.getColor(
R.styleable.stl_SmartTabLayout_stl_overlineColor, overlineColor);
overlineThickness = a.getDimensionPixelSize(
R.styleable.stl_SmartTabLayout_stl_overlineThickness, overlineThickness);
underlineColor = a.getColor(
R.styleable.stl_SmartTabLayout_stl_underlineColor, underlineColor);
underlineThickness = a.getDimensionPixelSize(
R.styleable.stl_SmartTabLayout_stl_underlineThickness, underlineThickness);
dividerColor = a.getColor(
R.styleable.stl_SmartTabLayout_stl_dividerColor, dividerColor);
dividerColorsId = a.getResourceId(
R.styleable.stl_SmartTabLayout_stl_dividerColors, dividerColorsId);
dividerThickness = a.getDimensionPixelSize(
R.styleable.stl_SmartTabLayout_stl_dividerThickness, dividerThickness);
drawDecorationAfterTab = a.getBoolean(
R.styleable.stl_SmartTabLayout_stl_drawDecorationAfterTab, drawDecorationAfterTab);
a.recycle();
final int[] indicatorColors = (indicatorColorsId == NO_ID)
? new int[] { indicatorColor }
: getResources().getIntArray(indicatorColorsId);
final int[] dividerColors = (dividerColorsId == NO_ID)
? new int[] { dividerColor }
: getResources().getIntArray(dividerColorsId);
this.defaultTabColorizer = new SimpleTabColorizer();
this.defaultTabColorizer.setIndicatorColors(indicatorColors);
this.defaultTabColorizer.setDividerColors(dividerColors);
this.topBorderThickness = overlineThickness;
this.topBorderColor = overlineColor;
this.bottomBorderThickness = underlineThickness;
this.bottomBorderColor = underlineColor;
this.borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.indicatorAlwaysInCenter = indicatorAlwaysInCenter;
this.indicatorWithoutPadding = indicatorWithoutPadding;
this.indicatorInFront = indicatorInFront;
this.indicatorThickness = indicatorThickness;
this.indicatorWidth = indicatorWidth;
this.indicatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.indicatorCornerRadius = indicatorCornerRadius;
this.indicatorGravity = indicatorGravity;
this.dividerHeight = DEFAULT_DIVIDER_HEIGHT;
this.dividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.dividerPaint.setStrokeWidth(dividerThickness);
this.dividerThickness = dividerThickness;
this.drawDecorationAfterTab = drawDecorationAfterTab;
this.indicationInterpolator = SmartTabIndicationInterpolator.of(indicationInterpolatorId);
}
/**
* Set the alpha value of the {@code color} to be the given {@code alpha} value.
*/
private static int setColorAlpha(int color, byte alpha) {
return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
}
/**
* Blend {@code color1} and {@code color2} using the given ratio.
*
* @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
* 0.0 will return {@code color2}.
*/
private static int blendColors(int color1, int color2, float ratio) {
final float inverseRation = 1f - ratio;
float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
return Color.rgb((int) r, (int) g, (int) b);
}
void setIndicationInterpolator(SmartTabIndicationInterpolator interpolator) {
indicationInterpolator = interpolator;
invalidate();
}
void setCustomTabColorizer(SmartTabLayout.TabColorizer customTabColorizer) {
this.customTabColorizer = customTabColorizer;
invalidate();
}
void setSelectedIndicatorColors(int... colors) {
// Make sure that the custom colorizer is removed
customTabColorizer = null;
defaultTabColorizer.setIndicatorColors(colors);
invalidate();
}
void setDividerColors(int... colors) {
// Make sure that the custom colorizer is removed
customTabColorizer = null;
defaultTabColorizer.setDividerColors(colors);
invalidate();
}
void onViewPagerPageChanged(int position, float positionOffset) {
selectedPosition = position;
selectionOffset = positionOffset;
if (positionOffset == 0f && lastPosition != selectedPosition) {
lastPosition = selectedPosition;
}
invalidate();
}
boolean isIndicatorAlwaysInCenter() {
return indicatorAlwaysInCenter;
}
SmartTabLayout.TabColorizer getTabColorizer() {
return (customTabColorizer != null) ? customTabColorizer : defaultTabColorizer;
}
@Override
protected void onDraw(Canvas canvas) {
if (!drawDecorationAfterTab) {
drawDecoration(canvas);
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (drawDecorationAfterTab) {
drawDecoration(canvas);
}
}
private void drawDecoration(Canvas canvas) {
final int height = getHeight();
final int width = getWidth();
final int tabCount = getChildCount();
final SmartTabLayout.TabColorizer tabColorizer = getTabColorizer();
final boolean isLayoutRtl = Utils.isLayoutRtl(this);
if (indicatorInFront) {
drawOverline(canvas, 0, width);
drawUnderline(canvas, 0, width, height);
}
// Thick colored underline below the current selection
if (tabCount > 0) {
View selectedTab = getChildAt(selectedPosition);
int selectedStart = Utils.getStart(selectedTab, indicatorWithoutPadding);
int selectedEnd = Utils.getEnd(selectedTab, indicatorWithoutPadding);
int left;
int right;
if (isLayoutRtl) {
left = selectedEnd;
right = selectedStart;
} else {
left = selectedStart;
right = selectedEnd;
}
int color = tabColorizer.getIndicatorColor(selectedPosition);
float thickness = indicatorThickness;
if (selectionOffset > 0f && selectedPosition < (getChildCount() - 1)) {
int nextColor = tabColorizer.getIndicatorColor(selectedPosition + 1);
if (color != nextColor) {
color = blendColors(nextColor, color, selectionOffset);
}
// Draw the selection partway between the tabs
float startOffset = indicationInterpolator.getLeftEdge(selectionOffset);
float endOffset = indicationInterpolator.getRightEdge(selectionOffset);
float thicknessOffset = indicationInterpolator.getThickness(selectionOffset);
View nextTab = getChildAt(selectedPosition + 1);
int nextStart = Utils.getStart(nextTab, indicatorWithoutPadding);
int nextEnd = Utils.getEnd(nextTab, indicatorWithoutPadding);
if (isLayoutRtl) {
left = (int) (endOffset * nextEnd + (1.0f - endOffset) * left);
right = (int) (startOffset * nextStart + (1.0f - startOffset) * right);
} else {
left = (int) (startOffset * nextStart + (1.0f - startOffset) * left);
right = (int) (endOffset * nextEnd + (1.0f - endOffset) * right);
}
thickness = thickness * thicknessOffset;
}
drawIndicator(canvas, left, right, height, thickness, color);
}
if (!indicatorInFront) {
drawOverline(canvas, 0, width);
drawUnderline(canvas, 0, getWidth(), height);
}
// Vertical separators between the titles
drawSeparator(canvas, height, tabCount);
}
private void drawSeparator(Canvas canvas, int height, int tabCount) {
if (dividerThickness <= 0) {
return;
}
final int dividerHeightPx = (int) (Math.min(Math.max(0f, dividerHeight), 1f) * height);
final SmartTabLayout.TabColorizer tabColorizer = getTabColorizer();
// Vertical separators between the titles
final int separatorTop = (height - dividerHeightPx) / 2;
final int separatorBottom = separatorTop + dividerHeightPx;
final boolean isLayoutRtl = Utils.isLayoutRtl(this);
for (int i = 0; i < tabCount - 1; i++) {
View child = getChildAt(i);
int end = Utils.getEnd(child);
int endMargin = Utils.getMarginEnd(child);
int separatorX = isLayoutRtl ? end - endMargin : end + endMargin;
dividerPaint.setColor(tabColorizer.getDividerColor(i));
canvas.drawLine(separatorX, separatorTop, separatorX, separatorBottom, dividerPaint);
}
}
private void drawIndicator(Canvas canvas, int left, int right, int height, float thickness,
int color) {
if (indicatorThickness <= 0 || indicatorWidth == 0) {
return;
}
float center;
float top;
float bottom;
switch (indicatorGravity) {
case GRAVITY_TOP:
center = indicatorThickness / 2f;
top = center - (thickness / 2f);
bottom = center + (thickness / 2f);
break;
case GRAVITY_CENTER:
center = height / 2f;
top = center - (thickness / 2f);
bottom = center + (thickness / 2f);
break;
case GRAVITY_BOTTOM:
default:
center = height - (indicatorThickness / 2f);
top = center - (thickness / 2f);
bottom = center + (thickness / 2f);
}
indicatorPaint.setColor(color);
if (indicatorWidth == AUTO_WIDTH) {
indicatorRectF.set(left, top, right, bottom);
} else {
float padding = (Math.abs(left - right) - indicatorWidth) / 2f;
indicatorRectF.set(left + padding, top, right - padding, bottom);
}
if (indicatorCornerRadius > 0f) {
canvas.drawRoundRect(
indicatorRectF, indicatorCornerRadius,
indicatorCornerRadius, indicatorPaint);
} else {
canvas.drawRect(indicatorRectF, indicatorPaint);
}
}
private void drawOverline(Canvas canvas, int left, int right) {
if (topBorderThickness <= 0) {
return;
}
// Thin overline along the entire top edge
borderPaint.setColor(topBorderColor);
canvas.drawRect(left, 0, right, topBorderThickness, borderPaint);
}
private void drawUnderline(Canvas canvas, int left, int right, int height) {
if (bottomBorderThickness <= 0) {
return;
}
// Thin underline along the entire bottom edge
borderPaint.setColor(bottomBorderColor);
canvas.drawRect(left, height - bottomBorderThickness, right, height, borderPaint);
}
private static class SimpleTabColorizer implements SmartTabLayout.TabColorizer {
private int[] indicatorColors;
private int[] dividerColors;
@Override
public final int getIndicatorColor(int position) {
return indicatorColors[position % indicatorColors.length];
}
@Override
public final int getDividerColor(int position) {
return dividerColors[position % dividerColors.length];
}
void setIndicatorColors(int... colors) {
indicatorColors = colors;
}
void setDividerColors(int... colors) {
dividerColors = colors;
}
}
}
================================================
FILE: library/src/main/java/com/ogaclejapan/smarttablayout/Utils.java
================================================
/**
* Copyright (C) 2015 ogaclejapan
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ogaclejapan.smarttablayout;
import android.view.View;
import android.view.ViewGroup;
import androidx.core.view.MarginLayoutParamsCompat;
import androidx.core.view.ViewCompat;
final class Utils {
static int getMeasuredWidth(View v) {
return (v == null) ? 0 : v.getMeasuredWidth();
}
static int getWidth(View v) {
return (v == null) ? 0 : v.getWidth();
}
static int getWidthWithMargin(View v) {
return getWidth(v) + getMarginHorizontally(v);
}
static int getStart(View v) {
return getStart(v, false);
}
static int getStart(View v, boolean withoutPadding) {
if (v == null) {
return 0;
}
if (isLayoutRtl(v)) {
return (withoutPadding) ? v.getRight() - getPaddingStart(v) : v.getRight();
} else {
return (withoutPadding) ? v.getLeft() + getPaddingStart(v) : v.getLeft();
}
}
static int getEnd(View v) {
return getEnd(v, false);
}
static int getEnd(View v, boolean withoutPadding) {
if (v == null) {
return 0;
}
if (isLayoutRtl(v)) {
return (withoutPadding) ? v.getLeft() + getPaddingEnd(v) : v.getLeft();
} else {
return (withoutPadding) ? v.getRight() - getPaddingEnd(v) : v.getRight();
}
}
static int getPaddingStart(View v) {
if (v == null) {
return 0;
}
return ViewCompat.getPaddingStart(v);
}
static int getPaddingEnd(View v) {
if (v == null) {
return 0;
}
return ViewCompat.getPaddingEnd(v);
}
static int getPaddingHorizontally(View v) {
if (v == null) {
return 0;
}
return v.getPaddingLeft() + v.getPaddingRight();
}
static int getMarginStart(View v) {
if (v == null) {
return 0;
}
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
return MarginLayoutParamsCompat.getMarginStart(lp);
}
static int getMarginEnd(View v) {
if (v == null) {
return 0;
}
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
return MarginLayoutParamsCompat.getMarginEnd(lp);
}
static int getMarginHorizontally(View v) {
if (v == null) {
return 0;
}
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
return MarginLayoutParamsCompat.getMarginStart(lp) + MarginLayoutParamsCompat.getMarginEnd(lp);
}
static boolean isLayoutRtl(View v) {
return ViewCompat.getLayoutDirection(v) == ViewCompat.LAYOUT_DIRECTION_RTL;
}
private Utils() { }
}
================================================
FILE: library/src/main/res/values/attrs.xml
================================================
================================================
FILE: publish.gradle
================================================
apply plugin: 'com.jfrog.bintray'
apply plugin: 'com.github.dcendents.android-maven'
// build a jar with source files
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
task javadoc(type: Javadoc) {
failOnError false
source = android.sourceSets.main.java.sourceFiles
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
classpath += configurations.compile
}
// build a jar with javadoc
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives sourcesJar
archives javadocJar
}
install {
repositories.mavenInstaller {
// This generates POM.xml with proper parameters
pom {
project {
packaging 'aar'
name ARTIFACT_NAME
description ARTIFACT_DESCRIPTION
url SITE_URL
licenses {
license {
name LICENCE_NAME
url LICENCE_URL
distribution LICENCE_DIST
}
}
developers {
developer {
id DEVELOPER_ID
name DEVELOPER_NAME
email DEVELOPER_EMAIL
}
}
issueManagement {
system ISSUE_SYSTEM
url ISSUE_URL
}
scm {
connection SCM_CONNECTION
developerConnection SCM_DEV_CONNECTION
url SCM_URL
}
}
}
}
}
bintray {
user = findProperty('BINTRAY_USER')
key = findProperty('BINTRAY_APIKEY')
configurations = ['archives']
dryRun = false
publish = true
pkg {
repo = "maven"
name = pkginfo.name
desc = pkginfo.description
websiteUrl = pkginfo.site
issueTrackerUrl = pkginfo.issue
vcsUrl = pkginfo.vcs
licenses = ["Apache-2.0"]
labels = ['android']
publicDownloadNumbers = true
version {
gpg {
sign = true
passphrase = findProperty('BINTRAY_GPG_PASSPHRASE')
}
mavenCentralSync {
sync = true
user = findProperty('NEXUS_USER')
password = findProperty('NEXUS_PASSWORD')
close = '1'
}
}
}
}
================================================
FILE: settings.gradle
================================================
include ':demo', ':library', ':utils-v4'
================================================
FILE: utils-v4/.gitignore
================================================
/build
================================================
FILE: utils-v4/LICENSE
================================================
Copyright (C) 2015 ogaclejapan
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: utils-v4/build.gradle
================================================
apply plugin: 'com.android.library'
apply plugin: 'com.github.ben-manes.versions'
apply plugin: 'com.github.hierynomus.license'
android {
compileSdkVersion COMPILE_SDK_VERSION as int
defaultConfig {
minSdkVersion 14
targetSdkVersion COMPILE_SDK_VERSION as int
versionCode VERSION_CODE as int
versionName VERSION_NAME
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation "androidx.fragment:fragment:${ANDROIDX_BASE_VERSION}"
}
license {
sourceSets {
main.java.srcDirs = android.sourceSets.main.java.srcDirs
main.resources.srcDirs = android.sourceSets.main.resources.srcDirs
}
ext.year = Calendar.getInstance().get(Calendar.YEAR)
ext.name = DEVELOPER_ID
}
pkginfo.name = ARTIFACT_NAME + '-V4Utils'
apply from: "${project.rootDir}/publish.gradle"
================================================
FILE: utils-v4/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/msk/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: utils-v4/src/main/AndroidManifest.xml
================================================
================================================
FILE: utils-v4/src/main/java/com/ogaclejapan/smarttablayout/utils/PagerItem.java
================================================
/**
* Copyright (C) 2015 ogaclejapan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ogaclejapan.smarttablayout.utils;
public abstract class PagerItem {
protected static final float DEFAULT_WIDTH = 1.f;
private final CharSequence title;
private final float width;
protected PagerItem(CharSequence title, float width) {
this.title = title;
this.width = width;
}
public CharSequence getTitle() {
return title;
}
public float getWidth() {
return width;
}
}
================================================
FILE: utils-v4/src/main/java/com/ogaclejapan/smarttablayout/utils/PagerItems.java
================================================
/**
* Copyright (C) 2015 ogaclejapan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ogaclejapan.smarttablayout.utils;
import android.content.Context;
import java.util.ArrayList;
public abstract class PagerItems extends ArrayList {
private final Context context;
protected PagerItems(Context context) {
this.context = context;
}
public Context getContext() {
return context;
}
}
================================================
FILE: utils-v4/src/main/java/com/ogaclejapan/smarttablayout/utils/ViewPagerItem.java
================================================
/**
* Copyright (C) 2015 ogaclejapan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ogaclejapan.smarttablayout.utils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.LayoutRes;
public class ViewPagerItem extends PagerItem {
private final int resource;
protected ViewPagerItem(CharSequence title, float width, @LayoutRes int resource) {
super(title, width);
this.resource = resource;
}
public static ViewPagerItem of(CharSequence title, @LayoutRes int resource) {
return of(title, DEFAULT_WIDTH, resource);
}
public static ViewPagerItem of(CharSequence title, float width, @LayoutRes int resource) {
return new ViewPagerItem(title, width, resource);
}
public View initiate(LayoutInflater inflater, ViewGroup container) {
return inflater.inflate(resource, container, false);
}
}
================================================
FILE: utils-v4/src/main/java/com/ogaclejapan/smarttablayout/utils/ViewPagerItemAdapter.java
================================================
/**
* Copyright (C) 2015 ogaclejapan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ogaclejapan.smarttablayout.utils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.lang.ref.WeakReference;
import androidx.annotation.NonNull;
import androidx.collection.SparseArrayCompat;
import androidx.viewpager.widget.PagerAdapter;
public class ViewPagerItemAdapter extends PagerAdapter {
private final ViewPagerItems pages;
private final SparseArrayCompat> holder;
private final LayoutInflater inflater;
public ViewPagerItemAdapter(ViewPagerItems pages) {
this.pages = pages;
this.holder = new SparseArrayCompat<>(pages.size());
this.inflater = LayoutInflater.from(pages.getContext());
}
@Override
public int getCount() {
return pages.size();
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
View view = getPagerItem(position).initiate(inflater, container);
container.addView(view);
holder.put(position, new WeakReference(view));
return view;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
holder.remove(position);
container.removeView((View) object);
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return object == view;
}
@Override
public CharSequence getPageTitle(int position) {
return getPagerItem(position).getTitle();
}
@Override
public float getPageWidth(int position) {
return getPagerItem(position).getWidth();
}
public View getPage(int position) {
final WeakReference weakRefItem = holder.get(position);
return (weakRefItem != null) ? weakRefItem.get() : null;
}
protected ViewPagerItem getPagerItem(int position) {
return pages.get(position);
}
}
================================================
FILE: utils-v4/src/main/java/com/ogaclejapan/smarttablayout/utils/ViewPagerItems.java
================================================
/**
* Copyright (C) 2015 ogaclejapan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ogaclejapan.smarttablayout.utils;
import android.content.Context;
import androidx.annotation.LayoutRes;
import androidx.annotation.StringRes;
public class ViewPagerItems extends PagerItems {
public ViewPagerItems(Context context) {
super(context);
}
public static Creator with(Context context) {
return new Creator(context);
}
public static class Creator {
private final ViewPagerItems items;
public Creator(Context context) {
items = new ViewPagerItems(context);
}
public Creator add(@StringRes int title, @LayoutRes int resource) {
return add(ViewPagerItem.of(items.getContext().getString(title), resource));
}
public Creator add(@StringRes int title, float width, @LayoutRes int resource) {
return add(ViewPagerItem.of(items.getContext().getString(title), width, resource));
}
public Creator add(CharSequence title, @LayoutRes int resource) {
return add(ViewPagerItem.of(title, resource));
}
public Creator add(ViewPagerItem item) {
items.add(item);
return this;
}
public ViewPagerItems create() {
return items;
}
}
}
================================================
FILE: utils-v4/src/main/java/com/ogaclejapan/smarttablayout/utils/v4/Bundler.java
================================================
/**
* Copyright (C) 2015 ogaclejapan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ogaclejapan.smarttablayout.utils.v4;
import android.annotation.TargetApi;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
import android.util.Size;
import android.util.SizeF;
import android.util.SparseArray;
import java.io.Serializable;
import java.util.ArrayList;
import androidx.fragment.app.Fragment;
public class Bundler {
private final Bundle bundle;
/**
* Constructs a new, empty Bundle.
*/
public Bundler() {
this(null);
}
private Bundler(Bundle b) {
bundle = (b == null) ? new Bundle() : new Bundle(b);
}
/**
* Constructs a Bundle containing a copy of the mappings from the given
* Bundle.
*
* @param b a Bundle to be copied.
*/
public static Bundler of(Bundle b) {
return new Bundler(b);
}
/**
* Inserts all mappings from the given Bundle into this Bundle.
*
* @param bundle a Bundle
* @return this
*/
public Bundler putAll(Bundle bundle) {
this.bundle.putAll(bundle);
return this;
}
/**
* Inserts a byte value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a byte
* @return this
*/
public Bundler putByte(String key, byte value) {
bundle.putByte(key, value);
return this;
}
/**
* Inserts a char value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a char, or null
* @return this
*/
public Bundler putChar(String key, char value) {
bundle.putChar(key, value);
return this;
}
/**
* Inserts a short value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a short
* @return this
*/
public Bundler putShort(String key, short value) {
bundle.putShort(key, value);
return this;
}
/**
* Inserts a float value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a float
* @return this
*/
public Bundler putFloat(String key, float value) {
bundle.putFloat(key, value);
return this;
}
/**
* Inserts a CharSequence value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a CharSequence, or null
* @return this
*/
public Bundler putCharSequence(String key, CharSequence value) {
bundle.putCharSequence(key, value);
return this;
}
/**
* Inserts a Parcelable value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a Parcelable object, or null
* @return this
*/
public Bundler putParcelable(String key, Parcelable value) {
bundle.putParcelable(key, value);
return this;
}
/**
* Inserts a Size value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a Size object, or null
* @return this
*/
@TargetApi(21)
public Bundler putSize(String key, Size value) {
bundle.putSize(key, value);
return this;
}
/**
* Inserts a SizeF value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a SizeF object, or null
* @return this
*/
@TargetApi(21)
public Bundler putSizeF(String key, SizeF value) {
bundle.putSizeF(key, value);
return this;
}
/**
* Inserts an array of Parcelable values into the mapping of this Bundle,
* replacing any existing value for the given key. Either key or value may
* be null.
*
* @param key a String, or null
* @param value an array of Parcelable objects, or null
* @return this
*/
public Bundler putParcelableArray(String key, Parcelable[] value) {
bundle.putParcelableArray(key, value);
return this;
}
/**
* Inserts a List of Parcelable values into the mapping of this Bundle,
* replacing any existing value for the given key. Either key or value may
* be null.
*
* @param key a String, or null
* @param value an ArrayList of Parcelable objects, or null
* @return this
*/
public Bundler putParcelableArrayList(String key,
ArrayList extends Parcelable> value) {
bundle.putParcelableArrayList(key, value);
return this;
}
/**
* Inserts a SparceArray of Parcelable values into the mapping of this
* Bundle, replacing any existing value for the given key. Either key
* or value may be null.
*
* @param key a String, or null
* @param value a SparseArray of Parcelable objects, or null
* @return this
*/
public Bundler putSparseParcelableArray(String key,
SparseArray extends Parcelable> value) {
bundle.putSparseParcelableArray(key, value);
return this;
}
/**
* Inserts an ArrayList value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value an ArrayList object, or null
* @return this
*/
public Bundler putIntegerArrayList(String key, ArrayList value) {
bundle.putIntegerArrayList(key, value);
return this;
}
/**
* Inserts an ArrayList value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value an ArrayList object, or null
* @return this
*/
public Bundler putStringArrayList(String key, ArrayList value) {
bundle.putStringArrayList(key, value);
return this;
}
/**
* Inserts an ArrayList value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value an ArrayList object, or null
* @return this
*/
@TargetApi(8)
public Bundler putCharSequenceArrayList(String key, ArrayList value) {
bundle.putCharSequenceArrayList(key, value);
return this;
}
/**
* Inserts a Serializable value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a Serializable object, or null
* @return this
*/
public Bundler putSerializable(String key, Serializable value) {
bundle.putSerializable(key, value);
return this;
}
/**
* Inserts a byte array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a byte array object, or null
* @return this
*/
public Bundler putByteArray(String key, byte[] value) {
bundle.putByteArray(key, value);
return this;
}
/**
* Inserts a short array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a short array object, or null
* @return this
*/
public Bundler putShortArray(String key, short[] value) {
bundle.putShortArray(key, value);
return this;
}
/**
* Inserts a char array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a char array object, or null
* @return this
*/
public Bundler putCharArray(String key, char[] value) {
bundle.putCharArray(key, value);
return this;
}
/**
* Inserts a float array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a float array object, or null
* @return this
*/
public Bundler putFloatArray(String key, float[] value) {
bundle.putFloatArray(key, value);
return this;
}
/**
* Inserts a CharSequence array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a CharSequence array object, or null
* @return this
*/
@TargetApi(8)
public Bundler putCharSequenceArray(String key, CharSequence[] value) {
bundle.putCharSequenceArray(key, value);
return this;
}
/**
* Inserts a Bundle value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a Bundle object, or null
* @return this
*/
public Bundler putBundle(String key, Bundle value) {
bundle.putBundle(key, value);
return this;
}
/**
* Inserts an {@link android.os.IBinder} value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
*
You should be very careful when using this function. In many
* places where Bundles are used (such as inside of Intent objects), the Bundle
* can live longer inside of another process than the process that had originally
* created it. In that case, the IBinder you supply here will become invalid
* when your process goes away, and no longer usable, even if a new process is
* created for you later on.
*
* @param key a String, or null
* @param value an IBinder object, or null
* @return this
*/
@TargetApi(18)
public Bundler putBinder(String key, IBinder value) {
bundle.putBinder(key, value);
return this;
}
/**
* Inserts a Boolean value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a Boolean, or null
* @return this
*/
public Bundler putBoolean(String key, boolean value) {
bundle.putBoolean(key, value);
return this;
}
/**
* Inserts an int value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value an int, or null
* @return this
*/
public Bundler putInt(String key, int value) {
bundle.putInt(key, value);
return this;
}
/**
* Inserts a long value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a long
* @return this
*/
public Bundler putLong(String key, long value) {
bundle.putLong(key, value);
return this;
}
/**
* Inserts a double value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a double
* @return this
*/
public Bundler putDouble(String key, double value) {
bundle.putDouble(key, value);
return this;
}
/**
* Inserts a String value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a String, or null
* @return this
*/
public Bundler putString(String key, String value) {
bundle.putString(key, value);
return this;
}
/**
* Inserts a boolean array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a boolean array object, or null
* @return this
*/
public Bundler putBooleanArray(String key, boolean[] value) {
bundle.putBooleanArray(key, value);
return this;
}
/**
* Inserts an int array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value an int array object, or null
* @return this
*/
public Bundler putIntArray(String key, int[] value) {
bundle.putIntArray(key, value);
return this;
}
/**
* Inserts a long array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a long array object, or null
* @return this
*/
public Bundler putLongArray(String key, long[] value) {
bundle.putLongArray(key, value);
return this;
}
/**
* Inserts a double array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a double array object, or null
* @return this
*/
public Bundler putDoubleArray(String key, double[] value) {
bundle.putDoubleArray(key, value);
return this;
}
/**
* Inserts a String array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a String array object, or null
* @return this
*/
public Bundler putStringArray(String key, String[] value) {
bundle.putStringArray(key, value);
return this;
}
/**
* Get the bundle.
*
* @return a bundle
*/
public Bundle get() {
return bundle;
}
/**
* Set the argument of Fragment.
*
* @param fragment a fragment
* @return a fragment
*/
public T into(T fragment) {
fragment.setArguments(get());
return fragment;
}
}
================================================
FILE: utils-v4/src/main/java/com/ogaclejapan/smarttablayout/utils/v4/FragmentPagerItem.java
================================================
/**
* Copyright (C) 2015 ogaclejapan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ogaclejapan.smarttablayout.utils.v4;
import android.content.Context;
import android.os.Bundle;
import com.ogaclejapan.smarttablayout.utils.PagerItem;
import androidx.fragment.app.Fragment;
public class FragmentPagerItem extends PagerItem {
private static final String TAG = "FragmentPagerItem";
private static final String KEY_POSITION = TAG + ":Position";
private final String className;
private final Bundle args;
protected FragmentPagerItem(CharSequence title, float width, String className, Bundle args) {
super(title, width);
this.className = className;
this.args = args;
}
public static FragmentPagerItem of(CharSequence title, Class extends Fragment> clazz) {
return of(title, DEFAULT_WIDTH, clazz);
}
public static FragmentPagerItem of(CharSequence title, Class extends Fragment> clazz,
Bundle args) {
return of(title, DEFAULT_WIDTH, clazz, args);
}
public static FragmentPagerItem of(CharSequence title, float width,
Class extends Fragment> clazz) {
return of(title, width, clazz, new Bundle());
}
public static FragmentPagerItem of(CharSequence title, float width,
Class extends Fragment> clazz, Bundle args) {
return new FragmentPagerItem(title, width, clazz.getName(), args);
}
public static boolean hasPosition(Bundle args) {
return args != null && args.containsKey(KEY_POSITION);
}
public static int getPosition(Bundle args) {
return (hasPosition(args)) ? args.getInt(KEY_POSITION) : 0;
}
static void setPosition(Bundle args, int position) {
args.putInt(KEY_POSITION, position);
}
public Fragment instantiate(Context context, int position) {
setPosition(args, position);
return Fragment.instantiate(context, className, args);
}
}
================================================
FILE: utils-v4/src/main/java/com/ogaclejapan/smarttablayout/utils/v4/FragmentPagerItemAdapter.java
================================================
/**
* Copyright (C) 2015 ogaclejapan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ogaclejapan.smarttablayout.utils.v4;
import android.view.ViewGroup;
import java.lang.ref.WeakReference;
import androidx.annotation.NonNull;
import androidx.collection.SparseArrayCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
public class FragmentPagerItemAdapter extends FragmentPagerAdapter {
private final FragmentPagerItems pages;
private final SparseArrayCompat> holder;
public FragmentPagerItemAdapter(FragmentManager fm, FragmentPagerItems pages) {
super(fm);
this.pages = pages;
this.holder = new SparseArrayCompat<>(pages.size());
}
@Override
public int getCount() {
return pages.size();
}
@Override
public Fragment getItem(int position) {
return getPagerItem(position).instantiate(pages.getContext(), position);
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
Object item = super.instantiateItem(container, position);
if (item instanceof Fragment) {
holder.put(position, new WeakReference((Fragment) item));
}
return item;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
holder.remove(position);
super.destroyItem(container, position, object);
}
@Override
public CharSequence getPageTitle(int position) {
return getPagerItem(position).getTitle();
}
@Override
public float getPageWidth(int position) {
return super.getPageWidth(position);
}
public Fragment getPage(int position) {
final WeakReference weakRefItem = holder.get(position);
return (weakRefItem != null) ? weakRefItem.get() : null;
}
protected FragmentPagerItem getPagerItem(int position) {
return pages.get(position);
}
}
================================================
FILE: utils-v4/src/main/java/com/ogaclejapan/smarttablayout/utils/v4/FragmentPagerItems.java
================================================
/**
* Copyright (C) 2015 ogaclejapan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ogaclejapan.smarttablayout.utils.v4;
import android.content.Context;
import android.os.Bundle;
import com.ogaclejapan.smarttablayout.utils.PagerItems;
import androidx.annotation.StringRes;
import androidx.fragment.app.Fragment;
public class FragmentPagerItems extends PagerItems {
public FragmentPagerItems(Context context) {
super(context);
}
public static Creator with(Context context) {
return new Creator(context);
}
public static class Creator {
private final FragmentPagerItems items;
public Creator(Context context) {
items = new FragmentPagerItems(context);
}
public Creator add(@StringRes int title, Class extends Fragment> clazz) {
return add(FragmentPagerItem.of(items.getContext().getString(title), clazz));
}
public Creator add(@StringRes int title, Class extends Fragment> clazz, Bundle args) {
return add(FragmentPagerItem.of(items.getContext().getString(title), clazz, args));
}
public Creator add(@StringRes int title, float width, Class extends Fragment> clazz) {
return add(FragmentPagerItem.of(items.getContext().getString(title), width, clazz));
}
public Creator add(@StringRes int title, float width, Class extends Fragment> clazz,
Bundle args) {
return add(FragmentPagerItem.of(items.getContext().getString(title), width, clazz, args));
}
public Creator add(CharSequence title, Class extends Fragment> clazz) {
return add(FragmentPagerItem.of(title, clazz));
}
public Creator add(CharSequence title, Class extends Fragment> clazz, Bundle args) {
return add(FragmentPagerItem.of(title, clazz, args));
}
public Creator add(FragmentPagerItem item) {
items.add(item);
return this;
}
public FragmentPagerItems create() {
return items;
}
}
}
================================================
FILE: utils-v4/src/main/java/com/ogaclejapan/smarttablayout/utils/v4/FragmentStatePagerItemAdapter.java
================================================
/**
* Copyright (C) 2015 ogaclejapan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ogaclejapan.smarttablayout.utils.v4;
import android.view.ViewGroup;
import java.lang.ref.WeakReference;
import androidx.annotation.NonNull;
import androidx.collection.SparseArrayCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
public class FragmentStatePagerItemAdapter extends FragmentStatePagerAdapter {
private final FragmentPagerItems pages;
private final SparseArrayCompat> holder;
public FragmentStatePagerItemAdapter(FragmentManager fm, FragmentPagerItems pages) {
super(fm);
this.pages = pages;
this.holder = new SparseArrayCompat<>(pages.size());
}
@Override
public int getCount() {
return pages.size();
}
@Override
public Fragment getItem(int position) {
return getPagerItem(position).instantiate(pages.getContext(), position);
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
Object item = super.instantiateItem(container, position);
if (item instanceof Fragment) {
holder.put(position, new WeakReference((Fragment) item));
}
return item;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
holder.remove(position);
super.destroyItem(container, position, object);
}
@Override
public CharSequence getPageTitle(int position) {
return getPagerItem(position).getTitle();
}
@Override
public float getPageWidth(int position) {
return getPagerItem(position).getWidth();
}
public Fragment getPage(int position) {
final WeakReference weakRefItem = holder.get(position);
return (weakRefItem != null) ? weakRefItem.get() : null;
}
protected FragmentPagerItem getPagerItem(int position) {
return pages.get(position);
}
}