效果图
基本原理:
在屏幕上,放置一个popWindows
,高度为整屏高度,宽度为0 ,这样popWindows
就不会显示,监听popWindows
的高度变化,得到键盘对应的状态与高度。
windowSoftInputMode
设置为adjustNothing
,自己调整页面高度。
在页面最下方放置一个填充视图,通过改变该视图的高度,来顶起和收回输入框。
实现代码:
package pers.yefeng.anotebook.view
import android.content.Context
import android.widget.PopupWindow
import android.animation.ValueAnimator
import cn.hutool.core.util.ObjectUtil
import android.graphics.Color
import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
import android.view.*
import android.app.Activity
import android.content.res.Resources
/**
* 软键盘适配 PopupWindow
*/
class KeyboardPopupWindow(
val context: Context,
private val rootLayout: ViewGroup,
private val fillingView: ViewGroup
) : PopupWindow(), ViewTreeObserver.OnGlobalLayoutListener {
var onKeyboardStateListener: OnKeyboardStateListener? = null
private var maxHeight = 0
private var isSoftKeyboardOpened = false
var keyboardHeight = 0
// 记录当前填充视图的高度
private var fillingViewHeight: Int = 0;
init {
val contentView = View(context)
setContentView(contentView)
// 这里加上操作栏的高度
maxHeight = getScreenHeight(contentView.context) + getNavigationBarHeight(context)
this.height = maxHeight
this.width = 0
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
this.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
this.inputMethodMode = INPUT_METHOD_NEEDED
contentView.viewTreeObserver.addOnGlobalLayoutListener(this)
rootLayout.post {
showAtLocation(
rootLayout,
Gravity.NO_GRAVITY,
0,
0
)
}
}
override fun onGlobalLayout() {
val rect = Rect()
this.contentView.getWindowVisibleDisplayFrame(rect)
if (rect.bottom > maxHeight) {
maxHeight = rect.bottom
}
// int screenHeight = DPUtil.getScreenHeight(this.getContentView().getContext());
//键盘的高度
keyboardHeight = maxHeight - rect.bottom
val visible: Boolean = keyboardHeight > maxHeight / 4
if (!isSoftKeyboardOpened && visible) {
isSoftKeyboardOpened = true
changeFill(true, keyboardHeight)
if (ObjectUtil.isNotNull(onKeyboardStateListener)) {
onKeyboardStateListener!!.onOpened(keyboardHeight)
}
} else if (isSoftKeyboardOpened && !visible) {
isSoftKeyboardOpened = false
changeFill(false, keyboardHeight)
if (ObjectUtil.isNotNull(onKeyboardStateListener)) {
onKeyboardStateListener!!.onClosed()
}
}
}
/**
* 获取导航栏高度
* 获取NavigationBar的高度
*/
private fun getNavigationBarHeight(context: Context): Int {
val resources: Resources = context.resources
val resourceId: Int = resources.getIdentifier("navigation_bar_height", "dimen", "android")
return resources.getDimensionPixelSize(resourceId)
}
/**
* 获取屏幕高度
*/
private fun getScreenHeight(context: Context): Int {
return context.resources.displayMetrics.heightPixels
}
/**
* 使用动画改变填充视图的高度
*/
private fun changeFill(show: Boolean, height: Int) {
//属性动画对象
val va: ValueAnimator = if (show) {
//显示view,高度从0变到height值
ValueAnimator.ofInt(0, height)
} else {
//隐藏view,高度从height变为0
// 当键盘消失时,高端已经变为0了,所以这里需要取上次记录的高度
ValueAnimator.ofInt(fillingViewHeight, 0)
}
fillingViewHeight = height
va.addUpdateListener { valueAnimator -> //获取当前的height值
val h = valueAnimator.animatedValue as Int
//动态更新view的高度
fillingView.layoutParams.height = h
fillingView.requestLayout()
}
va.duration = 1000
//开始动画
va.start()
}
/**
* 销毁时调用
*/
fun destroy() {
this.dismiss()
}
}
/**
* 键盘弹出与收回监听
*/
interface OnKeyboardStateListener {
fun onOpened(keyboardHeight: Int)
fun onClosed()
}
使用代码:
package pers.yefeng.anotebook.activity
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.database.Cursor
import android.graphics.*
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.text.*
import android.text.style.DynamicDrawableSpan
import android.text.style.ImageSpan
import android.text.style.StyleSpan
import android.util.DisplayMetrics
import android.view.MotionEvent
import android.view.View
import android.widget.EditText
import android.widget.Toast
import android.widget.ToggleButton
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import pers.yefeng.anotebook.R
import pers.yefeng.anotebook.contant.RequestCodeConstant
import pers.yefeng.anotebook.databinding.ActivityEditNoteBinding
import pers.yefeng.anotebook.util.LogUtil
import pers.yefeng.anotebook.view.KeyboardPopupWindow
import java.math.BigDecimal
import java.math.RoundingMode
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
class EditNoteActivity : AppCompatActivity() {
private lateinit var noteBinding: ActivityEditNoteBinding
private lateinit var keyboardPopupWindow: KeyboardPopupWindow
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
noteBinding = ActivityEditNoteBinding.inflate(layoutInflater)
setContentView(noteBinding.root)
keyboardPopupWindow = KeyboardPopupWindow(this, noteBinding.root, noteBinding.fillingView)
}
override fun onDestroy() {
super.onDestroy()
keyboardPopupWindow.destroy()
}
}
windowSoftInputMode配置
android:windowSoftInputMode="adjustNothing|stateHidden"
对应布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activity.EditNoteActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:background="@color/teal_700"
android:text="TOP"
/>
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<!-- 输入框 -->
<EditText
android:id="@+id/edit_info"
android:background="@color/black_overlay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:padding="10dp" />
<!-- 填充视图 -->
<LinearLayout
android:id="@+id/filling_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#607477"
android:orientation="vertical" />
</LinearLayout>
参考文档: