Related
In this part of my app, I am trying to implement deleting of selected favorite items via contextual action mode/bar, the problem is when I select an item then delete, it's deleted from the database and selected list but it is still available in recyclerView and it adds a duplicate from another item, the following gif clarify the problem
Edited the full adapter code FavoritesPostAdapter
public class FavoritesPostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final FragmentActivity fragmentActivity;
private final List<FavoritesEntity> favoritesList;
private View rootView;
private static final int CARD = 0;
private static final int CARD_MAGAZINE = 1;
private static final int TITLE = 2;
private static final int GRID = 3;
private static final int SDK_VERSION = Build.VERSION.SDK_INT;
public static final String TAG = "POST ADAPTER";
private int viewType;
public final Fragment fragment;
public final PostViewModel postViewModel;
private ActionMode mActionMode;
private boolean multiSelection = false;
// private int selectedPostPosition ;
private final List<FavoritesEntity> selectedPosts = new ArrayList<>();
private final List<RecyclerView.ViewHolder> myViewHolders = new ArrayList<>();
public FavoritesPostAdapter(FragmentActivity fragmentActivity,
List<FavoritesEntity> favoritesList, Fragment fragment,
PostViewModel postViewModel) {
this.fragmentActivity = fragmentActivity;
this.favoritesList = favoritesList;
this.fragment = fragment;
this.postViewModel = postViewModel;
}
public void setViewType(int viewType) {
this.viewType = viewType;
notifyDataSetChanged();
}
public int getViewType() {
return this.viewType;
}
private final ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
#Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
mActionMode = actionMode;
actionMode.getMenuInflater().inflate(R.menu.favorites_contextual_menu, menu);
applyStatusBarColor(R.color.contextualStatusBarColor);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
return true;
}
#Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
if (menuItem.getItemId() == R.id.delete_favorites_post) {
for (FavoritesEntity favoritesEntity : selectedPosts) {
postViewModel.deleteFavoritePost(favoritesEntity);
}
showSnackBar(selectedPosts.size() + " post/s deleted");
multiSelection = false;
selectedPosts.clear();
notifyDataSetChanged();
mActionMode.finish();
}
return true;
}
#Override
public void onDestroyActionMode(ActionMode actionMode) {
for (RecyclerView.ViewHolder holder : myViewHolders) {
changePostStyle(holder, R.color.cardBackgroundColor, R.color.strokeColor);
}
multiSelection = false;
selectedPosts.clear();
applyStatusBarColor(R.color.statusBarColor);
}
};
private void showSnackBar(String message){
Snackbar.make(rootView,message,Snackbar.LENGTH_SHORT).show();
}
private void applyStatusBarColor(int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
fragmentActivity.getWindow().setStatusBarColor(ContextCompat.getColor(fragmentActivity, color));
}
}
private void applySelection(RecyclerView.ViewHolder holder, FavoritesEntity currentSelectedPost) {
if (selectedPosts.contains(currentSelectedPost)) {
selectedPosts.remove(currentSelectedPost);
changePostStyle(holder, R.color.cardBackgroundColor, R.color.strokeColor);
applyActionModeTitle();
} else {
selectedPosts.add(currentSelectedPost);
changePostStyle(holder, R.color.cardBackgroundLightColor, R.color.primaryColor);
applyActionModeTitle();
}
}
private void changePostStyle(RecyclerView.ViewHolder holder, int backgroundColor, int strokeColor) {
if (holder instanceof CardViewHolder) {
((CardViewHolder) holder).cardLayoutBinding.mainLinearLayout.setBackgroundColor(
ContextCompat.getColor(fragmentActivity.getApplicationContext(),
backgroundColor)
);
((CardViewHolder) holder).cardLayoutBinding.cardView.setStrokeColor(
strokeColor);
}
}
private void applyActionModeTitle() {
if (selectedPosts.size() == 0) {
mActionMode.finish();
multiSelection = false;
} else if (selectedPosts.size() == 1) {
mActionMode.setTitle(selectedPosts.size() + " item selected");
} else {
mActionMode.setTitle(selectedPosts.size() + " items selected");
}
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(fragmentActivity);
View view;
if (this.viewType == CARD) {
final CardLayoutBinding cardLayoutBinding
= CardLayoutBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new FavoritesPostAdapter.CardViewHolder(cardLayoutBinding);
} else if (this.viewType == CARD_MAGAZINE) {
final CardMagazineBinding cardMagazineBinding
= CardMagazineBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new FavoritesPostAdapter.CardMagazineViewHolder(cardMagazineBinding);
} else if (this.viewType == TITLE) {
if (SDK_VERSION < Build.VERSION_CODES.LOLLIPOP) {
view = inflater.inflate(R.layout.title_layout_v15, parent, false);
} else {
view = inflater.inflate(R.layout.title_layout, parent, false);
}
return new FavoritesPostAdapter.TitleViewHolder(view);
} else {
if (SDK_VERSION < Build.VERSION_CODES.LOLLIPOP) {
view = inflater.inflate(R.layout.grid_layout_v15, parent, false);
} else {
view = inflater.inflate(R.layout.grid_layout, parent, false);
}
return new FavoritesPostAdapter.GridViewHolder(view);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
myViewHolders.add(holder);
rootView = holder.itemView.getRootView();
// selectedPostPosition = position;
int itemType = getViewType();
FavoritesEntity favoriteItem = favoritesList.get(position);
final Document document = Jsoup.parse(favoriteItem.getItem().getContent());
final Elements elements = document.select("img");
// Log.e("IMAGE", document.getAllElements().select("img").get(0).attr("src"));
switch (itemType) {
case CARD:
if (holder instanceof FavoritesPostAdapter.CardViewHolder) {
((FavoritesPostAdapter.CardViewHolder) holder).bind(favoriteItem);
((CardViewHolder) holder).cardLayoutBinding.cardView.setOnClickListener(view -> {
if (multiSelection) {
applySelection(holder, favoriteItem);
} else {
mActionMode.finish();
if (Objects.requireNonNull(Navigation.findNavController(
view
).getCurrentDestination()).getId() == R.id.nav_favorites) {
Navigation.findNavController(view)
.navigate(FavoritesFragmentDirections
.actionFavoritesFragmentToDetailsFragment(favoriteItem.getItem()));
}
}
}
);
((CardViewHolder) holder).cardLayoutBinding.cardView.setOnLongClickListener(view -> {
if (!multiSelection) {
multiSelection = true;
fragmentActivity.startActionMode(mActionModeCallback);
applySelection(holder, favoriteItem);
return true;
} else {
applySelection(holder, favoriteItem);
return true;
}
});
}
break;
case CARD_MAGAZINE:
if (holder instanceof FavoritesPostAdapter.CardMagazineViewHolder) {
FavoritesPostAdapter.CardMagazineViewHolder
cardMagazineViewHolder = (FavoritesPostAdapter.CardMagazineViewHolder) holder;
cardMagazineViewHolder.bind(favoriteItem);
}
break;
case TITLE:
if (holder instanceof FavoritesPostAdapter.TitleViewHolder) {
FavoritesPostAdapter.TitleViewHolder titleViewHolder = (FavoritesPostAdapter.TitleViewHolder) holder;
titleViewHolder.postTitle.setText(favoriteItem.getItem().getTitle());
Log.d("TITLE", "title layout called");
try {
Log.e("IMAGE", elements.get(0).attr("src"));
Glide.with(fragmentActivity).load(elements.get(0).attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(titleViewHolder.postImage);
} catch (IndexOutOfBoundsException e) {
titleViewHolder.postImage.setImageResource(R.drawable.no_image);
Log.e(TAG, e.toString());
}
if (position == getItemCount() - 1)
if (fragment instanceof HomeFragment) {
postViewModel.getPosts();
} else {
postViewModel.getPostListByLabel();
}
}
break;
case GRID:
if (holder instanceof FavoritesPostAdapter.GridViewHolder) {
FavoritesPostAdapter.GridViewHolder gridViewHolder = (FavoritesPostAdapter.GridViewHolder) holder;
gridViewHolder.postTitle.setText(favoriteItem.getItem().getTitle());
try {
Log.e("IMAGE", elements.get(0).attr("src"));
Glide.with(fragmentActivity).load(elements.get(0).attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(gridViewHolder.postImage);
} catch (IndexOutOfBoundsException e) {
gridViewHolder.postImage.setImageResource(R.drawable.no_image);
Log.e(TAG, e.toString());
}
if (position == getItemCount() - 1)
if (fragment instanceof HomeFragment) {
postViewModel.getPosts();
} else {
postViewModel.getPostListByLabel();
}
}
}
}
#Override
public int getItemCount() {
return favoritesList.size();
}
#Override
public void setHasStableIds(boolean hasStableIds) {
super.setHasStableIds(hasStableIds);
}
#Override
public long getItemId(int position) {
return position;
}
public static class CardViewHolder extends RecyclerView.ViewHolder {
final CardLayoutBinding cardLayoutBinding;
final Context context;
private CardViewHolder(final CardLayoutBinding binding) {
super(binding.getRoot());
cardLayoutBinding = binding;
context = cardLayoutBinding.getRoot().getContext();
}
private void bind(FavoritesEntity favoriteItem) {
final Document document = Jsoup.parse(favoriteItem.getItem().getContent());
final Elements elements = document.select("img");
// Log.e("IMAGE", document.getAllElements().select("img").get(0).attr("src"));
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat
("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
cardLayoutBinding.postTitle.setText(favoriteItem.getItem().getTitle());
try {
Log.e("IMAGE", elements.get(0).attr("src"));
Glide.with(context).load(elements.get(0).attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(cardLayoutBinding.postImage);
} catch (IndexOutOfBoundsException e) {
cardLayoutBinding.postImage.setImageResource(R.drawable.no_image);
Log.e(TAG, e.toString());
}
cardLayoutBinding.postDescription.setText(document.text());
try {
date = format.parse(favoriteItem.getItem().getPublished());
} catch (ParseException e) {
e.printStackTrace();
}
PrettyTime prettyTime = new PrettyTime();
cardLayoutBinding.postDate.setText(prettyTime.format(date));
}
}
public static class CardMagazineViewHolder extends RecyclerView.ViewHolder {
final CardMagazineBinding cardMagazineBinding;
final Context context;
private CardMagazineViewHolder(final CardMagazineBinding binding) {
super(binding.getRoot());
cardMagazineBinding = binding;
context = cardMagazineBinding.getRoot().getContext();
}
private void bind(FavoritesEntity favoriteItem) {
final Document document = Jsoup.parse(favoriteItem.getItem().getContent());
final Elements elements = document.select("img");
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat
("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
// Log.e("IMAGE", document.getAllElements().select("img").get(0).attr("src"));
cardMagazineBinding.postTitle.setText(favoriteItem.getItem().getTitle());
try {
Log.e("IMAGE", elements.get(0).attr("src"));
Glide.with(context).load(elements.get(0).attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(cardMagazineBinding.postImage);
} catch (IndexOutOfBoundsException e) {
cardMagazineBinding.postImage.setImageResource(R.drawable.no_image);
Log.e(TAG, e.toString());
}
try {
date = format.parse(favoriteItem.getItem().getPublished());
} catch (ParseException e) {
e.printStackTrace();
}
PrettyTime prettyTime = new PrettyTime();
cardMagazineBinding.postDate.setText(prettyTime.format(date));
}
}
public static class TitleViewHolder extends RecyclerView.ViewHolder {
TextView postTitle;
com.blogspot.abtallaldigital.utils.MyImageview postImage;
private TitleViewHolder(#NonNull View itemView) {
super(itemView);
postTitle = itemView.findViewById(R.id.postTitle);
postImage = itemView.findViewById(R.id.postImage);
}
}
public static class GridViewHolder extends RecyclerView.ViewHolder {
TextView postTitle;
MyImageview postImage;
private GridViewHolder(#NonNull View itemView) {
super(itemView);
postTitle = itemView.findViewById(R.id.postTitle);
postImage = itemView.findViewById(R.id.postImage);
}
}
}
favorites_contextual_menu xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/delete_favorites_post"
android:title="#string/delete_post"
app:showAsAction="ifRoom"
app:iconTint="#color/white"
android:icon="#drawable/ic_delete"
>
</item>
</menu>
PS: I tried to get the selected position from the holder and assign it to selectedPostPosition int value to use it in notifyItemRemoved(position); and notifyItemRangeChanged(position, getItemCount()); like in this answer but it doesn't fix the issue
The problem was happening because variable
private List<FavoritesEntity> favoritesList;
inside FavouritesFragment which kept all previous objects even after deleting items from a local database.
On every delete event, you were getting updates about database change -> postViewModel.metal favorites().observe, where you called addAll to this reference which contained all previous items.
Solution: https://github.com/dautovicharis/DummyApp2/commit/752a48fc98761d53c6a8f72076489a0f68ee348b
Source: https://github.com/dautovicharis/DummyApp2
Instead of using notifyItemRangeChanged(position, getItemCount());,
you can use
notifyItemRemoved(position);
this is because notifyItemRangeChanged(position, getItemCount()); does not remove the item like your requirements!
So I've been trying to fix this issue for so many hours but can't find the cause behind it.
Issue: My app shows data from my API into recyclerView, a standard feature. The issue comes when I use the search function. When I search for something, my search adapter shows the query data but going back to my Main fragment again, the view loader shows content fine but when I click on it, search items are actually being loaded instead.
Check my app to find out what I'm talking about: https://play.google.com/store/apps/details?id=envision.apps.newsextra
To reproduce, search for something, click on any article from search results, then go back and click any article from main feed, search items are actually being loaded instead.
Here's my main fragment:
public class FeedsFragment extends Fragment implements ArticleListener, LocalMessageCallback {
private PullRefreshLayout pullRefreshLayout;
private RecyclerView recyclerView;
private FeedsAdapter adapter;
private View layout;
private ArrayList<Object> data = new ArrayList<>();
private List<Favorites> favorites = new ArrayList<>();
private List<Articles> articles = new ArrayList<>();
private boolean init = true;
private DataViewModel dataViewModel;
private NativeAdsManager mNativeAdsManager;
private String sort_date = SharedPrefernces.getFeedSortDate();
public static FeedsFragment newInstance() {
return new FeedsFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
layout = inflater.inflate(R.layout.feeds_fragment_layout, container, false);
init_views();
setRecyclerView();
initNativeAds();
dataViewModel = ViewModelProviders.of(this).get(DataViewModel.class);
dataViewModel.getFavorites().observe(this, favorites -> {
if(favorites!=null){
this.favorites = favorites;
}
});
dataViewModel.getArticles().observe(this, articles -> {
this.articles = articles;
if(init) {
if (articles!= null && articles.size() > 0) {
//this.articles = articles;
data = new ArrayList<>();
if(SharedPrefernces.getActiveInterest().equalsIgnoreCase(getString(R.string.all_stories))){
data.add(new Info("Showing stories based on all your interests"));
}else{
data.add(new Info("Showing stories on "+SharedPrefernces.getActiveInterest()));
}
data.addAll(articles);
adapter.setData(data);
}
init = false;
}
});
new Handler().postDelayed(() -> {
//we first check if time set to fetch feeds again has elapsed
pullRefreshLayout.setRefreshing(true);
fetchFeeds();
SharedPrefernces.setReloadArticles(false);
}, 1000);
return layout;
}
//init view layouts
private void init_views(){
pullRefreshLayout = layout.findViewById(R.id.pullRefreshLayout);
int[] colorScheme = getResources().getIntArray(R.array.refresh_color_scheme);
pullRefreshLayout.setColorSchemeColors(colorScheme);
pullRefreshLayout.setOnRefreshListener(this::fetchFeeds);
}
//init recyclerview
private void setRecyclerView() {
recyclerView = (RecyclerView) layout.findViewById(R.id.recyclerView);
GridLayoutManager mLayoutManager = new GridLayoutManager(getActivity(), 1);
recyclerView.setLayoutManager(mLayoutManager);
adapter = new FeedsAdapter(getActivity(),recyclerView,this);
//int index = movies.size() - 1;
adapter.setLoadMoreListener(() -> recyclerView.post(() -> {
if(data.size()>0 && data.get(1) instanceof Articles){
adapter.setLoader();
loadMoreFeeds();
}
}));
recyclerView.setAdapter(adapter);
}
private void initNativeAds(){
mNativeAdsManager = new NativeAdsManager(getActivity(), getResources().getString(R.string.FACEBOOK_FEED_NATIVE_AD), 10);
mNativeAdsManager.loadAds();
mNativeAdsManager.setListener(new NativeAdsManager.Listener() {
#Override
public void onAdsLoaded() {
}
#Override
public void onAdError(AdError adError) {
}
});
}
private void fetchFeeds(){
if(!NetworkUtil.hasConnection(getActivity())) {
setNetworkError();
return;
}
NetworkService service = StringApiClient.createServiceWithToken(NetworkService.class);
try {
JSONObject jsonData = new JSONObject();
if(SharedPrefernces.getActiveInterest().equalsIgnoreCase(getString(R.string.all_stories))) {
jsonData.put("interests", new JSONArray(SharedPrefernces.getUserInterests()));
}else{
ArrayList<String> interest = new ArrayList<>();
interest.add(SharedPrefernces.getActiveInterest());
jsonData.put("interests", new JSONArray(interest));
}
if(SharedPrefernces.getUserUnfollowedFeedSources()!=null && SharedPrefernces.getUserUnfollowedFeedSources().size()>0) {
jsonData.put("sources", new JSONArray(SharedPrefernces.getUserUnfollowedFeedSources()));
}
jsonData.put("location", Misc.getCurrentCountryCode());
String requestBody = jsonData.toString();
Log.e("final requestbody",requestBody);
Call<String> callAsync = service.getArticles(requestBody);
callAsync.enqueue(new Callback<String>() {
#Override
public void onResponse(#NonNull Call<String> call, #NonNull Response<String> response) {
Log.e("response",String.valueOf(response.body()));
pullRefreshLayout.setRefreshing(false);
if(response.body()==null){
setNetworkError();
return;
}
try {
JSONObject res = new JSONObject(response.body());
// Add Your Logic
if(res.getString("status").equalsIgnoreCase("ok")){
//create a new object
data = new ArrayList<>();
if(SharedPrefernces.getActiveInterest().equalsIgnoreCase(getString(R.string.all_stories))){
data.add(new Info("Showing stories based on all your interests"));
}else{
data.add(new Info("Showing Stories on "+SharedPrefernces.getActiveInterest()));
}
sort_date = res.getString("date");
SharedPrefernces.setFeedSortDate(sort_date);
ArrayList<Articles> articles = JsonParser.getArticles(res.getJSONArray("feeds"));
//delete all previously store articles, and add new items to database
dataViewModel.deleteAllArticles();
dataViewModel.insertAllArticles(articles);
//append interests to our object list
data.addAll(articles);
//set data to adapter
adapter.setData(data);
//set last fetched time to sharedpreferences
if(articles.size()>0)SharedPrefernces.setArticleLastRefreshTime(System.currentTimeMillis());
}
}catch (Exception e){
e.printStackTrace();
Log.e("error",e.getMessage());
}
}
#Override
public void onFailure(#NonNull Call<String> call, #NonNull Throwable throwable) {
Log.e("error",String.valueOf(throwable.getMessage()));
setNetworkError();
pullRefreshLayout.setRefreshing(false);
}
});
} catch (JSONException e) {
Log.e("parse error",e.getMessage());
e.printStackTrace();
}
}
private void loadMoreFeeds(){
// ToDo
}
private void setNetworkError(){
dataViewModel.deleteAllArticles();
dataViewModel.insertAllArticles(articles);
pullRefreshLayout.setRefreshing(false);
data = new ArrayList<>();
if(SharedPrefernces.getActiveInterest().equalsIgnoreCase(App.getContext().getString(R.string.all_stories))){
data.add(new Info("Showing stories based on all your interests"));
}else{
data.add(new Info("Showing stories on "+SharedPrefernces.getActiveInterest()));
}
data.add(new Error(""));
adapter.setData(data);
}
#Override
public void OnItemClick(Articles article) {
//List<Integer> contestWinners = data.subList(0, 5);
int position = 0;
for (Articles arts: this.articles) {
if(arts.getId() == article.getId()){
position = this.articles.indexOf(arts);
}
}
Gson gson = new Gson();
String json = gson.toJson(article);
Intent intent = new Intent(getActivity(), FeedViewerActivity.class);
intent.putExtra("position", position);
intent.putExtra("article",json);
intent.putExtra(FeedViewerActivity.VIEW_TYPE, Constants.ARTICLE_VIEW);
intent.putExtra(FeedViewerActivity.VIEW_SIZE, get_feeds_view_size(this.articles,position));
startActivity(intent);
Objects.requireNonNull(getActivity()).overridePendingTransition(R.anim.slide_left_in, R.anim.still);
}
#Override
public void OnPinClick(Articles articles,String action) {
if(articles==null)return;
if(action.equalsIgnoreCase(getResources().getString(R.string.add_pin))){
dataViewModel.insertFavorites(ObjectMapper.mapFavorites(articles));
}else {
dataViewModel.deleteFavorite(ObjectMapper.mapFavorites(articles).getId());
}
}
#Override
public void OnShareClick(Articles articles) {
Intent share = new Intent(android.content.Intent.ACTION_SEND);
share.setType("text/plain");
share.putExtra(Intent.EXTRA_SUBJECT, articles.getTitle());
share.putExtra(Intent.EXTRA_TEXT, articles.getLink());
startActivity(Intent.createChooser(share, "Share Article"));
}
#Override
public boolean IsPinned(Articles articles) {
for (Favorites fav: favorites) {
if(fav.getId() == articles.getId()){
return true;
}
}
return false;
}
#Override
public void requestAds(int position) {
NativeAd ad = mNativeAdsManager.nextNativeAd();
if(ad!=null){
adapter.setAd(ad,position);
}
}
#Override
public void loadSingleFeedsActivity(Articles articles) {
Gson gson = new Gson();
String myJson = gson.toJson(articles);
Intent intent = new Intent(getActivity(), FeedSourceActivity.class);
intent.putExtra("article", myJson);
startActivity(intent);
}
#Override
public boolean isSingleFeedsActivity() {
return false;
}
#Override
public void onDestroy() {
LocalMessageManager.getInstance().send(R.id.remove_listener);
LocalMessageManager.getInstance().removeListener(this);
super.onDestroy();
}
#Override
public void onStart() {
super.onStart();
LocalMessageManager.getInstance().addListener(this);
}
#Override
public void handleMessage(#NonNull LocalMessage localMessage) {
if(localMessage.getId() == R.id.reload_feeds){
pullRefreshLayout.setRefreshing(true);
fetchFeeds();
SharedPrefernces.setReloadArticles(false);
}
if(localMessage.getId() == R.id.scroll_feeds_to_top){
recyclerView.smoothScrollToPosition(0);
}
}
}
Adapter:
public class FeedsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements View.OnClickListener {
private ArrayList<Object> data = new ArrayList<>();
private Context context;
private int lastItemPosition = -1;
private final int VIEW_TYPE_HEADER = 1;
private final int VIEW_TYPE_LIST = 2;
private final int VIEW_TYPE_LOADER = 3;
private final int VIEW_TYPE_NETWORK_ERROR = 4;
private final int VIEW_TYPE_INFO = 5;
private final int VIEW_TYPE_AD = 6;
private boolean isLoading = false;
private LoadMoreListener loadMoreListener;
private int visibleThreshold = 2;//visible items before loading next feeds
private int firstVisibleItem,lastVisibleItem, totalItemCount;
private ArticleListener articleListener;
public FeedsAdapter(Context context, RecyclerView mRecyclerView, ArticleListener articleListener) {
this.context=context;
this.articleListener = articleListener;
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LocalMessageManager.getInstance().send(R.id.recyclerview_scroll);
firstVisibleItem = linearLayoutManager.findFirstVisibleItemPosition();
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if ((firstVisibleItem + Constants.ADS.NUMBER_OF_ITEMS_BEFORE_REQUEST_AD) % Constants.ADS.LOAD_ADS_AT_POSITION == 0){
//
int pos = firstVisibleItem + Constants.ADS.NUMBER_OF_ITEMS_BEFORE_REQUEST_AD;
if(pos > lastItemPosition && data.size()>pos && data.get(pos - 1) != null/*dont load ad if we r currently making a request*/) {
if (!(data.get(pos) instanceof NativeAd)) {
articleListener.requestAds(pos);
}
}
}
if (!isLoading && NetworkUtil.hasConnection(context)) {
if (totalItemCount <= (lastVisibleItem + visibleThreshold)) {
if (loadMoreListener != null) {
loadMoreListener.onLoadMore();
}
isLoading = true;
}
}
}
});
}
#Override
public int getItemViewType(int position) {
if(data.get(position) instanceof Error)return VIEW_TYPE_NETWORK_ERROR;
if(data.get(position) instanceof Info)return VIEW_TYPE_INFO;
if(data.get(position) instanceof NativeAd)return VIEW_TYPE_AD;
if(SharedPrefernces.get_feed_images_show()
&& SharedPrefernces.get_feed_type() == 1
&& position==1 && data.get(position) instanceof Articles){
return VIEW_TYPE_HEADER;
}
if(data.get(position) == null)return VIEW_TYPE_LOADER;
return VIEW_TYPE_LIST;
}
public void setData(ArrayList<Object> objectList) {
Log.e("objectList size",String.valueOf(objectList.size()));
this.data.clear();
this.data.addAll(objectList);
Log.e("data size",String.valueOf(data.size()));
this.notifyDataSetChanged();
}
public void setMoreData(ArrayList<Articles> articles) {
//int start = data.size() + 2;
data.addAll(articles);
//this.notifyItemRangeInserted(start, articles.size());
this.notifyDataSetChanged();
}
public void setAd(NativeAd ad, int pos) {
data.add(pos, ad);
this.notifyItemInserted(pos);
}
#Override
public int getItemCount() {
return data != null ? data.size() : 0;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, final int position) {
lastItemPosition = position;
//Log.e("view type",String.valueOf(holder.getItemViewType()));
switch (holder.getItemViewType()) {
case VIEW_TYPE_LIST: case VIEW_TYPE_HEADER:
final ArticleViewHolder viewHolder = (ArticleViewHolder) holder;
viewHolder.bindTo((Articles) data.get(position));
break;
case VIEW_TYPE_LOADER:
final ViewLoader viewLoader = (ViewLoader) holder;
viewLoader.rotateLoading.start();
break;
case VIEW_TYPE_NETWORK_ERROR:
ViewError viewError = (ViewError) holder;
if(SharedPrefernces.getUseNightMode()){
viewError.img.setColorFilter(App.getContext().getResources().getColor(R.color.white));
}else{
viewError.img.setColorFilter(App.getContext().getResources().getColor(R.color.black));
}
break;
case VIEW_TYPE_INFO:
final ViewInfo viewInfo = (ViewInfo) holder;
Info info = (Info)data.get(position);
viewInfo.body.setText(info.getContent());
break;
case VIEW_TYPE_AD:
final AdsViewHolder adsViewHolder = (AdsViewHolder) holder;
NativeAd nativeAd = (NativeAd)data.get(position);
adsViewHolder.bind(nativeAd);
break;
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int i) {
RecyclerView.ViewHolder viewHolder = null;
LayoutInflater inflater = LayoutInflater.from(context);
switch (i) {
case VIEW_TYPE_HEADER:
View v = inflater.inflate(R.layout.feeds_header, parent, false);
viewHolder = new ArticleViewHolder(v, articleListener);
break;
case VIEW_TYPE_LIST:
View va;
if(SharedPrefernces.get_feed_images_show() && SharedPrefernces.get_feed_type()==0){
va = inflater.inflate(R.layout.large_image_feeds_list, parent, false);
}else{
va = inflater.inflate(R.layout.feeds_list, parent, false);
}
viewHolder = new ArticleViewHolder(va, articleListener);
break;
case VIEW_TYPE_LOADER:
View ld = inflater.inflate(R.layout.loader, parent, false);
viewHolder = new ViewLoader(ld);
break;
case VIEW_TYPE_NETWORK_ERROR:
View ne = inflater.inflate(R.layout.no_stories, parent, false);
viewHolder = new ViewError(ne);
break;
case VIEW_TYPE_INFO:
View info = inflater.inflate(R.layout.info, parent, false);
viewHolder = new ViewInfo(info);
break;
case VIEW_TYPE_AD:
View ads = inflater.inflate(R.layout.ad_item_large, parent, false);
viewHolder = new AdsViewHolder(ads);
break;
}
return viewHolder;
}
#Override
public void onClick(View view) {
//int pos = (int) view.getTag();
switch (view.getId()){
case R.id.pin:
break;
case R.id.share:
break;
}
}
public class ViewLoader extends RecyclerView.ViewHolder {
private RotateLoading rotateLoading;
ViewLoader(View view) {
super(view);
rotateLoading = (RotateLoading) view.findViewById(R.id.rotateloading);
}
}
public class ViewError extends RecyclerView.ViewHolder {
private ImageView img;
ViewError(View view) {
super(view);
img = view.findViewById(R.id.img);
}
}
public class ViewInfo extends RecyclerView.ViewHolder {
private TextView body;
ViewInfo(View view) {
super(view);
body = view.findViewById(R.id.body);
}
}
public void setLoaded(){
data.remove(data.size()-1);
this.notifyItemRemoved(data.size()-1);
isLoading = false;
}
public void setLoadMoreListener(LoadMoreListener loadMoreListener) {
this.loadMoreListener = loadMoreListener;
}
public void setLoader(){
data.add(null);
this.notifyItemInserted(data.size()-1);
}
}
Search Adapter:
public class SearchAdapter extends RecyclerView.Adapter< RecyclerView.ViewHolder>{
private List<Object> items = new ArrayList<>();
private final int VIEW_TYPE_MEDIA = 0;
private final int VIEW_TYPE_LOADING = 1;
private final int VIEW_TYPE_AD = 2;
private SearchClickListener searchClickListener;
private LoadMoreListener mOnLoadMoreListener;
private boolean isLoading;
private int visibleThreshold = 2;
public int firstVisibleItem,lastVisibleItem, totalItemCount;
private int lastItemPosition = -1;
public SearchAdapter(RecyclerView mRecyclerView, SearchClickListener searchClickListener) {
this.searchClickListener = searchClickListener;
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
firstVisibleItem = linearLayoutManager.findFirstVisibleItemPosition();
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if ((firstVisibleItem + Constants.ADS.NUMBER_OF_ITEMS_BEFORE_REQUEST_AD) % Constants.ADS.LOAD_ADS_AT_POSITION == 0){
//
int pos = firstVisibleItem + Constants.ADS.NUMBER_OF_ITEMS_BEFORE_REQUEST_AD;
if(pos > lastItemPosition && items.size()>pos && items.get(pos - 1) != null/*dont load ad if we r currently making a request*/) {
if (!(items.get(pos) instanceof NativeAd)) {
searchClickListener.requestAds(pos);
}
}
}
if (!isLoading && NetworkUtil.hasConnection(App.getContext())) {
if (totalItemCount <= (lastVisibleItem + visibleThreshold)) {
if (items.size() > 1 && mOnLoadMoreListener != null) {
mOnLoadMoreListener.onLoadMore();
}
isLoading = true;
}
}
}
});
}
public void setMoreAdapter(List<Search> data) {
items.addAll(data);
//items.addAll((items.size()-1),data);
this.notifyDataSetChanged();
}
public void setAdapter(List<Search> data) {
items = new ArrayList<>();
items.addAll(data);
this.notifyDataSetChanged();
}
#Override
public int getItemCount() {
return items != null ? items.size() : 0;
}
#Override
public int getItemViewType(int position) {
if(items.get(position) instanceof NativeAd)return VIEW_TYPE_AD;
if(items.get(position)==null && position == (items.size()-1)){
return VIEW_TYPE_LOADING;
}else{
return VIEW_TYPE_MEDIA;
}
}
public void setLoader() {
items.add(null);
this.notifyItemInserted(items.size() - 1);
}
public void setAd(NativeAd ad, int pos) {
items.add(pos, ad);
this.notifyItemInserted(pos);
}
public void removeLoader() {
items.remove(items.size() - 1);
this.notifyItemRemoved(items.size());
}
public void setOnLoadMoreListener(LoadMoreListener mOnLoadMoreListener) {
this.mOnLoadMoreListener = mOnLoadMoreListener;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
RecyclerView.ViewHolder viewHolder = null;
LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
switch (i) {
case VIEW_TYPE_MEDIA:
View v = inflater.inflate(R.layout.search_list, viewGroup, false);
viewHolder = new SearchViewHolder(v,searchClickListener);
viewHolder.itemView.setClickable(true);
break;
case VIEW_TYPE_LOADING:
View vL = inflater.inflate(R.layout.loader, viewGroup, false);
viewHolder = new LoadingViewHolder(vL);
break;
case VIEW_TYPE_AD:
View ads = inflater.inflate(R.layout.ad_item_small, viewGroup, false);
viewHolder = new AdsViewHolder(ads);
break;
}
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int i) {
lastItemPosition = i;
switch (holder.getItemViewType()) {
case VIEW_TYPE_LOADING:
LoadingViewHolder loadingViewHolder = (LoadingViewHolder) holder;
loadingViewHolder.progressBar.start();
break;
case VIEW_TYPE_MEDIA:
final SearchViewHolder viewHolder = (SearchViewHolder) holder;
final Search ci = (Search)items.get(i);
viewHolder.bindTo(ci);
break;
case VIEW_TYPE_AD:
final AdsViewHolder adsViewHolder = (AdsViewHolder) holder;
NativeAd nativeAd = (NativeAd)items.get(i);
adsViewHolder.bind(nativeAd);
break;
}
}
private static class LoadingViewHolder extends RecyclerView.ViewHolder {
private RotateLoading progressBar;
private LoadingViewHolder(View itemView) {
super(itemView);
progressBar = itemView.findViewById(R.id.rotateloading);
}
}
public void setLoaded() {
isLoading = false;
}
public interface SearchClickListener {
void onClick(Search search);
void requestAds(int position);
}
}
Seriously any help will be greatly appreciated.
Due to the character limit, I'm posting my viewHolder code here as a comment.
ArticleViewHolder:
public class ArticleViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, LocalMessageCallback {
private Articles articles;
private TextView title,interest,time,source,content;
private LinearLayout revealView;
private ImageView thumbnail,pin,source_link;
private Animation alphaAnimation;
private float pixelDensity;
private boolean flag = false;
private RelativeLayout revealLayout,layoutButtons;
private ArticleListener articleListener;
private CardView cardView;
private String pin_action = "add";
public ArticleViewHolder(View view, ArticleListener articleListener) {
super(view);
this.articleListener = articleListener;
pixelDensity = App.getContext().getResources().getDisplayMetrics().density;
alphaAnimation = AnimationUtils.loadAnimation(App.getContext(), R.anim.alpha_anim);
LocalMessageManager.getInstance().addListener(this);
FrameLayout source_holder = view.findViewById(R.id.source_holder);
cardView = view.findViewById(R.id.card_view);
source_link = view.findViewById(R.id.source_link);
revealLayout = view.findViewById(R.id.revealLayout);
revealView = view.findViewById(R.id.revealView);
layoutButtons = view.findViewById(R.id.layoutButtons);
title = view.findViewById(R.id.title);
content = view.findViewById(R.id.content);
interest = view.findViewById(R.id.interest);
thumbnail = view.findViewById(R.id.thumbnail);
time = view.findViewById(R.id.time);
source = view.findViewById(R.id.source);
ImageView reveal = view.findViewById(R.id.reveal);
ImageView close = view.findViewById(R.id.close);
pin = view.findViewById(R.id.pin);
ImageView share = view.findViewById(R.id.share);
reveal.setOnClickListener(this);
close.setOnClickListener(this);
pin.setOnClickListener(this);
share.setOnClickListener(this);
title.setOnClickListener(this);
content.setOnClickListener(this);
thumbnail.setOnClickListener(this);
source_holder.setOnClickListener(this);
}
public void bindTo(Articles articles) {
this.articles = articles;
title.setText(articles.getTitle());
content.setText(articles.getContent().replace("\n", " ").replace("\r", " "));
interest.setText(articles.getInterest());
//Log.e("thumbnail",String.valueOf(articles.getThumbnail()));
if(SharedPrefernces.get_feed_images_show()) {
thumbnail.setVisibility(View.VISIBLE);
ImageLoader.loadImage(thumbnail, articles.getThumbnail());
}else{
thumbnail.setVisibility(View.GONE);
}
time.setText(TimUtil.timeAgo(articles.getTimeStamp()));
String _source = articles.getSource();
source.setText(_source.substring(0,1).toUpperCase() + _source.substring(1).toLowerCase());
if(articleListener.IsPinned(articles)){
set_pin_view(true);
pin_action = App.getContext().getResources().getString(R.string.remove_pin);
}else{
set_pin_view(false);
pin_action = App.getContext().getResources().getString(R.string.add_pin);
}
if(flag){
revealView.setVisibility(View.VISIBLE);
layoutButtons.setVisibility(View.VISIBLE);
}else{
revealView.setVisibility(View.GONE);
layoutButtons.setVisibility(View.GONE);
}
if(articleListener.isSingleFeedsActivity()){
source_link.setVisibility(View.GONE);
}else{
source_link.setVisibility(View.VISIBLE);
}
if(!SharedPrefernces.getUseNightMode()){
cardView.setCardBackgroundColor(App.getContext().getResources().getColor(R.color.material_grey_100));
}
}
private void set_pin_view(boolean isPinned){
Drawable mDrawable;
if(!isPinned){
mDrawable = ContextCompat.getDrawable(App.getContext(),R.drawable.pin_outline);
mDrawable.setColorFilter(new
PorterDuffColorFilter(App.getContext().getResources().getColor(R.color.material_grey_200), PorterDuff.Mode.SRC_IN));
}else{
mDrawable = ContextCompat.getDrawable(App.getContext(),R.drawable.pin_outline);
mDrawable.setColorFilter(new
PorterDuffColorFilter(App.getContext().getResources().getColor(R.color.colorAccent), PorterDuff.Mode.SRC_IN));
}
pin.setImageDrawable(mDrawable);
}
#Override
public void onClick(View view) {
LocalMessageManager.getInstance().send(R.id.hide_reveal_layout,articles.getId());
if (articleListener != null) {
if(view.getId() == R.id.reveal || view.getId() == R.id.close){
revealLayout();
}else if(view.getId() == R.id.source_holder){
articleListener.loadSingleFeedsActivity(articles);
}else if(view.getId() == R.id.pin){
articleListener.OnPinClick(articles,pin_action);
if(pin_action.equalsIgnoreCase(App.getContext().getResources().getString(R.string.add_pin))){
pin_action = App.getContext().getResources().getString(R.string.remove_pin);
set_pin_view(true);
}else{
pin_action = App.getContext().getResources().getString(R.string.add_pin);
set_pin_view(false);
}
revealLayout();
}else if(view.getId() == R.id.share){
articleListener.OnShareClick(articles);
revealLayout();
}else if(view.getId() == R.id.title || view.getId() == R.id.thumbnail|| view.getId() == R.id.content) {
articleListener.OnItemClick(articles);
}
}
}
private void revealLayout() {
/*
MARGIN_RIGHT = 16;
FAB_BUTTON_RADIUS = 28;
*/
boolean isAttachedToWindow = ViewCompat.isAttachedToWindow(revealView);
if (isAttachedToWindow) {
int x = revealLayout.getRight();
int y = revealLayout.getBottom();
x -= ((28 * pixelDensity) + (16 * pixelDensity));
int hypotenuse = (int) Math.hypot(revealLayout.getWidth(), revealLayout.getHeight());
if (!flag) {
RelativeLayout.LayoutParams parameters = (RelativeLayout.LayoutParams)
revealView.getLayoutParams();
parameters.height = revealLayout.getHeight();
revealView.setLayoutParams(parameters);
Animator anim = ViewAnimationUtils.createCircularReveal(revealView, x, y, 0, hypotenuse);
anim.setDuration(200);
anim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animator) {
}
#Override
public void onAnimationEnd(Animator animator) {
layoutButtons.setVisibility(View.VISIBLE);
layoutButtons.startAnimation(alphaAnimation);
}
#Override
public void onAnimationCancel(Animator animator) {
}
#Override
public void onAnimationRepeat(Animator animator) {
}
});
revealView.setVisibility(View.VISIBLE);
anim.start();
flag = true;
} else {
Animator anim = ViewAnimationUtils.createCircularReveal(revealView, x, y, hypotenuse, 0);
anim.setDuration(200);
anim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animator) {
}
#Override
public void onAnimationEnd(Animator animator) {
revealView.setVisibility(View.GONE);
layoutButtons.setVisibility(View.GONE);
}
#Override
public void onAnimationCancel(Animator animator) {
}
#Override
public void onAnimationRepeat(Animator animator) {
}
});
anim.start();
flag = false;
}
}
}
#Override
public void handleMessage(#NonNull LocalMessage localMessage) {
switch (localMessage.getId()){
case R.id.hide_reveal_layout:
int id = localMessage.getArg1();
if(flag && id != articles.getId()){
revealLayout();
}
break;
case R.id.recyclerview_scroll:
if(flag){
revealLayout();
}
break;
}
}
}
There is Scroll by loading RecyclerView(Lazy loading). When an item quantity is incremented by adding the plus button to say 10. after loading the next set of items while scrolling, The item quantity(10) is reset to 1 and 10 will be displayed for another item.
In the same sequence, if the unit is changed(by the spinner) to another unit the corresponding image will be loaded but after scrolling the unit and image will change back to initial unit.
When the image of an item is clicked, a dialog will show the details of the selected item . Sometimes the image in the dialog is correct but image of the RecyclerView is incorrect, will take time to load or will not load.
public class ArticleRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
implements Filterable {
private static final String TAG = "ArticleAdapter";
private final int VIEW_TYPE_ITEM = 0;
private final int VIEW_TYPE_LOADING = 1;
private Context context;
private Dialog articleDescriptionDialog;
private UOMOffersLIistViewAdapter uomOffersLIistViewAdapter;
private List<ArticleDetails> articleDetailsList;
private HomeActivityViewModel homeActivityViewModel;
private ItemFilter itemFilter = new ItemFilter();
private GetViewListener getViewListener;
private int lastPosition = -1;
private OnLoadMoreListener loadMoreListener;
private boolean isLoading = false, isMoreDataAvailable = true;
/*
* isLoading - to set the remote loading and complete status to fix back to back load more call
* isMoreDataAvailable - to set whether more data from server available or not.
* It will prevent useless load more request even after all the server data loaded
* */
public ArticleRecyclerViewAdapter(Context context, List<ArticleDetails> articleDetailsList
, HomeActivityViewModel homeActivityViewModel, Fragment fragment) {
this.context = context;
this.articleDetailsList = articleDetailsList;
this.homeActivityViewModel = homeActivityViewModel;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
if (viewType == VIEW_TYPE_ITEM) {
return new ItemViewHolder(inflater.inflate(R.layout.recycler_view_article_layout, parent, false));
} else {
return new LoadHolder(inflater.inflate(R.layout.item_loading_layout, parent, false));
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
if (position >= getItemCount() - 1 && isMoreDataAvailable && !isLoading && loadMoreListener != null) {
isLoading = true;
loadMoreListener.onLoadMore();
}
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
populateItemRows((ItemViewHolder) viewHolder, position);
}
//No else part needed as load holder doesn't bind any data
}
#Override
public int getItemViewType(int position) {
if (articleDetailsList.get(position).isLoading()) {
return VIEW_TYPE_LOADING;
} else {
return VIEW_TYPE_ITEM;
}
}
#Override
public int getItemCount() {
return articleDetailsList.size();
}
#Override
public long getItemId(int position) {
return position;
}
/* VIEW HOLDERS */
static class ItemViewHolder extends RecyclerView.ViewHolder {
private ImageView stockImageView, copyStockImageView;
private TextView articleNameTextView, articleStockAmountTextView, articleOfferTextView, addCartTextView;
private NumberPicker articleStockQuantityNumberPicker;
private Spinner articleStockSpinner;
private ImageButton offerInfoImageButton;
private int selectedStockItemPosition, stockQuantity;
private String articleStockQuantityString = "";
private Integer stockId;
private double stockAmount;
public ItemViewHolder(View itemView) {
super(itemView);
stockImageView = itemView.findViewById(R.id.stockImgv);
copyStockImageView = itemView.findViewById(R.id.copyStockImgv);//for fly to cart animation
articleNameTextView = itemView.findViewById(R.id.articleNameTxt);
articleStockAmountTextView = itemView.findViewById(R.id.stock_amount_txt);
articleOfferTextView = itemView.findViewById(R.id.offer_txt);
offerInfoImageButton = itemView.findViewById(R.id.offerInfo_imgbtn);
addCartTextView = itemView.findViewById(R.id.addCart_txt);
articleStockSpinner = itemView.findViewById(R.id.stock_spnr);
articleStockQuantityNumberPicker = itemView.findViewById(R.id.qty_numberPicker);
articleStockQuantityNumberPicker.setMin(1);
articleStockQuantityNumberPicker.setUnit(1);
// no limit.
// viewHolder.quantityNumberPicker.setMax(15);
}
}
static class LoadHolder extends RecyclerView.ViewHolder {
public LoadHolder(View itemView) {
super(itemView);
}
}
public void setMoreDataAvailable(boolean moreDataAvailable) {
isMoreDataAvailable = moreDataAvailable;
}
/**
* notifyDataSetChanged is final method so we can't override it
* call articleRecyclerViewAdapter.notifyDataChanged(); after update the list
*/
public void notifyDataChanged() {
notifyDataSetChanged();
isLoading = false;
}
public interface OnLoadMoreListener {
void onLoadMore();
}
public void setLoadMoreListener(OnLoadMoreListener loadMoreListener) {
this.loadMoreListener = loadMoreListener;
}
private void populateItemRows(ItemViewHolder itemViewHolder, int itemPos) {
int itemPosition = itemViewHolder.getAdapterPosition();
try {
itemViewHolder.copyStockImageView.setImageResource(R.drawable.error_logo);//initialize with error logo.
itemViewHolder.stockImageView.setImageResource(R.drawable.error_logo);//initialize with error logo.
} catch (Exception e) {
e.printStackTrace();
}
itemViewHolder.articleNameTextView.setText(articleDetailsList.get(itemPosition).getArticleName());
itemViewHolder.articleNameTextView.setSelected(true);//for marquee
itemViewHolder.articleOfferTextView.setSelected(true);//for marquee
itemViewHolder.articleStockAmountTextView.setSelected(true);//for marquee
itemViewHolder.articleOfferTextView.setVisibility(View.GONE);//for demo (now no offer and schemes).
itemViewHolder.offerInfoImageButton.setVisibility(View.GONE);//for demo
setStockSpinner(itemViewHolder, itemPosition);
itemViewHolder.offerInfoImageButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//setUOMOffersDIalog(articleDetailsList.get(itemPosition));
}
});
itemViewHolder.articleStockQuantityNumberPicker.setValueChangedListener(new ValueChangedListener() {
#Override
public void valueChanged(int quantity, ActionEnum action) {
itemViewHolder.articleStockQuantityString = Integer.valueOf(quantity).toString();
}
});
itemViewHolder.addCartTextView.setOnClickListener(new SingleClickListener() {
#Override
public void onSingleClick(View v) {
itemViewHolder.articleStockQuantityString = Integer.valueOf(itemViewHolder.articleStockQuantityNumberPicker.getValue()).toString();
itemViewHolder.stockQuantity = Integer.valueOf(itemViewHolder.articleStockQuantityString);
itemViewHolder.stockAmount = articleDetailsList.get(itemPosition).getStockDetailsList()
.get(itemViewHolder.articleStockSpinner.getSelectedItemPosition()).getmRP();
itemViewHolder.stockId = articleDetailsList.get(itemPosition).getStockDetailsList()
.get(itemViewHolder.articleStockSpinner.getSelectedItemPosition()).getStockId();
Double stockDiscountAmount = 0.0;
//TODO: 26-Oct-19 discount hardcoded.
if (itemViewHolder.stockQuantity > 0) {
addItemToCart(itemViewHolder, itemViewHolder.stockId, itemViewHolder.stockQuantity, itemViewHolder.stockAmount, stockDiscountAmount);
} else {
//not using (used for when user enter invalid quantity)
MDToast.makeText(context, FinalVariables.ToastMessages.VALID_QUANTITY
, MDToast.LENGTH_SHORT, MDToast.TYPE_WARNING).show();
}
}
});
itemViewHolder.stockImageView.setOnClickListener(new SingleClickListener() {
#Override
public void onSingleClick(View v) {
itemViewHolder.articleOfferTextView.setVisibility(View.VISIBLE);
itemViewHolder.articleOfferTextView.setText( ""+ itemViewHolder.stockQuantity);
DialogUtils.showArticleDescription(context, articleDetailsList.get(itemPosition)
, itemViewHolder.selectedStockItemPosition, new DialogButtonClickListener() {
#Override
public void positiveButtonClick() {
//do nothing.
}
});
}
});
// Here you apply the animation when the view is bound
setAnimation(itemViewHolder.itemView, itemPosition);
}
private void setStockSpinner(ItemViewHolder itemViewHolder, int itemPosition) {
UOMSpinnerAdapter uomSpinnerAdapter = new UOMSpinnerAdapter(context, R.layout.spinner_uom_layout
, StringUtils.makeUomStringArrayForSpinner(articleDetailsList.get(itemPosition).getStockDetailsList()));
itemViewHolder.articleStockSpinner.setAdapter(uomSpinnerAdapter);
// Setting OnItemClickListener to the Spinner
itemViewHolder.articleStockSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
itemViewHolder.selectedStockItemPosition = position;
itemViewHolder.articleStockAmountTextView.setText(StringUtils.addAED(StringUtils.round3(
+articleDetailsList.get(itemPosition)
.getStockDetailsList().get(position).getmRP())));
//show image from link
try {
if (!articleDetailsList.get(itemPosition).getStockDetailsList().
get(position).getStockImageDetailsList().isEmpty()) {
ImageUtils.setGlide(context, itemViewHolder.stockImageView
, articleDetailsList.get(itemPosition).getStockDetailsList().
get(position).getStockImageDetailsList().get(0).getStockImage());
//same as stockImageView but used to animate fly to cart
ImageUtils.setGlide(context, itemViewHolder.copyStockImageView
, articleDetailsList.get(itemPosition).getStockDetailsList().
get(position).getStockImageDetailsList().get(0).getStockImage());
} else {
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
if (articleDetailsList.get(itemPosition).getStockDetailsList().size() > 0)
itemViewHolder.articleStockAmountTextView.setText(StringUtils.addAED(
StringUtils.round3(+articleDetailsList.get(itemPosition).getStockDetailsList().get(0).getmRP())));
itemViewHolder.selectedStockItemPosition = itemViewHolder.articleStockSpinner.getSelectedItemPosition();
}
});
}
private void addItemToCart(ItemViewHolder itemViewHolder, Integer stockId, Integer stockQuantity
, Double stockAmount, Double stockDiscountAmount) {
homeActivityViewModel.isItemAlreadyCarted(stockId
, new RetrieveDataListner() {
#Override
public void onDone(Object object) {
boolean isItemAlreadyCarted = (boolean) object;
if (!isItemAlreadyCarted) {//item not in cart,add to cart.
CartedItems cartedItem = new CartedItems();
cartedItem.setStockId(stockId);
cartedItem.setStockQuantity(stockQuantity);
cartedItem.setStockAmount(stockAmount);
cartedItem.setTotalAmount(CalcUtils.calculateTotalAmount(stockQuantity, stockAmount));
cartedItem.setNetAmount(CalcUtils.calculateNetAmount(stockQuantity
, stockAmount, stockDiscountAmount));
homeActivityViewModel.insertCartedItem(cartedItem
, new InsertDbSuccessListner() {
#Override
public void onSuccess() {
//set view to call back method for fly to cart animation
if (getViewListener != null){
getViewListener.onGetView(itemViewHolder.copyStockImageView);
}
itemViewHolder.articleStockQuantityNumberPicker.setValue(1);//reset item quantity in ui.
// TODO: 21-Mar-19 check fly to cart only in home activity or not
if (context instanceof MainActivity) {
MDToast.makeText(context, TextMessages.ITEM_ADDED
, MDToast.LENGTH_SHORT, MDToast.TYPE_SUCCESS).show();
} else if (context instanceof HomeActivity) {
try {
((HomeActivity) context).setCartDetails();
} catch (Exception e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure() {
}
});
} else {//item already carted,asking to add new quantity to carted quantity.
DialogUtils.showConformAlertDialog1(context, TextMessages.ITEM_ALREADY_CARTED
, TextMessages.ITEM_ALREADY_CARTED_WARNING
, StringValues.NO, StringValues.YES, new DialogButtonClickListener() {
#Override
public void positiveButtonClick() {//adding new quantity to existing quantity.
//set view to call back method for fly to cart animation
if (getViewListener != null)
getViewListener.onGetView(itemViewHolder.copyStockImageView);
homeActivityViewModel.updateCartedItemWithExistingValues(stockId, stockQuantity
, CalcUtils.calculateTotalAmount(stockQuantity, stockAmount),
CalcUtils.calculateNetAmount(stockQuantity, stockAmount, stockDiscountAmount)
, null);
itemViewHolder.articleStockQuantityNumberPicker.setValue(1);//reset item quantity in ui.
}
#Override
public void negativeButtonClick() {
}
});
}
}
});
}
/**
* Here is the key method to apply the animation
*/
private void setAnimation(View viewToAnimate, int position) {
// If the bound view wasn't previously displayed on screen, it's animated
if (position > lastPosition) {
Animation animation = AnimationUtils.loadAnimation(context, R.anim.fall_down);
viewToAnimate.startAnimation(animation);
lastPosition = position;
}
}
/**
* register listener for get article image(copyStockImageView)
* for fly to cart animation.
* the view get back to the call back(categorizedArticleFragment)).
*
* #param getViewListener
*/
public void setGetViewListener(GetViewListener getViewListener) {
this.getViewListener = getViewListener;
}
#Override
public Filter getFilter() {
return itemFilter;
}
private class ItemFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
String filterString = constraint.toString().toLowerCase();
FilterResults results = new FilterResults();
final List<ArticleDetails> list = articleDetailsList;
int count = list.size();
final List<ArticleDetails> nlist = new ArrayList<ArticleDetails>(count);
ArticleDetails filterableString;
for (int i = 0; i < count; i++) {
filterableString = list.get(i);
if (filterableString.getArticleName().toLowerCase().contains(filterString)) {
nlist.add(filterableString);
}
}
results.values = nlist;
results.count = nlist.size();
return results;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
articleDetailsList = (ArrayList<ArticleDetails>) results.values;
notifyDataSetChanged();
}
}
}
Fragments onCreate
#Override
public View onCreateView(#NonNull LayoutInflater layoutInflater, ViewGroup viewGroup,
Bundle savedInstanceState) {
context = viewGroup.getContext();
parentView = layoutInflater.inflate(R.layout.fragment_article, viewGroup, false);
homeActivityViewModel = getViewModel();
articleRecyclerView = parentView.findViewById(R.id.subcategory_rcylv);
Bundle bundle = getArguments();
Integer offset = bundle.getInt(TagNames.OFFSET, 0);
Integer numberOfRows = bundle.getInt(TagNames.NUMBER_OF_ROWS, FinalVariables.GETTING_DATA_COUNT_FROM_API);
filterTypeId = bundle.getInt(TagNames.FILTER_TYPE_ID, FilterTypes.SUGGESTED);
String filterText = bundle.getString(TagNames.FILTER_TEXT, "");
Integer categoryId = bundle.getInt(TagNames.CATEGORY_ID, 0);
initArticleRecyclerViewAdapter(articleDetailsList, filterText, categoryId, offset, numberOfRows);
return parentView;
}
Initializing Adapter
private void initArticleRecyclerViewAdapter(List<ArticleDetails> articleDetailsList
, String filterText, Integer categoryId, int offset, int numberOfRows) {
articleRecyclerViewAdapter = new ArticleRecyclerViewAdapter(context, this.articleDetailsList, homeActivityViewModel, this);
articleRecyclerViewAdapter.setLoadMoreListener(new ArticleRecyclerViewAdapter.OnLoadMoreListener() {
#Override
public void onLoadMore() {
articleRecyclerView.post(new Runnable() {
#Override
public void run() {
int index = ArticleFragment.this.articleDetailsList.size();
loadMore(filterText, categoryId, index, numberOfRows);
Log.e(TAG, "scrolling,index: " + index);
}
});
//Calling loadMore function in Runnable to fix the
// java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling error
}
});
articleRecyclerView.setHasFixedSize(true);
articleRecyclerView.setLayoutManager(new GridLayoutManager(context, 3));
articleRecyclerView.setAdapter(articleRecyclerViewAdapter);
load(filterText, categoryId, offset, numberOfRows);
}
Loading data
private void loadMore(String filterText, Integer categoryId, int offset, int numberOfRows) {
//add loading progress view
articleDetailsList.add(new ArticleDetails(true));
articleRecyclerViewAdapter.notifyItemInserted(articleDetailsList.size() - 1);
Integer storeId = FileUtils.getPreferenceInt(context
, Preferences.CUSTOMER_PREFERENCE, TagNames.STORE_ID);
Integer distributionChannelId = FileUtils.getPreferenceInt(context
, Preferences.CUSTOMER_PREFERENCE, TagNames.DISTRIBUTION_CHANNEL_ID);
homeActivityViewModel.getAllStockItemByCustomer(
DeviceUtils.getCredential(context, ServiceNames.GET_ALL_STOCK_ITEM_BY_CUSTOMER)
, storeId, distributionChannelId, filterTypeId, filterText, categoryId, offset, numberOfRows
, new ApiCallbackListener() {
#Override
public void onSuccess(Object object) {
try {
if (object != null) {
if (object instanceof GetAllStockItemByCustomerStatusReturn) {
//otp generation failed,
GetAllStockItemByCustomerStatusReturn getAllStockItemByCustomerStatusReturn
= (GetAllStockItemByCustomerStatusReturn) object;
showGetAllStockItemByCustomerErrorDialog(getAllStockItemByCustomerStatusReturn);
} else if (object instanceof List) {
List<GetAllStockItemByCustomerResponseData> getAllStockItemByCustomerResponseDataList
= (List<GetAllStockItemByCustomerResponseData>) object;
//remove loading view
articleDetailsList.remove(articleDetailsList.size() - 1);
List<ArticleDetails> result = new ArrayList<>();
result = homeActivityViewModel.getArticles(getAllStockItemByCustomerResponseDataList);
homeActivityViewModel.insertAllStockItemByCustomer(context,filterTypeId
, getAllStockItemByCustomerResponseDataList, null);
if (result.size() > 0) {
//add loaded data
articleDetailsList.addAll(result);
} else {//result size 0 means there is no more data available at server
articleRecyclerViewAdapter.setMoreDataAvailable(false);
//telling articleRecyclerViewAdapter to stop calling load more as no more server data available
//Toast.makeText(context, "No More Data Available", Toast.LENGTH_LONG).show();
}
articleRecyclerViewAdapter.notifyDataChanged();
//should call the custom method articleRecyclerViewAdapter.notifyDataChanged here to get the correct loading status
} else {
MDToast.makeText(context, FinalVariables.ToastMessages.SOMETHING_WENT_WRONG
, MDToast.LENGTH_SHORT, MDToast.TYPE_WARNING).show();
}
} else {
MDToast.makeText(context, FinalVariables.ToastMessages.SOMETHING_WENT_WRONG
, MDToast.LENGTH_SHORT, MDToast.TYPE_WARNING).show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private void load(String filterText, Integer categoryId, int offset, int numberOfRows) {
Integer storeId = FileUtils.getPreferenceInt(context
, Preferences.CUSTOMER_PREFERENCE, TagNames.STORE_ID);
Integer distributionChannelId = FileUtils.getPreferenceInt(context
, Preferences.CUSTOMER_PREFERENCE, TagNames.DISTRIBUTION_CHANNEL_ID);
homeActivityViewModel.getAllStockItemByCustomer(
DeviceUtils.getCredential(context, ServiceNames.GET_ALL_STOCK_ITEM_BY_CUSTOMER)
, storeId, distributionChannelId, filterTypeId, filterText, categoryId, offset, numberOfRows
, new ApiCallbackListener() {
#Override
public void onSuccess(Object object) {
try {
if (object != null) {
if (object instanceof GetAllStockItemByCustomerStatusReturn) {
GetAllStockItemByCustomerStatusReturn getAllStockItemByCustomerStatusReturn
= (GetAllStockItemByCustomerStatusReturn) object;
showGetAllStockItemByCustomerErrorDialog(getAllStockItemByCustomerStatusReturn);
} else if (object instanceof List) {
List<GetAllStockItemByCustomerResponseData> getAllStockItemByCustomerResponseDataList
= (List<GetAllStockItemByCustomerResponseData>) object;
if (!getAllStockItemByCustomerResponseDataList.isEmpty()) {
if (offset == 0 && getAllStockItemByCustomerResponseDataList.get(0)
.getCategoryID() == StatusCodes.INVALID_CODE
&& filterTypeId == FilterTypes.SUGGESTED) {
//shows all article from store (filter type 0) if no article is mapped in suggested.
filterTypeId = FilterTypes.ALL;
load(filterText, categoryId, offset, numberOfRows);
}
}
articleDetailsList.addAll(homeActivityViewModel.getArticles(getAllStockItemByCustomerResponseDataList));
homeActivityViewModel.insertAllStockItemByCustomer(context,filterTypeId
, getAllStockItemByCustomerResponseDataList, null);
articleRecyclerViewAdapter.notifyDataChanged();
} else {
}
} else {
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
View Complaint Activity:
In this i have done debugging but i was unable to know the reason why the data is coming twice after scrolling. I have even check the data coming from server through postman but the data is not coming duplicate its coming perfectly fine the problem seems to be in front end while we are putting the data through adapter inside activity on scrolling.
public class ViewComplaintActivity extends AppCompatActivity implements AbsListView.OnScrollListener, View.OnClickListener {
TextView tv_notData;
List<Ticket> _data = new ArrayList<Ticket>();
ComplaintsAdapter settlementAdapter;
private String TAG = "ViewComplaintActivity";
private int visibleThreshold = 5;
private int currentPage = 0;
private int previousTotal = 0;
private boolean loading = true;
private boolean userScrolled = false;
a) Its is a Complaints Activity where we are showing the data comimg from server through adapter.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_compliants);
ButterKnife.bind(this);
try {
settlementAdapter = new ComplaintsAdapter(ViewComplaintActivity.this, _data);
AlphaInAnimationAdapter animationAdapter = new AlphaInAnimationAdapter(settlementAdapter);
animationAdapter.setAbsListView(ll_complaint_list);
ll_complaint_list.setAdapter(animationAdapter);
getComplaints(Utility.getTerminalId(ViewComplaintActivity.this), 0);
ll_complaint_list.setOnScrollListener(this);
fab_raise_complaint.setOnClickListener(this);
fab_call_customer.setOnClickListener(this);
ivBackButton.setOnClickListener(this);
} catch (NullPointerException e) {
Log.e(TAG, e.toString());
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
private void getComplaints(String terminalid, int pagenumber) {
if (!NetworkConnection.isConnected(this)) {
Toast.makeText(this, R.string.no_connection, Toast.LENGTH_SHORT).show();
return;
}
progressBar.setVisibility(View.VISIBLE);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("subIdentifier", terminalid);
jsonObject.addProperty("pageLimit", "10");
jsonObject.addProperty("lastPageIndex", "" + pagenumber);
IRetrofit iRetrofit = MoApplication.getInstance().getRetrofitOsTickets(this).create(IRetrofit.class);
Log.e(TAG,"jsonObject "+jsonObject);
iRetrofit.getAllTicket(jsonObject, new Callback<Response>() {
#Override
public void success(Response response, Response response2) {
progressBar.setVisibility(View.GONE);
try {
if (response.getStatus() == 200) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody().in()));
String resultFromJson = bufferedReader.readLine();
/* Log.e(TAG, "resultFromJson " + resultFromJson);*/
GetTicketsResponse getSettlementsResDTO = new Gson().fromJson(resultFromJson, GetTicketsResponse.class);
switch (getSettlementsResDTO.getResCode()) {
case "0001":
if (null != getSettlementsResDTO.getTickets()) {
_data.addAll(getSettlementsResDTO.getTickets());
settlementAdapter.notifyDataSetChanged();
if (_data.isEmpty()) {
tv_notData.setVisibility(View.VISIBLE);
} else {
tv_notData.setVisibility(View.GONE);
}
} else {
Utility.showToast(ViewComplaintActivity.this, "No data found.");
}
break;
default:
Utility.showToast(ViewComplaintActivity.this, "Please try later.");
break;
}
}
} catch (NullPointerException e) {
Log.e(TAG, "" + e.toString());
} catch (Exception e) {
Log.e(TAG, "" + e.toString());
}
}
#Override
public void failure(RetrofitError error) {
if (Utility.parseException(error)){
Utility.mPosLogoutService((AppCompatActivity) ViewComplaintActivity.this);
}
progressBar.setVisibility(View.GONE);
}
});
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
/*Log.e(TAG,"one");*/
Log.e(TAG,"scrollState "+scrollState);
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
userScrolled = true;
fab_call_customer.hide();
fab_raise_complaint.hide();
} else {
fab_call_customer.show();
fab_raise_complaint.show();
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
/*Log.e(TAG,"two");*/
view.getSelectedItemPosition();
if (userScrolled) {
Log.e(TAG,"loading "+loading);
if (loading) {
Log.e(TAG,"totalItemCount "+totalItemCount);
Log.e(TAG,"previousTotal "+previousTotal);
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
currentPage++;
}
}
Log.e(TAG,"loadin after scroll "+loading);
if (!loading && ((totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold))) {
getComplaints(Utility.getTerminalId(ViewComplaintActivity.this), currentPage);
loading = true;
}
}
else {
Log.e(TAG,"user is idle");
}
}
#Override
public void onBackPressed() {
super.onBackPressed();
finish();
}
#Override
public void onClick(View v) {
if (NetworkConnection.isConnected(this)) {
if (v == fab_raise_complaint) {
Utility.navigate(this, RaiseComplaintActivity.class);
} else if (v == fab_call_customer) {
Intent callIntent = new Intent(Intent.ACTION_DIAL);
callIntent.setData(Uri.parse("tel:" + Uri.encode(Constant.helplineNumber.trim())));
callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(callIntent);
} else if (v == ivBackButton) {
startActivity(new Intent(ViewComplaintActivity.this, HomeQrActivity.class));
finish();
}
} else {
Toast.makeText(this, R.string.no_connection, Toast.LENGTH_SHORT).show();
}
}
public class ComplaintsAdapter extends BaseAdapter {
private static String TAG = "ComplaintsAdapter";
List<Ticket> _data;
Context _c;
ViewHolder holder;
public ComplaintsAdapter(Context context, List<Ticket> getData) {
_data = getData;
_c = context;
}
#Override
public int getCount() {
Log.e(TAG,"_data.size()"+_data.size());
return _data.size();
}
#Override
public Object getItem(int position) {
Log.e(TAG,"_data.get(position)"+_data.get(position));
return _data.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
/*Log.e(TAG,"getData"+_data.toString());*/
try {
if (view == null) {
/*Log.e(TAG,"getData1"+_data.toString());*/
LayoutInflater li = (LayoutInflater)
_c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = li.inflate(R.layout.row_item_view_complaints,
null);
} else {
/*Log.e(TAG,"getData2"+_data.toString());*/
view = convertView;
}
holder = new ViewHolder();
holder.tv_ticketNo = (TextView)
view.findViewById(R.id.tv_ticketNo);
holder.tv_submitDate = (TextView)
view.findViewById(R.id.tv_submitDate); //ticket comments
holder.tv_updatedDate = (TextView)
view.findViewById(R.id.tv_updatedDate);//will act as ticket
last modified time
holder.tv_ticketStatus = (TextView)
view.findViewById(R.id.tv_ticketStatus); //ticket status open
or close
holder.tv_comment = (TextView)
view.findViewById(R.id.tv_comment);
holder.tv_subject = (TextView)
view.findViewById(R.id.tv_subject);
final Ticket ticket = _data.get(position);
holder.tv_ticketNo.setText("#" + ticket.getTicketNo());
holder.tv_submitDate.setText(Html.fromHtml("<b> Created On:
</b>" + Utility.dateReformatting("yyyy-MM-dd HH:mm:ss", "dd
MMM yyyy", ticket.getCreated())));
holder.tv_updatedDate.setText(Html.fromHtml("<b> Modified On:
</b>" + Utility.dateReformatting("yyyy-MM-dd HH:mm:ss", "dd
MMM yyyy", ticket.getModified())));
holder.tv_ticketStatus.setText(ticket.getStatus());
holder.tv_subject.setText(null != ticket.getSubject() && !ticket.getSubject().isEmpty() ? ticket.getSubject().trim().replace("|", "") : "NA"); //comments
if (null != ticket.getComment() && !ticket.getComment().isEmpty()) {
holder.tv_comment.setText(Html.fromHtml(ticket.getComment()));
holder.tv_comment.setVisibility(View.VISIBLE);
} else {
holder.tv_comment.setVisibility(View.GONE);
}
//holder.tv_comment.setText(null != ticket.getComment() && !ticket.getComment().isEmpty() ? ticket.getComment() : ""); //comments
if (ticket.getStatus().startsWith("Open"))
holder.tv_ticketStatus.setTextColor(_c.getResources().getColor(R.color.green_success));
else
Log.e(TAG,"getData4"+_data.toString());
holder.tv_ticketStatus.setTextColor(_c.getResources().getColor(R.color_color));
} catch (NullPointerException e) {
Log.e(TAG, e.toString());
} catch (Exception e) {
Log.e(TAG, e.toString());
}
return view;
}
public void set_data(List<Ticket> _data) {
this._data = _data;
}
static class ViewHolder {
public TextView tv_ticketStatus, tv_ticketNo, tv_submitDate,
tv_updatedDate, tv_comment, tv_subject;
}
}
java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged()
so i'm working with a custom listView when you reach the final of the list i show a footer view and create a listener to implements on the Activity/Fragment that will use the listView. My problem is when i call a services on this method and update the array with the data of the adapter i received that error. I call too a services on the adapter because i need the data of the specific showing row.
Here go my code:
Custom listView:
public class EndlessListView extends ListView implements OnScrollListener {
private View footer;
private boolean isLoading;
private EndlessListener listener;
private ArrayAdapter<?> adapter;
private boolean isReachEnd;
public EndlessListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.setOnScrollListener(this);
}
public EndlessListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setOnScrollListener(this);
}
public EndlessListView(Context context) {
super(context);
this.setOnScrollListener(this);
}
public void refreshList() {
this.removeFooterView(footer);
this.requestLayout();
adapter.notifyDataSetChanged();
isLoading = false;
}
public void setListener(EndlessListener listener) {
this.listener = listener;
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (getAdapter() == null)
return;
if (getAdapter().getCount() == 0)
return;
int l = visibleItemCount + firstVisibleItem;
if (l >= totalItemCount && !isLoading && !isReachEnd) {
// It is time to add new data. We call the listener
this.addFooterView(footer);
this.requestLayout();
isLoading = true;
listener.loadData();
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
public void setLoadingView(int resId) {
LayoutInflater inflater = (LayoutInflater) super.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
footer = (View) inflater.inflate(resId, null);
this.addFooterView(footer);
}
public void setAdapter(ArrayAdapter<?> adapter) {
super.setAdapter(adapter);
this.adapter = adapter;
this.removeFooterView(footer);
}
public void addNewData(ArrayList data) {
this.removeFooterView(footer);
this.requestLayout();
adapter.addAll(data);
adapter.notifyDataSetChanged();
isLoading = false;
}
public void removeFooter() {
if (adapter != null) {
this.removeFooterView(footer);
isLoading = false;
this.adapter.notifyDataSetChanged();
this.requestLayout();
}
}
public void addFooter() {
if (adapter != null) {
this.addFooterView(footer);
isLoading = true;
this.adapter.notifyDataSetChanged();
this.requestLayout();
}
}
public EndlessListener setListener() {
return listener;
}
public static interface EndlessListener {
public void loadData();
}
public boolean isReachEnd() {
return isReachEnd;
}
public void setReachEnd(boolean isReachEnd) {
this.isReachEnd = isReachEnd;
}
public boolean isLoading() {
return isLoading;
}
public void setLoading(boolean isLoading) {
this.isLoading = isLoading;
}
#Override
public ArrayAdapter<?> getAdapter() {
return adapter;
}
public View getFooter() {
return footer;
}
#Override
public void setClickable(boolean clickable) {
super.setClickable(clickable);
}}
Get data and update the array of the adapter:
public void getMatcher() {
try {
gettingMatcher = true;
Timber.i(getString(R.string.getting_mather));
HomeRequest.createMatcherRequest(getActivity(), new RequestController.ResponseListener<JSONObject>() {
#Override
public void onStart() {
((AuthorizedActivity) getActivity()).showProgress(true);
}
#Override
public void onResponse(JSONObject response) {
parseMatcher(response);
}
#Override
public void onError(int error, String message) {
gettingMatcher = false;
((AuthorizedActivity) getActivity()).showProgress(false);
Utils.showToast(getActivity(), getString(R.string.error_unexpected));
}
});
} catch (Exception e) {
gettingMatcher = false;
Timber.e(e, getString(R.string.error_getting_matcher));
Crashlytics.logException(e);
}
}
/**
* Parse the data from the web services and load the UI
*
* #param response
*/
private void parseMatcher(JSONObject response) {
MatcherResponse matcherResponse = new MatcherResponse(false, "");
ArrayList<User> auxDiscover = new ArrayList<>();
if (response == null) {
Timber.e(getString(R.string.error_empty_response));
Utils.showToast(getActivity(), getString(R.string.error_empty_response));
((AuthorizedActivity) getActivity()).showProgress(false);
gettingMatcher = false;
return;
}
try {
Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();
matcherResponse = gson.fromJson(response.toString(), MatcherResponse.class);
if (matcherResponse.isSuccess()) {
auxDiscover = matcherResponse.getDiscover();
if (auxDiscover.size() > 0) {
discoversData.addAll(auxDiscover);
adapter.notifyDataSetChanged();
discovers.removeFooter();
}
} else {
Timber.e(getString(R.string.error_parsing_matcher));
Utils.showToast(getActivity(), getString(R.string.error_unexpected));
}
gettingMatcher = false;
((AuthorizedActivity) getActivity()).showProgress(false);
} catch (Exception e) {
((AuthorizedActivity) getActivity()).showProgress(false);
gettingMatcher = false;
Timber.e(getString(R.string.error_parsing_matcher));
Crashlytics.logException(e);
}
}
Adapter:
public class DiscoverEndlessAdapter extends ArrayAdapter {
private ArrayList<User> itemList;
private Context ctx;
private static OnClickDiscoverEvents onClickDiscoverEvents;
private DiscoverFragment fragment;
public DiscoverEndlessAdapter(Context ctx, ArrayList<User> itemList, OnClickDiscoverEvents onClickDiscoverEvents, DiscoverFragment fragment) {
super(ctx, R.layout.adapter_discover, itemList);
this.itemList = itemList;
this.ctx = ctx;
this.onClickDiscoverEvents = onClickDiscoverEvents;
this.fragment = fragment;
}
#Override
public int getCount() {
return itemList.size();
}
#Override
public User getItem(int i) {
return itemList.get(i);
}
#Override
public long getItemId(int i) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
return getDiscoverRow(position, convertView, parent, holder);
}
static class ViewHolder {
#InjectView(R.id.adapter_discover_title)
TextView title;
#InjectView(R.id.adapter_discover_subtitle)
TextView subTitle;
#InjectView(R.id.adapter_discover_avatar)
ImageView avatar;
#InjectView(R.id.adapter_discover_connect)
TextView connectWith;
#InjectView(R.id.adapter_discover_message)
ImageButton message;
#InjectView(R.id.adapter_discover_description)
TextView description;
#InjectView(R.id.adapter_discover_more)
ImageButton more;
#InjectView(R.id.adapter_discover_avatar_connection)
ImageView avatarConnection;
#InjectView(R.id.adapter_discover_avatar_connection_2)
ImageView avatarConnection2;
#InjectView(R.id.adapter_discover_avatar_connection_3)
ImageView avatarConnection3;
#InjectView(R.id.adapter_discover_avatar_connection_4)
ImageView avatarConnection4;
#InjectView(R.id.adapter_discover_connection_opacity)
TextView avatarConnectionOpacity;
#InjectView(R.id.adapter_discover_message)
ImageButton messageButton;
#InjectView(R.id.adapter_discover_more)
ImageButton optionButton;
/*OnClick*/
#OnClick(R.id.adapter_discover_message)
public void onGoChat(View view) {
onClickDiscoverEvents.showProfile(view);
}
#OnClick(R.id.adapter_discover_more)
public void onShowMoreOption(View view) {
onClickDiscoverEvents.showOption(view);
}
private Context ctx;
public ViewHolder(View view, Context ctx) {
ButterKnife.inject(this, view);
this.ctx = ctx;
}
}
/**
* Create the view for the chat data row
*
* #param position
* #param convertView
* #param parent
* #param holder
* #return
*/
private View getDiscoverRow(int position, View convertView, ViewGroup parent, ViewHolder holder) {
try {
User user = getItem(position);
if (convertView != null) {
holder = (ViewHolder) convertView.getTag();
} else {
LayoutInflater inflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.adapter_discover, parent, false);
holder = new ViewHolder(convertView, ctx);
convertView.setTag(holder);
}
// Title
String title = Utils.getNameFromUser(user);
holder.title.setText(title);
// Subtitle
String subTitle = Utils.getRoleAndLocationFromUser(user);
holder.subTitle.setText(subTitle);
// Description
String description = user.getAccomplishment();
holder.description.setText(description);
//Avatar
Utils.setImageToImageViewFromUrl(ctx, holder.avatar, user.getImage(), user.getFirstName(), user.getLastName());
//ImageButtons
holder.more.setFocusable(false);
holder.message.setFocusable(false);
//CommonConnections
if (user.getCommonConnections() == null)
getCommonConnections(user.getId(), position);
else {
ArrayList<Connection> connections = user.getCommonConnections();
if (connections.size() > 0) {
if ((connections.size() > 0) && (holder.avatarConnection.getVisibility() != View.VISIBLE)) {
Connection connection = connections.get(0);
Utils.setImageToImageViewFromUrl(ctx, holder.avatarConnection, connection.getImage(), connection.getFirstName(), connection.getLastName());
holder.avatarConnection.setVisibility(View.VISIBLE);
}
if ((connections.size() > 1) && (holder.avatarConnection2.getVisibility() != View.VISIBLE)) {
Connection connection = connections.get(1);
Utils.setImageToImageViewFromUrl(ctx, holder.avatarConnection2, connection.getImage(), connection.getFirstName(), connection.getLastName());
holder.avatarConnection2.setVisibility(View.VISIBLE);
}
if ((connections.size() > 2) && (holder.avatarConnection3.getVisibility() != View.VISIBLE)) {
Connection connection = connections.get(2);
Utils.setImageToImageViewFromUrl(ctx, holder.avatarConnection3, connection.getImage(), connection.getFirstName(), connection.getLastName());
holder.avatarConnection3.setVisibility(View.VISIBLE);
}
if ((connections.size() > 3) && (holder.avatarConnection4.getVisibility() != View.VISIBLE)) {
Connection connection = connections.get(3);
Utils.setImageToImageViewFromUrl(ctx, holder.avatarConnection4, connection.getImage(), connection.getFirstName(), connection.getLastName());
holder.avatarConnection4.setVisibility(View.VISIBLE);
}
if ((connections.size() > 4) && (holder.avatarConnectionOpacity.getVisibility() != View.VISIBLE)) {
int quantity = connections.size() - 4;
String moreConnections = "+" + quantity;
if (quantity > 0) {
holder.avatarConnectionOpacity.setText(moreConnections);
holder.avatarConnectionOpacity.setVisibility(View.VISIBLE);
}
}
} else {
if (isCommon(user.getSkills())) {
holder.avatarConnection.setImageResource(R.drawable.ic_common_skill);
} else
holder.avatarConnection.setVisibility(View.GONE);
if (isCommon(user.getCompanies())) {
holder.avatarConnection2.setImageResource(R.drawable.ic_common_work);
} else
holder.avatarConnection2.setVisibility(View.GONE);
holder.avatarConnection3.setVisibility(View.GONE);
holder.avatarConnection4.setVisibility(View.GONE);
holder.avatarConnectionOpacity.setVisibility(View.GONE);
}
}
//Buttons
holder.messageButton.setTag(user);
holder.optionButton.setTag(user);
} catch (Exception e) {
Timber.e(e, ctx.getString(R.string.error_getview_discover_adapter));
Crashlytics.log(ctx.getString(R.string.error_getview_discover_adapter));
}
return convertView;
}
public void getCommonConnections(long caliberId, final int position) {
try {
Timber.i(ctx.getString(R.string.getting_common_connections));
HomeRequest.createCommonConnectionsRequest(ctx, caliberId, new RequestController.
ResponseListener<JSONObject>() {
#Override
public void onStart() {
}
#Override
public void onResponse(JSONObject response) {
parseCommonConnections(response, position);
}
#Override
public void onError(int error, String message) {
Toast.makeText(ctx, ctx.getString(R.string.error_unexpected), Toast.LENGTH_SHORT).show();
}
});
} catch (Exception e) {
Timber.e(e, ctx.getString(R.string.error_getting_common_connections));
Crashlytics.logException(e);
}
}
/**
* Parse the data from the web services and load the UI
*
* #param response
*/
private void parseCommonConnections(JSONObject response, int position) {
CommonConnectionsResponse commonConnectionsResponse = new CommonConnectionsResponse();
ArrayList<Connection> connections = new ArrayList<>();
if (response == null) {
Timber.e(ctx.getString(R.string.error_empty_response));
return;
}
try {
Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();
commonConnectionsResponse = gson.fromJson(response.toString(), CommonConnectionsResponse.class);
if (commonConnectionsResponse.isSuccess()) {
itemList.get(position).setCommonConnections(commonConnectionsResponse.getConnections().getConnections());
fragment.discoversData.get(position).setCommonConnections(commonConnectionsResponse.getConnections().getConnections());
} else {
itemList.get(position).setCommonConnections(new ArrayList<Connection>());
fragment.discoversData.get(position).setCommonConnections(new ArrayList<Connection>());
Timber.i(ctx.getString(R.string.no_common_connections, Utils.getNameFromUser(fragment.discoversData.get(position))));
}
notifyDataSetChanged();
} catch (Exception e) {
Timber.e(ctx.getString(R.string.error_parsing_common_connections));
Crashlytics.logException(e);
}
}
/**
* Return common value of skill or company object from list
*
* #param list
* #return
*/
private boolean isCommon(ArrayList<?> list) {
if ((list != null) && (list.size() > 0)) {
for (int i = 0; i < list.size(); i++) {
Object object = list.get(i);
if (object instanceof Skill) {
if (((Skill) object).isCommon())
return true;
} else if (object instanceof Company) {
if (((Company) object).isCommon())
return true;
} else
return false;
}
} else
return false;
return false;
}
}
Any help would be greatly appreciated.
You can try to call setAdapter(null) before modifying data inside the Adapter.