I wrote a wrapper around FirestorePagingAdapter. This works fine most of the times. But there are occasions where this crashes with
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter
I will show you the complete wrapper. Also the last log message I see before the crash is
[Paging adapter] Data loading finished.
I also noticed, when I slowly scroll the list it works fine. Only if I scroll the list fast it crashes eventually.
So here is the code. I cannot figure out where the problem is. Any help is highly appreciated
public abstract class PagingAdapter<T extends RecyclerItem> extends FirestorePagingAdapter<T, RecyclerViewHolder<T, ? extends ViewBinding>> implements Function1<CombinedLoadStates, Unit> {
protected final String TAG = this.getClass().getSimpleName();
private final SnapshotParser<T> mParser;
private SortedList<T> mListItems;
private int mTryCount;
private boolean mReverseFill = false;
private PagingAdapter(#NonNull FirestorePagingOptions<T> options, PagingAdapterCallback<T> callback) {
super(options);
mListItems = new SortedList(RecyclerItem.class, new SortedListAdapterCallback<T>(this) {
#Override
public int compare(T o1, T o2) {
return callback.compare(o1, o2);
}
#Override
public boolean areContentsTheSame(T oldItem, T newItem) {
return callback.areContentsTheSame(oldItem, newItem);
}
#Override
public boolean areItemsTheSame(T item1, T item2) {
return callback.areContentsTheSame(item1, item2);
}
});
mParser = options.getParser();
this.mTryCount = 0;
}
public void setReverseFill(boolean reverseFill) {
this.mReverseFill = reverseFill;
}
#Override
public int getItemViewType(int position) {
return getList().get(position).getRecyclerItemType().getId();
}
public SortedList<T> getList() {
return mListItems;
}
#NonNull
#Override
public RecyclerViewHolder<T, ? extends ViewBinding> onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
RecyclerViewHolder<T, ? extends ViewBinding> viewHolder = onCreateViewHolder(parent, RecyclerItemType
.get(viewType));
if (viewHolder == null) {
throw new NullPointerException("Your list contains items for that you did not specify a view holder for");
}
return viewHolder;
}
public abstract RecyclerViewHolder<T, ? extends ViewBinding> onCreateViewHolder(#NonNull ViewGroup parent, RecyclerItemType itemType);
#Override
public void onBindViewHolder(RecyclerViewHolder<T, ? extends ViewBinding> holder, int position) {
try {
//and trigger the paging to load around with the correct "position"
super.onBindViewHolder(holder, getPagingPosition(position)); //<--Needed to page the list, be we do not really use it (see below)
} catch (Exception ignore) {
//If this fails, because there are less items in the list, do not crash, this is fine
Log.d(TAG, "onBindViewHolder: ");
}
holder.bind(getList().get(position));
}
protected int getPagingPosition(int requestedPosition) {
return requestedPosition;
}
#Override
protected void onBindViewHolder(#NonNull RecyclerViewHolder<T, ? extends ViewBinding> holder, int position, #NonNull T model) {
//We do not use this method
}
#Override
public int getItemCount() {
return getList().size();
}
#Override
public Unit invoke(CombinedLoadStates states) {
LoadState refresh = states.getRefresh();
LoadState append = states.getAppend();
if (refresh instanceof LoadState.Error || append instanceof LoadState.Error) {
//The previous load (either initial or additional) failed
Log.d(TAG, "[Paging adapter] An error occurred while loading the data");
if (mTryCount < 3) {
mTryCount += 1;
retry();
}
}
if (refresh instanceof LoadState.Loading) {
Log.d(TAG, "[Paging adapter] Loading initial data");
}
if (append instanceof LoadState.Loading) {
Log.d(TAG, "[Paging adapter] Loading more data");
}
if (append instanceof LoadState.NotLoading) {
LoadState.NotLoading notLoading = (LoadState.NotLoading) append;
if (notLoading.getEndOfPaginationReached()) {
Log.d(TAG, "[Paging adapter] No further documents");
mTryCount = 0;
return null;
}
if (refresh instanceof LoadState.NotLoading) {
Log.d(TAG, "[Paging adapter] Data loading finished");
mTryCount = 0;
List<T> items = new ArrayList<>();
if (mReverseFill) {
for (int i = snapshot().size() - 1; i >= 0; i--) {
T currentItem = mParser.parseSnapshot(snapshot().get(i));
if (getList().indexOf(currentItem) == SortedList.INVALID_POSITION)
items.add(currentItem);
}
} else {
for (DocumentSnapshot snapshot : snapshot()) {
T currentItem = mParser.parseSnapshot(snapshot);
if (getList().indexOf(currentItem) == SortedList.INVALID_POSITION)
items.add(currentItem);
}
}
addAll(items);
return null;
}
}
return null;
}
public void addAll(Collection<T> items) {
getList().beginBatchedUpdates();
getList().addAll(items);
getList().endBatchedUpdates();
}
public abstract static class PagingAdapterCallback<T extends RecyclerItem> {
public abstract int compare(T o1, T o2);
public abstract boolean areContentsTheSame(T oldItem, T newItem);
public abstract boolean areItemsTheSame(T item1, T item2);
}
}
Just in case you wonder. These are the other two wrapper:
public abstract class RecyclerViewHolder<T extends RecyclerItem, E extends ViewBinding> extends RecyclerView.ViewHolder {
public final String TAG = this.getClass().getSimpleName();
protected final E b;
private final Context mContext;
protected RecyclerViewHolder(#NonNull E b) {
super(b.getRoot());
this.b = b;
this.mContext = b.getRoot().getContext();
}
public abstract void bind(T item);
protected Context getContext() {
return mContext;
}
protected Context requireContext() {
return getContext();
}
protected String getString(int resId) {
return mContext.getString(resId);
}
protected String getString(int resId, Object... args) {
return mContext.getString(resId, args);
}
}
and
public interface RecyclerItem {
#NonNull
RecyclerItemType getRecyclerItemType();
default void setRecyclerItemType(RecyclerItemType type){
//Does not do anything per default
}
}
Related
Can someone explain my error by showing me why each update of the Room database causes an event on the liveData despite the removeObservers that I apply before?
When I reactivate observe, I have as many events as there were updates. If I do 6 updateLobjet, I can have updateLobjetsList 8 times.
What should I do to be notified only once, or none if one is not possible, but continuing to observe for next event?
I need to use a LiveData to retrieve my data from the Room database and have it displayed by the RecyclerView.
I really need help to solve that.
Fragment:
public class MainFragment extends Fragment {
private ArrayList<Lobjet> mLobjets = new ArrayList<>();
RecyclerView mRecyclerView;
private LobjetViewModel mLobjetViewModel;
SampleAdapter mSampleAdapter;
LiveData<List<Lobjet>> mLDgetAll;
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
this.mSampleAdapter = new SampleAdapter();
this.mRecyclerView = view.findViewById(R.id.sampleRecyclerView);
this.mRecyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));
this.mRecyclerView.setAdapter(this.mSampleAdapter);
Button add = view.findViewById(R.id.add);
add.setOnClickListener(v -> {
Random r = new Random();
this.mLobjetViewModel.insertLobjet(new Lobjet("Lobjet" + (r.nextInt(9999)), this.mLobjets.size()));
});
this.mLobjetViewModel = new ViewModelProvider(this, ViewModelFactory.getInstance(getContext())).get(LobjetViewModel.class);
this.mLDgetAll = this.mLobjetViewModel.getAll();
this.mLDgetAll.observe(getViewLifecycleOwner(), this::updateLobjetsList);
this.setUpItemTouchHelper();
return view;
}
public void updateLobjetsList(List<Lobjet> lobjets) {
this.mSampleAdapter.setAdapterDatas(this.mLobjets = new ArrayList<>(lobjets));
}
private void updateLobjet(Lobjet lobjet) {
this.mLobjetViewModel.updateLobjet(lobjet);
}
private void setUpItemTouchHelper() {
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) {
#Override
public int getSwipeDirs(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder) {
// pas de swipe en mode sélection d'articles
return super.getSwipeDirs(recyclerView, viewHolder);
}
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
final int from = viewHolder.getBindingAdapterPosition();
final int to = target.getBindingAdapterPosition();
Collections.swap(mLobjets, from, to);
mSampleAdapter.notifyItemMoved(from, to);
return true;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
}
// mettre à jour les données à la fin du drop
#Override
public void clearView(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
if (mLDgetAll != null && mLDgetAll.hasObservers())
mLDgetAll.removeObservers(getViewLifecycleOwner());
for (int i = 0; i < mLobjets.size(); i++) {
mLobjets.get(i).setOrder(i);
updateLobjet(mLobjets.get(i));
}
if (mLDgetAll != null) {
mLDgetAll.observe(getViewLifecycleOwner(),
lobjets -> {
updateLobjetsList(lobjets);
});
}
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
itemTouchHelper.attachToRecyclerView(this.mRecyclerView);
}
}
Model:
#Entity(indices = {
#Index(value = {"id"}, unique = true),
#Index(value = {"lobjetName"}, unique = true)
})
public class Lobjet {
#PrimaryKey(autoGenerate = true)
private long id;
#ColumnInfo(name = "lobjetName")
private String name;
private long order;
public Lobjet(String name, long order) {
this.name = name;
this.order = order;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public void setId(long id) {
this.id = id;
}
public long getOrder() {
return order;
}
public void setOrder(long order) {
this.order = order;
}
}
Dao:
#Dao
public interface LobjetDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
long createLobjet(Lobjet lobjet);
#Query("SELECT * FROM Lobjet ORDER BY `order`, lobjetName")
LiveData<List<Lobjet>> getAll();
#Update
int updateLobjet(Lobjet lobjet);
}
Database:
#Database(
version = 1,
entities = {Lobjet.class},
)
public abstract class getDB extends RoomDatabase {
private static final String DB_NAME = "MyDatabase.db";
private static getDB INSTANCE;
public static synchronized getDB getInstance(Context context) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), getDB.class, DB_NAME)
.fallbackToDestructiveMigration()
.build();
}
return INSTANCE;
}
public abstract LobjetDao lobjetDao();
}
Factory:
public class ViewModelFactory implements ViewModelProvider.Factory {
private final LobjetDataRepository lobjetDataSource;
private final Executor executor;
private static volatile ViewModelFactory factory;
public static ViewModelFactory getInstance(Context context) {
if (factory == null) {
synchronized (ViewModelFactory.class) {
if (factory == null) {
factory = new ViewModelFactory(context);
}
}
}
return factory;
}
private ViewModelFactory(Context context) {
getDB database = getDB.getInstance(context);
this.lobjetDataSource = new LobjetDataRepository(database.lobjetDao());
this.executor = Executors.newSingleThreadExecutor();
}
#Override
#NotNull
public <T extends ViewModel> T create(Class<T> modelClass) {
if (modelClass.isAssignableFrom(LobjetViewModel.class)) {
return (T) new LobjetViewModel(lobjetDataSource, executor);
}
throw new IllegalArgumentException("Unknown ViewModel class");
}
}
Repository:
public class LobjetDataRepository {
private final LobjetDao mLobjetDao;
public LobjetDataRepository(LobjetDao lobjetDao) {
this.mLobjetDao = lobjetDao;
}
public LiveData<List<Lobjet>> getAll() {
return this.mLobjetDao.getAll();
}
public void createLobjet(Lobjet lobjet) {
lobjet.setId(mLobjetDao.createLobjet(lobjet));
}
public void updateLobjet(Lobjet lobjet) {
mLobjetDao.updateLobjet(lobjet);
}
}
ViewModel:
public class LobjetViewModel extends ViewModel {
private final LobjetDataRepository lobjetDataSource;
private final Executor executor;
public LobjetViewModel(LobjetDataRepository lobjetDataSource, Executor executor) {
this.lobjetDataSource = lobjetDataSource;
this.executor = executor;
}
public LiveData<List<Lobjet>> getAll() {
return lobjetDataSource.getAll();
}
public void insertLobjet(Lobjet lobjet) {
executor.execute(() -> lobjetDataSource.createLobjet(lobjet));
}
public void updateLobjet(Lobjet lobjet) {
executor.execute(() -> lobjetDataSource.updateLobjet(lobjet));
}
}
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'm using a hashmap to get data from another class. I check the logcat and the data for hashmap has been set and it contains the key. But when I try to get the hashmap from the other class I'm getting NullPointerException and when I try to check if the map contains the key it doesn't
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.continueItem:
CustomAdapter a = new CustomAdapter();
CustomAdapter.InputTextListener i = a.new InputTextListener();
HashMap<String, Integer> hashMap = i.getHashMap();
inputTime = hashMap.get("EDITTEXT VALUE");
Log.d(TAG, "onOptionsItemSelected: " + hashMap.get("EDITTEXT VALUE"));
Log.d(TAG, "onOptionsItemSelected: " + hashMap.containsKey("EDITTEXT VALUE"));
retrieveInputTime(inputTime);
break;
}
return super.onOptionsItemSelected(item);
}
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
private static final String TAG = "CustomAdapter";
private ArrayList<Integer> mWorkTW = new ArrayList<>();
private ArrayList<Integer> mWorkET = new ArrayList<>();
private ArrayList<Integer> mRestTW = new ArrayList<>();
private ArrayList<Integer> mRestET = new ArrayList<>();
private Context mContext;
private int numberOfIntervals;
public CustomAdapter() {
}
public CustomAdapter(Context context, ArrayList<Integer> mWorkTW, ArrayList<Integer> mWorkET, ArrayList<Integer> mRestTW, ArrayList<Integer> mRestET, int numberOfIntervals) {
this.mWorkTW = mWorkTW;
this.mWorkET = mWorkET;
this.mRestTW = mRestTW;
this.mRestET = mRestET;
this.mContext = context;
this.numberOfIntervals = numberOfIntervals;
//this.inputTimeIntegerWET = inputTimeIntegerWET;
Log.d(TAG, "CustomAdapter: " + numberOfIntervals);
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View customView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.time_row, viewGroup, false);
ViewHolder holder = new ViewHolder(customView, new InputTextListener());
return holder;
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder viewHolder, final int i) {
Log.d(TAG, "onBindViewHolder: called");
viewHolder.workTextView.setText(R.string.work_text_view);
viewHolder.restTextView.setText(R.string.rest_text_view);
viewHolder.workEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus)
viewHolder.workEditText.setHint("");
else
viewHolder.workEditText.setHint(mWorkET.get(viewHolder.getAdapterPosition()));
}
});
viewHolder.restEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus)
viewHolder.restEditText.setHint("");
else
viewHolder.restEditText.setHint(mRestET.get(viewHolder.getAdapterPosition()));
}
});
}
#Override
public int getItemCount() {
Log.d(TAG, "" + numberOfIntervals);
return numberOfIntervals;
}
public class ViewHolder extends RecyclerView.ViewHolder {
public InputTextListener inputTextListener;
TextView workTextView;
EditText workEditText;
TextView restTextView;
EditText restEditText;
ConstraintLayout parentLayout;
public ViewHolder(#NonNull View itemView, InputTextListener inputTextListener) {
super(itemView);
workTextView = itemView.findViewById(R.id.workTextView);
workEditText = itemView.findViewById(R.id.workEditText);
restTextView = itemView.findViewById(R.id.restTextView);
restEditText = itemView.findViewById(R.id.restEditText);
parentLayout = itemView.findViewById(R.id.parentLayout);
this.inputTextListener = inputTextListener;
workEditText.addTextChangedListener(inputTextListener);
}
}
class InputTextListener implements TextWatcher {
String inputTimeString;
int inputTime;
HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
public HashMap<String, Integer> getHashMap() {
return hashMap;
}
public InputTextListener() {
}
public void setHashMap(HashMap<String, Integer> hashMap) {
this.hashMap = hashMap;
}
/*public int getInputTime() {
return inputTime;
}*/
public void setInputTime(int inputTime) {
this.inputTime= inputTime;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
try {
Log.d(TAG, "onTextChanged: I've made it to here!");
inputTimeString = s.toString().trim();
inputTime = Integer.parseInt(inputTimeString);
setInputTime(inputTime);
hashMap.put("EDITTEXT VALUE", inputTime);
Log.d(TAG, "onTextChanged: " + inputTime);
int bla = inputTime + 2;
Log.d(TAG, "onTextChanged: " + bla);
Log.d(TAG, "onTextChanged: " + hashMap.containsKey("EDITTEXT VALUE"));
Log.d(TAG, "onTextChanged: " + hashMap.get("EDITTEXT VALUE"));
setHashMap(hashMap);
} catch (NumberFormatException NFE) {
mWorkET = null;
}
}
#Override
public void afterTextChanged(Editable s) {
}
}
}
I'm expecting for the hashmap being able to access the data from the other class.
Here is some of the code you posted:
CustomAdapter a = new CustomAdapter();
CustomAdapter.InputTextListener i = a.new InputTextListener();
HashMap<String, Integer> hashMap = i.getHashMap();
What this is going to do is create a new instance of your CustomAdapter class, then create a new instance of your InputTextListener class, and finally retrieve the HashMap stored inside the text listener.
Again, this is all happening with new instances of these classes. Therefore, the HashMap is empty (since nothing has populated it).
You are probably assuming that i would be the "same" listener instance as you're using elsewhere in your app. This is not the case. You will need to access that listener somehow rather than creating a new instance.
Looking at your code, this doesn't really seem feasible. Each ViewHolder has its own instance of InputTextListener... how will your options menu know which ViewHolder you're trying to interact with?
Chances are good that you're going to need to go back to the drawing board and come up with a different way to solve whatever problem you're attempting.
İ have a problem about RecyclerView filter.
İ am using edittext on text change method for filter text query in recyclerview but when i filter my Product List is changing.
Note : all of that in fragment and fragment in viewpager.
My problem is that : when i write something it is working but at the same time my product list's elements are changing to result of filter.
So in example at first
MyList Has 40 items
FilteredDataList is empty
After i write "a" in edittext after that FilteredDataList is has 30 items but MyList has same 30 items. But i have not set anything to Mylist
My Data List ,i get it from sqlite
productList = new ArrayList<>();
productList = handler.getAllProduct();
My Filter Method
private List<Product> filter(List<Product> models, String query) {
query = query.toLowerCase();
List<Product> filteredModelList = new ArrayList<>();
filteredModelList.clear();
for (Product model : models) {
final String text = model.get_ProductName().toLowerCase();
if (text.contains(query)) {
filteredModelList.add(model);
}
}
return filteredModelList;
}
My Edittext OnChange Metod
searchEdt.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
}
#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 (s.length() != 0) {
List<Product> filteredModelList = filter( productList, s.toString());
rcAdapter.animateTo(filteredModelList);
pager_recycler_view.scrollToPosition(0);
} else {
rcAdapter.animateTo(productList);
pager_recycler_view.scrollToPosition(0);
}
}
});
My AdapterClass
public class ProductRecyclerViewAdapter extends RecyclerView.Adapter< ProductRecyclerViewHolder > {
private List<Product> itemList;
private Context context;
public ProductRecyclerViewAdapter(Context context, List<Product> itemList) {
this.itemList = itemList;
this.context = context;
}
#Override
public ProductRecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.product_card_single_item, null);
ProductRecyclerViewHolder rcv = new ProductRecyclerViewHolder(layoutView);
return rcv;
}
#Override
public void onBindViewHolder(ProductRecyclerViewHolder holder, int position) {
holder.productName.setText(itemList.get(position).get_ProductName());
holder.productWatCode.setText("%" +itemList.get(position).get_ProductWatCode());
holder.productPOR.setText("%" +itemList.get(position).get_ProductPOR());
holder.productRSP.setText("£" +itemList.get(position).get_ProductRSP());
holder.productDescription.setText(itemList.get(position).get_ProductDescription());
holder.productSKU.setText(itemList.get(position).get_ProductSKU());
holder.productPrice.setText("£" + itemList.get(position).get_ProductPrice());
// holder.productCountCart.setText("");
Picasso.with(context)
.load( "http://firmabayi.com/images/ilanK/" +itemList.get(position).get_ProductPhoto())
.placeholder(R.drawable.add_icon)
.error(R.drawable.minus_icon)
.into(holder.productPhoto);
// holder.countryPhoto.setImageResource(itemList.get(position).get_ProductName());
}
#Override
public int getItemCount() {
return this.itemList.size();
}
public void animateTo(List<Product> itemList) {
applyAndAnimateRemovals(itemList);
applyAndAnimateAdditions(itemList);
applyAndAnimateMovedItems(itemList);
}
private void applyAndAnimateRemovals(List<Product> newModels) {
for (int i = itemList.size() - 1; i >= 0; i--) {
final Product model = itemList.get(i);
if (!newModels.contains(model)) {
removeItem(i);
}
}
}
private void applyAndAnimateAdditions(List<Product> newModels) {
for (int i = 0, count = newModels.size(); i < count; i++) {
final Product model = newModels.get(i);
if (!itemList.contains(model)) {
addItem(i, model);
}
}
}
private void applyAndAnimateMovedItems(List<Product> newModels) {
for (int toPosition = newModels.size() - 1; toPosition >= 0; toPosition--) {
final Product model = newModels.get(toPosition);
final int fromPosition = itemList.indexOf(model);
if (fromPosition >= 0 && fromPosition != toPosition) {
moveItem(fromPosition, toPosition);
}
}
}
public Product removeItem(int position) {
final Product model = itemList.remove(position);
notifyItemRemoved(position);
return model;
}
public void addItem(int position, Product model) {
itemList.add(position, model);
notifyItemInserted(position);
}
public void moveItem(int fromPosition, int toPosition) {
final Product model = itemList.remove(fromPosition);
itemList.add(toPosition, model);
notifyItemMoved(fromPosition, toPosition);
}
}
i solved my problem it is only about adapter class one line :(
in adapter class
instead of
this.itemList = itemList;
use that
this.itemList = new ArrayList<>(itemList);
It is about your productList.
When you create a object like doing this;
Class a = b();
You are cloning your object. In deep, they are the same object.
So when you filtered object named a, b is being effected from this.
In short, don't do this. Instead of cloning object you should add each items to a from b by one by.
Like this;
productList = new ArrayList<>();
for( int i = 0 ; i <arrayFromSource.size() ; i++ )
{
productList.add(arrayFromSource.get(i));
}
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.