I have a design for messages.
2 xml files are responsible for the design:
recyclerview_item_incoming.xml
recyclerview_item_outgoing.xml
It looks like this:
Messages are stored inside the RecyclerView.
RecyclerView is connected to SQLite via LiveData -> Room.
More Info
To work with the RecyclerView I have 2 classes:
MessageListAdapter:
package com.mardaunt.telesupp.recyclerview;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import com.mardaunt.telesupp.room.Message;
public class MessageListAdapter extends ListAdapter<Message, MessageViewHolder> {
public static int idMessage = 0; // I added that
public static int countMessages = 0; // I added that
public MessageListAdapter(#NonNull DiffUtil.ItemCallback<Message> diffCallback) {
super(diffCallback);
}
#Override
public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
countMessages = getItemCount();
//System.out.println(countMessages + " - This countMessages\n" + idMessage + "- This idMessage; " + viewType + " - viewType");
System.out.println(idMessage);
//if(idMessage >= countMessages) idMessage = 0;
// I need to pass the create index method of the current message.!!!!!!!!!!
// The getItem() function returns a Message object.
return MessageViewHolder.create(parent, getItem(idMessage));
}
#Override
public void onBindViewHolder(MessageViewHolder holder, int position) {
Message current = getItem(position);
//System.out.println(current.getId() + " " + current.getPhone() + " " + current.getText());
holder.bind(current.getPhone() ,current.getText()); // Бинтим только тело телефон и сообщение!
idMessage = position+1;
if(idMessage >= countMessages) idMessage = 0;
}
public static class MessageDiff extends DiffUtil.ItemCallback<Message> {
#Override
public boolean areItemsTheSame(#NonNull Message oldItem, #NonNull Message newItem) {
return oldItem == newItem;
}
#Override
public boolean areContentsTheSame(#NonNull Message oldItem, #NonNull Message newItem) {
return oldItem.getText().equals(newItem.getText());
}
}
}
MessageViewHolder:
package com.mardaunt.telesupp.recyclerview;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.mardaunt.telesupp.R;
import com.mardaunt.telesupp.room.Message;
class MessageViewHolder extends RecyclerView.ViewHolder {
private final TextView phoneItemView;
private final TextView messageItemView;
private MessageViewHolder(View itemView) {
super(itemView);
messageItemView = itemView.findViewById(R.id.text_view_message);
phoneItemView = itemView.findViewById(R.id.text_view_phone);
}
public void bind(String phone, String message) {
phoneItemView.setText(phone);
messageItemView.setText(message);
}
//The method decides which design to choose for the message bubble.
static MessageViewHolder create(ViewGroup parent, Message current) {
View view;
if (current.getNature().equals("outgoing"))
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recyclerview_item_outgoing, parent, false);
else view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recyclerview_item_incoming, parent, false);
return new MessageViewHolder(view);
}
}
And Message:
package com.mardaunt.telesupp.room;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
#Entity(tableName = "messages_table")
public class Message {
#PrimaryKey(autoGenerate = true)
private int id;
private String phone;
private String text;
private String nature;
public Message(int id,
#NonNull String phone,
#NonNull String text,
#NonNull String nature
) {
this.id = id;
this.phone = phone;
this.text = text;
this.nature = nature; // incoming OR outgoing
}
public int getId(){return this.id;}
public String getPhone(){return this.phone;}
public String getText(){return this.text;}
public String getNature(){return this.nature;}
}
Problem:
I want the incoming messages to be located on the left. And outgoing messages are located on the right.
To do this, I slightly changed the static method of the MessageViewHolder class:
//The method decides which design to choose for the message bubble.
static MessageViewHolder create(ViewGroup parent, Message current) {
View view;
if (current.getNature().equals("outgoing"))
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recyclerview_item_outgoing, parent, false);
else view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recyclerview_item_incoming, parent, false);
return new MessageViewHolder(view);
}
But the problem is that I do not know how I can correctly pass the Message objects to this method?
As you can see, I tried to pass the Message object in the MessageListAdapter class:
#Override
public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
countMessages = getItemCount();
// I need to pass the create index method of the current message.!!!!!!!!!!
// The getItem() function returns a Message object.
return MessageViewHolder.create(parent, getItem(idMessage));
}
#Override
public void onBindViewHolder(MessageViewHolder holder, int position) {
Message current = getItem(position);
holder.bind(current.getPhone() ,current.getText()); // Бинтим только тело телефон и сообщение!
idMessage = position+1;
if(idMessage >= countMessages) idMessage = 0;
}
I added 2 static variable (idMessage, countMessages), but this worked don't correctly.
How I can add Message object for method MessageViewHolder.create(...) ?
Project on GitHub: https://github.com/MinorityMeaning/HelloApp
The getItemViewType() is the right place for determining the item layout. So you need to move the logic there, so override it in the adapter:
#Override
public int getItemViewType(int position) {
if (getItem(position).getNature().equals("outgoing"))
return R.layout.recyclerview_item_outgoing;
else
return R.layout.recyclerview_item_incoming;
}
And that being reflected in the viewType parameter of onCreateViewHolder(), so it holds the right layout for the current item. So you'd pass that to the ViewHolder:
#Override
public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return MessageViewHolder.create(parent, viewType);
}
And In ViewHolder set that as your layout:
static MessageViewHolder create(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(viewType, parent, false);
return new MessageViewHolder(view);
}
Related
I am Creating a ChatApp and in which i am getting message of Different type
Text
Image
Audio
Video
I am checking that whether the message is of me or the other user in getItemViewType method of my recyler Adapter and than checking the message type in OnCreateViewholder and passing the particular Binding at runtime so that My App works smoothly but the problem is that The app is lagging when the I click on previous activity item to open my chat activity it takes its sweet time of 1 - 2 seconds and when i am in my Chat Activity the Screen Lags when i scroll the total amount of views which are used in recyler view are 10 5 for sending side and 5 for receiving side
Following is my MessageAdapter Which also contains Viewholder
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import com.example.chat.R;
import com.example.chat.callBacks.SentMessageCallback;
import com.example.chat.databinding.ItemMessageReceiveAudioBinding;
import com.example.chat.databinding.ItemMessageReceiveBinding;
import com.example.chat.databinding.ItemMessageReceiveFileBinding;
import com.example.chat.databinding.ItemMessageReceiveImageBinding;
import com.example.chat.databinding.ItemMessageReceiveVideoBinding;
import com.example.chat.databinding.ItemMessageSentAudioBinding;
import com.example.chat.databinding.ItemMessageSentBinding;
import com.example.chat.databinding.ItemMessageSentFileBinding;
import com.example.chat.databinding.ItemMessageSentImageBinding;
import com.example.chat.databinding.ItemMessageSentVideoBinding;
import com.example.chat.models.Message;
import java.util.ArrayList;
import java.util.List;
//************************************************************
public class MessageAdapter
extends RecyclerView.Adapter
//************************************************************
{
private static final int TYPE_MESSAGE_SENT = 0;
private static final int TYPE_MESSAGE_RECEIVED = 1;
private static final int TYPE_IMAGE_SENT = 2;
private static final int TYPE_IMAGE_RECEIVED = 3;
private static final int MSG_UPDATE_SEEK_BAR = 1845;
Context mContext;
private LayoutInflater inflater;
private List<Message> messageList = new ArrayList<>();
private SentMessageCallback callback;
boolean isNewMessage = false;
Message mMessage;
//************************************************************
public MessageAdapter(Context mContext, LayoutInflater inflater,
SentMessageCallback callback)
//************************************************************
{
this.inflater = inflater;
this.callback = callback;
this.mContext = mContext;
}
//************************************************************
#Override
public int getItemViewType(int position)
//************************************************************
{
mMessage = messageList.get(position);
if (mMessage.isMe()) {
return TYPE_MESSAGE_SENT;
} else {
return TYPE_MESSAGE_RECEIVED;
}
}
//************************************************************
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent,
int viewType)
//************************************************************
{
View view;
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
switch (viewType) {
case TYPE_MESSAGE_SENT:
// view = inflater.inflate(R.layout.item_message_sent, parent, false);
switch (mMessage.getType()) {
case "text":
return new SentMessageViewHolder(ItemMessageSentBinding.inflate(layoutInflater, parent, false), mMessage);
case "audio":
return new SentMessageViewHolder(ItemMessageSentAudioBinding.inflate(layoutInflater, parent, false), mMessage);
case "image":
return new SentMessageViewHolder(ItemMessageSentImageBinding.inflate(layoutInflater, parent, false), mMessage);
case "video":
return new SentMessageViewHolder(ItemMessageSentVideoBinding.inflate(layoutInflater, parent, false), mMessage);
case "file":
return new SentMessageViewHolder(ItemMessageSentFileBinding.inflate(layoutInflater, parent, false), mMessage);
}
break;
case TYPE_MESSAGE_RECEIVED:
// view = inflater.inflate(R.layout.item_message_receive, parent, false);
switch (mMessage.getType()) {
case "text":
return new ReceivedMessageHolder(ItemMessageReceiveBinding.inflate(layoutInflater, parent, false), mMessage);
case "audio":
return new ReceivedMessageHolder(ItemMessageReceiveAudioBinding.inflate(layoutInflater, parent, false), mMessage);
case "image":
return new ReceivedMessageHolder(ItemMessageReceiveImageBinding.inflate(layoutInflater, parent, false), mMessage);
case "video":
return new ReceivedMessageHolder(ItemMessageReceiveVideoBinding.inflate(layoutInflater, parent, false), mMessage);
case "file":
return new ReceivedMessageHolder(ItemMessageReceiveFileBinding.inflate(layoutInflater, parent, false), mMessage);
}
break;
}
return null;
}
//************************************************************
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder,
int position)
//************************************************************
{
Message message = messageList.get(position);
holder.setIsRecyclable(false);
if (message.isMe()) {
SentMessageViewHolder messageViewHolder = (SentMessageViewHolder) holder;
messageViewHolder.setData(mContext, message, messageViewHolder, position, callback);
} else {
ReceivedMessageHolder messageHolder = (ReceivedMessageHolder) holder;
messageHolder.setData(mContext, message, messageHolder, position, callback);
}
}
//************************************************************
#Override
public int getItemCount()
//************************************************************
{
return messageList.size();
}
//************************************************************
public void addAllMessages(List<Message> messageList)
//************************************************************
{
this.messageList.addAll(messageList);
notifyDataSetChanged();
}
//************************************************************
public void addNewMessage(Message message, boolean isNewMessage)
//************************************************************
{
messageList.add((message));
this.isNewMessage = isNewMessage;
notifyDataSetChanged();
}
//************************************************************
public void setMargins(View v, int l, int t, int r, int b)
//************************************************************
{
if (v.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
p.setMargins(l, t, r, b);
v.requestLayout();
}
}
//************************************************************
//************************************************************
public static class SentMessageViewHolder
extends RecyclerView.ViewHolder
//************************************************************
//************************************************************
{
TextView messageTxt, textTime;
ImageView text_sendTick;
RelativeLayout textMessageLayout;
ConstraintLayout messageOutGoingLayout;
public View parent;
//************************************************************
public SentMessageViewHolder(#NonNull ItemMessageSentBinding mSentText, Message message)
//************************************************************
{
super(mSentText.getRoot());
parent = itemView;
}
//************************************************************
public SentMessageViewHolder(#NonNull ItemMessageSentAudioBinding mSentAudio, Message message)
//************************************************************
{
super(mSentAudio.getRoot());
parent = itemView;
}
//************************************************************
public SentMessageViewHolder(#NonNull ItemMessageSentImageBinding mSentImage, Message message)
//************************************************************
{
super(mSentImage.getRoot());
parent = itemView;
}
//************************************************************
public SentMessageViewHolder(#NonNull ItemMessageSentVideoBinding mSentVideo, Message message)
//************************************************************
{
super(mSentVideo.getRoot());
parent = itemView;
}
//************************************************************
public SentMessageViewHolder(#NonNull ItemMessageSentFileBinding mSentFile, Message message)
//************************************************************
{
super(mSentFile.getRoot());
parent = itemView;
}
//************************************************************
public void setData(Context context, Message item,
SentMessageViewHolder viewHolder, int itemPosition,
SentMessageCallback callback)
//************************************************************
{
// messageTxt.setText(item.getMessage());
parent.setOnClickListener(v ->
{
callback.onSentMessageClick(item, viewHolder, itemPosition);
});
parent.setOnLongClickListener(v ->
{
callback.onSentMessageLongClick(item, viewHolder, itemPosition);
return false;
});
}
}
//************************************************************
//************************************************************
public static class ReceivedMessageHolder
extends RecyclerView.ViewHolder
//************************************************************
//************************************************************
{
TextView messageTxt, textTime;
RelativeLayout textMessageLayout;
ConstraintLayout incomingMessageLayout;
public View parent;
//************************************************************
public ReceivedMessageHolder(#NonNull ItemMessageReceiveBinding mReceiveText, Message message)
//************************************************************
{
super(mReceiveText.getRoot());
parent = itemView;
}
//************************************************************
public ReceivedMessageHolder(#NonNull ItemMessageReceiveAudioBinding mReceiveAudio, Message message)
//************************************************************
{
super(mReceiveAudio.getRoot());
parent = itemView;
}
//************************************************************
public ReceivedMessageHolder(#NonNull ItemMessageReceiveImageBinding mReceiveImage, Message message)
//************************************************************
{
super(mReceiveImage.getRoot());
parent = itemView;
}
//************************************************************
public ReceivedMessageHolder(#NonNull ItemMessageReceiveVideoBinding mReceiveVideo, Message message)
//************************************************************
{
super(mReceiveVideo.getRoot());
parent = itemView;
}
//************************************************************
public ReceivedMessageHolder(#NonNull ItemMessageReceiveFileBinding mReceiveFile, Message message)
//************************************************************
{
super(mReceiveFile.getRoot());
parent = itemView;
}
//************************************************************
ReceivedMessageHolder(View itemView)
//************************************************************
{
super(itemView);
parent = itemView;
messageTxt = itemView.findViewById(R.id.receivedTxt);
textTime = itemView.findViewById(R.id.textview_time);
textMessageLayout = itemView.findViewById(R.id.textMessageLayout);
incomingMessageLayout = itemView.findViewById(R.id.incoming_layout_bubble);
}
//************************************************************
public void setData(Context context, Message item, ReceivedMessageHolder viewHolder,
int itemPosition, SentMessageCallback callback)
//************************************************************
{
// messageTxt.setText(item.getMessage());
parent.setOnClickListener(v -> {
callback.onReceivedMessageClick(item, viewHolder, itemPosition);
});
parent.setOnLongClickListener(v -> {
callback.onReceivedMessageLongClick(item, viewHolder, itemPosition);
return false;
});
}
}
}
Here is the code I used in my RecycleView adapter class. I don't know this is the right way or not to use View Binding. If you have a better solution answer me. Thank you.
#Override
public CategoryAdapter.MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.common_circle_image, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull CategoryAdapter.MyViewHolder holder, final int position) {
holder.binding.img.setBackgroundResource(addAdapterData.get(position).getItemUrl());
holder.binding.txt.setText(addAdapterData.get(position).getItemName());
}
#Override
public int getItemCount() {
return addAdapterData.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
CommonCircleImageBinding binding;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
binding = CommonCircleImageBinding.bind(itemView);
binding.cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
commonItemClick.onItemClick(getAdapterPosition(),"");
}
});
}
}
Also, I want to know is it right to use R.layout.layout_name and ViewBinding in the same class.
What you need to do is pass the generated binding class object to the holder class constructor. In your example, You have common_circle_image XML file for RecyclerView item and the generated class is CommonCircleImageBinding so like this you use the onCreateViewHolder to pass the generated binding class to the ViewHolder class
#NonNull
#Override
public CategoryAdapter.MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
CommonCircleImageBinding itemBinding = CommonCircleImageBinding .inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new MyViewHolder(itemBinding);
}
and use the holder class like this so you can use these fields in onBindViewHolder
static class MyViewHolder extends RecyclerView.ViewHolder {
private TextView txt;
private ImageView img;
MyViewHolder(CommonCircleImageBinding itemBinding) {
super(itemBinding.getRoot());
img = itemBinding.img ;
txt = itemBinding.txt ;
}
}
Here is full view binding recycler view code in java, you can do as like:
package com.jbws.myviewbindingdemo.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.jbws.myviewbindingdemo.databinding.RowXmlViewBinding;
import com.jbws.myviewbindingdemo.pojo.ModelObject;
import java.util.ArrayList;
public class RecyclerViewListAdapter extends RecyclerView.Adapter<RecyclerViewListAdapter.ViewHolder> {
public ArrayList<ModelObject> modelObjectArrayList;
public RecyclerViewListAdapter(ArrayList<ModelObject> modelObjectArrayList) {
this.modelObjectArrayList = modelObjectArrayList;
}
#NonNull
#Override
public RecyclerViewListAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new ViewHolder(RowXmlViewBinding.inflate(LayoutInflater.from(parent.getContext()),
parent, false));
}
#Override
public void onBindViewHolder(#NonNull RecyclerViewListAdapter.ViewHolder holder, final int position) {
ModelObject modelObject = modelObjectArrayList.get(position);
holder.rowXmlViewBinding.txtObjectName.setText(modelObject.getFullName());
holder.rowXmlViewBinding.btnUpdateName.setOnClickListener(view -> {
Log.i("LOG_TAG", "Full Name: " + modelObject.getFullName);
});
}
#Override
public int getItemCount() {
return modelObjectArrayList == null ? 0 :
modelObjectArrayList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private RowXmlViewBinding rowXmlViewBinding;
public ViewHolder(RowXmlViewBinding rowXmlViewBinding) {
super(rowXmlViewBinding.getRoot());
this.rowXmlViewBinding = rowXmlViewBinding;
}
}
}
For the folks looking for a solution in Kotlin, here it is:
It's a minimal example, where the adapter gets an array of Strings and displays each of the them in a layout called recyclerview_item in a TextView called itemTextView.
It's based on #SomeshKumar's answer and answers #Vijay Villiers question on how to get rid of the private TextView txt;
Edit: New Version:
I noticed the generated ...Binding has a .bind() function, so let's use it. (I guess it might be less resource-heavy?)
class SampleAdapter(private val context: Context, private val content: Array<String>) :
RecyclerView.Adapter<SampleAdapter.CustomViewHolder>()
{
class CustomViewHolder(view: View) : RecyclerView.ViewHolder(view)
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int) =
CustomViewHolder(
// Alternatively inflate like usual, if you don't need binding
RecyclerviewItemBinding
.inflate(LayoutInflater.from(context), viewGroup, false)
.root
)
override fun getItemCount() = content.size
override fun onBindViewHolder(viewHolder: CustomViewHolder, position: Int)
{
RecyclerviewItemBinding.bind(viewHolder.itemView).apply{
itemTextView.text = content[position]
}
}
}
Edit: Old Version:
class SampleAdapter(private val context: Context, private val content: Array<String>) :
RecyclerView.Adapter<SampleAdapter.CustomViewHolder>()
{
class CustomViewHolder(var viewBinding: RecyclerviewItemBinding) :
RecyclerView.ViewHolder(viewBinding.root)
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int) =
CustomViewHolder(
RecyclerviewItemBinding
.inflate(LayoutInflater.from(context), viewGroup, false)
)
override fun getItemCount() = content.size
override fun onBindViewHolder(viewHolder: CustomViewHolder, position: Int)
{
viewHolder.viewBinding.apply {
itemTextView.text = content[position]
}
}
}
Here is full recycler view adapter class in java :
public class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.MyViewHolder> {
private List<Note> notes;
private ItemNotesBinding notesBinding;
public NotesAdapter(List<Note> notes) {
this.notes = notes;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
notesBinding = ItemNotesBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new MyViewHolder(notesBinding);
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
Note note = notes.get(position);
notesBinding.tvTitle.setText(note.getNote());
}
#Override
public int getItemCount() {
return notes.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
ItemNotesBinding notesBinding;
public MyViewHolder(#NonNull ItemNotesBinding binding) {
super(binding.getRoot());
notesBinding = binding;
}
}
}
You can create CommonCircleImageBinding directly in onCreateViewHolder by CommonCircleImageBinding.inflate(LayoutInflater.from(parent.getContext()))
Then pass it to MyViewHolder
I using the firebase API. to send and retrieve messages. However I am having trouble in trying to set layout for the sender/retriever so Messages will align left/right. At the moment I only have one layout which both the sender/retriever is using, but not sure on how to set different layouts.
public class MessageAdapter extends RecyclerView.Adapter<MessageViewHolder> {
private List<Message> messagesList;
private DatabaseReference databaseReference;
private DatabaseReference userDatabaseRef;
private FirebaseAuth firebaseAuth;
private Context context;
private String imageUrl;
public MessageAdapter(List<Message> messagesList) {
this.messagesList = messagesList;
}
#Override
public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Message c = messagesList.get(viewType);
String sender = c.getFrom();
databaseReference = FirebaseDatabase.getInstance().getReference().child("Users").child(sender);
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.messages_layout, parent, false);
return new MessageViewHolder(v);
}
#Override
public void onBindViewHolder(final MessageViewHolder viewHolder, final int index) {
final Message c = messagesList.get(index);
final String sender = c.getFrom();
databaseReference = FirebaseDatabase.getInstance().getReference().child("Users").child(sender);
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
imageUrl = dataSnapshot.child("image").getValue(String.class);
viewHolder.setUserimage(context,imageUrl);
String name = dataSnapshot.child("name").getValue().toString();
viewHolder.displayName.setText(name);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
viewHolder.messageText.setText(c.getMessage());
viewHolder.time.setText(EpochtimeToDateAndTimeString(c.getTime()));
}
#Override
public int getItemCount() {
return messagesList.size();
}
public String EpochtimeToDateAndTimeString(long time) {
Date date = new Date(time);
DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
format.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
String formatted = format.format(date);
return formatted;
}
}
MessageViewHolder
public class MessageViewHolder extends RecyclerView.ViewHolder {
public TextView messageText;
public TextView displayName;
public TextView time;
public ImageView showImage;
public MessageViewHolder(View view) {
super(view);
messageText = (TextView) view.findViewById(R.id.messagetext);
displayName = (TextView) view.findViewById(R.id.displayname);
time = (TextView) view.findViewById(R.id.timestamp);
showImage = (ImageView) view.findViewById(R.id.imageview_post_userimage3);
}
You should use different view holders for sender's and receiver's messages.
Create two layouts: R.layout.sender_message_layout and R.layout.receiver_message_layout
Create two viewholders: SenderMessageHolder and ReceiverMessageHolder.
Define view type integer constants:
public static final int VIEW_TYPE_SENDER = 1;
public static final int VIEW_TYPE_RECEIVER = 2;
Implement getItemViewType(int position) on your adapter:
if(messageList.get(position).getFrom().equals(...)) {
return VIEW_TYPE_SENDER;
} else {
return VIEW_TYPE_RECEIVER;
}
In onCreateViewHolder() function check view type and create different holders for different senders:
View v;
if(viewType == VIEW_TYPE_SENDER) {
v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.sender_message_layout, parent, false);
return new SenderMessageHolder(v);
} else {
v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.receiver_message_layout, parent, false);
return new ReceiverMessageHolder(v);
}
In onBindViewHolder() check holder instance to bind values to views accordingly:
if(holder instanceof SenderMessageHolder) {
((SenderMessageHolder) holder).textView.setText(...);
...
} else {
((ReceiverMessageHolder) holder).textView.setText(...);
...
}
Hope that it helps!
There are a couple of ways to do this. The way I would recommend is by overriding the public int getItemViewType(int position) for your adapter. You must implement logic here to help you determine the layout file to return and returning a view type constant.
public static int INCOMING = 1;
public static int OUTGOING = 2;
#Override
public int getItemViewType(int position) {
int viewType = -1;
Message m = messagesList.get(position)
//Logic here determining if m is left or right aligned and
// return either
// MessageAdapter.OUTGOING OR MessageAdapter.INCOMING
return viewType;
}
Create a left-aligned layout file and a right-aligned layout file. Your new onCreateviewHolder() adapter method will look something like this:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Message c = messagesList.get(viewType);
String sender = c.getFrom();
databaseReference = FirebaseDatabase.getInstance().getReference().child("Users").child(sender);
View v;
if( viewType == MessageAdapter.INCOMING ) {
v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.messages_layout_left, parent, false);
}else if ( viewType == MessageAdapter.OUTGOING) {
v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.messages_layout_right, parent, false);
}
return new MessageViewHolder(v);
}
I know how to create a ListView in Android but I need some kind of accordion list view in Android. Something like this:
The accordion list should have a section header and should toggle when clicking on the section header.
How can I build such an accordion ListView?
You could use ExpandableListView. Documentation here
you change child_view.xml content.
http://www.learn-android-easily.com/2013/07/android-expandablelistview-example.html
http://theopentutorials.com/tutorials/android/listview/android-expandable-list-view-example/
I have build a super light Accordion list recently via Constraint layout . It's worth to have a look at :
https://github.com/draxdave/ConstraintAccordion
Why would you use ListView? Android has something new now called RecyclerView which takes place for the ListView: https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html
Here is the way of making a header using RecyclerView:
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
public class HeaderAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
String[] data;
public HeaderAdapter(String[] data) {
this.data = data;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
//inflate your layout and pass it to view holder
return new VHItem(null);
} else if (viewType == TYPE_HEADER) {
//inflate your layout and pass it to view holder
return new VHHeader(null);
}
throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof VHItem) {
String dataItem = getItem(position);
//cast holder to VHItem and set data
} else if (holder instanceof VHHeader) {
//cast holder to VHHeader and set data for header.
}
}
#Override
public int getItemCount() {
return data.length + 1;
}
#Override
public int getItemViewType(int position) {
if (isPositionHeader(position))
return TYPE_HEADER;
return TYPE_ITEM;
}
private boolean isPositionHeader(int position) {
return position == 0;
}
private String getItem(int position) {
return data[position - 1];
}
class VHItem extends RecyclerView.ViewHolder {
TextView title;
public VHItem(View itemView) {
super(itemView);
}
}
class VHHeader extends RecyclerView.ViewHolder {
Button button;
public VHHeader(View itemView) {
super(itemView);
}
}
}
Link on git: https://gist.github.com/hister/d56c00fb5fd2dfaf279b
Im attempting to add two different views to the GridviewLayoutManager using a custom adapter.
However, I cant seem to reference the headerview correctly. When the onbindViewHolder is called it is expecting a "ViewHolder" response, however i really want to reference the HeaderView i crated
Because I cant access the correct view, I also cant reference the TextView within the XML layout I am calling.
here is my customer adaptor class:
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.squareup.picasso.Picasso;
import java.util.ArrayList;
public class ElementsAdapter extends RecyclerView.Adapter<ElementsAdapter.ViewHolder> {
private ArrayList<String> mDataset;
private ArrayList<Integer> mDatamap;
public Context context;
private static final int VIEW_HEADER = 0;
private static final int VIEW_NORMAL = 1;
private View headerView;
private int datasetSize;
public class HeaderHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public TextView headertext;
public HeaderHolder(View v) {
super(v);
headertext = (TextView) v.findViewById(R.id.headertext);
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public TextView txtHeader;
public TextView txtFooter;
public ImageView imgImage;
public ViewHolder(View v) {
super(v);
txtHeader = (TextView) v.findViewById(R.id.firstLine);
txtFooter = (TextView) v.findViewById(R.id.secondLine);
imgImage = (ImageView) v.findViewById(R.id.icon);
}
}
public ElementsAdapter(ArrayList<String> myDataset, ArrayList<Integer> myDatamap) {
mDataset = myDataset;
myDatamap = mDatamap;
}
#Override
public int getItemViewType(int position) {
return isHeader(position) == 1 ? VIEW_HEADER : VIEW_NORMAL;
}
#Override
public int getItemCount() {
return mDataset.size();
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_HEADER) {
// create a new view
View sub_view = LayoutInflater.from(parent.getContext()).inflate(R.layout.header, parent, false);
Context context = sub_view.getContext();
// set the view's size, margins, paddings and layout parameters
ViewHolder vh = new ViewHolder(sub_view);
return vh;
// return new HeaderViewHolder(headerView);
} else {
// create a new view
View sub_view = LayoutInflater.from(parent.getContext()).inflate(R.layout.sub_layout, parent, false);
context = sub_view.getContext();
// set the view's size, margins, paddings and layout parameters
ViewHolder vh = new ViewHolder(sub_view);
return vh;
}
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
if (isHeader(position) == 1) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
final String name = mDataset.get(position);
// holder.txtHeader.setText(mDataset.get(position));
viewHolder.headertext.setText(name);
} else {
// - get element from your dataset at this position
// - replace the contents of the view with that element
final String name = mDataset.get(position);
Picasso.with(context).load("http://www.500kgiveaway.co.uk/"+name).resize(200,200).into(viewHolder.imgImage);
// holder.txtHeader.setText(mDataset.get(position));
viewHolder.txtHeader.setText(name);
viewHolder.txtHeader.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick (View v){
//remove(name);
}
}
);
viewHolder.txtFooter.setText("Footer: "+mDataset.get(position));
}
//ViewHolder holder = (ViewHolder) viewHolder;
//holder.textView.setText("Position " + (position - 1));
}
public int isHeader(int position) {
return mDatamap.get(position) ==1 ? 1:0;}
}
It seems to me that the isHeader() method will always return 0, since you compare a String with a integer. I assume you would like want to check the position of the current item to be 1.
Try this code instead:
public boolean isHeader(int position) {
return position == 1;
}
Then replace
if (isHeader(position) == 1)...
with
if (isHeader(position))...
I hope this helps.
Edit
The above was intended. Sorry.
In the class definition ElementsAdapter.ViewHolder is inserted as the ViewHolder type. This works for the normal
ElementsAdapter.ViewHolder extends RecyclerView.ViewHolder
but not for
ElementsAdapter.HeaderHolder extends RecyclerView.ViewHolder
since it doesn't extend ElementsAdapter.ViewHolder.
You should therfore specify RecyclerView.ViewHolder instead as a generic type to support both of your types.