10

I have a chat screen where i can chat with other user, i am sending chat data (message,time and sender via List) to RecyclerAdapter which populate chat views with data.Now i have one more List which has data with different layout. Like this

Demo Screen

Here is my method from where i am calling second arraylist into RecyclerAdapter

public void TransferResultTo_Activity(List<Image_data_Wrapper> list) {
        Log.d(TAG,"Here is data Result From AsyncTask "+list.size());
        getResult=list;
        Image_data_Wrapper Image=getResult.get(0);
        Log.d(TAG,"Result from Image data "+Image.getPage_Title());
        adapter=new Chat_Adapter(this,message,getResult);
        adapter.notifyDataSetChanged();
    }

As in above method when i try to call adapter by recyclerView.setAdapter(adapter); it remove all messages from screen and shows my image data. but i want to keep all messages and show image with data via new list

Here is my Chat Adapter

public class Chat_Adapter extends RecyclerView.Adapter<Chat_Adapter.ViewHolder> {

    private String UserID;
    private Context context;
    //TAG FOR TRACKING SELF MESSAGE
    private int Self_Msg=0;
    //ARRAYLIST OF MESSAGES OBJECT CONTAINING ALL THE MESSAGES IN THE THREAD
    private List<Chat_Wrapper> arrayList_message;
    public static final String TAG="###CHAT_ADAPTER###";
    private List<Image_data_Wrapper> data_Result;
    boolean valtype;


    public Chat_Adapter(Context context, List<Chat_Wrapper> message) {
        //UserID = userID;
        this.context = context;
        this.arrayList_message = message;
        Log.d(TAG,"Chat Adapter Calling");
    }
    public Chat_Adapter(Context context, List<Image_data_Wrapper> result,boolean val) {
        this.context = context;
        this.data_Result = result;
        this.valtype=val;
        Log.d(TAG,"Image data Chat Adapter Calling");
    }

    public Chat_Adapter() {
    }


    @Override
    public Chat_Adapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView;
        Log.d(TAG,"On Create View Holder Calling ");
        if (viewType==Self_Msg){
            itemView= LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_screen_message_item,parent,false);
            Log.d(TAG,"On Create View Holder Calling View Type is "+itemView);
        }

        else if (valtype==true){
            itemView= LayoutInflater.from(parent.getContext()).inflate(R.layout.Image_data_layout,parent,false);
            Log.d(TAG,"ON CREATE VIEW HOLDER RUNNING AND data RESULT VIEW TYPE IS RUNNING "+viewType);
        }

        else {
            itemView= LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_screen_message_item,parent,false);
            Log.d(TAG,"On Create View Holder Calling View Type is "+itemView);
        }
        return new ViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(Chat_Adapter.ViewHolder holder, int position) {
        if (valtype==true){
            Image_data_Wrapper wrapper=data_Result.get(position);
            holder.data_title.setText(wrapper.getPage_Title());
            holder.data_link.setText(wrapper.getPage_Link());
            holder.data_snippet.setText(wrapper.getPage_Desc());
            Picasso.with(context).load(wrapper.getPage_ImageThumb()).into(holder.Image_Image);
            valtype=false;

        }
        else {

            Log.d(TAG,"On Bind VIew Holder Context "+context);
            Chat_Wrapper wrapper=arrayList_message.get(position);
            Log.d(TAG,"On Bind VIew Holder Chat Wrapper "+wrapper);
            holder.Message.setText(wrapper.getMessage());
            holder.TimeStamp.setText(wrapper.getTimestamp());

        }



    }

    @Override
    public int getItemCount() {
        Log.d(TAG,"Get Item Count Running");
        int items;
        if (valtype==true){
            Log.d(TAG,"data Result Array is not empty");
            items=data_Result.size();
            Log.d(TAG,"Total Number Of Item "+items);
            return items;
        }
        else {
            Log.d(TAG,"ARRAY SIZE AT GETITEM COUNT "+arrayList_message.size());
            return arrayList_message.size();
        }





    }


    @Override
    public int getItemViewType(int position) {
        Log.d(TAG,"Get Item View Type Running");
        return position;
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        TextView Message,TimeStamp,data_title,data_link,data_snippet;
        ImageView User_image,Image_Image;
        ImageButton data_SendButton;


         public ViewHolder(View itemView) {
             super(itemView);
             Message= (TextView) itemView.findViewById(R.id.Single_Item_Chat_Message);
             TimeStamp= (TextView) itemView.findViewById(R.id.Single_Item_Chat_TimeStamp);
             User_image= (ImageView) itemView.findViewById(R.id.Single_Item_Chat_ImageView);

             if (valtype==true){
                 Log.d(TAG,"Setting View For Image data ");
                 data_title= (TextView) itemView.findViewById(R.id.Image_data_Title);
                 data_link= (TextView) itemView.findViewById(R.id.Image_data_Link);
                 data_snippet= (TextView) itemView.findViewById(R.id.Image_data_Snippet);

                 Image_Image= (ImageView) itemView.findViewById(R.id.Image_data_Image);
                 data_SendButton= (ImageButton) itemView.findViewById(R.id.Image_data_SendButton);
             }




        }
    }

    public void updateDataWithdataResult(List<Image_data_Wrapper> list,boolean setValue){
        Log.d(TAG,"Update Data Method Calling");
        this.data_Result=list;
        this.valtype=setValue;
    }
}

The issues i am facing with this code

  • With notifiydatasetchanged only Adapter constructor is calling(Correct one) but never getItemCount()
  • Is sending boolean value with data is right way to check, If calls from ImageArrayList only then enter in its related data or views?
  • What is the best way to get both List into single RecyclerView with their own layout

UPDATE : When i call adapter by recyclerView.setAdapter(adapter) it worked as i want but adapter.notifyDataSetChanged make call stuck at adapter constructor (Thanks to DanielLaneDC). So can i leave it like that or adapter.notifyDataSetChanged should work?

androidXP
  • 1,692
  • 3
  • 27
  • 58

4 Answers4

35

It sounds like you want to show two different lists of items in the same RecyclerView using the same RecyclerView.Adapter. Thankfully, this is the kind of thing RecyclerView.Adapter can handle really nicely.

Here's how your RecyclerView.Adapter should look:

public class ChatAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    final int VIEW_TYPE_MESSAGE = 0;
    final int VIEW_TYPE_IMAGE = 1;

    Context context;
    List<ChatWrapper> messages;
    List<ImageDataWrapper> images;

    public ChatAdapter(Context context, List<ChatWrapper> messages, List<ImageDataWrapper> images){
        this.context = context;
        this.messages = messages;
        this.images = images;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
        if(viewType == VIEW_TYPE_MESSAGE){
            return new MessageViewHolder(itemView);
        }

        if(viewType == VIEW_TYPE_IMAGE){
            return new ImageViewHolder(itemView);
        }

        return null;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position){
        if(viewHolder instanceof MessageViewHolder){
            ((MessageViewHolder) viewHolder).populate(messages.get(position));
        }

        if(viewHolder instanceof ImageViewHolder){
            ((ImageViewHolder) viewHolder).populate(images.get(position - messages.size()));
        }
    }

    @Override
    public int getItemCount(){
        return messages.size() + images.size();
    }

    @Override
    public int getItemViewType(int position){
        if(position < messages.size()){
            return VIEW_TYPE_MESSAGE;
        }

        if(position - messages.size() < images.size()){
            return VIEW_TYPE_IMAGE;
        }

        return -1;
    }

    public class MessageViewHolder extends RecyclerView.ViewHolder {
        TextView message;
        TextView timeStamp;
        ImageView userImage;

        public MessageViewHolder(View itemView){
            super(itemView);

            message = (TextView) itemView.findViewById(R.id.Single_Item_Chat_Message);
            timeStamp = (TextView) itemView.findViewById(R.id.Single_Item_Chat_TimeStamp);
            userImage = (ImageView) itemView.findViewById(R.id.Single_Item_Chat_ImageView);
        }

        public void populate(ChatWrapper chatWrapper){
            message.setText(chatWrapper.getMessage());
            userImage.setText(chatWrapper.getTimestamp());
        }
    }

    public class ImageViewHolder extends RecyclerView.ViewHolder {
        TextView dataTitle;
        TextView dataLink;
        TextView dataSnippet;
        ImageView image;
        ImageButton dataSendButton;

        public ImageViewHolder(View itemView){
            super(itemView);

            dataTitle = (TextView) itemView.findViewById(R.id.Image_data_Title);
            dataLink = (TextView) itemView.findViewById(R.id.Image_data_Link);
            dataSnippet = (TextView) itemView.findViewById(R.id.Image_data_Snippet);
            image = (ImageView) itemView.findViewById(R.id.Image_data_Image);
            dataSendButton = (ImageButton) itemView.findViewById(R.id.Image_data_SendButton);
        }

        public void populate(ImageDataWrapper imageDataWrapper){
            dataTitle.setText(imageDataWrapper.getPage_Title());
            dataLink.setText(imageDataWrapper.getPage_Link());
            dataSnippet.setText(imageDataWrapper.getPage_Desc());
            Picasso.with(context).load(imageDataWrapper.getPage_ImageThumb()).into(image);
        }
    }
}

Here's what your Activity is going to need:

List<ChatWrapper> messages;
List<ImageDataWrapper> images;
ChatAdapter adapter;

@Override
public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);

    messages = new ArrayList<>();
    images = new ArrayList<>();
    ChatAdapter adapter = new ChatAdapter(this, messages, images);

    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setAdapter(adapter);
}

public void addMessage(ChatWrapper message){
    messages.add(message);
    adapter.notifyDataSetChanged();
}

public void removeMessage(ChatWrapper message){
    if(messages.remove(message)){
        adapter.notifyDataSetChanged();
    }
}

public void addImage(ImageDataWrapper image){
    images.add(image);
    adapter.notifyDataSetChanged();
}

public void removeImage(ImageDataWrapper image){
    if(images.remove(image)){
        adapter.notifyDataSetChanged();
    }
}

When you want to update the data, just call addMessage(ChatWrapper) or removeMessage(ChatWrapper) for messages and addImage(ImageDataWrapper) or removeImage(ImageDataWrapper) for images.

Some key points:

  • You don't want to create any new Adapters or Lists, you simply want to call methods on our existing objects.
  • Your Adapter should behave as if it always has to deal with messages and images, but just that it can handle 0 messages or 0 images (no valType variable, getItemCount() should always return the size of both lists combined, etc.)
