1

I'm making a chat app. I have a recycler view. I use Picasso to download the images of the user and the friend he is chatting with. To not download the image for every message, when I download the first image I just put it into a bitmap and use it every time.

PROBLEM: I can't call NotifyDataSetChanged. I have tried every possible thing I've found on the net.

ERROR : 
Cannot call this method while RecyclerView is computing a layout or scrolling android.support.v7.widget.RecyclerView

I have to "manually" refresh the view so that the images are loaded. No problem for the text. It runs with the Handler but it doesn't even go in the function (so the view isn't refreshed). Here is my code (everything works except this notifydatasetchanged)

public class ChatActivity extends AppCompatActivity {

    private RecyclerView recyclerChat;

    private ListMessageAdapter adapter;

    private EditText editWriteMessage;
    private LinearLayoutManager linearLayoutManager;


    private FirebaseAuth mAuth;
    private DatabaseReference UserRef, MessageIDREF, UserMessageRef, SendMessageUser;

    private List<ChatClass> ListOfChats = new ArrayList<>();
    private ImageButton btnSend;
    private String  currentUserID, currentDateMessage, currentTimeMessage;

    private CommunActivit obj = new CommunActivit();
    private  String FRIENDID;
    private Toolbar mToolbar;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat);



        mAuth=FirebaseAuth.getInstance();
        currentUserID = mAuth.getCurrentUser().getUid();

        MessageIDREF = FirebaseDatabase.getInstance().getReference().child("Friends");
        UserMessageRef = FirebaseDatabase.getInstance().getReference().child("Message").child(obj.getsIDMESSAGE());

        btnSend = (ImageButton) findViewById(R.id.btnSend);
        editWriteMessage = (EditText) findViewById(R.id.editWriteMessage) ;

        FRIENDID = obj.getsIDFRIEND();


        mToolbar = (Toolbar) findViewById(R.id.main_app_bar2);
        setSupportActionBar(mToolbar);
        ActionBar ab = getSupportActionBar();

        final String Title = obj.getsFRIENDName();
        ab.setTitle(Title);

        btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                SendMessageToGroupChat();

                editWriteMessage.setText("");
            }
        });
        retrievedata();
/*recyclerChat = (RecyclerView)findViewById(R.id.recyclerChat);
adapter = new ListMessageAdapter(this,ListOfChats);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
recyclerChat.setLayoutManager(layoutManager);

recyclerChat.setItemAnimator(new DefaultItemAnimator());
recyclerChat.setAdapter(adapter);*/

        linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        recyclerChat = (RecyclerView) findViewById(R.id.recyclerChat);
        recyclerChat.setLayoutManager(linearLayoutManager);
        adapter = new ListMessageAdapter(this, ListOfChats);
        recyclerChat.setAdapter(adapter);

 /*  editWriteMessage.setOnTouchListener(new View.OnTouchListener() {
       @Override
       public boolean onTouch(View v, MotionEvent event) {

       recyclerChat.smoothScrollToPosition(adapter.getItemCount());

       return false;
   }
   });*/


    }


    private void SendMessageToGroupChat() {



        String message = editWriteMessage.getText().toString();
        String messageKey = UserMessageRef.push().getKey();

        if (TextUtils.isEmpty(message)) {
            Toast.makeText(this, "Please write a message first", Toast.LENGTH_SHORT).show();
        } else {
            Calendar calendarDate = Calendar.getInstance();
            SimpleDateFormat DateFormat = new SimpleDateFormat("MMM dd, yyyy");
            currentDateMessage = DateFormat.format(calendarDate.getTime());

            Calendar calendarTime = Calendar.getInstance();
            SimpleDateFormat TimeFormat = new SimpleDateFormat("hh:mm a");
            currentTimeMessage = TimeFormat.format(calendarTime.getTime());


            HashMap<String, Object> groupMessageKey = new HashMap<>();

            UserMessageRef.updateChildren(groupMessageKey);

            SendMessageUser = UserMessageRef.child(messageKey);

            HashMap<String, Object> messageInfoMap = new HashMap<>();

            messageInfoMap.put("name", obj.getsUsername());
            messageInfoMap.put("message", message);
            messageInfoMap.put("date", currentDateMessage);
            messageInfoMap.put("time", currentTimeMessage);
            messageInfoMap.put("image", obj.getsUserImageLink());

            SendMessageUser.updateChildren(messageInfoMap);
        }

    }

    public void retrievedata() {



        UserMessageRef.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) /**** HERE IS THE PROBLEM***/
            {

                if (dataSnapshot.exists()) {
                    DisplayMessages(dataSnapshot);
                }

            }

            @Override
            public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
                if (dataSnapshot.exists()) {
                    DisplayMessages(dataSnapshot);
                }
            }

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

            }

            @Override
            public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

            }

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

            }
        });
    }


    private void DisplayMessages(DataSnapshot dataSnapshot) 
    {
        Iterator iterator = dataSnapshot.getChildren().iterator();

while (iterator.hasNext()) {

    String chatDate = (String) ((DataSnapshot) iterator.next()).getValue();
    String chatImage = (String) ((DataSnapshot) iterator.next()).getValue();
    String chatMessage = (String) ((DataSnapshot) iterator.next()).getValue();
    String chatName = (String) ((DataSnapshot) iterator.next()).getValue();
    String chatTime = (String) ((DataSnapshot) iterator.next()).getValue();


    ListOfChats.add(new ChatClass(chatName, chatMessage, chatDate + "     " + chatTime, chatImage));


    adapter.notifyDataSetChanged();
    recyclerChat.smoothScrollToPosition(adapter.getItemCount()); /**AUTO SCROLL**/

}


}

class ListMessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private Context context;
    private ChatClass consersation;
    private List<ChatClass> List_of_chats;
    private CommunActivit obj = new CommunActivit();

    private Bitmap BitmapUser;
    private Bitmap BitmapFriend;

    public ListMessageAdapter(Context context, List<ChatClass> List_of_chats) {
        this.context = context;
        this.List_of_chats = List_of_chats;

    }

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


        if (viewType == 1) {
            View view = LayoutInflater.from(context).inflate(R.layout.rc_item_message_friend, parent, false);
            return new ItemMessageFriendHolder(view);
        } else if (viewType == 2) {
            View view = LayoutInflater.from(context).inflate(R.layout.rc_item_message_user, parent, false);
            return new ItemMessageUserHolder(view);
        }
        return null;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {



        final int pos =position;
        if (holder instanceof ItemMessageFriendHolder) {
            ((ItemMessageFriendHolder) holder).txtContent.setText(List_of_chats.get(position).getMessage());

            if (BitmapFriend == null) {


                Picasso.get().load(List_of_chats.get(position).getImageLink()).into(new Target() {


                    @Override
                    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                        BitmapFriend = bitmap;
                    }

                    @Override
                    public void onBitmapFailed(Exception e, Drawable errorDrawable) {

                    }

                    @Override
                    public void onPrepareLoad(Drawable placeHolderDrawable) {

                    }
                });
                ((ItemMessageFriendHolder) holder).avata.setImageBitmap(BitmapFriend);



            }
            else
            {

                ((ItemMessageFriendHolder) holder).avata.setImageBitmap(BitmapFriend);


                new Handler().post(new Runnable() {
                    @Override
                    public void run() {
                        notifyDataSetChanged();
                    }
                });

            }
        }
        else
        {
            if (holder instanceof ItemMessageUserHolder) {
                ((ItemMessageUserHolder) holder).txtContent.setText(List_of_chats.get(position).getMessage());
                if (BitmapUser == null) {


                    Picasso.get().load(List_of_chats.get(position).getImageLink()).into(new Target() {


                        @Override
                        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                            BitmapUser = bitmap;
                        }

                        @Override
                        public void onBitmapFailed(Exception e, Drawable errorDrawable) {

                        }

                        @Override
                        public void onPrepareLoad(Drawable placeHolderDrawable) {

                        }
                    });
                    ((ItemMessageUserHolder) holder).avata.setImageBitmap(BitmapUser);
                } else {
                    ((ItemMessageUserHolder) holder).avata.setImageBitmap(BitmapUser);


                    new Handler().post(new Runnable() {
                        @Override
                        public void run() {
                            notifyDataSetChanged();
                        }
                    });


                }
            }
        }
    }

    @Override
    public int getItemViewType(int position) {
        int viewtype = -1;
        if (List_of_chats.get(position).getUsername().equals(obj.getsUsername())) {
            return 2;
        }
        else
            {
            return 1;
        }
    }

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

class ItemMessageUserHolder extends RecyclerView.ViewHolder {


    public TextView txtContent;
    public CircleImageView avata;

    public ItemMessageUserHolder(View itemView) {
        super(itemView);
        txtContent = (TextView) itemView.findViewById(R.id.textContentUser);
        avata = (CircleImageView) itemView.findViewById(R.id.imageView2);
    }
}

class ItemMessageFriendHolder extends RecyclerView.ViewHolder {
    public TextView txtContent;
    public CircleImageView avata;

    public ItemMessageFriendHolder(View itemView) {
        super(itemView);
        txtContent = (TextView) itemView.findViewById(R.id.textContentFriend);
        avata = (CircleImageView) itemView.findViewById(R.id.imageView3);
    }
}
Pratik Butani
  • 60,504
  • 58
  • 273
  • 437
ImNeos
  • 527
  • 6
  • 17

2 Answers2

1

It is because you're calling notifyDataSetChanged inside your onBindViewHolder like this:

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

    ...

    ((ItemMessageFriendHolder) holder).avata.setImageBitmap(BitmapFriend);


    new Handler().post(new Runnable() {
        @Override
        public void run() {
            notifyDataSetChanged();
        }
    });

   ...

}

You shouldn't force the Adapter to refresh itself before it isn't finished binding the view yet.

So, you need to remove the following code from your onBindViewHolder:

new Handler().post(new Runnable() {
    @Override
    public void run() {
        notifyDataSetChanged();
    }
});

then just call notifyDataSetChanged() when you need it. For example, when your dataset is changed.

ישו אוהב אותך
  • 28,609
  • 11
  • 78
  • 96
  • Hello, I already use : adapter.notifyDataSetChanged(); if the displaymessage() methods => the view are updated but the problem is that the images inside the view are not updated so all I see is text. If a move my finger on the screen, all the images are instantly put in the view... – ImNeos Feb 28 '19 at 12:10
  • The image is loading because you always check if previous bitmap is null. Just move the `((ItemMessageFriendHolder) holder).avata.setImageBitmap(BitmapFriend);` into your `public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from)` – ישו אוהב אותך Mar 01 '19 at 04:06
  • But if I do that, at every new message the image will be downloaded but what I want is the image to be downloaded one time and then stored into the BitmapFriend so I can use it instead of downloading the image again – ImNeos Mar 01 '19 at 15:50
  • But you're right it was because I checked everytime if bitmap is null Instead of that I used boolean and it works Thanks – ImNeos Mar 01 '19 at 15:57
0

Try this.

dialeecyclerView.post(new Runnable()
                {
                    @Override
                    public void run() {
                    myadapter.notifyDataSetChanged();
                }
                });

Source - Cannot call this method while RecyclerView is computing a layout or scrolling when try remove item from recyclerview