ScrollToPosition for Lazy Loading RecyclerView - android

I'm trying to build a simple activity for messaging in android.
I have a RecyclerView that list the messages and I set a ScrollListener on it.
When the firstVisibleItem is the third of the list (RecyclerView stack from end), I will make a call to load other elements.
I add this element to my array and I set a new adapter on my RecyclerView for the update.
When I set the new adapter, the list starts one more time from the last message.
So far I have tried to use scrollToPosition to go to the message where the user was before scrollListener was triggered, but still not work at all because the lastVisibleItem become 1 every time.
Is there a way to maintain the last firstVisible element and set this element as the top element after reset the Adapter?
This is the onCreateMethod.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chat);
lastOffset = 0;
requestSize = 20;
loadMessage()
mAdapter = new ChatAdapter(this,mess);
llm = new LinearLayoutManager(this);
llm.setStackFromEnd(true);
mex_list.setLayoutManager(llm);
mex_list.setAdapter(mAdapter);
mex_list.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int lastVisible = llm.findLastVisibleItemPosition();
int firstVisible = llm.findFirstVisibleItemPosition();
lastFirstVisibleItem = firstVisibleItem; //lastFirstVisibleItem is a global variable
if(firstVisible <= 3){
if(!loading) {
loading = true;
loadMessage();
}
}
Log.d("Test Scroll", "LAST: "+ lastVisible+" FIRST: "+firstVisible);
}
});}
This is where the laodMessage() result was managed.
public void manageResult(Object pr, int caller) {
if(caller == MESSAGES_REQ){
if(mess == null)
mess = new ArrayList<>();
ArrayList<Message> tail = (ArrayList<Message>) pr;
if(tail != null && tail.size() > 0) {
lastOffset += tail.size();
mess.addAll(tail);
mAdapter = new ChatAdapter(ctx, mess);
mex_list.setAdapter(mAdapter);
}
llm.scrollToPosition(lastFirstVisibleItem);//lastFirstVisibleItem became always 1
spinner.setVisibility(View.GONE);
printEmpMex(View.GONE);
mex_list.setVisibility(View.VISIBLE);
loading = false;
}
}
This is the ChatAdapter.
public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ViewHolder> {
private ArrayList<Message> listOfMessages;
private static Context ctx;
private static final int SENT = 100;
private static final int RECIVED = 200;
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView text;
public TextView hour;
public ViewHolder(View v) {
super(v);
text = (TextView) v.findViewById(R.id.text_body);
hour = (TextView) v.findViewById(R.id.time);
}
void bind(Message m){
text.setText(m.getBody());
hour.setText(formatDateAndHour(m.getDate()));
}
}
public ChatAdapter(Context ctx, ArrayList<Message> m) {
listOfMessages = m;
this.ctx = ctx;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View view;
if (viewType == SENT) {
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.single_mex_sent, parent, false);
return new ViewHolder(view);
}
else if (viewType == RECIVED){
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.single_mex_recived, parent, false);
return new ViewHolder(view);
}
return null;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Message mex = listOfMessages.get(position);
holder.bind(mex);
}
#Override
public int getItemCount() {
return listOfMessages.size();
}
#Override
public int getItemViewType(int position) {
Message m = listOfMessages.get(position);
if(m.getSender().getIdUser().equals(loggedUser.getIdUser()))
return SENT;
else
return RECIVED;
}}
I really don't know how to make it work.
Every approach is welcome.
Thanks in advance.

Related

NestedRecyclerView's onBindViewHolder gettting called multiple times and not getting recycled

