【问题标题】:How to receive the chat message in real time using the recyclerview in android java如何使用android java中的recyclerview实时接收聊天消息
【发布时间】:2021-03-31 09:42:53
【问题描述】:

我已经使用 recyclerview 实现了聊天视图列表。但是列表没有实时向接收者显示消息。

如果我发送消息,接收者需要实时接收消息。我怎样才能使它工作。

我对广播接收器一无所知。在这里,我正在使用聊天屏幕。需要在我的应用中显示聊天消息。

问题是如果用户向我发送消息,我无法看到该消息。我需要通过recyclerview接收消息。

在我的聊天片段中:

  private RecyclerView mChatMessageRecyclerView;
    
     @Override
        public void onResume() {
            super.onResume();
            IntentFilter filter = new IntentFilter("Message_send");
            registerForContextMenu(mChatMessageRecyclerView);
        }
    
        @Override
        public void onPause() {
            super.onPause();
            unregisterForContextMenu(mChatMessageRecyclerView);
        }
    
        private final BroadcastReceiver mReceiverRecyclerview = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                mAdapter.notifyDataSetChanged();
                mAdapter.notifyItemInserted(getItemCount() + 1);
                mChatMessageRecyclerView.smoothScrollToPosition(getItemCount() + 1);
            }
        };
    
     LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
            
     @Override
        public void onViewCreated(View v, @Nullable Bundle b) {
            super.onViewCreated(v, b);
    linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
            linearLayoutManager.setStackFromEnd(true);
            linearLayoutManager.setSmoothScrollbarEnabled(true);
            linearLayoutManager.setReverseLayout(false);
         mChatMessageRecyclerView.setLayoutManager(linearLayoutManager);
            LocalBroadcastReceiver br = new LocalBroadcastReceiver() {
                @Override
                public void onReceiveIntent(@NonNull Context context, @NonNull Intent intent) {
                    mAdapter.notifyDataSetChanged();
                }
            };
            mChatMessageRecyclerView.getLayoutManager().smoothScrollToPosition(mChatMessageRecyclerView, new RecyclerView.State(), mChatMessageRecyclerView.getAdapter().getItemCount());
            
            int newMsgPosition = mAdapter.getItemCount();
            mChatMessageRecyclerView.setHasFixedSize(true);
            mChatMessageRecyclerView.setAdapter(mAdapter);
    }
    
     mSendMsg.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    String msgContent = mSendMsgInputBox.getText().toString();
    
                    if(!TextUtils.isEmpty(msgContent))
                    {
                        execute(new PostMessageRequest(message, Chatmessage.getmId(),

mPostMessageListener);
                    mSendMsgInputBox.setText("");
                }
            }
        });

我的适配器:

       private static final int VIEW_TYPE_MESSAGE_SENT = 1;
        private static final int VIEW_TYPE_MESSAGE_RECEIVED = 2;
        private List<MyMessageModel> msgDtoList; 
        MyMessageModel msgDto = this.msgDtoList.get(position);


         @Override
    public int getItemViewType(int position) {
        MyMessageModel message = (MyMessageModel) msgDtoList.get(position);

        if (message.getId().equals(Profile.getId())) {
            // If the current user is the sender of the message
            return VIEW_TYPE_MESSAGE_SENT;
        } else {
            // If some other user sent the message
            return VIEW_TYPE_MESSAGE_RECEIVED;
        }
    }

 @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;

        if (viewType == VIEW_TYPE_MESSAGE_SENT) {
            view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.sender_message_layout, parent, false);
            return new SenderMessageHolder(view);

        } else if (viewType == VIEW_TYPE_MESSAGE_RECEIVED) {
            view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.receiver_message_layout, parent, false);
            return new ReceiverMessageHolder(view);
        }
    return null;
    }
             public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
                LastMessageContent message = (LastMessageContent) msgDtoList.get(position);

                switch (holder.getItemViewType()) {
                    case VIEW_TYPE_MESSAGE_SENT:
                        populateSentViewHolder(holder, position);   break;
                    case VIEW_TYPE_MESSAGE_RECEIVED:
                        populateReceivedViewHolder(holder, position);

                }

 @SuppressLint("ResourceAsColor")
    private void populateSentViewHolder(RecyclerView.ViewHolder holder, int position) {
        MyMessageModal msgDto = this.msgDtoList.get(position);
        ((SenderMessageHolder) holder).rightMsgTextView.setText(msgDto.getmMSg());
}

 @SuppressLint("ResourceAsColor")
    private void populateReceivedViewHolder(RecyclerView.ViewHolder holder, int position) {
        MyMessageModal msgDto = this.msgDtoList.get(position);
        ((ReceiverMessageHolder) holder).leftMsgTextView.setText(msgDto.getmMSg());
}

