其他
一起来打造一个仿微信聊天的键盘
https://juejin.cn/user/2365804752418232
android:id="@+id/cl_voiceRoom_inputRoot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:focusable="true"
android:focusableInTouchMode="true"
android:orientation="vertical"
tools:visibility="visible">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/imEditBgCL"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/im_chat_bottom_bg"
android:minHeight="60dp">
// 键盘顶部,表情输入框等
</androidx.constraintlayout.widget.ConstraintLayout>
// 指定面板占位
<androidx.fragment.app.FragmentContainerView
android:id="@+id/imMiddlewareVP"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#F0EFEF"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/imEditBgCL"
tools:visibility="visible"
/>
</LinearLayout>
android:inputType="textMultiLine"
android:inputType="text"
//然后在代码中进行如下设置:
binding.imMiddlewareET.run {
imeOptions = EditorInfo.IME_ACTION_SEND
setHorizontallyScrolling(false)
maxLines = Int.MAX_VALUE
}
rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect rect = new Rect();
rootView.getWindowVisibleDisplayFrame(rect);
int heightDiff = rootView.getHeight() - rect.bottom;
boolean isSoftKeyboardOpened = heightDiff > 0;
// 处理软键盘打开或关闭的逻辑
}
});
取消Edit焦点 关闭键盘 打开emoji面板
隐藏面板 设置获取焦点 打开键盘其他场景下切换没什么问题,但是当键盘和自定义面板切换时有可能出现这样的问题
private fun showSoftKeyBoard(view: View) {
val imm = mContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
imm?.showSoftInput(view, InputMethodManager.SHOW_FORCED)
}
// 隐藏键盘
private fun hideSoftKeyBoard(view: View) {
val imm = mContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
if (imm != null && imm.isActive) {
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
}
* Synonym for {@link #hideSoftInputFromWindow(IBinder, int, **ResultReceiver**)}
* without a result: request to hide the soft input window from the
* context of the window that is currently accepting input.
*
* @param windowToken The token of the window that is making the request,
* as returned by {@link View#getWindowToken() View.getWindowToken()}.
* @param flags Provides additional operating flags. Currently may be
* 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) {
return hideSoftInputFromWindow(windowToken, flags, null);
}
ResultReceiver resultReceiver) {
return hideSoftInputFromWindow(windowToken, flags, resultReceiver,
SoftInputShowHideReason.HIDE_SOFT_INPUT);
}
return showSoftInput(view, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT);
}
val imm = mActivity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
if (imm != null && imm.isActive) {
val resultReceiver = object : ResultReceiver(Handler(Looper.getMainLooper())) {
override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
super.onReceiveResult(resultCode, resultData)
// 在这里处理软键盘隐藏完成后的逻辑
callback.invoke()
//...
}
}
imm.hideSoftInputFromWindow(view.windowToken, 0, resultReceiver)
}
}
Unicode编码:Emoji表情的Unicode编码可以直接嵌入到字符串中,例如 "\u2764\ufe0f"表示一个红色的心形Emoji。其中,\u 是Unicode转义字符,后面跟着 4 个十六进制数表示该字符的 Unicode 编码。 Unicode代码点:Unicode代码点是Unicode编码的十进制表示,可以使用 &# 后跟代码点数字和分号 ; 来表示Emoji,例如 😀 表示一个笑脸 Emoji。在XML中,可以使用&#x后跟代码点的十六进制表示来表示Emoji,例如 😀 表示一个笑脸 Emoji。 Emoji表情符号:在Android 4.4及以上版本中,可以直接使用Emoji表情符号来表示 Emoji,例如😊表示一个微笑的Emoji。在 Android 4.3及以下版本中,需要使用第一种或第二种方式来表示Emoji。
UI布局 数据
https://unicode.org/Public/emoji/14.0/emoji-test.txt
val pattern = Regex("^(\S+)\s+;\s+fully-qualified\s+#\s+((?:\S+\s+)+)(.+)$")
val filterNotNull = readAssetsFile("emoji.txt", IMApplication.context)
.trim()
.lines()
.map { line ->
val matchResult = pattern.find(line)
if (matchResult != null) {
val (emoji, codePointHex, comment) = matchResult.destructured
val codePoint = emoji.drop(2).toInt(16)
EmojiEntry(emoji, codePoint, "E${emoji.take(2)}", comment,codePointHex)
} else {
null
}
}.filterNotNull()
emit(filterNotNull)
}
"com.google.android.gms.fonts",
"com.google.android.gms",
"Montserrat Subrayada",
R.array.com_google_android_gms_fonts_certs
)
val config = FontRequestEmojiCompatConfig(this, fontRequest)
EmojiCompat.init(config)
<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
app:fontProviderAuthority="com.google.android.gms.fonts"
app:fontProviderPackage="com.google.android.gms"
app:fontProviderQuery="Montserrat Subrayada"
app:fontProviderCerts="@array/com_google_android_gms_fonts_certs">
</font-family>
val inputConnection =
editText.onCreateInputConnection(
EditorInfo()
)
// 找到要删除的字符的边界
val text = editText.text.toString()
val index = editText.selectionStart
var deleteLength = 1
if (index > 0 && index <= text.length) {
val codePoint = text.codePointBefore(index)
deleteLength = if (Character.isSupplementaryCodePoint(codePoint)) 2 else 1
}
inputConnection.deleteSurroundingText(deleteLength, 0)
}
首先,通过editText.onCreateInputConnection(EditorInfo())方法获取输入连接器(InputConnection),它可以用于向EditText发送文本和控制命令。在这里,我们使用它来删除文本。 接着,获取EditText中当前的文本,并找到要删除的字符的边界。通过editText.selectionStart方法获取当前文本的光标位置,然后使用text.codePointBefore(index)方法获取光标位置前面一个字符的Unicode编码点。如果该字符是一个Unicode表情符号,它可能由多个Unicode编码点组成,因此需要使用Character.isSupplementaryCodePoint(codePoint)方法来判断该字符是否需要删除多个编码点。 最后,使用inputConnection.deleteSurroundingText(deleteLength, 0)方法删除要删除的字符。其中,deleteLength是要删除的字符数,0 表示没有要插入的新文本。
首先,通过root.viewTreeObserver.addOnGlobalLayoutListener方法添加一个全局布局监听器,该监听器可以监听整个布局树的变化,包括软键盘的弹出和隐藏。 在监听器的回调函数中,通过root.getWindowVisibleDisplayFrame(r)方法获取当前窗口的可见区域(不包括软键盘),并通过root.rootView.height方法获取整个布局树的高度,从而计算出软键盘的高度keypadHeight。 接着,通过计算屏幕高度的15%来判断软键盘是否弹出。如果软键盘高度超过了屏幕高度的15%,则认为软键盘已经弹出。 如果软键盘已经弹出,则通过imMiddlewareRV.scrollToPosition(mAdapter.getItemCount() - 1)方法将RecyclerView滚动到最后一条消息的位置,以确保用户始终能看到最新的消息。
val r = Rect()
root.getWindowVisibleDisplayFrame(r)
val screenHeight = root.rootView.height
val keypadHeight = screenHeight - r.bottom
//键盘是否弹出
val diff = screenHeight * 0.15
if (keypadHeight > diff) { // 15% of the screen height
imMiddlewareRV.scrollToPosition(mAdapter.getItemCount() - 1);
}
}