I am using nestedRecyclerView with Vertical parent recyclerview and horizontal child views. I have list of 10 items and each item contains a list for child recyclerview.
When I run the app, parent adapter's onbindviewholder getting called 10 items which is the total size of the parent list. I want to know why it is happening. It should call 4 or 5 onbindviewholder depending on the screen size but it is calling for all items which is unintended.
Parent Recyclerview initialization
homeRecyclerViewAdapter = new HomeRecyclerViewAdapter(getContext(), MR, (AppCompatActivity) getActivity());
homeRecyclerViewAdapter.setDataList(homeArrayList);
LinearLayoutManager mLinearLayoutManager3 = new LinearLayoutManager(getContext());
mLinearLayoutManager3.setOrientation(CustomLinearLayoutManager.VERTICAL);
homeRecyclerView.setLayoutManager(mLinearLayoutManager3);
homeRecyclerView.setAdapter(homeRecyclerViewAdapter);
Parent Adapter -
public class HomeRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List dataList;
private Context mContext;
Typeface MR;
static String PACKAGE_NAME;
Utils utils;
CustomClickEvents customClickEvents;
AppCompatActivity appCompatActivity;
public HomeRecyclerViewAdapter(Context mContext, Typeface MR, int type, CustomClickEvents customClickEvents) {
this.mContext = mContext;
this.MR = MR;
this.customClickEvents = customClickEvents;
utils = new Utils();
}
RecyclerView.RecycledViewPool pool;
public HomeRecyclerViewAdapter(Context mContext, Typeface MR, AppCompatActivity appCompatActivity) {
pool = new RecyclerView.RecycledViewPool();
this.mContext = mContext;
this.MR = MR;
this.appCompatActivity = appCompatActivity;
utils = new Utils();
}
public void setDataList(List upiAppsList) {
this.dataList = upiAppsList;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
PACKAGE_NAME = mContext.getPackageName();
if (viewType == Constants.HomePageViewTypes.TYPE_0) {
view = LayoutInflater.from(mContext).inflate(R.layout.recyclerview_layout, null);
return new RecyclerViewHolder(view);
} else if (viewType == Constants.HomePageViewTypes.TYPE_1) {
view = LayoutInflater.from(mContext).inflate(R.layout.recyclerview_layout, null);
return new RecyclerViewHolder(view);
} else if (viewType == Constants.HomePageViewTypes.TYPE_2) {
view = LayoutInflater.from(mContext).inflate(R.layout.recyclerview_layout, null);
return new RecyclerViewHolder(view);
}
return null;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder itemViewHolder, int position) {
Log.d("recyclerview", "HomeRecyclerViewHolder onbindviewholder + pos" + position);
Log.d("recyclerview", "HomeRecyclerViewHolder dataList.get(position):" + dataList.get(position));
RecyclerViewHolder holder = (RecyclerViewHolder) itemViewHolder;
holder.commonRecyclerViewAdapter.setDataList(((HomePageViewModel) dataList.get(position)).getDataList());
}
public void setbackgroundcolortoitem(int position, RecyclerViewHolder viewHolder, boolean is_checked) {
int modulus_position = position % 4;
Log.d("tag", "position ---- > " + modulus_position);
}
class RecyclerViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
RecyclerView mRecyclerView;
CommonRecyclerViewAdapter commonRecyclerViewAdapter;
public RecyclerViewHolder(View itemView) {
super(itemView);
mRecyclerView = itemView.findViewById(R.id.mrecyclerview);
mRecyclerView.setPadding((int) (8 * utils.getScreenDensity(mContext)), 0, 0, (int) (8 * utils.getScreenDensity(mContext)));
// mRecyclerView.requestDisallowInterceptTouchEvent(false);
commonRecyclerViewAdapter = new CommonRecyclerViewAdapter(mContext, MR, 6, appCompatActivity);
LinearLayoutManager mLinearLayoutManager3 = new LinearLayoutManager(appCompatActivity, LinearLayoutManager.HORIZONTAL, false) {
#Override
public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {
// force height of viewHolder here, this will override layout_height from xml
Log.d("upisdk", "HomeRecyclerView getwidth:" + getWidth());
lp.width = (int) (getWidth() / 1.6);
return true;
}
};
mLinearLayoutManager3.setOrientation(CustomLinearLayoutManager.HORIZONTAL);
mRecyclerView.setLayoutManager(mLinearLayoutManager3);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setAdapter(commonRecyclerViewAdapter);
}
#Override
public void onClick(View v) {
int position = getLayoutPosition();
switch (v.getId()) {
case R.id.mainlayout:
break;
}
}
}
#Override
public int getItemCount() {
return dataList.size();
}
#Override
public int getItemViewType(int position) {
if (dataList.get(position) instanceof HomePageViewModel) {
if (((HomePageViewModel) dataList.get(position)).getLayoutType() == Constants.HomePageViewTypes.TYPE_0) {
return Constants.HomePageViewTypes.TYPE_0;
}
}
return -1;
}
}
I want parent onbindviewholder to get called for the items displaying currently on screen which is the supposed behaviour of RecyclerView.
Is the parent View of the Parent RecycleView a ScrollView or NestedScrollView, bro? If true, please remove it