【问题讨论】:

  • 您将消息发送到哪里?
  • 聊天屏幕。例如像whatsapp。如果我发送消息接收者需要实时接收消息。我正在使用回收站视图
  • 你能发布一些你的代码吗?主要是,您如何收听消息以及您的 RecyclerView.Adapter 是什么样的?您是否使用 BroadcastReceiver 来接收消息?很难说可能是什么问题。另外,您确定您的侦听器/BroadcastReceiver 接收到消息吗?
  • @J.Jefferson 更新了我的代码
  • 这段代码有几个问题。您应该查看 Android Docs for BroadcastReceivers 以及它们是如何工作的。首先弄清楚如何注册一个 BroadcastReceiver 来监听某些事件,比如发送/接收消息。您可能需要请求许可才能执行此操作。此外,来自 BroadcastReceiver.onReceive(context,intent) 的 Intent 应该包含您的数据。您必须从该对象中获取消息。最后,您需要通过一种方法将该消息传递到您的适配器,以将其附加到消息列表并在您的适配器类中调用 notifyDataSetChange()。

标签: android android-recyclerview adapter


【解决方案1】:

为了在您的RecycleView 上显示实时数据,您需要实现两件事:

  1. 监听实时数据的数据源。这可能是 来自例如WebSocketsFirebase、一些消息队列等
  2. 然后你需要通知你的适配器关于传入的消息 然后更新您的RecycleView。您希望在每次收到消息时仅使用新消息而不是整个列表来更新RecycleView。为了 您将使用ListAdaptorDiffUtil.ItemCallback。这是一个很好的教程,解释了如何管理 RecycleView 更新(用 Kotlin 解释):https://developer.android.com/codelabs/kotlin-android-training-diffutil-databinding/#0

【讨论】:

  • 我正在使用 android 和 java。我的数据来自我们的后端数据服务。不是来自 Firebase。那么您是否提到我需要使用 Websockets 收听消息更新?\
  • 确实,使用 socket.io 是实时双向通信的一个不错的选择。它将像任何其他发布/订阅模型(等推送器、firebase 云消息传递)一样完成这项工作。继续使用 socket.io,您需要在后端实现一个 socket.io 服务器,并在您的应用程序上使用一个 socket.io Android 客户端。 socket.io-client-java 是您可以使用的一个库:github.com/socketio/socket.io-client-java
  • 您可以在互联网上找到许多使用 socket.io 实现实时架构的示例。这是两个:medium.com/@mrcurious/…medium.com/@mayuroks/…
  • 请注意,如果您只依赖 WebSockets,您需要让您的应用程序在后台运行,即使用户没有主动使用它。但是之后; 1)Android无论如何都可以杀死你的进程,2)即使它没有杀死它,你的应用程序也很容易耗尽电池。所以最好的方法可能是结合使用 WebSockets 和 FCM。当你的应用程序运行时使用 WebSockets,当不使用 FCM 时。
  • 不幸的是,具有 REST API 的后端不足以满足您的用例。您需要一个能够在服务器和客户端之间进行双向通信的接口。将消息发送到您的回收站视图是第二步。
【解决方案2】:

按照这个步骤。

将这些添加到您的 app-module build.Gradle 文件中:

 dependencies {
    // [...]

    implementation 'com.pusher:pusher-java-client:1.8.0'
    implementation 'com.android.support:recyclerview-v7:28.0.0'
}

打开为您生成的activity_main.xml,将内容替换为以下内容:

 <?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerViewContents"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:textSize="16sp"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_margin="16dp"
        android:background="@drawable/rounded_corner"
        android:textColor="@android:color/black"
        android:visibility="gone"
        android:layout_gravity="right|bottom"
        android:id="@+id/textViewNewContents" />

</FrameLayout>

