├── .circleci └── config.yml ├── .github └── ISSUE_TEMPLATE │ └── enablement-issue-template.md ├── .gitignore ├── README.md ├── SnapTablayout ├── .gitignore ├── bintrayv1.gradle ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── fridayof1995 │ │ └── tabanimation │ │ ├── SnapTabLayout.kt │ │ ├── StackTransformer.kt │ │ └── ViewUtil.kt │ └── res │ ├── drawable │ ├── ic_ring.xml │ ├── shadow_ring.xml │ └── tab_indicator.xml │ ├── layout │ └── snap_tab_view.xml │ └── values │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ └── strings.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── fridayof1995 │ │ └── sample │ │ └── ActivityTest.kt │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── fridayof1995 │ │ └── sample │ │ ├── EmptyFragment.kt │ │ ├── FragmentArgumentDelegate.kt │ │ ├── LauncherActivity.kt │ │ ├── MainActivity.kt │ │ ├── MainFragment.kt │ │ ├── Util.kt │ │ └── ViewPagerAdapter.kt │ └── res │ ├── drawable-v24 │ ├── ic_comment_white.xml │ ├── ic_launcher_foreground.xml │ ├── ic_ring.xml │ ├── ic_view_white.xml │ ├── ic_white_email.xml │ ├── ic_white_poll.xml │ └── ic_white_whatshot.xml │ ├── drawable │ ├── ic_add_button.xml │ ├── ic_launcher_background.xml │ ├── selected_background.xml │ ├── shape_round_rect.xml │ ├── tab_gradient_collapsed.xml │ └── tab_gradient_expanded.xml │ ├── layout │ ├── activity_launcher.xml │ ├── activity_main.xml │ └── fragment_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── array.xml │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── settings.gradle /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | working_directory: ~/code 5 | docker: 6 | # Android the primary container 7 | - image: circleci/android:api-29 8 | environment: 9 | JVM_OPTS: -Xmx3200m 10 | steps: 11 | - checkout 12 | - restore_cache: 13 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} 14 | - run: 15 | name: Download Dependencies 16 | command: ./gradlew androidDependencies 17 | - save_cache: 18 | paths: 19 | - ~/.gradle 20 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} 21 | - run: 22 | name: Run UnitTest 23 | command: ./gradlew testDebugUnitTest 24 | - run: 25 | name: Setup emulator 26 | command: sdkmanager "system-images;android-16;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-16;default;armeabi-v7a" 27 | - run: 28 | name: Launch emulator 29 | command: export LD_LIBRARY_PATH=${ANDROID_HOME}/emulator/lib64:${ANDROID_HOME}/emulator/lib64/qt/lib && emulator64-arm -avd test -noaudio -no-boot-anim -no-window -accel auto -verbose 30 | background: true 31 | - run: 32 | name: Wait emulator 33 | command: | 34 | # wait for it to have booted 35 | circle-android wait-for-boot 36 | # unlock the emulator screen 37 | sleep 30 38 | adb shell input keyevent 82 39 | - run: 40 | name: Run EspressoTests 41 | command: ./gradlew connectedDebugAndroidTest 42 | - store_artifacts: 43 | path: app/build/reports 44 | destination: reports 45 | - store_test_results: 46 | path: app/build/test-results -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enablement-issue-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enablement Issue Template 3 | about: Use this template for enablement items. 4 | title: "[Enablement]" 5 | labels: Medium Priority 6 | assignees: '' 7 | 8 | --- 9 | 10 | As an engineer, I want to blah blah, so that I can blah blah. 11 | 12 | ## Description 13 | 14 | ## Helpful Links 15 | 16 | ## Type 17 | - [ ] Kotlin Conversion 18 | - [ ] General refactoring. 19 | - [ ] Combining similar code. 20 | - [ ] Creating reusable code 21 | 22 | ## Checklist 23 | - [ ] Requires QA testing 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | .idea 4 | /local.properties 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | .DS_Store 9 | /build 10 | /captures 11 | .externalNativeBuild 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # SnapTabLayout [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Android%20library%20for%20fluid%20tablayout%20animation&url=https://github.com/nirukk52/SnapTabLayout&hashtags=android,design,animation,androiddev,developers) 3 | ### Show some :heart: and star the repo to support the project 4 | 6 | [![Android Arsenal]( https://img.shields.io/badge/Android%20Arsenal-SnapTabLayout-blue.svg?style=flat )]( https://android-arsenal.com/details/1/7243 ) 7 | [![Material Up](https://img.shields.io/badge/materialup-SnapTablayout-blue.svg)](https://www.uplabs.com/posts/snaptablayout) 8 | [![Android_Weekly](https://img.shields.io/badge/androidweekly-%23332-blue.svg)](http://androidweekly.net/issues/issue-332) 9 | 10 | 11 | This library is the implementation of TabLayout as seen on popular messaging app Snapchat Snapchat.com. 12 | 13 | It can be used to animate Three or Five tabs. 14 | 15 | ## 👏 Demo 16 | ![snaptablayout demo 1](https://user-images.githubusercontent.com/28961063/46969922-3c1dd680-d0d5-11e8-81b6-60cf032dcb92.gif) ![snaptablayout demo 2](https://user-images.githubusercontent.com/28961063/47112776-f26bf200-d274-11e8-9475-879e402a1aa9.gif) 17 | 18 | ## Contents 19 | - [Installation](#💻-Installation) 20 | - [How to use / Sample](#❔-Usage) 21 | - [Customization](#📐-Customization) 22 | - [Bugs and feedback](#bugs-and-feedback) 23 | 24 | ## 💻 Installation 25 | 26 | implementation 'com.fridayof1995.tabanimation:SnapTablayout:0.0.7' 27 | 28 | ## ❔ Usage 29 | ### Step 1 30 | ```xml 31 | 40 | ``` 41 | ### Step 2 42 | * ### Tab Number (Required) 43 | This parameter specifies the number of tabs required: 44 | ``` 45 | setNumOfTabs(SnapTabLayout.NumOfTabs.THREE); 46 | or 47 | setNumOfTabs(SnapTabLayout.NumOfTabs.FIVE); 48 | ``` 49 | 50 | * ### Setting the icons (Required) 51 | This parameter specifies the icons to be inflated: 52 | All of the below are ImageButtons so you can set image, background etc. 53 | ![tab_name_explain](https://user-images.githubusercontent.com/28961063/47033253-f4a95000-d191-11e8-91b6-c95fce21b439.png) 54 | ``` 55 | tabLayout.smallCenterButton.setImageResource() 56 | tabLayout.largeCenterButton.setImageResource() 57 | tabLayout.startButton.setImageResource() 58 | tabLayout.endButton.setImageResource() 59 | 60 | //Below required only when using five tabs. 61 | tabLayout.midStart.setImageResource() 62 | tabLayout.midEnd.setImageResource() 63 | ``` 64 | 65 | ## 📐 Customization 66 | 67 | * ### Background (Optional) 68 | This parameter sets the background in extended and collapsed tab mode: 69 | ``` 70 | tabLayout.setBackgroundCollapsed(R.drawable.tab_gradient_collapsed) // By default black fall gradient. 71 | tabLayout.setBackgroundExpanded(R.drawable.tab_gradient_expanded) 72 | ``` 73 | 74 | * ### Color Transition in Icons (Optional) 75 | This parameter sets the *ColorFilter* in extended and collapsed tab mode: 76 | ``` 77 | // When the layout moves from expanded to collapsed: Icons color transitions from white to black. 78 | tabLayout.setTransitionIconColors(R.color.white, R.color.black) 79 | 80 | ``` 81 | 82 | * ### Color Transition in ViewPager Background (Optional) 83 | This parameter gives a smooth color transition to the background of viewpager as seen in demo: 84 | ``` 85 | tabLayout.setVpTransitionBgColors(LeftSideColor: android.R.color.holo_purple 86 | , CenterColor: android.R.color.black 87 | , RightSideColor: android.R.color.holo_orange_dark) 88 | 89 | ``` 90 | ## Bugs and Feedback 91 | 92 | For bugs, feature requests, and discussion please use [GitHub Issues](https://github.com/nirukk52/SnapTabLayout/issues). 93 | 94 | ## 👨 Developed By 95 | 96 | ``` 97 | Niranjan Kurambhatti 98 | ``` 99 | 100 | ## 📃 License 101 | 102 | Copyright 2018 Niranjan Kurambhatti 103 | 104 | Licensed under the Apache License, Version 2.0 (the "License"); 105 | you may not use this file except in compliance with the License. 106 | You may obtain a copy of the License at 107 | 108 | http://www.apache.org/licenses/LICENSE-2.0 109 | 110 | Unless required by applicable law or agreed to in writing, software 111 | distributed under the License is distributed on an "AS IS" BASIS, 112 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 113 | See the License for the specific language governing permissions and 114 | limitations under the License. 115 | -------------------------------------------------------------------------------- /SnapTablayout/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /SnapTablayout/bintrayv1.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.github.dcendents.android-maven' 2 | apply plugin: 'com.jfrog.bintray' 3 | //apply from: 'keystore.gradle'//the file, containing the bintray API key 4 | //ext { 5 | // // This configuration will result in: 6 | // // compile '::' 7 | // groupId = 'com.fridayof1995.tabanimation' // package name of the project 8 | // artifactId = "SnapTabLayout" // module name of the library 9 | // libVersion = "0.0.1" 10 | //} 11 | version = libraryVersion 12 | group = publishedGroupId 13 | //take a look at https://github.com/bintray/gradle-bintray-plugin#buildgradle 14 | 15 | // Bintray 16 | Properties properties = new Properties() 17 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 18 | 19 | bintray { 20 | user = properties.getProperty("bintray.user") 21 | key = properties.getProperty("bintray.apikey") 22 | 23 | configurations = ['archives'] 24 | pkg { 25 | repo = bintrayRepo 26 | name = bintrayName 27 | desc = libraryDescription 28 | websiteUrl = siteUrl 29 | vcsUrl = gitUrl 30 | licenses = allLicenses 31 | publish = true 32 | publicDownloadNumbers = true 33 | version { 34 | desc = libraryDescription 35 | gpg { 36 | sign = true //Determines whether to GPG sign the files. The default is false 37 | passphrase = properties.getProperty("bintray.gpg.password") 38 | //Optional. The passphrase for GPG signing' 39 | } 40 | } 41 | } 42 | } 43 | 44 | install { 45 | repositories.mavenInstaller { 46 | pom.project { 47 | packaging 'aar' 48 | groupId publishedGroupId 49 | artifactId artifact 50 | version libraryVersion 51 | name artifact // pom.project.name must be same as bintray.pkg.name 52 | } 53 | } 54 | } 55 | 56 | //just copy paste the lines below 57 | if (project.hasProperty("kotlin")) { //Kotlin libraries 58 | task sourcesJar(type: Jar) { 59 | classifier = 'sources' 60 | from android.sourceSets.main.java.srcDirs 61 | } 62 | 63 | task javadoc(type: Javadoc, dependsOn: dokka) { 64 | 65 | } 66 | } else if (project.hasProperty("android")) { 67 | task sourcesJar(type: Jar) { 68 | classifier = 'sources' 69 | from android.sourceSets.main.java.srcDirs 70 | } 71 | 72 | task javadoc(type: Javadoc) { 73 | source = android.sourceSets.main.java.srcDirs 74 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 75 | } 76 | } else { // Java libraries 77 | task sourcesJar(type: Jar, dependsOn: classes) { 78 | classifier = 'sources' 79 | from sourceSets.main.allSource 80 | } 81 | } 82 | 83 | task javadocJar(type: Jar, dependsOn: javadoc) { 84 | classifier = 'javadoc' 85 | from javadoc.destinationDir 86 | // options.encoding = 'UTF-8' 87 | } 88 | 89 | artifacts { 90 | archives javadocJar 91 | archives sourcesJar 92 | } -------------------------------------------------------------------------------- /SnapTablayout/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | apply plugin: 'org.jetbrains.dokka-android' 5 | apply plugin: 'com.jfrog.bintray' 6 | apply plugin: 'maven-publish' 7 | 8 | ext { 9 | bintrayRepo = 'maven' 10 | bintrayName = 'SnapTablayout' 11 | 12 | publishedGroupId = 'com.fridayof1995.tabanimation' 13 | libraryName = 'SnapTablayout' 14 | artifact = 'SnapTablayout' 15 | 16 | libraryDescription = 'Android library for fluid tablayout animation.' 17 | 18 | siteUrl = 'https://github.com/nirukk52/SnapTabLayout' 19 | gitUrl = 'https://github.com/nirukk52/SnapTabLayout.git' 20 | 21 | libraryVersion = '0.0.3' 22 | 23 | developerId = 'nirukk52' 24 | developerName = 'Niranjan Kurambhatti' 25 | developerEmail = 'nirukk52@gmail.com' 26 | 27 | licenseName = 'The Apache Software License, Version 2.0' 28 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 29 | allLicenses = ["Apache-2.0"] 30 | } 31 | 32 | android { 33 | compileSdkVersion 29 34 | 35 | defaultConfig { 36 | minSdkVersion 16 37 | targetSdkVersion 29 38 | versionCode 1 39 | versionName "1.0" 40 | 41 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 42 | 43 | } 44 | 45 | buildTypes { 46 | release { 47 | minifyEnabled false 48 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 49 | } 50 | } 51 | 52 | } 53 | 54 | dokka { 55 | outputFormat = 'html' 56 | outputDirectory = "$buildDir/javadoc" 57 | } 58 | 59 | dependencies { 60 | testImplementation 'junit:junit:4.12' 61 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 62 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' 63 | 64 | compileOnly 'androidx.constraintlayout:constraintlayout:1.1.3' 65 | compileOnly 'androidx.legacy:legacy-support-v4:1.0.0' 66 | compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 67 | } 68 | 69 | if (project.rootProject.file('local.properties').exists()) 70 | { 71 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' 72 | apply from: 'bintrayv1.gradle' 73 | } 74 | -------------------------------------------------------------------------------- /SnapTablayout/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /SnapTablayout/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /SnapTablayout/src/main/java/com/fridayof1995/tabanimation/SnapTabLayout.kt: -------------------------------------------------------------------------------- 1 | package com.fridayof1995.tabanimation 2 | 3 | import android.animation.ArgbEvaluator 4 | import android.animation.FloatEvaluator 5 | import android.content.Context 6 | import androidx.annotation.ColorInt 7 | import androidx.viewpager.widget.ViewPager 8 | import android.util.AttributeSet 9 | import android.util.Log 10 | import android.util.TypedValue 11 | import android.view.LayoutInflater 12 | import android.view.View 13 | import android.view.ViewTreeObserver 14 | import android.widget.FrameLayout 15 | import android.widget.ImageButton 16 | import kotlinx.android.synthetic.main.snap_tab_view.view.* 17 | 18 | 19 | /** 20 | * Created by Depression on 11-08-2018. 21 | */ 22 | class SnapTabLayout 23 | @JvmOverloads 24 | constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) 25 | : FrameLayout(context, attrs, defStyleAttr), androidx.viewpager.widget.ViewPager.OnPageChangeListener { 26 | 27 | 28 | val mArgbEvaluator: ArgbEvaluator = ArgbEvaluator() 29 | var mCenterColor: Int = 0 30 | var mSideColor: Int = 0 31 | var mCenterScale: Float = 0.80f 32 | 33 | private var defaultOffsetFromCenter: Int = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP 34 | , 80f, resources.displayMetrics).toInt() 35 | 36 | private var expandedAt: Int = 0 37 | var numOfTabs: NumOfTabs = NumOfTabs.FIVE 38 | set(value) { 39 | field = value 40 | invalidate() 41 | requestLayout() 42 | } 43 | 44 | private var mEndViewsTranslationX: Int? = null 45 | private var mMidViewsTranslationX: Int? = null 46 | private var mCenterTranslationY: Int? = null 47 | 48 | lateinit var smallCenterButton: ImageButton 49 | lateinit var largeCenterButton: ImageButton 50 | lateinit var startButton: ImageButton 51 | lateinit var endButton: ImageButton 52 | lateinit var midStart: ImageButton 53 | lateinit var midEnd: ImageButton 54 | lateinit var vpager: androidx.viewpager.widget.ViewPager 55 | 56 | var mBgRight: Int = 0 57 | var mBgLeft: Int = 0 58 | var mBgCenter: Int = 0 59 | 60 | init { 61 | init() 62 | val a = context.theme.obtainStyledAttributes( 63 | attrs, 64 | R.styleable.VPAnimatedTabLayout, 65 | 0, 0) 66 | 67 | try { 68 | val anumOfTabs: Int = a.getInt(R.styleable.VPAnimatedTabLayout_numOfTabs 69 | , 3) 70 | if (anumOfTabs == 3) { 71 | numOfTabs = NumOfTabs.THREE 72 | mid_start.visibility = View.GONE 73 | mid_end.visibility = View.GONE 74 | expandedAt = 1 75 | } else { 76 | numOfTabs = NumOfTabs.FIVE 77 | mid_start.visibility = View.VISIBLE 78 | mid_end.visibility = View.VISIBLE 79 | expandedAt = 2 80 | } 81 | } finally { 82 | a.recycle() 83 | } 84 | } 85 | 86 | private fun init() { 87 | LayoutInflater.from(context).inflate(R.layout.snap_tab_view, this, true) 88 | smallCenterButton = findViewById(R.id.bottom_center) 89 | largeCenterButton = findViewById(R.id.center) 90 | startButton = findViewById(R.id.start) 91 | endButton = findViewById(R.id.end) 92 | midStart = findViewById(R.id.mid_start) 93 | midEnd = findViewById(R.id.mid_end) 94 | } 95 | 96 | override fun onPageScrollStateChanged(state: Int) { 97 | 98 | //if(mCenterMovePosition = state) 99 | } 100 | 101 | override fun onPageSelected(position: Int) { 102 | Log.e("onPageSelected", "position $position") 103 | } 104 | 105 | override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { 106 | if (position == expandedAt - 1) expandTabs(positionOffset, position) // expand 107 | else if (position == expandedAt) collapseTabs(positionOffset, position) // collapse 108 | 109 | if (numOfTabs == NumOfTabs.THREE) { 110 | moveIndicatorWithThreeTabs(positionOffset, position) 111 | } else { 112 | moveIndicatorWithFiveTabs(position) 113 | } 114 | // changeSelectedBackground(position) 115 | } 116 | 117 | private fun moveIndicatorWithThreeTabs(positionOffset: Float, position: Int) { 118 | if (position == expandedAt - 1) moveIndicatorToEnd(positionOffset) // expand 119 | else if (position == expandedAt) moveIndicatorToStart(positionOffset) // collapse 120 | } 121 | 122 | private fun moveIndicatorWithFiveTabs(position: Int) { 123 | 124 | if (position < expandedAt) when (position) { 125 | 0 -> { 126 | mIndicator.x = 127 | start.x + (start.width / 8) 128 | } 129 | 1 -> { 130 | mIndicator.x = 131 | mid_start.x + (mid_start.width / 8) 132 | } 133 | } 134 | else if (position > expandedAt) when (position) { 135 | 3 -> { 136 | mIndicator.x = 137 | mid_end.x + (mid_end.width / 8) 138 | } 139 | 4 -> { 140 | mIndicator.x = 141 | end.x + (end.width / 8) 142 | 143 | } 144 | } 145 | else if (position == expandedAt) { 146 | mIndicator.x = 147 | mid_end.x + (mid_end.width / 8) 148 | } 149 | } 150 | 151 | private fun moveIndicatorToStart(positionOffset: Float) { 152 | mIndicator.translationX = 153 | (positionOffset * (start.x + start.width - convertDpToPixel(6f, context))) 154 | } 155 | 156 | private fun moveIndicatorToEnd(positionOffset: Float) { 157 | mIndicator.translationX = 158 | ((positionOffset - 1) * (start.x + start.width - convertDpToPixel(8f, context))) 159 | } 160 | 161 | private fun collapseTabs(positionOffset: Float, position: Int) { 162 | // collapses at position == 1 163 | setTabChangingColor(positionOffset, position) 164 | //changeSelectedBackground(position,positionOffset) 165 | } 166 | 167 | private fun expandTabs(positionOffset: Float, position: Int) { 168 | // expands at position == 0 169 | setTabChangingColor(1 - positionOffset, position) 170 | //changeSelectedBackground(position,1 - positionOffset) 171 | } 172 | 173 | /** 174 | * Hide extra buttons if using three tabs. 175 | * Sets onClickListener on each view. 176 | * Sets defaultOffset from center while collapsing. 177 | */ 178 | fun setupWithViewPager(viewPager: androidx.viewpager.widget.ViewPager) { 179 | vpager = viewPager 180 | if (numOfTabs == NumOfTabs.THREE) { 181 | mid_start.visibility = View.GONE 182 | mid_end.visibility = View.GONE 183 | expandedAt = 1 184 | } else { 185 | mid_start.visibility = View.VISIBLE 186 | mid_end.visibility = View.VISIBLE 187 | expandedAt = 2 188 | } 189 | viewPager.addOnPageChangeListener(this) 190 | viewPager.setPageTransformer(true, StackTransformer()) 191 | smallCenterButton.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { 192 | override fun onGlobalLayout() { 193 | defaultOffsetFromCenter = (center.width / 2) 194 | if (numOfTabs == NumOfTabs.FIVE) { 195 | if (expandedAt < 2) { 196 | mMidViewsTranslationX = (center.x - mid_start.x + defaultOffsetFromCenter - 40).toInt() 197 | mEndViewsTranslationX = (center.x - start.x - mid_start.width + defaultOffsetFromCenter - 40).toInt() 198 | } else { 199 | mMidViewsTranslationX = (center.x - mid_start.x + defaultOffsetFromCenter - 40 * 6).toInt() 200 | mEndViewsTranslationX = (center.x - start.x - mid_start.width + defaultOffsetFromCenter - 40 * 6).toInt() 201 | } 202 | } else { 203 | mMidViewsTranslationX = (center.x - mid_start.x - defaultOffsetFromCenter + 40).toInt() 204 | mEndViewsTranslationX = (center.x - start.x - mid_start.width - defaultOffsetFromCenter + 40).toInt() 205 | } 206 | mCenterTranslationY = height - smallCenterButton.bottom 207 | 208 | smallCenterButton.viewTreeObserver.removeOnGlobalLayoutListener(this) 209 | viewPager.currentItem = expandedAt 210 | } 211 | }) 212 | 213 | center.setOnClickListener { 214 | if (viewPager.currentItem != expandedAt) { 215 | viewPager.currentItem = expandedAt 216 | } 217 | } 218 | start.setOnClickListener { 219 | if (viewPager.currentItem != 0) { 220 | viewPager.currentItem = 0 221 | } 222 | } 223 | end.setOnClickListener { 224 | if (viewPager.currentItem != numOfTabs.value - 1) { 225 | viewPager.currentItem = numOfTabs.value - 1 226 | } 227 | if (numOfTabs == NumOfTabs.FIVE) { 228 | mIndicator.x = 229 | end.x + (end.width / 8) 230 | } 231 | } 232 | mid_start.setOnClickListener { 233 | if (viewPager.currentItem != expandedAt - 1) { 234 | viewPager.currentItem = expandedAt - 1 235 | } 236 | } 237 | mid_end.setOnClickListener { 238 | if (viewPager.currentItem != expandedAt + 1) { 239 | viewPager.currentItem = expandedAt + 1 240 | } 241 | } 242 | } 243 | 244 | private fun moveViews(fractionFromCenter: Float) { 245 | 246 | val endTranslation: Float = mEndViewsTranslationX?.times(fractionFromCenter) 247 | ?: defaultOffsetFromCenter.toFloat() 248 | 249 | val midTranslation: Float = mMidViewsTranslationX?.times(fractionFromCenter) 250 | ?: defaultOffsetFromCenter.toFloat() 251 | 252 | start.translationX = endTranslation 253 | end.translationX = -endTranslation 254 | mid_start.translationX = midTranslation 255 | mid_end.translationX = -midTranslation 256 | } 257 | 258 | private fun moveAndScaleCenter(fractionFromCenter: Float) { 259 | 260 | val centerTranslationY: Float? = mCenterTranslationY?.times(fractionFromCenter) 261 | 262 | if (centerTranslationY != null) { 263 | center.translationY = centerTranslationY * 4f 264 | smallCenterButton.translationY = centerTranslationY * 4f 265 | } 266 | 267 | val centerScale: Float = 1 - fractionFromCenter 268 | val mScaleEvaluator = FloatEvaluator() 269 | val scale = mScaleEvaluator.evaluate(fractionFromCenter, 1, mCenterScale) 270 | center.scaleX = scale 271 | center.scaleY = scale 272 | 273 | smallCenterButton.alpha = centerScale 274 | smallCenterButton.scaleY = centerScale 275 | smallCenterButton.scaleX = centerScale 276 | } 277 | 278 | /** 279 | * Transitions icon colors. 280 | * Transitions ViewPager background color. 281 | * Transitions SnapTablayout background. 282 | */ 283 | private fun setTabChangingColor(fractionFromCenter: Float, position: Int) { 284 | 285 | val color = mArgbEvaluator.evaluate(fractionFromCenter, mCenterColor, mSideColor) as Int 286 | center.setColorFilter(color) 287 | bottom_center.setColorFilter(color) 288 | start.setColorFilter(color) 289 | end.setColorFilter(color) 290 | mid_end.setColorFilter(color) 291 | mid_start.setColorFilter(color) 292 | 293 | if (position < expandedAt) { 294 | 295 | val colorToLeft = mArgbEvaluator.evaluate(fractionFromCenter, mBgCenter, mBgLeft) as Int 296 | vpager.setBackgroundColor(colorToLeft) 297 | 298 | } else if (position == expandedAt) { 299 | 300 | val colorToRight = mArgbEvaluator.evaluate(fractionFromCenter, mBgCenter, mBgRight) as Int 301 | vpager.setBackgroundColor(colorToRight) 302 | 303 | } 304 | mIndicator.alpha = fractionFromCenter 305 | mIndicator.scaleX = fractionFromCenter 306 | 307 | transitionBackground.alpha = fractionFromCenter 308 | transitionBackground2.alpha = 1 - fractionFromCenter 309 | 310 | moveViews(fractionFromCenter) 311 | moveAndScaleCenter(fractionFromCenter) 312 | 313 | } 314 | 315 | // Public Methods 316 | fun setBackgroundCollapsed(background: Int) { 317 | transitionBackground.setBackgroundResource(background) 318 | } 319 | 320 | fun setBackgroundExpanded(background: Int) { 321 | transitionBackground2.setBackgroundResource(background) 322 | } 323 | 324 | fun setVpTransitionBgColors(@ColorInt leftSideColor: Int, @ColorInt centerColor: Int, @ColorInt rightSideColor: Int) { 325 | mBgLeft = leftSideColor 326 | mBgCenter = centerColor 327 | mBgRight = rightSideColor 328 | } 329 | 330 | fun setTransitionIconColors(@ColorInt centerColor: Int, @ColorInt sideColor: Int) { 331 | mCenterColor = centerColor 332 | mSideColor = sideColor 333 | } 334 | 335 | fun setIndicatorColor(@ColorInt colorInt: Int) { 336 | mIndicator.setColorFilter(colorInt) 337 | } 338 | 339 | fun setCenterScale(scaleDownCenterIcon: Float) { 340 | mCenterScale = scaleDownCenterIcon 341 | } 342 | 343 | 344 | //TODO Change background of tab on selection. 345 | private fun changeSelectedBackground(position: Int, fractionFromCenter: Float) { 346 | val alphaValue = mArgbEvaluator.evaluate(fractionFromCenter, 0, 255) as Int 347 | // Log.e("background", "position $position fractionFromCenter $fractionFromCenter alphaValue$alphaValue ") 348 | when (position) { 349 | 0 -> { 350 | start.background.alpha = alphaValue 351 | mid_start.background.alpha = 0 352 | mid_end.background.alpha = 0 353 | end.background.alpha = 0 354 | } 355 | 1 -> { 356 | start.background.alpha = alphaValue 357 | mid_start.background.alpha = alphaValue 358 | mid_end.background.alpha = 0 359 | end.background.alpha = 0 360 | } 361 | 2 -> { 362 | start.background.alpha = 0 363 | mid_start.background.alpha = 0 364 | mid_end.background.alpha = 0 365 | end.background.alpha = 0 366 | } 367 | 3 -> { 368 | start.background.alpha = 0 369 | mid_start.background.alpha = 0 370 | mid_end.background.alpha = alphaValue 371 | end.background.alpha = 0 372 | } 373 | 4 -> { 374 | start.background.alpha = 0 375 | mid_start.background.alpha = 0 376 | mid_end.background.alpha = 0 377 | end.background.alpha = alphaValue 378 | } 379 | } 380 | } 381 | 382 | enum class NumOfTabs(val value: Int) { 383 | THREE(3), 384 | FIVE(5) 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /SnapTablayout/src/main/java/com/fridayof1995/tabanimation/StackTransformer.kt: -------------------------------------------------------------------------------- 1 | package com.fridayof1995.tabanimation 2 | 3 | import androidx.viewpager.widget.ViewPager 4 | import android.view.View 5 | 6 | 7 | /** 8 | * Created by Depression on 12-08-2018. 9 | */ 10 | class StackTransformer : androidx.viewpager.widget.ViewPager.PageTransformer { 11 | override fun transformPage(view: View, position: Float) { 12 | view.pivotX = if (position < 0) (view.width).toFloat() else 1f 13 | view.scaleX = if (position < 0) 1f else 1f 14 | } 15 | } -------------------------------------------------------------------------------- /SnapTablayout/src/main/java/com/fridayof1995/tabanimation/ViewUtil.kt: -------------------------------------------------------------------------------- 1 | package com.fridayof1995.tabanimation 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.util.DisplayMetrics 6 | import android.view.View 7 | import android.view.inputmethod.InputMethodManager 8 | 9 | /** 10 | * Created by Depression on 12-08-2018. 11 | */ 12 | 13 | 14 | fun convertDpToPixel(dp: Float, context: Context): Float { 15 | val resources = context.resources 16 | val metrics = resources.displayMetrics 17 | return dp * (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT) 18 | } 19 | 20 | fun convertPixelsToDp(px: Float, context: Context): Float { 21 | val resources = context.resources 22 | val metrics = resources.displayMetrics 23 | return px / (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT) 24 | } -------------------------------------------------------------------------------- /SnapTablayout/src/main/res/drawable/ic_ring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SnapTablayout/src/main/res/drawable/shadow_ring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /SnapTablayout/src/main/res/drawable/tab_indicator.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /SnapTablayout/src/main/res/layout/snap_tab_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 20 | 21 | 31 | 32 | 42 | 43 | 53 | 54 | 64 | 65 | 76 | 77 | 88 | 89 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /SnapTablayout/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /SnapTablayout/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | #b5505050 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SnapTablayout/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5dp 4 | 8dp 5 | -------------------------------------------------------------------------------- /SnapTablayout/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | TabAnimation 3 | 4 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.3.61' 5 | ext.dokka_version = '0.9.17' 6 | repositories { 7 | google() 8 | jcenter() 9 | } 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:3.5.3' 12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 13 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' 14 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' 15 | classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:$dokka_version" 16 | // NOTE: Do not place your application dependencies here; they belong 17 | // in the individual module build.gradle files 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | google() 24 | jcenter() 25 | } 26 | tasks.withType(Javadoc).all { enabled = false } 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | android.enableJetifier=true 10 | android.useAndroidX=true 11 | org.gradle.jvmargs=-Xmx1536m 12 | # When configured, Gradle will run in incubating parallel mode. 13 | # This option should only be used with decoupled projects. More details, visit 14 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 15 | # org.gradle.parallel=true 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nirukk52/SnapTabLayout/6ed13bb61909ef9231832e799cb2fd64aba6033f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Jan 25 16:14:04 CST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 29 9 | defaultConfig { 10 | applicationId "com.fridayof1995.sample" 11 | minSdkVersion 16 12 | targetSdkVersion 29 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | 24 | lintOptions { 25 | abortOnError false 26 | } 27 | } 28 | 29 | dependencies { 30 | implementation fileTree(dir: 'libs', include: ['*.jar']) 31 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 32 | implementation 'androidx.appcompat:appcompat:1.0.0' 33 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 34 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 35 | implementation 'com.google.android.material:material:1.0.0' 36 | implementation project(':SnapTablayout') 37 | // implementation 'com.fridayof1995.tabanimation:SnapTablayout:0.0.1' 38 | 39 | testImplementation 'junit:junit:4.12' 40 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 41 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' 42 | } 43 | -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /sample/src/androidTest/java/com/fridayof1995/sample/ActivityTest.kt: -------------------------------------------------------------------------------- 1 | package com.fridayof1995.sample 2 | 3 | import android.widget.TextView 4 | import androidx.test.core.app.ActivityScenario 5 | import androidx.test.espresso.Espresso 6 | import androidx.test.espresso.Espresso.onData 7 | import androidx.test.espresso.Espresso.onView 8 | import androidx.test.espresso.action.ViewActions 9 | import androidx.test.espresso.assertion.ViewAssertions 10 | import androidx.test.espresso.matcher.ViewMatchers 11 | import androidx.test.espresso.matcher.ViewMatchers.withId 12 | import androidx.test.espresso.matcher.ViewMatchers.withText 13 | import androidx.test.ext.junit.runners.AndroidJUnit4 14 | import androidx.test.filters.SmallTest 15 | import org.hamcrest.CoreMatchers 16 | import org.hamcrest.CoreMatchers.* 17 | import org.hamcrest.core.Is 18 | import org.hamcrest.core.Is.`is` 19 | import org.junit.Before 20 | import org.junit.Test 21 | import org.junit.runner.RunWith 22 | 23 | 24 | @RunWith(AndroidJUnit4::class) 25 | @SmallTest 26 | class ActivityTest { 27 | 28 | @Before 29 | fun launchActivity() { 30 | ActivityScenario.launch(LauncherActivity::class.java) 31 | } 32 | 33 | @Test 34 | fun performFiveTabTest() { 35 | onView(allOf(instanceOf(TextView::class.java), 36 | ViewMatchers.withParent(ViewMatchers.withResourceName("action_bar")))) 37 | .check(ViewAssertions.matches(withText(R.string.app_name))) 38 | 39 | onView(withId(R.id.spinner_num_tabs)).perform(ViewActions.click()) 40 | onData(allOf(`is`(instanceOf(Int::class.java)), `is`(5))).perform(ViewActions.click()) 41 | onView(withId(R.id.btGo)).perform(ViewActions.click()) 42 | 43 | onView(withId(R.id.viewPager)).perform(ViewActions.swipeLeft()) 44 | onView(withText("3")).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 45 | onView(withId(R.id.viewPager)).perform(ViewActions.swipeLeft()) 46 | onView(withText("4")).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 47 | onView(withId(R.id.viewPager)).perform(ViewActions.swipeRight()) 48 | onView(withText("3")).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 49 | onView(withId(R.id.viewPager)).perform(ViewActions.swipeRight()) 50 | onView(withText("2")).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 51 | onView(withId(R.id.viewPager)).perform(ViewActions.swipeRight()) 52 | onView(withText("1")).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 53 | onView(withId(R.id.viewPager)).perform(ViewActions.swipeRight()) 54 | onView(withText("0")).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 55 | 56 | } 57 | 58 | @Test 59 | fun performThreeTabTest() { 60 | 61 | onView(allOf(instanceOf(TextView::class.java), 62 | ViewMatchers.withParent(ViewMatchers.withResourceName("action_bar")))) 63 | .check(ViewAssertions.matches(withText(R.string.app_name))) 64 | 65 | onView(withId(R.id.spinner_num_tabs)).perform(ViewActions.click()) 66 | onData(allOf(`is`(instanceOf(Int::class.java)), `is`(3))).perform(ViewActions.click()) 67 | onView(withId(R.id.btGo)).perform(ViewActions.click()) 68 | 69 | onView(withId(R.id.viewPager)).perform(ViewActions.swipeLeft()) 70 | onView(withText("2")).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 71 | onView(withId(R.id.viewPager)).perform(ViewActions.swipeRight()) 72 | onView(withText("1")).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 73 | onView(withId(R.id.viewPager)).perform(ViewActions.swipeRight()) 74 | onView(withText("0")).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 75 | } 76 | 77 | 78 | } -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fridayof1995/sample/EmptyFragment.kt: -------------------------------------------------------------------------------- 1 | package com.fridayof1995.sample 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.Button 9 | 10 | 11 | class EmptyFragment : androidx.fragment.app.Fragment() { 12 | 13 | 14 | private var fragmentNumber by FragmentArgumentDelegate() 15 | 16 | companion object { 17 | fun newInstance(fragmentNumber: Number) = EmptyFragment().apply { 18 | this.fragmentNumber = fragmentNumber 19 | } 20 | } 21 | 22 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 23 | 24 | val view: View = inflater.inflate(R.layout.fragment_main, container, false) 25 | 26 | val btFragmentNumber: Button = view.findViewById(R.id.btFragmentNumber) as Button 27 | btFragmentNumber.text = fragmentNumber.toString() 28 | 29 | return view 30 | } 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fridayof1995/sample/FragmentArgumentDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.fridayof1995.sample 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | 6 | /** 7 | * Created by Depression on 14-08-2018. 8 | */ 9 | class FragmentArgumentDelegate : kotlin.properties.ReadWriteProperty { 10 | 11 | var value: T? = null 12 | 13 | override operator fun getValue(thisRef: androidx.fragment.app.Fragment, property: kotlin.reflect.KProperty<*>): T { 14 | if (value == null) { 15 | val args = thisRef.arguments ?: throw IllegalStateException("Cannot read property ${property.name} if no arguments have been set") 16 | @Suppress("UNCHECKED_CAST") 17 | value = args.get(property.name) as T 18 | } 19 | return value ?: throw IllegalStateException("Property ${property.name} could not be read") 20 | } 21 | 22 | override operator fun setValue(thisRef: androidx.fragment.app.Fragment, property: kotlin.reflect.KProperty<*>, value: T) { 23 | if (thisRef.arguments == null) thisRef.arguments = android.os.Bundle() 24 | 25 | val args = thisRef.arguments 26 | val key = property.name 27 | 28 | when (value) { 29 | is String -> args?.putString(key, value) 30 | is Int -> args?.putInt(key, value) 31 | is Short -> args?.putShort(key, value) 32 | is Long -> args?.putLong(key, value) 33 | is Byte -> args?.putByte(key, value) 34 | is ByteArray -> args?.putByteArray(key, value) 35 | is Char -> args?.putChar(key, value) 36 | is CharArray -> args?.putCharArray(key, value) 37 | is CharSequence -> args?.putCharSequence(key, value) 38 | is Float -> args?.putFloat(key, value) 39 | is Bundle -> args?.putBundle(key, value) 40 | // is Binder -> BundleCompat.putBinder(args, key, value) 41 | is android.os.Parcelable -> args?.putParcelable(key, value) 42 | is java.io.Serializable -> args?.putSerializable(key, value) 43 | else -> throw IllegalStateException("Type ${value.javaClass.canonicalName} of property ${property.name} is not supported") 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/fridayof1995/sample/LauncherActivity.kt: -------------------------------------------------------------------------------- 1 | package com.fridayof1995.sample 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import androidx.appcompat.app.AppCompatActivity 6 | import android.widget.ArrayAdapter 7 | import com.fridayof1995.tabanimation.SnapTabLayout 8 | import kotlinx.android.synthetic.main.activity_launcher.* 9 | 10 | 11 | class LauncherActivity : AppCompatActivity() { 12 | 13 | override fun onCreate(savedInstanceState: Bundle?) { 14 | super.onCreate(savedInstanceState) 15 | setContentView(R.layout.activity_launcher) 16 | 17 | 18 | val numOfTabs = ArrayList()//Creating an empty arraylist 19 | numOfTabs.add(SnapTabLayout.NumOfTabs.THREE.value)//Adding object in arraylist 20 | numOfTabs.add(SnapTabLayout.NumOfTabs.FIVE.value) 21 | val adapter = ArrayAdapter(this, 22 | android.R.layout.simple_spinner_item, numOfTabs) 23 | spinner_num_tabs.adapter = adapter 24 | 25 | btGo.setOnClickListener { 26 | val intent = Intent(this@LauncherActivity, MainActivity::class.java) 27 | intent.putExtra("numOfTabs", spinner_num_tabs.selectedItem.toString().toInt()) 28 | startActivity(intent) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fridayof1995/sample/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.fridayof1995.sample 2 | 3 | import android.animation.ArgbEvaluator 4 | import android.os.Bundle 5 | import androidx.core.content.ContextCompat 6 | import androidx.appcompat.app.AppCompatActivity 7 | import com.fridayof1995.tabanimation.SnapTabLayout 8 | import kotlinx.android.synthetic.main.activity_main.* 9 | 10 | 11 | class MainActivity : AppCompatActivity() { 12 | 13 | val viewPagerAdapter: ViewPagerAdapter = ViewPagerAdapter(supportFragmentManager); 14 | 15 | val mArgbEvaluator: ArgbEvaluator = ArgbEvaluator() 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | setContentView(R.layout.activity_main) 20 | 21 | viewPagerAdapter.addFragment(MainFragment.newInstance(0)) 22 | viewPagerAdapter.addFragment(MainFragment.newInstance(1)) 23 | viewPagerAdapter.addFragment(MainFragment.newInstance(2)) 24 | viewPager.adapter = viewPagerAdapter 25 | 26 | val numTab = intent.getIntExtra("numOfTabs", 3) 27 | 28 | /** 29 | * Library methods and example usage. 30 | */ 31 | tabLayout.numOfTabs = if (numTab.equals(SnapTabLayout.NumOfTabs.THREE.value)) { 32 | SnapTabLayout.NumOfTabs.THREE 33 | } else { 34 | viewPagerAdapter.addFragment(MainFragment.newInstance(3)) 35 | viewPagerAdapter.addFragment(MainFragment.newInstance(4)) 36 | SnapTabLayout.NumOfTabs.FIVE 37 | } 38 | 39 | tabLayout.setBackgroundCollapsed(R.drawable.tab_gradient_collapsed) 40 | tabLayout.setBackgroundExpanded(R.drawable.tab_gradient_expanded) 41 | 42 | tabLayout.smallCenterButton.setImageResource(R.drawable.ic_view_white) 43 | tabLayout.largeCenterButton.setImageResource(R.drawable.shadow_ring) 44 | tabLayout.startButton.setImageResource(R.drawable.ic_comment_white) 45 | tabLayout.endButton.setImageResource(R.drawable.ic_white_whatshot) 46 | tabLayout.midStart.setImageResource(R.drawable.ic_white_poll) 47 | tabLayout.midEnd.setImageResource(R.drawable.ic_white_email) 48 | 49 | // tabLayout.setTransitionIconColors(ContextCompat.getColor(this@MainActivity, android.R.color.white) 50 | // , ContextCompat.getColor(this@MainActivity, R.color.colorGrey)) 51 | 52 | tabLayout.setCenterScale(0.85f) 53 | 54 | tabLayout.setIndicatorColor(ContextCompat.getColor(this@MainActivity, R.color.colorGrey)) 55 | 56 | tabLayout.setVpTransitionBgColors(ContextCompat.getColor(this@MainActivity, android.R.color.holo_purple) 57 | , ContextCompat.getColor(this@MainActivity, android.R.color.black) 58 | , ContextCompat.getColor(this@MainActivity, android.R.color.holo_orange_dark)) 59 | 60 | tabLayout.smallCenterButton.setOnClickListener { 61 | toast("Bottom Center Clicked. Show some bottom sheet.") 62 | } 63 | tabLayout.setupWithViewPager(viewPager) 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fridayof1995/sample/MainFragment.kt: -------------------------------------------------------------------------------- 1 | package com.fridayof1995.sample 2 | 3 | 4 | import android.os.Bundle 5 | import androidx.fragment.app.Fragment 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.widget.Button 10 | import android.widget.RelativeLayout 11 | import com.fridayof1995.tabanimation.SnapTabLayout 12 | 13 | 14 | class MainFragment : androidx.fragment.app.Fragment() { 15 | 16 | 17 | private var fragmentNumber by FragmentArgumentDelegate() 18 | 19 | companion object { 20 | fun newInstance(fragmentNumber: Number) = MainFragment().apply { 21 | this.fragmentNumber = fragmentNumber 22 | } 23 | } 24 | 25 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 26 | 27 | val view: View = inflater.inflate(R.layout.fragment_main, container, false) 28 | 29 | val btFragmentNumber: Button = view.findViewById(R.id.btFragmentNumber) as Button 30 | val backdropCard: RelativeLayout = view.findViewById(R.id.backdropCard) as RelativeLayout 31 | 32 | val numTab = activity?.intent?.getIntExtra("numOfTabs", 3) 33 | if (numTab!!.equals(SnapTabLayout.NumOfTabs.FIVE.value)) { 34 | if (fragmentNumber == 2) { 35 | backdropCard.visibility = View.INVISIBLE 36 | } 37 | } else if (numTab.equals(SnapTabLayout.NumOfTabs.THREE.value)) { 38 | if (fragmentNumber == 1) { 39 | backdropCard.visibility = View.INVISIBLE 40 | } 41 | } 42 | 43 | btFragmentNumber.text = fragmentNumber.toString() 44 | btFragmentNumber.setOnClickListener { 45 | context?.toast("Fragment number $fragmentNumber") 46 | } 47 | return view 48 | } 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fridayof1995/sample/Util.kt: -------------------------------------------------------------------------------- 1 | package com.fridayof1995.sample 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.view.View 6 | import android.view.inputmethod.InputMethodManager 7 | import android.widget.Toast 8 | 9 | /** 10 | * Created by Depression on 12-08-2018. 11 | */ 12 | fun Context.toast(message: CharSequence) = 13 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show() 14 | 15 | fun hideKeyboardFrom(context: Context?, view: View) { 16 | val imm = context?.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager 17 | imm.hideSoftInputFromWindow(view.getWindowToken(), 0) 18 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/fridayof1995/sample/ViewPagerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.fridayof1995.sample 2 | 3 | import androidx.fragment.app.Fragment 4 | import androidx.fragment.app.FragmentManager 5 | import androidx.fragment.app.FragmentPagerAdapter 6 | 7 | import java.util.* 8 | 9 | /** 10 | * Created by Depression on 10-08-2018. 11 | */ 12 | class ViewPagerAdapter(fm: androidx.fragment.app.FragmentManager) : androidx.fragment.app.FragmentPagerAdapter(fm) { 13 | 14 | private val mFragmentList = ArrayList() 15 | 16 | override fun getItem(position: Int): androidx.fragment.app.Fragment { 17 | if(position == 1){ 18 | 19 | } 20 | return mFragmentList[position] 21 | } 22 | 23 | override fun getCount(): Int { 24 | return mFragmentList.size 25 | } 26 | 27 | fun addFragment(fragment: androidx.fragment.app.Fragment) { 28 | mFragmentList.add(fragment) 29 | notifyDataSetChanged() 30 | } 31 | 32 | fun addFragmentAt(fragment: androidx.fragment.app.Fragment, position: Int) { 33 | mFragmentList.add(position, fragment) 34 | notifyDataSetChanged() 35 | } 36 | 37 | companion object { 38 | private val TAG = "ViewPagerAdapter" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-v24/ic_comment_white.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-v24/ic_ring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-v24/ic_view_white.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-v24/ic_white_email.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-v24/ic_white_poll.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-v24/ic_white_whatshot.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_add_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/selected_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/shape_round_rect.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/tab_gradient_collapsed.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/tab_gradient_expanded.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | 23 | 24 |