Unable to show the message list in left and right side of the chat room, always showing all the message in left side(receiver side)

I am using recyclerview to show all my message in my app. But it always showing the messages in the left side of the chat room.
How can I achieve this?
This is my code:
Here my adapter always goes to the else part and listing all the message in the left side of the screen(i.e) other user messages
Sample image
Adapter:
public class ChatMessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<Messages> msgDtoList;
private final List<Messages> mData;
public static final int VIEW_TYPE_SENDER = 1;
public static final int VIEW_TYPE_RECEIVER = 2;
public ChatMessageAdapter(List<Messages> msgDtoList) {
this.msgDtoList = msgDtoList;
mData = msgDtoList;
}
public void setData(#Nullable Messages[] data) {
msgDtoList.clear();
for (Messages model : data) {
if (model instanceof Messages) {
mData.add(model);
continue;
}
}
#Override
public int getItemViewType(int position) {
Messages msgDto = this.msgDtoList.get(position);
if (msgDto.getmSender().getId() == Profile.getInstance().getId()) {
return VIEW_TYPE_SENDER;
}
else {
return VIEW_TYPE_RECEIVER;
}
}
#Override
public int getItemCount() {
return msgDtoList.size();
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
View v;
if(mMessages.getmSender().getId() == Profile.getInstance().getId()) {
sentViewHolder(holder, position);
} else if (msgDto.getmSender().getId() != Profile.getInstance().getId()) {
receivedViewHolder(holder, position);
}
}
#SuppressLint("ResourceAsColor")
private void sentViewHolder(RecyclerView.ViewHolder holder, int position) {
Messages msgDto = this.msgDtoList.get(position);
((SenderMessageHolder)holder).rightMsgTextView.setText(msgDto.getmMSg());
((SenderMessageHolder)holder).mDateTimestamp.setText(msgDto.getmTimestamp());
((SenderMessageHolder)holder).rightMsgTextView.setTextColor(R.color.yellow);
((SenderMessageHolder)holder).rightMsgTextView.setGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
}
#SuppressLint("ResourceAsColor")
private void receivedViewHolder(RecyclerView.ViewHolder holder, int position) {
Messages msgDto = this.msgDtoList.get(position);
((ReceiverMessageHolder)holder).leftMsgTextView.setText(msgDto.getmMSg());
((ReceiverMessageHolder)holder).mDateTimestamp.setText(msgDto.getmTimestamp());
((ReceiverMessageHolder)holder).leftMsgTextView.setTextColor(R.color.red);
((ReceiverMessageHolder)holder).leftMsgTextView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Messages msgDto = this.msgDtoList.get(viewType);
if (msgDto.getmSender().getId() == Profile.getInstance().getId() ) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.sender_message_layout, parent, false);
return new SenderMessageHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.receiver_message_layout, parent, false);
return new ReceiverMessageHolder(view);
}
}
}
}
In ChatFragment.java
#Override
public void onViewCreated(View v, #Nullable Bundle b) {
super.onViewCreated(v, b);
mChat = (TextView) v.findViewById(R.id.chat_messages);
mSendMsgInputBox = (EditText) v.findViewById(R.id.chat_input_msg);
mSendMsg = (ImageView) v.findViewById(R.id.chat_send_msg);
mChatMessageRecyclerView = (RecyclerView) v.findViewById(R.id.recycler_view);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
linearLayoutManager.setStackFromEnd(true);
linearLayoutManager.setSmoothScrollbarEnabled(true);
mChatMessageRecyclerView.setLayoutManager(linearLayoutManager);
final List<Messages> msgDtoList = new ArrayList<LastMessageContent>();
int newMsgPosition = msgDtoList.size() - 1;
mChatMessageRecyclerView.scrollToPosition(0);
mChatMessageRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
mChatMessageRecyclerView.setHasFixedSize(true);
mChatMessageRecyclerView.setAdapter(mAdapter);
ArrayList<Messages> messagesList = new ArrayList<>();
for (int i=0;i<10;i++) {
// messagesList.add(new LastMessageContent( mSendMsgInputBox.getText().toString(), Profile.getInstance().getId() == mChatMessage.getmSender().getId() ? ChatMessageAdapter.VIEW_TYPE_SENDER : ChatMessageAdapter.VIEW_TYPE_RECEIVER));
}
List<List<Messages>> itemList = new ArrayList<>();
mSendMsg.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String msgContent = mSendMsgInputBox.getText().toString();
if(!TextUtils.isEmpty(msgContent))
{
Messages msgDto = mChatMessage.getmessages();
msgDtoList.add(msgDto);
itemList.add(msgDtoList);
mAdapter.notifyDataSetChanged();
if (mSendMsgInputBox.toString() != "") {
LastMessageContent msgDto1 = new LastMessageContent(mChatMessage.getmLastMessageContent().getmMSg(), mChatMessage.getId());
System.out.println("msgDto1msgDto1 ==" + msgDto1);
msgDtoList.add(msgDto1);
}
mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
#Override
public void onItemRangeInserted(int positionStart, int itemCount) {
super.onItemRangeInserted(positionStart, itemCount);
int messageCount = mAdapter.getItemCount();
int lastVisiblePosition =
linearLayoutManager.findLastCompletelyVisibleItemPosition();
// If the recycler view is initially being loaded or the
// user is at the bottom of the list, scroll to the bottom
// of the list to show the newly added message.
if (lastVisiblePosition == -1 ||
(positionStart >= (friendlyMessageCount - 1) &&
lastVisiblePosition == (positionStart - 1))) {
mChatMessageRecyclerView.scrollToPosition(positionStart);
}
}
});
}
}
you are checking your viewType twice. you should not check it again in onCreateViewHolder.
try something like this:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_SENDER) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.sender_message_layout, parent, false);
return new SenderMessageHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.receiver_message_layout, parent, false);
return new ReceiverMessageHolder(view);
}
}

