简体   繁体   English

滚动时 RecyclerView 中的项目显示不正确

[英]Incorrect displaying items in RecyclerView while scrolling

Could you please assist in following issue:您能否协助解决以下问题:

I have incorrect displaying items in my messenger app.我的 Messenger 应用程序中显示的项目不正确。

My layout for items:我的项目布局:

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginHorizontal="10dp"
    android:layout_marginTop="10dp"
    app:cardElevation="0dp">

        <TextView
            android:id="@+id/message_my_message"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="end"
            android:gravity="end"
            android:textColor="@color/black"
            android:textSize="14sp"
            android:visibility="invisible"
            />

        <TextView
            android:id="@+id/message_your_message"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="start"
            android:gravity="start"
            android:textColor="@color/black"
            android:textSize="14sp"
            android:visibility="invisible"
            />

</com.google.android.material.card.MaterialCardView>

My layout for dialog activity:我的对话框活动布局:

<RelativeLayout 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=".DialogActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/dialog_appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/Theme.AnonymousAd.AppBarOverlay">

        <include
            android:id="@+id/dialog_app_bar"
            layout="@layout/app_bar_layout"
            >

        </include>

    </com.google.android.material.appbar.AppBarLayout>



        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/dialog_send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_alignParentBottom="true"
            android:clickable="true"
            android:contentDescription="TODO"
            android:focusable="true"
            android:src="@drawable/ic_send"
            android:visibility="invisible"
            app:fabSize="mini"
            tools:ignore="SpeakableTextPresentCheck" />

        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/dialog_attach"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_alignParentBottom="true"
            android:clickable="true"
            android:contentDescription="TODO"
            android:focusable="true"
            android:src="@drawable/ic_attach"
            app:fabSize="mini"
            tools:ignore="SpeakableTextPresentCheck" />

        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/dialog_gift"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentStart="true"
            android:layout_alignParentBottom="true"
            android:clickable="true"
            android:contentDescription="TODO"
            android:focusable="true"
            android:src="@drawable/ic_gift"
            app:fabSize="mini"
            tools:ignore="SpeakableTextPresentCheck" />

        <EditText
            android:id="@+id/dialog_message"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_toLeftOf="@+id/dialog_send"
            android:layout_toRightOf="@+id/dialog_gift"
            android:autofillHints=""
            android:backgroundTint="@color/teal_700"
            android:hint="@string/dialog_enter_message"
            android:inputType="textCapSentences|textMultiLine"
            android:maxHeight="120dp"
            android:maxLength="500"
            android:minHeight="48dp"
            android:textColorHint="#757575"
            android:textSize="16sp" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/dialog_recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_above="@+id/dialog_message"
            android:layout_below="@+id/dialog_appbar"
            android:layout_marginBottom="5dp"
            android:focusedByDefault="true"
            android:scrollbars="vertical" />

Class DialogActivity Class DialogActivity

public class DialogActivity extends AppCompatActivity {

    private RecyclerView dialogRecyclerView;
    private LinearLayoutManager layoutManager;
    private final List<Message> messageList = new ArrayList<>();
    private MessageAdapter messageAdapter;
    private String idText;
    private String userNameText;
    private EditText dialogMessage;
    private Toolbar toolbar;
    private FloatingActionButton dialogSend;
    private FloatingActionButton dialogAttach;
    private String downloadedImageUrl;
    private StorageTask uploadTask;
    private Uri imageUri;
    private DatabaseReference dialogsDataBase;
    private ProgressDialog loadingBar;

    private FirebaseFirestore db = FirebaseFirestore.getInstance();
    private CollectionReference answersDataBase = db.collection("AnswersDataBase");
    private StorageReference imageDataBase;

