如何使用android java中的recyclerview实时接收聊天消息

问题描述

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

如果我发送消息,接收方需要实时接收消息。我怎样才能让它发挥作用。

我不知道 broadCastReceiver。在这里,我正在使用聊天屏幕。需要在我的应用中显示聊天消息。

问题是,如果用户向我发送消息,我无法看到该消息。我需要通过 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,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());
}

解决方法

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

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

遵循此步骤。

将这些添加到您的应用模块 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()
}

现在它是可见的。