├── .gitignore
├── .idea
├── .name
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── gradle.xml
├── misc.xml
├── runConfigurations.xml
└── vcs.xml
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── myapplication
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── myapplication
│ │ │ └── MainActivity.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── myapplication
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── image1.jpeg
├── image2.gif
├── image3.gif
├── image4.gif
├── readme.md
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | My Application
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | xmlns:android
17 |
18 | ^$
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | xmlns:.*
28 |
29 | ^$
30 |
31 |
32 | BY_NAME
33 |
34 |
35 |
36 |
37 |
38 |
39 | .*:id
40 |
41 | http://schemas.android.com/apk/res/android
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | .*:name
51 |
52 | http://schemas.android.com/apk/res/android
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | name
62 |
63 | ^$
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | style
73 |
74 | ^$
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | .*
84 |
85 | ^$
86 |
87 |
88 | BY_NAME
89 |
90 |
91 |
92 |
93 |
94 |
95 | .*
96 |
97 | http://schemas.android.com/apk/res/android
98 |
99 |
100 | ANDROID_ATTRIBUTE_ORDER
101 |
102 |
103 |
104 |
105 |
106 |
107 | .*
108 |
109 | .*
110 |
111 |
112 | BY_NAME
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion 'android-R'
7 | buildToolsVersion "29.0.2"
8 |
9 | defaultConfig {
10 | applicationId "com.example.myapplication"
11 | minSdkVersion 'R'
12 | targetSdkVersion 'R'
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 | compileOptions {
26 | sourceCompatibility = 1.8
27 | targetCompatibility = 1.8
28 | }
29 |
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
35 | implementation 'androidx.appcompat:appcompat:1.1.0'
36 | implementation 'androidx.core:core-ktx:1.2.0'
37 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
38 | testImplementation 'junit:junit:4.12'
39 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
40 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
41 | }
42 |
--------------------------------------------------------------------------------
/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
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.myapplication
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.example.myapplication", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/myapplication/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.myapplication
2 |
3 | import android.os.Bundle
4 | import android.os.Looper
5 | import android.util.Log
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import android.view.WindowInsets
9 | import android.view.WindowInsetsAnimation
10 | import androidx.appcompat.app.AppCompatActivity
11 | import androidx.core.view.updateLayoutParams
12 | import androidx.core.view.updateMargins
13 | import kotlinx.android.synthetic.main.activity_main.*
14 |
15 |
16 | class MainActivity : AppCompatActivity() {
17 | private val TAG = "MainActivity"
18 | override fun onCreate(savedInstanceState: Bundle?) {
19 | super.onCreate(savedInstanceState)
20 | window.setDecorFitsSystemWindows(false)
21 | setContentView(R.layout.activity_main)
22 | content.postDelayed({
23 | content.windowInsetsController?.hide(WindowInsets.Type.navigationBars())
24 | },5000)
25 | content.setOnApplyWindowInsetsListener { view, windowInsets ->
26 | val statusBars = windowInsets.getInsets(WindowInsets.Type.statusBars())
27 | // It's also possible to use multiple types
28 | val insets = windowInsets.getInsets(WindowInsets.Type.navigationBars())
29 | val insets2 = windowInsets.getInsets(WindowInsets.Type.ime())
30 | Log.e(
31 | TAG, "navigationBars:" + insets.top + " " + insets.bottom +
32 | " " + windowInsets.isVisible(WindowInsets.Type.navigationBars())
33 | )
34 | Log.e(TAG, "statusBars:" + statusBars.top + " " + statusBars.bottom)
35 | Log.e(TAG, "insets2:" + insets2.top + " " + insets2.bottom)
36 | findViewById(android.R.id.content).setPadding(0, 0, 0, insets.bottom)
37 | windowInsets
38 | }
39 | val callback = object : WindowInsetsAnimation.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
40 | override fun onProgress(
41 | insets: WindowInsets,
42 | animations: MutableList
43 | ): WindowInsets {
44 | val navigationBars = insets.getInsets(WindowInsets.Type.navigationBars())
45 | val ime = insets.getInsets(WindowInsets.Type.ime())
46 | Log.e(
47 | TAG, "ime:" + ime.top +
48 | " " + ime.bottom
49 | )
50 | val parmas = (content.layoutParams as ViewGroup.MarginLayoutParams)
51 | parmas.bottomMargin = ime.bottom - navigationBars.bottom
52 | content.layoutParams = parmas
53 | return insets
54 | }
55 |
56 | override fun onStart(
57 | animation: WindowInsetsAnimation,
58 | bounds: WindowInsetsAnimation.Bounds
59 | ): WindowInsetsAnimation.Bounds {
60 | Log.e(
61 | TAG,
62 | "start lowerBound:" + bounds.lowerBound.top + " " + bounds.lowerBound.bottom
63 | )
64 | Log.e(
65 | TAG,
66 | "start upperBound:" + bounds.upperBound.top + " " + bounds.upperBound.bottom
67 | )
68 | Log.e(TAG, "start time:" + animation.durationMillis)
69 | return super.onStart(animation, bounds)
70 | }
71 |
72 | override fun onEnd(animation: WindowInsetsAnimation) {
73 | Log.e(TAG, "end:" + animation.durationMillis)
74 | super.onEnd(animation)
75 | }
76 |
77 | override fun onPrepare(animation: WindowInsetsAnimation) {
78 | Log.e(TAG, "onPrepare:" + animation.durationMillis)
79 | super.onPrepare(animation)
80 | }
81 |
82 | }
83 | content.setWindowInsetsAnimationCallback(callback)
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
21 |
22 |
--------------------------------------------------------------------------------
/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/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siyehua/WindowInsetsAnimation/1f36cc229d3cd4e7f39f1ebf2d006beb5e96dca2/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siyehua/WindowInsetsAnimation/1f36cc229d3cd4e7f39f1ebf2d006beb5e96dca2/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siyehua/WindowInsetsAnimation/1f36cc229d3cd4e7f39f1ebf2d006beb5e96dca2/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siyehua/WindowInsetsAnimation/1f36cc229d3cd4e7f39f1ebf2d006beb5e96dca2/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siyehua/WindowInsetsAnimation/1f36cc229d3cd4e7f39f1ebf2d006beb5e96dca2/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siyehua/WindowInsetsAnimation/1f36cc229d3cd4e7f39f1ebf2d006beb5e96dca2/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siyehua/WindowInsetsAnimation/1f36cc229d3cd4e7f39f1ebf2d006beb5e96dca2/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siyehua/WindowInsetsAnimation/1f36cc229d3cd4e7f39f1ebf2d006beb5e96dca2/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siyehua/WindowInsetsAnimation/1f36cc229d3cd4e7f39f1ebf2d006beb5e96dca2/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siyehua/WindowInsetsAnimation/1f36cc229d3cd4e7f39f1ebf2d006beb5e96dca2/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | My Application
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/myapplication/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.myapplication
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 | }
18 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.3.61'
5 | repositories {
6 | google()
7 | jcenter()
8 |
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.6.1'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | google()
22 | jcenter()
23 |
24 | }
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/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=-Xmx1536m
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
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siyehua/WindowInsetsAnimation/1f36cc229d3cd4e7f39f1ebf2d006beb5e96dca2/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Mar 30 14:38:52 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/image1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siyehua/WindowInsetsAnimation/1f36cc229d3cd4e7f39f1ebf2d006beb5e96dca2/image1.jpeg
--------------------------------------------------------------------------------
/image2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siyehua/WindowInsetsAnimation/1f36cc229d3cd4e7f39f1ebf2d006beb5e96dca2/image2.gif
--------------------------------------------------------------------------------
/image3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siyehua/WindowInsetsAnimation/1f36cc229d3cd4e7f39f1ebf2d006beb5e96dca2/image3.gif
--------------------------------------------------------------------------------
/image4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siyehua/WindowInsetsAnimation/1f36cc229d3cd4e7f39f1ebf2d006beb5e96dca2/image4.gif
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # WindowInsets - 获取导航栏,状态栏,键盘的高度和状态
2 | ## 背景
3 | 最新的 Android Q(11) 推出了许多功能,有一个比较重要的功能(需梯子):
4 | [Synchronized IME transitions](https://android-developers.googleblog.com/2020/03/android-11-developer-preview-2.html)
5 |
6 | > A new set of APIs let you synchronize your app’s content with the IME (input method editor, aka soft keyboard) and system bars as they animate on and offscreen, making it much easier to create natural, intuitive and jank-free IME transitions. For frame-perfect transitions, a new insets animation listener notifies apps of per-frame changes to insets while the system bars or the IME animate. Additionally, apps can take control of the IME and system bar transitions through the WindowInsetsAnimationController API. For example, app-driven IME experiences let apps control the IME in response to overscrolling the app UI. Give these new IME transitions a try and let us know what other transitions are important to you.
7 |
8 | 按照文章的意思,可以监听键盘的高度变化,光介绍就非常让人激动人心.
9 |
凡是搞过键盘的同学都知道,监听 Android 键盘的高度非常复杂,网上的一些黑科技也只对某些场景,有些场景就是无法处理。
10 |
而且有一个非常关键的点:键盘只有完全弹出来了才知道高度,当我们想根据键盘的上升做一个动画时,就很难做到-无法知道键盘动画的时间和最终高度
11 |
12 | 我们来看看官方给的效果图:
13 |
14 | 
15 |
16 | 话不多说,我们来测试一下
17 |
18 | ## 测试
19 | ### 引入
20 | 1. 前期准备
21 |
先将 Android Studio 和 Gradle, Android SDK 更新到最新, 我的版本分别是:
22 | Android Studio 3.6.1, Gradle 5.6.4, Android SDK R
23 |
24 |
有 Pixel 手机的直接更新到最新版本 Q ,没有的可以下载最新的 Q 镜像
25 |
26 | 2. 更新 gradle 配置
27 |
28 | ```groovy
29 | android {
30 | compileSdkVersion 'android-R'
31 | buildToolsVersion "29.0.2"
32 |
33 | defaultConfig {
34 | applicationId "com.example.myapplication"
35 | minSdkVersion 'R'
36 | targetSdkVersion 'R'
37 | versionCode 1
38 | versionName "1.0"
39 |
40 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
41 | }
42 |
43 | //....
44 |
45 | }
46 | ```
47 |
48 | ### 使用
49 | 1. 先设置 FitSystemWindows 为 false:
50 |
51 | ```kotlin
52 | //非常重要,没有这句话监听无法生效
53 | window.setDecorFitsSystemWindows(false)
54 | ```
55 |
56 | 2. 再对 view 设置监听:
57 |
58 | ```kotlin
59 | class MainActivity : AppCompatActivity() {
60 |
61 | override fun onCreate(savedInstanceState: Bundle?) {
62 | super.onCreate(savedInstanceState)
63 | //非常重要,没有这句话监听无法生效
64 | window.setDecorFitsSystemWindows(false)
65 | setContentView(R.layout.activity_main)
66 | val callback = object : WindowInsetsAnimation.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
67 | override fun onProgress(
68 | insets: WindowInsets,
69 | animations: MutableList
70 | ): WindowInsets {
71 | Log.e("MainActivity", "ime:" + insets.getInsets(WindowInsets.Type.ime()).top +
72 | " " + insets.getInsets(WindowInsets.Type.ime()).bottom)
73 | return insets
74 | }
75 | }
76 | content.setWindowInsetsAnimationCallback(callback)
77 | }
78 | }
79 | ```
80 |
81 | 运行结果:
82 |
83 | ```
84 | MainActivity: ime:0 0
85 | MainActivity: ime:0 0
86 | MainActivity: ime:0 9
87 | MainActivity: ime:0 37
88 | MainActivity: ime:0 98
89 | MainActivity: ime:0 207
90 | MainActivity: ime:0 351
91 | MainActivity: ime:0 526
92 | MainActivity: ime:0 684
93 | MainActivity: ime:0 799
94 | MainActivity: ime:0 895
95 | MainActivity: ime:0 1020
96 | MainActivity: ime:0 1062
97 | MainActivity: ime:0 1095
98 | MainActivity: ime:0 1117
99 | MainActivity: ime:0 1134
100 | MainActivity: ime:0 1146
101 | MainActivity: ime:0 1152
102 | MainActivity: ime:0 1155
103 | ```
104 |
105 | 可以看到很清晰的打印出了键盘的每一帧高度,这样我们就可以根据高度回调,实现文章开头的效果
106 |
107 | 对比微信键盘弹出和 Android Q 的键盘弹出,可以看到微信的上升和下降,都不是完全吻合,但是 Android Q 一直是稳稳的贴着:
108 |
109 | 
110 |
111 | ### 详细代码
112 |
113 |
114 | ```kotlin
115 | class MainActivity : AppCompatActivity() {
116 | override fun onCreate(savedInstanceState: Bundle?) {
117 | super.onCreate(savedInstanceState)
118 | //非常重要,没有这句话监听无法生效
119 | window.setDecorFitsSystemWindows(false)
120 | setContentView(R.layout.activity_main)
121 | val callback = object : WindowInsetsAnimation.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
122 | override fun onProgress(
123 | insets: WindowInsets,
124 | animations: MutableList
125 | ): WindowInsets {
126 | val navigationBars = insets.getInsets(WindowInsets.Type.navigationBars())
127 | val ime = insets.getInsets(WindowInsets.Type.ime())
128 | Log.e(
129 | TAG, "ime:" + ime.top +
130 | " " + ime.bottom
131 | )
132 | val parmas = (content.layoutParams as ViewGroup.MarginLayoutParams)
133 | parmas.bottomMargin = ime.bottom - navigationBars.bottom
134 | content.layoutParams = parmas
135 | return insets
136 | return insets
137 | }
138 | }
139 | content.setWindowInsetsAnimationCallback(callback)
140 | }
141 | }
142 | ```
143 |
144 | ## 分析
145 |
146 | 除了上面例子用到的 onProgress() 方法,WindowInsetsAnimation.Callback 还有其他的属性和方法值得关注:
147 |
148 | 1. 分发方式
149 |
150 | 构造 WindowInsetsAnimation.Callback(int) 传入一个int 值表示分发方式,目前有两个值:
151 |
152 | * DISPATCH_MODE_CONTINUE_ON_SUBTREE :继续分发动画事件
153 | * DISPATCH_MODE_STOP :不再分发
154 |
155 | 这两个值和 view 的事件分发很类似,这里就不多解释了。
156 |
157 | 2. 键盘弹出的开始和结束
158 |
159 | 假设需求仅仅是想获取键盘的高度,不需要实时获取高度变化,可以重写 start() 方法
160 |
161 | ```kotlin
162 | val callback = object : WindowInsetsAnimation.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
163 | override fun onStart(
164 | animation: WindowInsetsAnimation,
165 | bounds: WindowInsetsAnimation.Bounds
166 | ): WindowInsetsAnimation.Bounds {
167 | Log.e(TAG,"start lowerBound:" + bounds.lowerBound.top + " " + bounds.lowerBound.bottom)
168 | Log.e( TAG,"start upperBound:" + bounds.upperBound.top + " " + bounds.upperBound.bottom)
169 | Log.e(TAG, "start time:" + animation.durationMillis)
170 | return super.onStart(animation, bounds)
171 | }
172 | }
173 | ```
174 |
175 | 其中 bounds 表示目标对象,从里面可以拿到动画结束后键盘有多高。
176 |
animation 中可以获取动画的执行时间,透明度等等。
177 |
178 | ## 导航栏,状态栏高度和状态
179 |
180 | 获取高度之前先来了解一下 WindowInsets.Type 有什么类型,上面我们用到了 ime() 是键盘,除了键盘,还有其他的类型,包括:
181 |
182 | ```
183 | android.view.WindowInsets.Type.STATUS_BARS, //状态栏
184 | android.view.WindowInsets.Type.NAVIGATION_BARS, //导航栏
185 | android.view.WindowInsets.Type.CAPTION_BAR,
186 | android.view.WindowInsets.Type.IME, //键盘
187 | android.view.WindowInsets.Type.WINDOW_DECOR,
188 | android.view.WindowInsets.Type.SYSTEM_GESTURES,
189 | android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES,
190 | and android.view.WindowInsets.Type.TAPPABLE_ELEMENT
191 | ```
192 |
193 | 类型很多,我们通常关心键盘,状态栏和导航栏
194 |
195 | ### 获取高度和状态
196 |
197 | 在 Android Q 之前,获取状态栏高度通常是通过反射获取。但是有了 WindowInsets 就不用这么麻烦了:
198 |
199 | ```kotlin
200 | content.setOnApplyWindowInsetsListener { view, windowInsets ->
201 | //状态栏
202 | val statusBars = windowInsets.getInsets(WindowInsets.Type.statusBars())
203 | //导航栏
204 | val navigationBars = windowInsets.getInsets(WindowInsets.Type.navigationBars())
205 | //键盘
206 | val ime = windowInsets.getInsets(WindowInsets.Type.ime())
207 | windowInsets
208 | }
209 | ```
210 |
211 | 上面代码可以获取导航栏和状态栏的高度,假设要获取隐藏和显示,可以通过:
212 |
213 | ```kotlin
214 | //注意:setOnApplyWindowInsetsListener 一设置监听就会回调,此时获取的 navigationBars 是否可见是 false
215 | //等绘制完成再去获取就是 true,这个稍微比较坑一点
216 | windowInsets.isVisible(WindowInsets.Type.navigationBars())
217 | ```
218 |
219 | ### 控制各种状态栏的显示和隐藏
220 |
221 | 在 Q 之前,控制导航栏和状态,需要用上各种谜之属性:
222 |
223 | ```kotlin
224 | view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
225 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
226 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
227 | ```
228 |
229 | 新版是这样的:
230 |
231 | ```kotlin
232 | //对状态栏和键盘也可以同样控制
233 | content.windowInsetsController?.show(WindowInsets.Type.navigationBars())
234 | content.windowInsetsController?.hide(WindowInsets.Type.navigationBars())
235 | ```
236 |
237 | ## 其他
238 |
239 | 一番测试下来,新版的 API 对于之前来说,可以说是非常好用了。
240 |
目前存在以下几个问题:
241 |
242 | 1. 不支持旧版
243 |
如果仅是在 Android Q 上使用,那这个工具就没这么香了,希望能通过 androidx 或 support 方式支持
244 |
245 | 2. FitSystemWindows
246 |
在有虚拟导航栏的手机上,FitSystemWindows 设置为 false,会强制改变 Activity 与导航栏的关系。
247 |
默认情况下, Activity 在导航栏的上面,它们处于同一层,但是设置为 false 之后,导航栏会直接覆盖在 Activity 的上面。
248 | 不过这可以通过给 Activity 的 parent 设置一个 padding 来解决。
249 |
250 | 3. 等 Q 发布后,还需要测试国产 ROM,以及第三方键盘的兼容性
251 |
252 | ## 总结
253 |
254 | 总的来说这个工具的出现,使获取,管理键盘等 APP 以外的装饰都变得非常友好。
255 |
由于 Android Q 现在还是预览版,还没有公布源码,暂时不知道其内部实现原理,等后期公布了源码再分析一下原理
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name='My Application'
2 | include ':app'
3 |
--------------------------------------------------------------------------------