    @SuppressLint("WrongViewCast")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dialog);

        idText = getIntent().getExtras().get("idText").toString();
        userNameText = getIntent().getExtras().get("userNameText").toString();

        dialogsDataBase = FirebaseDatabase.getInstance().getReference().child("DialogsDataBase").child(idText);
        imageDataBase = FirebaseStorage.getInstance().getReference().child("imageDataBase");

        toolbar = findViewById(R.id.dialog_app_bar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setTitle(userNameText);

        messageAdapter = new MessageAdapter(this, messageList);
        dialogRecyclerView = findViewById(R.id.dialog_recycler_view);
        dialogRecyclerView.setHasFixedSize(true);
        layoutManager = new LinearLayoutManager(this);
        layoutManager.setStackFromEnd(true);
        dialogRecyclerView.setLayoutManager(layoutManager);
        dialogRecyclerView.setAdapter(messageAdapter);

        loadingBar = new ProgressDialog(this);

        dialogMessage = findViewById(R.id.dialog_message);

        dialogAttach = findViewById(R.id.dialog_attach);

        dialogAttach.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setAction(Intent.ACTION_GET_CONTENT);
                intent.setType("image/*");
                //startActivityForResult(intent, 438);
                someActivityResultLauncher.launch(intent);
            }
        });

        dialogMessage.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                if(dialogMessage.getText().toString().length() > 0){
                    dialogAttach.setVisibility(View.INVISIBLE);
                    dialogSend.setVisibility(View.VISIBLE);
                }
                else {
                    dialogAttach.setVisibility(View.VISIBLE);
                    dialogSend.setVisibility(View.INVISIBLE);
                }
            }

            @Override
            public void afterTextChanged(Editable editable) {

            }
        });

        dialogSend = findViewById(R.id.dialog_send);

        dialogSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(dialogMessage.getText().toString().length() > 0){
                    answersDataBase.document(idText).update("answer", dialogMessage.getText().toString());
                    dialogsDataBase.push().setValue(new Message(Paper.book().read("userName"), dialogMessage.getText().toString(), "text"));
                    dialogMessage.setText("");
                }
            }
        });

    }

    @Override
    protected void onStart() {
        super.onStart();

        dialogsDataBase.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
                Message message = snapshot.getValue(Message.class);

                messageList.add(message);

                messageAdapter.notifyDataSetChanged();

                dialogRecyclerView.smoothScrollToPosition(dialogRecyclerView.getAdapter().getItemCount());
            }

            @Override
            public void onChildChanged(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {

            }

            @Override
            public void onChildRemoved(@NonNull DataSnapshot snapshot) {

            }

            @Override
            public void onChildMoved(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {

            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {

            }
        });
    }

    ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            new ActivityResultCallback<ActivityResult>() {
                @Override
                public void onActivityResult(ActivityResult result) {
                    if (result.getResultCode() == Activity.RESULT_OK) {

                        loadingBar.setTitle("Sending Image");
                        loadingBar.setMessage("Please wait, we are sending that image");
                        loadingBar.setCanceledOnTouchOutside(false);
                        loadingBar.show();

                        // There are no request codes
                        Intent data = result.getData();
                        imageUri = data.getData();

                        StorageReference filePath = imageDataBase.child(System.currentTimeMillis() + ".jpg");

                        uploadTask = filePath.putFile(imageUri);
                        uploadTask.continueWithTask(new Continuation() {
                            @Override
                            public Object then(@NonNull Task task) throws Exception {

                                if(!task.isSuccessful()){
                                    throw task.getException();
                                }

                                return filePath.getDownloadUrl();
                            }
                        }).addOnCompleteListener(new OnCompleteListener<Uri>() {
                            @Override
                            public void onComplete(@NonNull Task<Uri> task) {

                                if(task.isSuccessful()){

                                    Uri downloadUrl = task.getResult();
                                    downloadedImageUrl = downloadUrl.toString();

                                    answersDataBase.document(idText).update("answer", "Photo");
                                    dialogsDataBase.push().setValue(new Message(Paper.book().read("userName"), downloadedImageUrl, "image"));
                                    loadingBar.dismiss();
                                    recreate();
                                }

                            }
                        });
                    }
                }
            });

    @Override
    protected void onDestroy() {
        super.onDestroy();
        messageList.clear();
    }

    @Override
    protected void onPause() {
        super.onPause();
        messageList.clear();
    }
}

Adapter class适配器 class

public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MessageViewHolderNew> {

    private Context context;
    private List<Message> messagesList;
    private Dialog dialog;
    private String userNameString;

    public MessageAdapter(Context context, List<Message> messagesList){

        this.context = context;
        this.messagesList = messagesList;
        userNameString = Paper.book().read("userName");

    }

    public class MessageViewHolderNew extends RecyclerView.ViewHolder{

        private TextView messageMyMessage;
        private TextView messageYourMessage;
        private ImageView messageMyImage;
        private ImageView messageYourImage;



        public MessageViewHolderNew(@NonNull View itemView) {
            super(itemView);

            messageMyMessage = (TextView) itemView.findViewById(R.id.message_my_message);
            messageYourMessage = (TextView) itemView.findViewById(R.id.message_your_message);
            messageMyImage = (ImageView) itemView.findViewById(R.id.message_my_image);
            messageYourImage = (ImageView) itemView.findViewById(R.id.message_your_image);

        }
    }

    @NonNull
    @Override
    public MessageViewHolderNew onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.message_items_layout, parent, false);

        return new MessageViewHolderNew(view);
    }

    @Override
    public void onBindViewHolder(@NonNull MessageViewHolderNew holder, int position) {

        if(messagesList.get(position).getUserName().equals(userNameString)){
            holder.messageMyMessage.setText(messagesList.get(position).getMessage());
            holder.messageMyMessage.setVisibility(View.VISIBLE);
        }
        else{
            holder.messageYourMessage.setText(messagesList.get(position).getMessage());
            holder.messageYourMessage.setVisibility(View.VISIBLE);
        }

   }

    @Override
    public int getItemCount() {
        return messagesList.size();
    }

}

