安卓适配输入法

897 阅读2分钟

效果图

1635573942134.gif

基本原理:

在屏幕上,放置一个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>

参考文档:

这或许就是你想要的聊天键盘处理方案

Android 使用动画来动态改变View的高度