DanielLaneDC
  • 1,731
  • 11
  • 9
  • Still stuck at adapter construtor nothing is calling afterwards and Log says Size of each arraylist `Chat Adapter Calling 2 1` . I did as you mentioned but still not working – androidXP Feb 25 '17 at 02:16
  • I edited my answer to include how you should create the adapter. – DanielLaneDC Feb 25 '17 at 03:38
  • As u can see in my above code i made `//adapter=new Chat_Adapter(this,list,true);` without creating any new instance of adapter or list. So i tried this but still no luck btw thx for ur efforts. – androidXP Feb 25 '17 at 10:06
  • Calling `new Chat_Adapter` is going to be creating a new adapter. In your `TransferResultTo_Activity` method you are creating a new adapter. You need to just modify the list without recreating the adapter, and then notify the adapter by keeping a reference to the same adapter you passed to the RecyclerView. – DanielLaneDC Feb 25 '17 at 10:10
  • No actually i was just testing that time but as u said i am using commented code adapter in my code above – androidXP Feb 25 '17 at 10:12
  • In your `TransferResultTo_Activity` method in your opening post, you create a new adapter here: `Chat_Adapter chatAdapter=new Chat_Adapter();` – DanielLaneDC Feb 25 '17 at 10:14
  • I don't understand why i stuck at constructor whenever i calling with image data and even as i shown above i am getting its sizs 2 and 1 – androidXP Feb 25 '17 at 10:14
  • If you add the rest of your relevant code to the question, I can edit my answer to fix up the other bits that are broken. – DanielLaneDC Feb 25 '17 at 10:17
  • Please Check Update Question – androidXP Feb 25 '17 at 10:41
  • I have edited my answer to include the code you need to use to add/remove messages and images. Make sure that you're never creating more adapters or lists than the ones we make in `Activity.onCreate(Bundle)`. – DanielLaneDC Feb 25 '17 at 12:04
  • As i edit my code above i am not creating any new instance of adapter or list cuz its class variables.I will update to you soon after testing your edited code.Thx – androidXP Feb 25 '17 at 12:17
  • Same Issue is coming code stuck at Constructor. But not when i call `recyclerview.setadapter` method.But you deserve a upvote for sure – androidXP Feb 25 '17 at 13:46
  • When you say "coming code stuck at Constructor" could you please elaborate on what you mean? – DanielLaneDC Feb 25 '17 at 13:48
  • Code stuck at constructor means whenever i call `notifydatasetchanged` i can see in Log that constructor is running with both array size values but get item count never calls after that or any other method. – androidXP Feb 25 '17 at 20:20
  • What do you mean "constructor is running"? The Adapter's constructor shouldn't be called again so it's odd that it sounds like it is. – DanielLaneDC Feb 26 '17 at 02:25
  • whenever i call `notifydatasetchanged` it shows in Log that (I made Log in constructor See above code) Constructor get call and after that no log is generated. – androidXP Feb 26 '17 at 11:35
  • Thank for solution. this is perfect solution for OP's problem. should be accepted as answer – Saurabh Bhandari Jan 17 '18 at 10:48
  • This approach worked great for me in similar situation – Victoria Klimova Dec 11 '18 at 18:54
  • How can I show the DateTimeView in android – sejn Apr 08 '21 at 11:26
1

First you need to combine both array in single arraylist like.. firsArrlist.addAll(secondArrList);

and pass into recyclerview adapter and migrate with some flag and take another viewholder for particular flag to set that view in recyclerview.

And total count is must both firstArrlist.size + secondArrList.size

Chirag.T
  • 746
  • 1
  • 6
  • 18
  • Can you please elaborate ur answer bit more it would be very helpful for me – androidXP Feb 25 '17 at 10:09
  • I am trying to do this here https://stackoverflow.com/questions/55259345/combining-two-arrays-of-model-class-type-firebase-android but it is not working – DragonFire Mar 20 '19 at 23:51
0
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
class ViewHolder0 extends RecyclerView.ViewHolder {
    ...
    public ViewHolder0(View itemView){
    ...
    }
}

class ViewHolder2 extends RecyclerView.ViewHolder {
    ...
    public ViewHolder2(View itemView){
    ...
}

@Override
public int getItemViewType(int position) {
    // Just as an example, return 0 or 2 depending on position
    // Note that unlike in ListView adapters, types don't have to be contiguous
    return position % 2 * 2;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
     switch (viewType) {
         case 0: return new ViewHolder0(...);
         case 2: return new ViewHolder2(...);
         ...
     }
}

@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
    switch (holder.getItemViewType()) {
        case 0:
            ViewHolder0 viewHolder0 = (ViewHolder0)holder;
            ...
            break;

        case 2:
            ViewHolder2 viewHolder2 = (ViewHolder2)holder;
            ...
            break;
    }
}

}

Chirag.T
  • 746
  • 1
  • 6
  • 18
0

I had your same problem. You can create a Pojo class if you need and enter the association for example a chat can have multiple images. Then build the chat object and insert a list of images in it. Before passing the list to the adapter, populate it with the original data. I know this may not be a nice solution for your needs, but remember that you can have full control, without segmenting the lists.

AlexPad
  • 10,364
  • 3
  • 38
  • 48