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();
}
}
});
}
I have a moneyConversion class which converts strings as I type them into an EditText which there are multiple of due to being contained in a listview. The conversion works fine for the EditText, but for some reason, the first list item always goes into a loop and hangs when typing something in, where as all other identical EditText boxes work absolutely fine and format without issue.
Here's my adapter.
public class DietyAmountAdapter extends ArrayAdapter {
Context context;
int layoutResourceId;
ArrayList<Dieties> data = null;
int counterTotal;
int currentlyActive;
public DietyAmountAdapter(Context context, int layoutResourceId, ArrayList<Dieties> data){
super(context, layoutResourceId, data);
this.context = context;
this.layoutResourceId = layoutResourceId;
this.data = data;
}
public void addViews(){
SharedPreferences sharedPref = ((Activity) context).getPreferences(Context.MODE_PRIVATE);
currentlyActive = 0;
this.clear();
for(int i = 1; i <= counterTotal; i++) {
boolean a = sharedPref.getBoolean(Integer.toString(i), false);
if (a) {
currentlyActive = currentlyActive + 1;
}
}
}
public View getView(int position, View convertView, ViewGroup parent) {
View ListItem = convertView;
Holder holder;
final Dieties Diety = data.get(position);
if (ListItem == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
ListItem = inflater.inflate(layoutResourceId, parent, false);
holder = new Holder();
holder.banner = (ImageView) ListItem.findViewById(R.id.DietyBanner);
holder.title = (TextView) ListItem.findViewById(R.id.titleText);
holder.text1 = (EditText) ListItem.findViewById(R.id.text1Amount);
holder.text2 = (EditText) ListItem.findViewById(R.id.text2Amount);
holder.text1Text = (TextView) ListItem.findViewById(R.id.text1Text);
holder.text2Text = (TextView) ListItem.findViewById(R.id.text2Text);
ListItem.setTag(holder);
} else {
holder = (Holder) ListItem.getTag();
}
MyTextWatcher text2Watcher = (new MyTextWatcher() {
public Holder holder;
public void setView(Holder newHolder){
holder = newHolder;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (holder.text2.isFocused()) {
holder.text2.removeTextChangedListener(this);
String raw = moneyConversion.unformatToString(s.toString());
double text1Value = moneyConversion.gettext1Value(Double.parseDouble(raw));
holder.text2.setText(moneyConversion.formatLive(raw));
holder.text1.setText(moneyConversion.format(text1Value));
holder.text2.addTextChangedListener(this);
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
MyTextWatcher text1Watcher = (new MyTextWatcher() {
public Holder holder;
public void setView(Holder newHolder){
holder = newHolder;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (holder.text1.isFocused()) {
holder.text1.removeTextChangedListener(this);
String text1Value = moneyConversion.unformatToString(s.toString());
String formattedValue = moneyConversion.formatLive(text1Value);
double text2Value = moneyConversion.gettext2Value(Double.parseDouble(text1Value));
holder.text1.setText(formattedValue);
holder.text2.setText(moneyConversion.format(text2Value));
holder.text1.addTextChangedListener(this);
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
HolderOnFocusChangeListener text1FocusListener = new HolderOnFocusChangeListener() {
public DietyAmountAdapter.Holder holder;
public void setHolder(Holder newHolder){
holder = newHolder;
}
#Override
public void onFocusChange(View v, boolean hasFocus) {
// Timer timer = new Timer();
// TimerTask t1 = new TimerTask() {
// #Override
// public void run() {
// holder.text1.clearFocus();
// }
// };
if (hasFocus){
holder.text1.setBackgroundColor(Color.parseColor("#5aa0ce"));
holder.text1Text.setBackgroundColor(Color.parseColor("#5aa0ce"));
holder.text1.setSelection(holder.text1.getText().length());
}
if (!hasFocus) {
if (!holder.text1.getText().toString().isEmpty()) {
holder.text1.setSelection(holder.text1.getText().length());
// holder.text2.setText(moneyConversion.format());
}
holder.text1.setBackgroundColor(Color.parseColor("#424242"));
holder.text1Text.setBackgroundColor(Color.parseColor("#424242"));
// timer.scheduleAtFixedRate(runOnUiThread(t1), 1, 500);
}
}
};
HolderOnFocusChangeListener text2FocusListener = new HolderOnFocusChangeListener() {
public DietyAmountAdapter.Holder holder;
public void setHolder(Holder newHolder){
holder = newHolder;
}
#Override
public void onFocusChange(View v, boolean hasFocus) {
// Timer timer = new Timer();
// TimerTask t2 = new TimerTask() {
// #Override
// public void run() {
// holder.text2.clearFocus();
// }
// };
if (hasFocus){
holder.text2.setBackgroundColor(Color.parseColor("#5aa0ce"));
holder.text2Text.setBackgroundColor(Color.parseColor("#5aa0ce"));
holder.text2.setSelection(holder.text2.getText().length());
}
if (!hasFocus) {
if (!holder.text2.getText().toString().isEmpty()) {
holder.text2.setSelection(holder.text2.getText().length());
}
holder.text2.setBackgroundColor(Color.parseColor("#424242"));
holder.text2Text.setBackgroundColor(Color.parseColor("#424242"));
}
}
};
text2FocusListener.setHolder(holder);
text1FocusListener.setHolder(holder);
text1Watcher.setView(holder);
text2Watcher.setView(holder);
holder.text2.addTextChangedListener(text2Watcher);
holder.text1.addTextChangedListener(text1Watcher);
holder.text2.setOnFocusChangeListener(text2FocusListener);
holder.text1.setOnFocusChangeListener(text1FocusListener);
holder.title.setText(Diety.title);
holder.banner.setImageResource(Diety.banner);
return ListItem;
}
static class Holder {
ImageView banner;
TextView title;
EditText text1;
EditText text2;
TextView text2Text;
TextView text1Text;
}
}
Here's my moneyConversion class:
public class moneyConversion {
public int currentlyActive;
public int counterTotal;
Context context;
public static double gettext1Value(double value){
double newValue = (value / 1.05);
return newValue;
}
public static double gettext2Value(double value){
double newValue = (value * 1.05);
return newValue;
}
public static String format(double value){
DecimalFormat df = new DecimalFormat();
df.applyPattern("###,###,###,###.##");
String formattedValue = df.format(value);
Log.d(TAG, formattedValue);
return formattedValue;
}
public static String formatLive(String s) {
if (s.endsWith(".")) {
return s;
}
DecimalFormat df = new DecimalFormat();
df.applyPattern("###,###,###,###.##");
Double value = Double.parseDouble(s);
String formattedValue = df.format(value);
// Log.d(TAG, formattedValue);
return formattedValue;
}
public static double unformat(String s){
if (s != null && s.isEmpty() ){
return 0;
}
String string = s.replace(",", "");
if (string.isEmpty()) {
return 0;
}
// if (string.equals(".")){
// return Double.parseDouble("0");
// }
return Double.parseDouble(string);
}
public static String unformatToString(String s){
if (s != null && s.isEmpty() ){
return "0";
}
String string = s.replace(",", "");
if (string.isEmpty()) {
return "0";
}
// if (string.equals(".")){
// return Double.parseDouble("0");
// }
return string;
}
}
Write your textchangeListener inside if (ListItem == null) { condition block, it will work.
What actually happening in your case is whenever you scroll your listview on change listener will call for each and every row that is visible.
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.