How to change Recycler Item Color?

I have RecyclerView and I am inflating data. My requirement is that I have to show first item text in black color, the rest of the items should be in grey. And when user scroll then that grey color item should be black and its follower would be in grey again. I was thinking to this using position but I am confused.
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>
implements RecyclerViewFastScroller.BubbleTextGetter {
private List<String> mDataArray;
public RecyclerViewAdapter(List<String> dataset) {
mDataArray = dataset;
}
#Override
public int getItemCount() {
if (mDataArray == null)
return 0;
return mDataArray.size();
}
#Override
public RecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler_view_layout, parent, false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.mTextView.setText(mDataArray.get(position));
}
#Override
public String getTextToShowInBubble(int pos) {
if (pos < 0 || pos >= mDataArray.size())
return null;
String name = mDataArray.get(pos);
if (name == null || name.length() < 1)
return null;
return mDataArray.get(pos).substring(0, 1);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
#Bind(R.id.tv_alphabet)
public TextView mTextView;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
My updated code for onScrollIng and dynamic changing
#Override
public void onBindViewHolder(MessageAdapter.ViewHolder holder, int position) {
/*Tuesday, April 10, 2001 3:51 PM*/
msgModels = msgList.get(position);
if (msgList.get(position) != null) {
holder.msgTitle.setText(msgModels.getMessageTitle());
holder.msgDescription.setText(msgModels.getMessageDescription());
holder.msgDate.setText(msgModels.getMessageDate());
}
if (position == firstVisible) {
holder.msgTitle.setTextColor(Color.RED);
holder.msgDescription.setTextColor(Color.RED);
holder.msgDate.setTextColor(Color.RED);
}else{
holder.msgTitle.setTextColor(Color.BLUE);
holder.msgDescription.setTextColor(Color.BLUE);
holder.msgDate.setTextColor(Color.BLUE);
}
}
#Override
public int getItemCount() {
return msgList.size();
}
private int firstVisible = 0;
public void changeItem(int position){
firstVisible = position;
notifyItemChanged(firstVisible);
notifyDataSetChanged();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView msgTitle, msgDescription,msgDate;
public ViewHolder(View view) {
super(view);
msgTitle = (TextView)view.findViewById(R.id.msgTitle);
msgDescription = (TextView)view.findViewById(R.id.msgDescription);
msgDate = (TextView)view.findViewById(R.id.msgDate);
}
}
}
My Activity where I am calling Recycler onScroll
private ProgressDialog mProgressDialog = null;
private static final String API_MSG_CALL = "API_MSG_CALL";
private Map<String, String> mapobject;
private LinearLayoutManager linearLayoutManager;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_message, null);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_viewLaws);
mRecyclerView.setAdapter(msgAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
msgAdapter = new MessageAdapter(msgList, getActivity(), this);
linearLayoutManager = new LinearLayoutManager(getActivity());
//msgAdapter.notifyDataSetChanged();
mProgressDialog = new ProgressDialog(getActivity());
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int firstVisible = linearLayoutManager.findFirstCompletelyVisibleItemPosition();
msgAdapter.changeItem(firstVisible);
}
});
use holder.itemView.setBackgroundColor(...);
Example
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.itemView.setBackgroundColor(yourcolor);
holder.mTextView.setText(mDataArray.get(position));
}
Update:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
if(position==0){
holder.itemView.setBackgroundColor(Color.BLACK);
}else{
holder.itemView.setBackgroundColor(Color.GREY);
}
holder.mTextView.setText(mDataArray.get(position));
}
first in your main Activity use this code to find the first visible item:
recyclerview.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int firstVisible = linearLayoutManager.findFirstCompletelyVisibleItemPosition();
RecyclerViewAdapter.changeItem(firstVisible);
}
});
then in your Adapter add this method:
private int firstVisible = 0;
public void changeItem(int position){
firstVisible = position;
notifyItemChanged(firstVisible);
notifyDataSetChanged();
}
and in onBindViewHolder add this code:
if (position == firstVisible) {
holder.txtNumber.setBackgroundColor(mContext.getResources().getColor(R.color.black));
}
I just use a text as an example but you can change the cardview's background too

RecyclerView Adapter is laggy because of onBindViewHolder()

I have an app with a RecyclerView and an customized Adapter which gets that from a parse backend.
My adapter contains a few TextViews, a Button, a small ImageView. Some of my TextViews require a function (calculating upvote count and stuff like this) in my Adapter's onBindViewHolder() to get their data.
I've noticed that my scrolling is really slow and laggy because of the functions that are called in onBindViewHolder().
Where should I set my data and call my functions in order to get a smooth scrolling ?
EDIT 1:
Here's my Adapter Code:
public class PostAdapter extends RecyclerView.Adapter<PostAdapter.ViewHolder> {
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView postName;
public ImageView postCatIcon;
public TextView postDays;
public TextView postDaysLabel;
public TextView postHours;
public TextView postHoursLabel;
public TextView postMinutes;
public TextView postMinutesLabel;
public TextView postDistance;
public TextView postLikes;
public TextView postAuthor;
public LikeButton likepost;
private Post post;
public ViewHolder(View itemView) {
super(itemView);
postName = (TextView) itemView.findViewById(R.id.post_name);
postCatIcon = (ImageView) itemView.findViewById(R.id.post_cat_icon);
postDays = (TextView) itemView.findViewById(R.id.post_days);
postDaysLabel = (TextView) itemView.findViewById(R.id.post_days_label);
postHours = (TextView) itemView.findViewById(R.id.post_hours);
postHoursLabel = (TextView) itemView.findViewById(R.id.post_hours_label);
postMinutes = (TextView) itemView.findViewById(R.id.post_minutes);
postMinutesLabel = (TextView) itemView.findViewById(R.id.post_minutes_label);
postDistance = (TextView) itemView.findViewById(R.id.post_distance);
likePost = (LikeButton) itemView.findViewById(R.id.like_the_post);
postAuthor = (TextView) itemView.findViewById(R.id.post_author);
postLikes = (TextView) itemView.findViewById(R.id.post_likes);
}
public void setData(Post post){
this.post = post;
updateTimeRemaining(System.currentTimeMillis());
}
public void updateTimeRemaining(long currentTime) {
long diff = post.getDate().getTime() - currentTime;
int minutes = (int) ((diff / (1000*60)) % 60);
int hours = (int) ((diff / (1000*60*60)) % 24);
int days = (int) (diff / (1000*60*60*24));
String strgMinutes = "00";
String strgHours = "00";
String strgDays = "00";
if(minutes < 10)
strgMinutes = "0" + String.valueOf(minutes);
else
strgMinutes = String.valueOf(minutes);
if(hours < 10)
strgHours = "0" + String.valueOf(hours);
else
strgHours = String.valueOf(hours);
if(days < 10){
strgDays = "0" + String.valueOf(days);
}else
strgDays = String.valueOf(days);
postDays.setText(strgDays);
postHours.setText(strgHours);
postMinutes.setText(strgMinutes);
}
}
public static class ProgressViewHolder extends PostAdapter.ViewHolder {
public ProgressBar progressBar;
public ProgressViewHolder(View v) {
super(v);
progressBar = (ProgressBar) v.findViewById(R.id.progressBar);
}
}
private List<Post> mPosts;
private Context mContext;
private ListFragment mFragment;
private final int VIEW_ITEM = 1;
private final int VIEW_PROG = 0;
ArrayList<PostAdapter.ViewHolder> viewHoldersList;
private Handler handler = new Handler();
private Runnable updateRemainingTimeRunnable = new Runnable() {
#Override
public void run() {
long currentTime = System.currentTimeMillis();
synchronized (viewHoldersList) {
for (PostAdapter.ViewHolder holder : viewHoldersList) {
holder.updateTimeRemaining(currentTime);
}
}
handler.postDelayed(this, 60000);
}
};
// The minimum amount of items to have below your current scroll position before loading more.
private int visibleThreshold = 2;
private int lastVisibleItem, totalItemCount;
private boolean loading;
private OnLoadMoreListener onLoadMoreListener;
public PostAdapter(List<Post> posts, Context context, ListFragment fragment, RecyclerView recyclerView) {
mContext = context;
mPosts = posts;
mFragment = fragment;
if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!loading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
loading = true;
}
}
});
}
viewHoldersList = new ArrayList<>();
startUpdateTimer();
}
public PostAdapter(List<Post> posts, Context context, RecyclerView recyclerView) {
mContext = context;
mPosts = posts;
if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!loading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
loading = true;
}
}
});
}
viewHoldersList = new ArrayList<>();
startUpdateTimer();
}
private void startUpdateTimer() {
handler.postDelayed(updateRemainingTimeRunnable,60000);
}
public PostAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View postView;
ViewHolder viewHolder;
if (viewType == VIEW_ITEM) {
postView = inflater.inflate(R.layout.list_item, parent, false);
viewHolder = new ViewHolder(postView);
} else {
postView = inflater.inflate(R.layout.loading_item, parent, false);
viewHolder = new ProgressViewHolder(postView);
}
return viewHolder;
}
public void onBindViewHolder(final PostAdapter.ViewHolder viewHolder, final int position) {
if (viewHolder instanceof ViewHolder) {
final Post post = mPosts.get(position);
if(post != null){
synchronized (viewHoldersList) {
viewHolder.setData(post);
viewHoldersList.add(viewHolder);
}
Category cat = post.getCategory();
TextView nameTextView = viewHolder.postName;
TextView authorTextView = viewHolder.postAuthor;
final TextView likesTextView = viewHolder.postLikes;
ImageView CatIconView = viewHolder.postCatIcon;
final LikeButton likeButtonView = viewHolder.likePost;
TextView postDistance = viewHolder.postDistance;
ParseUser postAuthor = null;
if(mFragment != null) {
if (mFragment.getIfFilteredByDistance()) {
postDistance.setVisibility(View.VISIBLE);
Location postLocation = new Location("Post location");
if(post.getLocation() != null){
postLocation.setLongitude(post.getLocation().getLongitude());
postLocation.setLatitude(post.getLocation().getLatitude());
GPSTracker gpsTracker = new GPSTracker(mContext);
if (gpsTracker.canGetLocation()) {
Location myLocation = new Location("My location");
myLocation.setLatitude(gpsTracker.getLatitude());
myLocation.setLongitude(gpsTracker.getLongitude());
postDistance.setText((Math.round(myLocation.distanceTo(postLocation)) / 1000) + " km");
} else
gpsTracker.showSettingsAlert();
}
} else
postDistance.setVisibility(View.INVISIBLE);
}
try{
postAuthor = post.getAuthor().fetchIfNeeded();
}catch (ParseException e){
Log.e("Parse:", e.getMessage());
}
saveActivitiesToLocalDatastore(post);
final int likeCount = ((PostApplication)mContext).getPostLikeCount(post);
likesTextView.setText(String.valueOf(likeCount));
likeButtonView.setLiked(((PostApplication)mContext).getIfLiked(post));
viewHolder.itemView.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
Intent intent = new Intent(mContext, DetailActivity.class);
intent.putExtra("PostID",post.getObjectId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
});
likeButtonView.setOnLikeListener(new OnLikeListener() {
#Override
public void liked(LikeButton likeButton) {
if(((PostApplication)mContext).likePost(post))
likesTextView.setText(String.valueOf(Integer.parseInt(likesTextView.getText().toString()) + 1));
}
#Override
public void unLiked(LikeButton likeButton) {
if(((PostApplication)mContext).unlikePost(post))
likesTextView.setText(String.valueOf(Integer.parseInt(likesTextView.getText().toString()) -1));
}
});
nameTextView.setText(post.getTitle());
authorTextView.setText(post.getAuthor().getUsername());
CatIconView.setImageDrawable(ContextCompat.getDrawable(mContext,mContext.getResources().getIdentifier(cat.getIconName()+"_18dp","drawable",mContext.getPackageName())));
}
} else {
((ProgressViewHolder) viewHolder).progressBar.setIndeterminate(true);
}
}
public int getItemCount() {
return mPosts.size();
}
private void saveActivitiesToLocalDatastore(final Post post){
// Saves Likes to Parse local datastore
}
#Override
public int getItemViewType(int position) {
return mposts.get(position) != null ? VIEW_ITEM : VIEW_PROG;
}
public void setLoaded() {
loading = false;
}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
public interface OnLoadMoreListener {
void onLoadMore();
}
EDIT 2:
I followed this in order to fix my countdown's flickering:
Recyclerview with multiple countdown timers causes flickering
EDIT 3:
I tried running the app without the additionals methods like saveActivitiesInBackgroud, and the listeners and it works really smooth
If u want use RecyclerView u need to use onBindViewHolder. Its not problem with OnBindViewHolder. Problem is with ur spaghetti code and also u not store the informations, so this cause ur recyclerview to lags
Try not to change ViewHolder state outside of onBindViewHolder(). ViewHolder is representing view on screen but not representing data itself. RecyclerView tend to reuse views when they become invisible on screen and as result reuse ViewHolder attached to that view.
Never store ViewHolder - it's RecyclerView responsibility to manage them.
If you update your timer only once in 60 sec just notifyDataSetChanges instead of directly update ViewHolder. And update view state in onBindViewHolder with proper time.