In this place I check user name.在这个地方我检查用户名。

   if(messagesList.get(position).getUserName().equals(userNameString)){
            holder.messageMyMessage.setText(messagesList.get(position).getMessage());
            holder.messageMyMessage.setVisibility(View.VISIBLE);
        }
        else{
            holder.messageYourMessage.setText(messagesList.get(position).getMessage());
            holder.messageYourMessage.setVisibility(View.VISIBLE);
        }

If user name is my name then I set message and visible to holder.messageMyMessage.如果用户名是我的名字,那么我设置消息并对 holder.messageMyMessage 可见。 Else I set message and visible to holder.messageYourMessage.否则我设置消息并对 holder.messageYourMessage 可见。 But sometimes message and visible are set to both messages while scrolling or sent new message.但有时在滚动或发送新消息时,消息和可见都设置为两个消息。 . . See attached screenshot for details详情见附件截图

Your problem's here:你的问题在这里:

if(messagesList.get(position).getUserName().equals(userNameString)){
    holder.messageMyMessage.setText(messagesList.get(position).getMessage());
    holder.messageMyMessage.setVisibility(View.VISIBLE);
} else {
    holder.messageYourMessage.setText(messagesList.get(position).getMessage());
    holder.messageYourMessage.setVisibility(View.VISIBLE);
}

You're only making stuff visible , you're never hiding the other thing.你只是让东西可见,你从不隐藏其他东西。

So if a specific ViewHolder is used to display one kind of message, it'll be made visible in onBindViewHolder .因此,如果使用特定的ViewHolder来显示一种消息,它将在onBindViewHolder中可见。 The view in that ViewHolder's layout will be set to VISIBLE .该 ViewHolder 布局中的视图将设置为VISIBLE

Then, if you scroll down the list and the same ViewHolder is reused to display the other kind of message, the other message view in the layout will be set to VISIBLE .然后,如果您向下滚动列表并重复使用相同的 ViewHolder来显示其他类型的消息,则布局中的其他消息视图将设置为VISIBLE The other message view hasn't changed, it's still in the same state, VISIBLE .另一个消息视图没有改变,它仍然在同一个 state, VISIBLE中。 So you see them both (depending on how the layout works, one might be covering the other).因此,您会同时看到它们(取决于布局的工作方式,一个可能会覆盖另一个)。

When you're using a RecyclerView , because the ViewHolder s are reused ( recycled ) you need to set them up correctly for each item, clearing all the previous state.当您使用RecyclerView时,因为ViewHolder重复使用(已回收),您需要为每个项目正确设置它们,清除所有以前的 state。 So in your case, for each message display, you have to either make it visible or hide it.因此,在您的情况下,对于每个消息显示,您必须使其可见隐藏它。 You can't do nothing, because that can leave you with old state from the previous item it was displaying, right?你不能什么都不做,因为这可能会给你留下旧的 state 从它显示的上一个项目,对吗?

So you need to do this:所以你需要这样做:

if(messagesList.get(position).getUserName().equals(userNameString)){
    holder.messageMyMessage.setText(messagesList.get(position).getMessage());
    holder.messageMyMessage.setVisibility(View.VISIBLE);
    // now you need to make sure the other is -not- visible!
    holder.messageYourMessage.setVisibility(View.GONE);
} else {
    holder.messageYourMessage.setText(messagesList.get(position).getMessage());
    holder.messageYourMessage.setVisibility(View.VISIBLE);
    // same thing - explicitly hide the other one, assume it could be visible
    holder.messageMyMessage.setVisibility(View.GONE);
}

This is the number one thing you need to do with a RecyclerView - in onBindViewHolder , always set up everything that can change depending on the item.这是使用RecyclerView需要做的第一件事 - 在onBindViewHolder中,始终设置可以根据项目更改的所有内容 Assume it has old data or the wrong thing set, and explicitly initialise everything.假设它有旧数据或设置了错误的东西,并显式初始化所有内容。 It's like a whiteboard - when you start using it you need to clean it, because maybe there's some old stuff on there它就像一块白板——当你开始使用它时,你需要清洁它,因为那里可能有一些旧东西

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM