├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── colors.xml │ │ │ └── themes.xml │ │ ├── drawable-xxhdpi │ │ │ ├── ic_audio.png │ │ │ ├── ic_back.png │ │ │ ├── ic_face.png │ │ │ ├── ic_more.png │ │ │ └── ic_keyboard.png │ │ ├── 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 │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── drawable │ │ │ ├── bg_edit_person_info.xml │ │ │ ├── bg_hollow.xml │ │ │ ├── bg_hollow3.xml │ │ │ ├── bg_hollow4.xml │ │ │ ├── bg_send_person_info.xml │ │ │ ├── bg_hollow2.xml │ │ │ ├── bg_edit_send_person_info.xml │ │ │ ├── bg_send.xml │ │ │ └── ic_launcher_background.xml │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── xml │ │ │ ├── backup_rules.xml │ │ │ └── data_extraction_rules.xml │ │ ├── layout │ │ │ ├── activity_example3.xml │ │ │ ├── item_msg.xml │ │ │ ├── activity_main.xml │ │ │ ├── dialog_input.xml │ │ │ ├── activity_example_dialog.xml │ │ │ ├── activity_example2.xml │ │ │ └── activity_example1.xml │ │ ├── values-night │ │ │ └── themes.xml │ │ └── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ ├── java │ │ └── com │ │ │ └── flyjingfish │ │ │ └── switchkeyboard │ │ │ ├── MainActivity.java │ │ │ ├── MsgAdapter.java │ │ │ ├── Example3Activity.java │ │ │ ├── ExampleDialogActivity.java │ │ │ ├── BaseActivity.java │ │ │ ├── SysNotifyUtils.java │ │ │ ├── RomHelper.java │ │ │ ├── Example2Activity.java │ │ │ ├── Example1Activity.java │ │ │ ├── Example3Fragment.java │ │ │ ├── InputDialog.java │ │ │ └── StatusBarHelper.java │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── switchkeyboard ├── settings.gradle ├── apk └── release │ ├── app-release.apk │ └── output-metadata.json ├── screenshot ├── download_qrcode.png └── Screenrecording_20230221_192034.gif ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── library ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── flyjingfish │ │ │ └── switchkeyboardlib │ │ │ ├── AutoShowKeyboardType.java │ │ │ ├── MenuModeView.java │ │ │ ├── SystemKeyboardUtils.java │ │ │ ├── SwitchKeyboardUtil.java │ │ │ └── BaseSwitchKeyboardUtil.java │ │ └── AndroidManifest.xml └── build.gradle ├── .gitignore ├── gradle.properties ├── gradlew.bat ├── gradlew ├── LICENSE └── README.md /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /switchkeyboard: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/switchkeyboard -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "SwitchKeyboard" 2 | include ':app' 3 | include ':library' 4 | -------------------------------------------------------------------------------- /apk/release/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/apk/release/app-release.apk -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SwitchKeyboard 3 | -------------------------------------------------------------------------------- /screenshot/download_qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/screenshot/download_qrcode.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/app/src/main/res/drawable-xxhdpi/ic_audio.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/app/src/main/res/drawable-xxhdpi/ic_back.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/app/src/main/res/drawable-xxhdpi/ic_face.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/app/src/main/res/drawable-xxhdpi/ic_more.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/app/src/main/res/drawable-xxhdpi/ic_keyboard.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /screenshot/Screenrecording_20230221_192034.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/screenshot/Screenrecording_20230221_192034.gif -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/SwitchKeyboard/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_edit_person_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /library/src/main/java/com/flyjingfish/switchkeyboardlib/AutoShowKeyboardType.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboardlib; 2 | 3 | public enum AutoShowKeyboardType { 4 | ALWAYS_SHOW,FIRST_SHOW 5 | } 6 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_hollow.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_hollow3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_hollow4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_send_person_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_hollow2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_edit_send_person_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_send.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Oct 11 11:29:56 CST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | local.properties 16 | build/ 17 | .idea 18 | androidTest/ 19 | test/ 20 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /apk/release/output-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "artifactType": { 4 | "type": "APK", 5 | "kind": "Directory" 6 | }, 7 | "applicationId": "com.flyjingfish.switchkeyboard", 8 | "variantName": "release", 9 | "elements": [ 10 | { 11 | "type": "SINGLE", 12 | "filters": [], 13 | "properties": [], 14 | "versionCode": 1, 15 | "versionName": "1.0", 16 | "enabled": true, 17 | "outputFile": "app-release.apk" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | android { 3 | compileSdkVersion rootProject.ext.sdkVersion 4 | 5 | defaultConfig { 6 | minSdkVersion rootProject.ext.minSdkVersion 7 | targetSdkVersion rootProject.ext.sdkVersion 8 | } 9 | compileOptions { 10 | sourceCompatibility JavaVersion.VERSION_1_8 11 | targetCompatibility JavaVersion.VERSION_1_8 12 | } 13 | } 14 | 15 | dependencies { 16 | implementation 'androidx.appcompat:appcompat:1.3.1' 17 | } -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_example3.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_msg.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /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 | 23 | -keep class com.flyjingfish.switchkeyboardlib.** { *; } -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/switchkeyboard/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboard; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | 8 | import com.flyjingfish.switchkeyboard.databinding.ActivityMainBinding; 9 | 10 | 11 | public class MainActivity extends AppCompatActivity { 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater()); 17 | setContentView(binding.getRoot()); 18 | binding.example1.setOnClickListener(v -> startActivity(new Intent(this, Example1Activity.class))); 19 | binding.example2.setOnClickListener(v -> startActivity(new Intent(this, Example2Activity.class))); 20 | binding.example3.setOnClickListener(v -> startActivity(new Intent(this, Example3Activity.class))); 21 | binding.example4.setOnClickListener(v -> startActivity(new Intent(this, ExampleDialogActivity.class))); 22 | } 23 | 24 | @Override 25 | protected void onDestroy() { 26 | super.onDestroy(); 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /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 | # Enables namespacing of each library's R class so that its R class includes only the 19 | # resources declared in the library itself and none from the library's dependencies, 20 | # thereby reducing the size of the R class for that library 21 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/switchkeyboard/MsgAdapter.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboard; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | 7 | import androidx.annotation.NonNull; 8 | import androidx.recyclerview.widget.RecyclerView; 9 | 10 | import com.flyjingfish.switchkeyboard.databinding.ItemMsgBinding; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | 16 | public class MsgAdapter extends RecyclerView.Adapter { 17 | private List list = new ArrayList<>(); 18 | 19 | public MsgAdapter(List list) { 20 | this.list = list; 21 | } 22 | 23 | @NonNull 24 | @Override 25 | public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 26 | return new MyViewHolder(ItemMsgBinding.inflate(LayoutInflater.from(parent.getContext()),parent,false).getRoot()); 27 | } 28 | 29 | @Override 30 | public int getItemCount() { 31 | return list.size(); 32 | } 33 | 34 | @Override 35 | public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { 36 | ItemMsgBinding binding = ItemMsgBinding.bind(holder.itemView); 37 | binding.tvMsg.setText(list.get(position)); 38 | } 39 | 40 | public static class MyViewHolder extends RecyclerView.ViewHolder { 41 | 42 | 43 | public MyViewHolder(@NonNull View itemView) { 44 | super(itemView); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/switchkeyboard/Example3Activity.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboard; 2 | 3 | import android.os.Bundle; 4 | import android.view.KeyEvent; 5 | 6 | import androidx.appcompat.app.AppCompatActivity; 7 | import androidx.fragment.app.Fragment; 8 | import androidx.fragment.app.FragmentTransaction; 9 | 10 | import com.flyjingfish.switchkeyboard.databinding.ActivityExample3Binding; 11 | import com.flyjingfish.switchkeyboardlib.SwitchKeyboardUtil; 12 | 13 | public class Example3Activity extends BaseActivity { 14 | private ActivityExample3Binding binding; 15 | private Example3Fragment example3Fragment; 16 | 17 | @Override 18 | public String getTitleString() { 19 | return Fragment.class.getSimpleName(); 20 | } 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | binding = ActivityExample3Binding.inflate(getLayoutInflater()); 26 | setContentView(binding.getRoot()); 27 | new SwitchKeyboardUtil(this).setSystemUi(); 28 | FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 29 | example3Fragment = new Example3Fragment(); 30 | transaction.replace(R.id.container, example3Fragment).commitAllowingStateLoss(); 31 | } 32 | 33 | 34 | @Override 35 | public boolean onKeyDown(int keyCode, KeyEvent event) { 36 | if (example3Fragment.onKeyDown(keyCode, event)) {//传递給fragment 37 | return true; 38 | } 39 | return super.onKeyDown(keyCode, event); 40 | } 41 | 42 | 43 | } -------------------------------------------------------------------------------- /library/src/main/java/com/flyjingfish/switchkeyboardlib/MenuModeView.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboardlib; 2 | 3 | import android.view.View; 4 | 5 | public class MenuModeView { 6 | /** 7 | * 点击打开菜单的按钮 8 | */ 9 | public View clickToggleView; 10 | /** 11 | * 盛放 clickToggleView 点击打开的菜单布局 12 | */ 13 | public View toggleViewContainer; 14 | /** 15 | * 返回按钮 16 | */ 17 | public View backView; 18 | /** 19 | * clickToggleView 按钮是否在菜单布局中。 false 代表按钮和输入框一栏里,true代表在下方 20 | */ 21 | public boolean clickToggleViewIsMenuContainer; 22 | 23 | public MenuModeView(View clickToggleView, View toggleViewContainer) { 24 | this.clickToggleView = clickToggleView; 25 | this.toggleViewContainer = toggleViewContainer; 26 | } 27 | 28 | public MenuModeView(View clickToggleView, View toggleViewContainer, boolean clickToggleViewIsMenuContainer) { 29 | this.clickToggleView = clickToggleView; 30 | this.toggleViewContainer = toggleViewContainer; 31 | this.clickToggleViewIsMenuContainer = clickToggleViewIsMenuContainer; 32 | } 33 | 34 | public MenuModeView(View clickToggleView, View toggleViewContainer, View backView) { 35 | this.clickToggleView = clickToggleView; 36 | this.toggleViewContainer = toggleViewContainer; 37 | this.backView = backView; 38 | } 39 | 40 | public MenuModeView(View clickToggleView, View toggleViewContainer, View backView, boolean clickToggleViewIsMenuContainer) { 41 | this.clickToggleView = clickToggleView; 42 | this.toggleViewContainer = toggleViewContainer; 43 | this.backView = backView; 44 | this.clickToggleViewIsMenuContainer = clickToggleViewIsMenuContainer; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/switchkeyboard/ExampleDialogActivity.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboard; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.appcompat.app.AppCompatActivity; 6 | import androidx.fragment.app.DialogFragment; 7 | 8 | import com.flyjingfish.switchkeyboard.databinding.ActivityExampleDialogBinding; 9 | import com.flyjingfish.switchkeyboardlib.SwitchKeyboardUtil; 10 | 11 | public class ExampleDialogActivity extends BaseActivity { 12 | private ActivityExampleDialogBinding binding; 13 | 14 | @Override 15 | public String getTitleString() { 16 | return DialogFragment.class.getSimpleName(); 17 | } 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | binding = ActivityExampleDialogBinding.inflate(getLayoutInflater()); 23 | setContentView(binding.getRoot()); 24 | 25 | //请注意这块也是必须做的,不然首次打开DialogFragment时你会看到状态栏被改变了 26 | //当然如果你已经给你的 Activity 设置了透明状态栏,这句是可以忽略掉的 27 | new SwitchKeyboardUtil(this).setSystemUi(); 28 | 29 | binding.llMsgContent.setOnClickListener(v -> { 30 | InputDialog inputDialog = InputDialog.getDialog(binding.etContent.getText().toString()); 31 | inputDialog.setOnContentCallBack(new InputDialog.OnContentCallBack() { 32 | @Override 33 | public void onSendContent(String content) { 34 | 35 | } 36 | 37 | @Override 38 | public void onContent(String content) { 39 | binding.etContent.setText(content); 40 | } 41 | }); 42 | inputDialog.show(getSupportFragmentManager(), "inputDialog"); 43 | }); 44 | } 45 | 46 | 47 | 48 | 49 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 31 | 32 | 36 | 37 | 41 | 42 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/switchkeyboard/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboard; 2 | 3 | import android.graphics.Color; 4 | import android.os.Bundle; 5 | import android.view.ViewGroup; 6 | import android.view.ViewTreeObserver; 7 | import android.view.Window; 8 | 9 | import androidx.appcompat.app.AppCompatActivity; 10 | 11 | import com.flyjingfish.titlebarlib.TitleBar; 12 | 13 | public class BaseActivity extends AppCompatActivity { 14 | protected TitleBar titleBar; 15 | 16 | public boolean isShowTitleBar(){ 17 | return true; 18 | } 19 | 20 | public String getTitleString(){ 21 | return this.getClass().getSimpleName(); 22 | } 23 | 24 | public boolean titleAboveContent(){ 25 | return false; 26 | } 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | StatusBarHelper.setLightStatusBar(this,true,true); 32 | titleBar = new TitleBar(this); 33 | titleBar.setShadow(4, Color.parseColor("#406200EE"), TitleBar.ShadowType.GRADIENT); 34 | titleBar.setTitleGravity(TitleBar.TitleGravity.CENTER); 35 | titleBar.setOnBackViewClickListener(v -> finish()); 36 | if (isShowTitleBar()){ 37 | titleBar.show(); 38 | }else { 39 | titleBar.hide(); 40 | } 41 | titleBar.setTitle(getTitleString()); 42 | titleBar.setAboveContent(titleAboveContent()); 43 | titleBar.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 44 | @Override 45 | public void onGlobalLayout() { 46 | titleBar.getViewTreeObserver().removeOnGlobalLayoutListener(this); 47 | ViewGroup contentView = findViewById(Window.ID_ANDROID_CONTENT); 48 | int height = titleBar.getHeight() - titleBar.getShadowHeight(); 49 | contentView.setPadding(0,height,0,0); 50 | } 51 | }); 52 | titleBar.attachToWindow(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | compileSdkVersion rootProject.ext.sdkVersion 7 | 8 | defaultConfig { 9 | applicationId "com.flyjingfish.switchkeyboard" 10 | minSdkVersion 21 11 | targetSdkVersion rootProject.ext.sdkVersion 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | } 17 | buildFeatures { 18 | viewBinding true 19 | } 20 | 21 | buildTypes { 22 | release { 23 | minifyEnabled true 24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 25 | signingConfig signingConfigs.debug 26 | } 27 | } 28 | compileOptions { 29 | sourceCompatibility JavaVersion.VERSION_1_8 30 | targetCompatibility JavaVersion.VERSION_1_8 31 | } 32 | 33 | } 34 | 35 | configurations.all { 36 | resolutionStrategy { 37 | force 'androidx.core:core-ktx:1.6.0' 38 | force 'androidx.core:core:1.6.0' 39 | 40 | force 'androidx.appcompat:appcompat:1.3.1' 41 | force "androidx.activity:activity:1.3.1" 42 | force "androidx.fragment:fragment:1.3.1" 43 | } 44 | } 45 | dependencies { 46 | 47 | implementation 'androidx.appcompat:appcompat:1.3.1' 48 | implementation 'com.google.android.material:material:1.3.0' 49 | implementation 'androidx.constraintlayout:constraintlayout:2.1.2' 50 | implementation project(path: ':library') 51 | testImplementation 'junit:junit:4.+' 52 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 53 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 54 | implementation 'com.github.FlyJingFish:TitleBar:1.2.8' 55 | //OpenImageFullLib 是完整版,如果您不想自定义图片引擎和视频播放器引擎可直接引用以下库 56 | // implementation 'io.github.FlyJingFish.OpenImage:OpenImageFullLib:1.7.7' 57 | //OpenImageLib 是基础库,没有引入图片引擎和视频播放器 58 | //至少需要实现BigImageHelper来定制您的图片引擎,如需定制视频播放功能,详细看Wiki文档 59 | // implementation 'io.github.FlyJingFish.OpenImage:OpenImageLib:1.7.7' 60 | //OpenImageGlideLib 引入Glide(4.12.0)图片引擎,没有引入视频播放器;如需定制视频播放功能,详细看Wiki文档,如果不想定制可直接使用上边的库 61 | // implementation 'io.github.FlyJingFish.OpenImage:OpenImageGlideLib:1.7.7' 62 | 63 | 64 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 16 | 17 | 28 | 29 | 40 | 41 | 52 | 53 | 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/switchkeyboard/SysNotifyUtils.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboard; 2 | 3 | import android.text.Html; 4 | import android.text.SpannableStringBuilder; 5 | import android.text.Spanned; 6 | import android.text.TextUtils; 7 | import android.text.style.ClickableSpan; 8 | import android.text.style.URLSpan; 9 | import android.view.View; 10 | 11 | public class SysNotifyUtils { 12 | 13 | public static CharSequence getClickableContentFromHtml(String html, OnContentClickListener onHtmlClickListener) { 14 | Spanned spanned = Html.fromHtml(html); 15 | SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(spanned); 16 | SpannableStringBuilder newSpannable = deleteNAndR(spannableStringBuilder); 17 | URLSpan[] urlSpans = newSpannable.getSpans(0, spanned.length(), URLSpan.class); 18 | for (final URLSpan span : urlSpans) { 19 | setLinkClickEnabled(newSpannable, span, onHtmlClickListener); 20 | } 21 | return newSpannable; 22 | } 23 | 24 | private static void setLinkClickEnabled(final SpannableStringBuilder spannableStringBuilder, 25 | final URLSpan urlSpan, OnContentClickListener onHtmlClickListener) { 26 | int start = spannableStringBuilder.getSpanStart(urlSpan); 27 | int end = spannableStringBuilder.getSpanEnd(urlSpan); 28 | int flags = spannableStringBuilder.getSpanFlags(urlSpan); 29 | ClickableSpan clickableSpan = new ClickableSpan() { 30 | 31 | public void onClick(View view) { 32 | if (onHtmlClickListener != null) { 33 | onHtmlClickListener.onClickContent(urlSpan.getURL()); 34 | } 35 | } 36 | }; 37 | spannableStringBuilder.setSpan(clickableSpan, start, end, flags); 38 | } 39 | 40 | public static SpannableStringBuilder deleteNAndR(SpannableStringBuilder ssb) { 41 | if (TextUtils.isEmpty(ssb.toString())) { 42 | return ssb; 43 | } 44 | 45 | if (ssb.length() == 1) { 46 | char ch = ssb.charAt(0); 47 | if ((ch == '\r') || (ch == '\n')) { 48 | return new SpannableStringBuilder(); 49 | } 50 | return ssb; 51 | } 52 | 53 | int lastIdx = ssb.length() - 1; 54 | char lastChar = ssb.charAt(lastIdx); 55 | 56 | if (lastChar != '\n' && lastChar != '\r') { 57 | lastIdx++; 58 | return new SpannableStringBuilder(ssb.subSequence(0, lastIdx)); 59 | } 60 | 61 | return deleteNAndR(new SpannableStringBuilder(ssb.subSequence(0, lastIdx))); 62 | } 63 | 64 | public interface OnContentClickListener { 65 | void onClickContent(String url); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /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/flyjingfish/switchkeyboard/RomHelper.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboard; 2 | 3 | import android.os.Build; 4 | import android.os.Environment; 5 | import android.text.TextUtils; 6 | 7 | import java.io.File; 8 | import java.io.FileInputStream; 9 | import java.util.Properties; 10 | 11 | /** 12 | * Created by zangpeng on 2018/2/24. 13 | */ 14 | 15 | public class RomHelper { 16 | class AvailableRomType { 17 | public static final int MIUI = 1; 18 | public static final int FLYME = 2; 19 | public static final int ANDROID_NATIVE = 3; 20 | public static final int NA = 4; 21 | } 22 | 23 | public static int getLightStatusBarAvailableRomType() { 24 | if (isFlymeV4OrAbove()) { 25 | return AvailableRomType.FLYME; 26 | } 27 | 28 | //开发版 7.7.13 及以后版本采用了系统API,旧方法无效但不会报错 29 | if (isMiUIV7OrAbove()) { 30 | return AvailableRomType.ANDROID_NATIVE; 31 | } 32 | 33 | if (isMiUIV6OrAbove()) { 34 | return AvailableRomType.MIUI; 35 | } 36 | 37 | if (isAndroidMOrAbove()) { 38 | return AvailableRomType.ANDROID_NATIVE; 39 | } 40 | 41 | return AvailableRomType.NA; 42 | } 43 | 44 | //Flyme V4的displayId格式为 [Flyme OS 4.x.x.xA] 45 | //Flyme V5的displayId格式为 [Flyme 5.x.x.x beta] 46 | private static boolean isFlymeV4OrAbove() { 47 | String displayId = Build.DISPLAY; 48 | if (!TextUtils.isEmpty(displayId) && displayId.contains("Flyme")) { 49 | String[] displayIdArray = displayId.split(" "); 50 | for (String temp : displayIdArray) { 51 | //版本号4以上,形如4.x. 52 | if (temp.matches("^[4-9]\\.(\\d+\\.)+\\S*")) { 53 | return true; 54 | } 55 | } 56 | } 57 | return false; 58 | } 59 | 60 | //Android Api 23以上 61 | private static boolean isAndroidMOrAbove() { 62 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; 63 | } 64 | 65 | private static final String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code"; 66 | 67 | private static boolean isMiUIV6OrAbove() { 68 | try { 69 | final Properties properties = new Properties(); 70 | properties.load(new FileInputStream(new File(Environment.getRootDirectory(), "build.prop"))); 71 | String uiCode = properties.getProperty(KEY_MIUI_VERSION_CODE, null); 72 | if (uiCode != null) { 73 | int code = Integer.parseInt(uiCode); 74 | return code >= 4; 75 | } else { 76 | return false; 77 | } 78 | 79 | } catch (final Exception e) { 80 | return false; 81 | } 82 | 83 | } 84 | 85 | static boolean isMiUIV7OrAbove() { 86 | try { 87 | final Properties properties = new Properties(); 88 | properties.load(new FileInputStream(new File(Environment.getRootDirectory(), "build.prop"))); 89 | String uiCode = properties.getProperty(KEY_MIUI_VERSION_CODE, null); 90 | if (uiCode != null) { 91 | int code = Integer.parseInt(uiCode); 92 | return code >= 5; 93 | } else { 94 | return false; 95 | } 96 | 97 | } catch (final Exception e) { 98 | return false; 99 | } 100 | 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /library/src/main/java/com/flyjingfish/switchkeyboardlib/SystemKeyboardUtils.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboardlib; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Rect; 6 | import android.view.View; 7 | import android.view.ViewTreeObserver; 8 | import android.view.inputmethod.InputMethodManager; 9 | 10 | import androidx.annotation.NonNull; 11 | 12 | public class SystemKeyboardUtils { 13 | private View rootView;//activity的根视图 14 | private int rootViewVisibleHeight;//纪录根视图的显示高度 15 | private OnKeyBoardListener onKeyBoardListener; 16 | private boolean isRequestFocus = true; 17 | 18 | public SystemKeyboardUtils(Activity activity,boolean isDialogFragment) { 19 | //获取activity的根视图 20 | rootView = activity.getWindow().getDecorView(); 21 | //获取当前根视图在屏幕上显示的大小 22 | if (isDialogFragment){ 23 | Rect r = new Rect(); 24 | rootView.getWindowVisibleDisplayFrame(r); 25 | int visibleHeight = r.height(); 26 | if (visibleHeight > 0){ 27 | rootViewVisibleHeight = visibleHeight; 28 | } 29 | } 30 | //监听视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变 31 | rootView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener); 32 | } 33 | 34 | private ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener = () -> { 35 | if (isRequestFocus){ 36 | rootView.requestFocus(); 37 | } 38 | //获取当前根视图在屏幕上显示的大小 39 | Rect r = new Rect(); 40 | rootView.getWindowVisibleDisplayFrame(r); 41 | int visibleHeight = r.height(); 42 | if (rootViewVisibleHeight == 0) { 43 | rootViewVisibleHeight = visibleHeight; 44 | return; 45 | } 46 | 47 | //根视图显示高度没有变化,可以看作软键盘显示/隐藏状态没有改变 48 | if (rootViewVisibleHeight == visibleHeight) { 49 | return; 50 | } 51 | 52 | //根视图显示高度变小超过200,可以看作软键盘显示了 53 | if (rootViewVisibleHeight - visibleHeight > 200) { 54 | isRequestFocus = false; 55 | if (onKeyBoardListener != null) { 56 | onKeyBoardListener.onShow(rootViewVisibleHeight - visibleHeight); 57 | } 58 | rootViewVisibleHeight = visibleHeight; 59 | return; 60 | } 61 | 62 | //根视图显示高度变大超过200,可以看作软键盘隐藏了 63 | if (visibleHeight - rootViewVisibleHeight > 200) { 64 | if (onKeyBoardListener != null) { 65 | onKeyBoardListener.onHide(visibleHeight - rootViewVisibleHeight); 66 | } 67 | rootViewVisibleHeight = visibleHeight; 68 | return; 69 | } 70 | 71 | }; 72 | 73 | public void onDestroy(){ 74 | rootView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener); 75 | } 76 | 77 | public void setOnKeyBoardListener(OnKeyBoardListener onKeyBoardListener) { 78 | this.onKeyBoardListener = onKeyBoardListener; 79 | } 80 | 81 | public interface OnKeyBoardListener { 82 | void onShow(int height); 83 | 84 | void onHide(int height); 85 | } 86 | 87 | public static void hideSoftInput(@NonNull final View view) { 88 | InputMethodManager imm = 89 | (InputMethodManager)view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 90 | if (imm == null) return; 91 | imm.hideSoftInputFromWindow(view.getWindowToken(), 0); 92 | } 93 | public static void showSoftInput(Context context) { 94 | InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); 95 | if (imm == null) { 96 | return; 97 | } 98 | imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_input.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 15 | 16 | 17 | 26 | 30 | 42 | 51 | 65 | 66 | 67 | 77 | 78 | 83 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_example_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 12 | 16 | 20 | 21 | 22 | 23 | 30 | 31 | 32 | 41 | 48 | 60 | 76 | 77 | 86 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/switchkeyboard/Example2Activity.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboard; 2 | 3 | import android.os.Bundle; 4 | import android.view.KeyEvent; 5 | import android.view.MotionEvent; 6 | import android.view.View; 7 | 8 | import androidx.annotation.NonNull; 9 | import androidx.appcompat.app.AppCompatActivity; 10 | import androidx.lifecycle.Lifecycle; 11 | import androidx.recyclerview.widget.LinearLayoutManager; 12 | import androidx.recyclerview.widget.RecyclerView; 13 | 14 | import com.flyjingfish.switchkeyboard.databinding.ActivityExample2Binding; 15 | import com.flyjingfish.switchkeyboardlib.AutoShowKeyboardType; 16 | import com.flyjingfish.switchkeyboardlib.MenuModeView; 17 | import com.flyjingfish.switchkeyboardlib.SwitchKeyboardUtil; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | public class Example2Activity extends BaseActivity { 23 | private SwitchKeyboardUtil switchKeyboardUtil; 24 | private ActivityExample2Binding binding; 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | binding = ActivityExample2Binding.inflate(getLayoutInflater()); 30 | setContentView(binding.getRoot()); 31 | switchKeyboardUtil = new SwitchKeyboardUtil(this); 32 | switchKeyboardUtil.setMenuViewHeightEqualKeyboard(true); 33 | switchKeyboardUtil.setUseSwitchAnim(true); 34 | switchKeyboardUtil.setAutoShowKeyboard(true, AutoShowKeyboardType.FIRST_SHOW); 35 | switchKeyboardUtil.attachLifecycle(this); 36 | switchKeyboardUtil.setInputEditText(binding.etContent); 37 | switchKeyboardUtil.setAudioBtn(binding.tvAudio); 38 | switchKeyboardUtil.setAudioTouchView(binding.tvAudioTouch); 39 | switchKeyboardUtil.setMenuViewContainer(binding.llMenu); 40 | switchKeyboardUtil.setToggleMenuViews(new MenuModeView(binding.tvMore, binding.llMenuBtn), 41 | new MenuModeView(binding.tvFace, binding.llEmoji, binding.tvFaceBack,true), 42 | new MenuModeView(binding.tvWord, binding.llWord, binding.tvWordBack,true), 43 | new MenuModeView(binding.tvGift, binding.llGift, binding.tvGiftBack,true) 44 | ); 45 | switchKeyboardUtil.setOnKeyboardMenuListener(new SwitchKeyboardUtil.OnKeyboardMenuListener() { 46 | @Override 47 | public void onScrollToBottom() { 48 | scrollToBottom(); 49 | } 50 | 51 | @Override 52 | public void onCallShowKeyboard() { 53 | 54 | } 55 | 56 | @Override 57 | public void onCallHideKeyboard() { 58 | } 59 | 60 | @Override 61 | public void onKeyboardHide(int keyboardHeight) { 62 | 63 | } 64 | 65 | @Override 66 | public void onKeyboardShow(int keyboardHeight) { 67 | binding.tvAudio.setImageResource(R.drawable.ic_audio); 68 | } 69 | 70 | @Override 71 | public void onShowMenuLayout(View layoutView) { 72 | binding.tvAudio.setImageResource(layoutView == binding.tvAudioTouch?R.drawable.ic_keyboard:R.drawable.ic_audio); 73 | } 74 | 75 | @Override 76 | public void onHideMenuViewContainer() { 77 | binding.tvAudio.setImageResource(R.drawable.ic_audio); 78 | } 79 | }); 80 | 81 | binding.rv.setOnTouchListener((v, event) -> { 82 | if (event.getAction() == MotionEvent.ACTION_DOWN){ 83 | switchKeyboardUtil.hideMenuAndKeyboard(); 84 | } 85 | return false; 86 | }); 87 | 88 | List msgList = new ArrayList<>(); 89 | for (int i = 0; i < 60; i++) { 90 | msgList.add("item=" + i); 91 | } 92 | MsgAdapter msgAdapter = new MsgAdapter(msgList); 93 | binding.rv.setAdapter(msgAdapter); 94 | binding.rv.setLayoutManager(new LinearLayoutManager(this)); 95 | 96 | View.OnLayoutChangeListener onLayoutChangeListener = (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> scrollToBottom(); 97 | binding.rv.addOnScrollListener(new RecyclerView.OnScrollListener() { 98 | @Override 99 | public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { 100 | super.onScrollStateChanged(recyclerView, newState); 101 | if (newState != RecyclerView.SCROLL_STATE_IDLE){ 102 | binding.rv.removeOnLayoutChangeListener(onLayoutChangeListener); 103 | }else { 104 | binding.rv.addOnLayoutChangeListener(onLayoutChangeListener); 105 | } 106 | } 107 | }); 108 | binding.rv.postDelayed(() -> scrollToBottom(),200); 109 | } 110 | 111 | 112 | @Override 113 | public boolean onKeyDown(int keyCode, KeyEvent event) { 114 | if (switchKeyboardUtil.onKeyDown(keyCode, event)) { 115 | return true; 116 | } 117 | return super.onKeyDown(keyCode, event); 118 | } 119 | 120 | @Override 121 | protected void onDestroy() { 122 | super.onDestroy(); 123 | } 124 | 125 | private void scrollToBottom() { 126 | // if (!binding.rv.canScrollVertically(1)){ 127 | if (getLifecycle().getCurrentState() == Lifecycle.State.RESUMED && binding.rv.getAdapter() != null){ 128 | binding.rv.scrollToPosition(binding.rv.getAdapter().getItemCount() - 1); 129 | } 130 | // } 131 | } 132 | } -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/switchkeyboard/Example1Activity.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboard; 2 | 3 | import android.os.Bundle; 4 | import android.view.KeyEvent; 5 | import android.view.MotionEvent; 6 | import android.view.View; 7 | import android.widget.Toast; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.appcompat.app.AppCompatActivity; 11 | import androidx.lifecycle.Lifecycle; 12 | import androidx.recyclerview.widget.LinearLayoutManager; 13 | import androidx.recyclerview.widget.RecyclerView; 14 | 15 | import com.flyjingfish.switchkeyboard.databinding.ActivityExample1Binding; 16 | import com.flyjingfish.switchkeyboardlib.MenuModeView; 17 | import com.flyjingfish.switchkeyboardlib.SwitchKeyboardUtil; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | public class Example1Activity extends BaseActivity { 23 | private SwitchKeyboardUtil switchKeyboardUtil; 24 | private ActivityExample1Binding binding; 25 | private boolean dark; 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | binding = ActivityExample1Binding.inflate(getLayoutInflater()); 30 | setContentView(binding.getRoot()); 31 | switchKeyboardUtil = new SwitchKeyboardUtil(this); 32 | switchKeyboardUtil.setMenuViewHeightEqualKeyboard(false); 33 | switchKeyboardUtil.setUseSwitchAnim(true); 34 | switchKeyboardUtil.setUseMenuUpAnim(true); 35 | switchKeyboardUtil.attachLifecycle(this); 36 | switchKeyboardUtil.setInputEditText(binding.etContent); 37 | switchKeyboardUtil.setAudioBtn(binding.tvAudio); 38 | switchKeyboardUtil.setAudioTouchView(binding.tvAudioTouch); 39 | switchKeyboardUtil.setMenuViewContainer(binding.llMenu); 40 | switchKeyboardUtil.setToggleMenuViews(new MenuModeView(binding.tvMore,binding.llMenuBtn), 41 | new MenuModeView(binding.ivFace,binding.llEmoji), 42 | new MenuModeView(binding.tvGift,binding.llGift), 43 | new MenuModeView(binding.tvWord,binding.llWord)); 44 | switchKeyboardUtil.setOnKeyboardMenuListener(new SwitchKeyboardUtil.OnKeyboardMenuListener() { 45 | @Override 46 | public void onScrollToBottom() { 47 | scrollToBottom(); 48 | } 49 | 50 | @Override 51 | public void onCallShowKeyboard() { 52 | 53 | } 54 | 55 | @Override 56 | public void onCallHideKeyboard() { 57 | } 58 | 59 | @Override 60 | public void onKeyboardHide(int keyboardHeight) { 61 | 62 | } 63 | 64 | @Override 65 | public void onKeyboardShow(int keyboardHeight) { 66 | binding.tvAudio.setImageResource(R.drawable.ic_audio); 67 | binding.ivFace.setImageResource(R.drawable.ic_face); 68 | } 69 | 70 | 71 | 72 | @Override 73 | public void onShowMenuLayout(View layoutView) { 74 | binding.tvAudio.setImageResource(layoutView == binding.tvAudioTouch?R.drawable.ic_keyboard:R.drawable.ic_audio); 75 | binding.ivFace.setImageResource(layoutView == binding.llEmoji?R.drawable.ic_keyboard:R.drawable.ic_face); 76 | } 77 | 78 | @Override 79 | public void onHideMenuViewContainer() { 80 | binding.tvAudio.setImageResource(R.drawable.ic_audio); 81 | binding.ivFace.setImageResource(R.drawable.ic_face); 82 | } 83 | }); 84 | binding.rv.setOnTouchListener((v, event) -> { 85 | if (event.getAction() == MotionEvent.ACTION_DOWN){ 86 | switchKeyboardUtil.hideMenuAndKeyboard(); 87 | } 88 | return false; 89 | }); 90 | 91 | binding.tvVideo.setOnClickListener(v -> { 92 | // Toast.makeText(this,"去视频通话",Toast.LENGTH_SHORT).show() 93 | dark = !dark; 94 | StatusBarHelper.setLightStatusBar(this,dark,false); 95 | switchKeyboardUtil.setSystemUi(); 96 | }); 97 | List msgList = new ArrayList<>(); 98 | for (int i = 0; i < 60; i++) { 99 | msgList.add("item="+i); 100 | } 101 | MsgAdapter msgAdapter = new MsgAdapter(msgList); 102 | binding.rv.setAdapter(msgAdapter); 103 | binding.rv.setLayoutManager(new LinearLayoutManager(this)); 104 | 105 | View.OnLayoutChangeListener onLayoutChangeListener = (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> scrollToBottom(); 106 | binding.rv.addOnScrollListener(new RecyclerView.OnScrollListener() { 107 | @Override 108 | public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { 109 | super.onScrollStateChanged(recyclerView, newState); 110 | if (newState != RecyclerView.SCROLL_STATE_IDLE){ 111 | binding.rv.removeOnLayoutChangeListener(onLayoutChangeListener); 112 | }else { 113 | binding.rv.addOnLayoutChangeListener(onLayoutChangeListener); 114 | } 115 | } 116 | }); 117 | binding.rv.postDelayed(() -> scrollToBottom(),200); 118 | } 119 | 120 | 121 | private void scrollToBottom() { 122 | // if (!binding.rv.canScrollVertically(1)){ 123 | if (getLifecycle().getCurrentState() == Lifecycle.State.RESUMED && binding.rv.getAdapter() != null){ 124 | binding.rv.scrollToPosition(binding.rv.getAdapter().getItemCount() - 1); 125 | } 126 | // } 127 | } 128 | 129 | @Override 130 | public boolean onKeyDown(int keyCode, KeyEvent event) { 131 | if (switchKeyboardUtil.onKeyDown(keyCode, event)){ 132 | return true; 133 | } 134 | return super.onKeyDown(keyCode, event); 135 | } 136 | 137 | 138 | @Override 139 | protected void onDestroy() { 140 | super.onDestroy(); 141 | } 142 | } -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/switchkeyboard/Example3Fragment.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboard; 2 | 3 | import android.os.Bundle; 4 | import android.view.KeyEvent; 5 | import android.view.LayoutInflater; 6 | import android.view.MotionEvent; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.Toast; 10 | 11 | import androidx.annotation.NonNull; 12 | import androidx.annotation.Nullable; 13 | import androidx.fragment.app.Fragment; 14 | import androidx.lifecycle.Lifecycle; 15 | import androidx.recyclerview.widget.LinearLayoutManager; 16 | import androidx.recyclerview.widget.RecyclerView; 17 | 18 | import com.flyjingfish.switchkeyboard.databinding.ActivityExample1Binding; 19 | import com.flyjingfish.switchkeyboardlib.MenuModeView; 20 | import com.flyjingfish.switchkeyboardlib.SwitchKeyboardUtil; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | public class Example3Fragment extends Fragment { 26 | 27 | private com.flyjingfish.switchkeyboard.databinding.ActivityExample1Binding binding; 28 | private SwitchKeyboardUtil switchKeyboardUtil; 29 | private boolean dark; 30 | @Nullable 31 | @Override 32 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 33 | binding = ActivityExample1Binding.inflate(inflater,container,false); 34 | return binding.getRoot(); 35 | } 36 | 37 | @Override 38 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 39 | super.onViewCreated(view, savedInstanceState); 40 | 41 | switchKeyboardUtil = new SwitchKeyboardUtil(requireActivity()); 42 | switchKeyboardUtil.setMenuViewHeightEqualKeyboard(false); 43 | switchKeyboardUtil.setUseSwitchAnim(true); 44 | switchKeyboardUtil.setInputEditText(binding.etContent); 45 | switchKeyboardUtil.setAudioBtn(binding.tvAudio); 46 | switchKeyboardUtil.setAudioTouchView(binding.tvAudioTouch); 47 | switchKeyboardUtil.setMenuViewContainer(binding.llMenu); 48 | switchKeyboardUtil.setToggleMenuViews(new MenuModeView(binding.tvMore,binding.llMenuBtn), 49 | new MenuModeView(binding.ivFace,binding.llEmoji), 50 | new MenuModeView(binding.tvGift,binding.llGift), 51 | new MenuModeView(binding.tvWord,binding.llWord)); 52 | switchKeyboardUtil.attachLifecycle(this); 53 | switchKeyboardUtil.setOnKeyboardMenuListener(new SwitchKeyboardUtil.OnKeyboardMenuListener() { 54 | @Override 55 | public void onScrollToBottom() { 56 | scrollToBottom(); 57 | } 58 | 59 | @Override 60 | public void onCallShowKeyboard() { 61 | 62 | } 63 | 64 | @Override 65 | public void onCallHideKeyboard() { 66 | } 67 | 68 | @Override 69 | public void onKeyboardHide(int keyboardHeight) { 70 | 71 | } 72 | 73 | @Override 74 | public void onKeyboardShow(int keyboardHeight) { 75 | binding.tvAudio.setImageResource(R.drawable.ic_audio); 76 | binding.ivFace.setImageResource(R.drawable.ic_face); 77 | } 78 | 79 | 80 | 81 | @Override 82 | public void onShowMenuLayout(View layoutView) { 83 | binding.tvAudio.setImageResource(layoutView == binding.tvAudioTouch?R.drawable.ic_keyboard:R.drawable.ic_audio); 84 | binding.ivFace.setImageResource(layoutView == binding.llEmoji?R.drawable.ic_keyboard:R.drawable.ic_face); 85 | } 86 | 87 | @Override 88 | public void onHideMenuViewContainer() { 89 | binding.tvAudio.setImageResource(R.drawable.ic_audio); 90 | binding.ivFace.setImageResource(R.drawable.ic_face); 91 | } 92 | }); 93 | binding.rv.setOnTouchListener((v, event) -> { 94 | if (event.getAction() == MotionEvent.ACTION_DOWN){ 95 | switchKeyboardUtil.hideMenuAndKeyboard(); 96 | } 97 | return false; 98 | }); 99 | binding.tvVideo.setOnClickListener(v -> { 100 | dark = !dark; 101 | StatusBarHelper.setLightStatusBar(requireActivity(),dark,false); 102 | // Toast.makeText(requireActivity(),"去视频通话",Toast.LENGTH_SHORT).show() 103 | }); 104 | List msgList = new ArrayList<>(); 105 | for (int i = 0; i < 60; i++) { 106 | msgList.add("item="+i); 107 | } 108 | MsgAdapter msgAdapter = new MsgAdapter(msgList); 109 | binding.rv.setAdapter(msgAdapter); 110 | binding.rv.setLayoutManager(new LinearLayoutManager(requireActivity())); 111 | 112 | View.OnLayoutChangeListener onLayoutChangeListener = (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> scrollToBottom(); 113 | binding.rv.addOnScrollListener(new RecyclerView.OnScrollListener() { 114 | @Override 115 | public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { 116 | super.onScrollStateChanged(recyclerView, newState); 117 | if (newState != RecyclerView.SCROLL_STATE_IDLE){ 118 | binding.rv.removeOnLayoutChangeListener(onLayoutChangeListener); 119 | }else { 120 | binding.rv.addOnLayoutChangeListener(onLayoutChangeListener); 121 | } 122 | } 123 | }); 124 | binding.rv.postDelayed(() -> scrollToBottom(),200); 125 | } 126 | 127 | private void scrollToBottom() { 128 | if (getLifecycle().getCurrentState() == Lifecycle.State.RESUMED && binding.rv.getAdapter() != null){ 129 | binding.rv.scrollToPosition(binding.rv.getAdapter().getItemCount() - 1); 130 | } 131 | } 132 | 133 | public boolean onKeyDown(int keyCode, KeyEvent event) { 134 | return switchKeyboardUtil.onKeyDown(keyCode, event); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /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/flyjingfish/switchkeyboard/InputDialog.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboard; 2 | 3 | import android.app.Dialog; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.text.Editable; 7 | import android.text.TextUtils; 8 | import android.text.TextWatcher; 9 | import android.util.Log; 10 | import android.view.Gravity; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.view.ViewTreeObserver; 15 | import android.view.Window; 16 | 17 | import androidx.annotation.NonNull; 18 | import androidx.annotation.Nullable; 19 | import androidx.fragment.app.DialogFragment; 20 | import androidx.fragment.app.FragmentManager; 21 | 22 | import com.flyjingfish.switchkeyboard.databinding.DialogInputBinding; 23 | import com.flyjingfish.switchkeyboardlib.BaseSwitchKeyboardUtil; 24 | import com.flyjingfish.switchkeyboardlib.MenuModeView; 25 | import com.flyjingfish.switchkeyboardlib.SwitchKeyboardUtil; 26 | 27 | 28 | public class InputDialog extends DialogFragment { 29 | 30 | private static final String CONTENT = "content"; 31 | private String content; 32 | private boolean isShowMenu; 33 | protected OnContentCallBack onContentCallBack; 34 | private com.flyjingfish.switchkeyboard.databinding.DialogInputBinding binding; 35 | @Nullable 36 | @Override 37 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 38 | binding = DialogInputBinding.inflate(inflater,container,false); 39 | return binding.getRoot(); 40 | } 41 | 42 | @Override 43 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 44 | super.onViewCreated(view, savedInstanceState); 45 | Bundle bundle = getArguments(); 46 | if (bundle != null){ 47 | content = bundle.getString(CONTENT); 48 | } 49 | SwitchKeyboardUtil switchKeyboardUtil = new SwitchKeyboardUtil(requireActivity()); 50 | switchKeyboardUtil.setMenuViewHeightEqualKeyboard(true); 51 | switchKeyboardUtil.setUseSwitchAnim(true); 52 | switchKeyboardUtil.setUseMenuUpAnim(true); 53 | switchKeyboardUtil.attachLifecycle(this); 54 | switchKeyboardUtil.setInputEditText(binding.etContent); 55 | switchKeyboardUtil.setMenuViewContainer(binding.llMenu); 56 | switchKeyboardUtil.setAutoShowKeyboard(true); 57 | switchKeyboardUtil.setToggleMenuViews( 58 | new MenuModeView(binding.ivFace,binding.llEmoji)); 59 | switchKeyboardUtil.setOnKeyboardMenuListener(new SwitchKeyboardUtil.OnKeyboardMenuListener() { 60 | @Override 61 | public void onScrollToBottom() { 62 | } 63 | 64 | @Override 65 | public void onCallShowKeyboard() { 66 | 67 | } 68 | 69 | @Override 70 | public void onCallHideKeyboard() { 71 | } 72 | 73 | @Override 74 | public void onKeyboardHide(int keyboardHeight) { 75 | Log.e("inputDialog","====2="+isShowMenu); 76 | if (!isShowMenu){ 77 | dismiss(); 78 | } 79 | } 80 | 81 | @Override 82 | public void onKeyboardShow(int keyboardHeight) { 83 | binding.ivFace.setImageResource(R.drawable.ic_face); 84 | isShowMenu = false; 85 | Log.e("inputDialog","====3="+isShowMenu); 86 | } 87 | 88 | 89 | 90 | @Override 91 | public void onShowMenuLayout(View layoutView) { 92 | isShowMenu = true; 93 | Log.e("inputDialog","====4="+isShowMenu); 94 | binding.ivFace.setImageResource(layoutView == binding.llEmoji?R.drawable.ic_keyboard:R.drawable.ic_face); 95 | } 96 | 97 | @Override 98 | public void onHideMenuViewContainer() { 99 | binding.ivFace.setImageResource(R.drawable.ic_face); 100 | } 101 | }); 102 | 103 | 104 | if (!TextUtils.isEmpty(content)){ 105 | binding.etContent.setText(content); 106 | binding.etContent.setSelection(content.length()); 107 | } 108 | binding.tvSend.setOnClickListener(v -> { 109 | if (onContentCallBack != null){ 110 | onContentCallBack.onSendContent(binding.etContent.getText().toString()); 111 | binding.etContent.setText(""); 112 | } 113 | }); 114 | binding.etContent.addTextChangedListener(new TextWatcher() { 115 | @Override 116 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 117 | 118 | } 119 | 120 | @Override 121 | public void onTextChanged(CharSequence s, int start, int before, int count) { 122 | 123 | } 124 | 125 | @Override 126 | public void afterTextChanged(Editable s) { 127 | if (onContentCallBack != null){ 128 | onContentCallBack.onContent(binding.etContent.getText().toString()); 129 | } 130 | } 131 | }); 132 | 133 | view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 134 | @Override 135 | public void onGlobalLayout() { 136 | binding.etContent.requestFocus(); 137 | view.getViewTreeObserver().removeOnGlobalLayoutListener(this); 138 | } 139 | }); 140 | } 141 | 142 | @Override 143 | public void onCreate(@Nullable Bundle savedInstanceState) { 144 | super.onCreate(savedInstanceState); 145 | setStyle(DialogFragment.STYLE_NORMAL,R.style.DimEnabledInputDialog); 146 | } 147 | 148 | @Override 149 | public void onStart() { 150 | super.onStart(); 151 | Dialog dialog = getDialog(); 152 | Window window; 153 | if (dialog != null && (window = dialog.getWindow()) != null) { 154 | window.setGravity(Gravity.BOTTOM); 155 | window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); 156 | } 157 | } 158 | 159 | @Override 160 | public void show(@NonNull FragmentManager manager, @Nullable String tag) { 161 | try { 162 | manager.beginTransaction().remove(this).commit(); 163 | super.show(manager, tag); 164 | } catch (Exception ignored) { 165 | } 166 | } 167 | 168 | @Override 169 | public void dismiss() { 170 | dismissAllowingStateLoss(); 171 | } 172 | 173 | public static InputDialog getDialog(String content){ 174 | InputDialog inputDialog = new InputDialog(); 175 | Bundle bundle = new Bundle(); 176 | bundle.putString(CONTENT,content); 177 | inputDialog.setArguments(bundle); 178 | return inputDialog; 179 | } 180 | 181 | public void setOnContentCallBack(OnContentCallBack onContentCallBack) { 182 | this.onContentCallBack = onContentCallBack; 183 | } 184 | 185 | public interface OnContentCallBack{ 186 | void onSendContent(String content); 187 | void onContent(String content); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/switchkeyboard/StatusBarHelper.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboard; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.graphics.Rect; 7 | import android.os.Build; 8 | import android.view.View; 9 | import android.view.Window; 10 | import android.view.WindowManager; 11 | 12 | import androidx.annotation.RequiresApi; 13 | 14 | import java.lang.reflect.Field; 15 | import java.lang.reflect.Method; 16 | 17 | 18 | /** 19 | * Created by zangpeng on 2018/2/24. 20 | */ 21 | 22 | public class StatusBarHelper { 23 | /** 24 | * 修改状态栏为全透明 25 | */ 26 | public static void transparencyBar(Activity activity) { 27 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 28 | Window window = activity.getWindow(); 29 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 30 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 31 | window.setStatusBarColor(Color.TRANSPARENT); 32 | window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE 33 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); 34 | 35 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 36 | Window window = activity.getWindow(); 37 | window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, 38 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 39 | } 40 | } 41 | 42 | /** 43 | * 修改状态栏颜色 44 | */ 45 | public static void setStatusBarColor(Activity activity, int colorId) { 46 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 47 | Window window = activity.getWindow(); 48 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 49 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 50 | window.setStatusBarColor(activity.getResources().getColor(colorId)); 51 | } 52 | } 53 | 54 | /** 55 | * 需要MIUIV6以上 56 | * 57 | * @param dark 是否把状态栏文字及图标颜色设置为深色 58 | * @return boolean 成功执行返回true 59 | */ 60 | private static void MIUISetStatusBarLightMode(Object object, boolean dark) { 61 | Window window = null; 62 | if (object instanceof Activity) { 63 | window = ((Activity) object).getWindow(); 64 | } else if (object instanceof Window) { 65 | window = (Window) object; 66 | } 67 | 68 | if (window != null) { 69 | Class clazz = window.getClass(); 70 | try { 71 | int darkModeFlag; 72 | Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams"); 73 | Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE"); 74 | darkModeFlag = field.getInt(layoutParams); 75 | Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class); 76 | if (dark) { 77 | extraFlagField.invoke(window, darkModeFlag, darkModeFlag);//状态栏透明且黑色字体 78 | } else { 79 | extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字体 80 | } 81 | 82 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && RomHelper.isMiUIV7OrAbove()) { 83 | //开发版 7.7.13 及以后版本采用了系统API,旧方法无效但不会报错,所以两个方式都要加上 84 | if (dark) { 85 | window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); 86 | } else { 87 | window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); 88 | } 89 | } 90 | 91 | } catch (Exception ignore) { 92 | 93 | } 94 | } 95 | } 96 | 97 | 98 | /** 99 | * @param dark true 字体颜色为黑色,false为白色 100 | * @param isFullMode 是否在全屏模式下 101 | */ 102 | public static void setLightStatusBar(final Activity activity, final boolean dark, boolean isFullMode) { 103 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 104 | int DEVICE_FIRM = -1; 105 | if (DEVICE_FIRM == -1) { 106 | DEVICE_FIRM = RomHelper.getLightStatusBarAvailableRomType(); 107 | } 108 | switch (DEVICE_FIRM) { 109 | case RomHelper.AvailableRomType.MIUI: 110 | MIUISetStatusBarLightMode(activity, dark); 111 | break; 112 | 113 | case RomHelper.AvailableRomType.FLYME: 114 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { 115 | setAndroidNativeLightStatusBar(activity, dark, isFullMode); 116 | } else { 117 | setFlymeLightStatusBar(activity, dark); 118 | } 119 | break; 120 | 121 | case RomHelper.AvailableRomType.ANDROID_NATIVE: 122 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 123 | setAndroidNativeLightStatusBar(activity, dark, isFullMode); 124 | } 125 | break; 126 | 127 | case RomHelper.AvailableRomType.NA: 128 | // N/A do nothing 129 | break; 130 | } 131 | } 132 | 133 | 134 | } 135 | 136 | @RequiresApi(api = Build.VERSION_CODES.M) 137 | public static void setLightStatusBar(final Window window, final boolean dark, boolean isFullMode) { 138 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 139 | switch (RomHelper.getLightStatusBarAvailableRomType()) { 140 | case RomHelper.AvailableRomType.MIUI: 141 | MIUISetStatusBarLightMode(window, dark); 142 | break; 143 | 144 | case RomHelper.AvailableRomType.FLYME: 145 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { 146 | setAndroidNativeLightStatusBar(window, dark, isFullMode); 147 | } else { 148 | setFlymeLightStatusBar(window, dark); 149 | } 150 | break; 151 | 152 | case RomHelper.AvailableRomType.ANDROID_NATIVE: 153 | setAndroidNativeLightStatusBar(window, dark, isFullMode); 154 | break; 155 | 156 | case RomHelper.AvailableRomType.NA: 157 | // N/A do nothing 158 | break; 159 | } 160 | } 161 | 162 | 163 | } 164 | 165 | private static boolean setFlymeLightStatusBar(Object obj, boolean dark) { 166 | boolean result = false; 167 | Window window = null; 168 | if (obj instanceof Activity) { 169 | window = ((Activity) obj).getWindow(); 170 | } else if (obj instanceof Window) { 171 | window = ((Window) obj); 172 | } 173 | 174 | if (window != null) { 175 | try { 176 | WindowManager.LayoutParams lp = window.getAttributes(); 177 | Field darkFlag = WindowManager.LayoutParams.class 178 | .getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON"); 179 | Field meizuFlags = WindowManager.LayoutParams.class 180 | .getDeclaredField("meizuFlags"); 181 | darkFlag.setAccessible(true); 182 | meizuFlags.setAccessible(true); 183 | int bit = darkFlag.getInt(null); 184 | int value = meizuFlags.getInt(lp); 185 | if (dark) { 186 | value |= bit; 187 | } else { 188 | value &= ~bit; 189 | } 190 | meizuFlags.setInt(lp, value); 191 | window.setAttributes(lp); 192 | result = true; 193 | } catch (Exception ignore) { 194 | } 195 | } 196 | return result; 197 | } 198 | 199 | @RequiresApi(api = Build.VERSION_CODES.M) 200 | private static void setAndroidNativeLightStatusBar(Object obj, boolean dark, boolean isFullMode) { 201 | View decor = null; 202 | if (obj instanceof Activity) { 203 | decor = ((Activity) obj).getWindow().getDecorView(); 204 | } else if (obj instanceof Window) { 205 | decor = ((Window) obj).getDecorView(); 206 | } 207 | 208 | if (decor == null) { 209 | return; 210 | } 211 | 212 | if (dark) { 213 | if (isFullMode) { 214 | decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); 215 | } else { 216 | decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); 217 | } 218 | } else { 219 | // We want to change tint color to white again. 220 | // You can also record the flags in advance so that you can turn UI back completely if 221 | // you have set other flags before, such as translucent or full screen. 222 | if (isFullMode) { 223 | decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); 224 | } else { 225 | decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); 226 | } 227 | } 228 | } 229 | 230 | /** 231 | * 获得通知栏高度 232 | */ 233 | public static int getStatusBarHeight(Context context) { 234 | int statusBarHeight = 0; 235 | try { 236 | Rect frame = new Rect(); 237 | ((Activity) context).getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); 238 | statusBarHeight = frame.top; 239 | } catch (Exception e) { 240 | } 241 | if (statusBarHeight == 0) { 242 | try { 243 | int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); 244 | statusBarHeight = context.getResources().getDimensionPixelSize(resourceId); 245 | } catch (Exception e) { 246 | } 247 | } 248 | 249 | return statusBarHeight; 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2022] [FlyJingFish] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwitchKeyboard 2 | ## 切换键盘和菜单的工具类,旨在解决切换键盘和菜单时的跳动问题 3 | 4 | [![](https://jitpack.io/v/FlyJingFish/SwitchKeyboard.svg)](https://jitpack.io/#FlyJingFish/SwitchKeyboard) 5 | [![GitHub stars](https://img.shields.io/github/stars/FlyJingFish/SwitchKeyboard.svg)](https://github.com/FlyJingFish/SwitchKeyboard/stargazers) 6 | [![GitHub forks](https://img.shields.io/github/forks/FlyJingFish/SwitchKeyboard.svg)](https://github.com/FlyJingFish/SwitchKeyboard/network) 7 | [![GitHub issues](https://img.shields.io/github/issues/FlyJingFish/SwitchKeyboard.svg)](https://github.com/FlyJingFish/SwitchKeyboard/issues) 8 | [![GitHub license](https://img.shields.io/github/license/FlyJingFish/SwitchKeyboard.svg)](https://github.com/FlyJingFish/SwitchKeyboard/blob/master/LICENSE) 9 | 10 | 11 | ## [点此下载apk,也可扫下边二维码下载](https://github.com/FlyJingFish/SwitchKeyboard/blob/master/apk/release/app-release.apk?raw=true) 12 | 13 | show 14 | 15 | show 16 | 17 | 18 | ## 第一步,根目录build.gradle 19 | 20 | ```gradle 21 | allprojects { 22 | repositories { 23 | ... 24 | maven { url 'https://jitpack.io' } 25 | } 26 | } 27 | ``` 28 | ## 第二步,需要引用的build.gradle (最新版本[![](https://jitpack.io/v/FlyJingFish/SwitchKeyboard.svg)](https://jitpack.io/#FlyJingFish/SwitchKeyboard)) 29 | 30 | ```gradle 31 | dependencies { 32 | implementation 'com.github.FlyJingFish:SwitchKeyboard:1.2.3' 33 | } 34 | ``` 35 | ## 第三步,使用说明 36 | 37 | **布局要求** 38 | 39 | ```xml 40 | 47 | 48 | 59 | 60 | 61 | 71 | 72 | 79 | 80 | 81 | 82 | 87 | 88 | 89 | 90 | 97 | 98 | 99 | 100 | 101 | 102 | ``` 103 | 104 | **使用说明** 105 | 106 | ```java 107 | 108 | public class Example2Activity extends AppCompatActivity { 109 | private SwitchKeyboardUtil switchKeyboardUtil; 110 | 111 | @Override 112 | protected void onCreate(Bundle savedInstanceState) { 113 | super.onCreate(savedInstanceState); 114 | setContentView(R.layout.activity_example1); 115 | switchKeyboardUtil = new SwitchKeyboardUtil(this); 116 | //是否让菜单高度和键盘高度一样(首次可能会有误差) 117 | switchKeyboardUtil.setMenuViewHeightEqualKeyboard(false); 118 | //切换时是否使用动画(默认开启) 119 | switchKeyboardUtil.setUseSwitchAnim(true); 120 | //菜单之间切换时从底部弹出 setUseSwitchAnim(true) 时才起作用 121 | switchKeyboardUtil.setUseMenuUpAnim(true); 122 | //所有设置设置这个之后才起效 123 | switchKeyboardUtil.attachLifecycle(this); 124 | //输入框(必须设置) 125 | switchKeyboardUtil.setInputEditText(etContent); 126 | //切换语音的按钮(不必设置) 127 | switchKeyboardUtil.setAudioBtn(tvAudio); 128 | //语音录制按钮(不必设置) 129 | switchKeyboardUtil.setAudioTouchVIew(tvAudioTouch); 130 | //存放所有菜单的布局(必须设置) 131 | switchKeyboardUtil.setMenuViewContainer(llMenu); 132 | //进入页面时是否自动弹出键盘 133 | switchKeyboardUtil.setAutoShowKeyboard(true, AutoShowKeyboardType.FIRST_SHOW); 134 | //设置切换菜单的切换按钮和菜单布局(不必设置) 135 | switchKeyboardUtil.setToggleMenuViews( 136 | new MenuModeView(tvMore,llMenuBtn), 137 | new MenuModeView(ivFace,llEmoji), 138 | new MenuModeView(tvGift,llGift), 139 | new MenuModeView(tvWord,llWord)); 140 | //设置监听 141 | switchKeyboardUtil.setOnKeyboardMenuListener(new BaseSwitchKeyboardUtil.OnKeyboardMenuListener() { 142 | @Override 143 | public void onScrollToBottom() { 144 | //如果你需要让聊天内容在打开菜单或键盘时滑动到底部,则在此写代码 145 | scrollToBottom(); 146 | } 147 | 148 | @Override 149 | public void onCallShowKeyboard() { 150 | //当调用显示键盘前回调 151 | } 152 | 153 | @Override 154 | public void onCallHideKeyboard() { 155 | //当调用隐藏键盘前回调 156 | } 157 | 158 | @Override 159 | public void onKeyboardHide(int keyboardHeight) { 160 | //当键盘隐藏后回调 161 | } 162 | 163 | @Override 164 | public void onKeyboardShow(int keyboardHeight) { 165 | //当键盘显示后回调 166 | tvAudio.setImageResource(R.drawable.ic_audio); 167 | ivFace.setImageResource(R.drawable.ic_face); 168 | } 169 | 170 | @Override 171 | public void onShowMenuLayout(View layoutView) { 172 | //当显示某个菜单布局(即 MenuModeView.toggleViewContainer )时回调 173 | tvAudio.setImageResource(layoutView == tvAudioTouch?R.drawable.ic_keyboard:R.drawable.ic_audio); 174 | ivFace.setImageResource(layoutView == llEmoji?R.drawable.ic_keyboard:R.drawable.ic_face); 175 | } 176 | 177 | @Override 178 | public void onHideMenuViewContainer() { 179 | //当收起菜单时回调这个方法 180 | binding.tvAudio.setImageResource(R.drawable.ic_audio); 181 | binding.ivFace.setImageResource(R.drawable.ic_face); 182 | } 183 | }); 184 | //你的输入框如果需要设置 setOnTouchListener 请调用这个,否则将会影响切换动画 185 | switchKeyboardUtil.setEtContentOnTouchListener(new View.OnTouchListener() { 186 | @Override 187 | public boolean onTouch(View v, MotionEvent event) { 188 | return false; 189 | } 190 | }); 191 | 192 | recyclerView.setOnTouchListener(new View.OnTouchListener() { 193 | @Override 194 | public boolean onTouch(View v, MotionEvent event) { 195 | if (event.getAction() == MotionEvent.ACTION_DOWN){ 196 | //收起键盘或菜单 197 | switchKeyboardUtil.hideMenuAndKeyboard(); 198 | } 199 | return false; 200 | } 201 | }); 202 | //这个是保持消息平滑移动的关键 203 | View.OnLayoutChangeListener onLayoutChangeListener = (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> scrollToBottom(); 204 | recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { 205 | @Override 206 | public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { 207 | super.onScrollStateChanged(recyclerView, newState); 208 | if (newState != RecyclerView.SCROLL_STATE_IDLE){ 209 | recyclerView.removeOnLayoutChangeListener(onLayoutChangeListener); 210 | }else { 211 | recyclerView.addOnLayoutChangeListener(onLayoutChangeListener); 212 | } 213 | } 214 | }); 215 | } 216 | 217 | 218 | @Override 219 | public boolean onKeyDown(int keyCode, KeyEvent event) { 220 | //此处是用于点击手机返回按钮时关闭菜单 221 | if (switchKeyboardUtil.onKeyDown(keyCode, event)) { 222 | return true; 223 | } 224 | return super.onKeyDown(keyCode, event); 225 | } 226 | 227 | private void scrollToBottom() { 228 | recyclerView.scrollToPosition(recyclerView.getAdapter().getItemCount() - 1); 229 | } 230 | } 231 | ``` 232 | 233 | 234 | 235 | **更多代码中使用方式如下:** 236 | 237 | 1、假如有按钮**在输入框一栏** 打开底部隐藏菜单时 详情可看[Example1Activity](https://github.com/FlyJingFish/SwitchKeyboard/blob/master/app/src/main/java/com/flyjingfish/switchkeyboard/Example1Activity.java) 对应布局 [activity_example1](https://github.com/FlyJingFish/SwitchKeyboard/blob/master/app/src/main/res/layout/activity_example1.xml) 238 | 239 | 2、假如有按钮**不在输入框一栏时** 打开底部隐藏菜单时 详情可看[Example2Activity](https://github.com/FlyJingFish/SwitchKeyboard/blob/master/app/src/main/java/com/flyjingfish/switchkeyboard/Example2Activity.java) 对应布局 [activity_example2](https://github.com/FlyJingFish/SwitchKeyboard/blob/master/app/src/main/res/layout/activity_example2.xml) 240 | 241 | 3、如果**在Fragment中使用** 详情可看[Example3Activity](https://github.com/FlyJingFish/SwitchKeyboard/blob/master/app/src/main/java/com/flyjingfish/switchkeyboard/Example3Activity.java) 对应Fragment [Example3Fragment](https://github.com/FlyJingFish/SwitchKeyboard/blob/master/app/src/main/java/com/flyjingfish/switchkeyboard/Example3Fragment.java) 242 | 243 | 4、如果**在DialogFragment中使用** 详情可看[ExampleDialogActivity](https://github.com/FlyJingFish/SwitchKeyboard/blob/master/app/src/main/java/com/flyjingfish/switchkeyboard/ExampleDialogActivity.java) 对应DialogFragment [InputDialog](https://github.com/FlyJingFish/SwitchKeyboard/blob/master/app/src/main/java/com/flyjingfish/switchkeyboard/InputDialog.java) 244 | 245 | ## 混淆规则 246 | 247 | ```pro 248 | 249 | -keep class com.flyjingfish.switchkeyboardlib.** { *; } 250 | 251 | ``` 252 | 253 | ## 特别注意(如果不注意的话可能会显示异常) 254 | 255 | 1、本工具类设置了布局全屏显示,并设置了透明状态栏,请自行适配顶部返回键一栏距离顶部的距离(可搭配使用[TitleBar](https://github.com/FlyJingFish/TitleBar),使用教程看[BaseActivity](https://github.com/FlyJingFish/SwitchKeyboard/blob/master/app/src/main/java/com/flyjingfish/switchkeyboard/BaseActivity.java))和状态栏字体颜色 256 | 257 | **PS:** 如果你设置状态栏字体颜色后出现问题,可在你的代码后**再次调用 setSystemUi() 来修正** 258 | 259 | (正常来说如果你在onCreate 中设置过一次字体颜色是没有问题的,但如果在其他地方还有设置字体颜色是有可能影响的,原因是你切换字体颜色的代码影响了原本的设置) 260 | 261 | 2、布局中不可使用 fitsSystemWindows 属性为true,本库目前已将布局中影响较为大的几个节点自动设置为了false 262 | 263 | **PS:** 如果你非要设置 fitsSystemWindows 将会导致显示异常,所以请尽可能的不要使用这个属性 264 | 265 | ## 关于打开页面时键盘的状态 266 | 267 | 1、如果想自动弹出键盘,可通过设置**switchKeyboardUtil.setAutoShowKeyboard()** 或者如果是Activity您可在 AndroidManifest.xml 中设置 windowSoftInputMode 为 stateAlwaysVisible 或 stateVisible 268 | 269 | 2、有些机型会自动打开键盘,可设置 windowSoftInputMode 模式来解决,例如你不希望打开键盘就可以设置 stateAlwaysHidden 或 stateHidden 270 | 271 | 272 | ## 最后推荐我写的另外一些库 273 | 274 | - [OpenImage 轻松实现在应用内点击小图查看大图的动画放大效果](https://github.com/FlyJingFish/OpenImage) 275 | 276 | - [AndroidAOP 一个注解就可请求权限,禁止多点,切换线程等等,更可定制出属于你的 Aop 代码](https://github.com/FlyJingFish/AndroidAOP) 277 | 278 | - [主页查看更多开源库](https://github.com/FlyJingFish) 279 | 280 | 281 | 282 | -------------------------------------------------------------------------------- /library/src/main/java/com/flyjingfish/switchkeyboardlib/SwitchKeyboardUtil.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboardlib; 2 | 3 | import android.animation.AnimatorSet; 4 | import android.animation.ObjectAnimator; 5 | import android.app.Activity; 6 | import android.view.KeyEvent; 7 | import android.view.View; 8 | import android.view.ViewTreeObserver; 9 | import android.widget.FrameLayout; 10 | import android.widget.LinearLayout; 11 | import android.widget.RelativeLayout; 12 | 13 | import androidx.annotation.NonNull; 14 | import androidx.lifecycle.LifecycleOwner; 15 | 16 | public class SwitchKeyboardUtil extends BaseSwitchKeyboardUtil { 17 | private OnKeyboardMenuListener onKeyboardMenuListener; 18 | private final MenuModeView IDLE = new MenuModeView(null,null); 19 | private MenuModeView menuMode = IDLE; 20 | private MenuModeView[] menuModeViews; 21 | private View lastVisibleView; 22 | private MenuModeView lastMenuModeView; 23 | 24 | public SwitchKeyboardUtil(Activity activity) { 25 | super(activity); 26 | } 27 | 28 | /** 29 | * 30 | * @param menuModeView 展开的点击按钮和对应的隐藏布局 31 | */ 32 | public void setToggleMenuViews(MenuModeView... menuModeView) { 33 | menuModeViews = menuModeView; 34 | } 35 | 36 | @Override 37 | protected void onCreate(@NonNull LifecycleOwner owner) { 38 | super.onCreate(owner); 39 | super.setOnKeyboardMenuListener(new OnKeyboardMenuListener() { 40 | @Override 41 | public void onScrollToBottom() { 42 | if (onKeyboardMenuListener != null) { 43 | onKeyboardMenuListener.onScrollToBottom(); 44 | } 45 | } 46 | 47 | @Override 48 | public void onKeyboardHide(int keyboardHeight) { 49 | if (menuMode == IDLE && menuViewContainer != null){ 50 | menuViewContainer.setVisibility(View.GONE); 51 | } 52 | if (onKeyboardMenuListener != null) { 53 | onKeyboardMenuListener.onKeyboardHide(keyboardHeight); 54 | } 55 | } 56 | 57 | @Override 58 | public void onKeyboardShow(int keyboardHeight) { 59 | menuMode = IDLE; 60 | isShowMenu = false; 61 | if (onKeyboardMenuListener != null) { 62 | onKeyboardMenuListener.onKeyboardShow(keyboardHeight); 63 | } 64 | } 65 | 66 | @Override 67 | public void onCallShowKeyboard() { 68 | menuMode = IDLE; 69 | if (onKeyboardMenuListener != null) { 70 | onKeyboardMenuListener.onCallShowKeyboard(); 71 | } 72 | } 73 | 74 | @Override 75 | public void onCallHideKeyboard() { 76 | if (onKeyboardMenuListener != null) { 77 | onKeyboardMenuListener.onCallHideKeyboard(); 78 | } 79 | } 80 | 81 | @Override 82 | public void onShowMenuLayout(View layoutView) { 83 | if (onKeyboardMenuListener != null) { 84 | onKeyboardMenuListener.onShowMenuLayout(layoutView); 85 | } 86 | } 87 | 88 | @Override 89 | public void onHideMenuViewContainer() { 90 | if (onKeyboardMenuListener != null) { 91 | onKeyboardMenuListener.onHideMenuViewContainer(); 92 | } 93 | } 94 | }); 95 | if (menuModeViews != null){ 96 | int childNum = menuViewContainer.getChildCount(); 97 | for (MenuModeView menuModeView : menuModeViews) { 98 | boolean isHasView = false; 99 | for (int i = 0; i < childNum; i++) { 100 | View view = menuViewContainer.getChildAt(i); 101 | if (view == menuModeView.toggleViewContainer){ 102 | isHasView = true; 103 | } 104 | } 105 | if (!isHasView){ 106 | throw new IllegalArgumentException("menuModeView.toggleViewContainer 必须是 menuViewContainer 的子View"); 107 | } 108 | menuModeView.clickToggleView.setOnClickListener(v -> { 109 | if (menuModeView.clickToggleViewIsMenuContainer){ 110 | switchMenuIn(menuModeView); 111 | }else { 112 | switchMenu(menuModeView); 113 | } 114 | }); 115 | if (menuModeView.backView != null){ 116 | menuModeView.backView.setOnClickListener(v -> { 117 | for (MenuModeView menuModeView2 : menuModeViews) { 118 | menuModeView2.toggleViewContainer.setVisibility(View.GONE); 119 | } 120 | if (lastVisibleView != null){ 121 | lastVisibleView.setVisibility(View.VISIBLE); 122 | } 123 | setSwitchAnim(lastMenuModeView,menuViewContainer.getVisibility(),false); 124 | }); 125 | } 126 | } 127 | } 128 | } 129 | 130 | private void switchMenu(MenuModeView clickViewMenuMode){ 131 | recordLastVisibleView(); 132 | int menuViewContainerVisibility = menuViewContainer.getVisibility(); 133 | if (menuMode == IDLE){ 134 | menuMode = clickViewMenuMode; 135 | boolean showMenu = toggleMoreView(); 136 | for (MenuModeView menuModeView : menuModeViews) { 137 | menuModeView.toggleViewContainer.setVisibility(clickViewMenuMode == menuModeView?View.VISIBLE:View.GONE); 138 | } 139 | setSwitchAnim(clickViewMenuMode,menuViewContainerVisibility,showMenu); 140 | if (onKeyboardMenuListener != null){ 141 | onKeyboardMenuListener.onShowMenuLayout(clickViewMenuMode.toggleViewContainer); 142 | } 143 | }else if (menuMode != clickViewMenuMode){ 144 | isShowMenu = true; 145 | menuMode = clickViewMenuMode; 146 | for (MenuModeView menuModeView : menuModeViews) { 147 | menuModeView.toggleViewContainer.setVisibility(clickViewMenuMode == menuModeView?View.VISIBLE:View.GONE); 148 | } 149 | setSwitchAnim(clickViewMenuMode,menuViewContainerVisibility,false); 150 | if (onKeyboardMenuListener != null){ 151 | onKeyboardMenuListener.onShowMenuLayout(clickViewMenuMode.toggleViewContainer); 152 | } 153 | }else { 154 | boolean showMenu = toggleMoreView(); 155 | if (showMenu){ 156 | for (MenuModeView menuModeView : menuModeViews) { 157 | menuModeView.toggleViewContainer.setVisibility(clickViewMenuMode == menuModeView?View.VISIBLE:View.GONE); 158 | } 159 | setSwitchAnim(clickViewMenuMode,menuViewContainerVisibility,true); 160 | if (onKeyboardMenuListener != null){ 161 | onKeyboardMenuListener.onShowMenuLayout(clickViewMenuMode.toggleViewContainer); 162 | } 163 | }else { 164 | showKeyboardAnim(); 165 | } 166 | } 167 | } 168 | 169 | 170 | 171 | private void switchMenuIn(MenuModeView clickViewMenuMode){ 172 | recordLastVisibleView(); 173 | for (MenuModeView menuModeView : menuModeViews) { 174 | menuModeView.toggleViewContainer.setVisibility(clickViewMenuMode == menuModeView?View.VISIBLE:View.GONE); 175 | } 176 | setSwitchAnim(clickViewMenuMode,menuViewContainer.getVisibility(),false); 177 | } 178 | 179 | private void setSwitchAnim(MenuModeView clickViewMenuMode, int menuViewContainerVisibility,boolean isHideKeyboard){ 180 | if (lastVisibleView == null || !useSwitchAnim || (menuViewHeightEqualKeyboard && keyboardHeight == 0)){ 181 | return; 182 | } 183 | final ViewHeight viewHeight = new ViewHeight(menuViewContainer); 184 | if (menuViewContainerVisibility == View.GONE){ 185 | viewHeight.setViewHeight(0); 186 | } 187 | clickViewMenuMode.toggleViewContainer.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 188 | @Override 189 | public void onGlobalLayout() { 190 | stopSwitchAnim(); 191 | clickViewMenuMode.toggleViewContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this); 192 | 193 | menuViewContainer.setVisibility(View.VISIBLE); 194 | int startHeight = menuViewContainerVisibility == View.GONE?0: menuViewContainer.getHeight(); 195 | int marginVertical = 0; 196 | if (menuViewContainer instanceof FrameLayout){ 197 | FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) clickViewMenuMode.toggleViewContainer.getLayoutParams(); 198 | marginVertical = layoutParams.topMargin+layoutParams.bottomMargin; 199 | }else if (menuViewContainer instanceof RelativeLayout){ 200 | RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) clickViewMenuMode.toggleViewContainer.getLayoutParams(); 201 | marginVertical = layoutParams.topMargin+layoutParams.bottomMargin; 202 | }else if (menuViewContainer instanceof LinearLayout){ 203 | LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) clickViewMenuMode.toggleViewContainer.getLayoutParams(); 204 | marginVertical = layoutParams.topMargin+layoutParams.bottomMargin; 205 | } 206 | clickViewMenuMode.toggleViewContainer.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); 207 | int endHeight = clickViewMenuMode.toggleViewContainer.getMeasuredHeight()+menuViewContainer.getPaddingTop()+menuViewContainer.getPaddingBottom()+marginVertical; 208 | int distance = Math.abs(startHeight - endHeight); 209 | int duration = distance/SWITCH_ANIM_SPEED; 210 | if (duration<200){ 211 | duration = 200; 212 | } 213 | int finalEndHeight = menuViewHeightEqualKeyboard?keyboardHeight:endHeight; 214 | if (startHeight != finalEndHeight){ 215 | ObjectAnimator switchAnim1 = ObjectAnimator.ofInt(viewHeight,"viewHeight",startHeight,finalEndHeight); 216 | switchAnim1.setDuration(duration); 217 | switchAnim = new AnimatorSet(); 218 | if (!isHideKeyboard && useMenuUpAnim){ 219 | ObjectAnimator switchAnim2 = ObjectAnimator.ofFloat(clickViewMenuMode.toggleViewContainer,"translationY",finalEndHeight,0); 220 | switchAnim2.setDuration(duration); 221 | switchAnim.playTogether(switchAnim1,switchAnim2); 222 | }else { 223 | switchAnim.playTogether(switchAnim1); 224 | } 225 | switchAnim.start(); 226 | }else { 227 | viewHeight.setViewHeight(finalEndHeight); 228 | if (!isHideKeyboard && useMenuUpAnim){ 229 | ObjectAnimator switchAnim2 = ObjectAnimator.ofFloat(clickViewMenuMode.toggleViewContainer,"translationY",finalEndHeight,0); 230 | switchAnim2.setDuration(duration); 231 | switchAnim = new AnimatorSet(); 232 | switchAnim.playTogether(switchAnim2); 233 | switchAnim.start(); 234 | } 235 | } 236 | } 237 | }); 238 | } 239 | 240 | 241 | 242 | private void recordLastVisibleView(){ 243 | for (MenuModeView menuModeView : menuModeViews) { 244 | if (menuModeView.toggleViewContainer.getVisibility() == View.VISIBLE){ 245 | lastVisibleView = menuModeView.toggleViewContainer; 246 | lastMenuModeView = menuModeView; 247 | } 248 | } 249 | } 250 | 251 | @Override 252 | public boolean onKeyDown(int keyCode, KeyEvent event) { 253 | if (super.onKeyDown(keyCode, event)){ 254 | menuMode = IDLE; 255 | return true; 256 | } 257 | return false; 258 | } 259 | 260 | @Override 261 | public void setOnKeyboardMenuListener(OnKeyboardMenuListener onKeyboardMenuListener) { 262 | this.onKeyboardMenuListener = onKeyboardMenuListener; 263 | } 264 | 265 | } 266 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_example2.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 12 | 16 | 20 | 21 | 22 | 23 | 30 | 31 | 32 | 41 | 48 | 60 | 76 | 77 | 86 | 100 | 101 | 112 | 119 | 124 | 134 | 145 | 156 | 157 | 158 | 164 | 173 | 183 | 193 | 194 | 195 | 196 | 197 | 198 | 204 | 210 | 217 | 218 | 224 | 230 | 237 | 245 | 253 | 254 | 259 | 265 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_example1.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 12 | 16 | 20 | 21 | 22 | 23 | 30 | 31 | 32 | 41 | 45 | 52 | 64 | 80 | 89 | 98 | 112 | 113 | 121 | 131 | 135 | 146 | 150 | 161 | 162 | 163 | 173 | 181 | 186 | 195 | 205 | 215 | 216 | 217 | 223 | 232 | 242 | 252 | 253 | 254 | 255 | 256 | 257 | 262 | 269 | 270 | 271 | 279 | 285 | 292 | 299 | 300 | 305 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | -------------------------------------------------------------------------------- /library/src/main/java/com/flyjingfish/switchkeyboardlib/BaseSwitchKeyboardUtil.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.switchkeyboardlib; 2 | 3 | import static android.util.TypedValue.COMPLEX_UNIT_DIP; 4 | 5 | import android.animation.AnimatorSet; 6 | import android.animation.ObjectAnimator; 7 | import android.annotation.SuppressLint; 8 | import android.app.Activity; 9 | import android.app.Dialog; 10 | import android.content.Context; 11 | import android.content.SharedPreferences; 12 | import android.graphics.Color; 13 | import android.os.Build; 14 | import android.os.Handler; 15 | import android.os.Looper; 16 | import android.util.TypedValue; 17 | import android.view.KeyEvent; 18 | import android.view.MotionEvent; 19 | import android.view.View; 20 | import android.view.ViewGroup; 21 | import android.view.ViewTreeObserver; 22 | import android.view.Window; 23 | import android.view.WindowInsets; 24 | import android.view.WindowManager; 25 | import android.view.inputmethod.InputMethodManager; 26 | import android.widget.EditText; 27 | import android.widget.FrameLayout; 28 | import android.widget.LinearLayout; 29 | import android.widget.RelativeLayout; 30 | 31 | import androidx.annotation.NonNull; 32 | import androidx.annotation.Nullable; 33 | import androidx.annotation.RequiresApi; 34 | import androidx.fragment.app.DialogFragment; 35 | import androidx.fragment.app.Fragment; 36 | import androidx.fragment.app.FragmentActivity; 37 | import androidx.lifecycle.Lifecycle; 38 | import androidx.lifecycle.LifecycleEventObserver; 39 | import androidx.lifecycle.LifecycleOwner; 40 | 41 | 42 | public class BaseSwitchKeyboardUtil { 43 | protected SystemKeyboardUtils keyboardUtils; 44 | protected Handler handler = new Handler(Looper.getMainLooper()); 45 | protected boolean isShowMenu; 46 | protected View audioBtn; 47 | protected View audioTouchVIew; 48 | protected EditText etContent; 49 | protected Activity activity; 50 | protected LifecycleOwner lifecycleOwner; 51 | protected ViewGroup menuViewContainer; 52 | protected boolean menuViewHeightEqualKeyboard; 53 | protected boolean useSwitchAnim = true; 54 | protected boolean useMenuUpAnim = false; 55 | protected boolean keyboardIsShow; 56 | protected int keyboardHeight; 57 | protected static final int SWITCH_ANIM_SPEED = 2; 58 | private View.OnTouchListener etContentOnTouchListener; 59 | private boolean isRecordKeyboardHeight; 60 | protected boolean isAutoShowKeyboard; 61 | protected boolean isAutoSetFitsSystemWindowsFalse = true; 62 | protected AutoShowKeyboardType autoShowKeyboardType; 63 | 64 | public BaseSwitchKeyboardUtil(Activity activity) { 65 | this.activity = activity; 66 | } 67 | 68 | /** 69 | * 70 | * @param menuViewHeightEqualKeyboard 是否让菜单高度和键盘高度一样(首次可能会有误差) 71 | */ 72 | public void setMenuViewHeightEqualKeyboard(boolean menuViewHeightEqualKeyboard) { 73 | this.menuViewHeightEqualKeyboard = menuViewHeightEqualKeyboard; 74 | } 75 | 76 | /** 77 | * 78 | * @param etContent 文本消息输入框(不可为空) 79 | */ 80 | public void setInputEditText(@NonNull EditText etContent) { 81 | this.etContent = etContent; 82 | } 83 | 84 | /** 85 | * 86 | * @param audioBtn 语音消息按钮(可为空) 87 | */ 88 | public void setAudioBtn(@Nullable View audioBtn) { 89 | this.audioBtn = audioBtn; 90 | } 91 | 92 | /** 93 | * 94 | * @param audioTouchVIew 语音消息按住说话按钮(可为空) 95 | */ 96 | public void setAudioTouchView(@Nullable View audioTouchVIew) { 97 | this.audioTouchVIew = audioTouchVIew; 98 | } 99 | 100 | /** 101 | * 102 | * @param menuViewContainer 菜单总包裹布局(不可为空) 103 | */ 104 | public void setMenuViewContainer(@NonNull FrameLayout menuViewContainer) { 105 | this.menuViewContainer = menuViewContainer; 106 | } 107 | 108 | /** 109 | * 110 | * @param menuViewContainer 菜单总包裹布局(不可为空) 111 | */ 112 | public void setMenuViewContainer(@NonNull RelativeLayout menuViewContainer) { 113 | this.menuViewContainer = menuViewContainer; 114 | } 115 | 116 | /** 117 | * 118 | * @param menuViewContainer 菜单总包裹布局(不可为空) 119 | */ 120 | public void setMenuViewContainer(@NonNull LinearLayout menuViewContainer) { 121 | this.menuViewContainer = menuViewContainer; 122 | } 123 | 124 | public boolean isAutoShowKeyboard() { 125 | return isAutoShowKeyboard; 126 | } 127 | 128 | /** 129 | * 设置显示页面时是否自动弹出键盘 130 | * @param autoShowKeyboard 是否自动弹出键盘 131 | * @param autoShowKeyboardType 自动弹出的类型,传入{@link AutoShowKeyboardType#FIRST_SHOW}则只是首次进入页面时才自动弹出键盘 132 | * 传入{@link AutoShowKeyboardType#ALWAYS_SHOW}则是每次显示页面时都会自动弹出键盘 133 | */ 134 | public void setAutoShowKeyboard(boolean autoShowKeyboard,AutoShowKeyboardType autoShowKeyboardType) { 135 | isAutoShowKeyboard = autoShowKeyboard; 136 | this.autoShowKeyboardType = autoShowKeyboardType; 137 | } 138 | /** 139 | * 设置显示页面时是否自动弹出键盘,默认首次为首次进入页面自动弹出键盘 140 | * @param autoShowKeyboard 是否自动弹出键盘 141 | */ 142 | public void setAutoShowKeyboard(boolean autoShowKeyboard) { 143 | setAutoShowKeyboard(autoShowKeyboard,AutoShowKeyboardType.FIRST_SHOW); 144 | } 145 | 146 | /** 147 | * 是否自动设置菜单栏及其父布局 {@link View#setFitsSystemWindows} 为 false (默认自动设置) 148 | * @param autoSetFitsSystemWindowsFalse false为不自动设置,如果您布局中存在 fitsSystemWindows 为true 的情况,这将导致显示不正常。此项默认为true 149 | */ 150 | public void setAutoSetFitsSystemWindowsFalse(boolean autoSetFitsSystemWindowsFalse) { 151 | isAutoSetFitsSystemWindowsFalse = autoSetFitsSystemWindowsFalse; 152 | } 153 | 154 | protected void saveKeyboardHeight(int value){ 155 | SharedPreferences sp = activity.getApplication().getSharedPreferences("SwitchKeyboardData", Context.MODE_PRIVATE); 156 | sp.edit().putInt("KeyboardHeight", value).apply(); 157 | } 158 | 159 | protected int getKeyboardHeight(){ 160 | SharedPreferences sp = activity.getApplication().getSharedPreferences("SwitchKeyboardData", Context.MODE_PRIVATE); 161 | isRecordKeyboardHeight = sp.contains("KeyboardHeight"); 162 | return sp.getInt("KeyboardHeight", (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,240,activity.getResources().getDisplayMetrics())); 163 | } 164 | 165 | /** 166 | * 根据生命周期来确保不发生内存泄漏 167 | * @param fragmentActivity View 在{@link FragmentActivity}中 168 | */ 169 | public void attachLifecycle(FragmentActivity fragmentActivity){ 170 | attachLifecycleOwner(fragmentActivity); 171 | } 172 | 173 | /** 174 | * 根据生命周期来确保不发生内存泄漏 175 | * @param fragment View 在{@link Fragment}中 176 | */ 177 | public void attachLifecycle(Fragment fragment){ 178 | attachLifecycleOwner(fragment); 179 | } 180 | 181 | /** 182 | * 根据生命周期来确保不发生内存泄漏 183 | * @param dialogFragment View 在{@link DialogFragment}中 184 | */ 185 | public void attachLifecycle(DialogFragment dialogFragment){ 186 | attachLifecycleOwner(dialogFragment); 187 | } 188 | 189 | protected void attachLifecycleOwner(LifecycleOwner lifecycleOwner){ 190 | this.lifecycleOwner = lifecycleOwner; 191 | if (lifecycleOwner instanceof Fragment){ 192 | lifecycleOwner = ((Fragment) lifecycleOwner).getViewLifecycleOwner(); 193 | } 194 | lifecycleOwner.getLifecycle().addObserver(new MyLifecycleEventObserver()); 195 | } 196 | 197 | protected class MyLifecycleEventObserver implements LifecycleEventObserver{ 198 | @Override 199 | public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { 200 | if (event == Lifecycle.Event.ON_CREATE){ 201 | onCreate(source); 202 | } else if (event == Lifecycle.Event.ON_DESTROY){ 203 | onDestroy(source); 204 | } 205 | } 206 | } 207 | 208 | @SuppressLint("ClickableViewAccessibility") 209 | protected void onCreate(@NonNull LifecycleOwner owner){ 210 | keyboardUtils = new SystemKeyboardUtils(activity,lifecycleOwner instanceof DialogFragment); 211 | keyboardUtils.setOnKeyBoardListener(onKeyBoardListener); 212 | 213 | if (isAutoSetFitsSystemWindowsFalse){ 214 | setFitsSystemWindows(etContent); 215 | setFitsSystemWindows(menuViewContainer); 216 | } 217 | 218 | setSystemUi(activity.getWindow()); 219 | if (lifecycleOwner instanceof DialogFragment){ 220 | Dialog dialog = ((DialogFragment) lifecycleOwner).requireDialog(); 221 | setSystemUi(dialog.getWindow()); 222 | checkSoftMode(activity.getWindow(),false); 223 | checkSoftMode(dialog.getWindow(),isAutoShowKeyboard); 224 | }else { 225 | checkSoftMode(activity.getWindow(),isAutoShowKeyboard); 226 | } 227 | int menuHeight = getKeyboardHeight(); 228 | if (menuViewHeightEqualKeyboard){ 229 | ViewGroup.LayoutParams layoutParams = menuViewContainer.getLayoutParams(); 230 | layoutParams.height = menuHeight; 231 | menuViewContainer.setLayoutParams(layoutParams); 232 | } 233 | if (isRecordKeyboardHeight){ 234 | keyboardHeight = menuHeight; 235 | } 236 | if (audioBtn != null){ 237 | audioBtn.setOnClickListener(v -> { 238 | 239 | if (keyboardIsShow){ 240 | hideKeyboard(); 241 | menuViewContainer.setVisibility(View.GONE); 242 | etContent.setVisibility(View.GONE); 243 | if (audioTouchVIew != null){ 244 | audioTouchVIew.setVisibility(View.VISIBLE); 245 | } 246 | if (onKeyboardMenuListener != null){ 247 | onKeyboardMenuListener.onShowMenuLayout(audioTouchVIew); 248 | } 249 | }else if (menuViewContainer.getVisibility() == View.VISIBLE){ 250 | menuViewContainer.setVisibility(View.GONE); 251 | etContent.setVisibility(View.GONE); 252 | if (audioTouchVIew != null){ 253 | audioTouchVIew.setVisibility(View.VISIBLE); 254 | } 255 | if (onKeyboardMenuListener != null){ 256 | onKeyboardMenuListener.onShowMenuLayout(audioTouchVIew); 257 | } 258 | }else if (audioTouchVIew != null && audioTouchVIew.getVisibility() == View.GONE){ 259 | etContent.setVisibility(View.GONE); 260 | audioTouchVIew.setVisibility(View.VISIBLE); 261 | if (onKeyboardMenuListener != null){ 262 | onKeyboardMenuListener.onShowMenuLayout(audioTouchVIew); 263 | } 264 | }else { 265 | etContent.setVisibility(View.VISIBLE); 266 | if (audioTouchVIew != null){ 267 | audioTouchVIew.setVisibility(View.GONE); 268 | } 269 | showKeyboard(); 270 | } 271 | isShowMenu = false; 272 | }); 273 | } 274 | 275 | etContent.setOnTouchListener((v, event) -> { 276 | if (!keyboardIsShow && event.getAction() == MotionEvent.ACTION_DOWN){ 277 | showKeyboardAnim(); 278 | } 279 | if (etContentOnTouchListener != null){ 280 | return etContentOnTouchListener.onTouch(v,event); 281 | } 282 | return false; 283 | }); 284 | Window window; 285 | if (lifecycleOwner instanceof DialogFragment){ 286 | Dialog dialog = ((DialogFragment) lifecycleOwner).requireDialog(); 287 | window = dialog.getWindow(); 288 | }else { 289 | window = activity.getWindow(); 290 | } 291 | SystemUiVisibilityListener listener = new SystemUiVisibilityListener(); 292 | window.getDecorView().setOnApplyWindowInsetsListener(listener); 293 | window.getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(listener); 294 | } 295 | 296 | private class SystemUiVisibilityListener implements View.OnApplyWindowInsetsListener,ViewTreeObserver.OnGlobalLayoutListener { 297 | private int lastFlag; 298 | private int lastHeight; 299 | @Override 300 | public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { 301 | if (!hasFullScreenFlag()){ 302 | if (keyboardIsShow){ 303 | ViewGroup.LayoutParams layoutParams = menuViewContainer.getLayoutParams(); 304 | lastHeight = layoutParams.height; 305 | layoutParams.height = 0; 306 | menuViewContainer.setLayoutParams(layoutParams); 307 | // menuViewContainer.setVisibility(View.GONE); 308 | } 309 | } 310 | return v.onApplyWindowInsets(insets); 311 | } 312 | 313 | @Override 314 | public void onGlobalLayout() { 315 | Window window; 316 | if (lifecycleOwner instanceof DialogFragment){ 317 | Dialog dialog = ((DialogFragment) lifecycleOwner).requireDialog(); 318 | window = dialog.getWindow(); 319 | }else { 320 | window = activity.getWindow(); 321 | } 322 | int flag = window.getDecorView().getSystemUiVisibility(); 323 | if (!hasFullScreenFlag()){ 324 | setSystemUi(window); 325 | } 326 | if (!hasFullScreenFlag(lastFlag)){ 327 | if (keyboardIsShow){ 328 | ViewGroup.LayoutParams layoutParams = menuViewContainer.getLayoutParams(); 329 | layoutParams.height = lastHeight; 330 | menuViewContainer.setLayoutParams(layoutParams); 331 | // handler.postDelayed(() -> menuViewContainer.setVisibility(View.VISIBLE),150); 332 | }else { 333 | menuViewContainer.requestLayout(); 334 | } 335 | } 336 | lastFlag = flag; 337 | } 338 | } 339 | 340 | protected void onDestroy(@NonNull LifecycleOwner owner){ 341 | keyboardUtils.onDestroy(); 342 | handler.removeCallbacksAndMessages(null); 343 | stopSwitchAnim(); 344 | } 345 | 346 | protected void showKeyboardAnim(){ 347 | if (useSwitchAnim){ 348 | int startHeight = menuViewContainer.getHeight(); 349 | if (startHeight != keyboardHeight && keyboardHeight > 0){ 350 | stopSwitchAnim(); 351 | ViewHeight viewHeight = new ViewHeight(menuViewContainer); 352 | int distance = Math.abs(startHeight - keyboardHeight); 353 | int duration = distance/SWITCH_ANIM_SPEED; 354 | if (duration<200){ 355 | duration = 200; 356 | } 357 | ObjectAnimator switchAnim1 = ObjectAnimator.ofInt(viewHeight,"viewHeight",startHeight,keyboardHeight); 358 | switchAnim1.setDuration(duration); 359 | switchAnim = new AnimatorSet(); 360 | switchAnim.playTogether(switchAnim1); 361 | switchAnim.start(); 362 | } 363 | } 364 | } 365 | 366 | public void hideKeyboard() { 367 | if (onKeyboardMenuListener != null){ 368 | onKeyboardMenuListener.onCallHideKeyboard(); 369 | } 370 | InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); 371 | imm.hideSoftInputFromWindow(etContent.getWindowToken(), 0); 372 | etContent.clearFocus(); 373 | } 374 | 375 | protected void showKeyboard() { 376 | if (onKeyboardMenuListener != null){ 377 | onKeyboardMenuListener.onCallShowKeyboard(); 378 | } 379 | etContent.setSelection(etContent.getText().length()); 380 | etContent.requestFocus(); 381 | InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); 382 | imm.showSoftInput(etContent, 0); 383 | } 384 | 385 | public void setSystemUi(){ 386 | Window window; 387 | if (lifecycleOwner instanceof DialogFragment){ 388 | Dialog dialog = ((DialogFragment) lifecycleOwner).requireDialog(); 389 | window = dialog.getWindow(); 390 | }else { 391 | window = activity.getWindow(); 392 | } 393 | setSystemUi(window); 394 | } 395 | 396 | public void setSystemUi(Window window){ 397 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 398 | // window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 399 | window.setStatusBarColor(Color.TRANSPARENT); 400 | int flag = window.getDecorView().getSystemUiVisibility(); 401 | window.getDecorView().setSystemUiVisibility(flag | View.SYSTEM_UI_FLAG_LAYOUT_STABLE 402 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); 403 | } 404 | 405 | private boolean hasFullScreenFlag(){ 406 | Window window; 407 | if (lifecycleOwner instanceof DialogFragment){ 408 | Dialog dialog = ((DialogFragment) lifecycleOwner).requireDialog(); 409 | window = dialog.getWindow(); 410 | }else { 411 | window = activity.getWindow(); 412 | } 413 | int flag = window.getDecorView().getSystemUiVisibility(); 414 | return hasFullScreenFlag(flag); 415 | } 416 | 417 | private boolean hasFullScreenFlag(int flag){ 418 | return (flag & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; 419 | } 420 | 421 | private void setFitsSystemWindows(View view){ 422 | view.setFitsSystemWindows(false); 423 | if (view.getParent() instanceof ViewGroup && ((ViewGroup) view.getParent()).getId() != Window.ID_ANDROID_CONTENT){ 424 | setFitsSystemWindows((View) view.getParent()); 425 | } 426 | } 427 | 428 | public boolean onKeyDown(int keyCode, KeyEvent event) { 429 | if (keyCode == KeyEvent.KEYCODE_BACK && isShowMenu){ 430 | menuViewContainer.setVisibility(View.GONE); 431 | isShowMenu = false; 432 | if (onKeyboardMenuListener != null){ 433 | onKeyboardMenuListener.onHideMenuViewContainer(); 434 | } 435 | return true; 436 | } 437 | return false; 438 | } 439 | 440 | protected AnimatorSet switchAnim; 441 | private final SystemKeyboardUtils.OnKeyBoardListener onKeyBoardListener = new SystemKeyboardUtils.OnKeyBoardListener() { 442 | @Override 443 | public void onShow(int height) { 444 | stopSwitchAnim(); 445 | ViewGroup.LayoutParams layoutParams = menuViewContainer.getLayoutParams(); 446 | layoutParams.height = height; 447 | menuViewContainer.setLayoutParams(layoutParams); 448 | // int startHeight = menuViewContainer.getHeight(); 449 | // if (menuViewHeightEqualKeyboard || startHeight <= height || !useSwitchAnim){ 450 | // ViewGroup.LayoutParams layoutParams = menuViewContainer.getLayoutParams(); 451 | // layoutParams.height = height; 452 | // menuViewContainer.setLayoutParams(layoutParams); 453 | // }else { 454 | // stopSwitchAnim(); 455 | // ViewHeight viewHeight = new ViewHeight(menuViewContainer); 456 | // int distance = Math.abs(startHeight - height); 457 | // int duration = distance/SWITCH_ANIM_SPEED; 458 | // if (duration<200){ 459 | // duration = 200; 460 | // } 461 | // switchAnim = ObjectAnimator.ofInt(viewHeight,"viewHeight",startHeight,height); 462 | // switchAnim.setDuration(duration); 463 | // switchAnim.start(); 464 | // } 465 | 466 | menuViewContainer.setVisibility(View.INVISIBLE); 467 | saveKeyboardHeight(height); 468 | keyboardHeight = height; 469 | if (onKeyboardMenuListener != null){ 470 | onKeyboardMenuListener.onKeyboardShow(height); 471 | onKeyboardMenuListener.onScrollToBottom(); 472 | } 473 | keyboardIsShow = true; 474 | etContent.post(() -> etContent.requestFocus()); 475 | } 476 | 477 | @Override 478 | public void onHide(int height) { 479 | if (onKeyboardMenuListener != null){ 480 | onKeyboardMenuListener.onKeyboardHide(height); 481 | } 482 | if (!(BaseSwitchKeyboardUtil.this instanceof SwitchKeyboardUtil) && !isShowMenu){ 483 | menuViewContainer.setVisibility(View.GONE); 484 | } 485 | keyboardIsShow = false; 486 | } 487 | }; 488 | 489 | protected void stopSwitchAnim(){ 490 | if (switchAnim != null){ 491 | switchAnim.cancel(); 492 | } 493 | } 494 | 495 | public static class ViewHeight{ 496 | private final View view; 497 | 498 | public ViewHeight(View view) { 499 | this.view = view; 500 | } 501 | 502 | public void setViewHeight(int height){ 503 | ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); 504 | layoutParams.height = height; 505 | view.setLayoutParams(layoutParams); 506 | } 507 | } 508 | /** 509 | * 切换键盘和更多菜单 510 | */ 511 | public boolean toggleMoreView(){ 512 | if (!isShowMenu){ 513 | isShowMenu = true; 514 | menuViewContainer.setVisibility(View.VISIBLE); 515 | if (audioTouchVIew != null){ 516 | audioTouchVIew.setVisibility(View.GONE); 517 | } 518 | etContent.setVisibility(View.VISIBLE); 519 | if (!menuViewHeightEqualKeyboard && !useSwitchAnim){ 520 | handler.postDelayed(() -> { 521 | ViewGroup.LayoutParams layoutParams = menuViewContainer.getLayoutParams(); 522 | layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; 523 | menuViewContainer.setLayoutParams(layoutParams); 524 | },200); 525 | } 526 | 527 | hideKeyboard(); 528 | }else { 529 | isShowMenu = false; 530 | etContent.setVisibility(View.VISIBLE); 531 | if (audioTouchVIew != null){ 532 | audioTouchVIew.setVisibility(View.GONE); 533 | } 534 | showKeyboard(); 535 | } 536 | if (onKeyboardMenuListener != null){ 537 | onKeyboardMenuListener.onScrollToBottom(); 538 | } 539 | return isShowMenu; 540 | } 541 | 542 | private OnKeyboardMenuListener onKeyboardMenuListener; 543 | 544 | public interface OnKeyboardMenuListener{ 545 | void onScrollToBottom(); 546 | void onCallShowKeyboard(); 547 | void onCallHideKeyboard(); 548 | void onKeyboardHide(int keyboardHeight); 549 | void onKeyboardShow(int keyboardHeight); 550 | void onShowMenuLayout(View layoutView); 551 | void onHideMenuViewContainer(); 552 | } 553 | 554 | /** 555 | * 设置监听 556 | * @param onKeyboardMenuListener 557 | */ 558 | public void setOnKeyboardMenuListener(OnKeyboardMenuListener onKeyboardMenuListener) { 559 | this.onKeyboardMenuListener = onKeyboardMenuListener; 560 | } 561 | 562 | public void checkSoftMode(){ 563 | checkSoftMode(activity.getWindow(),false); 564 | } 565 | 566 | public void checkSoftMode(Window window,boolean isSetAutoShowKeyboardFlag){ 567 | final WindowManager.LayoutParams attrs = window.getAttributes(); 568 | int softMode = attrs.softInputMode; 569 | 570 | if (((softMode & WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING) == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING) 571 | ||((softMode & WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) 572 | ||((softMode & WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)){ 573 | 574 | int newSoftMode = softMode; 575 | newSoftMode &= ~WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; 576 | newSoftMode &= ~WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; 577 | newSoftMode &= ~WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; 578 | attrs.softInputMode = newSoftMode|WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED; 579 | if (isSetAutoShowKeyboardFlag){ 580 | attrs.softInputMode = attrs.softInputMode|((lifecycleOwner instanceof DialogFragment)?WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:(autoShowKeyboardType == AutoShowKeyboardType.ALWAYS_SHOW?WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)); 581 | } 582 | window.setAttributes(attrs); 583 | }else { 584 | if (isSetAutoShowKeyboardFlag){ 585 | attrs.softInputMode = softMode|((lifecycleOwner instanceof DialogFragment)?WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:(autoShowKeyboardType == AutoShowKeyboardType.ALWAYS_SHOW?WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)); 586 | window.setAttributes(attrs); 587 | } 588 | } 589 | } 590 | 591 | public boolean isUseSwitchAnim() { 592 | return useSwitchAnim; 593 | } 594 | 595 | public void setUseSwitchAnim(boolean useSwitchAnim) { 596 | this.useSwitchAnim = useSwitchAnim; 597 | } 598 | 599 | public boolean isUseMenuUpAnim() { 600 | return useMenuUpAnim; 601 | } 602 | 603 | public void setUseMenuUpAnim(boolean useMenuUpAnim) { 604 | this.useMenuUpAnim = useMenuUpAnim; 605 | } 606 | 607 | public void setEtContentOnTouchListener(View.OnTouchListener etContentOnTouchListener) { 608 | this.etContentOnTouchListener = etContentOnTouchListener; 609 | } 610 | 611 | public void hideMenuAndKeyboard(){ 612 | if (isShowMenu){ 613 | menuViewContainer.setVisibility(View.GONE); 614 | isShowMenu = false; 615 | if (onKeyboardMenuListener != null){ 616 | onKeyboardMenuListener.onHideMenuViewContainer(); 617 | } 618 | }else { 619 | hideKeyboard(); 620 | } 621 | } 622 | } 623 | --------------------------------------------------------------------------------