在 android 中以编程方式滑动以回复聊天消息,例如 whats app

Swipe to reply in chat message like whats app programmatically in android

我目前正在开发聊天应用程序。我需要滑动才能以编程方式回复 android 中的 WhatsApp 等特定消息。请帮助我实现这一目标。提前致谢。

link我指的是 https://github.com/shainsingh89/SwipeToReply.

你应该阅读这篇滑动回复聊天消息希望它能帮助你 https://medium.com/mindorks/swipe-to-reply-android-recycler-view-ui-c11365f8999f

好吧,在这里我将列出主要挑战和建议的解决方案,您可以在 github over here:

上找到整个项目

挑战 1:带有引用文本的新布局

  1. 为发送者和接收者添加两个新的聊天消息布局 send_message_quoted_row & received_message_quoted_row ->> 布局可能比这更好,但对现在。

  2. 修改 MessageAdapter 以接受它们作为新类型,并更新 onBindViewHolder 中的引用文本:

    private fun getItemViewType(message: Message): Int {
        return if (message.type == MessageType.SEND)
            if (message.quotePos == -1) R.layout.send_message_row
            else R.layout.send_message_quoted_row
        else
            if (message.quotePos == -1) R.layout.received_message_row
            else R.layout.received_message_quoted_row
    }

    override fun onBindViewHolder(holder: MessageViewHolder, position: Int) {
        val message = messageList[position]
        holder.txtSendMsg.text = message.body
        holder.txtQuotedMsg?.text = message.quote
    }

    class MessageViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        var txtSendMsg = view.txtBody!!
        var txtQuotedMsg: TextView? = view.textQuote
    }
  1. Message 数据添加新构造函数 class 以接受引用和原始消息的位置(在当前消息中被引用
data class Message(var body: String, var time: Long, var type: Int) {

    var quote: String = ""
    var quotePos: Int = -1

    constructor(
        body: String,
        time: Long,
        type: Int,
        quote: String,
        quotePos: Int
    ) : this(body, time, type) {
        this.quote = quote
        this.quotePos = quotePos
    }

}

object MessageType {
    const val SEND = 1
    const val RECEIVED = 2
}
  1. SampleMessages
  2. 中添加用于测试的示例引用消息

挑战 2:将引用布局嵌入到像 WhatsApp 一样动画的回复布局中

在WhatsApp中:引用版面作为回复版面的一部分出现,在原版面之后从下往上逐渐出现。此外,当按下取消按钮时,它会将动画反转到底部。

  • 通过使用自定义动画解决 class 通过更改引用 TextView 的高度,然后使用视图。Gone/Visible 显示布局。
class ResizeAnim(var view: View, private val startHeight: Int, private val targetHeight: Int) :
    Animation() {

    override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
        if (startHeight == 0 || targetHeight == 0) {
            view.layoutParams.height =
                (startHeight + (targetHeight - startHeight) * interpolatedTime).toInt()
        } else {
            view.layoutParams.height = (startHeight + targetHeight * interpolatedTime).toInt()
        }
        view.requestLayout()
    }

    override fun willChangeBounds(): Boolean {
        return true
    }
}

并在 activity showQuotedMessage() & hideReplyLayout()

内处理此动画
private fun hideReplyLayout() {

    val resizeAnim = ResizeAnim(reply_layout, mainActivityViewModel.currentMessageHeight, 0)
    resizeAnim.duration = ANIMATION_DURATION

    Handler().postDelayed({
        reply_layout.layout(0, -reply_layout.height, reply_layout.width, 0)
        reply_layout.requestLayout()
        reply_layout.forceLayout()
        reply_layout.visibility = View.GONE

    }, ANIMATION_DURATION - 50)

    reply_layout.startAnimation(resizeAnim)
    mainActivityViewModel.currentMessageHeight = 0

    resizeAnim.setAnimationListener(object : Animation.AnimationListener {
        override fun onAnimationStart(animation: Animation?) {
        }

        override fun onAnimationEnd(animation: Animation?) {
            val params = reply_layout.layoutParams
            params.height = 0
            reply_layout.layoutParams = params
        }

        override fun onAnimationRepeat(animation: Animation?) {
        }
    })

}

private fun showQuotedMessage(message: Message) {
    edit_message.requestFocus()
    val inputMethodManager =
        getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
    inputMethodManager.showSoftInput(edit_message, InputMethodManager.SHOW_IMPLICIT)

    textQuotedMessage.text = message.body
    val height = textQuotedMessage.getActualHeight()
    val startHeight = mainActivityViewModel.currentMessageHeight

    if (height != startHeight) {

        if (reply_layout.visibility == View.GONE)
            Handler().postDelayed({
                reply_layout.visibility = View.VISIBLE
            }, 50)

        val targetHeight = height - startHeight

        val resizeAnim =
            ResizeAnim(
                reply_layout,
                startHeight,
                targetHeight
            )

        resizeAnim.duration = ANIMATION_DURATION
        reply_layout.startAnimation(resizeAnim)

        mainActivityViewModel.currentMessageHeight = height

    }

}

private fun TextView.getActualHeight(): Int {
    textQuotedMessage.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
    return this.measuredHeight
}

挑战三:计算新引用文字高度的真实值

特别是当用户在当前有引用消息的情况下滑动另一条不同高度的消息时需要引用文本高度需要expanded/shrink。

  • 通过使用 getHeight() 函数以编程方式膨胀引用的 TextView 并将其文本设置为新文本,将其高度与旧文本的高度进行比较,以及相应地操纵动画。

  • 这已经包含在顶级方法中,我使用 currentMessageHeight 整数跟踪 ViewModel 中的旧高度。

挑战 4:将 OnClickListener 添加到引用的消息中

  • 所以要转到引用消息的原始位置,我们在Message class中将其注册为一个字段,当它是-1时,则不是引用消息;否则它是引用消息。
  • 点击侦听器由 MessageAdapter
  • 中的自定义 QuoteClickListener 界面处理

预览:

添加新消息: