├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── ids.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── themes.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── drawable-xxhdpi
│ │ │ │ ├── ic_bg_home.webp
│ │ │ │ ├── ic_bg_home_black.png
│ │ │ │ ├── ic_bg_home_bottom.webp
│ │ │ │ ├── ic_home_add_device.png
│ │ │ │ ├── ic_home_title_add.png
│ │ │ │ ├── ic_test_product1.webp
│ │ │ │ ├── ic_test_product2.webp
│ │ │ │ ├── ic_test_product3.webp
│ │ │ │ ├── ic_test_product4.webp
│ │ │ │ ├── ic_test_product5.webp
│ │ │ │ ├── ic_test_product6.webp
│ │ │ │ ├── ic_bg_home_bottom2.webp
│ │ │ │ ├── ic_home_title_notice.png
│ │ │ │ └── ic_home_title_notice_dot.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── drawable
│ │ │ │ ├── shape_color_white_radius_48.xml
│ │ │ │ ├── ripple_radius_48.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── values-night
│ │ │ │ └── themes.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── layout
│ │ │ │ └── activity_main.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── xiaoguang
│ │ │ │ └── widget
│ │ │ │ └── easyscrollview
│ │ │ │ ├── immersionbar
│ │ │ │ ├── ImmersionCallback.java
│ │ │ │ ├── OnBarListener.java
│ │ │ │ ├── OnNavigationBarListener.java
│ │ │ │ ├── OnKeyboardListener.java
│ │ │ │ ├── BarHide.java
│ │ │ │ ├── RequestManagerFragment.java
│ │ │ │ ├── SupportRequestManagerFragment.java
│ │ │ │ ├── Constants.java
│ │ │ │ ├── EMUI3NavigationBarObserver.java
│ │ │ │ ├── BarProperties.java
│ │ │ │ ├── NavigationBarObserver.java
│ │ │ │ ├── ImmersionDelegate.java
│ │ │ │ ├── OSUtils.java
│ │ │ │ ├── BarParams.java
│ │ │ │ ├── FitsKeyboard.java
│ │ │ │ ├── SpecialBarFontUtils.java
│ │ │ │ ├── RequestManagerRetriever.java
│ │ │ │ ├── BarConfig.java
│ │ │ │ └── NotchUtils.java
│ │ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── xiaoguang
│ │ │ └── widget
│ │ │ └── easyscrollview
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── xiaoguang
│ │ └── widget
│ │ └── easyscrollview
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── screenshots
├── top.jpg
├── top1.jpg
├── top2.jpg
├── top3.jpg
└── top4.jpg
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── settings.gradle
├── gradle.properties
├── gradlew.bat
├── README.md
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/screenshots/top.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/screenshots/top.jpg
--------------------------------------------------------------------------------
/screenshots/top1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/screenshots/top1.jpg
--------------------------------------------------------------------------------
/screenshots/top2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/screenshots/top2.jpg
--------------------------------------------------------------------------------
/screenshots/top3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/screenshots/top3.jpg
--------------------------------------------------------------------------------
/screenshots/top4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/screenshots/top4.jpg
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | EasyScrollView
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_bg_home.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/drawable-xxhdpi/ic_bg_home.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | .cxx
10 | local.properties
11 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_bg_home_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/drawable-xxhdpi/ic_bg_home_black.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_bg_home_bottom.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/drawable-xxhdpi/ic_bg_home_bottom.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_home_add_device.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/drawable-xxhdpi/ic_home_add_device.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_home_title_add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/drawable-xxhdpi/ic_home_title_add.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_test_product1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/drawable-xxhdpi/ic_test_product1.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_test_product2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/drawable-xxhdpi/ic_test_product2.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_test_product3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/drawable-xxhdpi/ic_test_product3.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_test_product4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/drawable-xxhdpi/ic_test_product4.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_test_product5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/drawable-xxhdpi/ic_test_product5.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_test_product6.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/drawable-xxhdpi/ic_test_product6.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_bg_home_bottom2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/drawable-xxhdpi/ic_bg_home_bottom2.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_home_title_notice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/drawable-xxhdpi/ic_home_title_notice.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_home_title_notice_dot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITxiaoguang/CoolScrollViewDemo/master/app/src/main/res/drawable-xxhdpi/ic_home_title_notice_dot.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jan 05 11:26:32 CST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_color_white_radius_48.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/ImmersionCallback.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | /**
4 | * The interface Immersion callback.
5 | *
6 | * @author geyifeng
7 | * @date 2019 /4/11 5:04 PM
8 | */
9 | interface ImmersionCallback extends OnNavigationBarListener, Runnable {
10 | }
11 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | //dependencyResolutionManagement {
2 | // repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
3 | // repositories {
4 | // google()
5 | // mavenCentral()
6 | // jcenter() // Warning: this repository is going to shut down soon
7 | // }
8 | //}
9 | //rootProject.name = "EasyScrollView"
10 | include ':app'
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ripple_radius_48.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/OnBarListener.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | /**
4 | * The interface On bar listener.
5 | *
6 | * @author geyifeng
7 | * @date 2019 -05-10 18:22
8 | */
9 | public interface OnBarListener {
10 |
11 | /**
12 | * On bar info change.
13 | *
14 | * @param barProperties the bar info
15 | */
16 | void onBarChange(BarProperties barProperties);
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/test/java/com/xiaoguang/widget/easyscrollview/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview
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)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/OnNavigationBarListener.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | /**
4 | * The interface On navigation bar listener.
5 | *
6 | * @author geyifeng
7 | * @date 2019 /4/10 6:12 PM
8 | */
9 | public interface OnNavigationBarListener {
10 | /**
11 | * On navigation bar change.
12 | *
13 | * @param show the show
14 | */
15 | void onNavigationBarChange(boolean show);
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/OnKeyboardListener.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | /**
4 | * 软键盘监听
5 | *
6 | * @author geyifeng
7 | * @date 2017/8/28
8 | */
9 | public interface OnKeyboardListener {
10 | /**
11 | * On keyboard change.
12 | *
13 | * @param isPopup the is popup 是否弹出
14 | * @param keyboardHeight the keyboard height 软键盘高度
15 | */
16 | void onKeyboardChange(boolean isPopup, int keyboardHeight);
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
11 |
12 | #D8D8D8
13 | #F2F2F2
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/BarHide.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | /**
4 | * bar的状态
5 | *
6 | * @author geyifeng
7 | * @date 2017/4/25
8 | */
9 | public enum BarHide {
10 | /**
11 | * 隐藏状态栏
12 | * Flag hide status bar bar hide.
13 | */
14 | FLAG_HIDE_STATUS_BAR,
15 | /**
16 | * 隐藏导航栏
17 | * Flag hide navigation bar bar hide.
18 | */
19 | FLAG_HIDE_NAVIGATION_BAR,
20 | /**
21 | * 隐藏状态栏和导航栏
22 | * Flag hide bar bar hide.
23 | */
24 | FLAG_HIDE_BAR,
25 | /**
26 | * 显示状态栏和导航栏
27 | * Flag show bar bar hide.
28 | */
29 | FLAG_SHOW_BAR
30 | }
31 |
--------------------------------------------------------------------------------
/app/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
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/xiaoguang/widget/easyscrollview/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.xiaoguang.widget.easyscrollview", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
21 |
22 |
--------------------------------------------------------------------------------
/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 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | id 'kotlin-android-extensions'
5 | }
6 |
7 | android {
8 | compileSdk 31
9 |
10 | defaultConfig {
11 | applicationId "com.xiaoguang.widget.easyscrollview"
12 | minSdk 16
13 | targetSdk 31
14 | versionCode 1
15 | versionName "1.0.0"
16 |
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility JavaVersion.VERSION_1_8
28 | targetCompatibility JavaVersion.VERSION_1_8
29 | }
30 | kotlinOptions {
31 | jvmTarget = '1.8'
32 | }
33 | }
34 |
35 | dependencies {
36 |
37 | implementation 'androidx.core:core-ktx:1.7.0'
38 | implementation 'androidx.appcompat:appcompat:1.4.0'
39 | implementation 'com.google.android.material:material:1.4.0'
40 | implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
41 | testImplementation 'junit:junit:4.+'
42 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
43 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
44 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/RequestManagerFragment.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | import android.app.Activity;
4 | import android.app.Dialog;
5 | import android.app.Fragment;
6 | import android.content.res.Configuration;
7 | import android.os.Bundle;
8 | import androidx.annotation.Nullable;
9 |
10 | /**
11 | * @author geyifeng
12 | * @date 2019/4/11 6:43 PM
13 | */
14 | public final class RequestManagerFragment extends Fragment {
15 |
16 | private ImmersionDelegate mDelegate;
17 |
18 | public ImmersionBar get(Object o) {
19 | if (mDelegate == null) {
20 | mDelegate = new ImmersionDelegate(o);
21 | }
22 | return mDelegate.get();
23 | }
24 |
25 | public ImmersionBar get(Activity activity, Dialog dialog) {
26 | if (mDelegate == null) {
27 | mDelegate = new ImmersionDelegate(activity, dialog);
28 | }
29 | return mDelegate.get();
30 | }
31 |
32 | @Override
33 | public void onActivityCreated(@Nullable Bundle savedInstanceState) {
34 | super.onActivityCreated(savedInstanceState);
35 | if (mDelegate != null) {
36 | mDelegate.onActivityCreated(getResources().getConfiguration());
37 | }
38 | }
39 |
40 | @Override
41 | public void onResume() {
42 | super.onResume();
43 | if (mDelegate != null) {
44 | mDelegate.onResume();
45 | }
46 | }
47 |
48 | @Override
49 | public void onDestroy() {
50 | super.onDestroy();
51 | if (mDelegate != null) {
52 | mDelegate.onDestroy();
53 | mDelegate = null;
54 | }
55 | }
56 |
57 | @Override
58 | public void onConfigurationChanged(Configuration newConfig) {
59 | super.onConfigurationChanged(newConfig);
60 | if (mDelegate != null) {
61 | mDelegate.onConfigurationChanged(newConfig);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/SupportRequestManagerFragment.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | import android.app.Activity;
4 | import android.app.Dialog;
5 | import android.content.res.Configuration;
6 | import android.os.Bundle;
7 | import androidx.annotation.Nullable;
8 | import androidx.fragment.app.Fragment;
9 |
10 | /**
11 | * @author geyifeng
12 | * @date 2019/4/11 6:43 PM
13 | */
14 | public final class SupportRequestManagerFragment extends Fragment {
15 |
16 | private ImmersionDelegate mDelegate;
17 |
18 | public ImmersionBar get(Object o) {
19 | if (mDelegate == null) {
20 | mDelegate = new ImmersionDelegate(o);
21 | }
22 | return mDelegate.get();
23 | }
24 |
25 | public ImmersionBar get(Activity activity, Dialog dialog) {
26 | if (mDelegate == null) {
27 | mDelegate = new ImmersionDelegate(activity, dialog);
28 | }
29 | return mDelegate.get();
30 | }
31 |
32 | @Override
33 | public void onActivityCreated(@Nullable Bundle savedInstanceState) {
34 | super.onActivityCreated(savedInstanceState);
35 | if (mDelegate != null) {
36 | mDelegate.onActivityCreated(getResources().getConfiguration());
37 | }
38 | }
39 |
40 | @Override
41 | public void onResume() {
42 | super.onResume();
43 | if (mDelegate != null) {
44 | mDelegate.onResume();
45 | }
46 | }
47 |
48 | @Override
49 | public void onDestroy() {
50 | super.onDestroy();
51 | if (mDelegate != null) {
52 | mDelegate.onDestroy();
53 | mDelegate = null;
54 | }
55 | }
56 |
57 | @Override
58 | public void onConfigurationChanged(Configuration newConfig) {
59 | super.onConfigurationChanged(newConfig);
60 | if (mDelegate != null) {
61 | mDelegate.onConfigurationChanged(newConfig);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/Constants.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | import com.xiaoguang.widget.easyscrollview.R;
4 |
5 | /**
6 | * @author geyifeng
7 | * @date 2019/4/12 6:39 PM
8 | */
9 | class Constants {
10 | /**
11 | * android 4.4或者emui3状态栏ID识位
12 | */
13 | static final int IMMERSION_ID_STATUS_BAR_VIEW = R.id.immersion_status_bar_view;
14 | /**
15 | * android 4.4或者emui3导航栏ID识位
16 | */
17 | static final int IMMERSION_ID_NAVIGATION_BAR_VIEW = R.id.immersion_navigation_bar_view;
18 | /**
19 | * 状态栏高度标识位
20 | */
21 | static final String IMMERSION_STATUS_BAR_HEIGHT = "status_bar_height";
22 | /**
23 | * 导航栏竖屏高度标识位
24 | */
25 | static final String IMMERSION_NAVIGATION_BAR_HEIGHT = "navigation_bar_height";
26 | /**
27 | * 导航栏横屏高度标识位
28 | */
29 | static final String IMMERSION_NAVIGATION_BAR_HEIGHT_LANDSCAPE = "navigation_bar_height_landscape";
30 | /**
31 | * 导航栏宽度标识位位
32 | */
33 | static final String IMMERSION_NAVIGATION_BAR_WIDTH = "navigation_bar_width";
34 | /**
35 | * MIUI导航栏显示隐藏标识位
36 | */
37 | static final String IMMERSION_MIUI_NAVIGATION_BAR_HIDE_SHOW = "force_fsg_nav_bar";
38 | /**
39 | * EMUI导航栏显示隐藏标识位
40 | */
41 | static final String IMMERSION_EMUI_NAVIGATION_BAR_HIDE_SHOW = "navigationbar_is_min";
42 | /**
43 | * MIUI状态栏字体黑色与白色标识位
44 | */
45 | static final String IMMERSION_MIUI_STATUS_BAR_DARK = "EXTRA_FLAG_STATUS_BAR_DARK_MODE";
46 | /**
47 | * MIUI导航栏图标黑色与白色标识位
48 | */
49 | static final String IMMERSION_MIUI_NAVIGATION_BAR_DARK = "EXTRA_FLAG_NAVIGATION_BAR_DARK_MODE";
50 |
51 | /**
52 | * 自动改变字体颜色的临界值标识位
53 | */
54 | static final int IMMERSION_BOUNDARY_COLOR = 0xFFBABABA;
55 |
56 | /**
57 | * 修复状态栏与布局重叠标识位,默认不修复
58 | */
59 | static final int FLAG_FITS_DEFAULT = 0X00;
60 | /**
61 | * 修复状态栏与布局重叠标识位,使用titleBar方法修复
62 | */
63 | static final int FLAG_FITS_TITLE = 0X01;
64 | /**
65 | * 修复状态栏与布局重叠标识位,使用titleBarMarginTop方法修复
66 | */
67 | static final int FLAG_FITS_TITLE_MARGIN_TOP = 0X02;
68 | /**
69 | * 修复状态栏与布局重叠标识位,使用StatusBarView方法修复
70 | */
71 | static final int FLAG_FITS_STATUS = 0X03;
72 | /**
73 | * 修复状态栏与布局重叠标识位,使用fitsSystemWindows方法修复
74 | */
75 | static final int FLAG_FITS_SYSTEM_WINDOWS = 0X04;
76 | }
77 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/EMUI3NavigationBarObserver.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | import android.app.Application;
4 | import android.database.ContentObserver;
5 | import android.net.Uri;
6 | import android.os.Build;
7 | import android.os.Handler;
8 | import android.os.Looper;
9 | import android.provider.Settings;
10 |
11 | import java.util.ArrayList;
12 |
13 | import static com.xiaoguang.widget.easyscrollview.immersionbar.Constants.IMMERSION_EMUI_NAVIGATION_BAR_HIDE_SHOW;
14 |
15 | /**
16 | * 华为Emui3状态栏监听器
17 | *
18 | * @author geyifeng
19 | * @date 2019/4/10 6:02 PM
20 | */
21 | final class EMUI3NavigationBarObserver extends ContentObserver {
22 |
23 | private ArrayList mCallbacks;
24 | private Application mApplication;
25 | private Boolean mIsRegister = false;
26 |
27 | static EMUI3NavigationBarObserver getInstance() {
28 | return NavigationBarObserverInstance.INSTANCE;
29 | }
30 |
31 | private EMUI3NavigationBarObserver() {
32 | super(new Handler(Looper.getMainLooper()));
33 | }
34 |
35 | void register(Application application) {
36 | this.mApplication = application;
37 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && mApplication != null
38 | && mApplication.getContentResolver() != null && !mIsRegister) {
39 | Uri uri = Settings.System.getUriFor(IMMERSION_EMUI_NAVIGATION_BAR_HIDE_SHOW);
40 | if (uri != null) {
41 | mApplication.getContentResolver().registerContentObserver(uri, true, this);
42 | mIsRegister = true;
43 | }
44 | }
45 | }
46 |
47 | @Override
48 | public void onChange(boolean selfChange) {
49 | super.onChange(selfChange);
50 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && mApplication != null && mApplication.getContentResolver() != null
51 | && mCallbacks != null && !mCallbacks.isEmpty()) {
52 | int show = Settings.System.getInt(mApplication.getContentResolver(), IMMERSION_EMUI_NAVIGATION_BAR_HIDE_SHOW, 0);
53 | for (ImmersionCallback callback : mCallbacks) {
54 | callback.onNavigationBarChange(show != 1);
55 | }
56 | }
57 | }
58 |
59 | void addOnNavigationBarListener(ImmersionCallback callback) {
60 | if (callback == null) {
61 | return;
62 | }
63 | if (mCallbacks == null) {
64 | mCallbacks = new ArrayList<>();
65 | }
66 | if (!mCallbacks.contains(callback)) {
67 | mCallbacks.add(callback);
68 | }
69 | }
70 |
71 | void removeOnNavigationBarListener(ImmersionCallback callback) {
72 | if (callback == null || mCallbacks == null) {
73 | return;
74 | }
75 | mCallbacks.remove(callback);
76 | }
77 |
78 | private static class NavigationBarObserverInstance {
79 | private static final EMUI3NavigationBarObserver INSTANCE = new EMUI3NavigationBarObserver();
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/BarProperties.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | /**
4 | * Bar相关信息
5 | *
6 | * @author geyifeng
7 | * @date 2019-05-10 18:43
8 | */
9 | public class BarProperties {
10 |
11 | /**
12 | * 是否是竖屏
13 | */
14 | private boolean portrait;
15 | /**
16 | * 是否是左横屏
17 | */
18 | private boolean landscapeLeft;
19 | /**
20 | * 是否是右横屏
21 | */
22 | private boolean landscapeRight;
23 | /**
24 | * 是否是刘海屏
25 | */
26 | private boolean notchScreen;
27 | /**
28 | * 是否有导航栏
29 | */
30 | private boolean hasNavigationBar;
31 | /**
32 | * 状态栏高度,刘海屏横竖屏有可能状态栏高度不一样
33 | */
34 | private int statusBarHeight;
35 | /**
36 | * 导航栏高度
37 | */
38 | private int navigationBarHeight;
39 | /**
40 | * 导航栏宽度
41 | */
42 | private int navigationBarWidth;
43 | /**
44 | * 刘海屏高度
45 | */
46 | private int notchHeight;
47 | /**
48 | * ActionBar高度
49 | */
50 | private int actionBarHeight;
51 |
52 | public boolean isPortrait() {
53 | return portrait;
54 | }
55 |
56 | void setPortrait(boolean portrait) {
57 | this.portrait = portrait;
58 | }
59 |
60 | public boolean isLandscapeLeft() {
61 | return landscapeLeft;
62 | }
63 |
64 | void setLandscapeLeft(boolean landscapeLeft) {
65 | this.landscapeLeft = landscapeLeft;
66 | }
67 |
68 | public boolean isLandscapeRight() {
69 | return landscapeRight;
70 | }
71 |
72 | void setLandscapeRight(boolean landscapeRight) {
73 | this.landscapeRight = landscapeRight;
74 | }
75 |
76 | public boolean isNotchScreen() {
77 | return notchScreen;
78 | }
79 |
80 | void setNotchScreen(boolean notchScreen) {
81 | this.notchScreen = notchScreen;
82 | }
83 |
84 | public boolean hasNavigationBar() {
85 | return hasNavigationBar;
86 | }
87 |
88 | void setNavigationBar(boolean hasNavigationBar) {
89 | this.hasNavigationBar = hasNavigationBar;
90 | }
91 |
92 | public int getStatusBarHeight() {
93 | return statusBarHeight;
94 | }
95 |
96 | void setStatusBarHeight(int statusBarHeight) {
97 | this.statusBarHeight = statusBarHeight;
98 | }
99 |
100 | public int getNavigationBarHeight() {
101 | return navigationBarHeight;
102 | }
103 |
104 | void setNavigationBarHeight(int navigationBarHeight) {
105 | this.navigationBarHeight = navigationBarHeight;
106 | }
107 |
108 | public int getNavigationBarWidth() {
109 | return navigationBarWidth;
110 | }
111 |
112 | void setNavigationBarWidth(int navigationBarWidth) {
113 | this.navigationBarWidth = navigationBarWidth;
114 | }
115 |
116 | public int getNotchHeight() {
117 | return notchHeight;
118 | }
119 |
120 | void setNotchHeight(int notchHeight) {
121 | this.notchHeight = notchHeight;
122 | }
123 |
124 | public int getActionBarHeight() {
125 | return actionBarHeight;
126 | }
127 |
128 | void setActionBarHeight(int actionBarHeight) {
129 | this.actionBarHeight = actionBarHeight;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/NavigationBarObserver.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | import android.app.Application;
4 | import android.database.ContentObserver;
5 | import android.net.Uri;
6 | import android.os.Build;
7 | import android.os.Handler;
8 | import android.os.Looper;
9 | import android.provider.Settings;
10 |
11 | import java.util.ArrayList;
12 |
13 | import static com.xiaoguang.widget.easyscrollview.immersionbar.Constants.IMMERSION_EMUI_NAVIGATION_BAR_HIDE_SHOW;
14 | import static com.xiaoguang.widget.easyscrollview.immersionbar.Constants.IMMERSION_MIUI_NAVIGATION_BAR_HIDE_SHOW;
15 |
16 | /**
17 | * 导航栏显示隐藏处理,目前只支持emui和miui带有导航栏的手机
18 | *
19 | * @author geyifeng
20 | * @date 2019/4/10 6:02 PM
21 | */
22 | final class NavigationBarObserver extends ContentObserver {
23 |
24 | private ArrayList mListeners;
25 | private Application mApplication;
26 | private Boolean mIsRegister = false;
27 |
28 | static NavigationBarObserver getInstance() {
29 | return NavigationBarObserverInstance.INSTANCE;
30 | }
31 |
32 | private NavigationBarObserver() {
33 | super(new Handler(Looper.getMainLooper()));
34 | }
35 |
36 | void register(Application application) {
37 | this.mApplication = application;
38 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && mApplication != null
39 | && mApplication.getContentResolver() != null && !mIsRegister) {
40 | Uri uri = null;
41 | if (OSUtils.isMIUI()) {
42 | uri = Settings.Global.getUriFor(IMMERSION_MIUI_NAVIGATION_BAR_HIDE_SHOW);
43 | } else if (OSUtils.isEMUI()) {
44 | if (OSUtils.isEMUI3_x() || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
45 | uri = Settings.System.getUriFor(IMMERSION_EMUI_NAVIGATION_BAR_HIDE_SHOW);
46 | } else {
47 | uri = Settings.Global.getUriFor(IMMERSION_EMUI_NAVIGATION_BAR_HIDE_SHOW);
48 | }
49 | }
50 | if (uri != null) {
51 | mApplication.getContentResolver().registerContentObserver(uri, true, this);
52 | mIsRegister = true;
53 | }
54 | }
55 | }
56 |
57 | @Override
58 | public void onChange(boolean selfChange) {
59 | super.onChange(selfChange);
60 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && mApplication != null && mApplication.getContentResolver() != null
61 | && mListeners != null && !mListeners.isEmpty()) {
62 | int show = 0;
63 | if (OSUtils.isMIUI()) {
64 | show = Settings.Global.getInt(mApplication.getContentResolver(), IMMERSION_MIUI_NAVIGATION_BAR_HIDE_SHOW, 0);
65 | } else if (OSUtils.isEMUI()) {
66 | if (OSUtils.isEMUI3_x() || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
67 | show = Settings.System.getInt(mApplication.getContentResolver(), IMMERSION_EMUI_NAVIGATION_BAR_HIDE_SHOW, 0);
68 | } else {
69 | show = Settings.Global.getInt(mApplication.getContentResolver(), IMMERSION_EMUI_NAVIGATION_BAR_HIDE_SHOW, 0);
70 | }
71 | }
72 | for (OnNavigationBarListener onNavigationBarListener : mListeners) {
73 | onNavigationBarListener.onNavigationBarChange(show != 1);
74 | }
75 | }
76 | }
77 |
78 | void addOnNavigationBarListener(OnNavigationBarListener listener) {
79 | if (listener == null) {
80 | return;
81 | }
82 | if (mListeners == null) {
83 | mListeners = new ArrayList<>();
84 | }
85 | if (!mListeners.contains(listener)) {
86 | mListeners.add(listener);
87 | }
88 | }
89 |
90 | void removeOnNavigationBarListener(OnNavigationBarListener listener) {
91 | if (listener == null || mListeners == null) {
92 | return;
93 | }
94 | mListeners.remove(listener);
95 | }
96 |
97 | private static class NavigationBarObserverInstance {
98 | private static final NavigationBarObserver INSTANCE = new NavigationBarObserver();
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CoolScrollViewDemo-一个炫酷的ScrollView
2 |
3 | # 给你一个性能超高,超简单的滑动列表渐变UI思路。
4 |
5 | ## 项目演示
6 |
7 |
8 |

9 |

10 |

11 |

12 |

13 |
14 |
15 | ### 核心代码
16 |
17 | ``` xml
18 |
20 |
27 |
28 |
29 |
35 |
36 |
37 |
50 | ```
51 |
52 | ``` kotlin
53 | private fun initSize() {
54 | statusBarHeight = ImmersionBar.getStatusBarHeight(this)
55 | val textMaxSize: IntArray = measureView(tv_home)
56 | // 得到文字宽度
57 | textMaxWidth = textMaxSize[0]
58 | // 得到文字高度
59 | textMAxHeight = textMaxSize[1]
60 | // 文字距离左侧 = 24dp
61 | DP24 = dp2px(this, 24)
62 | // 文字距离顶部 = 58dp + statusBarHeight
63 | DP58 = dp2px(this, 58)
64 |
65 | // 变化的X轴 = 屏幕宽度 / 2 - 文字宽度 / 2 - marginStart24
66 | textChangeX =
67 | getScreenWidth(this) / 2 - textMaxWidth / 2 - DP24
68 | // 变化的Y轴 = 高度58 - (toolbar高度22 - 文字高度) / 2
69 | textChangeY = DP58 - (dp2px(this, 44) - textMAxHeight) / 2
70 | // 每个item宽高 = (屏幕宽度 - margin8 * 3) / 2
71 | deviceItemSize = (getScreenWidth(this) - dp2px(this, 8 * 3)) / 2
72 | // 滑动变化颜色的距离 = 图片高度276 - CardView穿透上去的83 - toolbar高度44
73 | scrollHeight = dp2px(this, 276 - 83 - 44) - statusBarHeight
74 | }
75 |
76 | private fun initListener() {
77 | nsv_home.setOnScrollChangeListener { _: NestedScrollView?, _: Int, scrollY: Int, _: Int, _: Int ->
78 | when {
79 | // 在顶部
80 | scrollY <= 0 -> {
81 | // 标题栏全透明
82 | v_title_bar.alpha = 0f
83 | // 图片蒙版全透明
84 | iv_home_bg_black.alpha = 0f
85 | // 文字大小(28sp)还原
86 | tv_home.scaleX = 1f
87 | tv_home.scaleY = 1f
88 | // 文字x,y轴便宜
89 | tv_home.x = DP24.toFloat()
90 | tv_home.y = (DP58 + statusBarHeight).toFloat()
91 | }
92 | // 在scrollHeight范围内
93 | scrollY < scrollHeight -> {
94 | // 标题栏全透明
95 | v_title_bar.alpha = 0f
96 | // 滑动百分比
97 | val scale: Float = scrollY.toFloat() / scrollHeight
98 | // 图片蒙版透明度跟随滑动变化
99 | iv_home_bg_black.alpha = scale
100 | // 文字大小跟随滑动变化 原始大小(28sp)到18sp之间变化
101 | tv_home.scaleX = 1 - scale * (1 - SIZE18)
102 | tv_home.scaleY = 1 - scale * (1 - SIZE18)
103 | val changeX = (scale * textChangeX).toInt()
104 | val changeY = (scale * textChangeY).toInt()
105 | // 文字x,y轴偏移,原始位置到居中之间的变化
106 | tv_home.x = (DP24 + changeX).toFloat()
107 | tv_home.y = (DP58 + statusBarHeight - changeY).toFloat()
108 | }
109 | // 在划出scrollHeight范围
110 | else -> {
111 | // 标题栏全黑
112 | v_title_bar.alpha = 1.0f
113 | // 图片蒙版全黑
114 | iv_home_bg_black.alpha = 1.0f
115 | // 文字大小改成18sp
116 | tv_home.scaleX = SIZE18
117 | tv_home.scaleY = SIZE18
118 | // 文字x,y轴偏移,即居中显示
119 | tv_home.x = (DP24 + textChangeX).toFloat()
120 | tv_home.y = (DP58 + statusBarHeight - textChangeY).toFloat()
121 | }
122 | }
123 | }
124 | }
125 | ```
126 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/ImmersionDelegate.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | import android.app.Activity;
4 | import android.app.Dialog;
5 | import android.content.res.Configuration;
6 | import android.os.Build;
7 | import androidx.fragment.app.DialogFragment;
8 | import androidx.fragment.app.Fragment;
9 | import android.view.Surface;
10 |
11 | /**
12 | * @author geyifeng
13 | * @date 2019/4/12 4:01 PM
14 | */
15 | class ImmersionDelegate implements Runnable {
16 |
17 | private ImmersionBar mImmersionBar;
18 | private BarProperties mBarProperties;
19 | private OnBarListener mOnBarListener;
20 | private int mNotchHeight;
21 |
22 | ImmersionDelegate(Object o) {
23 | if (o instanceof Activity) {
24 | if (mImmersionBar == null) {
25 | mImmersionBar = new ImmersionBar((Activity) o);
26 | }
27 | } else if (o instanceof Fragment) {
28 | if (mImmersionBar == null) {
29 | if (o instanceof DialogFragment) {
30 | mImmersionBar = new ImmersionBar((DialogFragment) o);
31 | } else {
32 | mImmersionBar = new ImmersionBar((Fragment) o);
33 | }
34 | }
35 | } else if (o instanceof android.app.Fragment) {
36 | if (mImmersionBar == null) {
37 | if (o instanceof android.app.DialogFragment) {
38 | mImmersionBar = new ImmersionBar((android.app.DialogFragment) o);
39 | } else {
40 | mImmersionBar = new ImmersionBar((android.app.Fragment) o);
41 | }
42 | }
43 | }
44 | }
45 |
46 | ImmersionDelegate(Activity activity, Dialog dialog) {
47 | if (mImmersionBar == null) {
48 | mImmersionBar = new ImmersionBar(activity, dialog);
49 | }
50 | }
51 |
52 | public ImmersionBar get() {
53 | return mImmersionBar;
54 | }
55 |
56 | void onActivityCreated(Configuration configuration) {
57 | barChanged(configuration);
58 | }
59 |
60 | void onResume() {
61 | if (mImmersionBar != null) {
62 | mImmersionBar.onResume();
63 | }
64 | }
65 |
66 | void onDestroy() {
67 | mBarProperties = null;
68 | if (mImmersionBar != null) {
69 | mImmersionBar.onDestroy();
70 | mImmersionBar = null;
71 | }
72 | }
73 |
74 | void onConfigurationChanged(Configuration newConfig) {
75 | if (mImmersionBar != null) {
76 | mImmersionBar.onConfigurationChanged(newConfig);
77 | barChanged(newConfig);
78 | }
79 | }
80 |
81 | /**
82 | * 横竖屏切换监听
83 | * Orientation change.
84 | *
85 | * @param configuration the configuration
86 | */
87 | private void barChanged(Configuration configuration) {
88 | if (mImmersionBar != null && mImmersionBar.initialized() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
89 | mOnBarListener = mImmersionBar.getBarParams().onBarListener;
90 | if (mOnBarListener != null) {
91 | final Activity activity = mImmersionBar.getActivity();
92 | if (mBarProperties == null) {
93 | mBarProperties = new BarProperties();
94 | }
95 | mBarProperties.setPortrait(configuration.orientation == Configuration.ORIENTATION_PORTRAIT);
96 | int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
97 | if (rotation == Surface.ROTATION_90) {
98 | mBarProperties.setLandscapeLeft(true);
99 | mBarProperties.setLandscapeRight(false);
100 | } else if (rotation == Surface.ROTATION_270) {
101 | mBarProperties.setLandscapeLeft(false);
102 | mBarProperties.setLandscapeRight(true);
103 | } else {
104 | mBarProperties.setLandscapeLeft(false);
105 | mBarProperties.setLandscapeRight(false);
106 | }
107 | activity.getWindow().getDecorView().post(this);
108 | }
109 | }
110 | }
111 |
112 | @Override
113 | public void run() {
114 | if (mImmersionBar != null && mImmersionBar.getActivity() != null) {
115 | Activity activity = mImmersionBar.getActivity();
116 | BarConfig barConfig = new BarConfig(activity);
117 | mBarProperties.setStatusBarHeight(barConfig.getStatusBarHeight());
118 | mBarProperties.setNavigationBar(barConfig.hasNavigationBar());
119 | mBarProperties.setNavigationBarHeight(barConfig.getNavigationBarHeight());
120 | mBarProperties.setNavigationBarWidth(barConfig.getNavigationBarWidth());
121 | mBarProperties.setActionBarHeight(barConfig.getActionBarHeight());
122 | boolean notchScreen = NotchUtils.hasNotchScreen(activity);
123 | mBarProperties.setNotchScreen(notchScreen);
124 | if (notchScreen && mNotchHeight == 0) {
125 | mNotchHeight = NotchUtils.getNotchHeight(activity);
126 | mBarProperties.setNotchHeight(mNotchHeight);
127 | }
128 | mOnBarListener.onBarChange(mBarProperties);
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
19 |
20 |
24 |
25 |
30 |
31 |
33 |
40 |
41 |
42 |
43 |
44 |
49 |
50 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
71 |
72 |
76 |
77 |
78 |
84 |
85 |
90 |
91 |
102 |
103 |
116 |
117 |
118 |
119 |
120 |
133 |
134 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/OSUtils.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.text.TextUtils;
5 |
6 | import java.lang.reflect.Method;
7 |
8 | /**
9 | * 手机系统判断
10 | *
11 | * @author geyifeng
12 | * @date 2017/4/18
13 | */
14 | public class OSUtils {
15 |
16 | private static final String KEY_MIUI_VERSION_NAME = "ro.miui.ui.version.name";
17 | private static final String KEY_EMUI_VERSION_NAME = "ro.build.version.emui";
18 | private static final String KEY_DISPLAY = "ro.build.display.id";
19 |
20 | /**
21 | * 判断是否为miui
22 | * Is miui boolean.
23 | *
24 | * @return the boolean
25 | */
26 | public static boolean isMIUI() {
27 | String property = getSystemProperty(KEY_MIUI_VERSION_NAME, "");
28 | return !TextUtils.isEmpty(property);
29 | }
30 |
31 | /**
32 | * 判断miui版本是否大于等于6
33 | * Is miui 6 later boolean.
34 | *
35 | * @return the boolean
36 | */
37 | public static boolean isMIUI6Later() {
38 | String version = getMIUIVersion();
39 | int num;
40 | if ((!version.isEmpty())) {
41 | try {
42 | num = Integer.valueOf(version.substring(1));
43 | return num >= 6;
44 | } catch (NumberFormatException e) {
45 | return false;
46 | }
47 | } else
48 | return false;
49 | }
50 |
51 | /**
52 | * 获得miui的版本
53 | * Gets miui version.
54 | *
55 | * @return the miui version
56 | */
57 | public static String getMIUIVersion() {
58 | return isMIUI() ? getSystemProperty(KEY_MIUI_VERSION_NAME, "") : "";
59 | }
60 |
61 | /**
62 | * 判断是否为emui
63 | * Is emui boolean.
64 | *
65 | * @return the boolean
66 | */
67 | public static boolean isEMUI() {
68 | String property = getSystemProperty(KEY_EMUI_VERSION_NAME, "");
69 | return !TextUtils.isEmpty(property);
70 | }
71 |
72 | /**
73 | * 得到emui的版本
74 | * Gets emui version.
75 | *
76 | * @return the emui version
77 | */
78 | public static String getEMUIVersion() {
79 | return isEMUI() ? getSystemProperty(KEY_EMUI_VERSION_NAME, "") : "";
80 | }
81 |
82 | /**
83 | * 判断是否为emui3.1版本
84 | * Is emui 3 1 boolean.
85 | *
86 | * @return the boolean
87 | */
88 | public static boolean isEMUI3_1() {
89 | String property = getEMUIVersion();
90 | if ("EmotionUI 3".equals(property) || property.contains("EmotionUI_3.1")) {
91 | return true;
92 | }
93 | return false;
94 | }
95 |
96 | /**
97 | * 判断是否为emui3.0版本
98 | * Is emui 3 1 boolean.
99 | *
100 | * @return the boolean
101 | */
102 | public static boolean isEMUI3_0() {
103 | String property = getEMUIVersion();
104 | if (property.contains("EmotionUI_3.0")) {
105 | return true;
106 | }
107 | return false;
108 | }
109 |
110 | /**
111 | * 判断是否为emui3.x版本
112 | * Is emui 3 x boolean.
113 | *
114 | * @return the boolean
115 | */
116 | public static boolean isEMUI3_x() {
117 | return OSUtils.isEMUI3_0() || OSUtils.isEMUI3_1();
118 | }
119 |
120 | /**
121 | * 判断是否为flymeOS
122 | * Is flyme os boolean.
123 | *
124 | * @return the boolean
125 | */
126 | public static boolean isFlymeOS() {
127 | return getFlymeOSFlag().toLowerCase().contains("flyme");
128 | }
129 |
130 | /**
131 | * 判断flymeOS的版本是否大于等于4
132 | * Is flyme os 4 later boolean.
133 | *
134 | * @return the boolean
135 | */
136 | public static boolean isFlymeOS4Later() {
137 | String version = getFlymeOSVersion();
138 | int num;
139 | if (!version.isEmpty()) {
140 | try {
141 | if (version.toLowerCase().contains("os")) {
142 | num = Integer.valueOf(version.substring(9, 10));
143 | } else {
144 | num = Integer.valueOf(version.substring(6, 7));
145 | }
146 | return num >= 4;
147 | } catch (NumberFormatException e) {
148 | return false;
149 | }
150 | }
151 | return false;
152 | }
153 |
154 | /**
155 | * 判断flymeOS的版本是否等于5
156 | * Is flyme os 5 boolean.
157 | *
158 | * @return the boolean
159 | */
160 | public static boolean isFlymeOS5() {
161 | String version = getFlymeOSVersion();
162 | int num;
163 | if (!version.isEmpty()) {
164 | try {
165 | if (version.toLowerCase().contains("os")) {
166 | num = Integer.valueOf(version.substring(9, 10));
167 | } else {
168 | num = Integer.valueOf(version.substring(6, 7));
169 | }
170 | return num == 5;
171 | } catch (NumberFormatException e) {
172 | return false;
173 | }
174 | }
175 | return false;
176 | }
177 |
178 |
179 | /**
180 | * 得到flymeOS的版本
181 | * Gets flyme os version.
182 | *
183 | * @return the flyme os version
184 | */
185 | public static String getFlymeOSVersion() {
186 | return isFlymeOS() ? getSystemProperty(KEY_DISPLAY, "") : "";
187 | }
188 |
189 | private static String getFlymeOSFlag() {
190 | return getSystemProperty(KEY_DISPLAY, "");
191 | }
192 |
193 | private static String getSystemProperty(String key, String defaultValue) {
194 | try {
195 | @SuppressLint("PrivateApi") Class> clz = Class.forName("android.os.SystemProperties");
196 | Method method = clz.getMethod("get", String.class, String.class);
197 | return (String) method.invoke(clz, key, defaultValue);
198 | } catch (Exception e) {
199 | e.printStackTrace();
200 | }
201 | return defaultValue;
202 | }
203 | }
204 |
--------------------------------------------------------------------------------
/app/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 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/BarParams.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | import android.graphics.Color;
4 | import android.view.View;
5 | import android.view.WindowManager;
6 |
7 | import androidx.annotation.ColorInt;
8 | import androidx.annotation.FloatRange;
9 |
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 | /**
14 | * 沉浸式参数信息
15 | *
16 | * @author geyifeng
17 | * @date 2017 /5/9
18 | */
19 | public class BarParams implements Cloneable {
20 | /**
21 | * 状态栏颜色
22 | * The Status bar color.
23 | */
24 | @ColorInt
25 | public int statusBarColor = Color.TRANSPARENT;
26 | /**
27 | * 导航栏颜色
28 | * The Navigation bar color.
29 | */
30 | @ColorInt
31 | public int navigationBarColor = Color.BLACK;
32 |
33 | /**
34 | * The Default navigation bar color.
35 | */
36 | public int defaultNavigationBarColor = Color.BLACK;
37 | /**
38 | * 状态栏透明度
39 | * The Status bar alpha.
40 | */
41 | @FloatRange(from = 0f, to = 1f)
42 | public float statusBarAlpha = 0.0f;
43 |
44 | @FloatRange(from = 0f, to = 1f)
45 | public float statusBarTempAlpha = 0.0f;
46 | /**
47 | * 导航栏透明度
48 | * The Navigation bar alpha.
49 | */
50 | @FloatRange(from = 0f, to = 1f)
51 | public float navigationBarAlpha = 0.0f;
52 | @FloatRange(from = 0f, to = 1f)
53 | public float navigationBarTempAlpha = 0.0f;
54 | /**
55 | * 有导航栏的情况,全屏显示
56 | * The Full screen.
57 | */
58 | public boolean fullScreen = false;
59 | /**
60 | * 是否隐藏了导航栏
61 | * The Hide navigation bar.
62 | */
63 | public boolean hideNavigationBar = false;
64 | /**
65 | * 隐藏Bar
66 | * The Bar hide.
67 | */
68 | public BarHide barHide = BarHide.FLAG_SHOW_BAR;
69 | /**
70 | * 状态栏字体深色与亮色标志位
71 | * The Dark font.
72 | */
73 | public boolean statusBarDarkFont = false;
74 |
75 | /**
76 | * 导航栏图标深色与亮色标志位
77 | * The Navigation bar dark icon.
78 | */
79 | public boolean navigationBarDarkIcon = false;
80 | /**
81 | * 是否启用 自动根据StatusBar颜色调整深色模式与亮色模式
82 | * The Auto status bar dark mode enable.
83 | */
84 | public boolean autoStatusBarDarkModeEnable = false;
85 |
86 | /**
87 | * 是否启用 自动根据NavigationBar颜色调整深色模式与亮色模式
88 | * The Auto navigation bar dark mode enable.
89 | */
90 | public boolean autoNavigationBarDarkModeEnable = false;
91 |
92 | /**
93 | * The Auto status bar dark mode alpha.
94 | */
95 | @FloatRange(from = 0f, to = 1f)
96 | public float autoStatusBarDarkModeAlpha = 0.0f;
97 |
98 | /**
99 | * The Auto navigation bar dark mode alpha.
100 | */
101 | @FloatRange(from = 0f, to = 1f)
102 | public float autoNavigationBarDarkModeAlpha = 0.0f;
103 |
104 | /**
105 | * 是否可以修改状态栏颜色
106 | * The Status bar flag.
107 | */
108 | public boolean statusBarColorEnabled = true;
109 | /**
110 | * 状态栏变换后的颜色
111 | * The Status bar color transform.
112 | */
113 | @ColorInt
114 | public int statusBarColorTransform = Color.BLACK;
115 | /**
116 | * 导航栏变换后的颜色
117 | * The Navigation bar color transform.
118 | */
119 | @ColorInt
120 | public int navigationBarColorTransform = Color.BLACK;
121 | /**
122 | * 支持view变色
123 | * The View map.
124 | */
125 | Map> viewMap = new HashMap<>();
126 | /**
127 | * The View alpha.
128 | */
129 | @FloatRange(from = 0f, to = 1f)
130 | public float viewAlpha = 0.0f;
131 | /**
132 | * The Status bar color content view.
133 | */
134 | @ColorInt
135 | public int contentColor = Color.TRANSPARENT;
136 | /**
137 | * The Status bar color content view transform.
138 | */
139 | @ColorInt
140 | public int contentColorTransform = Color.BLACK;
141 | /**
142 | * The Status bar content view alpha.
143 | */
144 | @FloatRange(from = 0f, to = 1f)
145 | public float contentAlpha = 0.0f;
146 | /**
147 | * 解决标题栏与状态栏重叠问题
148 | * The Fits.
149 | */
150 | public boolean fits = false;
151 | /**
152 | * 解决标题栏与状态栏重叠问题
153 | * The Title bar view.
154 | */
155 | public View titleBarView;
156 | /**
157 | * 解决标题栏与状态栏重叠问题
158 | * The Status bar view by height.
159 | */
160 | public View statusBarView;
161 | /**
162 | * 是否可以解决标题栏与状态栏重叠问题
163 | */
164 | public boolean fitsLayoutOverlapEnable = true;
165 | /**
166 | * flymeOS状态栏字体变色
167 | * The Flyme os status bar font color.
168 | */
169 | @ColorInt
170 | public int flymeOSStatusBarFontColor;
171 | @ColorInt
172 | public int flymeOSStatusBarFontTempColor;
173 | /**
174 | * 结合actionBar使用
175 | * The Is support action bar.
176 | */
177 | public boolean isSupportActionBar = false;
178 | /**
179 | * 解决软键盘与输入框冲突问题
180 | * The Keyboard enable.
181 | */
182 | public boolean keyboardEnable = false;
183 |
184 | /**
185 | * 软键盘属性
186 | * The Keyboard mode.
187 | */
188 | public int keyboardMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
189 | | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
190 | /**
191 | * 是否能修改导航栏颜色
192 | * The Navigation bar enable.
193 | */
194 | public boolean navigationBarEnable = true;
195 | /**
196 | * 是否能修改4.4手机以及华为emui3.1导航栏颜色
197 | * The Navigation bar with kitkat enable.
198 | */
199 | public boolean navigationBarWithKitkatEnable = true;
200 | /**
201 | * 是否可以修改emui3系列手机导航栏
202 | * The Navigation bar with emui 3 enable.
203 | */
204 | public boolean navigationBarWithEMUI3Enable = true;
205 | /**
206 | * 是否可以沉浸式
207 | * The Init enable.
208 | */
209 | public boolean barEnable = true;
210 | /**
211 | * 软键盘监听类
212 | * The On keyboard listener.
213 | */
214 | OnKeyboardListener onKeyboardListener;
215 |
216 | /**
217 | * 导航栏显示隐藏监听
218 | */
219 | OnNavigationBarListener onNavigationBarListener;
220 |
221 | /**
222 | * 横竖屏监听
223 | */
224 | OnBarListener onBarListener;
225 |
226 | @Override
227 | protected BarParams clone() {
228 | BarParams barParams = null;
229 | try {
230 | barParams = (BarParams) super.clone();
231 | } catch (CloneNotSupportedException ignored) {
232 | }
233 | return barParams;
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/FitsKeyboard.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | import android.graphics.Rect;
4 | import android.os.Build;
5 | import android.view.View;
6 | import android.view.ViewTreeObserver;
7 | import android.view.Window;
8 | import android.widget.FrameLayout;
9 |
10 | import androidx.drawerlayout.widget.DrawerLayout;
11 | import androidx.fragment.app.Fragment;
12 |
13 | /**
14 | * 适配软键盘弹出问题
15 | *
16 | * @author geyifeng
17 | * @date 2018/11/9 10:24 PM
18 | */
19 | class FitsKeyboard implements ViewTreeObserver.OnGlobalLayoutListener {
20 |
21 | private ImmersionBar mImmersionBar;
22 | private Window mWindow;
23 | private View mDecorView;
24 | private View mContentView;
25 | private View mChildView;
26 | private int mPaddingLeft = 0, mPaddingTop = 0, mPaddingRight = 0, mPaddingBottom = 0;
27 | private int mTempKeyboardHeight;
28 | private boolean mIsAddListener;
29 |
30 | FitsKeyboard(ImmersionBar immersionBar) {
31 | mImmersionBar = immersionBar;
32 | mWindow = immersionBar.getWindow();
33 | mDecorView = mWindow.getDecorView();
34 | FrameLayout frameLayout = mDecorView.findViewById(android.R.id.content);
35 | if (immersionBar.isDialogFragment()) {
36 | Fragment supportFragment = immersionBar.getSupportFragment();
37 | if (supportFragment != null) {
38 | mChildView = supportFragment.getView();
39 | } else {
40 | android.app.Fragment fragment = immersionBar.getFragment();
41 | if (fragment != null) {
42 | mChildView = fragment.getView();
43 | }
44 | }
45 | } else {
46 | mChildView = frameLayout.getChildAt(0);
47 | if (mChildView != null) {
48 | if (mChildView instanceof DrawerLayout) {
49 | mChildView = ((DrawerLayout) mChildView).getChildAt(0);
50 | }
51 | }
52 | }
53 | if (mChildView != null) {
54 | mPaddingLeft = mChildView.getPaddingLeft();
55 | mPaddingTop = mChildView.getPaddingTop();
56 | mPaddingRight = mChildView.getPaddingRight();
57 | mPaddingBottom = mChildView.getPaddingBottom();
58 | }
59 | mContentView = mChildView != null ? mChildView : frameLayout;
60 |
61 | }
62 |
63 | void enable(int mode) {
64 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
65 | mWindow.setSoftInputMode(mode);
66 | if (!mIsAddListener) {
67 | mDecorView.getViewTreeObserver().addOnGlobalLayoutListener(this);
68 | mIsAddListener = true;
69 | }
70 | }
71 | }
72 |
73 | void disable() {
74 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && mIsAddListener) {
75 | if (mChildView != null) {
76 | mContentView.setPadding(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom);
77 | } else {
78 | mContentView.setPadding(mImmersionBar.getPaddingLeft(),
79 | mImmersionBar.getPaddingTop(),
80 | mImmersionBar.getPaddingRight(),
81 | mImmersionBar.getPaddingBottom());
82 | }
83 | }
84 | }
85 |
86 | void cancel() {
87 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && mIsAddListener) {
88 | mDecorView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
89 | mIsAddListener = false;
90 | }
91 | }
92 |
93 | @Override
94 | public void onGlobalLayout() {
95 | if (mImmersionBar != null && mImmersionBar.getBarParams() != null && mImmersionBar.getBarParams().keyboardEnable) {
96 | BarConfig barConfig = mImmersionBar.getBarConfig();
97 | int bottom = 0, keyboardHeight, navigationBarHeight = barConfig.isNavigationAtBottom() ? barConfig.getNavigationBarHeight() : barConfig.getNavigationBarWidth();
98 | boolean isPopup = false;
99 | Rect rect = new Rect();
100 | //获取当前窗口可视区域大小
101 | mDecorView.getWindowVisibleDisplayFrame(rect);
102 | keyboardHeight = mContentView.getHeight() - rect.bottom;
103 | if (keyboardHeight != mTempKeyboardHeight) {
104 | mTempKeyboardHeight = keyboardHeight;
105 | if (!ImmersionBar.checkFitsSystemWindows(mWindow.getDecorView().findViewById(android.R.id.content))) {
106 | if (mChildView != null) {
107 | if (mImmersionBar.getBarParams().isSupportActionBar) {
108 | keyboardHeight += mImmersionBar.getActionBarHeight() + barConfig.getStatusBarHeight();
109 | }
110 | if (mImmersionBar.getBarParams().fits) {
111 | keyboardHeight += barConfig.getStatusBarHeight();
112 | }
113 | if (keyboardHeight > navigationBarHeight) {
114 | bottom = keyboardHeight + mPaddingBottom;
115 | isPopup = true;
116 | }
117 | mContentView.setPadding(mPaddingLeft, mPaddingTop, mPaddingRight, bottom);
118 | } else {
119 | bottom = mImmersionBar.getPaddingBottom();
120 | keyboardHeight -= navigationBarHeight;
121 | if (keyboardHeight > navigationBarHeight) {
122 | bottom = keyboardHeight + navigationBarHeight;
123 | isPopup = true;
124 | }
125 | mContentView.setPadding(mImmersionBar.getPaddingLeft(),
126 | mImmersionBar.getPaddingTop(),
127 | mImmersionBar.getPaddingRight(),
128 | bottom);
129 | }
130 | } else {
131 | keyboardHeight -= navigationBarHeight;
132 | if (keyboardHeight > navigationBarHeight) {
133 | isPopup = true;
134 | }
135 | }
136 | if (keyboardHeight < 0) {
137 | keyboardHeight = 0;
138 | }
139 | if (mImmersionBar.getBarParams().onKeyboardListener != null) {
140 | mImmersionBar.getBarParams().onKeyboardListener.onKeyboardChange(isPopup, keyboardHeight);
141 | }
142 | if (!isPopup && mImmersionBar.getBarParams().barHide != BarHide.FLAG_SHOW_BAR) {
143 | mImmersionBar.setBar();
144 | }
145 | }
146 | }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview
2 |
3 | import android.content.Context
4 | import android.graphics.Point
5 | import android.os.Build
6 | import android.os.Bundle
7 | import android.view.View
8 | import android.view.ViewGroup
9 | import android.view.WindowManager
10 | import androidx.appcompat.app.AppCompatActivity
11 | import androidx.core.widget.NestedScrollView
12 | import com.xiaoguang.widget.easyscrollview.immersionbar.ImmersionBar
13 | import kotlinx.android.synthetic.main.activity_main.*
14 |
15 | class MainActivity : AppCompatActivity() {
16 |
17 | private var DP24 = 0
18 | private var DP58 = 0
19 | private var SIZE18 = 18.toFloat() / 28
20 |
21 | private var statusBarHeight = 0
22 | private var scrollHeight = 0
23 | private var textMaxWidth = 0
24 | private var textMAxHeight = 0
25 | private var textChangeX = 0
26 | private var textChangeY = 0
27 | private var deviceItemSize = 0
28 |
29 | override fun onCreate(savedInstanceState: Bundle?) {
30 | super.onCreate(savedInstanceState)
31 | setContentView(R.layout.activity_main)
32 |
33 | ImmersionBar.with(this).init()
34 |
35 | initSize()
36 | initTitleBar()
37 | initListener()
38 | }
39 |
40 | private fun initTitleBar() {
41 | val params: ViewGroup.LayoutParams = v_title_bar.layoutParams
42 | params.height = statusBarHeight + dp2px(this, 44)
43 | v_title_bar.layoutParams = params
44 | rl_title_top.y = statusBarHeight.toFloat()
45 | // 文字距离左侧 = 24dp
46 | tv_home.x = DP24.toFloat()
47 | // 文字距离顶部 = 58dp + statusBarHeight
48 | tv_home.y = (DP58 + statusBarHeight).toFloat()
49 | }
50 |
51 | private fun initSize() {
52 | statusBarHeight = ImmersionBar.getStatusBarHeight(this)
53 | val textMaxSize: IntArray = measureView(tv_home)
54 | // 得到文字宽度
55 | textMaxWidth = textMaxSize[0]
56 | // 得到文字高度
57 | textMAxHeight = textMaxSize[1]
58 | // 文字距离左侧 = 24dp
59 | DP24 = dp2px(this, 24)
60 | // 文字距离顶部 = 58dp + statusBarHeight
61 | DP58 = dp2px(this, 58)
62 |
63 | // 变化的X轴 = 屏幕宽度 / 2 - 文字宽度 / 2 - marginStart24
64 | textChangeX =
65 | getScreenWidth(this) / 2 - textMaxWidth / 2 - DP24
66 | // 变化的Y轴 = 高度58 - (toolbar高度22 - 文字高度) / 2
67 | textChangeY = DP58 - (dp2px(this, 44) - textMAxHeight) / 2
68 | // 每个item宽高 = (屏幕宽度 - margin8 * 3) / 2
69 | deviceItemSize = (getScreenWidth(this) - dp2px(this, 8 * 3)) / 2
70 | // 滑动变化颜色的距离 = 图片高度276 - CardView穿透上去的83 - toolbar高度44
71 | scrollHeight = dp2px(this, 276 - 83 - 44) - statusBarHeight
72 | }
73 |
74 | private fun initListener() {
75 | nsv_home.setOnScrollChangeListener { _: NestedScrollView?, _: Int, scrollY: Int, _: Int, _: Int ->
76 | when {
77 | // 在顶部
78 | scrollY <= 0 -> {
79 | // 标题栏全透明
80 | v_title_bar.alpha = 0f
81 | // 图片蒙版全透明
82 | iv_home_bg_black.alpha = 0f
83 | // 文字大小(28sp)还原
84 | tv_home.scaleX = 1f
85 | tv_home.scaleY = 1f
86 | // 文字x,y轴便宜
87 | tv_home.x = DP24.toFloat()
88 | tv_home.y = (DP58 + statusBarHeight).toFloat()
89 | }
90 | // 在scrollHeight范围内
91 | scrollY < scrollHeight -> {
92 | // 标题栏全透明
93 | v_title_bar.alpha = 0f
94 | // 滑动百分比
95 | val scale: Float = scrollY.toFloat() / scrollHeight
96 | // 图片蒙版透明度跟随滑动变化
97 | iv_home_bg_black.alpha = scale
98 | // 文字大小跟随滑动变化 原始大小(28sp)到18sp之间变化
99 | tv_home.scaleX = 1 - scale * (1 - SIZE18)
100 | tv_home.scaleY = 1 - scale * (1 - SIZE18)
101 | val changeX = (scale * textChangeX).toInt()
102 | val changeY = (scale * textChangeY).toInt()
103 | // 文字x,y轴偏移,原始位置到居中之间的变化
104 | tv_home.x = (DP24 + changeX).toFloat()
105 | tv_home.y = (DP58 + statusBarHeight - changeY).toFloat()
106 | }
107 | // 在划出scrollHeight范围
108 | else -> {
109 | // 标题栏全黑
110 | v_title_bar.alpha = 1.0f
111 | // 图片蒙版全黑
112 | iv_home_bg_black.alpha = 1.0f
113 | // 文字大小改成18sp
114 | tv_home.scaleX = SIZE18
115 | tv_home.scaleY = SIZE18
116 | // 文字x,y轴偏移,即居中显示
117 | tv_home.x = (DP24 + textChangeX).toFloat()
118 | tv_home.y = (DP58 + statusBarHeight - textChangeY).toFloat()
119 | }
120 | }
121 | }
122 | }
123 |
124 | // util
125 | /**
126 | * dp2px
127 | */
128 | private fun dp2px(context: Context, dpValue: Int): Int {
129 | val scale = context.applicationContext.resources.displayMetrics.density
130 | return (dpValue * scale + 0.5f).toInt()
131 | }
132 |
133 | /**
134 | * Measure the view.
135 | *
136 | * @param view The view.
137 | * @return arr[0]: view's width, arr[1]: view's height
138 | */
139 | private fun measureView(view: View): IntArray {
140 | var lp = view.layoutParams
141 | if (lp == null) {
142 | lp = ViewGroup.LayoutParams(
143 | ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
144 | )
145 | }
146 | val widthSpec = ViewGroup.getChildMeasureSpec(0, 0, lp.width)
147 | val lpHeight = lp.height
148 | val heightSpec: Int = if (lpHeight > 0) {
149 | View.MeasureSpec.makeMeasureSpec(lpHeight, View.MeasureSpec.EXACTLY)
150 | } else {
151 | View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
152 | }
153 | view.measure(widthSpec, heightSpec)
154 | return intArrayOf(view.measuredWidth, view.measuredHeight)
155 | }
156 |
157 | /**
158 | * Return the width of screen, in pixel.
159 | *
160 | * @return the width of screen, in pixel
161 | */
162 | private fun getScreenWidth(context: Context): Int {
163 | val wm = context.getSystemService(WINDOW_SERVICE) as WindowManager
164 | ?: return -1
165 | val point = Point()
166 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
167 | wm.defaultDisplay.getRealSize(point)
168 | } else {
169 | wm.defaultDisplay.getSize(point)
170 | }
171 | return point.x
172 | }
173 |
174 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/SpecialBarFontUtils.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.Activity;
5 | import android.os.Build;
6 | import android.view.View;
7 | import android.view.Window;
8 | import android.view.WindowManager;
9 |
10 | import java.lang.reflect.Field;
11 | import java.lang.reflect.InvocationTargetException;
12 | import java.lang.reflect.Method;
13 |
14 | /**
15 | * Flyme OS 修改状态栏字体颜色工具类
16 | *
17 | * @author gyf
18 | * @date 2017 /05/30
19 | */
20 | class SpecialBarFontUtils {
21 | private static Method mSetStatusBarColorIcon;
22 | private static Method mSetStatusBarDarkIcon;
23 | private static Field mStatusBarColorFiled;
24 | private static int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0;
25 |
26 | static {
27 | try {
28 | mSetStatusBarColorIcon = Activity.class.getMethod("setStatusBarDarkIcon", int.class);
29 | } catch (NoSuchMethodException ignored) {
30 |
31 | }
32 | try {
33 | mSetStatusBarDarkIcon = Activity.class.getMethod("setStatusBarDarkIcon", boolean.class);
34 | } catch (NoSuchMethodException ignored) {
35 |
36 | }
37 | try {
38 | mStatusBarColorFiled = WindowManager.LayoutParams.class.getField("statusBarColor");
39 | } catch (NoSuchFieldException ignored) {
40 |
41 | }
42 | try {
43 | Field field = View.class.getField("SYSTEM_UI_FLAG_LIGHT_STATUS_BAR");
44 | SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = field.getInt(null);
45 | } catch (NoSuchFieldException ignored) {
46 |
47 | } catch (IllegalAccessException ignored) {
48 |
49 | }
50 | }
51 |
52 | /**
53 | * 判断颜色是否偏黑色
54 | *
55 | * @param color 颜色
56 | * @param level 级别
57 | * @return boolean boolean
58 | */
59 | static boolean isBlackColor(int color, int level) {
60 | int grey = toGrey(color);
61 | return grey < level;
62 | }
63 |
64 | /**
65 | * 颜色转换成灰度值
66 | *
67 | * @param rgb 颜色
68 | * @return the int
69 | * @return 灰度值
70 | */
71 | static int toGrey(int rgb) {
72 | int blue = rgb & 0x000000FF;
73 | int green = (rgb & 0x0000FF00) >> 8;
74 | int red = (rgb & 0x00FF0000) >> 16;
75 | return (red * 38 + green * 75 + blue * 15) >> 7;
76 | }
77 |
78 | /**
79 | * 设置状态栏字体图标颜色
80 | *
81 | * @param activity 当前activity
82 | * @param color 颜色
83 | */
84 | public static void setStatusBarDarkIcon(Activity activity, int color) {
85 | if (mSetStatusBarColorIcon != null) {
86 | try {
87 | mSetStatusBarColorIcon.invoke(activity, color);
88 | } catch (IllegalAccessException e) {
89 | e.printStackTrace();
90 | } catch (InvocationTargetException e) {
91 | e.printStackTrace();
92 | }
93 | } else {
94 | boolean whiteColor = isBlackColor(color, 50);
95 | if (mStatusBarColorFiled != null) {
96 | setStatusBarDarkIcon(activity, whiteColor, whiteColor);
97 | setStatusBarDarkIcon(activity.getWindow(), color);
98 | } else {
99 | setStatusBarDarkIcon(activity, whiteColor);
100 | }
101 | }
102 | }
103 |
104 | /**
105 | * 设置状态栏字体图标颜色(只限全屏非activity情况)
106 | *
107 | * @param window 当前窗口
108 | * @param color 颜色
109 | */
110 | public static void setStatusBarDarkIcon(Window window, int color) {
111 | try {
112 | setStatusBarColor(window, color);
113 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
114 | setStatusBarDarkIcon(window.getDecorView(), true);
115 | }
116 | } catch (Exception e) {
117 | e.printStackTrace();
118 | }
119 | }
120 |
121 | /**
122 | * 设置状态栏字体图标颜色
123 | *
124 | * @param activity 当前activity
125 | * @param dark 是否深色 true为深色 false 为白色
126 | */
127 | public static void setStatusBarDarkIcon(Activity activity, boolean dark) {
128 | setStatusBarDarkIcon(activity, dark, true);
129 | }
130 |
131 | private static boolean changeMeizuFlag(WindowManager.LayoutParams winParams, String flagName, boolean on) {
132 | try {
133 | Field f = winParams.getClass().getDeclaredField(flagName);
134 | f.setAccessible(true);
135 | int bits = f.getInt(winParams);
136 | Field f2 = winParams.getClass().getDeclaredField("meizuFlags");
137 | f2.setAccessible(true);
138 | int meizuFlags = f2.getInt(winParams);
139 | int oldFlags = meizuFlags;
140 | if (on) {
141 | meizuFlags |= bits;
142 | } else {
143 | meizuFlags &= ~bits;
144 | }
145 | if (oldFlags != meizuFlags) {
146 | f2.setInt(winParams, meizuFlags);
147 | return true;
148 | }
149 | } catch (NoSuchFieldException e) {
150 | e.printStackTrace();
151 | } catch (IllegalAccessException e) {
152 | e.printStackTrace();
153 | } catch (IllegalArgumentException e) {
154 | e.printStackTrace();
155 | } catch (Throwable e) {
156 | e.printStackTrace();
157 | }
158 | return false;
159 | }
160 |
161 | /**
162 | * 设置状态栏颜色
163 | *
164 | * @param view
165 | * @param dark
166 | */
167 | private static void setStatusBarDarkIcon(View view, boolean dark) {
168 | int oldVis = view.getSystemUiVisibility();
169 | int newVis = oldVis;
170 | if (dark) {
171 | newVis |= SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
172 | } else {
173 | newVis &= ~SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
174 | }
175 | if (newVis != oldVis) {
176 | view.setSystemUiVisibility(newVis);
177 | }
178 | }
179 |
180 | /**
181 | * 设置状态栏颜色
182 | *
183 | * @param window
184 | * @param color
185 | */
186 | private static void setStatusBarColor(Window window, int color) {
187 | WindowManager.LayoutParams winParams = window.getAttributes();
188 | if (mStatusBarColorFiled != null) {
189 | try {
190 | int oldColor = mStatusBarColorFiled.getInt(winParams);
191 | if (oldColor != color) {
192 | mStatusBarColorFiled.set(winParams, color);
193 | window.setAttributes(winParams);
194 | }
195 | } catch (IllegalAccessException e) {
196 | e.printStackTrace();
197 | }
198 | }
199 | }
200 |
201 | /**
202 | * 设置状态栏字体图标颜色(只限全屏非activity情况)
203 | *
204 | * @param window 当前窗口
205 | * @param dark 是否深色 true为深色 false 为白色
206 | */
207 | public static void setStatusBarDarkIcon(Window window, boolean dark) {
208 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
209 | changeMeizuFlag(window.getAttributes(), "MEIZU_FLAG_DARK_STATUS_BAR_ICON", dark);
210 | } else {
211 | View decorView = window.getDecorView();
212 | setStatusBarDarkIcon(decorView, dark);
213 | setStatusBarColor(window, 0);
214 | }
215 | }
216 |
217 | private static void setStatusBarDarkIcon(Activity activity, boolean dark, boolean flag) {
218 | if (mSetStatusBarDarkIcon != null) {
219 | try {
220 | mSetStatusBarDarkIcon.invoke(activity, dark);
221 | } catch (IllegalAccessException e) {
222 | e.printStackTrace();
223 | } catch (InvocationTargetException e) {
224 | e.printStackTrace();
225 | }
226 | } else {
227 | if (flag) {
228 | setStatusBarDarkIcon(activity.getWindow(), dark);
229 | }
230 | }
231 | }
232 |
233 | @SuppressLint("PrivateApi")
234 | static void setMIUIBarDark(Window window, String key, boolean dark) {
235 | if (window != null) {
236 | Class extends Window> clazz = window.getClass();
237 | try {
238 | int darkModeFlag;
239 | Class> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
240 | Field field = layoutParams.getField(key);
241 | darkModeFlag = field.getInt(layoutParams);
242 | Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
243 | if (dark) {
244 | //状态栏透明且黑色字体
245 | extraFlagField.invoke(window, darkModeFlag, darkModeFlag);
246 | } else {
247 | //清除黑色字体
248 | extraFlagField.invoke(window, 0, darkModeFlag);
249 | }
250 | } catch (Exception ignored) {
251 |
252 | }
253 | }
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/RequestManagerRetriever.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | import android.app.Activity;
4 | import android.app.Dialog;
5 | import android.os.Build;
6 | import android.os.Handler;
7 | import android.os.Looper;
8 | import android.os.Message;
9 | import androidx.annotation.NonNull;
10 | import androidx.annotation.Nullable;
11 | import androidx.annotation.RequiresApi;
12 | import androidx.fragment.app.DialogFragment;
13 | import androidx.fragment.app.Fragment;
14 | import androidx.fragment.app.FragmentActivity;
15 | import androidx.fragment.app.FragmentManager;
16 |
17 | import java.util.HashMap;
18 | import java.util.Map;
19 |
20 | /**
21 | * The type Request manager retriever.
22 | *
23 | * @author geyifeng
24 | * @date 2019 /4/12 4:21 PM
25 | */
26 | class RequestManagerRetriever implements Handler.Callback {
27 |
28 | private String mTag = ImmersionBar.class.getName();
29 |
30 | private Handler mHandler;
31 |
32 | private static final int ID_REMOVE_FRAGMENT_MANAGER = 1;
33 | private static final int ID_REMOVE_SUPPORT_FRAGMENT_MANAGER = 2;
34 |
35 | private static class Holder {
36 | private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();
37 | }
38 |
39 | /**
40 | * Gets instance.
41 | *
42 | * @return the instance
43 | */
44 | static RequestManagerRetriever getInstance() {
45 | return Holder.INSTANCE;
46 | }
47 |
48 | private RequestManagerRetriever() {
49 | mHandler = new Handler(Looper.getMainLooper(), this);
50 | }
51 |
52 | private final Map mPendingFragments = new HashMap<>();
53 | private final Map mPendingSupportFragments = new HashMap<>();
54 |
55 | /**
56 | * Get immersion bar.
57 | *
58 | * @param activity the activity
59 | * @return the immersion bar
60 | */
61 | public ImmersionBar get(Activity activity) {
62 | checkNotNull(activity, "activity is null");
63 | String tag = mTag + System.identityHashCode(activity);
64 | if (activity instanceof FragmentActivity) {
65 | return getSupportFragment(((FragmentActivity) activity).getSupportFragmentManager(), tag).get(activity);
66 | } else {
67 | return getFragment(activity.getFragmentManager(), tag).get(activity);
68 | }
69 | }
70 |
71 | /**
72 | * Get immersion bar.
73 | *
74 | * @param fragment the fragment
75 | * @param isOnly the is only
76 | * @return the immersion bar
77 | */
78 | public ImmersionBar get(Fragment fragment, boolean isOnly) {
79 | checkNotNull(fragment, "fragment is null");
80 | checkNotNull(fragment.getActivity(), "fragment.getActivity() is null");
81 | if (fragment instanceof DialogFragment) {
82 | checkNotNull(((DialogFragment) fragment).getDialog(), "fragment.getDialog() is null");
83 | }
84 | String tag = mTag;
85 | if (isOnly) {
86 | tag += fragment.getClass().getName();
87 | } else {
88 | tag += System.identityHashCode(fragment);
89 | }
90 | return getSupportFragment(fragment.getChildFragmentManager(), tag).get(fragment);
91 | }
92 |
93 |
94 | /**
95 | * Get immersion bar.
96 | *
97 | * @param fragment the fragment
98 | * @param isOnly the is only
99 | * @return the immersion bar
100 | */
101 | @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
102 | public ImmersionBar get(android.app.Fragment fragment, boolean isOnly) {
103 | checkNotNull(fragment, "fragment is null");
104 | checkNotNull(fragment.getActivity(), "fragment.getActivity() is null");
105 | if (fragment instanceof android.app.DialogFragment) {
106 | checkNotNull(((android.app.DialogFragment) fragment).getDialog(), "fragment.getDialog() is null");
107 | }
108 | String tag = mTag;
109 | if (isOnly) {
110 | tag += fragment.getClass().getName();
111 | } else {
112 | tag += System.identityHashCode(fragment);
113 | }
114 | return getFragment(fragment.getChildFragmentManager(), tag).get(fragment);
115 | }
116 |
117 | /**
118 | * Get immersion bar.
119 | *
120 | * @param activity the activity
121 | * @param dialog the dialog
122 | * @return the immersion bar
123 | */
124 | public ImmersionBar get(Activity activity, Dialog dialog) {
125 | checkNotNull(activity, "activity is null");
126 | checkNotNull(dialog, "dialog is null");
127 | String tag = mTag + System.identityHashCode(dialog);
128 | if (activity instanceof FragmentActivity) {
129 | return getSupportFragment(((FragmentActivity) activity).getSupportFragmentManager(), tag).get(activity, dialog);
130 | } else {
131 | return getFragment(activity.getFragmentManager(), tag).get(activity, dialog);
132 | }
133 | }
134 |
135 | /**
136 | * Destroy.
137 | *
138 | * @param fragment the fragment
139 | */
140 | public void destroy(Fragment fragment, boolean isOnly) {
141 | if (fragment == null) {
142 | return;
143 | }
144 | String tag = mTag;
145 | if (isOnly) {
146 | tag += fragment.getClass().getName();
147 | } else {
148 | tag += System.identityHashCode(fragment);
149 | }
150 | getSupportFragment(fragment.getChildFragmentManager(), tag, true);
151 | }
152 |
153 | /**
154 | * Destroy.
155 | *
156 | * @param activity the activity
157 | * @param dialog the dialog
158 | */
159 | public void destroy(Activity activity, Dialog dialog) {
160 | if (activity == null || dialog == null) {
161 | return;
162 | }
163 | String tag = mTag + System.identityHashCode(dialog);
164 | if (activity instanceof FragmentActivity) {
165 | SupportRequestManagerFragment fragment = getSupportFragment(((FragmentActivity) activity).getSupportFragmentManager(), tag, true);
166 | if (fragment != null) {
167 | fragment.get(activity, dialog).onDestroy();
168 | }
169 | } else {
170 | RequestManagerFragment fragment = getFragment(activity.getFragmentManager(), tag, true);
171 | if (fragment != null) {
172 | fragment.get(activity, dialog).onDestroy();
173 | }
174 | }
175 | }
176 |
177 | @Override
178 | public boolean handleMessage(Message msg) {
179 | boolean handled = true;
180 | switch (msg.what) {
181 | case ID_REMOVE_FRAGMENT_MANAGER:
182 | android.app.FragmentManager fm = (android.app.FragmentManager) msg.obj;
183 | mPendingFragments.remove(fm);
184 | break;
185 | case ID_REMOVE_SUPPORT_FRAGMENT_MANAGER:
186 | FragmentManager supportFm = (FragmentManager) msg.obj;
187 | mPendingSupportFragments.remove(supportFm);
188 | break;
189 | default:
190 | handled = false;
191 | break;
192 | }
193 | return handled;
194 | }
195 |
196 | private RequestManagerFragment getFragment(android.app.FragmentManager fm, String tag) {
197 | return getFragment(fm, tag, false);
198 | }
199 |
200 | private RequestManagerFragment getFragment(android.app.FragmentManager fm, String tag, boolean destroy) {
201 | RequestManagerFragment fragment = (RequestManagerFragment) fm.findFragmentByTag(tag);
202 | if (fragment == null) {
203 | fragment = mPendingFragments.get(fm);
204 | if (fragment == null) {
205 | if (destroy) {
206 | return null;
207 | }
208 | fragment = new RequestManagerFragment();
209 | mPendingFragments.put(fm, fragment);
210 | fm.beginTransaction().add(fragment, tag).commitAllowingStateLoss();
211 | mHandler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
212 | }
213 | }
214 | if (destroy) {
215 | fm.beginTransaction().remove(fragment).commitAllowingStateLoss();
216 | return null;
217 | }
218 | return fragment;
219 | }
220 |
221 | private SupportRequestManagerFragment getSupportFragment(FragmentManager fm, String tag) {
222 | return getSupportFragment(fm, tag, false);
223 | }
224 |
225 | private SupportRequestManagerFragment getSupportFragment(FragmentManager fm, String tag, boolean destroy) {
226 | SupportRequestManagerFragment fragment = (SupportRequestManagerFragment) fm.findFragmentByTag(tag);
227 | if (fragment == null) {
228 | fragment = mPendingSupportFragments.get(fm);
229 | if (fragment == null) {
230 | if (destroy) {
231 | return null;
232 | }
233 | fragment = new SupportRequestManagerFragment();
234 | mPendingSupportFragments.put(fm, fragment);
235 | fm.beginTransaction().add(fragment, tag).commitAllowingStateLoss();
236 | mHandler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
237 | }
238 | }
239 | if (destroy) {
240 | fm.beginTransaction().remove(fragment).commitAllowingStateLoss();
241 | return null;
242 | }
243 | return fragment;
244 | }
245 |
246 | private static void checkNotNull(@Nullable T arg, @NonNull String message) {
247 | if (arg == null) {
248 | throw new NullPointerException(message);
249 | }
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/BarConfig.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | import static com.xiaoguang.widget.easyscrollview.immersionbar.Constants.IMMERSION_EMUI_NAVIGATION_BAR_HIDE_SHOW;
4 | import static com.xiaoguang.widget.easyscrollview.immersionbar.Constants.IMMERSION_MIUI_NAVIGATION_BAR_HIDE_SHOW;
5 | import static com.xiaoguang.widget.easyscrollview.immersionbar.Constants.IMMERSION_NAVIGATION_BAR_HEIGHT;
6 | import static com.xiaoguang.widget.easyscrollview.immersionbar.Constants.IMMERSION_NAVIGATION_BAR_HEIGHT_LANDSCAPE;
7 | import static com.xiaoguang.widget.easyscrollview.immersionbar.Constants.IMMERSION_NAVIGATION_BAR_WIDTH;
8 | import static com.xiaoguang.widget.easyscrollview.immersionbar.Constants.IMMERSION_STATUS_BAR_HEIGHT;
9 |
10 | import android.annotation.SuppressLint;
11 | import android.annotation.TargetApi;
12 | import android.app.Activity;
13 | import android.content.Context;
14 | import android.content.res.Configuration;
15 | import android.content.res.Resources;
16 | import android.os.Build;
17 | import android.provider.Settings;
18 | import android.util.DisplayMetrics;
19 | import android.util.TypedValue;
20 | import android.view.Display;
21 | import android.view.View;
22 | import android.view.WindowManager;
23 |
24 | import com.xiaoguang.widget.easyscrollview.R;
25 |
26 | //import com.xiaoguang.widget.easyscrollview.R;
27 |
28 | /**
29 | * The type Bar config.
30 | *
31 | * @author geyifeng
32 | * @date 2017 /5/11
33 | */
34 | class BarConfig {
35 |
36 | private final int mStatusBarHeight;
37 | private final int mActionBarHeight;
38 | private final boolean mHasNavigationBar;
39 | private final int mNavigationBarHeight;
40 | private final int mNavigationBarWidth;
41 | private final boolean mInPortrait;
42 | private final float mSmallestWidthDp;
43 |
44 | /**
45 | * Instantiates a new Bar config.
46 | *
47 | * @param activity the activity
48 | */
49 | BarConfig(Activity activity) {
50 | Resources res = activity.getResources();
51 | mInPortrait = (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
52 | mSmallestWidthDp = getSmallestWidthDp(activity);
53 | mStatusBarHeight = getInternalDimensionSize(activity, IMMERSION_STATUS_BAR_HEIGHT);
54 | mActionBarHeight = getActionBarHeight(activity);
55 | mNavigationBarHeight = getNavigationBarHeight(activity);
56 | mNavigationBarWidth = getNavigationBarWidth(activity);
57 | mHasNavigationBar = (mNavigationBarHeight > 0);
58 | }
59 |
60 | @TargetApi(14)
61 | private int getActionBarHeight(Activity activity) {
62 | int result = 0;
63 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
64 | View actionBar = activity.getWindow().findViewById(R.id.action_bar_container);
65 | if (actionBar != null) {
66 | result = actionBar.getMeasuredHeight();
67 | }
68 | if (result == 0) {
69 | TypedValue tv = new TypedValue();
70 | activity.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true);
71 | result = TypedValue.complexToDimensionPixelSize(tv.data, activity.getResources().getDisplayMetrics());
72 | }
73 | }
74 | return result;
75 | }
76 |
77 | @TargetApi(14)
78 | private int getNavigationBarHeight(Context context) {
79 | int result = 0;
80 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
81 | if (hasNavBar((Activity) context)) {
82 | String key;
83 | if (mInPortrait) {
84 | key = IMMERSION_NAVIGATION_BAR_HEIGHT;
85 | } else {
86 | key = IMMERSION_NAVIGATION_BAR_HEIGHT_LANDSCAPE;
87 | }
88 | return getInternalDimensionSize(context, key);
89 | }
90 | }
91 | return result;
92 | }
93 |
94 | @TargetApi(14)
95 | private int getNavigationBarWidth(Context context) {
96 | int result = 0;
97 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
98 | if (hasNavBar((Activity) context)) {
99 | return getInternalDimensionSize(context, IMMERSION_NAVIGATION_BAR_WIDTH);
100 | }
101 | }
102 | return result;
103 | }
104 |
105 | @TargetApi(14)
106 | private boolean hasNavBar(Activity activity) {
107 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
108 | //判断小米手机是否开启了全面屏,开启了,直接返回false
109 | if (Settings.Global.getInt(activity.getContentResolver(), IMMERSION_MIUI_NAVIGATION_BAR_HIDE_SHOW, 0) != 0) {
110 | return false;
111 | }
112 | //判断华为手机是否隐藏了导航栏,隐藏了,直接返回false
113 | if (OSUtils.isEMUI()) {
114 | if (OSUtils.isEMUI3_x() || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
115 | if (Settings.System.getInt(activity.getContentResolver(), IMMERSION_EMUI_NAVIGATION_BAR_HIDE_SHOW, 0) != 0) {
116 | return false;
117 | }
118 | } else {
119 | if (Settings.Global.getInt(activity.getContentResolver(), IMMERSION_EMUI_NAVIGATION_BAR_HIDE_SHOW, 0) != 0) {
120 | return false;
121 | }
122 | }
123 | }
124 | }
125 | //其他手机根据屏幕真实高度与显示高度是否相同来判断
126 | WindowManager windowManager = activity.getWindowManager();
127 | Display d = windowManager.getDefaultDisplay();
128 |
129 | DisplayMetrics realDisplayMetrics = new DisplayMetrics();
130 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
131 | d.getRealMetrics(realDisplayMetrics);
132 | }
133 |
134 | int realHeight = realDisplayMetrics.heightPixels;
135 | int realWidth = realDisplayMetrics.widthPixels;
136 |
137 | DisplayMetrics displayMetrics = new DisplayMetrics();
138 | d.getMetrics(displayMetrics);
139 |
140 | int displayHeight = displayMetrics.heightPixels;
141 | int displayWidth = displayMetrics.widthPixels;
142 |
143 | return (realWidth - displayWidth) > 0 || (realHeight - displayHeight) > 0;
144 | }
145 |
146 | private int getInternalDimensionSize(Context context, String key) {
147 | int result = 0;
148 | try {
149 | int resourceId = Resources.getSystem().getIdentifier(key, "dimen", "android");
150 | if (resourceId > 0) {
151 | int sizeOne = context.getResources().getDimensionPixelSize(resourceId);
152 | int sizeTwo = Resources.getSystem().getDimensionPixelSize(resourceId);
153 |
154 | if (sizeTwo >= sizeOne) {
155 | return sizeTwo;
156 | } else {
157 | float densityOne = context.getResources().getDisplayMetrics().density;
158 | float densityTwo = Resources.getSystem().getDisplayMetrics().density;
159 | float f = sizeOne * densityTwo / densityOne;
160 | return (int) ((f >= 0) ? (f + 0.5f) : (f - 0.5f));
161 | }
162 | }
163 | } catch (Resources.NotFoundException ignored) {
164 | return 0;
165 | }
166 | return result;
167 | }
168 |
169 | @SuppressLint("NewApi")
170 | private float getSmallestWidthDp(Activity activity) {
171 | DisplayMetrics metrics = new DisplayMetrics();
172 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
173 | activity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
174 | } else {
175 | activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
176 | }
177 | float widthDp = metrics.widthPixels / metrics.density;
178 | float heightDp = metrics.heightPixels / metrics.density;
179 | return Math.min(widthDp, heightDp);
180 | }
181 |
182 | /**
183 | * Should a navigation bar appear at the bottom of the screen in the current
184 | * device configuration? A navigation bar may appear on the right side of
185 | * the screen in certain configurations.
186 | *
187 | * @return True if navigation should appear at the bottom of the screen, False otherwise.
188 | */
189 | boolean isNavigationAtBottom() {
190 | return (mSmallestWidthDp >= 600 || mInPortrait);
191 | }
192 |
193 | /**
194 | * Get the height of the system status bar.
195 | *
196 | * @return The height of the status bar (in pixels).
197 | */
198 | int getStatusBarHeight() {
199 | return mStatusBarHeight;
200 | }
201 |
202 | /**
203 | * Get the height of the action bar.
204 | *
205 | * @return The height of the action bar (in pixels).
206 | */
207 | int getActionBarHeight() {
208 | return mActionBarHeight;
209 | }
210 |
211 | /**
212 | * Does this device have a system navigation bar?
213 | *
214 | * @return True if this device uses soft key navigation, False otherwise.
215 | */
216 | boolean hasNavigationBar() {
217 | return mHasNavigationBar;
218 | }
219 |
220 | /**
221 | * Get the height of the system navigation bar.
222 | *
223 | * @return The height of the navigation bar (in pixels). If the device does not have soft navigation keys, this will always return 0.
224 | */
225 | int getNavigationBarHeight() {
226 | return mNavigationBarHeight;
227 | }
228 |
229 | /**
230 | * Get the width of the system navigation bar when it is placed vertically on the screen.
231 | *
232 | * @return The width of the navigation bar (in pixels). If the device does not have soft navigation keys, this will always return 0.
233 | */
234 | int getNavigationBarWidth() {
235 | return mNavigationBarWidth;
236 | }
237 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaoguang/widget/easyscrollview/immersionbar/NotchUtils.java:
--------------------------------------------------------------------------------
1 | package com.xiaoguang.widget.easyscrollview.immersionbar;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.Activity;
5 | import android.content.Context;
6 | import android.content.res.Configuration;
7 | import android.os.Build;
8 | import android.util.TypedValue;
9 | import android.view.DisplayCutout;
10 | import android.view.View;
11 | import android.view.Window;
12 | import android.view.WindowInsets;
13 |
14 | import java.lang.reflect.InvocationTargetException;
15 | import java.lang.reflect.Method;
16 |
17 | /**
18 | * 刘海屏判断
19 | * The type Notch utils.
20 | *
21 | * @author geyifeng
22 | * @date 2018 /11/14 12:09 AM
23 | */
24 | public class NotchUtils {
25 |
26 | /**
27 | * 系统属性
28 | * The constant SYSTEM_PROPERTIES.
29 | */
30 | private static final String SYSTEM_PROPERTIES = "android.os.SystemProperties";
31 | /**
32 | * 小米刘海
33 | * The constant NOTCH_XIAO_MI.
34 | */
35 | private static final String NOTCH_XIAO_MI = "ro.miui.notch";
36 | /**
37 | * 华为刘海
38 | * The constant NOTCH_HUA_WEI.
39 | */
40 | private static final String NOTCH_HUA_WEI = "com.huawei.android.util.HwNotchSizeUtil";
41 | /**
42 | * VIVO刘海
43 | * The constant NOTCH_VIVO.
44 | */
45 | private static final String NOTCH_VIVO = "android.util.FtFeature";
46 | /**
47 | * OPPO刘海
48 | * The constant NOTCH_OPPO.
49 | */
50 | private static final String NOTCH_OPPO = "com.oppo.feature.screen.heteromorphism";
51 |
52 |
53 | /**
54 | * 判断是否是刘海屏
55 | * Has notch screen boolean.
56 | *
57 | * @param activity the activity
58 | * @return the boolean
59 | */
60 | public static boolean hasNotchScreen(Activity activity) {
61 | return activity != null && (hasNotchAtXiaoMi(activity) ||
62 | hasNotchAtHuaWei(activity) ||
63 | hasNotchAtOPPO(activity) ||
64 | hasNotchAtVIVO(activity) ||
65 | hasNotchAtAndroidP(activity));
66 | }
67 |
68 | /**
69 | * 判断是否是刘海屏
70 | * Has notch screen boolean.
71 | *
72 | * @param view the view
73 | * @return the boolean
74 | */
75 | public static boolean hasNotchScreen(View view) {
76 | return view != null && (hasNotchAtXiaoMi(view.getContext()) ||
77 | hasNotchAtHuaWei(view.getContext()) ||
78 | hasNotchAtOPPO(view.getContext()) ||
79 | hasNotchAtVIVO(view.getContext()) ||
80 | hasNotchAtAndroidP(view));
81 | }
82 |
83 | /**
84 | * Has notch at android p boolean.
85 | *
86 | * @param view the view
87 | * @return the boolean
88 | */
89 | private static boolean hasNotchAtAndroidP(View view) {
90 | return getDisplayCutout(view) != null;
91 | }
92 |
93 | /**
94 | * Android P 刘海屏判断
95 | * Has notch at android p boolean.
96 | *
97 | * @param activity the activity
98 | * @return the boolean
99 | */
100 | private static boolean hasNotchAtAndroidP(Activity activity) {
101 | return getDisplayCutout(activity) != null;
102 | }
103 |
104 |
105 | /**
106 | * 获得DisplayCutout
107 | * Gets display cutout.
108 | *
109 | * @param activity the activity
110 | * @return the display cutout
111 | */
112 | private static DisplayCutout getDisplayCutout(Activity activity) {
113 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
114 | if (activity != null) {
115 | Window window = activity.getWindow();
116 | if (window != null) {
117 | WindowInsets windowInsets = window.getDecorView().getRootWindowInsets();
118 | if (windowInsets != null) {
119 | return windowInsets.getDisplayCutout();
120 | }
121 | }
122 | }
123 | }
124 | return null;
125 | }
126 |
127 | private static DisplayCutout getDisplayCutout(View view) {
128 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
129 | if (view != null) {
130 | WindowInsets windowInsets = view.getRootWindowInsets();
131 | if (windowInsets != null) {
132 | return windowInsets.getDisplayCutout();
133 | }
134 | }
135 | }
136 | return null;
137 | }
138 |
139 | /**
140 | * 小米刘海屏判断.
141 | * Has notch at xiao mi int.
142 | *
143 | * @param context the context
144 | * @return the int
145 | */
146 | private static boolean hasNotchAtXiaoMi(Context context) {
147 | int result = 0;
148 | if ("Xiaomi".equals(Build.MANUFACTURER)) {
149 | try {
150 | ClassLoader classLoader = context.getClassLoader();
151 | @SuppressLint("PrivateApi")
152 | Class> aClass = classLoader.loadClass(SYSTEM_PROPERTIES);
153 | Method method = aClass.getMethod("getInt", String.class, int.class);
154 | result = (Integer) method.invoke(aClass, NOTCH_XIAO_MI, 0);
155 |
156 | } catch (NoSuchMethodException ignored) {
157 | } catch (IllegalAccessException ignored) {
158 | } catch (InvocationTargetException ignored) {
159 | } catch (ClassNotFoundException ignored) {
160 | }
161 | }
162 | return result == 1;
163 | }
164 |
165 | /**
166 | * 华为刘海屏判断
167 | * Has notch at hua wei boolean.
168 | *
169 | * @param context the context
170 | * @return the boolean
171 | */
172 | private static boolean hasNotchAtHuaWei(Context context) {
173 | boolean result = false;
174 | try {
175 | ClassLoader classLoader = context.getClassLoader();
176 | Class> aClass = classLoader.loadClass(NOTCH_HUA_WEI);
177 | Method get = aClass.getMethod("hasNotchInScreen");
178 | result = (boolean) get.invoke(aClass);
179 | } catch (ClassNotFoundException ignored) {
180 | } catch (NoSuchMethodException ignored) {
181 | } catch (Exception ignored) {
182 | }
183 | return result;
184 | }
185 |
186 | /**
187 | * VIVO刘海屏判断
188 | * Has notch at vivo boolean.
189 | *
190 | * @param context the context
191 | * @return the boolean
192 | */
193 | private static boolean hasNotchAtVIVO(Context context) {
194 | boolean result = false;
195 | try {
196 | ClassLoader classLoader = context.getClassLoader();
197 | @SuppressLint("PrivateApi")
198 | Class> aClass = classLoader.loadClass(NOTCH_VIVO);
199 | Method method = aClass.getMethod("isFeatureSupport", int.class);
200 | result = (boolean) method.invoke(aClass, 0x00000020);
201 | } catch (ClassNotFoundException ignored) {
202 | } catch (NoSuchMethodException ignored) {
203 | } catch (Exception ignored) {
204 | }
205 | return result;
206 | }
207 |
208 | /**
209 | * OPPO刘海屏判断
210 | * Has notch at oppo boolean.
211 | *
212 | * @param context the context
213 | * @return the boolean
214 | */
215 | private static boolean hasNotchAtOPPO(Context context) {
216 | try {
217 | return context.getPackageManager().hasSystemFeature(NOTCH_OPPO);
218 | } catch (Exception ignored) {
219 | return false;
220 | }
221 | }
222 |
223 | /**
224 | * 获得刘海屏高度
225 | * Notch height int.
226 | *
227 | * @param activity the activity
228 | * @return the int
229 | */
230 | public static int getNotchHeight(Activity activity) {
231 | int notchHeight = 0;
232 | int statusBarHeight = ImmersionBar.getStatusBarHeight(activity);
233 | DisplayCutout displayCutout = getDisplayCutout(activity);
234 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && displayCutout != null) {
235 | if (activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
236 | notchHeight = displayCutout.getSafeInsetTop();
237 | } else {
238 | if (displayCutout.getSafeInsetLeft() == 0) {
239 | notchHeight = displayCutout.getSafeInsetRight();
240 | } else {
241 | notchHeight = displayCutout.getSafeInsetLeft();
242 | }
243 | }
244 | } else {
245 | if (hasNotchAtXiaoMi(activity)) {
246 | notchHeight = getXiaoMiNotchHeight(activity);
247 | }
248 | if (hasNotchAtHuaWei(activity)) {
249 | notchHeight = getHuaWeiNotchSize(activity)[1];
250 | }
251 | if (hasNotchAtVIVO(activity)) {
252 | notchHeight = dp2px(activity, 32);
253 | if (notchHeight < statusBarHeight) {
254 | notchHeight = statusBarHeight;
255 | }
256 | }
257 | if (hasNotchAtOPPO(activity)) {
258 | notchHeight = 80;
259 | if (notchHeight < statusBarHeight) {
260 | notchHeight = statusBarHeight;
261 | }
262 | }
263 | }
264 | return notchHeight;
265 | }
266 |
267 | /**
268 | * Gets xiao mi notch height.
269 | *
270 | * @param context the context
271 | * @return the xiao mi notch height
272 | */
273 | private static int getXiaoMiNotchHeight(Context context) {
274 | int resourceId = context.getResources().getIdentifier("notch_height", "dimen", "android");
275 | if (resourceId > 0) {
276 | return context.getResources().getDimensionPixelSize(resourceId);
277 | } else {
278 | return 0;
279 | }
280 | }
281 |
282 | /**
283 | * Get hua wei notch size int [ ].
284 | *
285 | * @param context the context
286 | * @return the int [ ]
287 | */
288 | private static int[] getHuaWeiNotchSize(Context context) {
289 | int[] ret = new int[]{0, 0};
290 | try {
291 | ClassLoader cl = context.getClassLoader();
292 | Class> aClass = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
293 | Method get = aClass.getMethod("getNotchSize");
294 | return (int[]) get.invoke(aClass);
295 | } catch (ClassNotFoundException ignored) {
296 | return ret;
297 | } catch (NoSuchMethodException ignored) {
298 | return ret;
299 | } catch (Exception ignored) {
300 | return ret;
301 | }
302 | }
303 |
304 | /**
305 | * Dp 2 px int.
306 | *
307 | * @param context the context
308 | * @param dpValue the dp value
309 | * @return the int
310 | */
311 | private static int dp2px(Context context, int dpValue) {
312 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.getResources().getDisplayMetrics());
313 | }
314 |
315 | }
316 |
--------------------------------------------------------------------------------