From 774a8bf29c6cc88d04ce85d5929eee4fb2c580f9 Mon Sep 17 00:00:00 2001 From: huangjian Date: Thu, 12 May 2022 17:26:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=88=BF=E9=97=B4=E5=8F=91=E9=80=81=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E4=BD=93=E9=AA=8C=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android_crop_lib/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 2 +- .../erban/avroom/fragment/BaseRoomFragment.kt | 28 +- .../erban/avroom/helper/ShowSoftInputUtils.kt | 75 +++++ .../yizhuan/erban/avroom/helper/SoftInput.kt | 291 ++++++++++++++++++ .../res/layout/fragment_av_room_game.xml | 4 +- .../res/layout/fragment_game_room.xml | 1 + .../res/layout/fragment_single_room.xml | 1 + build.gradle | 2 +- library/build.gradle | 10 +- 10 files changed, 390 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/com/yizhuan/erban/avroom/helper/ShowSoftInputUtils.kt create mode 100644 app/src/main/java/com/yizhuan/erban/avroom/helper/SoftInput.kt diff --git a/android_crop_lib/build.gradle b/android_crop_lib/build.gradle index ce6b7de3c..50e167082 100644 --- a/android_crop_lib/build.gradle +++ b/android_crop_lib/build.gradle @@ -25,7 +25,7 @@ android { dependencies { api 'androidx.annotation:annotation:1.1.0' api 'androidx.legacy:legacy-support-v4:1.0.0' - implementation "androidx.core:core-ktx:1.3.2" + implementation "androidx.core:core-ktx:1.6.0" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } repositories { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d6a14f12e..9967a9dd0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -561,7 +561,7 @@ android:launchMode="singleTask" android:screenOrientation="portrait" android:theme="@style/AVRoomTheme" - android:windowSoftInputMode="adjustPan" /> + android:windowSoftInputMode="adjustNothing" /> ?> : private var myUid: Long = 0 protected lateinit var messageView: MessageView protected lateinit var bottomView: BottomView - protected lateinit var inputLayout: RelativeLayout + protected lateinit var inputLayout: View protected lateinit var inputEdit: EditText protected lateinit var inputSend: ImageView protected lateinit var microView: MicroView @@ -158,17 +160,6 @@ open class BaseRoomFragment?> : */ private var showGiftValue = false private var redPackageSendDialog: RedPackageSendDialog? = null - private val mOnSoftKeyBoardChangeListener: OnSoftKeyBoardChangeListener = - object : OnSoftKeyBoardChangeListener { - override fun keyBoardShow(height: Int) { - /*软键盘显示:执行隐藏title动画,并修改listview高度和装载礼物容器的高度*/ - } - - override fun keyBoardHide(height: Int) { - /*软键盘隐藏:隐藏聊天输入框并显示聊天按钮,执行显示title动画,并修改listview高度和装载礼物容器的高度*/ - inputLayout.visibility = View.GONE - } - } private var dynamicFaceDialog: DynamicFaceDialog? = null private var giftDialog: GiftDialog? = null @@ -212,7 +203,6 @@ open class BaseRoomFragment?> : false } - messageView.setOnLongClickListener { _, account, name -> showInputLayout() if (atProxy == null) atProxy = AtProxy(inputEdit) @@ -279,6 +269,7 @@ open class BaseRoomFragment?> : if (isRoomMin) { GiftValueMrg.get().updateRoomGiftValue(false) } + } @CallSuper @@ -690,7 +681,10 @@ open class BaseRoomFragment?> : * 软键盘显示与隐藏的监听 */ private fun softKeyboardListener() { - SoftKeyBoardListener.setListener(activity, mOnSoftKeyBoardChangeListener) + val llInput = inputLayout.findViewById(R.id.ll_input) + setWindowSoftInput(llInput, llInput) { + inputLayout.isVisible = hasSoftInput() + } } @SuppressLint("CheckResult") @@ -988,6 +982,7 @@ open class BaseRoomFragment?> : override fun onSendMsgSuccess(msg: String?) { inputEdit.setText("") + KeyBoardUtils.hideKeyBoard(activity, inputEdit) } /** @@ -1321,5 +1316,4 @@ open class BaseRoomFragment?> : RoomMsgActivity.start(mContext) } } - } \ No newline at end of file diff --git a/app/src/main/java/com/yizhuan/erban/avroom/helper/ShowSoftInputUtils.kt b/app/src/main/java/com/yizhuan/erban/avroom/helper/ShowSoftInputUtils.kt new file mode 100644 index 000000000..d621b8c0c --- /dev/null +++ b/app/src/main/java/com/yizhuan/erban/avroom/helper/ShowSoftInputUtils.kt @@ -0,0 +1,75 @@ +package com.yizhuan.erban.avroom.helper + +import android.app.Activity +import android.content.Context +import android.view.inputmethod.InputMethodManager +import android.widget.EditText +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.fragment.app.Fragment + + +// + +/** + * 弹出软键盘 + * 如果要求页面显示立刻弹出软键盘,建议在onResume方法中调用 + */ +fun EditText.showSoftInput() { + isFocusable = true + isFocusableInTouchMode = true + requestFocus() + if (isSystemInsetsAnimationSupport()) { + ViewCompat.getWindowInsetsController(this)?.show(WindowInsetsCompat.Type.ime()) + } else { + postDelayed({ + val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.showSoftInput(this, 0) + }, 300) + } +} + +/** 隐藏软键盘 */ +fun Activity.hideSoftInput() { + currentFocus?.let { + val imm = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(it.windowToken, InputMethodManager.HIDE_NOT_ALWAYS) + } ?: let { + ViewCompat.getWindowInsetsController(window.decorView)?.hide(WindowInsetsCompat.Type.ime()) + } +} + +/** 隐藏软键盘 */ +fun Fragment.hideSoftInput() = requireActivity().hideSoftInput() + +/** 隐藏软键盘 */ +fun EditText.hideSoftInput() { + ViewCompat.getWindowInsetsController(this)?.hide(WindowInsetsCompat.Type.ime()) +} +// + + +// + +/** 软键盘是否显示 */ +fun Activity.hasSoftInput(): Boolean { + return ViewCompat.getRootWindowInsets(window.decorView) + ?.isVisible(WindowInsetsCompat.Type.ime()) ?: false +} + +/** 软键盘是否显示 */ +fun Fragment.hasSoftInput(): Boolean { + return requireActivity().hasSoftInput() +} + +/** 当前软键盘显示高度 */ +fun Activity.getSoftInputHeight(): Int { + val softInputHeight = ViewCompat.getRootWindowInsets(window.decorView) + ?.getInsets(WindowInsetsCompat.Type.ime())?.bottom + return softInputHeight ?: 0 +} + +/** 当前软键盘显示高度 */ +fun Fragment.getSoftInputHeight(): Int { + return requireActivity().getSoftInputHeight() +} \ No newline at end of file diff --git a/app/src/main/java/com/yizhuan/erban/avroom/helper/SoftInput.kt b/app/src/main/java/com/yizhuan/erban/avroom/helper/SoftInput.kt new file mode 100644 index 000000000..37ae7a72d --- /dev/null +++ b/app/src/main/java/com/yizhuan/erban/avroom/helper/SoftInput.kt @@ -0,0 +1,291 @@ +package com.yizhuan.erban.avroom.helper + +/* + * Copyright (C) 2018 Drake, Inc. https://github.com/liangjingkanji + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.app.Activity +import android.app.Dialog +import android.os.Build +import android.view.View +import android.view.Window +import android.view.WindowManager +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsAnimationCompat +import androidx.core.view.WindowInsetsCompat +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.Fragment +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import kotlin.math.min + + +/** + * 软键盘弹出后要求指定视图[float]悬浮在软键盘之上 + * 本方法重复调用会互相覆盖, 例如Fragment调用会覆盖其Activity的调用 + * + * Api21以上本方法使用[WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING] + * Api21下调用无效 + * + * @param float 需要悬浮在软键盘之上的视图 + * @param transition 当软键盘显示隐藏时需要移动的视图, 使用[View.setTranslationY]移动 + * @param editText 指定的视图存在焦点才触发软键盘监听, null则全部视图都触发 + * @param margin 悬浮视图和软键盘间距 + * @param onChanged 监听软键盘是否显示 + * + * @see getSoftInputHeight 软键盘高度 + */ +@JvmOverloads +fun Activity.setWindowSoftInput( + float: View? = null, + transition: View? = float?.parent as? View, + editText: View? = null, + margin: Int = 0, + onChanged: (() -> Unit)? = null, +) = window.setWindowSoftInput(float, transition, editText, margin, onChanged) + +/** + * 如果Fragment不是立即创建, 请为Fragment所在的Activity配置[[WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING]] + * + * 软键盘弹出后要求指定视图[float]悬浮在软键盘之上 + * 本方法重复调用会互相覆盖, 例如Fragment调用会覆盖其Activity的调用 + * + * Api21以上本方法使用[WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING] + * Api21下调用无效 + * + * @param float 需要悬浮在软键盘之上的视图 + * @param transition 当软键盘显示隐藏时需要移动的视图, 使用[View.setTranslationY]移动 + * @param editText 指定的视图存在焦点才触发软键盘监听, null则全部视图都触发 + * @param margin 悬浮视图和软键盘间距 + * @param onChanged 监听软键盘是否显示 + * + * @see getSoftInputHeight 软键盘高度 + */ +@JvmOverloads +fun Fragment.setWindowSoftInput( + float: View? = null, + transition: View? = view, + editText: View? = null, + margin: Int = 0, + onChanged: (() -> Unit)? = null, +) = requireActivity().window.setWindowSoftInput(float, transition, editText, margin, onChanged) + +/** + * 软键盘弹出后要求指定视图[float]悬浮在软键盘之上 + * 本方法重复调用会互相覆盖, 例如Fragment调用会覆盖其Activity的调用 + * + * Api21以上本方法使用[WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING] + * Api21下调用无效 + * + * @param float 需要悬浮在软键盘之上的视图 + * @param transition 当软键盘显示隐藏时需要移动的视图, 使用[View.setTranslationY]移动 + * @param editText 指定的视图存在焦点才触发软键盘监听, null则全部视图都触发 + * @param margin 悬浮视图和软键盘间距 + * @param onChanged 监听软键盘是否显示 + * + * @see getSoftInputHeight 软键盘高度 + */ +@JvmOverloads +fun DialogFragment.setWindowSoftInput( + float: View? = null, + transition: View? = view, + editText: View? = null, + margin: Int = 0, + onChanged: (() -> Unit)? = null, +) = dialog?.window?.setWindowSoftInput(float, transition, editText, margin, onChanged) + +/** + * 软键盘弹出后要求指定视图[float]悬浮在软键盘之上 + * 本方法重复调用会互相覆盖, 例如Fragment调用会覆盖其Activity的调用 + * + * Api21以上本方法使用[WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING] + * Api21下调用无效 + * + * @param float 需要悬浮在软键盘之上的视图 + * @param transition 当软键盘显示隐藏时需要移动的视图, 使用[View.setTranslationY]移动 + * @param editText 指定的视图存在焦点才触发软键盘监听, null则全部视图都触发 + * @param margin 悬浮视图和软键盘间距 + * @param onChanged 监听软键盘是否显示 + * + * @see getSoftInputHeight 软键盘高度 + */ +@JvmOverloads +fun BottomSheetDialogFragment.setWindowSoftInput( + float: View? = null, + transition: View? = dialog?.findViewById(com.google.android.material.R.id.design_bottom_sheet), + editText: View? = null, + margin: Int = 0, + onChanged: (() -> Unit)? = null, +) = dialog?.window?.setWindowSoftInput(float, transition, editText, margin, onChanged) + +/** + * 软键盘弹出后要求指定视图[float]悬浮在软键盘之上 + * 本方法重复调用会互相覆盖, 例如Fragment调用会覆盖其Activity的调用 + * + * Api21以上本方法使用[WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING] + * Api21下调用无效 + * + * @param float 需要悬浮在软键盘之上的视图 + * @param transition 当软键盘显示隐藏时需要移动的视图, 使用[View.setTranslationY]移动 + * @param editText 指定的视图存在焦点才触发软键盘监听, null则全部视图都触发 + * @param margin 悬浮视图和软键盘间距 + * @param onChanged 监听软键盘是否显示 + * + * @see getSoftInputHeight 软键盘高度 + */ +@JvmOverloads +fun Dialog.setWindowSoftInput( + float: View? = null, + transition: View? = window?.decorView, + editText: View? = null, + margin: Int = 0, + onChanged: (() -> Unit)? = null, +) = window?.setWindowSoftInput(float, transition, editText, margin, onChanged) + +/** + * 软键盘弹出后要求指定视图[float]悬浮在软键盘之上 + * 本方法重复调用会互相覆盖, 例如Fragment调用会覆盖其Activity的调用 + * + * Api21以上本方法使用[WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING] + * Api21下调用无效 + * + * @param float 需要悬浮在软键盘之上的视图 + * @param transition 当软键盘显示隐藏时需要移动的视图, 使用[View.setTranslationY]移动 + * @param editText 指定的视图存在焦点才触发软键盘监听, null则全部视图都触发 + * @param margin 悬浮视图和软键盘间距 + * @param onChanged 监听软键盘是否显示 + * + * @see getSoftInputHeight 软键盘高度 + */ +@JvmOverloads +fun Window.setWindowSoftInput( + float: View? = null, + transition: View? = null, + editText: View? = null, + margin: Int = 0, + onChanged: (() -> Unit)? = null, +) { + // Api21下不需要用户体验, 直接不适配 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return + // 部分系统不支持WindowInsets使用兼容方案处理 + if (!decorView.isSystemInsetsAnimationSupport()) { + return setWindowSoftInputCompatible(float, transition, editText, margin, onChanged) + } + setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING) + var matchEditText = false + var hasSoftInput = false + var floatInitialBottom = 0 + var startAnimation: WindowInsetsAnimationCompat? = null + var transitionY = 0f + val callback = + object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) { + + override fun onStart( + animation: WindowInsetsAnimationCompat, + bounds: WindowInsetsAnimationCompat.BoundsCompat + ): WindowInsetsAnimationCompat.BoundsCompat { + if (float == null || transition == null) return bounds + hasSoftInput = ViewCompat.getRootWindowInsets(decorView) + ?.isVisible(WindowInsetsCompat.Type.ime()) ?: false + startAnimation = animation + if (hasSoftInput) matchEditText = editText == null || editText.hasFocus() + if (hasSoftInput) { + floatInitialBottom = run { + val r = IntArray(2) + float.getLocationInWindow(r) + r[1] + float.height + } + } + return bounds + } + + override fun onEnd(animation: WindowInsetsAnimationCompat) { + super.onEnd(animation) + if (matchEditText) onChanged?.invoke() + } + + override fun onProgress( + insets: WindowInsetsCompat, + runningAnimations: MutableList + ): WindowInsetsCompat { + val fraction = startAnimation?.fraction + if (fraction == null || float == null || transition == null || !matchEditText) return insets + val softInputHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom + val softInputTop = decorView.bottom - softInputHeight + val offset = (softInputTop - floatInitialBottom - margin).toFloat() + if (hasSoftInput && softInputTop < floatInitialBottom) { + transition.translationY = offset + transitionY = transition.translationY + } else if (!hasSoftInput) { + transition.translationY = min(transitionY - transitionY * (fraction + 0.5f), 0f) + } + return insets + } + } + ViewCompat.setWindowInsetsAnimationCallback(decorView, callback) +} + +/** 部分系统不支持WindowInsets使用兼容方案处理 */ +private fun Window.setWindowSoftInputCompatible( + float: View? = null, + transition: View? = float?.parent as? View, + editText: View? = null, + margin: Int = 0, + onChanged: (() -> Unit)? = null, +) { + setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) + var shown = false + var matchEditText = false + decorView.viewTreeObserver.addOnGlobalLayoutListener { + val canTransition = float != null && transition != null + val floatBottom = if (canTransition) { + val r = IntArray(2) + float!!.getLocationInWindow(r) + r[1] + float.height + } else 0 + val decorBottom = decorView.bottom + val rootWindowInsets = + ViewCompat.getRootWindowInsets(decorView) ?: return@addOnGlobalLayoutListener + val softInputHeight = rootWindowInsets.getInsets(WindowInsetsCompat.Type.ime()).bottom + val hasSoftInput = rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime()) + val offset = (decorBottom - floatBottom - softInputHeight - margin).toFloat() + if (hasSoftInput) { + matchEditText = editText == null || editText.hasFocus() + if (!shown && matchEditText) { + transition?.translationY = offset + onChanged?.invoke() + } + shown = true + } else { + if (shown && matchEditText) { + transition?.translationY = 0f + onChanged?.invoke() + } + shown = false + } + } +} + + +private var isSupportInsetsFiled: Boolean? = null + +/** 判断系统是否支持[WindowInsetsAnimationCompat] */ +fun View.isSystemInsetsAnimationSupport(): Boolean { + var supportInsets = isSupportInsetsFiled + if (supportInsets == null) { + supportInsets = ViewCompat.getWindowInsetsController(this) != null + isSupportInsetsFiled = supportInsets + } + return supportInsets +} diff --git a/app/src/module_mini_world/res/layout/fragment_av_room_game.xml b/app/src/module_mini_world/res/layout/fragment_av_room_game.xml index 80ab47b97..42f9797c6 100644 --- a/app/src/module_mini_world/res/layout/fragment_av_room_game.xml +++ b/app/src/module_mini_world/res/layout/fragment_av_room_game.xml @@ -309,11 +309,12 @@ +