CodeXiaoMai

CodeXiaoMai的博客


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

Dialog 中监听键盘弹出与收起事件

发表于 2019-08-07 分类于 Android 阅读次数:
本文字数: 6.6k

最近遇到了一个在 Dialog 中监听键盘弹出与收起事件的需求,经过查询资料再与自己的需求相结合然后思考,最终解决了问题。现将中间遇到的问题与解决办法记录下来。

需求与效果

demo

思路

自定义 Dialog,通过设置 Dialog 的 “windowSoftInputMode” 配合监听 Dialog 所在 Window 的高度变化来判断键盘当前的状态。

实现

布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rootView"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_gravity="bottom"
android:background="@android:color/white"
android:orientation="horizontal">

<EditText
android:id="@+id/editText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="@null"
android:hint="点此参加讨论~"
android:maxLines="1"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:singleLine="true"
android:textColor="@android:color/black"
android:textSize="13sp" />

<TextView
android:id="@+id/sendButton"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="发送"
android:textColor="@android:color/black"
android:textSize="13sp" />
</LinearLayout>

-w246

style

1
2
3
4
5
6
7
<style name="Dialog" parent="Theme.AppCompat.Dialog">
<item name="android:background">@android:color/transparent</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowSoftInputMode">adjustPan|stateVisible</item>
<item name="android:backgroundDimEnabled">false</item>
</style>

Dialog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class MyDialog(context: Context, private val callback: Callback) : Dialog(context, R.style.Dialog) {

companion object {
private const val TAG = "MyDialog"
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContentView(R.layout.mydioalog)

window?.attributes = window?.attributes?.apply {
width = WindowManager.LayoutParams.MATCH_PARENT
// 因为 adjustPan 在键盘弹出后布局高度大于屏幕上除键盘之外的可用区域的高度时才会起效,
// 而我们监听的就是布局在键盘弹出与收起时可见区域的高度的变化,从而判断键盘当前的状态的。
// 但是不同的手机屏幕高度不同,所以需要动态设置。
// 可用高度 = 屏幕高度 - 状态栏高度
height = Resources.getSystem().displayMetrics.heightPixels - getStatusBarHeight()
gravity = Gravity.BOTTOM
}

rootView.viewTreeObserver.addOnGlobalLayoutListener {
val decorView = window.decorView

val visibleDisplayRect = Rect()

// 检测当前视图所在的 Window 可见区域的大小
decorView.getWindowVisibleDisplayFrame(visibleDisplayRect)

val visibleHeight = decorView.height

Log.d(TAG, "visibleHeight $visibleHeight")

when {
decorViewVisibleHeight == 0 -> {
// do nothing
}
decorViewVisibleHeight > visibleDisplayRect.height() -> callback.onKeyboardShow()
else -> callback.onKeyboardDismiss()
}

decorViewVisibleHeight = visibleHeight
}
}

/**
* 获取状态栏高度
*/
private fun getStatusBarHeight(): Int {
val resourceId = Resources.getSystem().getIdentifier(
"status_bar_height", "dimen", "android"
)

return if (resourceId > 0) {
context.resources.getDimensionPixelSize(resourceId)
} else {
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
25f,
Resources.getSystem().displayMetrics
).toInt()
}
}

private var decorViewVisibleHeight = 0

interface Callback {

/**
* 键盘弹出
*/
fun onKeyboardShow()

/**
* 键盘收起
*/
fun onKeyboardDismiss()
}
}

添加显示 Dialog 的点击事件

在 Activity 中添加一个按钮,并为它添加点击事件如下:

1
2
3
4
5
6
7
8
9
10
button.setOnClickListener {
MyDialog(this, object : MyDialog.Callback {
override fun onKeyboardShow() {
textView.text = "键盘显示"
}
override fun onKeyboardDismiss() {
textView.text = "键盘隐藏"
}
}).show()
}

-w246

此时已经可以监听到键盘的弹出与收起事件了,但是仔细看输入框会发现下半部分被键盘盖住了。

解决问题

经过研究发现这与 EditText 的 background 属性有关,先把 android:background="@null" 去掉,效果如下:

-w246

那么 background 设置为 null 与默认值的差别在哪里呢?我们通过 EditText 的源码找一下答案。

1
2
3
4
5
6
7
8
9
10
public class EditText extends TextView {
public EditText(Context context) {
this(context, null);
}

public EditText(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.editTextStyle);
}
...
}

从源码中可以看出 EditText 的默认 style 为 com.android.internal.R.attr.editTextStyle,接下来全局搜索看一下这个 style 的特征:

-w810

一路追踪 parent,最终发现顶级 parent 如下:

1
2
3
4
5
<style name="Widget.EditText">
...
<item name="background">?attr/editTextBackground</item>
...
</style>

再全局搜索 editTextBackground 关键字:

-w810

从图中可以看出有很多 item 的 name 是 editTextBackground,随便打开一个,这里就以图中选中的为例,打开文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="@dimen/edit_text_inset_horizontal_material"
android:insetRight="@dimen/edit_text_inset_horizontal_material"
android:insetTop="@dimen/edit_text_inset_top_material"
android:insetBottom="@dimen/edit_text_inset_bottom_material">
<selector>
<item android:state_enabled="false">
<nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
android:tint="?attr/colorControlNormal" />
</item>
<item android:state_pressed="false" android:state_focused="false">
<nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
android:tint="?attr/colorControlNormal" />
</item>
<item>
<nine-patch android:src="@drawable/textfield_activated_mtrl_alpha"
android:tint="?attr/colorControlActivated" />
</item>
</selector>
</inset>

可以看到是通过 xml 设置了一个 InsetDrawable,这里效果如同给 EditText 设置 padding。所以我们给 EditText 添加 paddingTop 和 paddingBottom,运行后发现效果 OK。

使用 FragmentDialog 注意

因为 FragmentDialog 是一个 Fragment,所以既可以通过 getActivity().getWindow() 也可以使用 dialog.getWindow(),但是要注意这里应该使用 dialog.getWindow(),反之将不会有预期效果。

# Android # Dialog # 键盘
个人博客进化史
刘海屏适配
  • 文章目录
  • 站点概览
CodeXiaoMai

CodeXiaoMai

CodeXiaoMai的博客
12 日志
6 分类
19 标签
GitHub E-Mail
  1. 1. 需求与效果
  2. 2. 思路
  3. 3. 实现
    1. 3.1. 布局
    2. 3.2. style
    3. 3.3. Dialog
    4. 3.4. 添加显示 Dialog 的点击事件
  4. 4. 解决问题
  5. 5. 使用 FragmentDialog 注意
© 2019 CodeXiaoMai
|