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.
Related
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.
I was following this tutorial to implement pagination, and I was following it keenly, but I am uncertain how I should modify my current activity to implement OnLoadMoreListener. I am using an ApiService from retrofit to load data, unlike the tutorial that generates random strings.
This is how my current RecyclerView adapter looks like:
public class CuratedSectionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static int VIEW_TYPE_HEADER = 0;
private static int VIEW_TYPE_ITEM = 1;
private static int VIEW_TYPE_LOADING = 2;
private int lastVisibleItem, totalItemCount;
private int visibleThreshold = 2;
private boolean isLoading;
private List<Object> itemList;
private OnLoadMoreListener onLoadMoreListener;
public CuratedSectionAdapter(RecyclerView recyclerView, List<Object> itemList) {
this.itemList = itemList;
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 (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
isLoading = true;
}
}
});
}
private class ItemViewHolder extends RecyclerView.ViewHolder {
RecyclerView itemRecyclerView;
CuratedSectionNestedAdapter nestedAdapter;
LinearLayoutManager layoutManager;
ItemViewHolder(View view) {
super(view);
itemRecyclerView = view.findViewById(R.id.recyclerView_nested);
layoutManager = new LinearLayoutManager(view.getContext(), LinearLayoutManager.HORIZONTAL, false);
}
}
private class HeaderViewHolder extends RecyclerView.ViewHolder {
TextView textViewHeader;
Typeface montserratMedium = Typeface.createFromAsset(getApplicationContext().getAssets(), "fonts/Montserrat-Medium.ttf");
HeaderViewHolder(View view) {
super(view);
textViewHeader = view.findViewById(R.id.textView_header);
textViewHeader.setTypeface(montserratMedium);
}
}
private class LoadingViewHolder extends RecyclerView.ViewHolder {
public ProgressBar progressBar;
public LoadingViewHolder(View view) {
super(view);
progressBar = (ProgressBar) view.findViewById(R.id.progress_bar);
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder.getItemViewType() == VIEW_TYPE_HEADER) {
HeaderViewHolder viewHolder = (HeaderViewHolder) holder;
CuratedSectionHeader header = (CuratedSectionHeader) itemList.get(position);
viewHolder.textViewHeader.setText(header.getHeaderName());
} else if (holder.getItemViewType() == VIEW_TYPE_ITEM) {
ItemViewHolder viewHolder = (ItemViewHolder) holder;
List<CuratedSectionItem> items = (List<CuratedSectionItem>) itemList.get(position);
if (viewHolder.nestedAdapter != null) {
viewHolder.nestedAdapter.setItems(items);
} else {
viewHolder.nestedAdapter = new CuratedSectionNestedAdapter(items);
viewHolder.itemRecyclerView.setLayoutManager(viewHolder.layoutManager);
viewHolder.itemRecyclerView.setAdapter(viewHolder.nestedAdapter);
}
} else if (holder.getItemViewType() == VIEW_TYPE_LOADING) {
LoadingViewHolder loadingViewHolder = (LoadingViewHolder) holder;
loadingViewHolder.progressBar.setIndeterminate(true);
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_HEADER) {
return new HeaderViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.main_explore_header_row, parent, false));
} else if (viewType == VIEW_TYPE_ITEM) {
return new ItemViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.main_explore_row, parent, false));
} else if (viewType == VIEW_TYPE_LOADING) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_loading, parent, false);
return new LoadingViewHolder(view);
}
throw new RuntimeException("Adapter " + viewType + "not found");
}
#Override
public int getItemCount() {
return itemList.size();
}
#Override
public int getItemViewType(int position) {
if (itemList.get(position) instanceof CuratedSectionHeader) {
return VIEW_TYPE_HEADER;
} else {
return itemList.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM;
}
}
public void setOnLoadMoreListener(OnLoadMoreListener mOnLoadMoreListener) {
this.onLoadMoreListener = mOnLoadMoreListener;
}
public void setLoaded() {
isLoading = false;
}
}
The current architecture of my activity uses private classes to make API calls and load the data. At the moment, all items are loaded at once, but ideally there should be only 2 items loaded at a time. I am uncertain how I should load additional items by calling the API service again when I scroll to the bottom. At the very least, I am certain that I will have to use curatedSectionAdapter.setOnLoadMoreListener somehow.
This is how I load all of the items at the moment:
private class Sections {
List<CuratedSection> sections = new ArrayList<>();
public Thread[] thread;
private Sections() {}
public void setSections(ArrayList<CuratedSection> sections) {
this.sections = sections;
}
public void setSectionStories(String sectionId, List<CuratedSectionItem> stories) {
for(CuratedSection s : sections){
if(s.id != null && s.id.equals(sectionId)) {
s.stories = stories;
}
}
}
public void loadStories(String sessionKey) {
thread = new Thread[sections.size()];
for( int s = 0; s < sections.size(); s++) {
thread[s] = new Thread(new LoadStories(sessionKey, sections.get(s)));
thread[s].start();
}
for( int f = 0; f < sections.size(); f++) {
try {
thread[f].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
recyclerView.setLayoutManager(layoutManager);
curatedSectionAdapter = new CuratedSectionAdapter(recyclerView, this.getAdapterInfo());
recyclerView.setAdapter(curatedSectionAdapter);
}
public void loadSections(int numSections) {
swipeRefreshLayout.setRefreshing(false);
LoadSections load = new LoadSections(numSections);
load.run();
}
public List<Object> getAdapterInfo() {
List<Object> list = new ArrayList<>();
for (int i = 0; i < sections.size(); i++) {
CuratedSection section = sections.get(i);
CuratedSectionHeader header = new CuratedSectionHeader();
header.setHeaderName(section.header);
list.add(header);
list.add(section.stories);
}
return list;
}
}
private class LoadSections implements Runnable {
int numSections;
LoadSections(int numSections) {
this.numSections = numSections;
}
#Override
public void run() {
SharedPreferences prefs = getSharedPreferences("user_session", MODE_PRIVATE);
final String sessionKey = prefs.getString("session_key", null);
Call<JsonArray> call;
call = TravelersApi.endpoint().getCuratedSections(sessionKey);
call.enqueue(new Callback<JsonArray>() {
#Override
public void onResponse(Call<JsonArray> call, Response<JsonArray> response) {
if(response.code() != 200) {
Toast.makeText(getApplicationContext(), "Cannot load page as of the moment.", Toast.LENGTH_SHORT).show();
return;
}
JsonArray rawSections = response.body();
if(rawSections.size() == 0) {
//TODO: show placeholder
return;
}
ArrayList<CuratedSection> sections = new ArrayList<>();
for(int i = 0; i < numSections; i++) {
JsonObject jSection = rawSections.get(i).getAsJsonObject();
final CuratedSection section = new CuratedSection();
section.id = jSection.get("id").getAsString();
section.header = jSection.get("section_header").getAsString();
section.topicCount = jSection.get("topic_count").getAsInt();
section.isShown = jSection.get("is_shown").getAsBoolean();
section.stories = new ArrayList<>();
sections.add(section);
}
curated.setSections(sections);
curated.loadStories(sessionKey);
spinner.clearAnimation();
spinner.setVisibility(View.GONE);
header.setVisibility(View.VISIBLE);
swipeRefreshLayout.setVisibility(View.VISIBLE);
}
#Override
public void onFailure(Call<JsonArray> call, Throwable t) {
Log.d("ERROR!", t.toString());
t.printStackTrace();
}
});
}
}
You can achieve this by directly setting scroll listener to your recycler view.
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 (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
// Call Load more method here to load next page data
// Prevent multiple calls by using a boolean
isLoading=true; // boolean to Prevent multiple calls
}
}
});
OnLoadMoreListener is a interface you need to register it first.
For pagination your server should process the pagination count. So you also need the api to perform on page limit .
Iam trying to implement Endless Infinite Scrolling with RecyclerView, but I am only getting all records and even not getting any progress while trying to scroll at bottom.
and see this my code
this is my News_Adapter
public class NewsAdapter extends RecyclerView.Adapter {
private final int VIEW_ITEM = 1;
private final int VIEW_PROG = 0;
private List<News> newsList;
private Context context;
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 5;
private int lastVisibleItem, totalItemCount;
private boolean loading;
private OnLoadMoreListener onLoadMoreListener;
public NewsAdapter(List<News> newsList , RecyclerView recyclerView){
this.newsList = newsList;
this.context = context;
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;
}
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
// When went to the end of the list, load more posts
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (linearLayoutManager.findLastVisibleItemPosition() >= linearLayoutManager.getItemCount() - 1) {
// Grow List
}
}
}
});
}
}
#Override
public int getItemViewType(int position){
//return newsList.get(position) instanceof News ? VIEW_ITEM : VIEW_PROG;
return (position >= newsList.size()) ? VIEW_PROG : VIEW_ITEM;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {
RecyclerView.ViewHolder vh;
if (viewType == VIEW_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.news_list_row, parent, false);
vh = new MyViewHolder(v);
} else {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.progress_item, parent, false);
vh = new ProgressViewHolder(v);
}
return vh;
}
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView title;
public ImageView image,nextArrowimage;
public TextView desc;
private News news;
public MyViewHolder(View view) {
super(view);
ProgressBar progressBar = null;
title = (TextView) view.findViewById(R.id.News_title);
image = (ImageView) view.findViewById(R.id.News_imageView);
nextArrowimage = (ImageView)view.findViewById(R.id.news_NextArrow);
desc = (TextView) view.findViewById(R.id.News_desc);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String title = news.getTitle();
String desc = news.getDesc();
String image = "http://bitstobyte.in/upload/"+news.getImage();
Intent intent = new Intent(context, News_Activity.class);
intent.putExtra("title", title);
intent.putExtra("desc",desc);
intent.putExtra("imageUrl", image);
context.startActivity(intent);
Toast.makeText(v.getContext(), "OnClick :" + news.getTitle() + " \n "+news.getImage(),Toast.LENGTH_SHORT).show();
}
});
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder,int position){
//if (holder instanceof MyViewHolder) {
// if (getItemViewType(position)==VIEW_ITEM) {
if (newsList.get(position)!=null && getItemViewType(position)==VIEW_ITEM ) {
final News single_news= newsList.get(position);
((MyViewHolder) holder).title.setText(single_news.getTitle());
((MyViewHolder) holder).desc.setText(single_news.getDesc());
((MyViewHolder) holder).nextArrowimage.setImageResource(R.drawable.nextblackbutton);
context = ((MyViewHolder) holder).image.getContext();
Picasso.with(context).load("http://bitstobyte.in/upload/"+single_news.getImage()).placeholder(R.drawable.ic_favorite_white_24dp).error(R.drawable.ic_map_24dp).resize(100,100).into(((MyViewHolder) holder).image);
((MyViewHolder) holder).news= single_news;
} else {
((ProgressViewHolder) holder).progressBar.setIndeterminate(true);
}
}
public void setLoaded() {
loading = false;
}
#Override
public int getItemCount() {
return newsList.size()+1;
}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
public static class ProgressViewHolder extends RecyclerView.ViewHolder {
public ProgressBar progressBar;
public ProgressViewHolder(View v) {
super(v);
progressBar = (ProgressBar)v.findViewById(R.id.progressBar1);
}
}
}
You are getting error at below line right ?
((ProgressViewHolder) holder).progressBar.setIndeterminate(true);
Can you tell why you have used two ViewHolders ?
FYI :You can't use two ViewHolder means can't extends two ViewHolder.
Just change this function:
#Override
public int getItemViewType(int position){
//return newsList.get(position) instanceof News ? VIEW_ITEM : VIEW_PROG;
return (position >= newsList.size() || newsList.get(position)==null) ? VIEW_PROG : VIEW_ITEM;
}
And
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder,int position){
if (getItemViewType(position)==VIEW_ITEM ) {
final News single_news= newsList.get(position);
((MyViewHolder) holder).title.setText(single_news.getTitle());
((MyViewHolder) holder).desc.setText(single_news.getDesc());
((MyViewHolder) holder).nextArrowimage.setImageResource(R.drawable.nextblackbutton);
context = ((MyViewHolder) holder).image.getContext();
Picasso.with(context).load("http://bitstobyte.in/upload/"+single_news.getImage()).placeholder(R.drawable.ic_favorite_white_24dp).error(R.drawable.ic_map_24dp).resize(100,100).into(((MyViewHolder) holder).image);
((MyViewHolder) holder).news= single_news;
} else {
((ProgressViewHolder) holder).progressBar.setIndeterminate(true);
}
}
I have faced a problem.
I am working with recyclerview. I want the list post get updated When users scroll down and when they scroll to the left, the program call the post deletion function. And by scrolling to the right,the program calls updating post function. I wrote codes below. Could you explain with examples how I can do this? I wrote these functions but I failed.
CustomListAdapterForPostOrga
public class CustomListAdapterForPostOrgan extends BaseAdapter {
private Activity activity;
private LayoutInflater inflater;
private List<Posts> post;
private int visibleThreshold = 5;
private int lastVisibleItem, totalItemCount;
private boolean loading;
private OnLoadMoreListener onLoadMoreListener;
ImageLoader imageLoader = AppController.getInstance().getImageLoader();
public CustomListAdapterForPostOrgan(Activity activity, List<Posts> post) {
this.activity = activity;
this.post = post;
}
public CustomListAdapterForPostOrgan(List<Posts> post, RecyclerView recyclerView) {
this.post = post;
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;
}
}
});
}}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
///////////////////
#Override
public int getCount() {
return post.size();
}
#Override
public Object getItem(int location) {
return post.get(location);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v=convertView;
if (inflater == null)
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null)
convertView = inflater.inflate(R.layout.list_row_organ_news
, null);
if (imageLoader == null)
imageLoader = AppController.getInstance().getImageLoader();
NetworkImageView thumbNail = (NetworkImageView) convertView
.findViewById(R.id.organ_news__avatar_show_thumbnail);
TextView titr = (TextView) convertView.findViewById(R.id.txt_organ_news_list_organ);
TextView year = (TextView) convertView.findViewById(R.id.txt_date_posts_organ);
TextView fi = (TextView) convertView.findViewById(R.id.txt_cunter_for_organ_posts);
// getting billionaires data for the row
Posts m = post.get(position);
// thumbnail image
thumbNail.setImageUrl(m.getAvatar(), imageLoader);
// Wealth Source
year.setText("Wealth Source: " + String.valueOf(m.getPublish_at()));
titr.setText(String.valueOf(m.getTitle()));
// release year
year.setText(String.valueOf(m.getPublish_at()));
fi.setText(String.valueOf(m.getView_count()));
return convertView;
}
public static class ProgressViewHolder extends RecyclerView.ViewHolder {
public ProgressBar progressBar;
public ProgressViewHolder(View v) {
super(v);
progressBar = (ProgressBar) v.findViewById(R.id.progressBar1);
}
}
in main function:
private void handelpost(final List<Post> posts, Page pages) {
if (pages.getCurrentPage() > 1) {
} else {
recycler_post.setLayoutManager(new LinearLayoutManager(this));
final AdapterRcycelerViewPostOrgan
madapter = new
AdapterRcycelerViewPostOrgan(posts);
recycler_post.setAdapter(madapter);
recycler_post.setItemAnimator(new DefaultItemAnimator());
madapter.setOnLoadMoreListener(new OnLoadMoreListener() {
#Override
public void onLoadMore() {
posts.add(null);
madapter.notifyItemInserted(posts.size() - 1);
handler.postDelayed(new Runnable() {
#Override
public void run() {
// remove progress item
posts.remove(posts.size() - 1);
madapter.notifyItemRemoved(posts.size());
//add items one by one
int start = posts.size();
int end = start + 20;
/* for (int i = start + 1; i <= end; i++) {
studentList.add(new Student("Student " + i, "AndroidStudent" + i + "#gmail.com"));
mAdapter.notifyItemInserted(studentList.size());
}*/
// posts.set()
//or you can add all at once but do not forget to call mAdapter.notifyDataSetChanged();
}
}, 2000);
Toast.makeText(getApplicationContext(),"ok",Toast.LENGTH_LONG).show();
}
});
}
I want to know how I can do this function on my own lists. Please help me.
In onScrolled callback you have dx and dy. If dy>0 list is being scrolled down and if dy<0 its being scrolled up. Same with dx, just different axis
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..!!