**Create a new drawable named rounded_corner and paste this:**

  <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <stroke
        android:width="1dp" />

    <solid android:color="#ffffff" />

    <padding
        android:left="1dp"
        android:right="1dp"
        android:bottom="1dp"
        android:top="1dp" />

    <corners android:radius="5dp" />
</shape>

接下来,让我们为回收器视图创建一个适配器。创建一个名为 RecyclerListAdapter 的新类并粘贴:

  import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView

class RecyclerListAdapter(private val listener:OnLastPositionReached): RecyclerView.Adapter<RecyclerListAdapter.ViewHolder>() {

  private val contentList: ArrayList<String> = ArrayList()

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    return ViewHolder(
       LayoutInflater
         .from(parent.context)
         .inflate(android.R.layout.simple_list_item_1, parent, false)
    )
  }

  override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    holder.bind(contentList[position])

    if (position == contentList.size-1){
      listener.lastPositionReached()
    } else {
      listener.otherPosition()
    }
  }

  override fun getItemCount(): Int = contentList.size

  fun addItem(item:String) {
    contentList.add(item)
    notifyDataSetChanged()
  }

  inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val userName: TextView = itemView.findViewById(android.R.id.text1)

    fun bind(item: String) = with(itemView) {
      userName.text = item
    }
  }

  interface OnLastPositionReached {
    fun lastPositionReached()
    fun otherPosition()
  }
}

最后,打开 MainActivity 并进行如下设置:

   import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import android.view.View
import com.pusher.client.Pusher
import com.pusher.client.PusherOptions
import kotlinx.android.synthetic.main.activity_main.*
import org.json.JSONObject

class MainActivity : AppCompatActivity(), RecyclerListAdapter.OnLastPositionReached {

  private var count = 0
  private val recyclerListAdapter = RecyclerListAdapter(this)

  private var lastPosition = false
  override fun otherPosition() {
    lastPosition = false
  }

  override fun lastPositionReached() {
    lastPosition = true
    textViewNewContents.visibility = View.GONE
    count = 0
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setupClickListeners()
    setupRecyclerView()
    setupPusher()
  }
}

当点击显示新消息计数的文本视图时,它会立即向下滚动到最近的消息,将计数设置为 0 并隐藏文本视图。

下一个方法是 setupRecyclerView 方法。像这样设置它:

 private fun setupRecyclerView() {
  with(recyclerViewContents){
    layoutManager = LinearLayoutManager(this@MainActivity)
    adapter = recyclerListAdapter
  }

  recyclerListAdapter.addItem("Hello World")
  recyclerListAdapter.addItem("New article alert!")
  recyclerListAdapter.addItem("Pusher is actually awesome")
  recyclerListAdapter.addItem("Realtime functionality freely provided ")
  recyclerListAdapter.addItem("Checkout pusher.com/tutorials")
  recyclerListAdapter.addItem("You can also checkout blog.pusher.com")
  recyclerListAdapter.addItem("Learn how to update contents properly ")
  recyclerListAdapter.addItem("Hello World")
  recyclerListAdapter.addItem("New article alert!")
  recyclerListAdapter.addItem("Pusher is actually awesome")
  recyclerListAdapter.addItem("Realtime functionality freely provided ")
  recyclerListAdapter.addItem("Checkout pusher.com/tutorials")
  recyclerListAdapter.addItem("You can also checkout blog.pusher.com")
  recyclerListAdapter.addItem("Learn how to update contents properly ")
}

下一个方法是 setupPusher 方法。像这样设置它:

private fun setupPusher() {
  val options = PusherOptions()
  options.setCluster("PUSHER_CLUSTER")

  val pusher = Pusher("PUSHER_KEY", options)
  val channel = pusher.subscribe("my-channel")

  channel.bind("my-event") { channelName, eventName, data ->
    runOnUiThread {  
      if (!lastPosition){
        count ++
        textViewNewContents.text = count.toString()
        textViewNewContents.visibility = View.VISIBLE
        recyclerListAdapter.addItem(JSONObject(data).getString("message"))    
      } else {
        recyclerListAdapter.addItem(JSONObject(data).getString("message"))
        recyclerViewContents.scrollToPosition(recyclerListAdapter.itemCount-1)
      } 
    }
  }

  pusher.connect()
}

现在它是可见的。

【讨论】:

  • 我使用的是 java 而不是 Kotlin
  • 我可以在java的这个推送器中使用它吗
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多