Android Recyclerview load more on scrolltop

Hey community I want to make a RecyclerView like in the chat applications.
I tried setStackFromEnd and it's working good.
final LinearLayoutManager llm = new LinearLayoutManager(getApplicationContext());
llm.setOrientation(LinearLayoutManager.VERTICAL);
llm.setStackFromEnd(true);
My on load more code based on this tutorial, At the end of tutorial you should see a video : Recyclerview Bottom Progress
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
if (mOnLoadMoreListener != null) {
mOnLoadMoreListener.onLoadMore();
}
isLoading = true;
}
}
});
So I need to make a RecyclerView like in chat applications. Stack from end is working perfect but I need to trigger load more method when I go top instead of going bottom. Also add new items on top instead of bottom. Thanks in advance.
You don't need to make use of onScrollListener, if you simply want to show a Button "Load Earlier Messages" at the top. You can simply create an xml for your load more Button, make that as the first row of your RecyclerView and create an Interface to handle click events. I am posting code for the Adpater from one of my projects. Hope that will help you in getting some idea to proceed further.
/**
* Created by Mukesh on 21/12/2015.
*/
public class ChatListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
private List<UserMessage> userMessagesList;
private LayoutInflater mLayoutInflater;
private static final int ROW_TYPE_LOAD_EARLIER_MESSAGES = 0;
private static final int ROW_TYPE_SENDER = 1;
private static final int ROW_TYPE_RECEIVER = 2;
private int userId;
private boolean isLoadEarlierMsgs;
private LoadEarlierMessages mLoadEarlierMessages;
public ChatListAdapter(Context context, int userId, List<UserMessage> userMessagesList) {
mContext = context;
this.userMessagesList = userMessagesList;
mLayoutInflater = LayoutInflater.from(mContext);
this.userId = userId;
mLoadEarlierMessages = (LoadEarlierMessages) mContext;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case ROW_TYPE_LOAD_EARLIER_MESSAGES:
return new LoadEarlierMsgsViewHolder(mLayoutInflater.inflate(R.layout
.row_load_earlier_messages, parent, false));
case ROW_TYPE_SENDER:
return new SenderMsgViewHolder(mLayoutInflater.inflate(R.layout.row_sender_msg,
parent, false));
case ROW_TYPE_RECEIVER:
return new ReceiverMsgViewHolder(mLayoutInflater.inflate(R.layout
.row_receiver_msg, parent, false));
default:
return null;
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case ROW_TYPE_LOAD_EARLIER_MESSAGES:
LoadEarlierMsgsViewHolder loadEarlierMsgsViewHolder =
(LoadEarlierMsgsViewHolder) holder;
if (isLoadEarlierMsgs) {
loadEarlierMsgsViewHolder.btLoadEarlierMessages
.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mLoadEarlierMessages != null) {
mLoadEarlierMessages.onLoadEarlierMessages();
}
}
});
} else {
loadEarlierMsgsViewHolder.btLoadEarlierMessages.setVisibility(View.GONE);
}
break;
case ROW_TYPE_SENDER:
SenderMsgViewHolder senderMsgViewHolder = (SenderMsgViewHolder) holder;
// set data for your sender chat bubble
break;
case ROW_TYPE_RECEIVER:
ReceiverMsgViewHolder receiverMsgViewHolder = (ReceiverMsgViewHolder) holder;
// set data for your receiver chat bubble
break;
}
}
#Override
public int getItemCount() {
return userMessagesList.size() + 1;
}
#Override
public int getItemViewType(int position) {
if (position == 0) {
return ROW_TYPE_LOAD_EARLIER_MESSAGES; // row load earlier messages
} else if (userMessagesList.get(position - 1).getUser_id() == userId) {
return ROW_TYPE_SENDER; // sender row;
} else {
return ROW_TYPE_RECEIVER; // receiver row;
}
}
public interface LoadEarlierMessages {
void onLoadEarlierMessages();
}
public void setLoadEarlierMsgs(boolean isLoadEarlierMsgs) {
this.isLoadEarlierMsgs = isLoadEarlierMsgs;
}
static class LoadEarlierMsgsViewHolder extends RecyclerView.ViewHolder {
#Bind(R.id.btLoadEarlierMsgs) Button btLoadEarlierMessages;
public LoadEarlierMsgsViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
static class SenderMsgViewHolder extends RecyclerView.ViewHolder {
public SenderMsgViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
static class ReceiverMsgViewHolder extends RecyclerView.ViewHolder {
public ReceiverMsgViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
And finally implement the LoadEarlierMessages listener in your ChatActivity and Override the onLoadEarlierMessages() method
/**
* Created by Mukesh on 21/12/2015.
*/
public class ChatActivity extends AppCompatActivity
implements ChatListAdapter.LoadEarlierMessages {
// getting users recent messages and init RecyclerView
private void showUserMessages() {
// initialising LayoutManager for RecyclerView and setting that to RecyclerView
mLinearLayoutManager = new LinearLayoutManager(this);
mLinearLayoutManager.setStackFromEnd(true); // to start the list from bottom
rvChatsList.setLayoutManager(mLinearLayoutManager);
// initialising RecyclerView Adapter and setting that to the RecyclerView
mChatListAdapter = new ChatListAdapter(this, userId, userMessagesList);
rvChatsList.setAdapter(mChatListAdapter);
// getting count of friend/contact messages and toggling visibility of load more button accordingly
int count = mDataBaseHandler.getCountOfMessages(contactId);
if (count != 0) {
if (count > Constants.MESSAGES_LIMIT_FOR_LOCAL_DATABASE) {
mChatListAdapter.setLoadEarlierMsgs(true);
} else {
mChatListAdapter.setLoadEarlierMsgs(false);
}
userMessagesList.addAll(mDataBaseHandler.getAllMessagesOfContact(contactId));
mChatListAdapter.notifyDataSetChanged();
}
}
#Override
public void onLoadEarlierMessages() {
ArrayList<UserMessage> tempList = mDataBaseHandler
.getPreviousMessagesOfContact(contactId, userMessagesList.size());
if (tempList.size() > 0) {
if (tempList.size() < Constants.MESSAGES_LIMIT_FOR_LOCAL_DATABASE) {
mChatListAdapter.setLoadEarlierMsgs(false);
}
View v = rvChatsList.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
for (int i = 0; i < tempList.size(); i++) {
userMessagesList.add(0, tempList.get(i));
}
mChatListAdapter.notifyDataSetChanged();
mLinearLayoutManager.scrollToPositionWithOffset(tempList.size(), top);
} else {
mChatListAdapter.setLoadEarlierMsgs(false);
}
}
}
Hope this will help..!!

Categories

Resources