├── demo ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── drawable │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ ├── tab_contact_normal.png │ │ │ │ ├── bg_navigation.xml │ │ │ │ ├── bar_my.xml │ │ │ │ ├── bar_news.xml │ │ │ │ ├── bar_invite.xml │ │ │ │ ├── bar_manage.xml │ │ │ │ ├── bar_constact.xml │ │ │ │ ├── item_check.xml │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── drawable-hdpi │ │ │ │ ├── icon_add.png │ │ │ │ ├── tab_my_normal.png │ │ │ │ ├── tab_my_selected.png │ │ │ │ ├── tab_news_normal.png │ │ │ │ ├── tab_contact_normal.png │ │ │ │ ├── tab_invite_normal.png │ │ │ │ ├── tab_manage_normal.png │ │ │ │ ├── tab_news_selected.png │ │ │ │ ├── tab_contact_selected.png │ │ │ │ ├── tab_invite_selected.png │ │ │ │ └── tab_manage_selected.png │ │ │ ├── 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 │ │ │ │ ├── colors.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── layout │ │ │ │ ├── fragment_home.xml │ │ │ │ ├── activity_main.xml │ │ │ │ └── activity_main_viewpager.xml │ │ │ ├── menu │ │ │ │ └── navigation_menu.xml │ │ │ └── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ ├── java │ │ │ └── cn │ │ │ │ └── onestravel │ │ │ │ └── one │ │ │ │ └── navigation │ │ │ │ └── demo │ │ │ │ ├── BaseFragment.java │ │ │ │ ├── ThirdFragment.kt │ │ │ │ ├── SecondFragment.kt │ │ │ │ ├── FiveFragment.kt │ │ │ │ ├── FourFragment.kt │ │ │ │ ├── FirstFragment.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ └── MainViewPagerActivity.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── cn │ │ │ └── onestravel │ │ │ └── bottomview │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── cn │ │ └── onestravel │ │ └── bottomview │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── .gitattributes ├── OneBottomNavigationBar ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── styles.xml │ │ │ │ └── attrs.xml │ │ │ └── drawable │ │ │ │ └── default_blue_tab_tint.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── cn │ │ │ └── onestravel │ │ │ └── one │ │ │ └── navigation │ │ │ ├── utils │ │ │ ├── EventUtils.kt │ │ │ └── DensityUtils.kt │ │ │ └── androidx │ │ │ └── OneBottomNavigationBarX.kt │ ├── test │ │ └── java │ │ │ └── cn │ │ │ └── onestravel │ │ │ └── bottomview │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── cn │ │ └── onestravel │ │ └── bottomview │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── resources ├── SVID_20190130_155132_1.gif └── SVID_20190130_155440_1.gif ├── .idea ├── encodings.xml ├── vcs.xml ├── compiler.xml ├── misc.xml ├── runConfigurations.xml ├── gradle.xml ├── jarRepositories.xml ├── codeStyles │ └── Project.xml └── dbnavigator.xml ├── .gitignore ├── gradle.properties ├── project.properties ├── gradlew.bat ├── jcenter_publish.gradle ├── gradlew └── README.md /demo/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.java linguist-language=Java -------------------------------------------------------------------------------- /OneBottomNavigationBar/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':demo', ':OneBottomNavigationBar' 2 | -------------------------------------------------------------------------------- /OneBottomNavigationBar/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /OneBottomNavigationBar/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /resources/SVID_20190130_155132_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/resources/SVID_20190130_155132_1.gif -------------------------------------------------------------------------------- /resources/SVID_20190130_155440_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/resources/SVID_20190130_155440_1.gif -------------------------------------------------------------------------------- /demo/src/main/res/drawable/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/drawable/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable-hdpi/icon_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/drawable-hdpi/icon_add.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable-hdpi/tab_my_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/drawable-hdpi/tab_my_normal.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/drawable/ic_launcher_round.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable/tab_contact_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/drawable/tab_contact_normal.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable-hdpi/tab_my_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/drawable-hdpi/tab_my_selected.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable-hdpi/tab_news_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/drawable-hdpi/tab_news_normal.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /OneBottomNavigationBar/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable-hdpi/tab_contact_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/drawable-hdpi/tab_contact_normal.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable-hdpi/tab_invite_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/drawable-hdpi/tab_invite_normal.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable-hdpi/tab_manage_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/drawable-hdpi/tab_manage_normal.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable-hdpi/tab_news_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/drawable-hdpi/tab_news_selected.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable-hdpi/tab_contact_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/drawable-hdpi/tab_contact_selected.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable-hdpi/tab_invite_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/drawable-hdpi/tab_invite_selected.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable-hdpi/tab_manage_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onestravel/OneBottomNavigationBar/HEAD/demo/src/main/res/drawable-hdpi/tab_manage_selected.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches/build_file_checksums.ser 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | .DS_Store 9 | /build 10 | /captures 11 | .externalNativeBuild 12 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /demo/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/onestravel/one/navigation/demo/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package cn.onestravel.one.navigation.demo; 2 | 3 | import androidx.fragment.app.Fragment; 4 | 5 | /** 6 | * Created by wanghu on 2020-03-03 7 | */ 8 | public class BaseFragment extends Fragment { 9 | } 10 | -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/bg_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | BottomView 3 | 4 | 5 | Hello blank fragment 6 | 设置 7 | 拍照 8 | 首页 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/bar_my.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/bar_news.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/bar_invite.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/bar_manage.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/bar_constact.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /demo/src/test/java/cn/onestravel/bottomview/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package cn.onestravel.bottomview 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see [Testing documentation](http://d.android.com/tools/testing) 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, (2 + 2).toLong()) 16 | } 17 | } -------------------------------------------------------------------------------- /OneBottomNavigationBar/src/test/java/cn/onestravel/bottomview/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package cn.onestravel.bottomview 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see [Testing documentation](http://d.android.com/tools/testing) 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, (2 + 2).toLong()) 16 | } 17 | } -------------------------------------------------------------------------------- /OneBottomNavigationBar/src/main/res/drawable/default_blue_tab_tint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/item_check.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/fragment_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /demo/src/androidTest/java/cn/onestravel/bottomview/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package cn.onestravel.bottomview 2 | 3 | import android.content.Context 4 | import androidx.test.InstrumentationRegistry 5 | import androidx.test.runner.AndroidJUnit4 6 | 7 | import org.junit.Test 8 | import org.junit.runner.RunWith 9 | 10 | import org.junit.Assert.* 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see [Testing documentation](http://d.android.com/tools/testing) 16 | */ 17 | @RunWith(AndroidJUnit4::class) 18 | class ExampleInstrumentedTest { 19 | @Test 20 | fun useAppContext() { 21 | // Context of the app under test. 22 | val appContext = InstrumentationRegistry.getTargetContext() 23 | 24 | assertEquals("cn.onestravel.bottomview", appContext.packageName) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /demo/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 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/onestravel/one/navigation/demo/ThirdFragment.kt: -------------------------------------------------------------------------------- 1 | package cn.onestravel.one.navigation.demo 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | 8 | import cn.onestravel.bottomview.demo.R 9 | import kotlinx.android.synthetic.main.fragment_home.* 10 | 11 | /** 12 | * @author onestravel 13 | * @createTime 2019-08-04 11:52 14 | * @description TODO 15 | */ 16 | class ThirdFragment : BaseFragment() { 17 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 18 | savedInstanceState: Bundle?): View? { 19 | return inflater.inflate(R.layout.fragment_home, container, false).apply { init() } 20 | } 21 | fun init() { 22 | tvName?.let {it.text = "ThirdFragment"} 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /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 | 17 | 18 | -------------------------------------------------------------------------------- /OneBottomNavigationBar/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 | -------------------------------------------------------------------------------- /OneBottomNavigationBar/src/androidTest/java/cn/onestravel/bottomview/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package cn.onestravel.bottomview 2 | 3 | import android.content.Context 4 | import androidx.test.InstrumentationRegistry 5 | import androidx.test.runner.AndroidJUnit4 6 | 7 | import org.junit.Test 8 | import org.junit.runner.RunWith 9 | 10 | import org.junit.Assert.* 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see [Testing documentation](http://d.android.com/tools/testing) 16 | */ 17 | @RunWith(AndroidJUnit4::class) 18 | class ExampleInstrumentedTest { 19 | @Test 20 | fun useAppContext() { 21 | // Context of the app under test. 22 | val appContext = InstrumentationRegistry.getTargetContext() 23 | 24 | assertEquals("cn.onestravel.bottomview2.test", appContext.packageName) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /demo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/onestravel/one/navigation/demo/SecondFragment.kt: -------------------------------------------------------------------------------- 1 | package cn.onestravel.one.navigation.demo 2 | 3 | import android.app.Fragment 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.TextView 9 | 10 | import cn.onestravel.bottomview.demo.R 11 | import kotlinx.android.synthetic.main.fragment_home.* 12 | 13 | /** 14 | * @author onestravel 15 | * @createTime 2019-08-04 11:52 16 | * @description TODO 17 | */ 18 | class SecondFragment : BaseFragment() { 19 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 20 | savedInstanceState: Bundle?): View? { 21 | return inflater.inflate(R.layout.fragment_home, container, false).apply { init() } 22 | } 23 | fun init() { 24 | tvName?.let {it.text = "SecondFragment"} 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /demo/src/main/res/menu/navigation_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 12 | 13 | 20 | 21 | 25 | 29 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/onestravel/one/navigation/demo/FiveFragment.kt: -------------------------------------------------------------------------------- 1 | package cn.onestravel.one.navigation.demo 2 | 3 | import android.app.Fragment 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.TextView 9 | 10 | import cn.onestravel.bottomview.demo.R 11 | import kotlinx.android.synthetic.main.fragment_home.* 12 | 13 | /** 14 | * @author onestravel 15 | * @createTime 2019-08-04 11:52 16 | * @description TODO 17 | */ 18 | class FiveFragment : BaseFragment() { 19 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 20 | savedInstanceState: Bundle?): View? { 21 | val view = inflater.inflate(R.layout.fragment_home, container, false) 22 | return view 23 | } 24 | 25 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 26 | super.onViewCreated(view, savedInstanceState) 27 | init() 28 | } 29 | fun init() { 30 | tvName?.let {it.text = "FiveFragment"} 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/onestravel/one/navigation/demo/FourFragment.kt: -------------------------------------------------------------------------------- 1 | package cn.onestravel.one.navigation.demo 2 | 3 | import android.app.Fragment 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.TextView 9 | 10 | import cn.onestravel.bottomview.demo.R 11 | import kotlinx.android.synthetic.main.fragment_home.* 12 | 13 | /** 14 | * @author onestravel 15 | * @createTime 2019-08-04 11:52 16 | * @description TODO 17 | */ 18 | class FourFragment : BaseFragment() { 19 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 20 | savedInstanceState: Bundle?): View? { 21 | val view = inflater.inflate(R.layout.fragment_home, container, false) 22 | return view 23 | } 24 | 25 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 26 | super.onViewCreated(view, savedInstanceState) 27 | init() 28 | } 29 | fun init() { 30 | tvName?.let {it.text = "FourFragment"} 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/onestravel/one/navigation/demo/FirstFragment.kt: -------------------------------------------------------------------------------- 1 | package cn.onestravel.one.navigation.demo 2 | 3 | import android.app.Fragment 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.TextView 9 | 10 | import cn.onestravel.bottomview.demo.R 11 | import kotlinx.android.synthetic.main.fragment_home.* 12 | 13 | /** 14 | * @author onestravel 15 | * @createTime 2019-08-04 11:52 16 | * @description TODO 17 | */ 18 | class FirstFragment : BaseFragment() { 19 | 20 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 21 | savedInstanceState: Bundle?): View? { 22 | val view = inflater.inflate(R.layout.fragment_home, container, false) 23 | return view 24 | } 25 | 26 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 27 | super.onViewCreated(view, savedInstanceState) 28 | init() 29 | } 30 | 31 | fun init() { 32 | tvName?.let { it.text = "FirstFragment" } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /OneBottomNavigationBar/src/main/java/cn/onestravel/one/navigation/utils/EventUtils.kt: -------------------------------------------------------------------------------- 1 | package cn.onestravel.one.navigation.utils 2 | 3 | import android.graphics.Rect 4 | import android.view.View 5 | 6 | /** 7 | * @author onestravel 8 | * @version 1.0.0 9 | * @name EventUtils 10 | * @description //TODO 11 | * @createTime 2019/1/23 10:50 12 | */ 13 | object EventUtils { 14 | /** 15 | * 判断点击事件是否在view内 16 | * @param view 17 | * @param x 18 | * @param y 19 | * @return 20 | */ 21 | fun isInViewZone(view: View, x: Double, y: Double): Boolean { 22 | val mChangeImageBackgroundRect = Rect() 23 | view.getDrawingRect(mChangeImageBackgroundRect) 24 | val location = IntArray(2) 25 | view.getLocationOnScreen(location) 26 | mChangeImageBackgroundRect.left = location[0] 27 | mChangeImageBackgroundRect.top = location[1] 28 | mChangeImageBackgroundRect.right = mChangeImageBackgroundRect.right + location[0] 29 | mChangeImageBackgroundRect.bottom = mChangeImageBackgroundRect.bottom + location[1] 30 | return mChangeImageBackgroundRect.contains(x.toInt(), y.toInt()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /OneBottomNavigationBar/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /project.properties: -------------------------------------------------------------------------------- 1 | project.repo=one 2 | project.name=OneBottomNavigationBar 3 | project.groupId=cn.onestravel.one 4 | project.description=OneBottomNavigationBar is a custom View that implements the function of the navigation bar at the bottom of the App. It can achieve 2-5 navigation menus at the bottom (generally there are no more), and can achieve the effect of a certain menu. You can choose to make the third menu floating. This is a function that many apps have; you can change the color of icons and text when selected and unselected according to the resource file of the color change. You can freely control whether You need to check (checkable). When you select On, you can change to the selected color. When you select Off, you cannot change to the selected color. You can control which item is selected by default. You can set the number of unread messages (number or small red) in a navigation menu. point) 5 | project.artifact=one_bottom_navigationbar 6 | project.packaging=aar 7 | project.version=1.2.4-beta2 8 | project.siteUrl=https://github.com/onestravel/OneBottomNavigationBar 9 | project.gitUrl=https://github.com/onestravel/OneBottomNavigationBar.git 10 | project.licenseName=The Apache Software License, Version 2.0 11 | project.licenseUrl=http://www.apache.org/licenses/LICENSE-2.0.txt 12 | project.allLicenses=["Apache-2.0"] 13 | javadoc.name=OneBottomNavigationBar 14 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /OneBottomNavigationBar/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android-extensions' 3 | apply plugin: 'kotlin-android' 4 | apply from: '../jcenter_publish.gradle' 5 | 6 | android { 7 | compileSdkVersion 29 8 | buildToolsVersion "29.0.0" 9 | 10 | defaultConfig { 11 | minSdkVersion 15 12 | targetSdkVersion 29 13 | versionCode 110 14 | versionName "1.1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | lintOptions { 27 | abortOnError false 28 | } 29 | 30 | } 31 | 32 | dependencies { 33 | implementation fileTree(dir: 'libs', include: ['*.jar']) 34 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 35 | implementation 'androidx.appcompat:appcompat:1.0.2' 36 | testImplementation 'junit:junit:4.12' 37 | androidTestImplementation 'androidx.test:runner:1.1.0' 38 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' 39 | implementation "androidx.core:core-ktx:+" 40 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 41 | } 42 | repositories { 43 | mavenCentral() 44 | } 45 | -------------------------------------------------------------------------------- /demo/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android-extensions' 3 | apply plugin: 'kotlin-android' 4 | 5 | android { 6 | compileSdkVersion 29 7 | buildToolsVersion "29.0.0" 8 | defaultConfig { 9 | applicationId "cn.onestravel.bottomview.demo" 10 | minSdkVersion 15 11 | targetSdkVersion 29 12 | versionCode 1 13 | versionName "1.0" 14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | implementation fileTree(include: ['*.jar'], dir: 'libs') 26 | implementation Deps.appcompat 27 | implementation Deps.constraintlayout 28 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 29 | testImplementation 'junit:junit:4.12' 30 | androidTestImplementation 'androidx.test:runner:1.1.0' 31 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' 32 | implementation project(':OneBottomNavigationBar') 33 | implementation "androidx.core:core-ktx:+" 34 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 35 | // implementation 'cn.onestravel.one:one_bottom_navigationbar:1.2.2' 36 | 37 | } 38 | repositories { 39 | mavenCentral() 40 | } 41 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/onestravel/one/navigation/demo/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package cn.onestravel.one.navigation.demo 2 | 3 | import android.graphics.Color 4 | import androidx.appcompat.app.AppCompatActivity 5 | import android.os.Bundle 6 | 7 | import cn.onestravel.bottomview.demo.R 8 | import kotlinx.android.synthetic.main.activity_main.* 9 | 10 | 11 | class MainActivity : AppCompatActivity() { 12 | 13 | 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | 17 | setContentView(R.layout.activity_main) 18 | oneBottomLayout.setMenu(R.menu.navigation_menu) 19 | oneBottomLayout.setFragmentManager(supportFragmentManager, mainFragment ) 20 | oneBottomLayout.isReplace = false 21 | oneBottomLayout.addFragment(R.id.tab1, FirstFragment()) 22 | oneBottomLayout.addFragment(R.id.tab4, FourFragment()) 23 | oneBottomLayout.addFragment(R.id.tab5, FiveFragment()) 24 | oneBottomLayout.setFloatingEnable(true) 25 | oneBottomLayout.setTopLineColor(Color.RED) 26 | oneBottomLayout.setItemColorStateList(R.drawable.item_check) 27 | oneBottomLayout.setMsgCount(0, 32) 28 | oneBottomLayout.setMsgCount(2, 1) 29 | oneBottomLayout.setMsgCount(1, -1) 30 | oneBottomLayout.setOnItemSelectedListener { item, position -> 31 | // if (position == 1) { 32 | // oneBottomLayout.setFloatingEnable(true) 33 | // } else { 34 | // oneBottomLayout.setFloatingEnable(false) 35 | // } 36 | false 37 | } 38 | 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/onestravel/one/navigation/demo/MainViewPagerActivity.kt: -------------------------------------------------------------------------------- 1 | package cn.onestravel.one.navigation.demo 2 | 3 | import android.graphics.Color 4 | import androidx.appcompat.app.AppCompatActivity 5 | import android.os.Bundle 6 | 7 | import cn.onestravel.bottomview.demo.R 8 | import kotlinx.android.synthetic.main.activity_main.* 9 | import kotlinx.android.synthetic.main.activity_main.oneBottomLayout 10 | import kotlinx.android.synthetic.main.activity_main_viewpager.* 11 | 12 | 13 | class MainViewPagerActivity : AppCompatActivity() { 14 | 15 | 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | 19 | setContentView(R.layout.activity_main_viewpager) 20 | 21 | oneBottomLayout.setMenu(R.menu.navigation_menu) 22 | 23 | oneBottomLayout.attachViewPager(supportFragmentManager,viewPager, listOf(FirstFragment(),FiveFragment(),FirstFragment(),FiveFragment())) 24 | oneBottomLayout.setFloatingEnable(true) 25 | oneBottomLayout.setTopLineColor(Color.RED) 26 | oneBottomLayout.setItemColorStateList(R.drawable.item_check) 27 | oneBottomLayout.setMsgCount(0, 32) 28 | oneBottomLayout.setMsgCount(2, 1) 29 | oneBottomLayout.setMsgCount(1, -1) 30 | oneBottomLayout.setOnItemSelectedListener { item, position -> 31 | // if (position == 1) { 32 | // oneBottomLayout.setFloatingEnable(true) 33 | // } else { 34 | // oneBottomLayout.setFloatingEnable(false) 35 | // } 36 | false 37 | } 38 | 39 | 40 | 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /OneBottomNavigationBar/src/main/java/cn/onestravel/one/navigation/utils/DensityUtils.kt: -------------------------------------------------------------------------------- 1 | package cn.onestravel.one.navigation.utils 2 | 3 | import android.content.Context 4 | import android.content.res.Resources 5 | 6 | /** 7 | * @author onestravel 8 | * @version 1.0.0 9 | * @name DensityUtils 10 | * @description //TODO 11 | * @createTime 2019/1/22 11:11 12 | */ 13 | object DensityUtils { 14 | 15 | /** 16 | * dp转换为px 17 | * 18 | * @param context 19 | * @param dpValue 20 | * @return 21 | */ 22 | fun dpToPx(context: Context, dpValue: Float): Int {//dp转换为px 23 | val scale = context.resources.displayMetrics.density//获得当前屏幕密度 24 | return (dpValue * scale + 0.5f).toInt() 25 | } 26 | 27 | /** 28 | * px转换为dp 29 | * 30 | * @param context 31 | * @param pxValue 32 | * @return 33 | */ 34 | fun pxToDp(context: Context, pxValue: Float): Int {// 35 | val scale = context.resources.displayMetrics.density//获得当前屏幕密度 36 | return (pxValue / scale + 0.5f).toInt() 37 | } 38 | 39 | /** 40 | * dp转换为px 41 | * 42 | * @param resources 43 | * @param dpValue 44 | * @return 45 | */ 46 | fun dpToPx(resources: Resources, dpValue: Float): Int { 47 | val scale = resources.displayMetrics.density//获得当前屏幕密度 48 | return (dpValue * scale + 0.5f).toInt() 49 | } 50 | 51 | 52 | /** 53 | * px转换为dp 54 | * 55 | * @param resources 56 | * @param pxValue 57 | * @return 58 | */ 59 | fun pxToDp(resources: Resources, pxValue: Float): Int {// 60 | val scale = resources.displayMetrics.density//获得当前屏幕密度 61 | return (pxValue / scale + 0.5f).toInt() 62 | } 63 | 64 | 65 | /** 66 | * sp转换为px 67 | * 68 | * @param resources 69 | * @param spValue 70 | * @return 71 | */ 72 | fun spToPx(resources: Resources, spValue: Float): Int { 73 | val scale = resources.displayMetrics.scaledDensity//获得当前屏幕密度 74 | return (spValue * scale + 0.5f).toInt() 75 | } 76 | 77 | 78 | /** 79 | * px转换为sp 80 | * 81 | * @param resources 82 | * @param pxValue 83 | * @return 84 | */ 85 | fun pxToSp(resources: Resources, pxValue: Float): Int {// 86 | val scale = resources.displayMetrics.scaledDensity//获得当前屏幕密度 87 | return (pxValue / scale + 0.5f).toInt() 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/activity_main_viewpager.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /jcenter_publish.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.jfrog.bintray' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | //apply plugin: 'org.jetbrains.dokka-android' 4 | 5 | 6 | if (project.hasProperty("kotlin")) { //Kotlin libraries 7 | task sourcesJar(type: Jar) { 8 | classifier = 'sources' 9 | from android.sourceSets.main.java.srcDirs 10 | } 11 | 12 | task javadoc(type: Javadoc) { 13 | 14 | } 15 | } else if (project.hasProperty("android")) { // Android libraries 16 | task sourcesJar(type: Jar) { 17 | classifier = 'sources' 18 | from android.sourceSets.main.java.srcDirs 19 | } 20 | 21 | task javadoc(type: Javadoc) { 22 | source = android.sourceSets.main.java.srcDirs 23 | // classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 24 | } 25 | } else { // Java libraries 26 | task sourcesJar(type: Jar, dependsOn: classes) { 27 | classifier = 'sources' 28 | from sourceSets.main.allSource 29 | } 30 | } 31 | 32 | task javadocJar(type: Jar, dependsOn: javadoc) { 33 | classifier = 'javadoc' 34 | from javadoc.destinationDir 35 | } 36 | 37 | //解决kotlin javadoc.options抱错 38 | //dokka { 39 | //// outputFormat = 'html' 40 | //// outputDirectory = "$buildDir/javadoc" 41 | //} 42 | 43 | tasks.withType(Javadoc) { 44 | options.addStringOption('Xdoclint:none', '-quiet') 45 | options.addStringOption('encoding', 'UTF-8') 46 | options.addStringOption('charSet', 'UTF-8') 47 | } 48 | artifacts { 49 | archives javadocJar 50 | archives sourcesJar 51 | } 52 | sourceSets { 53 | main.java.srcDirs += 'src/main/kotlin' 54 | } 55 | 56 | // project 57 | Properties propertiesProject = new Properties() 58 | propertiesProject.load(project.rootProject.file('project.properties').newDataInputStream()) 59 | 60 | version = propertiesProject.getProperty("project.version") 61 | group = propertiesProject.getProperty("project.groupId") 62 | def versionName = propertiesProject.getProperty("project.version") 63 | def groupName = propertiesProject.getProperty("project.groupId") 64 | def projectRepo = propertiesProject.getProperty("project.repo") 65 | def artifact = propertiesProject.getProperty("project.artifact") 66 | def projectName = propertiesProject.getProperty("project.name") 67 | def projectDescription = propertiesProject.getProperty("project.description") 68 | def siteUrl = propertiesProject.getProperty("project.siteUrl") 69 | def gitUrl = propertiesProject.getProperty("project.gitUrl") 70 | def packageType = propertiesProject.getProperty("project.packaging") 71 | def licenseName = propertiesProject.getProperty("project.licenseName") 72 | def licenseUrl = propertiesProject.getProperty("project.licenseUrl") 73 | def allLicenses = propertiesProject.getProperty("project.allLicenses") 74 | 75 | // Bintray 76 | Properties propertiesLocal = new Properties() 77 | propertiesLocal.load(project.rootProject.file('local.properties').newDataInputStream()) 78 | 79 | def developerId = propertiesLocal.getProperty("developer.id") 80 | def developerName = propertiesLocal.getProperty("developer.name") 81 | def developerEmail = propertiesLocal.getProperty("developer.email") 82 | 83 | install { 84 | 85 | repositories.mavenInstaller { 86 | // This generates POM.xml with proper parameters 87 | pom { 88 | project { 89 | packaging packageType 90 | groupId groupName 91 | artifactId artifact 92 | 93 | // Add your description here 94 | name projectName 95 | description projectDescription 96 | url siteUrl 97 | 98 | // Set your license 99 | licenses { 100 | license { 101 | name licenseName 102 | url licenseUrl 103 | } 104 | } 105 | developers { 106 | developer { 107 | id developerId 108 | name developerName 109 | email developerEmail 110 | } 111 | } 112 | scm { 113 | connection gitUrl 114 | developerConnection gitUrl 115 | url siteUrl 116 | 117 | } 118 | } 119 | } 120 | } 121 | } 122 | 123 | 124 | bintray { 125 | user = propertiesLocal.getProperty("bintray.user") 126 | key = propertiesLocal.getProperty("bintray.apikey") 127 | 128 | configurations = ['archives'] 129 | pkg { 130 | repo = projectRepo 131 | name = projectName 132 | desc = projectDescription 133 | websiteUrl = siteUrl 134 | vcsUrl = gitUrl 135 | licenses = ["Apache-2.0"] 136 | publish = true 137 | publicDownloadNumbers = true 138 | version { 139 | name = versionName 140 | desc = projectDescription 141 | // gpg { 142 | // sign = true //Determines whether to GPG sign the files. The default is false 143 | // passphrase = properties.getProperty("bintray.gpg.password") 144 | // //Optional. The passphrase for GPG signing' 145 | // } 146 | } 147 | } 148 | } 149 | //./gradlew install 150 | //./gradlew bintrayUpload 151 | 152 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /demo/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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # [OneBottomNavigationBar](https://github.com/onestravel/OneBottomNavigationBar) 使用说明 4 | 5 | ## 简介: 6 | 7 | ##### OneBottomNavigationBar 是自定义的一个实现App应用底部导航栏功能的View,可以实现底部 2-5 个导航菜单(一般不会有更多),可以实现某一个菜单凸起的效果,如,有5个菜单,可以选择让第三个菜单凸起(floating),这是很多App都有的功能; 可以根据编写的颜色变化的资源文件来更改图标和文字选中时和未选中时的颜色,可以自由控制是否需要选中(checkable),选择开启时,可变换为选中颜色,选择关闭时,不能更改为选择颜色;可以控制默认选中哪一项;可以设置某一个导航菜单的未读消息数(数字或者小红点)。 8 | 9 | ### 先看一组效果图 10 | 11 | #### 五个菜单,没有凸起的,都是可选中的 12 | 13 | ![SVID_20190130_155132_1](resources/SVID_20190130_155132_1.gif) 14 | 15 |
图1
16 | 17 | #### 中间有凸起,并且中间的不能选中,但点击事件可以响应 18 | 19 | ![SVID_20190130_155440_1](resources/SVID_20190130_155440_1.gif) 20 | 21 |
图2
22 | 23 | ## 引入方法 24 | 25 | ### jcenter 26 | 27 | VERSION: [ ![VERSION](https://api.bintray.com/packages/onestravel/one/OneBottomNavigationBar/images/download.svg) ](https://bintray.com/onestravel/one/OneBottomNavigationBar) 28 | 29 | - 在model 中的build.gradle 中增加依赖 30 | 31 | ``` 32 | dependencies { 33 | implementation 'cn.onestravel.one:one_bottom_navigationbar:VERSION' 34 | } 35 | 36 | ``` 37 | 例如: 38 | ``` 39 | dependencies { 40 | implementation 'cn.onestravel.one:one_bottom_navigationbar:1.1.1' 41 | } 42 | ``` 43 | 44 | ### jitPack 45 | 46 | VERSION: [![VERSION](https://jitpack.io/v/onestravel/BottomNavigationBar.svg)](https://jitpack.io/#onestravel/BottomNavigationBar) 47 | 48 | 49 | - 在项目中的根目录下的 build.gradle (与model同级的) 中增加如下配置 50 | 51 | ``` 52 | 53 | allprojects { 54 | repositories { 55 | ... 56 | maven { url 'https://jitpack.io' } 57 | } 58 | } 59 | 60 | ``` 61 | 62 | - 在model 中的build.gradle 中增加依赖 63 | 64 | ``` 65 | dependencies { 66 | implementation 'com.github.onestravel:OneBottomNavigationBar:VERSION' 67 | } 68 | 69 | ``` 70 | 例如: 71 | ``` 72 | dependencies { 73 | implementation 'com.github.onestravel:OneBottomNavigationBar:1.1.1' 74 | } 75 | ``` 76 | 77 | ## 属性说明 78 | 79 | ### OneBottomNavigationBar 属性说明 80 | 81 | | 属性 | 参考值 | 说明 | 82 | | ------------------ | ---------------------- | ------------------------------------------------------------ | 83 | | app:oneItemIconTint | @drawable/tab_selecter | 整体的tab菜单的图片选中和未选中的颜色变化,传入一个资源drawable文件 | 84 | | app:oneItemTextColor | @drawable/tab_selecter | @drawable/tab_selecter@drawable/tab_selecter整体的tab菜单的图片选中和未选中的颜色变化,传入一个资源drawable文件 | 85 | | app:oneFloatingEnable | true/false | 是否开启浮动,默认为false,设置为true是,可以实现中间凸起 | 86 | | app:oneFloatingUp | 20dp | 设置Tab的上浮尺寸,比如:上浮20dp,上浮尺寸不可超过整个菜单高度的1/2 | 87 | | app:oneMenu | @menu/botom_menu | BottomNavigationBar导航栏的关键,设置导航栏的tab菜单 | 88 | | app:oneItemTextSize | 15sp | 设置导航栏文字的大小 | 89 | | app:oneItemIconWidth | 30dp | 设置导航栏Icon的宽度 | 90 | | app:oneItemIconHeight | 30dp | 设置导航栏Icon的高度 | 91 | | app:oneItemTextTopMargin | 5dp | 设置导航栏文字和Icon的间隔高度 | 92 | | app:oneItemTopLineColor |#CCCCCC | 设置导航栏顶部分割线颜色,想去掉该分割线可设置透明色 | 93 | | app:oneItemPadding |5dp | 设置正常导航栏上下边距 | 94 | | app:oneFloatingPadding |2dp | 设置浮动导航栏上下边距 | 95 | 96 | 97 | #### 示例1:不需要浮动(凸起)的菜单,图1效果 98 | 99 | ``` 100 | 101 | 110 | 111 | 112 | 113 | ``` 114 | 115 | #### 示例2:需要浮动(凸起)的菜单,图2效果 116 | 117 | ``` 118 | 129 | 130 | 131 | ``` 132 | 133 | ### menu 菜单属性值说明 134 | 135 | | 属性 | 参考值 | 说明 | 136 | | ----------------- | ------------------ | ------------------------------------------------------------ | 137 | | android:id | @+id/tab1 | 导航菜单 Item 的ID; | 138 | | android:icon | @drawable/bar_news | 导航菜单 Item 的图标,可以是图标选择器(selector),也可以是默认图标,根据与OneBottomNavigationBar 的属性 app:oneItemIconTint 更改选中与不选中的颜色变化,默认为蓝色和灰色; | 139 | | android:title | 首页 | 导航菜单 Item 的文字,可以默认为空字符串,表示不设置; | 140 | | android:checkable | true/false | 设置导航菜单 Item 是否可以选择,值影响选择与不选中效果,不影响点击事件; | 141 | | android:checked | true/false | 设置导航菜单 Item 是否默认选中,默认为第一个选中,请不要在多个Item上设置改; | 142 | | app:floating | true/false | 设置该导航菜单 Item 是否浮动,与OneBottomNavigationBar 的app:floatingEnable和 app:oneFloatingUp属性配合使用,默认为false,即不浮动(不凸起); | 143 | 144 | #### 示例1:不需要浮动(凸起)的菜单,图1效果 145 | 146 | ``` 147 | 148 | 150 | 151 | 155 | 159 | 163 | 167 | 171 | 172 | ``` 173 | 174 | #### 示例2:需要浮动(凸起)的菜单,图2效果 175 | 176 | ``` 177 | 178 | 180 | 181 | 185 | 189 | 195 | 199 | 203 | 204 | ``` 205 | 206 | ### 设置点击切换监听 207 | **在NavigationBar的Tab进行切换时,会回调改方法,可进行相应处理,如:未读消息数设置** 208 | ``` 209 | bottomView.setOnItemSelectedListener(new OneBottomNavigationBar.OnItemSelectedListener() { 210 | @Override 211 | public void onItemSelected(OneBottomNavigationBar.Item item, int position) { 212 | if(position==2){ 213 | bottomView.setFloatingEnable(true); 214 | }else { 215 | bottomView.setFloatingEnable(false); 216 | } 217 | } 218 | }); 219 | ``` 220 | 221 | ### 添加Fragment进行管理,点击自动切换 222 | **1. 需要先设置FragmentManager管理器和加载Fragment的ViewGroup,一般为FrameLayout ,需要设置id;之后调用addFragment,为对应的Tab添加Fragment,** 223 | ```java 224 | bottomView.setFragmentManager(getFragmentManager(),mainFragment); 225 | bottomView.addFragment(R.id.tab1,new FirstFragment()); 226 | bottomView.addFragment(R.id.tab2,new SecondFragment()); 227 | bottomView.addFragment(R.id.tab3,new ThirdFragment()); 228 | bottomView.addFragment(R.id.tab4,new FourFragment()); 229 | bottomView.addFragment(R.id.tab5,new FiveFragment()); 230 | ``` 231 | **2. 使用 ViewPager 来管理切换 Fragment** begin 1.2.4-beta 232 | java 233 | 234 | ```java 235 | ArrayList fragments = new ArrayList<>(); 236 | fragments.add(new FirstFragment()); 237 | fragments.add(new SecondFragment()); 238 | fragments.add(new ThirdFragment()); 239 | fragments.add(new FourFragment()); 240 | fragments.add(new FiveFragment()); 241 | bottomView.attachViewPager(getSupportFragmentManager(),viewPager, fragments); 242 | ``` 243 | 244 | kotlin 245 | 246 | ```kotlin 247 | bottomView.attachViewPager(supportFragmentManager,viewPager, listOf(FirstFragment(),ThirdFragment(),FourFragment(),FiveFragment())) 248 | ``` 249 | 250 | 注意:如果 tab 中有不需要展示 Fragment 的时候,在使用 ViewPager 管理切换 Fragment 时,也不应该包含对应的 Fragment 对象 251 | 252 | 253 | ### 版本更新说明 254 | 255 | 256 | #### 1.2.4-beta 257 | - 修复菜单突出时,上边界划线失效问题 258 | - 支持 Viewpager 切换 Fragment 259 | 260 | 261 | #### 1.2.2 262 | - 修复菜单突出时,上边界划线失效问题 263 | 264 | 265 | #### 1.2.0 266 | - 优化图标尺寸计算方式 267 | - 优化纬度角标绘制位置 268 | - 新增参数 ``app:oneItemPadding`` 和 ``app:oneFloatingPadding`` 设置上下边距 269 | 270 | 271 | #### 1.1.1 272 | - 开发语言更换为 kotlin 273 | - 新增使用androidx.fragment.app.Fragment和androidx.fragment.app.FragmentManager管理fragment的支持, 274 | 包路径为:cn.onestravel.one.navigation.androidx.OneBottomNavigationBar 275 | - 在jcenter上发布新包 276 | 277 | 278 | #### 1.1.0 279 | - 更换属性,部分属性前增加one前缀,解决属性冲突问题 280 | - BottomNavigationBar 更换为 OneBottomNavigationBar 281 | - 增加顶部分割线颜色属性,可设置分割线颜色(或透明色) 282 | 283 | 284 | #### 1.0.6 285 | - 可以Java Api 设置导航栏文字大小 286 | - 可以Java Api 设置图标的宽度和高度 287 | - Java Api 设置图标和文字间隔高度 288 | - 支持管理Fragment ,实现Fragment点击自动切换 289 | 290 | 291 | #### 1.0.5 292 | - 可以自定义导航栏文字大小 293 | - 可以自定义图标的宽度和高度 294 | - 自定义图标和文字间隔高度 295 | 296 | #### 1.0.4 297 | - 优化选中突出效果,支持选中放大,支持导航栏背景圆角 298 | 299 | #### 1.0.3 300 | - 在原基础上对封装进行优化,支持代码实现导航栏 301 | 302 | 303 | #### 1.0.2 304 | - 初版实现自定义底部导航栏 305 | 306 | 307 | 308 | 309 | 310 | # 温馨提示: 311 | **在使用过程中,如遇到任何问题,可发送邮件至server@onestravel.cn说明相关问题,我在看到邮件第一时间,会针对相应问题进行沟通解决,谢谢支持!**# 312 | -------------------------------------------------------------------------------- /.idea/dbnavigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | -------------------------------------------------------------------------------- /OneBottomNavigationBar/src/main/java/cn/onestravel/one/navigation/androidx/OneBottomNavigationBarX.kt: -------------------------------------------------------------------------------- 1 | package cn.onestravel.one.navigation.androidx 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.content.res.ColorStateList 6 | import android.graphics.Bitmap 7 | import android.graphics.Canvas 8 | import android.graphics.Color 9 | import android.graphics.Paint 10 | import android.graphics.PixelFormat 11 | import android.graphics.PorterDuff 12 | import android.graphics.Rect 13 | import android.graphics.drawable.BitmapDrawable 14 | import android.graphics.drawable.ColorDrawable 15 | import android.graphics.drawable.Drawable 16 | import android.graphics.drawable.GradientDrawable 17 | import android.graphics.drawable.NinePatchDrawable 18 | import android.graphics.drawable.StateListDrawable 19 | import android.os.Build 20 | 21 | import androidx.annotation.ColorInt 22 | import androidx.annotation.ColorRes 23 | import androidx.annotation.DrawableRes 24 | import androidx.annotation.IdRes 25 | import androidx.annotation.MenuRes 26 | import androidx.core.content.res.ResourcesCompat 27 | import androidx.core.graphics.drawable.DrawableCompat 28 | 29 | import android.text.TextUtils 30 | import android.util.AttributeSet 31 | import android.util.Log 32 | import android.view.MotionEvent 33 | import android.view.View 34 | import android.view.ViewGroup 35 | import android.widget.FrameLayout 36 | import android.widget.ImageView 37 | import android.widget.LinearLayout 38 | import android.widget.RelativeLayout 39 | import androidx.fragment.app.Fragment 40 | import androidx.fragment.app.FragmentManager 41 | import androidx.fragment.app.FragmentPagerAdapter 42 | import androidx.viewpager.widget.PagerAdapter 43 | import androidx.viewpager.widget.ViewPager 44 | import cn.onestravel.one.navigation.BuildConfig 45 | 46 | import org.xmlpull.v1.XmlPullParser 47 | import org.xmlpull.v1.XmlPullParserException 48 | 49 | import java.io.IOException 50 | 51 | import cn.onestravel.one.navigation.R 52 | import cn.onestravel.one.navigation.utils.DensityUtils 53 | import java.util.* 54 | import kotlin.math.sqrt 55 | 56 | /** 57 | * @author onestravel 58 | * @createTime 2019/1/20 9:48 AM 59 | * @description 可以凸起的底部导航菜单VIEW 60 | */ 61 | 62 | typealias OnItemSelectedListener = (item: OneBottomNavigationBar.Item, position: Int) -> Boolean 63 | 64 | class OneBottomNavigationBar : View { 65 | private var mViewPager: ViewPager? = null 66 | private var mLayoutParams: ViewGroup.LayoutParams? = null 67 | private val TAG = "BottomNavigationBar" 68 | 69 | // 导航菜单键列表 70 | private var itemList: MutableList = ArrayList() 71 | 72 | //总宽度 width 73 | private var mWidth = 0 74 | 75 | //总高度 height 76 | private var mHeight = 0 77 | 78 | //每个菜单的宽度 item width 79 | private var mItemWidth = 0 80 | 81 | //每个菜单的告诉 item height 82 | private var mItemHeight = 0 83 | 84 | //整体的上边距 85 | private var topPadding = DensityUtils.dpToPx(resources, 3f) 86 | 87 | //整体下边距 88 | private var bottomPadding = DensityUtils.dpToPx(resources, 3f) 89 | 90 | //文字相对于图标的边距 91 | private var textTop = DensityUtils.dpToPx(resources, 3f) 92 | 93 | //画笔 94 | private val mTextPaint: Paint by lazy { Paint() } 95 | 96 | //图标的状态颜色列表 97 | private var itemIconTintRes: ColorStateList? = null 98 | 99 | //文字的状态颜色列表 100 | private var itemColorStateList: ColorStateList? = null 101 | 102 | //Item菜单的选中事件 103 | private var onItemSelectedListener: OnItemSelectedListener? = null 104 | 105 | // 当前选中的坐标位置 106 | private var checkedPosition = 0 107 | private var showPosition = 0 108 | 109 | //是否开启上浮 110 | private var floatingEnable: Boolean = false 111 | 112 | //上浮距离 113 | private var floatingUp: Int = 0 114 | 115 | //背景资源 116 | private var bgDrawable: Drawable? = null 117 | 118 | //菜单的布局文件 119 | @MenuRes 120 | private var menuRes: Int = 0 121 | private var titleSize: Int = 0 122 | private var itemIconWidth: Int = 0 123 | private var itemIconHeight: Int = 0 124 | private var itemPadding: Int = 0 125 | private var itemFloatingPadding: Int = 0 126 | private val fragmentMap: MutableMap = HashMap() 127 | private var manager: FragmentManager? = null 128 | private var containerView: View? = null 129 | private var currentFragment: Fragment? = null 130 | /** 131 | * 是否替换Fragment,替换后Fragment 数据会清空 132 | * 133 | * @return 134 | */ 135 | /** 136 | * 设置是否替换Fragment,替换后Fragment 数据会清空 137 | * 138 | * @param replace 139 | */ 140 | var isReplace: Boolean = false 141 | private var linePaint: Paint? = null 142 | private val lineWidth by lazy { DensityUtils.dpToPx(context, 1f).toFloat() } 143 | private var topLineColor: Int = 0 144 | private final val DEFAULT_MSG_COUNT_TEXT_PADDING: Int = DensityUtils.dpToPx(resources, 2f) 145 | 146 | constructor(context: Context) : super(context) { 147 | init(context, null, 0) 148 | } 149 | 150 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { 151 | init(context, attrs, 0) 152 | } 153 | 154 | constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { 155 | init(context, attrs, defStyleAttr) 156 | } 157 | 158 | 159 | /** 160 | * 设置选中的监听事件 161 | * 162 | * @param onItemSelectedListener return true 选中事件由调用者自己处理 163 | * return false 选中事件将有本管理器自动实现 164 | */ 165 | fun setOnItemSelectedListener(onItemSelectedListener: OnItemSelectedListener) { 166 | this.onItemSelectedListener = onItemSelectedListener 167 | } 168 | 169 | /** 170 | * 设置选中 171 | * 172 | * @param position 选中位置 173 | */ 174 | fun setSelected(position: Int, vpPosition: Int = -1) { 175 | if (position < 0 || position >= itemList.size) { 176 | return 177 | } 178 | val item = itemList[position] 179 | if (item.isCheckable) { 180 | if (checkedPosition >= 0) { 181 | itemList[checkedPosition].isChecked = false 182 | } 183 | item.isChecked = true 184 | checkedPosition = position 185 | } 186 | postInvalidate() 187 | if (onItemSelectedListener != null && onItemSelectedListener!!.invoke(item, position)) { 188 | return 189 | } 190 | try { 191 | if (!item.showFragment) { 192 | return 193 | } 194 | if (mViewPager != null) { 195 | showPosition = if (vpPosition == -1) { 196 | if (needChangePos(position)) { 197 | 0.coerceAtLeast(position - 1) 198 | } else { 199 | position 200 | } 201 | } else { 202 | vpPosition 203 | } 204 | mViewPager?.currentItem = showPosition 205 | } else if (manager != null || containerView != null || containerView is ViewGroup || containerView!!.id != View.NO_ID) { 206 | showPosition = position; 207 | val fragment = fragmentMap[item.id] 208 | ?: throw RuntimeException("[" + item.id + "] fragment is null ") 209 | selectFragmentByManager(fragment) 210 | } 211 | 212 | } catch (e: Exception) { 213 | e.printStackTrace() 214 | } 215 | 216 | } 217 | 218 | /** 219 | * 绑定 ViewPager 使用,ViewPager 使用的 Fragment 数量需要与 Tab 数量一致,否则会出现 Fragment 显示与 tab 选中状态不对应的情况 220 | * @param viewPager 221 | */ 222 | fun attachViewPager(fragmentManager: FragmentManager, viewPager: ViewPager, fragments: List) { 223 | this.mViewPager = viewPager 224 | if (this.mViewPager != null) { 225 | mViewPager?.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { 226 | override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { 227 | } 228 | 229 | override fun onPageSelected(position: Int) { 230 | val item = itemList[position] 231 | var newPos = position 232 | if (needChangePos(position)) { 233 | newPos = position + 1 234 | } 235 | setSelected(newPos, position) 236 | } 237 | 238 | override fun onPageScrollStateChanged(state: Int) { 239 | } 240 | }) 241 | mViewPager?.adapter = ViewpagerAdapter(fragmentManager, fragments) 242 | mViewPager?.currentItem = 0 243 | } 244 | } 245 | 246 | private fun needChangePos(position: Int): Boolean { 247 | val notShowPos = findNotShowFragmentPos() 248 | if (notShowPos in 0..position) { 249 | return true 250 | } 251 | return false 252 | } 253 | 254 | private fun findNotShowFragmentPos(): Int { 255 | var pos = -1 256 | if (itemList == null || itemList.isEmpty()) { 257 | return pos 258 | } 259 | val item = itemList.find { item -> !item.showFragment } 260 | if (item != null) { 261 | pos = itemList.indexOf(item); 262 | } 263 | return pos; 264 | } 265 | 266 | /** 267 | * 设置未读消息数 268 | * 269 | * @param position 未读消息数的位置 270 | * @param count 未读消息数量 <0 是显示为小红点,没有数字 271 | * == 0 时不显示未读消息红点 272 | * >0 && <100 时显示对应的消息数量 273 | * >=100 时显示 99+ 274 | */ 275 | fun setMsgCount(position: Int, count: Int) { 276 | if (position < itemList.size) { 277 | itemList[position].msgCount = count 278 | postInvalidate() 279 | } 280 | } 281 | 282 | 283 | /** 284 | * 设置Menu 菜单资源文件 285 | * 286 | * @param menuRes 287 | */ 288 | fun setMenu(@MenuRes menuRes: Int) { 289 | this.menuRes = menuRes 290 | parseXml(menuRes) 291 | format() 292 | postInvalidate() 293 | } 294 | 295 | /** 296 | * 设置Item 菜单的图标颜色状态列表 297 | * 298 | * @param resId 图标颜色状态的资源文件 299 | */ 300 | fun setItemIconTint(@DrawableRes @ColorRes resId: Int) { 301 | this.itemIconTintRes = ResourcesCompat.getColorStateList(resources, resId, null) 302 | parseXml(menuRes) 303 | format() 304 | postInvalidate() 305 | } 306 | 307 | /** 308 | * 设置Item 菜单的文字颜色状态列表 309 | * 310 | * @param resId 文字颜色状态的资源文件 311 | */ 312 | fun setItemColorStateList(@DrawableRes @ColorRes resId: Int) { 313 | this.itemColorStateList = ResourcesCompat.getColorStateList(resources, resId, null) 314 | postInvalidate() 315 | } 316 | 317 | 318 | /** 319 | * 设置分割线颜色 320 | * @param color 321 | */ 322 | fun setTopLineColor(@ColorInt color: Int) { 323 | this.topLineColor = color 324 | invalidate() 325 | } 326 | 327 | 328 | /** 329 | * 设置分割线颜色 330 | * @param colorRes 331 | */ 332 | fun setTopLineColorRes(@ColorRes colorRes: Int) { 333 | this.topLineColor = resources.getColor(colorRes) 334 | invalidate() 335 | } 336 | 337 | 338 | /** 339 | * 设置是否开启浮动 340 | * 341 | * @param floatingEnable 342 | */ 343 | fun setFloatingEnable(floatingEnable: Boolean) { 344 | this.floatingEnable = floatingEnable 345 | postInvalidate() 346 | } 347 | 348 | /** 349 | * 设置上浮距离,不能超过导航栏高度的1/2 350 | * 351 | * @param floatingUp 352 | */ 353 | fun setFloatingUp(floatingUp: Int) { 354 | this.floatingUp = floatingUp 355 | postInvalidate() 356 | } 357 | 358 | /** 359 | * 设置FragmentManager,管理fragment 360 | * 361 | * @param fragmentManager fragment管理 362 | * @param fragmentContainerView fragment 将要添加的view 363 | */ 364 | fun setFragmentManager(fragmentManager: FragmentManager, fragmentContainerView: View) { 365 | this.manager = fragmentManager 366 | this.containerView = fragmentContainerView 367 | } 368 | 369 | /** 370 | * 添加Fragment 371 | * 372 | * @param tabId 373 | * @param fragment 374 | */ 375 | fun addFragment(@IdRes tabId: Int, fragment: Fragment) { 376 | fragmentMap[tabId] = fragment 377 | } 378 | 379 | /** 380 | * 获取布局参数 381 | * 382 | * @return 383 | */ 384 | override fun getLayoutParams(): ViewGroup.LayoutParams { 385 | return super.getLayoutParams() 386 | } 387 | 388 | /** 389 | * 设置图标大小 390 | * 391 | * @param itemIconWidth 392 | * @param itemIconHeight 393 | */ 394 | fun setItemIconSize(itemIconWidth: Int, itemIconHeight: Int) { 395 | this.itemIconWidth = itemIconWidth 396 | this.itemIconHeight = itemIconHeight 397 | format() 398 | postInvalidate() 399 | } 400 | 401 | /** 402 | * 设置Title文字大小 403 | * 404 | * @param titleSize 405 | */ 406 | fun setTitleSize(titleSize: Int) { 407 | this.titleSize = titleSize 408 | format() 409 | postInvalidate() 410 | } 411 | 412 | /** 413 | * 设置图标和title文字间距 414 | * 415 | * @param textTopMargin 416 | */ 417 | fun setTextTopMargin(textTopMargin: Int) { 418 | this.textTop = textTopMargin 419 | postInvalidate() 420 | } 421 | 422 | override fun onAttachedToWindow() { 423 | super.onAttachedToWindow() 424 | if (parent != null && parent is ViewGroup) { 425 | (parent as ViewGroup).clipChildren = false 426 | } 427 | } 428 | 429 | override fun setLayoutParams(params: ViewGroup.LayoutParams?) { 430 | super.setLayoutParams(params) 431 | updateLayoutParams() 432 | } 433 | 434 | private fun updateLayoutParams() { 435 | val params = layoutParams; 436 | if (floatingEnable) { 437 | val floatingUp = getFloatingUpHeight() 438 | when (params) { 439 | is LinearLayout.LayoutParams -> { 440 | params.topMargin = 0 - floatingUp 441 | } 442 | is RelativeLayout.LayoutParams -> { 443 | params.topMargin = 0 - floatingUp 444 | } 445 | is FrameLayout.LayoutParams -> { 446 | params.topMargin = 0 - floatingUp 447 | } 448 | is ViewGroup.MarginLayoutParams -> { 449 | params.topMargin = 0 - floatingUp 450 | } 451 | } 452 | } 453 | } 454 | 455 | 456 | /** 457 | * 初始化,获取该View的自定义属性,以及item 列表 458 | * 459 | * @param context 上下文 460 | * @param attrs 属性 461 | * @param defStyleAttr 默认样式 462 | */ 463 | @SuppressLint("ResourceType") 464 | private fun init(context: Context, attrs: AttributeSet?, defStyleAttr: Int) { 465 | if (attrs != null) { 466 | val ta = context.obtainStyledAttributes(attrs, R.styleable.One_StyleBottomLayout) 467 | itemIconTintRes = ta.getColorStateList(R.styleable.One_StyleBottomLayout_oneItemIconTint) 468 | itemColorStateList = ta.getColorStateList(R.styleable.One_StyleBottomLayout_oneItemTextColor) 469 | if (itemColorStateList == null) { 470 | itemColorStateList = ResourcesCompat.getColorStateList(resources, R.drawable.default_blue_tab_tint, null) 471 | } 472 | topLineColor = ta.getColor(R.styleable.One_StyleBottomLayout_oneItemTopLineColor, Color.TRANSPARENT) 473 | floatingEnable = ta.getBoolean(R.styleable.One_StyleBottomLayout_oneFloatingEnable, false) 474 | floatingUp = ta.getDimension(R.styleable.One_StyleBottomLayout_oneFloatingUp, 20f).toInt() 475 | titleSize = ta.getDimension(R.styleable.One_StyleBottomLayout_oneItemTextSize, DensityUtils.spToPx(resources, 12f).toFloat()).toInt() 476 | textTop = ta.getDimension(R.styleable.One_StyleBottomLayout_oneItemTextTopMargin, DensityUtils.dpToPx(resources, 3f).toFloat()).toInt() 477 | itemIconWidth = ta.getDimension(R.styleable.One_StyleBottomLayout_oneItemIconWidth, 0f).toInt() 478 | itemIconHeight = ta.getDimension(R.styleable.One_StyleBottomLayout_oneItemIconHeight, 0f).toInt() 479 | itemPadding = ta.getDimension(R.styleable.One_StyleBottomLayout_oneItemPadding, 0f).toInt() 480 | itemFloatingPadding = ta.getDimension(R.styleable.One_StyleBottomLayout_oneFloatingPadding, 0f).toInt() 481 | val xmlRes = ta.getResourceId(R.styleable.One_StyleBottomLayout_oneMenu, 0) 482 | parseXml(xmlRes) 483 | } 484 | format() 485 | } 486 | 487 | /** 488 | * 处理数据 489 | */ 490 | private fun format() { 491 | if (itemList.size > 5) { 492 | itemList = itemList.subList(0, 5) 493 | } 494 | bgDrawable = if (background != null && background is ColorDrawable) { 495 | background 496 | } else if (background is StateListDrawable) { 497 | background 498 | } else if (background is GradientDrawable) { 499 | background 500 | } else { 501 | ColorDrawable(Color.WHITE) 502 | } 503 | for (item in itemList) { 504 | item.titleSize = titleSize 505 | item.iconWidth = itemIconWidth 506 | item.iconHeight = itemIconHeight 507 | if (item.isFloating) { 508 | item.padding = itemFloatingPadding 509 | } else { 510 | item.padding = itemPadding; 511 | } 512 | } 513 | linePaint = createPaint(topLineColor, Paint.Style.FILL, lineWidth) 514 | } 515 | 516 | /** 517 | * 解析 menu 的 xml 的文件,得到相关的 导航栏菜单 518 | * 519 | * @param xmlRes 520 | */ 521 | private fun parseXml(xmlRes: Int) { 522 | try { 523 | if (xmlRes == 0) { 524 | return 525 | } 526 | itemList.clear() 527 | val xmlParser = resources.getXml(xmlRes) 528 | var event = xmlParser.eventType //先获取当前解析器光标在哪 529 | while (event != XmlPullParser.END_DOCUMENT) { //如果还没到文档的结束标志,那么就继续往下处理 530 | when (event) { 531 | XmlPullParser.START_DOCUMENT -> { 532 | if (BuildConfig.DEBUG) { 533 | Log.e(TAG, "xml解析开始") 534 | } 535 | } 536 | XmlPullParser.START_TAG -> 537 | //一般都是获取标签的属性值,所以在这里数据你需要的数据 538 | // Log.e(TAG, "当前标签是:" + xmlParser.getName()); 539 | if (xmlParser.name == "item") { 540 | val item = Item() 541 | for (i in 0 until xmlParser.attributeCount) { 542 | //两种方法获取属性值 543 | // Log.e(TAG, "第" + (i + 1) + "个属性:" + xmlParser.getAttributeName(i) 544 | // + ": " + xmlParser.getAttributeValue(i)); 545 | if ("id".equals(xmlParser.getAttributeName(i), ignoreCase = true)) { 546 | item.id = xmlParser.getAttributeResourceValue(i, 0) 547 | } else if ("icon".equals(xmlParser.getAttributeName(i), ignoreCase = true)) { 548 | val drawableId = xmlParser.getAttributeResourceValue(i, 0) 549 | val drawable = ResourcesCompat.getDrawable(resources, drawableId, null) 550 | item.drawable = drawable!!.constantState!!.newDrawable() 551 | var stateListDrawable = StateListDrawable() 552 | if (drawable is StateListDrawable) { 553 | stateListDrawable = drawable 554 | stateListDrawable.state = intArrayOf(android.R.attr.state_checked) 555 | stateListDrawable.mutate() 556 | } else { 557 | val selectedDrawable = tintListDrawable(drawable, itemIconTintRes) 558 | selectedDrawable.state = intArrayOf(android.R.attr.state_checked) 559 | stateListDrawable.addState(intArrayOf(android.R.attr.state_checked), selectedDrawable.current) 560 | stateListDrawable.addState(intArrayOf(android.R.attr.state_selected), selectedDrawable.current) 561 | stateListDrawable.addState(intArrayOf(android.R.attr.state_pressed), selectedDrawable.current) 562 | stateListDrawable.addState(intArrayOf(android.R.attr.state_focused), selectedDrawable.current) 563 | selectedDrawable.state = intArrayOf() 564 | stateListDrawable.addState(intArrayOf(), selectedDrawable.current) 565 | } 566 | item.icon = stateListDrawable 567 | } else if ("title" == xmlParser.getAttributeName(i)) { 568 | val titleId = xmlParser.getAttributeResourceValue(i, 0) 569 | if (titleId > 0) { 570 | item.title = resources.getString(titleId) 571 | } else { 572 | item.title = xmlParser.getAttributeValue(i) 573 | } 574 | } else if ("floating" == xmlParser.getAttributeName(i)) { 575 | item.isFloating = xmlParser.getAttributeBooleanValue(i, false) 576 | } else if ("checked" == xmlParser.getAttributeName(i)) { 577 | item.isChecked = xmlParser.getAttributeBooleanValue(i, false) 578 | } else if ("checkable" == xmlParser.getAttributeName(i)) { 579 | item.isCheckable = xmlParser.getAttributeBooleanValue(i, false) 580 | } else if ("showFragment" == xmlParser.getAttributeName(i)) { 581 | item.showFragment = xmlParser.getAttributeBooleanValue(i, true) 582 | } 583 | } 584 | if (item.isCheckable && item.isChecked) { 585 | checkedPosition = itemList.size 586 | } 587 | itemList.add(item) 588 | } 589 | XmlPullParser.TEXT -> { 590 | } 591 | XmlPullParser.END_TAG -> { 592 | if (BuildConfig.DEBUG) { 593 | Log.e(TAG, "xml解析结束") 594 | } 595 | } 596 | else -> { 597 | } 598 | }// Log.e(TAG, "Text:" + xmlParser.getText()); 599 | event = xmlParser.next() //将当前解析器光标往下一步移 600 | } 601 | } catch (e: XmlPullParserException) { 602 | e.printStackTrace() 603 | } catch (e: IOException) { 604 | e.printStackTrace() 605 | } 606 | 607 | } 608 | 609 | 610 | /** 611 | * 当初始化布局以后,进行默认选中 612 | * 613 | * @param changed 614 | * @param left 615 | * @param top 616 | * @param right 617 | * @param bottom 618 | */ 619 | override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { 620 | super.onLayout(changed, left, top, right, bottom) 621 | setSelected(checkedPosition, showPosition) 622 | } 623 | 624 | /** 625 | * 尺寸测量 626 | * 627 | * @param widthMeasureSpec 628 | * @param heightMeasureSpec 629 | */ 630 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { 631 | val specMode = MeasureSpec.getMode(heightMeasureSpec) 632 | mWidth = MeasureSpec.getSize(widthMeasureSpec) 633 | mItemWidth = (mWidth - paddingLeft - paddingRight) / itemList.size 634 | topPadding = paddingTop 635 | bottomPadding = paddingBottom 636 | setTextPaint(titleSize, Color.BLACK) 637 | val textHeight = getTextHeight("首页", mTextPaint) 638 | mHeight = if (specMode == View.MeasureSpec.AT_MOST) { 639 | itemIconHeight = if (itemIconHeight < 50) itemIconHeight else 50 640 | topPadding + bottomPadding + itemIconHeight + textHeight + textTop + itemPadding * 2 641 | } else { 642 | val height = MeasureSpec.getSize(heightMeasureSpec) 643 | this.itemIconHeight = height - topPadding - bottomPadding - textHeight - textTop - itemPadding * 2 644 | height 645 | } 646 | this.itemIconWidth = this.itemIconHeight 647 | mHeight += getFloatingUpHeight() 648 | mItemHeight = mHeight 649 | super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(mHeight, specMode)) 650 | layoutParams = layoutParams 651 | } 652 | 653 | 654 | /** 655 | * 进行绘制 656 | * 657 | * @param canvas 658 | */ 659 | override fun onDraw(canvas: Canvas) { 660 | super.onDraw(canvas) 661 | var floatingUpInt = getFloatingUpHeight() 662 | canvas.drawLine(0f, floatingUpInt.toFloat(), mWidth.toFloat(), floatingUpInt.toFloat(), linePaint!!) 663 | //画背景 664 | drawFloating(canvas) 665 | 666 | val rectInit = Rect() 667 | rectInit.set(0, (floatingUpInt + lineWidth / 2).toInt(), mWidth, mHeight) 668 | bgDrawable!!.bounds = rectInit 669 | bgDrawable!!.draw(canvas) 670 | 671 | //画Floating 672 | //画出所有导航菜单 673 | if (itemList.size > 0) { 674 | for (i in itemList.indices) { 675 | val item = itemList[i] 676 | drawItem(canvas, item, i) 677 | } 678 | } 679 | 680 | } 681 | 682 | /** 683 | * 画出上浮图标的背景 684 | * 685 | * @param canvas 686 | */ 687 | private fun drawFloating(canvas: Canvas) { 688 | if (!floatingEnable) { 689 | return 690 | } 691 | if (itemList.size > 0) { 692 | for (i in itemList.indices) { 693 | val item = itemList[i] 694 | if (item.isFloating) { 695 | var rect = getIconRect(item, i) 696 | val x = (rect.left + rect.right) / 2 697 | val y = (rect.top + rect.bottom) / 2 698 | val paint = createPaint(Color.WHITE, Paint.Style.FILL, 0f) 699 | val r = y - linePaint!!.strokeWidth / 2 700 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 701 | paint.colorFilter = bgDrawable!!.colorFilter 702 | } 703 | linePaint!!.style = Paint.Style.STROKE 704 | canvas.drawCircle(x.toFloat(), y.toFloat(), r, linePaint!!) 705 | paint.style = Paint.Style.FILL 706 | canvas.drawCircle(x.toFloat(), y.toFloat(), r - linePaint!!.strokeWidth / 2, paint) 707 | } 708 | } 709 | } 710 | } 711 | 712 | /** 713 | * 获取内容区域位置 714 | * 715 | */ 716 | private fun getItemRect(item: Item, position: Int): Rect { 717 | val rect = Rect() 718 | rect.left = paddingLeft + position * mItemWidth 719 | if (floatingEnable && item.isFloating) { 720 | rect.top = paddingTop + item.padding + getFloatingUpHeight() / 4 721 | } else { 722 | rect.top = paddingTop + getFloatingUpHeight() + item.padding 723 | } 724 | rect.right = rect.left + mItemWidth 725 | rect.bottom = mItemHeight - paddingBottom - item.padding 726 | return rect 727 | } 728 | 729 | /** 730 | * 画出每一个Item导航菜单 731 | * 732 | * @param canvas 733 | * @param item 734 | */ 735 | private fun drawItem(canvas: Canvas, item: Item?, position: Int) { 736 | if (item == null) { 737 | return 738 | } 739 | var rect = getItemRect(item, position) 740 | if (!TextUtils.isEmpty(item.title)) { 741 | var color = if (item.isChecked) itemColorStateList!!.getColorForState(intArrayOf(android.R.attr.state_checked), itemColorStateList!!.defaultColor) else itemColorStateList!!.defaultColor 742 | if (!item.isCheckable) { 743 | color = itemColorStateList!!.getColorForState(intArrayOf(android.R.attr.state_checked), itemColorStateList!!.defaultColor) 744 | } 745 | setTextPaint(if (item.titleSize == 0) DensityUtils.dpToPx(resources, 14f) else item.titleSize, color) 746 | val textHeight = getTextHeight(item.title, mTextPaint) 747 | val textX = (rect.left + rect.right) / 2 748 | val textY = rect.bottom - textHeight / 4 749 | canvas.drawText(item.title!!, textX.toFloat(), textY.toFloat(), mTextPaint) 750 | } 751 | if (item.icon != null) { 752 | val drawable: Drawable? = getIconDrawable(item, position) 753 | drawable?.let { 754 | it.draw(canvas) 755 | } 756 | } 757 | if (item.msgCount != 0) { 758 | drawItemMsgCount(item, position, canvas) 759 | } 760 | } 761 | 762 | /** 763 | * 画出消息数 764 | */ 765 | private fun drawItemMsgCount(item: Item, position: Int, canvas: Canvas) { 766 | val msgCountRect = getMsgCountRect(item, position) 767 | var x = (msgCountRect.left + msgCountRect.right) / 2 768 | var y = (msgCountRect.top + msgCountRect.bottom) / 2 769 | var r = (msgCountRect.bottom - msgCountRect.top) / 2 770 | 771 | if (item.msgCount > 0) { 772 | var countStr = when { 773 | item.msgCount > 99 -> { 774 | setTextPaint(DensityUtils.dpToPx(resources, 7f), Color.WHITE) 775 | "99+" 776 | } 777 | item.msgCount < 10 -> { 778 | setTextPaint(DensityUtils.dpToPx(resources, 9f), Color.WHITE) 779 | item.msgCount.toString() 780 | } 781 | else -> { 782 | setTextPaint(DensityUtils.dpToPx(resources, 8f), Color.WHITE) 783 | item.msgCount.toString() 784 | } 785 | } 786 | val paint = createPaint(Color.RED, Paint.Style.FILL, 0f) 787 | canvas.drawCircle(x.toFloat(), y.toFloat(), r.toFloat(), paint) 788 | canvas.drawText(countStr, x.toFloat(), (y + (r - DEFAULT_MSG_COUNT_TEXT_PADDING) / 2).toFloat(), mTextPaint) 789 | paint.style = Paint.Style.STROKE 790 | paint.color = Color.WHITE 791 | paint.strokeWidth = DensityUtils.dpToPx(resources, 1f).toFloat() 792 | canvas.drawCircle(x.toFloat(), y.toFloat(), r.toFloat(), paint) 793 | } else { 794 | val paint = createPaint(Color.RED, Paint.Style.FILL, 0f) 795 | canvas.drawCircle(x.toFloat(), y.toFloat(), r.toFloat(), paint) 796 | paint.style = Paint.Style.STROKE 797 | paint.color = Color.WHITE 798 | paint.strokeWidth = DensityUtils.dpToPx(resources, 1f).toFloat() 799 | canvas.drawCircle(x.toFloat(), y.toFloat(), r.toFloat(), paint) 800 | } 801 | } 802 | 803 | private fun getMsgCountRect(item: Item, position: Int): Rect { 804 | val r = if (item.msgCount > 0) { 805 | setTextPaint(DensityUtils.dpToPx(resources, 7f), Color.WHITE) 806 | getTextWidth("99+", mTextPaint!!) / 2 + DEFAULT_MSG_COUNT_TEXT_PADDING 807 | } else { 808 | 7 809 | } 810 | val rect = getIconRect(item, position) 811 | var x = (rect.left + rect.right) / 2 812 | var y = (rect.top + rect.bottom) / 2 813 | var r2 = y - rect.top 814 | val offset = sqrt(r2 * r2 * 1.0 / 2) 815 | val msgRect = Rect() 816 | msgRect.left = (x + offset - r).toInt() 817 | msgRect.top = (y - offset - r).toInt() 818 | if (item.msgCount > 0) { 819 | msgRect.left += r / 3 820 | msgRect.top += r / 3 821 | } 822 | msgRect.right = msgRect.left + 2 * r 823 | msgRect.bottom = msgRect.top + 2 * r 824 | return msgRect 825 | } 826 | 827 | 828 | private fun getFloatingUpHeight(): Int { 829 | if (floatingEnable) { 830 | for (item in itemList) { 831 | if (item.isFloating) { 832 | return floatingUp 833 | } 834 | } 835 | } 836 | return 0 837 | } 838 | 839 | private fun getIconDrawable(item: Item, position: Int): Drawable? { 840 | val drawable: Drawable? = if (item.isCheckable) { 841 | if (item.isChecked) { 842 | item.icon!!.state = intArrayOf(android.R.attr.state_checked) 843 | item.icon!!.current 844 | } else { 845 | item.icon!!.state = intArrayOf() 846 | item.icon!!.current 847 | } 848 | } else { 849 | item.drawable 850 | } 851 | drawable?.let { 852 | it.bounds = getIconRect(item, position) 853 | } 854 | return drawable 855 | } 856 | 857 | /** 858 | * 获取图标的位置 859 | */ 860 | private fun getIconRect(item: Item, position: Int): Rect { 861 | val rect = getItemRect(item, position) 862 | val to = Rect() 863 | val floating = getFloatingUpHeight() 864 | var iconSize = itemIconWidth.coerceAtMost(itemIconHeight) 865 | setTextPaint(titleSize, Color.BLACK) 866 | val textHeight = getTextHeight("首页", mTextPaint!!) 867 | if (TextUtils.isEmpty(item.title)) { 868 | iconSize += (textTop + textHeight) 869 | } 870 | to.top = rect.top 871 | if (floatingEnable && item.isFloating) { 872 | iconSize += floating * 3 / 4 873 | } 874 | iconSize += (itemPadding - item.padding) * 2//高度修复 875 | to.left = (rect.left + rect.right - iconSize) / 2 876 | to.right = to.left + iconSize 877 | to.bottom = to.top + iconSize 878 | return to 879 | } 880 | 881 | 882 | /** 883 | * 创建文字类型的画笔 884 | * 885 | * @param textSize 文字大小 886 | * @param textColor 文字颜色 887 | * @return 888 | */ 889 | private fun setTextPaint(textSize: Int, textColor: Int): Paint { 890 | return mTextPaint?.apply { 891 | this.color = textColor//设置画笔的颜色 892 | this.textSize = textSize.toFloat()//设置文字大小 893 | this.isAntiAlias = true//设置抗锯齿功能 true表示抗锯齿 false则表示不需要这功能 894 | this.textAlign = Paint.Align.CENTER 895 | this.style = Paint.Style.FILL_AND_STROKE 896 | } 897 | } 898 | 899 | /** 900 | * 创建图形的画笔 901 | * 902 | * @param color 画笔颜色 903 | * @return 904 | */ 905 | private fun createPaint(color: Int, style: Paint.Style, strokeWidth: Float): Paint { 906 | return Paint().apply { 907 | this.color = color 908 | this.isAntiAlias = true//设置抗锯齿功能 true表示抗锯齿 false则表示不需要这功能 909 | this.textAlign = Paint.Align.CENTER 910 | this.style = style 911 | this.strokeWidth = strokeWidth 912 | } 913 | } 914 | 915 | 916 | /** 917 | * 获取文字宽度 918 | * 919 | * @param text 文字 920 | * @param paint 画笔 921 | * @return 922 | */ 923 | private fun getTextWidth(text: String, paint: Paint): Int { 924 | val rect = Rect() // 文字所在区域的矩形 925 | paint.getTextBounds(text, 0, text.length, rect) 926 | return rect.width() 927 | } 928 | 929 | /** 930 | * 获取文字高度 931 | * 932 | * @param text 文字 933 | * @param paint 画笔 934 | * @return 935 | */ 936 | private fun getTextHeight(text: String?, paint: Paint): Int { 937 | val rect = Rect() 938 | paint.getTextBounds(text, 0, text!!.length, rect) 939 | return rect.height() 940 | } 941 | 942 | 943 | /** 944 | * 更改图片颜色 945 | * 946 | * @param drawable 947 | * @param colors 948 | * @return 949 | */ 950 | private fun tintListDrawable(drawable: Drawable, colors: ColorStateList?): Drawable { 951 | return if (colors != null) { 952 | val wrappedDrawable = DrawableCompat.wrap(drawable) 953 | DrawableCompat.setTintMode(wrappedDrawable, PorterDuff.Mode.MULTIPLY) 954 | DrawableCompat.setTintList(wrappedDrawable, colors) 955 | wrappedDrawable 956 | } else { 957 | drawable 958 | } 959 | } 960 | 961 | /** 962 | * 触摸事件监听 963 | * 964 | * @param event 965 | * @return 966 | */ 967 | override fun onTouchEvent(event: MotionEvent): Boolean { 968 | val x = event.rawX.toDouble() 969 | var y = event.rawY.toDouble() 970 | //获取控件在屏幕的位置 971 | val location = IntArray(2) 972 | getLocationOnScreen(location) 973 | val locationY = location[1] 974 | y -= locationY 975 | val action = event.action 976 | if (BuildConfig.DEBUG) { 977 | Log.e(TAG, "action = $action") 978 | } 979 | when (action) { 980 | MotionEvent.ACTION_DOWN -> return true 981 | // break; 982 | MotionEvent.ACTION_MOVE -> { 983 | } 984 | MotionEvent.ACTION_UP -> { 985 | for (i in itemList.indices) { 986 | val item = itemList[i] 987 | var startTop = 0 988 | if (!item.isFloating) { 989 | startTop = paddingTop + getFloatingUpHeight() 990 | } 991 | if (x > paddingLeft + mItemWidth * i && x < paddingLeft + mItemWidth * (i + 1)) { 992 | //图片文字内容宽度 993 | val width = mItemHeight - topPadding - bottomPadding 994 | //图片文字内容高度 995 | val height = mItemHeight - topPadding - bottomPadding 996 | if (!TextUtils.isEmpty(item.title)) { 997 | val color = if (item.isChecked) itemColorStateList!!.getColorForState(intArrayOf(android.R.attr.state_checked), itemColorStateList!!.defaultColor) else itemColorStateList!!.defaultColor 998 | setTextPaint(if (item.titleSize == 0) DensityUtils.dpToPx(resources, 14f) else item.titleSize, color) 999 | val textHeight = getTextHeight(item.title, mTextPaint!!) 1000 | val textY = startTop + height - textHeight / 4//上边距+图片文字内容高度 1001 | val w = textY - textHeight / 2 - topPadding 1002 | // width = height = height - textHeight - textTop; 1003 | } 1004 | val centerX = paddingLeft + i * mItemWidth + (mItemWidth - width) / 2 + width / 2 1005 | val centerY = mItemHeight / 2 1006 | val r = mItemHeight / 2 1007 | if (y >= getFloatingUpHeight() || item.isFloating && isInCircle(centerX, centerY, r, x.toInt(), y.toInt())) { 1008 | setSelected(i) 1009 | } 1010 | } 1011 | } 1012 | postInvalidate() 1013 | } 1014 | } 1015 | return super.onTouchEvent(event) 1016 | } 1017 | 1018 | 1019 | /** 1020 | * 判断触摸位置是否在圆形内部 1021 | * 1022 | * @param vCenterX 圆形的 X 坐标 1023 | * @param vCenterY 圆形的 Y 坐标 1024 | * @param r 圆形的半径 1025 | * @param touchX 触摸位置的 X 坐标 1026 | * @param touchY 触摸位置的 Y 坐标 1027 | * @return 1028 | */ 1029 | private fun isInCircle(vCenterX: Int, vCenterY: Int, r: Int, touchX: Int, touchY: Int): Boolean { 1030 | //点击位置x坐标与圆心的x坐标的距离 1031 | val distanceX = Math.abs(vCenterX - touchX) 1032 | //点击位置y坐标与圆心的y坐标的距离 1033 | val distanceY = Math.abs(vCenterY - touchY) 1034 | //点击位置与圆心的直线距离 1035 | val distanceZ = Math.sqrt(Math.pow(distanceX.toDouble(), 2.0) + Math.pow(distanceY.toDouble(), 2.0)).toInt() 1036 | 1037 | //如果点击位置与圆心的距离大于圆的半径,证明点击位置没有在圆内 1038 | return distanceZ <= r 1039 | } 1040 | 1041 | /** 1042 | * 导航菜单Item 的实体 1043 | */ 1044 | inner class Item { 1045 | var showFragment: Boolean = true 1046 | var id: Int = 0 1047 | var icon: StateListDrawable? = null 1048 | var drawable: Drawable? = null 1049 | var title: String? = null 1050 | var titleSize: Int = 0 1051 | var iconWidth: Int = 0 1052 | var iconHeight: Int = 0 1053 | var padding: Int = 0 1054 | var isFloating = false 1055 | var isChecked = false 1056 | var isCheckable = true 1057 | var msgCount = 0 1058 | } 1059 | 1060 | 1061 | internal fun drawable2Bitmap(drawable: Drawable?): Bitmap { 1062 | if (drawable is BitmapDrawable) { 1063 | return drawable.bitmap 1064 | } else if (drawable is NinePatchDrawable) { 1065 | val bitmap = Bitmap 1066 | .createBitmap( 1067 | drawable.intrinsicWidth, 1068 | drawable.intrinsicHeight, 1069 | if (drawable.opacity != PixelFormat.OPAQUE) 1070 | Bitmap.Config.ARGB_8888 1071 | else 1072 | Bitmap.Config.RGB_565) 1073 | val canvas = Canvas(bitmap) 1074 | drawable.setBounds(0, 0, drawable.intrinsicWidth, 1075 | drawable.intrinsicHeight) 1076 | drawable.draw(canvas) 1077 | return bitmap 1078 | } else { 1079 | val bitmap: Bitmap 1080 | if (drawable!!.intrinsicWidth <= 0 || drawable.intrinsicHeight <= 0) { 1081 | bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) // Single color bitmap will be created of 1x1 pixel 1082 | } else { 1083 | bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888) 1084 | } 1085 | val canvas = Canvas(bitmap) 1086 | drawable.setBounds(0, 0, canvas.width, canvas.height) 1087 | drawable.draw(canvas) 1088 | return bitmap 1089 | } 1090 | } 1091 | 1092 | /** 1093 | * 改变Fragment 1094 | * 1095 | * @param to 1096 | */ 1097 | private fun selectFragmentByManager(to: Fragment) { 1098 | if (!isReplace) { 1099 | hiddenFragment(currentFragment, to) 1100 | } else { 1101 | replaceFragment(currentFragment, to) 1102 | } 1103 | 1104 | } 1105 | 1106 | /** 1107 | * 隐藏Fragment 1108 | * 1109 | * @param from 1110 | * @param to 1111 | */ 1112 | private fun hiddenFragment(from: Fragment?, to: Fragment) { 1113 | val transaction = manager!!.beginTransaction() 1114 | if (from != null && from !== to) { 1115 | if (to.isAdded) { 1116 | transaction.hide(from).show(to) 1117 | } else { 1118 | transaction.add(containerView!!.id, to).hide(from).show(to) 1119 | } 1120 | } else if (to != null) { 1121 | transaction.replace(containerView!!.id, to).show(to) 1122 | } 1123 | transaction.commit() 1124 | currentFragment = to 1125 | } 1126 | 1127 | /** 1128 | * 替换Fragment 1129 | * 1130 | * @param from 1131 | * @param to 1132 | */ 1133 | private fun replaceFragment(from: Fragment?, to: Fragment) { 1134 | val transaction = manager!!.beginTransaction() 1135 | if (from != null && from !== to) { 1136 | transaction.remove(from) 1137 | transaction.replace(containerView!!.id, to).show(to) 1138 | } else if (to != null) { 1139 | transaction.replace(containerView!!.id, to).show(to) 1140 | } 1141 | transaction.commit() 1142 | currentFragment = to 1143 | } 1144 | 1145 | 1146 | private class ViewpagerAdapter(fragmentManager: FragmentManager, val fragments: List) : FragmentPagerAdapter(fragmentManager) { 1147 | override fun getCount(): Int { 1148 | return fragments.size 1149 | } 1150 | 1151 | 1152 | override fun getItem(position: Int): Fragment { 1153 | return fragments[position] 1154 | } 1155 | 1156 | 1157 | } 1158 | 1159 | } --------------------------------------------------------------------------------