There is a delay in updating the list of Recycler views. When items in the list are small, they are processed quickly, but when items are large, delays occur. The model of all items is the same, but it does not seem to be possible to use methods like NotifyItemChanged because it expresses different information and number.
#Override
public void onFolderCoverImgClick(int position) {
setVideoTopBarByFolderData(position);
AppVideoData.FolderData folderData = videoLayoutFolderList.get(position);
String folderId = folderData.getFolderId();
videoByFolderList.clear();
for (int i = 0; i < videoLayoutList.size(); i++) {
if (folderId.equals(videoLayoutList.get(i).getFolderId())) {
videoByFolderList.add(videoLayoutList.get(i));
}
}
String path = getFilesDir() + File.separator + "vid";
for (int i = 0; i < videoByFolderList.size(); i++) {
if (new File(path + File.separator + videoByFolderList.get(i).getId() + ".mp4").exists()) {
videoByFolderList.get(i).setVideoType("Download");
} else {
videoByFolderList.get(i).setVideoType("vimeo");
}
if(videoIdF.equals(videoByFolderList.get(i).getId())) {
videoPosF = i;
}
}
videoPortraitAdapter.updateList(videoByFolderList);
videoPortraitAdapter.checkSelectedVideoWhenFolder(videoPosF, videoIdF);
}
If I click on a specific view, it will be called back to the above method, and clear the list that was originally in the recycler view and update the new list to be shown. The rest of the code is responsible for additional processing. I tried to calculate the time using the 'System.currentTimeMillis' method, but it does not seem to cause delays in additional code.
public class VideoItemAdapter extends RecyclerView.Adapter<VideoItemAdapter.VideoViewHolder> {
public VideoItemAdapterListener videoItemAdapterListener;
public VideoItemDrawDoneListener videoItemDrawDoneListener;
private Context context;
private LiveActivity liveActivity;
private List<AppVideoData.AppYouTube> dataList;
private String skinColor;
private boolean fullScreen;
private boolean isFolder;
private int videoPosF;
private String videoIdF;
public VideoItemAdapter(Context context, List<AppVideoData.AppYouTube> dataList, String skinColor, boolean fullScreen, boolean isFolder) {
this.context = context;
this.dataList = dataList;
this.skinColor = skinColor;
this.fullScreen = fullScreen;
if (context instanceof LiveActivity) {
liveActivity = (LiveActivity) context;
}
this.isFolder = isFolder;
}
public void updateList(List<AppVideoData.AppYouTube> dataList) {
this.dataList = dataList;
notifyDataSetChanged();
}
public void setOnVideoItemClickListener(VideoItemAdapterListener videoItemAdapterListener) {
this.videoItemAdapterListener = videoItemAdapterListener;
}
public void setOnVideoItemDrawDoneListener(VideoItemDrawDoneListener videoItemDrawDoneListener) {
this.videoItemDrawDoneListener = videoItemDrawDoneListener;
}
#NonNull
#Override
public VideoItemAdapter.VideoViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.video_item, parent, false);
VideoViewHolder holder = new VideoViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(#NonNull final VideoItemAdapter.VideoViewHolder holder, final int position) {
Log.d("time", "onBindViewHolder: " + System.currentTimeMillis());
int adapterPosition = 0;
if(holder.getAdapterPosition() != RecyclerView.NO_POSITION) { adapterPosition = holder.getAdapterPosition(); }
AppVideoData.AppYouTube videoData = dataList.get(adapterPosition);
String videoType = videoData.getVideoType();
Glide.with(context).load(videoData.getVideoThumbnail()).into(holder.itemThumbnail);
holder.itemSelected.setImageResource(R.drawable.shining_selected);
holder.itemTitle.setText(videoData.getTitle());
holder.itemDivider.setBackgroundColor(Color.parseColor(skinColor));
holder.itemDuration.setText(getDuration(videoData.getDuration()));
changeVideo(holder, adapterPosition, videoData.getId());
if (!videoType.equals("Download")) {
holder.itemDownload.setVisibility(View.VISIBLE);
holder.itemDelete.setVisibility(View.INVISIBLE);
} else {
holder.itemDownload.setVisibility(View.INVISIBLE);
holder.itemDelete.setVisibility(View.VISIBLE);
}
if(fullScreen) {
holder.itemDownload.setVisibility(View.INVISIBLE);
holder.itemDelete.setVisibility(View.INVISIBLE);
}
Log.d("time", "onBindViewHolder1: " + System.currentTimeMillis());
}
#Override
public int getItemCount() {
return (dataList != null) ? dataList.size() : 0;
}
private String getDuration(int original) {
int hour = original / 60 / 60;
int min = (original - (hour * 60 * 60)) / 60;
int sec = original - (hour * 60 * 60) - (min * 60);
return (hour > 0) ? String.format("%02d:%02d:%02d", hour, min, sec) : String.format("%02d:%02d", min, sec);
}
public void checkSelectedVideoWhenFolder(int videoPosF, String videoIdF) {
this.videoPosF = videoPosF;
this.videoIdF = videoIdF;
notifyItemChanged(videoPosF);
}
private void changeVideo(VideoViewHolder holder, int position, String videoId) {
if (!liveActivity.isFolder) {
if ((liveActivity.videoPos == position)) {
holder.itemSelected.setVisibility(View.VISIBLE);
} else {
holder.itemSelected.setVisibility(View.INVISIBLE);
}
} else {
if ((videoPosF == position) && videoIdF.equals(videoId)) {
holder.itemSelected.setVisibility(View.VISIBLE);
} else {
holder.itemSelected.setVisibility(View.INVISIBLE);
}
}
}
public class VideoViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
protected RelativeLayout itemBody;
protected TextView itemTitle;
protected TextView itemDuration;
protected TextView itemDivider;
protected ImageView itemThumbnail;
protected ImageView itemSelected;
protected ImageButton itemDownload;
protected ImageButton itemDelete;
public VideoViewHolder(View itemView) {
super(itemView);
itemBody = (RelativeLayout) itemView.findViewById(R.id.video_item_body);
itemTitle = (TextView) itemView.findViewById(R.id.video_item_title);
itemDuration = (TextView) itemView.findViewById(R.id.video_item_duration);
itemDivider = (TextView) itemView.findViewById(R.id.video_item_divider);
itemThumbnail = (ImageView) itemView.findViewById(R.id.video_item_thumbnail);
itemSelected = (ImageView) itemView.findViewById(R.id.video_item_selected);
itemDownload = (ImageButton) itemView.findViewById(R.id.video_item_download);
itemDelete = (ImageButton) itemView.findViewById(R.id.video_item_delete);
itemBody.setOnClickListener(this);
itemDownload.setOnClickListener(this);
itemDelete.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.video_item_body:
videoItemAdapterListener.onVideoItemClick(getAdapterPosition(), fullScreen, itemSelected);
break;
case R.id.video_item_download:
videoItemAdapterListener.onVideoDownloadClick(getAdapterPosition(), fullScreen);
break;
case R.id.video_item_delete:
videoItemAdapterListener.onVideoDeleteClick(getAdapterPosition(), fullScreen);
break;
}
}
}
This is a recycler view adapter class. In the onBindViewHolder method of the adapter, even if I calculate the time using System.currentTimeMillis, I can not see anything that delay could happen. It took 0.005 to 0.01 seconds. There is a place of doubt, but I do not know exactly where to check it. It looks like there is a difference of about 0.6 seconds between 'onFolderCoverImgClick' and onBindViewHolder.
videoPortraitAdapter is an instance of VideoItemAdapter. If you know anyone, I would appreciate it if you could.
You can use new ListAdapter instead of RecyclerView.Adapter which provides the submitList() method which manages the differences in the lists and updates those only. Refer this link.
Better alternative option is by using setting up swipe to refresh and use
mSwipeRefreshLayout.setRefreshing(false); - in place where updating the adapter is done or NotifyItemChanged.
This has always worked for me, hope this helps you.
Related
The problem is that in my tablayout when im switching between tabs my list duplicating. So i need to remove list on onStop() to recreate it then. Or might be other better solution.
I have tried the following solutions
https://code-examples.net/en/q/1c97047
How to reset recyclerView position item views to original state after refreshing adapter
Remove all items from RecyclerView
My code of adapter
public class OnlineUsersAdapter extends RecyclerView.Adapter<OnlineUsersAdapter.OnlineUserViewHolder> {
private List<OnlineUser> onlineUsers = new ArrayList<>();
private OnItemClickListener.OnItemClickCallback onItemClickCallback;
private OnItemClickListener.OnItemClickCallback onChatClickCallback;
private OnItemClickListener.OnItemClickCallback onLikeClickCallback;
private Context context;
public OnlineUsersAdapter(Context context) {
this.onlineUsers = new ArrayList<>();
this.context = context;
}
#NonNull
#Override
public OnlineUsersAdapter.OnlineUserViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_user, parent, false);
return new OnlineUsersAdapter.OnlineUserViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull OnlineUsersAdapter.OnlineUserViewHolder holder, int position) {
OnlineUser user = onlineUsers.get(position);
Log.d("testList", "rating " + user.getRating() + " uid " + user.getUid());
holder.bind(user, position);
}
#Override
public int getItemCount() {
return onlineUsers.size();
}
class OnlineUserViewHolder extends RecyclerView.ViewHolder {
RelativeLayout container;
ImageView imageView, likeBtn, chatBtn;
TextView name, country;
private LottieAnimationView animationView;
OnlineUserViewHolder(View itemView) {
super(itemView);
context = itemView.getContext();
container = itemView.findViewById(R.id.item_user_container);
imageView = itemView.findViewById(R.id.user_img);
likeBtn = itemView.findViewById(R.id.search_btn_like);
chatBtn = itemView.findViewById(R.id.search_btn_chat);
name = itemView.findViewById(R.id.user_name);
country = itemView.findViewById(R.id.user_country);
animationView = itemView.findViewById(R.id.lottieAnimationView);
}
void bind(OnlineUser user, int position) {
ViewCompat.setTransitionName(imageView, user.getName());
if (FirebaseUtils.isUserExist() && user.getUid() != null) {
new FriendRepository().isLiked(user.getUid(), flag -> {
if (flag) {
likeBtn.setBackground(ContextCompat.getDrawable(context, R.drawable.ic_favorite));
animationView.setVisibility(View.VISIBLE);
} else {
likeBtn.setBackground(ContextCompat.getDrawable(context, R.drawable.heart_outline));
animationView.setVisibility(View.GONE);
}
});
}
if (user.getUid() != null) {
chatBtn.setOnClickListener(new OnItemClickListener(position, onChatClickCallback));
likeBtn.setOnClickListener(new OnItemClickListener(position, onLikeClickCallback));
}
imageView.setOnClickListener(new OnItemClickListener(position, onItemClickCallback));
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
if (user.getImage().equals(Consts.DEFAULT)) {
Glide.with(context).load(context.getResources().getDrawable(R.drawable.default_avatar)).into(imageView);
} else {
Glide.with(context).load(user.getImage()).thumbnail(0.5f).into(imageView);
}
country.setText(user.getCountry());
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f).setDuration(500);
animator.addUpdateListener(valueAnimator ->
animationView.setProgress((Float) valueAnimator.getAnimatedValue()));
if (animationView.getProgress() == 0f) {
animator.start();
} else {
animationView.setProgress(0f);
}
}
}
public OnlineUsersAdapter(OnItemClickListener.OnItemClickCallback onItemClickCallback,
OnItemClickListener.OnItemClickCallback onChatClickCallback,
OnItemClickListener.OnItemClickCallback onLikeClickCallback) {
this.onItemClickCallback = onItemClickCallback;
this.onChatClickCallback = onChatClickCallback;
this.onLikeClickCallback = onLikeClickCallback;
}
public void addUsers(List<OnlineUser> userList) {
int initSize = userList.size();
onlineUsers.addAll(userList);
// notifyItemRangeInserted(onlineUsers.size() - userList.size(), onlineUsers.size());
}
public String getLastItemId() {
return onlineUsers.get(onlineUsers.size() - 1).getUid();
}
public void clearData() {
List<OnlineUser> data = new ArrayList<>();
addUsers(data);
notifyDataSetChanged();
}
My code in fragment
#Override
public void onStop() {
super.onStop();
firstUid = "";
stopDownloadList = false;
List<OnlineUser> list = new ArrayList<>();
mAdapter.addUsers(list);
mAdapter.notifyDataSetChanged();
}
`users are added after callback
#Override
public void addUsers(List<OnlineUser> onlineUsers) {
if (firstUid.equals("")){
firstUid = onlineUsers.get(0).getUid();
}
if (!firstUid.equals("") && onlineUsers.contains(firstUid)){
stopDownloadList = true;
}
if (!stopDownloadList){
mAdapter.addUsers(onlineUsers);
}
setRefreshProgress(false);
isLoading = false;
isMaxData = true;
}
The line mAdapter.addUsers(onlineUsers); from addUsers method gets called twice. Looks like your asynchronous operation gets triggered twice (e. g. from repeating lifecycle methods like onCreate/onCreateView/onViewCreated).
Solution #1: request users a single time
Move your user requesting machinery to onCreate or onAttach. This will save network traffic but could lead to showing outdated data.
Solution #2: replaceUsers
Your clearData calls mAdapter.addUsers(new ArrayList<>()); (btw, take a look at Collections.emptyList()). Looks like you're trying to replace adapter data but appending instead. Replacement method could look like
public void replaceUsers(List<OnlineUser> userList) {
int oldSize = userList.size();
onlineUsers = userList;
notifyItemRangeRemoved(0, oldSize);
notifyItemRangeInserted(0, userList.size);
}
This version still requeses users every time your fragment gets focused but shows fresher data.
In my project, there is need of searching data from server using keyword. After search, i am displaying results using RecyclerView . While searching fast, the data in RecyclerView is duplicating. If searching slowly, it's working fine. Any suggestions are appreciated. Thank you.
The below code for making server call:
private void callSearchUserApi(final String searchText, int currentPage, boolean clearData) {
isApiCallInProcess = true;
String URL = "userinfo/api/v1/user-search/" + "?page=" + currentPage;
if (!Connectivity.isConnected(activity)) {
Common.snackBarNoConnection(activity, activity.getString(R.string.no_conection));
//setOnProgressbarVisibility(View.GONE);
return;
}
if (clearData) {
globalSearchUsersModelList.clear();
//BS globalSearchUserResultsAdapter.notifyDataSetChanged();
}
ApiInterface apiCall = ApiClient.getApiService(activity);
final Call<SearchUsersModel> globalUserSearchApiCall = apiCall.searchUser(
URL,
searchText);
globalUserSearchApiCall.enqueue(new Callback<SearchUsersModel>() {
#Override
public void onResponse(Call<SearchUsersModel> call, Response<SearchUsersModel> response) {
if (response.isSuccessful() && response.body().getStatus().equalsIgnoreCase(Common.SUCCESS_RESPONSE)) {
//BS globalSearchUsersModelList.addAll(response.body().getData().getData());
for (int i = 0; i < response.body().getData().getData().size(); i++) {
SearchUsersModel.DataBeanX.DataBean dataBean = new SearchUsersModel.DataBeanX.DataBean();
dataBean.setDesignation(response.body().getData().getData().get(i).getDesignation());
dataBean.setFull_name(response.body().getData().getData().get(i).getFull_name());
dataBean.setGender(response.body().getData().getData().get(i).getGender());
dataBean.setId(response.body().getData().getData().get(i).getId());
dataBean.setPlace(response.body().getData().getData().get(i).getPlace());
dataBean.setProfile_pic(response.body().getData().getData().get(i).getProfile_pic());
globalSearchUsersModelList.add(dataBean);
/*BS if (!globalSearchUsersModelList.contains(response.body().getData().getData().get(i)))
globalSearchUsersModelList.add(response.body().getData().getData().get(i));*/
}
CURRENT_PAGE = response.body().getData().getPage();
isLoading = false;
if (response.body().getData().isNext() == false)
isLastPage = true;
else
isLastPage = false;
if (globalSearchUsersModelList.size() == 0) {
rv_GlobalsearchList.setVisibility(View.GONE);
rl_placeholderGSPeople.setVisibility(View.VISIBLE);
tv_placeholderGSPeople.setText(activity.getString(R.string.no_search_found) + " " + searchText);
} else {
rv_GlobalsearchList.setVisibility(View.VISIBLE);
rl_placeholderGSPeople.setVisibility(View.GONE);
}
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
globalSearchUserResultsAdapter.notifyDataSetChanged();
}
});
}
if (searchTextsList.size() > 0) {
String sText = searchTextsList.get(0);
searchTextsList.remove(0);
callSearchUserApi(sText, FIRST_PAGE, true);
} else
isApiCallInProcess = false;
}
#Override
public void onFailure(Call<SearchUsersModel> call, Throwable t) {
isApiCallInProcess = false;
}
});
}
This is my Adapter.
public class GlobalSearchUserResultsAdapter extends RecyclerView.Adapter<GlobalSearchUserResultsAdapter.SearchViewHolder> {
private Context context;
private List<SearchUsersModel.DataBeanX.DataBean> searchUserList;
public GlobalSearchUserResultsAdapter(Context context, List<SearchUsersModel.DataBeanX.DataBean> searchUserList){
this.context = context;
this.searchUserList = searchUserList;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
#Override
public SearchViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.v("Search", "Adapter Activity : "+context);
View view = LayoutInflater.from(context).inflate(R.layout.global_search_row, parent, false);
return new GlobalSearchUserResultsAdapter.SearchViewHolder(view);
}
#Override
public void onBindViewHolder(GlobalSearchUserResultsAdapter.SearchViewHolder holder, int position) {
if ( searchUserList.get(position).getGender().equals("M")) {
holder.iv_userImage.setBackgroundResource(R.drawable.white_border_with_circle_appblue);
Common.setGlideImage((GlobalSearchActivity)context,
holder.iv_userImage,
/*searchUsersModel*/searchUserList.get(position).getProfile_pic(),
R.drawable.male,
true);
} else if (searchUserList.get(position).getGender().equals("F")) {
holder.iv_userImage.setBackgroundResource(R.drawable.white_border_with_circle_pink);
Common.setGlideImage((GlobalSearchActivity)context,
holder.iv_userImage,
searchUserList.get(position).getProfile_pic(),
R.drawable.female,
true);
} else {
Common.setGlideImage((GlobalSearchActivity)context,
holder.iv_userImage,
searchUserList.get(position).getProfile_pic(),
R.drawable.deafult_profilepic,
true);
}
holder.tv_userName.setText(searchUserList.get(position).getFull_name());
holder.tv_userName.setTypeface(Common
.getFontTypeface(context, GlobalConstants.FONT_AVENIR_MEDIUM));
holder.tv_place.setText(searchUserList.get(position).getPlace());
holder.tv_place.setTypeface(Common
.getFontTypeface(context, GlobalConstants.FONT_AVENIR_MEDIUM));
holder.designation.setText(searchUserList.get(position).getDesignation());
}
#Override
public int getItemCount() {
return searchUserList.size();
}
public class SearchViewHolder extends RecyclerView.ViewHolder{
private ImageView iv_userImage;
private TextView tv_userName;
private TextView tv_place;
private TextView designation;
public SearchViewHolder(View itemView) {
super(itemView);
this.iv_userImage = (ImageView) itemView.findViewById(R.id.imageSearch);
this.tv_userName = (TextView) itemView.findViewById(R.id.nameSearch);
tv_userName.setTypeface(Common.getFontTypeface(context,
GlobalConstants.FONT_AVENIR_MEDIUM));
this.designation = (TextView) itemView.findViewById(R.id.designation);
designation.setTypeface(Common.getFontTypeface(context,
GlobalConstants.FONT_AVENIR_MEDIUM));
this.tv_place = (TextView) itemView.findViewById(R.id.placeSearch);
tv_place.setTypeface(Common.getFontTypeface(context,
GlobalConstants.FONT_AVENIR_LIGHT));
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
context.startActivity(new Intent(context, ProfileActivity.class)//ThirdParty
.putExtra(GlobalConstants.KEY_THIRD_PARTY_ID, searchUserList.get(getAdapterPosition()).getId()));
}
});
}
}
}
You just had to clear the globalSearchUsersModelList list just before for loop, because API call is asynchronous.
globalSearchUsersModelList.clear();// Important one
for (int i = 0; i < response.body().getData().getData().size(); i++) {
// do your stuff
}
I think the issue come from your getItemId implementation. The way you implement it, the recycler view will identify an item according to its position in the list.
If you change this and use unique identification for instance searchUserList.get(position).id (if your User object has an unique ID) the problem should be fixed
You can also add in your activity adapter.setHasStableIds(true)
I have implemented recyclerview and it loads 10 tasks at a time. Once every task is completed, I am removing the task with a completed button. It loads the 11th item if I scroll to the bottom at one condition there has to be atleast one uncompleted task present on the page. If I clear all 10 items it just shows me loading animation and nothing happens. I have to close the app and open again to load the tasks again.
I have implemented a PaginationActivity, PaginationAdapter and scroll listener for the page. It loads data from a local database whenever you have reached the end of the page and you have got more tasks to load.
PaginationActivity.java
public class PaginationActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
PaginationAdapter adapter;
LinearLayoutManager linearLayoutManager;
public static ArrayList<String> reasonsdata = new ArrayList<String>();
public static String rslt="";
public static String[] reasons;
public static boolean chk = true;
public static String payTypeSelected;
public static String reasonSelected;
public static HashMap<String, EditText> drivernotesMap = new HashMap<String, EditText>();
public static HashMap<String, EditText> paynoteMap = new HashMap<String, EditText>();
public static HashMap<String, String> paytypeMap = new HashMap<String, String>();
RecyclerView rv;
ProgressBar progressBar;
private static final int PAGE_START = 0;
private boolean isLoading = false;
private boolean isLastPage = false;
private int TOTAL_PAGES;
private int currentPage = PAGE_START;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pagination_main);
fillspinner();
rv = (RecyclerView) findViewById(R.id.main_recycler);
progressBar = (ProgressBar) findViewById(R.id.main_progress);
adapter = new PaginationAdapter(this);
linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
rv.setLayoutManager(linearLayoutManager);
rv.setItemAnimator(new DefaultItemAnimator());
rv.setAdapter(adapter);
rv.addOnScrollListener(new PaginationScrollListener(linearLayoutManager) {
#Override
protected void loadMoreItems() {
isLoading = true;
currentPage += 1;
// mocking network delay for API call
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
loadNextPage();
}
}, 1000);
}
#Override
public int getTotalPageCount() {
return TOTAL_PAGES;
}
#Override
public boolean isLastPage() {
return isLastPage;
}
#Override
public boolean isLoading() {
return isLoading;
}
});
// mocking network delay for API call
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
loadFirstPage();
}
}, 1000);
}
private void fillspinner() {
reasonsdata.clear();
try
{
rslt="START";
CallReasons cr = new CallReasons();
cr.join();
cr.start();
while(rslt=="START") {
try {
Thread.sleep(10);
}catch(Exception ex) {
}
}
}
catch (Exception ex) {
ex.printStackTrace();
}
if(rslt == "Success"){
reasonsdata.add("<--- Select Reason --->");
for(int i =0; i< reasons.length;i++){
reasonsdata.add(reasons[i]);
}
}
}
public void loadFirstPage() {
Log.d(TAG, "loadFirstPage: ");
TOTAL_PAGES = Run.getTotalPages();
//List<Run> runs = Run.createRuns(adapter.getItemCount());
List<Run> runs = Run.createRuns(adapter.getMaxID());
progressBar.setVisibility(View.GONE);
adapter.addAll(runs);
if (currentPage <= TOTAL_PAGES) adapter.addLoadingFooter();
else isLastPage = true;
}
public void loadNextPage() {
Log.d(TAG, "loadNextPage: " + currentPage);
//List<Run> runs = Run.createRuns(adapter.getItemCount());
List<Run> runs = Run.createRuns(adapter.getMaxID());
adapter.removeLoadingFooter();
isLoading = false;
adapter.addAll(runs);
if (currentPage != TOTAL_PAGES) adapter.addLoadingFooter();
else isLastPage = true;
}
}
PaginationAdapter.java
public class PaginationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int ITEM = 0;
private static final int LOADING = 1;
private List<Run> runs = new ArrayList<>();
private Context context;
private boolean isLoadingAdded = false;
private String cash = "CASH";
private String cheque = "CHEQUE";
SQLiteDatabase db;
//PaginationActivity func_call;
public PaginationAdapter(Context context) {
this.context = context;
runs = new ArrayList<>();
//func_call = new PaginationActivity();
}
public List<Run> getRuns() {
return runs;
}
public void setRuns(List<Run> runs) {
this.runs = runs;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder = null;
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
switch (viewType) {
case ITEM:
viewHolder = getViewHolder(parent, inflater);
break;
case LOADING:
View v2 = inflater.inflate(R.layout.item_progress, parent, false);
viewHolder = new LoadingVH(v2);
//viewHolder = getViewHolder(parent, inflater);
//func_call.loadNextPage();
break;
}
return viewHolder;
}
#NonNull
private RecyclerView.ViewHolder getViewHolder(ViewGroup parent, LayoutInflater inflater) {
RecyclerView.ViewHolder viewHolder;
View v1 = inflater.inflate(R.layout.activity_main, parent, false);
viewHolder = new RunVH(v1);
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final Run run = runs.get(position);
switch (getItemViewType(position)) {
case ITEM:
final RunVH runVH = (RunVH) holder;
//adding text areas to show data
//Completed task button
runVH.btnSave.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(context, "Mark as Completed", Toast.LENGTH_LONG).show();
remove(run);
}
}
} catch (Exception ex) {
//pbbar.setVisibility(View.GONE);
ex.printStackTrace();
}
}
});
break;
case LOADING:
break;
}
}
#Override
public int getItemCount() {
return runs == null ? 0 : runs.size();
}
//#Override
public int getMaxID() {
if(runs == null || runs.size() == 0){
return 0;
}else{
Run test = runs.get(runs.size()-2);
int maxid = (int) test.getProperty(0);
return maxid;
}
}
#Override
public int getItemViewType(int position) {
return (position == runs.size() - 1 && isLoadingAdded) ? LOADING : ITEM;
}
public void add(Run run) {
runs.add(run);
notifyItemInserted(runs.size() - 1);
}
public void addAll(List<Run> runList) {
for (Run run : runList) {
add(run);
}
}
public void remove(Run run) {
int position = runs.indexOf(run);
if (position > -1) {
runs.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, runs.size());
}
/*if(runs.size() < 0){
clear();
func_call.loadNextPage();
}*/
}
public void clear() {
isLoadingAdded = false;
while (getItemCount() > 0) {
remove(getItem(0));
}
}
public boolean isEmpty() {
return getItemCount() == 0;
}
public void addLoadingFooter() {
isLoadingAdded = true;
add(new Run());
}
public void removeLoadingFooter() {
isLoadingAdded = false;
int position = runs.size() - 1;
Run item = getItem(position);
if (item != null) {
runs.remove(position);
notifyItemRemoved(position);
}
}
public Run getItem(int position) {
return runs.get(position);
}
protected class RunVH extends RecyclerView.ViewHolder {
private TextView textEsky;
private TextView textAdjusted;
private TextView textAddress;
private TextView textNew;
private TextView textTrusted;
private TextView textName;
private TextView textMobile;
private TextView textHome;
private TextView textWork;
private TextView payType;
private EditText textPayNote;
private EditText textDeliveryInst;
private EditText textDriverNotes;
private final Spinner reasons;
private RadioGroup rg;
private Button btnSave;
private CheckBox IsDriverData;
public RunVH(View itemView) {
super(itemView);
textEsky = (TextView) itemView.findViewById(R.id.idEsky);
textAdjusted = (TextView) itemView.findViewById(R.id.idAdjusted);
textAddress = (TextView) itemView.findViewById(R.id.idAddress);
textNew = (TextView) itemView.findViewById(R.id.idNew);
textTrusted = (TextView) itemView.findViewById(R.id.idTrusted);
textName = (TextView) itemView.findViewById(R.id.idName);
textMobile = (TextView) itemView.findViewById(R.id.idMobile);
textHome = (TextView) itemView.findViewById(R.id.idHome);
textWork = (TextView) itemView.findViewById(R.id.idWork);
payType = (TextView) itemView.findViewById(R.id.idPayType);
textPayNote = (EditText) itemView.findViewById(R.id.idPayNoteText);
textDeliveryInst = (EditText) itemView.findViewById(R.id.idDeliInsText);
textDriverNotes = (EditText) itemView.findViewById(R.id.idDriverNotesText);
IsDriverData = (CheckBox) itemView.findViewById(R.id.idCheckBox);
reasons = (Spinner) itemView.findViewById(R.id.idReason);
rg = (RadioGroup) itemView.findViewById(R.id.Radiogroup);
btnSave = (Button) itemView.findViewById(R.id.button);
}
}
protected class LoadingVH extends RecyclerView.ViewHolder {
public LoadingVH(View itemView) {
super(itemView);
}
}
}
PaginationScrollListener.java
public abstract class PaginationScrollListener extends RecyclerView.OnScrollListener {
LinearLayoutManager layoutManager;
public PaginationScrollListener(LinearLayoutManager layoutManager) {
this.layoutManager = layoutManager;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
if (!isLoading() && !isLastPage()) {
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount
&& firstVisibleItemPosition >= 0) {
loadMoreItems();
}
}
}
protected abstract void loadMoreItems();
public abstract int getTotalPageCount();
public abstract boolean isLastPage();
public abstract boolean isLoading();
}
I could be wrong, but it looks to me like the only time you call loadNextPage() is when the RecyclerView gets a scroll event. Removing ViewHolders doesn't cause scroll events iirc.
If you register an AdapterDataObserver on the PaginationAdapter, you can listen in on removal/add events and trigger loads when there aren't any tasks left. You could probably do the same thing with some small changes to that commented-out code in remove(Run):
if (runs.isEmpty()) {
func_call.loadNextPage();
}
EDIT:
Btw, you see where you called adapter = new PaginationAdapter(this); in the PaginationActivity.onCreate method? The this here is your PaginationActivity, which means you can assign it to a PaginationAdapter field like so:
/**
* #param context the activity this adapter will appear in
*/
public PaginationAdapter(Context context) {
this.context = context;
runs = new ArrayList<>();
func_call = (PaginationActivity)context;
}
That should be enough to prevent crashes. For better separation of concerns, you'll probably want to switch over to AdapterDataObserver-based logic (I think this is called the open/closed principle? Maybe?).
I have a list that I add Players too and the Player names are based off the size of the list. When I initially set the player list the names are correct but when I add a new Player to the list the name is incorrect. Please help below is the relevant code.
The method that adds the Players to the List:
public static void addNewPlayers(List<Player> playerArrayList, SharedPreferences sharedPreferences,
PlayerScoreCardAdapter playerScoreCardAdapter,
int numberOfPlayersToAdd) {
for (int i = 0; i < numberOfPlayersToAdd; i++) {
// TODO: 2016-03-19 Fix player name not updating
String name = String.format("%s%s",
Keys.KEY_DEFAULT_PLAYER_NAME, playerArrayList.size() + 1);
Player player = new Player(name,
sharedPreferences.getInt(Keys.KEY_PLAYER_SCORE + String.valueOf(i + 1),
Keys.KEY_DEFAULT_PLAYER_SCORE));
playerArrayList.add(i, player);
playerScoreCardAdapter.notifyItemInserted(playerArrayList.size());
}
}
The RecylcerView.Adapter:
public class PlayerScoreCardAdapter extends RecyclerView.Adapter<PlayerScoreCardAdapter.ScoreViewHolder> {
private List<Player> mPlayerList;
public PlayerScoreCardAdapter(List<Player> playerList) {
mPlayerList = playerList;
}
#Override
public ScoreViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.player_score_list_item, parent, false);
return new ScoreViewHolder(view);
}
#Override
public void onBindViewHolder(ScoreViewHolder holder, int position) {
holder.bindPlayers(mPlayerList.get(position));
}
#Override
public int getItemCount() {
return mPlayerList.size();
}
public class ScoreViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
public TextView mPlayerName;
public EditText mPlayerScore;
public Button mMinusButton, mAddButton;
public ScoreViewHolder(View itemView) {
super(itemView);
mPlayerName = (TextView) itemView.findViewById(R.id.textView_player_name);
mPlayerScore = (EditText) itemView.findViewById(R.id.editText_player_score);
mMinusButton = (Button) itemView.findViewById(R.id.button_minus);
mAddButton = (Button) itemView.findViewById(R.id.button_add);
mMinusButton.setOnClickListener(this);
mAddButton.setOnClickListener(this);
mPlayerName.setOnLongClickListener(this);
}
/**
* Binds the data from the player to the appropriate views.
*
* #param player The player data to be bound.
*/
public void bindPlayers(Player player) {
mPlayerName.setText(player.getName());
mPlayerScore.setText(String.valueOf(player.getScore()));
}
#Override
public void onClick(View v) {
int id = v.getId();
int updatedScoreCount = mPlayerList.get(getAdapterPosition()).getScore();
switch (id) {
case R.id.button_minus:
updatedScoreCount--;
if (updatedScoreCount < 0) {
Toast.makeText(itemView.getContext(), R.string.toast_text_score_to_low, Toast.LENGTH_LONG).show();
return;
} else {
updatePlayerScore(updatedScoreCount);
notifyItemChanged(getAdapterPosition());
}
break;
case R.id.button_add:
updatedScoreCount++;
if (updatedScoreCount >= 9999) {
Toast.makeText(itemView.getContext(), R.string.toast_text_score_to_high, Toast.LENGTH_LONG).show();
return;
} else {
updatePlayerScore(updatedScoreCount);
notifyItemChanged(getAdapterPosition());
}
break;
default:
}
}
#Override
public boolean onLongClick(View v) {
removePlayer(getAdapterPosition());
return true;
}
public void removePlayer(int positionInArrayList) {
mPlayerList.remove(positionInArrayList);
notifyItemRemoved(positionInArrayList);
}
private void updatePlayerScore(int updatedScoreCount) {
mPlayerList.get(getAdapterPosition()).setScore(updatedScoreCount);
}
}
}
The base number of Players in the list starts as two and their names show up as
Player 1 and Player 2. But when 1 is passed into numberOfPlayers in addNewPlayers the players all show up as Player 2.
Screen captures of the list before and after addNewPlayer().
try this: override the getItemCount in the adapter to reflect the size of the list it is using, and call that method instead to get the number of items in your list.
The problem was that I was adding the new Player to the beginning of the list and not the end of the list. To be honest I'm sure why this worked but it does. the new code looks like this.
public static void addNewPlayers(List<Player> playerArrayList, SharedPreferences sharedPreferences,
PlayerScoreCardAdapter playerScoreCardAdapter,
int numberOfPlayersToAdd) {
for (int i = 0; i < numberOfPlayersToAdd; i++) {
String name = String.format("%s%s", Keys.KEY_DEFAULT_PLAYER_NAME, playerArrayList.size() + 1);
Player player = new Player(name, sharedPreferences.getInt(Keys.KEY_PLAYER_SCORE + String.valueOf(i + 1),
Keys.KEY_DEFAULT_PLAYER_SCORE));
playerArrayList.add(playerArrayList.size(), player);
playerScoreCardAdapter.notifyItemInserted(playerArrayList.size());
}
}
i have been trying to implement the endless scroll feature for my product listing page of a ecommmerce app. The grids show the details of the items and when i scroll to the bottom i need to show a progress bar and then append the new grid of items.
The api call works like this, I need to send a start_row_number and limit, which will send me all the items from the start_row_number to limit. Example: start_row_number = 0 and limit = 10. This will return items from 0 to 10
After that i need to load more items when the user reaches the bottom of the grid, and append it to the gridview. So i will send start_row_number = 10 and limit = 10, this will return items form 10 to 20.
As of now, i can get the items from 0 to 10 but not after that. How can i create the endless scroll feature and make everything such that it doesn't give me error such as 'too much work on the main thread'
Here is my MainActivity:
public class ProductListing extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.product_listing_act);
init();
}
public void productListingApiCall(ProductListingCondtionModel productListingCondtionModel) {
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint(productListingCondtionModel.getBase_url()).setLogLevel(RestAdapter.LogLevel.FULL).build();
final ProductListingApi productListingApi =
restAdapter.create(ProductListingApi.class);
productListingApi.getFeed(productListingCondtionModel.getFile(),
productListingCondtionModel.getOperation_condition(),
productListingCondtionModel.getSearch_string_condition(),
productListingCondtionModel.getMinprice_condition(),
productListingCondtionModel.getMaxprice_condition(),
productListingCondtionModel.getMincusratings_condition(),
productListingCondtionModel.getMaxcusratings_condition(),
productListingCondtionModel.getDiscount_condition(),
productListingCondtionModel.getCatids_condition(),
productListingCondtionModel.getBrands_condition(),
productListingCondtionModel.getAffids_condition(),
productListingCondtionModel.getStart_row_condition(),
productListingCondtionModel.getLimit(),
productListingCondtionModel.getOrderby_condition(),
productListingCondtionModel.getSortby_condition(), new Callback<ProductListingPojo>() {
#Override
public void success(ProductListingPojo productListingPojo, Response response) {
final ProductListingPojo product = productListingPojo;
new Thread(new Runnable() {
#Override
public void run() {
String[] t = Arrays.copyOf(product.getTitle(),
product.getTitle().length);
int[] p = Arrays.copyOf(product.getSellingprice(),
product.getSellingprice().length);
int[] m = Arrays.copyOf(product.getMrp(),
product.getMrp().length);
int[] d = Arrays.copyOf(product.getDiscountpercent(),
product.getDiscountpercent().length);
String[] i = Arrays.copyOf(product.getProductimageSmall1(),
product.getProductimageSmall1().length);
for(int j = 0; j < t.length; j++) {
CategoryAllApi categoryAllApi = new CategoryAllApi();
categoryAllApi.setTitle(t[j]);
categoryAllApi.setPrice(p[j]);
categoryAllApi.setMrp(m[j]);
categoryAllApi.setDiscount(d[j]);
categoryAllApi.setImage(i[j]);
arrayList.add(categoryAllApi);
}
}
}).run();
setAdapter();
}
#Override
public void failure(RetrofitError error) {
tv_title_header.setText(error.getMessage());
Log.e("error", error.getMessage());
}
});
}
void setAdapter() {
adapter = new ProductListingGridAdapter(this, arrayList);
gv_product_listing_act.setAdapter(adapter);
}
}
Heres the Adapter:
public class ProductListingGridAdapter extends BaseAdapter {
public ProductListingGridAdapter(ProductListing productListing, ArrayList<CategoryAllApi> arrayList) {
this.arrayList= arrayList;
context = productListing;
inflater = ( LayoutInflater )context.
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return arrayList.size();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
public class Holder
{
ImageView im_pic;
TextView tv_title, tv_price, tv_mrp, tv_discount;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
GridView grid = (GridView) parent;
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
grid.setColumnWidth(width);
grid.setNumColumns(2);
int size = grid.getRequestedColumnWidth() / 2 ;
Double d = new Double(size * 2);
int h = d.intValue();
Holder holder = new Holder();
View rowView;
int index = grid.getFirstVisiblePosition();
View v = grid.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - grid.getPaddingTop());
grid.setSelectionFromTop(index, top);
rowView = inflater.inflate(R.layout.product_listing_gv_items_lay, null);
rowView.setLayoutParams(new GridView.LayoutParams(size, h));
holder.im_pic = (ImageView) rowView.findViewById(R.id.im_product_listing_gv_items_lay_pic);
holder.tv_title = (TextView) rowView.findViewById(R.id.tv_product_listing_gv_items_lay_title);
holder.tv_price = (TextView) rowView.findViewById(R.id.tv_product_listing_gv_items_lay_price);
holder.tv_mrp = (TextView) rowView.findViewById(R.id.tv_product_listing_gv_items_lay_mrp);
holder.tv_discount = (TextView) rowView.findViewById(R.id.tv_product_listing_gv_items_lay_discount);
holder.tv_title.setTypeface(EasyFonts.robotoMedium(rowView.getContext()));
holder.tv_price.setTypeface(EasyFonts.robotoBlack(rowView.getContext()));
holder.tv_mrp.setTypeface(EasyFonts.robotoLight(rowView.getContext()));
holder.tv_mrp.setPaintFlags(holder.tv_mrp.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
holder.tv_discount.setTypeface(EasyFonts.robotoLight(rowView.getContext()));
categoryAllApi = arrayList.get(position);
Ion.with(holder.im_pic).load(categoryAllApi.getImage());
holder.tv_title.setText(categoryAllApi.getTitle());
holder.tv_price.setText("Rs. " + categoryAllApi.getPrice());
holder.tv_mrp.setText("Rs. " + categoryAllApi.getMrp());
holder.tv_discount.setText("" + categoryAllApi.getDiscount() + "%");
rowView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(context, ProductDetails.class);
Bundle bundle = new Bundle();
bundle.putString("operation", "");
bundle.putString("productkey", "");
intent.putExtras(bundle);
context.startActivity(intent);
}
});
return rowView;
}
}
Heres the CategoryApiCall.java:
public class CategoryAllApi {
private String title, image;
private int price, mrp, discount;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getMrp() {
return mrp;
}
public void setMrp(int mrp) {
this.mrp = mrp;
}
public int getDiscount() {
return discount;
}
public void setDiscount(int discount) {
this.discount = discount;
}
}
I see you have used a GridView.
RecyclerView has been introduced by Google and overcomes flaws of listview and Gridview which filled the ram with junk and made app clunky.
Use RecyclerView with GridLayoutManager. Also, research endless scroll in RecyclerView (which is pretty easy to implement).
I strongly recommend this for your app, since I have tried the same and result is outstanding. App is faster, ram becomes light, and scrolling is great. Also there are many features like the recyclerView object will offer. About time Google took care of such things and raise app quality.
Process may look like a task but in long run it will help you and the app users a lot.