Create an adapter object, find recycler view id to set adapter and then set layout manager. In scroll listener, unable to get correct LastVisibleItemPosition, it return -1 to me. findFirstVisibleItemPosition() also returning -1.
//Here is Adapter
public class CategoryProduct extends RecyclerView.Adapter<RecyclerView.ViewHolder>
`enter code here`{
private static final int ITEM = 0;
private static final int LOADING = 1;
public String vertical = "";
Context context;
private boolean isLoadingAdded = false;
private boolean retryPageLoad = false;
private List<com.example.it.camanagement.model.CategoryProduct> dataSet;
public CategoryProduct(ArrayList<com.example.it.camanagement.model.CategoryProduct> data, Context context, String vertical) {
this.dataSet = data;
this.context = context;
this.vertical = vertical;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
RecyclerView.ViewHolder viewHolder = null;
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
switch (viewType) {
case ITEM:
View viewLoading;
if (vertical.equals("vertical")) {
viewLoading = LayoutInflater.from(parent.getContext())
.inflate(R.layout.category_product_list, parent, false);
} else {
viewLoading = LayoutInflater.from(parent.getContext())
.inflate(R.layout.category_product_list_grid, parent, false);
}
viewHolder = new MyViewHolder(viewLoading);
break;
case LOADING:
View viewLoading1 = inflater.inflate(R.layout.item_progress, parent, false);
viewHolder = new LoadingVH(viewLoading1);
break;
}
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int listPosition) {
if (listPosition == dataSet.size() - 1) {
CategoryDetails categoryDetails = (CategoryDetails) context;
categoryDetails.onBottomReached(listPosition);
}
switch (getItemViewType(listPosition)) {
case ITEM:
final MyViewHolder myViewHolder = (MyViewHolder) holder;
TextView textViewName = myViewHolder.productName;
// TextView textViewVersion = holder.textViewVersion;
ImageView imageView = myViewHolder.productImage;
RatingBar productRating = myViewHolder.productRasting;
TextView productCost = myViewHolder.productCost;
TextView productDiscount = myViewHolder.productDistCount;
TextView productCOLOR = myViewHolder.productColor;
TextView productId = myViewHolder.product_id;
TextView productModel = myViewHolder.productModel;
TextView productQuantity = myViewHolder.productQuantity;
productModel.setText(dataSet.get(listPosition).getModel());
productQuantity.setText(dataSet.get(listPosition).getQuantity());
productId.setText(dataSet.get(listPosition).getProduct_id());
textViewName.setText(dataSet.get(listPosition).getName());
Glide.with(context).load(dataSet.get(listPosition).getImage()).into(imageView);
productRating.setRating(Float.parseFloat(String.valueOf(dataSet.get(listPosition).getRating())));
productCost.setText("RM " + dataSet.get(listPosition).getPrice());
productDiscount.setText(dataSet.get(listPosition).getDiscount());
if (dataSet.get(listPosition).getSpecial().trim().length() != 0) {
myViewHolder.special.setText("RM " + dataSet.get(listPosition).getSpecial());
strikeThroughText(productCost);
}
break;
case LOADING:
LoadingVH loadingVH = (LoadingVH) holder;
if (retryPageLoad) {
loadingVH.mErrorLayout.setVisibility(View.VISIBLE);
loadingVH.mProgressBar.setVisibility(View.GONE);
loadingVH.mErrorTxt.setText(
errorMsg != null ?
errorMsg :
context.getString(R.string.error_msg_unknown));
} else {
loadingVH.mErrorLayout.setVisibility(View.GONE);
loadingVH.mProgressBar.setVisibility(View.VISIBLE);
}
break;
}
}
private void strikeThroughText(TextView price) {
price.setPaintFlags(price.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
}
#Override
public int getItemCount() {
return dataSet == null ? 0 : dataSet.size();
//return dataSet.size();
}
#Override
public int getItemViewType(int position) {
return (position == dataSet.size() - 1 && isLoadingAdded) ? LOADING : ITEM;
}
public com.example.it.camanagement.model.CategoryProduct getItem(int position) {
return dataSet.get(position);
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView productName, productColor, productCost, productDistCount, product_id, productModel, productQuantity, special;
RatingBar productRasting;
ImageView productImage;
public MyViewHolder(View itemView) {
super(itemView);
this.productName = (TextView) itemView.findViewById(R.id.product_name);
this.productColor = (TextView) itemView.findViewById(R.id.product_color);
this.productCost = (TextView) itemView.findViewById(R.id.cost);
this.productDistCount = (TextView) itemView.findViewById(R.id.discount);
this.productRasting = (RatingBar) itemView.findViewById(R.id.product_rating);
this.product_id = (TextView) itemView.findViewById(R.id.product_iid);
this.productQuantity = (TextView) itemView.findViewById(R.id.quantity);
this.productModel = (TextView) itemView.findViewById(R.id.modelProduct);
//this.textViewVersion = (TextView) itemView.findViewById(R.id.textViewVersion);
this.productImage = (ImageView) itemView.findViewById(R.id.product_image);
this.special = (TextView) itemView.findViewById(R.id.special);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
Intent i = new Intent(context, ProductDescription.class);
i.putExtra("productName", ((TextView) v.findViewById(R.id.product_name)).getText().toString());
i.putExtra("product_id", ((TextView) v.findViewById(R.id.product_iid)).getText().toString());
context.startActivity(i);
if (getPosition() == 0) {
// Toast.makeText(v.getContext(), " On CLick one", Toast.LENGTH_SHORT).show();
}
if (getPosition() == 1) {
//Toast.makeText(v.getContext(), " On CLick Two", Toast.LENGTH_SHORT).show();
}
if (getPosition() == 2) {
// Toast.makeText(v.getContext(), " On CLick Three", Toast.LENGTH_SHORT).show();
}
if (getPosition() == 3) {//
// Toast.makeText(v.getContext(), " On CLick Fore", Toast.LENGTH_SHORT).show();
}
}
});
}
}
protected class LoadingVH extends RecyclerView.ViewHolder {
private ProgressBar mProgressBar;
private ImageButton mRetryBtn;
private TextView mErrorTxt;
private LinearLayout mErrorLayout;
public LoadingVH(View itemView) {
super(itemView);
mProgressBar = (ProgressBar) itemView.findViewById(R.id.loadmore_progress);
mRetryBtn = (ImageButton) itemView.findViewById(R.id.loadmore_retry);
mErrorTxt = (TextView) itemView.findViewById(R.id.loadmore_errortxt);
mErrorLayout = (LinearLayout) itemView.findViewById(R.id.loadmore_errorlayout);
}
}
}
//Recycler View
categoryProduct = (RecyclerView) findViewById(R.id.categoryProductList);
categoryProductAdapter = new CategoryProduct(categoryProduct,categoryProductList, context);
categoryProduct.setAdapter(categoryProductAdapter);
categoryProduct.setNestedScrollingEnabled(false);
categoryProduct.setHasFixedSize(false);
girdLayoutManager = new GridLayoutManager(CategoryDetails.this, 2);
categoryProduct.setLayoutManager(girdLayoutManager);
//scroll Listener
scrollListener = new EndlessRecyclerViewScrollListener(girdLayoutManager) {
#Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
// Triggered only when new data needs to be appended to the list
// Add whatever code is needed to append new items to the bottom of the list
Log.d("pageCount", "" + page);
Log.d("FirstVisibleITEM", "" + girdLayoutManager.findFirstVisibleItemPosition());
Log.d("LastVisibleITEM", "" + girdLayoutManager.findLastVisibleItemPosition());
}
};
categoryProduct.addOnScrollListener(scrollListener);
Finally solved it, Issue was: I was creating the object of layout manager two times. one in method(API response received and after notify the adapter) and another one in OnCreate() method of activity. after removing one object from method( API response received and after notify the adapter), fixed the issue.
I am working on a chat app and I am very new to using ListViews and ArrayAdapters, as well as Custom Adapters. I am having a problem that causes all my items in my list to be of one layout, even they are supposed to change according to a booleans(true would be an outgoing chat bubble, and false would be an incoming chat bubble).
Here is my code for the retreival of the chat(It isn't the whole file, just a snippet):
Variable declarations:
final ArrayList<chatBubble> objects = new ArrayList<>();
final CustomAdapter customAdapter = new CustomAdapter(this, objects);
listView.setAdapter(customAdapter);
Code to get messages and add to list:
mDatabase.child("chat").child("publicDump").child("dumpedMessages").child("message" + i).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final String message = dataSnapshot.getValue(String.class);
if (message != null) {
final String extract = message.substring(message.lastIndexOf("<") + 1, message.indexOf(">"));
mDatabase.child("users").child("c").child("emailToUsername").child(mAuth.getCurrentUser().getEmail().replace(".", ",")).child("username").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String username = dataSnapshot.getValue(String.class);
if (username != null) {
if (username.equals(extract)) {
Log.i("Extract", extract);
int semicnt = 0;
int num = 0;
//Log.i("Loop", "Yes");
for (int i = 0; i < message.length(); i++) {
//Log.i("Loop", "y");
if (String.valueOf(message.charAt(i)).equals(":")) {
semicnt++;
//Log.i("cnt", String.valueOf(semicnt));
if (semicnt == 3) {
num = i;
i = message.length() - 1;
String time = message.substring(0, (Math.min(message.length(), num)));
String finalM = message.replace(time + ": ", "").replace("<" + extract + "> ", "");
chatBubble chat = new chatBubble(finalM, "From: " + extract + " At: " + time, true);
objects.add(chat);
}
}
}
} else {
int semicnt = 0;
int num = 0;
// Log.i("Loop", "Yes");
for (int i = 0; i < message.length(); i++) {
//Log.i("Loop", "y");
if (String.valueOf(message.charAt(i)).equals(":")) {
semicnt++;
//Log.i("cnt", String.valueOf(semicnt));
if (semicnt == 3) {
num = i;
i = message.length() - 1;
String time = message.substring(0, (Math.min(message.length(), num)));
String finalM = message.replace(time + ": ", "").replace("<" + extract + "> ", "");
chatBubble chat = new chatBubble(finalM, "From: " + extract + " At: " + time, false);
objects.add(chat);
}
}
}
}
customAdapter.notifyDataSetChanged();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Here is my code for my chatBubble:
package com.tejasmehta.codeychat;
public class chatBubble {
private String msg;
private String date;
private boolean myMessage;
public chatBubble(String msg, String date, boolean myMessage) {
this.msg = msg;
this.date = date;
this.myMessage = myMessage;
}
public String Msg() {
return msg;
}
public String Date() {
return date;
}
public boolean myMessage() {
return myMessage;
}
}
And here is my code for my customAdapter(which shows that if the boolean, myMessage, is true, it will load a different layout, and a different one for false):
public class CustomAdapter extends BaseAdapter {
private LayoutInflater inflater;
private ArrayList<chatBubble> objects;
private class ViewHolder {
TextView msg;
TextView date;
}
public CustomAdapter(Context context, ArrayList<chatBubble> objects) {
inflater = LayoutInflater.from(context);
this.objects = objects;
}
public int getCount() {
return objects.size();
}
public chatBubble getItem(int position) {
return objects.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
int layoutResource; // determined by view type
chatBubble ChatBubble = getItem(position);
if (ChatBubble.myMessage()) {
layoutResource = R.layout.right_bubble;
} else {
layoutResource = R.layout.left_bubble;
}
if(convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(layoutResource, null);
holder.msg = convertView.findViewById(R.id.txt_msg);
holder.date = convertView.findViewById(R.id.date);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.msg.setText(objects.get(position).Msg());
holder.date.setText(objects.get(position).Date());
return convertView;
}
}
Thank You for all of your help!
the problem is your getview
The view is being recycled so convertview already has a layout. so its not being reinflated to the correct layout.
you need to use getViewType() and getViewTypeCount() to tell the listview you want to use different layouts
http://android.amberfog.com/?p=296
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public int getItemViewType(int position) {
return getItem(position).myMessage()?0:1;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
int layoutResource; // determined by view type
chatBubble ChatBubble = getItem(position);
if (ChatBubble.myMessage()) {
layoutResource = R.layout.right_bubble;
} else {
layoutResource = R.layout.left_bubble;
}
if(convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(layoutResource, null);
holder.msg = convertView.findViewById(R.id.txt_msg);
holder.date = convertView.findViewById(R.id.date);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.msg.setText(objects.get(position).Msg());
holder.date.setText(objects.get(position).Date());
return convertView;
}
when i use this
#Override
public int getItemViewType(int position) {
return position;
}
my RecyclerView work without problem
now i want to use multi layout on it i use this
#Override
public int getItemViewType(int position) {
int type = -1;
if(mMessages.get(position).getDir().equals("left")) type = 1;
else if(mMessages.get(position).getDir().equals("right")) type = 0;
else if(mMessages.get(position).getDir().equals("typing")) type = 2;
return type;
//return position;
}
the problem when i used it's and scroll to top and back to bottom or just scrolling
its reorder item in RecyclerView like make the first item on it the last one or on middle or every where not i the correct position
just if i back to the first code of getItemViewType its work without
problem but i cant use multi layout
my full code
public class MessageAdapter1 extends RecyclerView.Adapter<MessageAdapter1.ViewHolder> {
private List<MessageList> mMessages;
private int[] mUsernameColors;
private Context context;
public MessageAdapter1(Context context, List<MessageList> messages) {
mMessages = messages;
this.context = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int layout = -1;
switch (viewType) {
case 0:
layout = R.layout.right_message;
break;
case 1:
layout = R.layout.left_message;
break;
case 2:
layout = R.layout.message_left;
break;
}
View v = LayoutInflater.from(parent.getContext()).inflate(layout , parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
MessageList message = mMessages.get(position);
//viewHolder.setMessage(message.getMessage());
//viewHolder.setUsername(message.getUsername());
viewHolder.setGroupMessage(message);
}
#Override
public int getItemCount() {
return mMessages.size();
}
#Override
public int getItemViewType(int position) {
int type = -1;
if(mMessages.get(position).getDir().equals("left")) type = 1;
else if(mMessages.get(position).getDir().equals("right")) type = 0;
else if(mMessages.get(position).getDir().equals("typing")) type = 2;
return type;
//return position;
}
public class ViewHolder extends RecyclerView.ViewHolder {
private LinearLayout groupMessage;
//private ImageView typing;
public ViewHolder(View itemView) {
super(itemView);
groupMessage = (LinearLayout)itemView.findViewById(R.id.messages);
}
public void setGroupMessage(MessageList m) {
if (null == groupMessage) return;
int i = 0;
if(m.getMessageStatus() == false){
m.setMessageStatus(true);
for (String message : m.getMessageList()) {
//TextView text = new TextView(activity);
TextView text = new MyTextView(context);
LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
p.setMargins(0, 0, 0, 2);
if (m.getDir().equals("left")) {
text.setTextColor(Color.BLACK);
p.gravity = Gravity.LEFT;
if(m.getMessageList().size() == 1){
text.setBackgroundResource(R.drawable.message_left_default);
}
else if (i == 0) {
text.setBackgroundResource(R.drawable.message_left_first);
} else if (i + 1 == m.getMessageList().size()) {
text.setBackgroundResource(R.drawable.message_left_last);
} else {
text.setBackgroundResource(R.drawable.message_left);
}
} else{
p.gravity = Gravity.RIGHT;
text.setTextColor(Color.WHITE);
if(m.getMessageList().size() == 1){
text.setBackgroundResource(R.drawable.message_right_default);
}
else if (i == 0) {
text.setBackgroundResource(R.drawable.message_right_first);
} else if (i + 1 == m.getMessageList().size()) {
text.setBackgroundResource(R.drawable.message_right_last);
} else {
text.setBackgroundResource(R.drawable.message_right);
}
}
text.setLayoutParams(p);
text.setText(message);
text.setPadding(8, 8, 8, 8);
text.setTextSize(18f);
//text.setTextAppearance(context, android.R.style.TextAppearance_Large);
groupMessage.addView(text);
i++;
}
}
}
}
}
I solve it by remove
int type = -1;
if(mMessages.get(position).getDir().equals("left")) type = 1;
else if(mMessages.get(position).getDir().equals("right")) type = 0;
else if(mMessages.get(position).getDir().equals("typing")) type = 2;
return type;
from
getItemViewType
and return position
and put the first code
int type = -1;
if(mMessages.get(position).getDir().equals("left")) type = 1;
else if(mMessages.get(position).getDir().equals("right")) type = 0;
else if(mMessages.get(position).getDir().equals("typing")) type = 2;
return type;
in onCreateViewHolder and use viewType as the current position
Below is my complete adapter class of sendbird chatting.Currently date with time is displayed with every message.viewHolder.getView("left_time") and viewHolder.getView("right_time") are views for displaying date with time and it is calculated in method getDisplayDateTime.I want date with time to be displayed only when there is a change in date.For example if there is set of messages dated 23rd june then date with time should be displayed only for the first message with date 23rd june.
I tried doing that by detecting whenever there is a change in date by storing date in xyz variable and comparing it with date of every message and whenever they are unequal then making date view of that message visible and assigning the new date to xyz variable for comparison with future messages.But this approach fails when i scroll down and scroll up list.
What is the ideal approach for achieving this?
public class SendBirdMessagingAdapter extends BaseAdapter {
private static final int TYPE_UNSUPPORTED = 0;
private static final int TYPE_MESSAGE = 1;
private static final int TYPE_SYSTEM_MESSAGE = 2;
private static final int TYPE_FILELINK = 3;
private static final int TYPE_BROADCAST_MESSAGE = 4;
private static final int TYPE_TYPING_INDICATOR = 5;
private final Context mContext;
private final LayoutInflater mInflater;
private final ArrayList<Object> mItemList;
private Hashtable<String, Long> mReadStatus;
private Hashtable<String, Long> mTypeStatus;
private List<MessagingChannel.Member> mMembers;
private long mMaxMessageTimestamp = Long.MIN_VALUE;
private long mMinMessageTimestamp = Long.MAX_VALUE;
public SendBirdMessagingAdapter(Context context) {
mContext = context;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mItemList = new ArrayList<Object>();
mReadStatus = new Hashtable<String, Long>();
mTypeStatus = new Hashtable<String, Long>();
}
#Override
public int getCount() {
return mItemList.size() + ((mTypeStatus.size() <= 0) ? 0 : 1);
}
#Override
public Object getItem(int position) {
if (position >= mItemList.size()) {
ArrayList<String> names = new ArrayList<String>();
for (MessagingChannel.Member member : mMembers) {
if (mTypeStatus.containsKey(member.getId())) {
names.add(member.getName());
}
}
return names;
}
return mItemList.get(position);
}
public void delete(Object object) {
mItemList.remove(object);
}
public void clear() {
mMaxMessageTimestamp = Long.MIN_VALUE;
mMinMessageTimestamp = Long.MAX_VALUE;
mReadStatus.clear();
mTypeStatus.clear();
mItemList.clear();
}
public void resetReadStatus(Hashtable<String, Long> readStatus) {
mReadStatus = readStatus;
}
public void setReadStatus(String userId, long timestamp) {
if (mReadStatus.get(userId) == null || mReadStatus.get(userId) < timestamp) {
mReadStatus.put(userId, timestamp);
}
}
public void setTypeStatus(String userId, long timestamp) {
if (userId.equals(SendBird.getUserId())) {
return;
}
if (timestamp <= 0) {
mTypeStatus.remove(userId);
} else {
mTypeStatus.put(userId, timestamp);
}
}
#Override
public long getItemId(int position) {
return position;
}
public void addMessageModel(MessageModel messageModel) {
if (messageModel.isPast()) {
mItemList.add(0, messageModel);
} else {
mItemList.add(messageModel);
}
updateMessageTimestamp(messageModel);
}
private void updateMessageTimestamp(MessageModel model) {
mMaxMessageTimestamp = mMaxMessageTimestamp < model.getTimestamp() ? model.getTimestamp() : mMaxMessageTimestamp;
mMinMessageTimestamp = mMinMessageTimestamp > model.getTimestamp() ? model.getTimestamp() : mMinMessageTimestamp;
}
public long getMaxMessageTimestamp() {
return mMaxMessageTimestamp == Long.MIN_VALUE ? Long.MAX_VALUE : mMaxMessageTimestamp;
}
public long getMinMessageTimestamp() {
return mMinMessageTimestamp == Long.MAX_VALUE ? Long.MIN_VALUE : mMinMessageTimestamp;
}
public void setMembers(List<MessagingChannel.Member> members) {
mMembers = members;
}
#Override
public int getItemViewType(int position) {
if (position >= mItemList.size()) {
return TYPE_TYPING_INDICATOR;
}
Object item = mItemList.get(position);
if (item instanceof Message) {
return TYPE_MESSAGE;
} else if (item instanceof FileLink) {
return TYPE_FILELINK;
} else if (item instanceof SystemMessage) {
return TYPE_SYSTEM_MESSAGE;
} else if (item instanceof BroadcastMessage) {
return TYPE_BROADCAST_MESSAGE;
}
return TYPE_UNSUPPORTED;
}
#Override
public int getViewTypeCount() {
return 6;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
final Object item = getItem(position);
if (convertView == null || ((ViewHolder) convertView.getTag()).getViewType() != getItemViewType(position)) {
viewHolder = new ViewHolder();
viewHolder.setViewType(getItemViewType(position));
switch (getItemViewType(position)) {
case TYPE_UNSUPPORTED:
convertView = new View(mInflater.getContext());
convertView.setTag(viewHolder);
break;
case TYPE_MESSAGE: {
TextView tv;
CircularImageView iv;
View v;
convertView = mInflater.inflate(R.layout.sendbird_view_messaging_message, parent, false);
v = convertView.findViewById(R.id.left_container);
viewHolder.setView("left_container", v);
iv = (CircularImageView) convertView.findViewById(R.id.img_left_thumbnail);
viewHolder.setView("left_thumbnail", iv);
tv = (TextView) convertView.findViewById(R.id.txt_left);
viewHolder.setView("left_message", tv);
tv = (TextView) convertView.findViewById(R.id.txt_left_name);
viewHolder.setView("left_name", tv);
tv = (TextView) convertView.findViewById(R.id.txt_left_time);
viewHolder.setView("left_time", tv);
v = convertView.findViewById(R.id.right_container);
viewHolder.setView("right_container", v);
iv = (CircularImageView) convertView.findViewById(R.id.img_right_thumbnail);
viewHolder.setView("right_thumbnail", iv);
tv = (TextView) convertView.findViewById(R.id.txt_right);
viewHolder.setView("right_message", tv);
tv = (TextView) convertView.findViewById(R.id.txt_right_name);
viewHolder.setView("right_name", tv);
tv = (TextView) convertView.findViewById(R.id.txt_right_time);
viewHolder.setView("right_time", tv);
tv = (TextView) convertView.findViewById(R.id.txt_right_status);
viewHolder.setView("right_status", tv);
convertView.setTag(viewHolder);
break;
}
case TYPE_SYSTEM_MESSAGE: {
convertView = mInflater.inflate(R.layout.sendbird_view_system_message, parent, false);
viewHolder.setView("message", convertView.findViewById(R.id.txt_message));
convertView.setTag(viewHolder);
break;
}
case TYPE_BROADCAST_MESSAGE: {
convertView = mInflater.inflate(R.layout.sendbird_view_system_message, parent, false);
viewHolder.setView("message", convertView.findViewById(R.id.txt_message));
convertView.setTag(viewHolder);
break;
}
case TYPE_FILELINK: {
TextView tv;
CircularImageView civ;
ImageView iv;
View v;
convertView = mInflater.inflate(R.layout.sendbird_view_messaging_filelink, parent, false);
v = convertView.findViewById(R.id.left_container);
viewHolder.setView("left_container", v);
civ = (CircularImageView) convertView.findViewById(R.id.img_left_thumbnail);
viewHolder.setView("left_thumbnail", civ);
iv = (ImageView) convertView.findViewById(R.id.img_left);
viewHolder.setView("left_image", iv);
tv = (TextView) convertView.findViewById(R.id.txt_left_name);
viewHolder.setView("left_name", tv);
tv = (TextView) convertView.findViewById(R.id.txt_left_time);
viewHolder.setView("left_time", tv);
v = convertView.findViewById(R.id.right_container);
viewHolder.setView("right_container", v);
civ = (CircularImageView) convertView.findViewById(R.id.img_right_thumbnail);
viewHolder.setView("right_thumbnail", civ);
iv = (ImageView) convertView.findViewById(R.id.img_right);
viewHolder.setView("right_image", iv);
tv = (TextView) convertView.findViewById(R.id.txt_right_name);
viewHolder.setView("right_name", tv);
tv = (TextView) convertView.findViewById(R.id.txt_right_time);
viewHolder.setView("right_time", tv);
tv = (TextView) convertView.findViewById(R.id.txt_right_status);
viewHolder.setView("right_status", tv);
convertView.setTag(viewHolder);
convertView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new AlertDialog.Builder(mContext)
.setTitle("Foodvite")
.setMessage("Do you want to download this file?")
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
try {
downloadUrl((FileLink) item, mContext);
} catch (IOException e) {
e.printStackTrace();
}
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
})
.create()
.show();
}
});
break;
}
case TYPE_TYPING_INDICATOR: {
convertView = mInflater.inflate(R.layout.sendbird_view_typing_indicator, parent, false);
viewHolder.setView("message", convertView.findViewById(R.id.txt_message));
convertView.setTag(viewHolder);
break;
}
}
}
viewHolder = (ViewHolder) convertView.getTag();
switch (getItemViewType(position)) {
case TYPE_UNSUPPORTED:
break;
case TYPE_MESSAGE:
Message message = (Message) item;
if (message.getSenderId().equals(SendBird.getUserId())) {
viewHolder.getView("left_container", View.class).setVisibility(View.GONE);
viewHolder.getView("right_container", View.class).setVisibility(View.VISIBLE);
displayUrlImage(viewHolder.getView("right_thumbnail", CircularImageView.class), message.getSenderImageUrl(), true);
viewHolder.getView("right_name", TextView.class).setText(message.getSenderName());
viewHolder.getView("right_message", TextView.class).setText(message.getMessage());
viewHolder.getView("right_time", TextView.class).setText(getDisplayDateTime(mContext, message.getTimestamp()));
int readCount = 0;
for (String key : mReadStatus.keySet()) {
if (key.equals(message.getSenderId())) {
readCount += 1;
continue;
}
if (mReadStatus.get(key) >= message.getTimestamp()) {
readCount += 1;
}
}
if (readCount < mReadStatus.size()) {
if (mReadStatus.size() - readCount > 1) {
viewHolder.getView("right_status", TextView.class).setText("Unread " + (mReadStatus.size() - readCount));
} else {
viewHolder.getView("right_status", TextView.class).setText("Unread");
}
} else {
viewHolder.getView("right_status", TextView.class).setText("");
}
viewHolder.getView("right_container").setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
new AlertDialog.Builder(mContext)
.setTitle("Foodvite")
.setMessage("Do you want to delete a message?")
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
SendBird.deleteMessage(((Message) item).getMessageId(), new DeleteMessageHandler() {
#Override
public void onError(SendBirdException e) {
e.printStackTrace();
}
#Override
public void onSuccess(long messageId) {
mSendBirdMessagingAdapter.delete(item);
mSendBirdMessagingAdapter.notifyDataSetChanged();
Toast.makeText(mContext, "Message has been deleted.", Toast.LENGTH_SHORT).show();
}
});
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
})
.create()
.show();
return true;
}
});
} else {
viewHolder.getView("left_container", View.class).setVisibility(View.VISIBLE);
viewHolder.getView("right_container", View.class).setVisibility(View.GONE);
displayUrlImage(viewHolder.getView("left_thumbnail", CircularImageView.class), message.getSenderImageUrl(), true);
viewHolder.getView("left_name", TextView.class).setText(message.getSenderName());
viewHolder.getView("left_message", TextView.class).setText(message.getMessage());
viewHolder.getView("left_time", TextView.class).setText(getDisplayDateTime(mContext, message.getTimestamp()));
if(isGroupChat)
viewHolder.getView("left_name", TextView.class).setVisibility(View.VISIBLE);
else
viewHolder.getView("left_name", TextView.class).setVisibility(View.GONE);
}
break;
case TYPE_SYSTEM_MESSAGE:
SystemMessage systemMessage = (SystemMessage) item;
viewHolder.getView("message", TextView.class).setText(Html.fromHtml(systemMessage.getMessage()));
break;
case TYPE_BROADCAST_MESSAGE:
BroadcastMessage broadcastMessage = (BroadcastMessage) item;
viewHolder.getView("message", TextView.class).setText(Html.fromHtml(broadcastMessage.getMessage()));
break;
case TYPE_FILELINK:
FileLink fileLink = (FileLink) item;
if (fileLink.getSenderId().equals(SendBird.getUserId())) {
viewHolder.getView("left_container", View.class).setVisibility(View.GONE);
viewHolder.getView("right_container", View.class).setVisibility(View.VISIBLE);
displayUrlImage(viewHolder.getView("right_thumbnail", ImageView.class), fileLink.getSenderImageUrl(), true);
viewHolder.getView("right_name", TextView.class).setText(fileLink.getSenderName());
if (fileLink.getFileInfo().getType().toLowerCase().startsWith("image")) {
displayUrlImage(viewHolder.getView("right_image", ImageView.class), fileLink.getFileInfo().getUrl());
} else {
viewHolder.getView("right_image", CircularImageView.class).setImageResource(R.drawable.sendbird_icon_file);
}
viewHolder.getView("right_time", TextView.class).setText(getDisplayDateTime(mContext, fileLink.getTimestamp()));
int readCount = 0;
for (String key : mReadStatus.keySet()) {
if (key.equals(fileLink.getSenderId())) {
continue;
}
if (mReadStatus.get(key) < fileLink.getTimestamp()) {
readCount += 1;
}
}
if (readCount < mReadStatus.size() - 1) {
viewHolder.getView("right_status", TextView.class).setText("Unread");
} else {
viewHolder.getView("right_status", TextView.class).setText("");
}
} else {
viewHolder.getView("left_container", View.class).setVisibility(View.VISIBLE);
viewHolder.getView("right_container", View.class).setVisibility(View.GONE);
displayUrlImage(viewHolder.getView("left_thumbnail", ImageView.class), fileLink.getSenderImageUrl(), true);
viewHolder.getView("left_name", TextView.class).setText(fileLink.getSenderName());
if (fileLink.getFileInfo().getType().toLowerCase().startsWith("image")) {
displayUrlImage(viewHolder.getView("left_image", ImageView.class), fileLink.getFileInfo().getUrl());
} else {
viewHolder.getView("left_image", CircularImageView.class).setImageResource(R.drawable.sendbird_icon_file);
}
if(isGroupChat)
viewHolder.getView("left_name", TextView.class).setVisibility(View.VISIBLE);
else
viewHolder.getView("left_name", TextView.class).setVisibility(View.GONE);
viewHolder.getView("left_time", TextView.class).setText(getDisplayDateTime(mContext, fileLink.getTimestamp()));
}
break;
case TYPE_TYPING_INDICATOR: {
int itemCount = ((List) item).size();
String typeMsg = ((List) item).get(0)
+ ((itemCount > 1) ? " +" + (itemCount - 1) : "")
+ ((itemCount > 1) ? " are " : " is ")
+ "typing...";
viewHolder.getView("message", TextView.class).setText(typeMsg);
break;
}
}
return convertView;
}
public boolean checkTypeStatus() {
/**
* Clear an old type status.
*/
for (String key : mTypeStatus.keySet()) {
Long ts = mTypeStatus.get(key);
if (System.currentTimeMillis() - ts > 10 * 1000L) {
mTypeStatus.remove(key);
return true;
}
}
return false;
}
private class ViewHolder {
private Hashtable<String, View> holder = new Hashtable<String, View>();
private int type;
public int getViewType() {
return this.type;
}
public void setViewType(int type) {
this.type = type;
}
public void setView(String k, View v) {
holder.put(k, v);
}
public View getView(String k) {
return holder.get(k);
}
public <T> T getView(String k, Class<T> type) {
return type.cast(getView(k));
}
}
}
private static String getDisplayDateTime(Context context, long milli) {
Date date = new Date(milli);
if (System.currentTimeMillis() - milli < 60 * 60 * 24 * 1000l) {
return DateFormat.getTimeFormat(context).format(date);
}
return DateFormat.getDateFormat(context).format(date) + " " + DateFormat.getTimeFormat(context).format(date);
}
Try below code for your method,
private static String getDisplayDateTime(Context context, long milli, long milli2)
{
Date date = new Date(milli);
Date previousDate = new Date(milli2);
String strCurrentDate = "", strPreviousDate = "";
if (System.currentTimeMillis() - milli < 60 * 60 * 24 * 1000l)
{
strCurrentDate = DateFormat.getTimeFormat(context).format(date);
if(milli2 != -1)
strPreviousDate = DateFormat.getTimeFormat(context).format(previousDate);
//return strCurrentDate.equals(strPreviousDate) ? "" : strCurrentDate;
}
else
{
strCurrentDate = DateFormat.getDateFormat(context).format(date) + " " + DateFormat.getTimeFormat(context).format(date);
if(milli2 != -1)
strPreviousDate = DateFormat.getDateFormat(context).format(strPreviousDate) + " " + DateFormat.getTimeFormat(context).format(strPreviousDate);
}
return strCurrentDate.equals(strPreviousDate) ? "" : strCurrentDate;
}
Now call your methods like below
if(position > 0)
{
final Object previousItem = getItem(position - 1);
Message previousMessage = (Message) previousItem;
viewHolder.getView("right_time", TextView.class).setText(getDisplayDateTime(mContext, message.getTimestamp(), previousMessage.getTimestamp()));
}
else
{
viewHolder.getView("right_time", TextView.class).setText(getDisplayDateTime(mContext, message.getTimestamp(), -1));
}
I had a similar problem (ref this SO Question) few months ago and nothing worked except extending from ArrayAdapter<MyObject> instead of BaseAdapter.
So, change the base class of your adapter to ArrayAdapter<MyObject> and implement the prompted method of the abstract class. you might need to move some you your existing code to these overridden methods for better performance.
You can force the system to redraw all items by calling BaseAdapter.notifyDataSetChanged()
When I scroll the recyclerView some items appear in positions where they shouldn't.
Where I should have only "First" or only "Second" suddenly I have both "First Second" in a view.
EDIT : most probably it reuses some views but how am I going to check if this is correct?
My code is :
#Override
public void onBindViewHolder(MyRecyclerAdapter.ViewHolder holder, int position) {
DiaryDay item = items.get(position);
holder.tvDay.setText(item.getDay());
holder.tvMonth.setText(item.getMonth());
showSplash(holder, item);
if (holder.tvSunBathingMins!=null) {
holder.tvSunBathingMins.setText(String.valueOf(item.getSunBathing()) + " mins sunbathing. ");
}
}
private void showSplash(ViewHolder holder, DiaryDay item) {
if ( item.getSun_1() + item.getSun_2() + item.getSun_3() + item.getSun_4() == 4 ) {
holder.tvSplash2.setText( "All day! Amazing!");
} else {
if (item.getSun_1() != 0) {
holder.tvSplash1.setText("First ");
}
if (item.getSun_2() != 0) {
holder.tvSplash2.setText("Second ");
}
if (item.getSun_3() != 0) {
holder.tvSplash3.setText("Third ");
}
if (item.getSun_4() != 0) {
holder.tvSplash4.setText("Fourth ");
}
}
}
#Override
public MyRecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(itemLayout, parent, false);
return new ViewHolder(v);
}
And finally my ViewHolder is :
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView tvDay;
public TextView tvMonth;
public TextView tvSplash1; public TextView tvSplash2; public TextView tvSplash3; public TextView tvSplash4;
public TextView tvSunBathingMins;
public TextView tvSumMins;
public ViewHolder(View itemView) {
super(itemView);
tvDay = (TextView) itemView.findViewById(R.id.dayID);
tvMonth = (TextView) itemView.findViewById(R.id.monthID);
tvSplash1 = (TextView) itemView.findViewById(R.id.sun1);
tvSplash2 = (TextView) itemView.findViewById(R.id.sun2);
tvSplash3 = (TextView) itemView.findViewById(R.id.sun3);
tvSplash4 = (TextView) itemView.findViewById(R.id.sun4);
tvSunBathingMins = (TextView) itemView.findViewById(R.id.sunbathMinsID);
tvSumMins = (TextView) itemView.findViewById(R.id.sumMinsID);
}
}
The problem is in the way you define if statements without else clause. What happens is when the view holder gets recycled, views such as tvSplash1, tvSplash2 and etc. still have values assigned from the view that got off the screen. That's why you need to reset these items as follows:
#Override
public void onBindViewHolder(MyRecyclerAdapter.ViewHolder holder, int position) {
DiaryDay item = items.get(position);
holder.tvDay.setText(item.getDay());
holder.tvMonth.setText(item.getMonth());
showSplash(holder, item);
if (holder.tvSunBathingMins!=null) {
holder.tvSunBathingMins.setText(String.valueOf(item.getSunBathing()) + " mins sunbathing. ");
} else {
holder.tvSunBathingMins.setText("");
}
}
private void showSplash(ViewHolder holder, DiaryDay item) {
if ( item.getSun_1() + item.getSun_2() + item.getSun_3() + item.getSun_4() == 4 ) {
holder.tvSplash2.setText( "All day! Amazing!");
} else {
if (item.getSun_1() != 0) {
holder.tvSplash1.setText("First ");
} else {
holder.tvSplash1.setText("");
}
if (item.getSun_2() != 0) {
holder.tvSplash2.setText("Second ");
} else {
holder.tvSplash2.setText("");
}
if (item.getSun_3() != 0) {
holder.tvSplash3.setText("Third ");
} else {
holder.tvSplash3.setText("");
}
if (item.getSun_4() != 0) {
holder.tvSplash4.setText("Fourth ");
} else {
holder.tvSplash4.setText("");
}
}
}