Add sheet from api step by step and dynamically - android

I am training to work with api and the problem arose that when scrolling the RecyclerView, I need the data to be added, namely, "page" is added, and thus the content is added.
I have achieved that before reaching the end of the list, the "onReachEndListener" method is triggered, but I cannot understand the implementation of adding to the sheet. PLEASE HELP ME!
public class MainActivity extends AppCompatActivity {
private SwitchMaterial switchMaterial;
private TextView textViewPopularity, textViewTopRated;
private RecyclerView recyclerViewPosters;
private MovieAdapter movieAdapter;
private MainViewModel viewModel;
private ProgressBar progressBar;
private static final int LOADER_ID = 122;
private LoaderManager loaderManager;
private static int page = 1;
private static String methodOfSort;
private static boolean isLoaning = true;
private static String lang;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_menu, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.itemMain:
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
break;
case R.id.itemFavourite:
Intent i = new Intent(this, FavouriteActivity.class);
startActivity(i);
break;
}
return super.onOptionsItemSelected(item);
}
private int getColumnCount() {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width = (int) (displayMetrics.widthPixels / displayMetrics.density);
return width / 185 > 2 ? width / 2 : 2;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lang = Locale.getDefault().getLanguage();
viewModel = new ViewModelProvider(this).get(MainViewModel.class);
recyclerViewPosters = findViewById(R.id.recyclerViewPosters);
switchMaterial = findViewById(R.id.switchSort);
textViewPopularity = findViewById(R.id.textViewPopularity);
textViewTopRated = findViewById(R.id.textViewTopRated);
progressBar = findViewById(R.id.progressBarLoading);
movieAdapter = new MovieAdapter();
recyclerViewPosters.setLayoutManager(new GridLayoutManager(this, getColumnCount()));
recyclerViewPosters.setAdapter(movieAdapter);
switchMaterial.setChecked(true);
myOnClick();
LiveData<List<Movie>> moviesFromLiveData = viewModel.getMovies();
moviesFromLiveData.observe(this, new Observer<List<Movie>>() {
#Override
public void onChanged(#Nullable List<Movie> movies) {
movieAdapter.setMovies(movies);
}
});
}
private void myOnClick() {
switchMaterial.setOnCheckedChangeListener((compoundButton, b) -> {
page = 1;
setMethodOfSort(b);
});
switchMaterial.setChecked(false);
textViewPopularity.setOnClickListener(view -> {
setMethodOfSort(false);
switchMaterial.setChecked(false);
});
textViewTopRated.setOnClickListener(view -> {
setMethodOfSort(true);
switchMaterial.setChecked(true);
});
movieAdapter.setOnPosterClickListener(position -> {
Movie movie = movieAdapter.getMovies().get(position);
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
intent.putExtra("id", movie.getId());
startActivity(intent);
});
movieAdapter.setOnReachEndListener(() -> {
if (!isLoaning){
viewModel.deleteAllMovies();
progressBar.setVisibility(View.VISIBLE);
viewModel.loadDataImage(lang, methodOfSort, Integer.toString(page));
Log.d("isLoaning", Integer.toString(page));
}
});
recyclerViewPosters.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (!recyclerView.canScrollVertically(1)) {
Log.d("isLoaning", Integer.toString(page));
LiveData<List<Movie>> moviesFromLiveData = viewModel.getMovies();
moviesFromLiveData.observe(MainActivity.this, new Observer<List<Movie>>() {
#Override
public void onChanged(#Nullable List<Movie> movies) {
viewModel.insertMovie(movies);
movieAdapter.addMovies(movies);
page++;
isLoaning = false;
}
});
}
}
});
}
private void setMethodOfSort(boolean b){
if(b) {
methodOfSort = SORT_BY_TOP_RATED;
Log.d("setMethodOfSort", methodOfSort);
textViewPopularity.setTextColor(getResources().getColor(R.color.white));
textViewTopRated.setTextColor(getResources().getColor(R.color.teal_200));
}
else {
methodOfSort = SORT_BY_POPULARITY;
Log.d("setMethodOfSort", methodOfSort);
textViewPopularity.setTextColor(getResources().getColor(R.color.teal_200));
textViewTopRated.setTextColor(getResources().getColor(R.color.white));
}
viewModel.loadData(lang, methodOfSort ,Integer.toString(page));
}
Model:
public class MainViewModel extends AndroidViewModel {
private static MovieDatabase database;
private LiveData<List<Movie>> movies;
private LiveData<List<FavouriteMovie>> favouriteMovies;
private LiveData<String> methodOfSort;
private CompositeDisposable compositeDisposable;
private MutableLiveData<Throwable> errors;
public static final String SMALL_POSTER_SIZE = "w185";
private static final String KEY_POSTER_PATH = "poster_path";
public static final String SORT_BY_POPULARITY = "popularity.desc";
public static final String SORT_BY_TOP_RATED = "vote_average.desc";
public static final int POPULARITY = 0;
public static final int TOP_RATED = 1;
public MainViewModel(#NonNull Application application) {
super(application);
database = MovieDatabase.getInstance(getApplication());
movies = database.movieDao().getAllMovies();
favouriteMovies = database.movieDao().getAllFavouriteMovies();
errors = new MutableLiveData<>();
}
public LiveData<Throwable> getErrors() {
return errors;
}
public void clearErrors() {
errors.setValue(null);
}
public void loadData(String lang, String sort, String page) {
ApiFactory apiFactory = ApiFactory.getInstance();
ApiService apiService = apiFactory.getApiService();
compositeDisposable = new CompositeDisposable();
Disposable disposable = apiService.getMovieResponse("32f91e104228c0c9ff3630899838e82e",
lang,
sort,
"1000",
page)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MovieResponse>() {
#Override
public void accept(MovieResponse movieResponse) throws Exception {
deleteAllMovies();
insertMovie(movieResponse.getMovies());
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
errors.setValue(throwable);
}
});
compositeDisposable.add(disposable);
}
public void loadDataImage(String lang, String sort, String page) {
ApiFactory apiFactory = ApiFactory.getInstance();
ApiService apiService = apiFactory.getApiService();
compositeDisposable = new CompositeDisposable();
Disposable disposable = apiService.getMovieResponse(
"32f91e104228c0c9ff3630899838e82e",
lang,
sort,
"1000",
page)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MovieResponse>() {
#Override
public void accept(MovieResponse movieResponse) throws Exception {
insertMovie(movieResponse.getMovies());
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
errors.setValue(throwable);
}
});
compositeDisposable.add(disposable);
}
public Movie getMovieById(int id) {
try {
return new GetMovieTask().execute(id).get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
public FavouriteMovie getFavouriteMovieById(int id) {
try {
return new GetFavouriteMovieTask().execute(id).get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
public LiveData<List<FavouriteMovie>> getFavouriteMovies() {
return favouriteMovies;
}
public void deleteAllMovies() {
new DeleteMoviesTask().execute();
}
#SuppressWarnings("unchecked")
public void insertMovie(List<Movie> movie) {
new InsertTask().execute(movie);
}
public void deleteMovie(Movie movie) {
new DeleteTask().execute(movie);
}
public LiveData<List<Movie>> getMovies() {
return movies;
}
public void insertFavouriteMovie(FavouriteMovie movie) {
new InsertFavouriteTask().execute(movie);
}
public void deleteFavouriteMovie(FavouriteMovie movie) {
new DeleteFavouriteTask().execute(movie);
}
private static class DeleteFavouriteTask extends AsyncTask<FavouriteMovie, Void, Void> {
#Override
protected Void doInBackground(FavouriteMovie... movies) {
if (movies != null && movies.length > 0) {
database.movieDao().deleteFavouriteMovie(movies[0]);
}
return null;
}
}
private static class InsertFavouriteTask extends AsyncTask<FavouriteMovie, Void, Void> {
#Override
protected Void doInBackground(FavouriteMovie... movies) {
if (movies != null && movies.length > 0) {
database.movieDao().insertFavouriteMovie(movies[0]);
}
return null;
}
}
private static class DeleteTask extends AsyncTask<Movie, Void, Void> {
#Override
protected Void doInBackground(Movie... movies) {
if (movies != null && movies.length > 0) {
database.movieDao().deleteMovie(movies[0]);
}
return null;
}
}
private static class InsertTask extends AsyncTask<List<Movie>, Void, Void> {
#Override
protected Void doInBackground(List<Movie>... movies) {
if (movies != null && movies.length > 0) {
database.movieDao().insertMovie(movies[0]);
}
return null;
}
}
private static class DeleteMoviesTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... integers) {
database.movieDao().deleteAllMovies();
return null;
}
}
private static class GetMovieTask extends AsyncTask<Integer, Void, Movie> {
#Override
protected Movie doInBackground(Integer... integers) {
if (integers != null && integers.length > 0) {
return database.movieDao().getMovieById(integers[0]);
}
return null;
}
}
private static class GetFavouriteMovieTask extends AsyncTask<Integer, Void, FavouriteMovie> {
#Override
protected FavouriteMovie doInBackground(Integer... integers) {
if (integers != null && integers.length > 0) {
return database.movieDao().getFavouriteMovieById(integers[0]);
}
return null;
}
}
#Override
protected void onCleared() {
compositeDisposable.dispose();
super.onCleared();
}
}
Adapter:
public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MovieViewHolder> {
private List<Movie> movies;
private OnPosterClickListener onPosterClickListener;
private OnReachEndListener onReachEndListener;
private onStartLoadingListener onStartLoadingListener;
public MovieAdapter(){
movies = new ArrayList<>();
}
public interface OnPosterClickListener{
void OnPosterClick(int position);
}
public interface OnReachEndListener{
void onReachEnd();
}
public interface onStartLoadingListener{
void onStartLoading();
}
public void setOnStartLoadingListener(onStartLoadingListener onStartLoadingListener) {
this.onStartLoadingListener = onStartLoadingListener;
}
public void setOnPosterClickListener(OnPosterClickListener onPosterClickListener) {
this.onPosterClickListener = onPosterClickListener;
}
public void setOnReachEndListener(OnReachEndListener onReachEndListener) {
this.onReachEndListener = onReachEndListener;
}
#NonNull
#Override
public MovieViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.movie_item, parent, false);
return new MovieViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull MovieViewHolder holder, int position) {
if (movies.size() >= 20 && position > movies.size() - 4 && onReachEndListener != null){
onReachEndListener.onReachEnd();
}
Movie movie = movies.get(position);
Picasso.get().load(movie.getPosterPath()).into(holder.imageViewSmallPoster);
}
#Override
public int getItemCount() {
return movies.size();
}
class MovieViewHolder extends RecyclerView.ViewHolder{
private ImageView imageViewSmallPoster;
public MovieViewHolder(#NonNull View itemView) {
super(itemView);
imageViewSmallPoster = itemView.findViewById(R.id.imageViewSmallPoster);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(onPosterClickListener != null){
onPosterClickListener.OnPosterClick(getAdapterPosition());
}
}
});
}
}
public void clear(){
this.movies.clear();
notifyDataSetChanged();
}
public List<Movie> getMovies() {
return movies;
}
public void addMovies(List<Movie> movies){
this.movies.addAll(movies);
notifyDataSetChanged();
}
public void setMovies(List<Movie> movies) {
this.movies = movies;
notifyDataSetChanged();
}
}

Related

Unable to update view on data change in Epoxy Controller

I am using Epoxy Controller for Recycler View. I am having trouble changing the view after data changed by the user action.
Basically I have a switch button in a view which is used inside a recycler view and I am trying to update the view on switch button state change. I am calling requestModelBuild() in setProductList() function of the epoxy controller but change is not reflected in the view.
public class SellerInventoryListEpoxyController extends EpoxyController {
private List<Product> productList = Collections.emptyList();
private Context context;
private SellerInventoryListEpoxyController.Callbacks callbacks;
public void setProductList(List<Product> productList, Context context, SellerInventoryListEpoxyController.Callbacks callbacks) {
this.productList = productList;
this.context = context;
this.callbacks = callbacks;
requestModelBuild();
}
#Override
protected void buildModels() {
for (int i = 0; i < productList.size(); i++) {
new InventoryProductDetailModel_()
.id(productList.get(i).getId())
.product(productList.get(i))
.position(i)
.listSize(productList.size())
.callbacks(callbacks)
.context(context)
.addTo(this);
}
}
public interface Callbacks {
void onViewComboClick(Product productComboList);
void onProductListingStatusChanged(Boolean newStatus, int productSellerId);
void onRecyclerViewReachEnd();
}
}
public class InventoryProductDetailModel extends EpoxyModelWithHolder<InventoryProductDetailModel.ViewHolder> implements CompoundButton.OnCheckedChangeListener {
#EpoxyAttribute
Product product;
#EpoxyAttribute
int position;
#EpoxyAttribute
int listSize;
#EpoxyAttribute
Context context;
#EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
SellerInventoryListEpoxyController.Callbacks callbacks;
#Override
protected ViewHolder createNewHolder() {
return new ViewHolder();
}
#Override
protected int getDefaultLayout() {
return R.layout.inventroy_item_layout;
}
private DrawableCrossFadeFactory factory =
new DrawableCrossFadeFactory.Builder().setCrossFadeEnabled(true).build();
#Override
public void bind(#NonNull InventoryProductDetailModel.ViewHolder holder) {
super.bind(holder);
holder.quantity.setText(String.format("Available :%d", product.getTotalStock()));
holder.brand.setText(product.getProduct().getBrandName());
holder.title.setText(product.getProduct().getTitle());
holder.category.setText(product.getProduct().getCategoryName());
holder.sku.setText(String.format("Sku: %s", product.getSku()));
holder.inventoryItemConstrainLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(context, ProductDetailActivity.class);
intent.putExtra("product_id", product.getId());
context.startActivity(intent);
}
});
if (product.getProductCombos() != null && product.getProductCombos().size() > 0) {
holder.variationCount.setVisibility(View.GONE);
holder.comboBtn.setVisibility(View.VISIBLE);
holder.comboBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
callbacks.onViewComboClick(product);
}
});
}
if (product.getSellerActive()) {
holder.productStatusSwitch.setText("Active");
holder.productStatusSwitch.setOnCheckedChangeListener(null);
holder.productStatusSwitch.setChecked(true);
holder.productStatusSwitch.setOnCheckedChangeListener(this);
holder.productStatusSwitch.setTextColor(context.getResources().getColor(R.color.colorAccent));
} else {
holder.productStatusSwitch.setText("Inactive");
holder.productStatusSwitch.setOnCheckedChangeListener(null);
holder.productStatusSwitch.setChecked(false);
holder.productStatusSwitch.setOnCheckedChangeListener(this);
holder.productStatusSwitch.setTextColor(Color.parseColor("#ff0000"));
}
holder.variationCount.setText(format("Variation(%d)", product.getVariantCount()));
holder.variationCount.setVisibility(View.VISIBLE);
holder.comboBtn.setVisibility(View.GONE);
loadImage(holder.productImage, Utils.getRequiredUrlForThisImage(holder.productImage, product.getProduct().getImage()));
if (position == listSize - 2) {
callbacks.onRecyclerViewReachEnd();
}
}
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
callbacks.onProductListingStatusChanged(isChecked, product.getId());
}
private void loadImage(ImageView imageView, String url) {
Glide.with(imageView.getContext()).asBitmap()
.load(Utils.getRequiredUrlForThisImage(imageView, url))
.apply(new RequestOptions().diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.fitCenter())
.transition(withCrossFade(factory))
.placeholder(R.mipmap.product)
.into(imageView);
}
#Override
public void unbind(#NonNull InventoryProductDetailModel.ViewHolder holder) {
super.unbind(holder);
}
public static class ViewHolder extends EpoxyHolder {
TextView quantity, brand, title, category, variationCount, comboBtn;
ImageView productImage, btn_product_detail;
ProgressBar progressBar;
ConstraintLayout inventoryItemConstrainLayout;
private TextView sku;
private Switch productStatusSwitch;
#Override
protected void bindView(#NonNull View itemView) {
productStatusSwitch = itemView.findViewById(R.id.productStatusSwitch);
quantity = itemView.findViewById(R.id.product_qty);
brand = itemView.findViewById(R.id.product_brand);
title = itemView.findViewById(R.id.product_title);
sku = itemView.findViewById(R.id.sku);
category = itemView.findViewById(R.id.product_category);
variationCount = itemView.findViewById(R.id.variantCount);
productImage = itemView.findViewById(R.id.product_image);
btn_product_detail = itemView.findViewById(R.id.btn_product_detail);
inventoryItemConstrainLayout = itemView.findViewById(R.id.inventory_item_constrain_layout);
comboBtn = itemView.findViewById(R.id.combo_btn);
progressBar = itemView.findViewById(R.id.progressbar);
progressBar.setVisibility(View.GONE);
}
}
#Override
public int hashCode() {
super.hashCode();
return product.hashCode();
}
#Override
public boolean equals(Object o) {
return super.equals(o);
}
}
private void addProductListingChangeObserver(final Boolean newStatus, final int productSellerId) {
ProductUpdate productUpdate = new ProductUpdate();
productUpdate.setSellerActive(newStatus);
mInventoryViewModel.updateProductSeller(productSellerId, productUpdate).observe(this, new Observer<Resource<ProductSeller>>() {
#Override
public void onChanged(Resource<ProductSeller> productSellerResource) {
if (productSellerResource.status == Status.ERROR) {
progressBar.setVisibility(View.GONE);
} else if (productSellerResource.status == Status.SUCCESS) {
progressBar.setVisibility(View.GONE);
if (productSellerResource.data != null && productSellerResource.data.isSellerActive() == newStatus) {
for (int i = 0; i < productList.size(); i++) {
if (productList.get(i).getId() == productSellerId) {
productList.get(i).setSellerActive(newStatus);
break;
}
}
sellerInventoryListEpoxyController.setProductList(productList, getContext(), InventoryFragment.this);
}
} else {
progressBar.setVisibility(View.VISIBLE);
}
}
});
}
In addProductListingChangeObserver() function one object of productList is modified and new productList is passed to the EpoxyController and requestModelbuild is called but the view is not modifying as expected.

Add Material Dialog from library

I want to use this from the material-dialog library without using the library in my build.gradle file.
Is there a way to do that?
Thanks to Abderrahim Soubai Elidrissi deleted answer I used LovelyDialog and implemented the following classes in my project:
AbsLovelyDialog.java
#SuppressWarnings({"unchecked", "WeakerAccess"})
public abstract class AbsLovelyDialog<T extends AbsLovelyDialog> {
private static final String KEY_SAVED_STATE_TOKEN = "key_saved_state_token";
private Dialog dialog;
private View dialogView;
private ImageView iconView;
private TextView topTitleView;
private TextView titleView;
private TextView messageView;
public AbsLovelyDialog(Context context) {
init(new AlertDialog.Builder(context));
}
public AbsLovelyDialog(Context context, int theme) {
init(new AlertDialog.Builder(context, theme));
}
private void init(AlertDialog.Builder dialogBuilder) {
dialogView = LayoutInflater.from(dialogBuilder.getContext()).inflate(getLayout(), null);
dialog = dialogBuilder.setView(dialogView).create();
iconView = findView(R.id.ld_icon);
titleView = findView(R.id.ld_title);
messageView = findView(R.id.ld_message);
topTitleView = findView(R.id.ld_top_title);
}
#LayoutRes
protected abstract int getLayout();
public T setMessage(#StringRes int message) {
return setMessage(string(message));
}
public T setMessage(CharSequence message) {
messageView.setVisibility(View.VISIBLE);
messageView.setText(message);
return (T) this;
}
public T setTitle(#StringRes int title) {
return setTitle(string(title));
}
public T setTopTitle(#StringRes int title) {
return setTopTitle(string(title));
}
public T setTitle(CharSequence title) {
titleView.setVisibility(View.VISIBLE);
titleView.setText(title);
return (T) this;
}
public T setTopTitle(CharSequence title) {
topTitleView.setVisibility(View.VISIBLE);
topTitleView.setText(title);
return (T) this;
}
public T setTopTitleColor(int color) {
topTitleView.setTextColor(color);
return (T) this;
}
public T setIcon(Bitmap bitmap) {
iconView.setVisibility(View.VISIBLE);
iconView.setImageBitmap(bitmap);
return (T) this;
}
public T setIcon(Drawable drawable) {
iconView.setVisibility(View.VISIBLE);
iconView.setImageDrawable(drawable);
return (T) this;
}
public T setIcon(#DrawableRes int iconRes) {
iconView.setVisibility(View.VISIBLE);
iconView.setImageResource(iconRes);
return (T) this;
}
public T setIconTintColor(int iconTintColor) {
iconView.setColorFilter(iconTintColor);
return (T) this;
}
public T setTitleGravity(int gravity) {
titleView.setGravity(gravity);
return (T) this;
}
public T setMessageGravity(int gravity) {
messageView.setGravity(gravity);
return (T) this;
}
public T setTopColor(#ColorInt int topColor) {
findView(R.id.ld_color_area).setBackgroundColor(topColor);
return (T) this;
}
public T setTopColorRes(#ColorRes int topColoRes) {
return setTopColor(color(topColoRes));
}
/*
* You should call method saveInstanceState on handler object and then use saved info to restore
* your dialog in onRestoreInstanceState. Static methods wasDialogOnScreen and getDialogId will
* help you in this.
*/
public T setInstanceStateHandler(int id, LovelySaveStateHandler handler) {
handler.handleDialogStateSave(id, this);
return (T) this;
}
public T setCancelable(boolean cancelable) {
dialog.setCancelable(cancelable);
return (T) this;
}
public T setSavedInstanceState(Bundle savedInstanceState) {
if (savedInstanceState != null) {
boolean hasSavedStateHere =
savedInstanceState.keySet().contains(KEY_SAVED_STATE_TOKEN) &&
savedInstanceState.getSerializable(KEY_SAVED_STATE_TOKEN) == getClass();
if (hasSavedStateHere) {
restoreState(savedInstanceState);
}
}
return (T) this;
}
public Dialog show() {
dialog.show();
return dialog;
}
public Dialog create() {
return dialog;
}
public void dismiss() {
dialog.dismiss();
}
void onSaveInstanceState(Bundle outState) {
outState.putSerializable(KEY_SAVED_STATE_TOKEN, getClass());
}
void restoreState(Bundle savedState) {
}
boolean isShowing() {
return dialog != null && dialog.isShowing();
}
protected String string(#StringRes int res) {
return dialogView.getContext().getString(res);
}
protected int color(#ColorRes int colorRes) {
return ContextCompat.getColor(getContext(), colorRes);
}
protected Context getContext() {
return dialogView.getContext();
}
protected <ViewClass extends View> ViewClass findView(int id) {
return (ViewClass) dialogView.findViewById(id);
}
protected class ClickListenerDecorator implements View.OnClickListener {
private View.OnClickListener clickListener;
private boolean closeOnClick;
protected ClickListenerDecorator(View.OnClickListener clickListener, boolean closeOnClick) {
this.clickListener = clickListener;
this.closeOnClick = closeOnClick;
}
#Override
public void onClick(View v) {
if (clickListener != null) {
if (clickListener instanceof LovelyDialogCompat.DialogOnClickListenerAdapter) {
LovelyDialogCompat.DialogOnClickListenerAdapter listener =
(LovelyDialogCompat.DialogOnClickListenerAdapter) clickListener;
listener.onClick(dialog, v.getId());
} else {
clickListener.onClick(v);
}
}
if (closeOnClick) {
dismiss();
}
}
}
}
LovelyChoiceDialog.java
public class LovelyChoiceDialog extends AbsLovelyDialog<LovelyChoiceDialog> {
private static final String KEY_ITEM_CHECKED_STATES = "key_item_checked_states";
private ListView choicesList;
private TextView confirmButton;
{
choicesList = findView(R.id.ld_choices);
}
public LovelyChoiceDialog(Context context) {
super(context);
}
public LovelyChoiceDialog(Context context, int theme) {
super(context, theme);
}
public <T> LovelyChoiceDialog setItems(T[] items, OnItemSelectedListener<T> itemSelectedListener) {
return setItems(Arrays.asList(items), itemSelectedListener);
}
public <T> LovelyChoiceDialog setItems(List<T> items, OnItemSelectedListener<T> itemSelectedListener) {
ArrayAdapter<T> adapter = new ArrayAdapter<>(getContext(),
R.layout.dialog_item_simple_text, android.R.id.text1,
items);
return setItems(adapter, itemSelectedListener);
}
public <T> LovelyChoiceDialog setItems(ArrayAdapter<T> adapter, OnItemSelectedListener<T> itemSelectedListener) {
choicesList.setOnItemClickListener(new ItemSelectedAdapter<>(itemSelectedListener));
choicesList.setAdapter(adapter);
return this;
}
public <T> LovelyChoiceDialog setItemsMultiChoice(T[] items, OnItemsSelectedListener<T> itemsSelectedListener) {
return setItemsMultiChoice(items, null, itemsSelectedListener);
}
public <T> LovelyChoiceDialog setItemsMultiChoice(T[] items, boolean[] selectionState, OnItemsSelectedListener<T> itemsSelectedListener) {
return setItemsMultiChoice(Arrays.asList(items), selectionState, itemsSelectedListener);
}
public <T> LovelyChoiceDialog setItemsMultiChoice(List<T> items, OnItemsSelectedListener<T> itemsSelectedListener) {
return setItemsMultiChoice(items, null, itemsSelectedListener);
}
public <T> LovelyChoiceDialog setItemsMultiChoice(List<T> items, boolean[] selectionState, OnItemsSelectedListener<T> itemsSelectedListener) {
ArrayAdapter<T> adapter = new ArrayAdapter<>(getContext(),
R.layout.dialog_item_simple_text_multichoice, android.R.id.text1,
items);
return setItemsMultiChoice(adapter, selectionState, itemsSelectedListener);
}
public <T> LovelyChoiceDialog setItemsMultiChoice(ArrayAdapter<T> adapter, OnItemsSelectedListener<T> itemsSelectedListener) {
return setItemsMultiChoice(adapter, null, itemsSelectedListener);
}
public <T> LovelyChoiceDialog setItemsMultiChoice(ArrayAdapter<T> adapter, boolean[] selectionState, OnItemsSelectedListener<T> itemsSelectedListener) {
LayoutInflater inflater = LayoutInflater.from(getContext());
View confirmBtnContainer = inflater.inflate(R.layout.dialog_item_footer_confirm, null);
confirmButton = (TextView) confirmBtnContainer.findViewById(R.id.ld_btn_confirm);
confirmButton.setOnClickListener(new ItemsSelectedAdapter<>(itemsSelectedListener));
choicesList.addFooterView(confirmBtnContainer);
ListView choicesList = findView(R.id.ld_choices);
choicesList.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
choicesList.setAdapter(adapter);
if (selectionState != null) {
for (int i = 0; i < selectionState.length; i++) {
choicesList.setItemChecked(i, selectionState[i]);
}
}
return this;
}
public LovelyChoiceDialog setConfirmButtonText(#StringRes int text) {
return setConfirmButtonText(string(text));
}
public LovelyChoiceDialog setConfirmButtonText(String text) {
if (confirmButton == null) {
throw new IllegalStateException(string(R.string.ex_msg_dialog_choice_confirm));
}
confirmButton.setText(text);
return this;
}
public LovelyChoiceDialog setConfirmButtonColor(int color) {
if (confirmButton == null) {
throw new IllegalStateException(string(R.string.ex_msg_dialog_choice_confirm));
}
confirmButton.setTextColor(color);
return this;
}
#Override
void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (isMultiChoiceList()) {
ListAdapter adapter = choicesList.getAdapter();
boolean[] checkedStates = new boolean[adapter.getCount()];
SparseBooleanArray checkedPositions = choicesList.getCheckedItemPositions();
for (int i = 0; i < checkedPositions.size(); i++) {
if (checkedPositions.valueAt(i)) {
checkedStates[checkedPositions.keyAt(i)] = true;
}
}
outState.putBooleanArray(KEY_ITEM_CHECKED_STATES, checkedStates);
}
}
#Override
void restoreState(Bundle savedState) {
super.restoreState(savedState);
if (isMultiChoiceList()) {
boolean[] checkedStates = savedState.getBooleanArray(KEY_ITEM_CHECKED_STATES);
if (checkedStates == null) {
return;
}
for (int index = 0; index < checkedStates.length; index++) {
choicesList.setItemChecked(index, checkedStates[index]);
}
}
}
#Override
protected int getLayout() {
return R.layout.dialog_choice;
}
private boolean isMultiChoiceList() {
return choicesList.getChoiceMode() == AbsListView.CHOICE_MODE_MULTIPLE;
}
public interface OnItemSelectedListener<T> {
void onItemSelected(int position, T item);
}
public interface OnItemsSelectedListener<T> {
void onItemsSelected(List<Integer> positions, List<T> items);
}
private class ItemSelectedAdapter<T> implements AdapterView.OnItemClickListener {
private OnItemSelectedListener<T> adaptee;
private ItemSelectedAdapter(OnItemSelectedListener<T> adaptee) {
this.adaptee = adaptee;
}
#Override
#SuppressWarnings("unchecked")
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (adaptee != null) {
adaptee.onItemSelected(position, (T) parent.getItemAtPosition(position));
}
dismiss();
}
}
private class ItemsSelectedAdapter<T> implements View.OnClickListener {
private OnItemsSelectedListener<T> adaptee;
private ItemsSelectedAdapter(OnItemsSelectedListener<T> adaptee) {
this.adaptee = adaptee;
}
#Override
#SuppressWarnings("unchecked")
public void onClick(View v) {
if (adaptee != null) {
SparseBooleanArray checkedItemPositions = choicesList.getCheckedItemPositions();
List<T> selectedItems = new ArrayList<>(checkedItemPositions.size());
List<Integer> selectedPositions = new ArrayList<>(checkedItemPositions.size());
ListAdapter adapter = choicesList.getAdapter();
for (int index = 0; index < adapter.getCount(); index++) {
if (checkedItemPositions.get(index)) {
selectedPositions.add(index);
selectedItems.add((T) adapter.getItem(index));
}
}
adaptee.onItemsSelected(selectedPositions, selectedItems);
}
dismiss();
}
}
}
LovelyDialogCompat.java
public class LovelyDialogCompat {
/**
* If you don't want to change implemented interfaces when migrating from standard dialogs
* to LovelyDialogs - use this method.
*/
public static View.OnClickListener wrap(Dialog.OnClickListener listener) {
return new DialogOnClickListenerAdapter(listener);
}
static class DialogOnClickListenerAdapter implements View.OnClickListener {
private Dialog.OnClickListener adapted;
DialogOnClickListenerAdapter(DialogInterface.OnClickListener adapted) {
this.adapted = adapted;
}
public void onClick(DialogInterface dialogInterface, int which) {
if (adapted != null) {
adapted.onClick(dialogInterface, which);
}
}
#Override
public void onClick(View v) {
}
}
}
LovelySaveStateHandler.java
public class LovelySaveStateHandler {
private static final String KEY_DIALOG_ID = "id";
private SparseArray<WeakReference<AbsLovelyDialog<?>>> handledDialogs;
public LovelySaveStateHandler() {
handledDialogs = new SparseArray<>();
}
public static boolean wasDialogOnScreen(Bundle savedInstanceState) {
return savedInstanceState.keySet().contains(KEY_DIALOG_ID);
}
public static int getSavedDialogId(Bundle savedInstanceState) {
return savedInstanceState.getInt(KEY_DIALOG_ID, -1);
}
public void saveInstanceState(Bundle outState) {
for (int index = handledDialogs.size() - 1; index >= 0; index--) {
WeakReference<AbsLovelyDialog<?>> dialogRef = handledDialogs.valueAt(index);
if (dialogRef.get() == null) {
handledDialogs.remove(index);
continue;
}
AbsLovelyDialog<?> dialog = dialogRef.get();
if (dialog.isShowing()) {
dialog.onSaveInstanceState(outState);
outState.putInt(KEY_DIALOG_ID, handledDialogs.keyAt(index));
return;
}
}
}
void handleDialogStateSave(int id, AbsLovelyDialog<?> dialog) {
handledDialogs.put(id, new WeakReference<AbsLovelyDialog<?>>(dialog));
}
}

RecyclerView reload same data when refresh

I have a problem, when i swipe to refresh the data, the first swipe is ok but after that every swipe reload and add the same data over and over again, by the end i have a list with same items over and over... I'm using a loader.
I tried to clear before but i don't understand what's wrong with my code, if someone could explain it to me. Thank You.
Here my code :
public abstract class NewsFragment extends Fragment implements LoaderManager.LoaderCallbacks<ArrayList<Articles>> {
protected ItemAdapter mArticleAdapter;
protected RecyclerView mRecyclerView;
protected NewsFragment.OnNewSelectedInterface mListener;
protected RecyclerView.LayoutManager mManager;
protected SwipeRefreshLayout mSwipeRefreshLayout;
protected LoaderManager mLoaderManager;
private boolean mStateSaved;
private static final int NEWS_LOAD_ID = 1;
public static final String KEY_LIST = "key_list";
public interface OnNewSelectedInterface {
void onListNewSelected(int index, ArrayList<Articles> articles);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.list_present_news, container, false);
mListener = (NewsFragment.OnNewSelectedInterface) getActivity();
mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipeContainer);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerview);
mManager = new LinearLayoutManager(getActivity());
mArticleAdapter = new ItemAdapter(getActivity(), new ArrayList<Articles>(), mListener);
mLoaderManager = getLoaderManager();
mStateSaved = mArticleAdapter.isStateSaved();
mRecyclerView.setAdapter(mArticleAdapter);
mRecyclerView.setLayoutManager(mManager);
getData();
refreshData();
if(!isNetworkAvailable())alertUserAboutError();
setDivider();
return view;
}
private void setDivider() {
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(mRecyclerView
.getContext(), DividerItemDecoration.VERTICAL);
mRecyclerView.addItemDecoration(dividerItemDecoration);
}
private void getData() {
getLoaderManager().initLoader(NEWS_LOAD_ID, null, this).forceLoad();
}
private void alertUserAboutError() {
AlertDialogFragment alertDialogFragment = new AlertDialogFragment();
alertDialogFragment.show(getActivity().getFragmentManager(), "error_dialog");
}
protected abstract String[] getUrl();
private boolean isNetworkAvailable() {
ConnectivityManager manager = (ConnectivityManager)
getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
boolean isAvailable = false;
if (networkInfo != null && networkInfo.isConnected()) {
isAvailable = true;
}
return isAvailable;
}
private void refreshData() {
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
mArticleAdapter.clear();
mSwipeRefreshLayout.setRefreshing(false);
}
});
mSwipeRefreshLayout.setColorSchemeResources(
android.R.color.holo_orange_light,
android.R.color.holo_red_light);
}
#Override
public Loader<ArrayList<Articles>> onCreateLoader(int id, Bundle args) {
return new NewsLoader(getActivity(), getUrl());
}
#Override
public void onLoadFinished(Loader<ArrayList<Articles>> loader, ArrayList<Articles> data) {
if (data != null && !data.isEmpty()) {
mArticleAdapter.addAll(data);
}
}
#Override
public void onLoaderReset(Loader<ArrayList<Articles>> loader) {
mArticleAdapter.clear();
}
}
My loader class :
public class NewsLoader extends AsyncTaskLoader<ArrayList<Articles>>{
private ArrayList<Articles> mArticlesArrayList;
private String[] mUrl;
public NewsLoader(Context context, String[] url) {
super(context);
mUrl = url;
}
#Override
public ArrayList<Articles> loadInBackground() {
OkHttpClient mClient = new OkHttpClient();
for (String aMUrl : mUrl) {
Request mRequest = new Request.Builder().url(aMUrl).build();
try {
Response response = mClient.newCall(mRequest).execute();
try {
if (response.isSuccessful()) {
String json = response.body().string();
getMultipleUrls(json);
}
} catch (IOException | JSONException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return mArticlesArrayList;
}
private void getMultipleUrls(String jsonData) throws JSONException {
if (mArticlesArrayList == null) {
mArticlesArrayList = getArticleForecast(jsonData);
} else {
mArticlesArrayList.addAll(getArticleForecast(jsonData));
}
}
private ArrayList<Articles> getArticleForecast(String jsonData) throws JSONException {
JSONObject forecast = new JSONObject(jsonData);
JSONArray articles = forecast.getJSONArray("articles");
ArrayList<Articles> listArticles = new ArrayList<>(articles.length());
for (int i = 0; i < articles.length(); i++) {
JSONObject jsonArticle = articles.getJSONObject(i);
Articles article = new Articles();
String urlImage = jsonArticle.getString("urlToImage");
article.setTitle(jsonArticle.getString("title"));
article.setDescription(jsonArticle.getString("description"));
article.setImageView(urlImage);
article.setArticleUrl(jsonArticle.getString("url"));
listArticles.add(i, article);
}
return listArticles;
}
}
My Adapter class :
public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ArticleViewHolder> {
private static final String TAGO = ItemAdapter.class.getSimpleName();
private final NewsFragment.OnNewSelectedInterface mListener;
private ArrayList<Articles> mArticlesList;
private Context mContext;
private int lastPosition = -1;
private boolean mStateSaved = false;
public boolean isStateSaved() {
return mStateSaved;
}
public void setStateSaved(boolean stateSaved) {
mStateSaved = stateSaved;
}
public ItemAdapter(Context context, ArrayList<Articles> articles, NewsFragment.OnNewSelectedInterface listener){
mContext = context;
mArticlesList = articles;
mListener = listener;
}
#Override
public ArticleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_card_view, parent, false);
ArticleViewHolder articleViewHolder = new ArticleViewHolder(view);
articleViewHolder.setIsRecyclable(false);
return articleViewHolder;
}
#Override
public void onBindViewHolder(ArticleViewHolder holder, int position) {
holder.bindArticle(mArticlesList.get(holder.getAdapterPosition()));
setAnimation(holder.itemView, holder.getAdapterPosition());
}
private void setAnimation(View viewToAnimate, int position) {
if (position > lastPosition) {
Animation animation = AnimationUtils.loadAnimation(viewToAnimate.getContext(), android.R.anim.slide_in_left);
viewToAnimate.startAnimation(animation);
lastPosition = position;
}
}
#Override
public int getItemCount() {
return mArticlesList.size();
}
public void clear() {
mArticlesList.clear();
notifyDataSetChanged();
}
public void addAll(ArrayList<Articles> articles) {
mArticlesList.addAll(articles);
notifyDataSetChanged();
}
protected class ArticleViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private ImageView mImageView;
private TextView mTitleTextView, mDescriptionTextView;
private FloatingActionButton mSaveButton;
private ArticleViewHolder(View itemView) {
super(itemView);
mImageView = (ImageView) itemView.findViewById(R.id.photoImageView);
mTitleTextView = (TextView) itemView.findViewById(R.id.titleWithoutImage);
mDescriptionTextView = (TextView) itemView.findViewById(R.id.descriptionTextView);
mSaveButton = (FloatingActionButton) itemView.findViewById(R.id.floatingActionButton);
itemView.setOnClickListener(this);
}
private void bindArticle(final Articles article) {
Glide.with(mContext).load(article.getImageView()).into(mImageView);
mTitleTextView.setText(article.getTitle());
mDescriptionTextView.setText(article.getDescription());
if(mDescriptionTextView.getText().equals("")){
mDescriptionTextView.setVisibility(View.GONE);
}
mSaveButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
insertArticle(article);
article.setStateSaved(true);
}
});
Log.v(TAGO, "Item id : " + getItemId()
+ "Item count : " + getItemCount()
+ "Item position : " + getAdapterPosition()
+ String.valueOf(article.isStateSaved()));
}
private void insertArticle(Articles articles) {
String title = articles.getTitle();
String description = articles.getDescription();
String url = articles.getArticleUrl();
ContentValues contentValues = new ContentValues();
contentValues.put(ArticleContract.ArticleEntry.COLUMN_TITLE_ARTICLE, title);
contentValues.put(ArticleContract.ArticleEntry.COLUMN_DESCRIPTION_ARTICLE, description);
contentValues.put(ArticleContract.ArticleEntry.COLUMN_URL_ARTICLE, url);
Uri uri = mContext.getContentResolver().insert(ArticleContract.ArticleEntry.CONTENT_URI, contentValues);
if(uri == null) {
Log.v(TAGO, "Error");
} else Toast.makeText(mContext, "Article Saved", Toast.LENGTH_SHORT).show();
}
#Override
public void onClick(View view) {
mListener.onListNewSelected(getLayoutPosition(), mArticlesList);
}
}
}
You are using ViewHolder#setIsRecyclable incorrectly; this method is meant to be used to prevent a ViewHolder from being recycled only while changes are being made to it. According to the documentation:
Calls to setIsRecyclable() should always be paired (one call to
setIsRecyclabe(false) should always be matched with a later call to
setIsRecyclable(true)).
This means none of your ViewHolder objects will be recycled, effectively making the use of a RecyclerView worthless, and preventing it from reusing the views when you attempt to bind new objects to your RecyclerView.
So, in short, remove that line of code.
I noticed a few other small issues with your adapter code as well, which can cause a multitude headaches in the future; so I took the liberty of highlighting some of the changes I would make.
Just for my own sanity, I will refer to your Articles class as Article.
It is usually not a good idea to pass around your Context all over the place. The View passed to your ViewHolder already has a reference to a Context, so you can use that instead.
As for the insertArticle() code, the Activity should be handling this anyway. So you can pass the Article back to the Activity by passing a listener to your Adapter (and subsequently, each ViewHolder) instead of the Context.
You should also consider using the DiffUtil class instead of just calling notifyDataSetChanged(); it is much more efficient. Just make sure your Article class is implementing equals() and hashCode() or it will not work.
I didn't include the animation code (that can easily be added back in) or the saved state code (mostly because I don't know what you were trying to do).
public class ArticleAdapter extends RecyclerView.Adapter<Article> {
private List<Article> mData;
private ArticleViewHolder.OnSelectedListener mOnSelectedListener;
private ArticleViewHolder.OnSaveListener mOnSaveListener;
public ArticleAdapter(ArticleViewHolder.OnSelectedListener onSelectedListener, ArticleViewHolder.OnSaveListener onSaveListener) {
mOnSelectedListener = onSelectedListener;
mOnSaveListener = onSaveListener;
mData = new ArrayList<>();
}
public void replaceData(final List<Article> data) {
final List<Article> oldData = new ArrayList<>(mData);
mData.clear();
if (data != null) {
mData.addAll(data);
}
DiffUtil.calculateDiff(new DiffUtil.Callback() {
#Override
public int getOldListSize() {
return oldData.size();
}
#Override
public int getNewListSize() {
return mData.size();
}
#Override
public int areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldData.get(oldItemPosition).equals(mData.get(newItemPosition));
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldData.get(oldItemPosition).equals(mData.get(newItemPosition));
}
}).dispatchUpdatesTo(this);
}
#Override
public ArticleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_card_view, parent, false);
return new SelectLocationViewHolder(view, mOnSelectedListener, mOnSaveListener);
}
#Override
public void onBindViewHolder(ArticleViewHolder holder, int position) {
holder.bind(mData.get(position));
}
#Override
public int getItemCount() {
return mData.size();
}
}
public class ArticleViewHolder extends RecyclerView.ViewHolder {
public interface OnSelectedListener {
void onSelected(Article article);
}
public interface OnSaveListener {
void onSave(Article article);
}
private View mView;
private Article mArticle;
private OnSelectedListener mOnSelectedListener;
private OnSaveListener mOnSaveListener;
private ImageView mImageView;
private TextView mTitleTextView, mDescriptionTextView;
private FloatingActionButton mSaveButton;
public ArticleViewHolder(View itemView, final OnSelectedListener onSelectedListener, final OnSaveListener onSaveListener) {
super(itemView);
mImageView = (ImageView) itemView.findViewById(R.id.photoImageView);
mTitleTextView = (TextView) itemView.findViewById(R.id.titleWithoutImage);
mDescriptionTextView = (TextView) itemView.findViewById(R.id.descriptionTextView);
mSaveButton = (FloatingActionButton) itemView.findViewById(R.id.floatingActionButton);
mView = itemView;
mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onSelectedListener.onSelected(mArticle);
}
});
mSaveButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onSaveListener.onSave(mArticle);
}
});
}
public void bind(Article article) {
mArticle = article;
mTitleTextView.setText(article.getTitle());
mDescriptionTextView.setText(article.getDescription());
if(TextUtils.isEmpty(article.getDescription())) {
mDescriptionTextView.setVisibility(View.GONE);
}
Glide.with(mView.getContext()).load(article.getImage()).into(mImageView);
}
}
Edit
The actual issue is that your loader uses the same ArrayList every time, and keeps adding the new results to it.
public class NewsLoader extends AsyncTaskLoader<List<Article>> {
private final String[] mUrls;
private final OkHttpClient mClient;
public NewsLoader(Context context, OkHttpClient client, String... urls) {
super(context);
mClient = client;
mUrls = urls;
}
#Override
public List<Article> loadInBackground() {
List<Article> articles = new ArrayList<>();
for (String url : mUrls) {
Request request = new Request.Builder().url(url).build();
try {
Response response = mClient.newCall(request).execute();
if (response.isSuccessful()) {
parseData(response.body().string(), articles);
}
} catch (IOException | JSONException e) {
e.printStackTrace();
}
}
return articles;
}
private void parseData(List<Article> articles, String data) throws JSONException {
JSONObject forecast = new JSONObject(data);
JSONArray a = forecast.getJSONArray("articles");
for (int i = 0; i < a.length(); i++) {
JSONObject o = a.getJSONObject(i);
Article article = new Article(
o.getString("title"),
o.getString("description"),
o.getString("url"),
o.getString("urlToImage"));
articles.add(article);
}
}
}
Also, you may have noticed, I made a small change to your Article constructor. You should consider making the Article class immutable, as this will prevent you from making mistakes when dealing with multithreading. It should look something like this:
public class Article {
private final String mTitle;
private final String mDescription;
private final String mUrl;
private final String mImageUrl;
public Article(String title, String description, String url, String imageUrl) {
mTitle = title;
mDescription = description;
mUrl = url;
mImageUrl = imageUrl;
}
public String title() {
return mTitle;
}
public String description() {
return mDescription;
}
public String url() {
return mUrl;
}
public String imageUrl() {
return mImageUrl;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Article other = (Article) o;
return mTitle != null && mTitle.equals(other.mTitle) &&
mDescription != null && mDescription.equals(other.mDescription) &&
mUrl != null && mUrl.equals(other.mUrl) &&
mImageUrl != null && mImageUrl.equals(other.mImageUrl);
}
#Override
public int hashCode() {
int result = mTitle != null ? mTitle.hashCode() : 0;
result = 31 * result + (mDescription != null ? mDescription.hashCode() : 0);
result = 31 * result + (mUrl != null ? mUrl.hashCode() : 0);
result = 31 * result + (mImageUrl != null ? mImageUrl.hashCode() : 0);
return result;
}
}
#Override
public void onBindViewHolder(ArticleViewHolder holder, int position) {
holder.bindArticle(mArticlesList.get(position));
setAnimation(holder.itemView, position);
}
public void addAll(ArrayList<Articles> articles) {
mArticlesList.clear();
mArticlesList.addAll(articles);
notifyDataSetChanged();
}
If this doesn't wrok then I think your api is giving you redundant data.
Why you are using articleViewHolder.setIsRecyclable(false);
One another place which might cause the problem is
private void getMultipleUrls(String jsonData) throws JSONException {
if (mArticlesArrayList == null) {
mArticlesArrayList = getArticleForecast(jsonData);
} else {
mArticlesArrayList.addAll(getArticleForecast(jsonData));
}
}
You are calling it from a loop add adding data to your arraylist. There somehow multiple data can be inserted in your ArrayList

Android Recyclerview, okhttp, receiving an java.lang.ClassCastException error

I am trying to load data into my recyclerview via url okhttp calls, and I am receiving the following error:
"java.lang.ClassCastException: SearchActivity cannot be cast to SearchForBarbershop$BarbershopRequesterResponse"
Here are the classes.
SearchActivity:
public class SearchActivity extends AppCompatActivity {
private RecyclerAdapter mAdapter;
private RecyclerView mRecyclerView;
private LinearLayoutManager mLinearLayoutManager;
private ArrayList<Barbershop> barbershopList;
private SearchForBarbershop searchForBarbershop;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
mLinearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
barbershopList = new ArrayList<>();
mAdapter = new RecyclerAdapter(barbershopList);
mRecyclerView.setAdapter(mAdapter);
setRecyclerViewScrollListener();
searchForBarbershop = new SearchForBarbershop(this);
}
#Override
protected void onStart() {
super.onStart();
if (barbershopList.size() == 0) {
requestShop();
}
}
private int getLastVisibleItemPosition() {
return mLinearLayoutManager.findLastVisibleItemPosition();
}
private void setRecyclerViewScrollListener() {
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
int totalItemCount = mRecyclerView.getLayoutManager().getItemCount();
if (!searchForBarbershop.isLoadingData() && totalItemCount == getLastVisibleItemPosition() + 1) {
requestShop();
}
}
});
}
private void requestShop() {
try {
searchForBarbershop.getBarbershop();
} catch (IOException e) {
e.printStackTrace();
}
}
//#Override
public void receivedNewShop(final Barbershop newBarbershop) {
runOnUiThread(new Runnable() {
#Override
public void run() {
barbershopList.add(newBarbershop);
mAdapter.notifyItemInserted(barbershopList.size());
}
});
}
}
SearchForBarbershop:
public class SearchForBarbershop {
public interface BarbershopRequesterResponse {
void receivedNewBarbershop(Barbershop barbershop);
}
private BarbershopRequesterResponse barbershopRequesterResponse;
private OkHttpClient client;
private boolean isLoadingData;
public boolean isLoadingData() { return isLoadingData; }
public SearchForBarbershop (Activity listeningActivity) {
client = new OkHttpClient();
isLoadingData = false;
barbershopRequesterResponse = (BarbershopRequesterResponse) listeningActivity;
}
public void getBarbershop() throws IOException {
String url = "http://pubapi.yp.com/search-api/search/devapi/search?searchloc=30043&term=barbers&format=json&sort=distance&radius=50&listingcount=20&key=gmj3x7mhsh";
Request request = new Request.Builder().url(url).build();
isLoadingData = true;
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
isLoadingData = false;
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
try {
JSONObject object = new JSONObject(response.body().string());
Barbershop receivedShop = new Barbershop(object);
barbershopRequesterResponse.receivedNewBarbershop(receivedShop);
isLoadingData = false;
} catch (JSONException e) {
isLoadingData = false;
e.printStackTrace();
}
}
});
}
}
Barbershop:
public class Barbershop{
private String name;
private String city;
public Barbershop(JSONObject barbershopJSON) {
try {
JSONArray businessArray = barbershopJSON.getJSONObject("searchResult").getJSONObject("searchListings").getJSONArray("searchListing");
for(int i = 0; i < businessArray.length(); i++) {
//Parse JSON and assign to variables
JSONObject business_object_1 = businessArray.getJSONObject(i);
String primaryc = business_object_1.getString("primaryCategory");
//Check to make sure the main category is "Barbers"
if((primaryc).equals("Barbers")) {
name = business_object_1.getString("businessName");
city = business_object_1.getString("city");
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
public String getCity() {
return city;
}
public String getName() {
return name;
}
}
RecyclerAdapter:
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ShopHolder> {
private ArrayList<Barbershop> mbarbershops;
public RecyclerAdapter(ArrayList<Barbershop> barbershops) {
mbarbershops = barbershops;
}
#Override
public RecyclerAdapter.ShopHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View inflatedView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cardview_barber_search, parent, false);
return new ShopHolder(inflatedView);
}
#Override
public void onBindViewHolder(RecyclerAdapter.ShopHolder holder, int position) {
Barbershop itemBarbershop = mbarbershops.get(position);
holder.bindShop(itemBarbershop);
}
#Override
public int getItemCount() {
return 0;
}
public static class ShopHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView mName;
private TextView mCity;
public ShopHolder(View view) {
super(view);
mName = (TextView) view.findViewById(R.id.textViewName);
mCity = (TextView) view.findViewById(R.id.textViewCity);
view.setOnClickListener(this);
}
#Override
public void onClick(View v) {
}
public void bindShop(Barbershop barbershop) {
mName.setText(barbershop.getName());
mCity.setText(barbershop.getCity());
}
}
}
It can't be cast because it doesn't implement that interface. Your commented-out #override statement should be a clue for you here.
Change it to:
public class SearchActivity extends AppCompatActivity
implements SearchForBarbershop.BarbershopRequesterResponse {
....
And don't forget to remove the comment for method you implemented:
#Override
public void receivedNewShop(final Barbershop newBarbershop) {
....
I think you need change constructor SearchForBarbershop
BarbershopRequesterResponse listener;
Context context
public SearchForBarbershop (Context context,BarbershopRequesterResponse listener) {
client = new OkHttpClient();
isLoadingData = false;
this.context = context;
this.listener = listener;
}
And change in SearchActivity
searchForBarbershop = new SearchForBarbershop(SearchActivity.this, this);

Can't update RecyclerView from my model Class?

I am working on a RecyclerView which must be Draggable & swipeable. Everything works perfect.
The Data is getting Fetched in one class called ExerciseDataProvider & the RV code is another Fragment RecyclerListViewFragment.
The problem is that i can't notify Data changed from the FetchExercise on postExecute method. So the Data's are not getting populated in the RV.
Please Guide me in a Right Direction.
ACTIVITY
public class DraggableSwipeableExampleActivity extends AppCompatActivity {
private static final String FRAGMENT_TAG_DATA_PROVIDER = "data provider";
private static final String FRAGMENT_LIST_VIEW = "list view";
private static final String FRAGMENT_TAG_ITEM_PINNED_DIALOG = "item pinned dialog";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(new ExampleDataProviderFragment(), FRAGMENT_TAG_DATA_PROVIDER)
.commit();
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new RecyclerListViewFragment(), FRAGMENT_LIST_VIEW)
.commit();
}
}
public AbstractDataProvider getDataProvider() {
final Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DATA_PROVIDER);
return ((ExampleDataProviderFragment) fragment).getDataProvider();
}
DATA PROVIDER
public class ExerciseDataProvider extends AbstractDataProvider {
private List<ConcreteData> mData;
private ConcreteData mLastRemovedData;
private int mLastRemovedPosition = -1;
public ExerciseDataProvider() {
new FetchExercise().execute();
mData = new LinkedList<>();
}
class FetchExercise extends AsyncTask<Void,Void,Void> {
#Override
protected Void doInBackground(Void... params) {
final int viewType = 0;
final int swipeReaction = RecyclerViewSwipeManager.REACTION_CAN_SWIPE_UP | RecyclerViewSwipeManager.REACTION_CAN_SWIPE_DOWN;
String url = "https://gist.githubusercontent.com/fake/cb9aa5494e7ee36ac3ca/raw/a4abfd19368063/exercise.JSON";
Log.d("Path", url);
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
String jsonData = response.body().string();
try {
JSONArray jsonArray = new JSONArray(jsonData);
for (int i = 0; i < jsonArray.length(); i++) {
final long id = i;
JSONObject jsonObject = jsonArray.getJSONObject(i);
String exercise_name = jsonObject.getString("name");
int exercise_duration = jsonObject.getInt("duration");
mData.add(new ConcreteData(id, viewType, exercise_name, exercise_duration, swipeReaction));
Log.d("exercise_name", exercise_name);
}
} catch (JSONException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
}
#Override
public int getCount() {
return mData.size();
}
#Override
public Data getItem(int index) {
if (index < 0 || index >= getCount()) {
throw new IndexOutOfBoundsException("index = " + index);
}
return mData.get(index);
}
#Override
public int undoLastRemoval() {
if (mLastRemovedData != null) {
int insertedPosition;
if (mLastRemovedPosition >= 0 && mLastRemovedPosition < mData.size()) {
insertedPosition = mLastRemovedPosition;
} else {
insertedPosition = mData.size();
}
mData.add(insertedPosition, mLastRemovedData);
mLastRemovedData = null;
mLastRemovedPosition = -1;
return insertedPosition;
} else {
return -1;
}
}
#Override
public void moveItem(int fromPosition, int toPosition) {
if (fromPosition == toPosition) {
return;
}
final ConcreteData item = mData.remove(fromPosition);
mData.add(toPosition, item);
mLastRemovedPosition = -1;
}
#Override
public void removeItem(int position) {
//noinspection UnnecessaryLocalVariable
final ConcreteData removedItem = mData.remove(position);
mLastRemovedData = removedItem;
mLastRemovedPosition = position;
}
public static final class ConcreteData extends Data {
private final long mId;
private final String mText;
private final int mViewType;
private final int mDuration;
private boolean mPinned;
ConcreteData(long id, int viewType, String text, int duration, int swipeReaction) {
mId = id;
mViewType = viewType;
mText = text;
mDuration = duration;
}
#Override
public int getViewType() {
return mViewType;
}
#Override
public int getDuration() {
return mDuration;
}
#Override
public long getId() {
return mId;
}
#Override
public String toString() {
return mText;
}
#Override
public String getText() {
return mText;
}
#Override
public boolean isPinned() {
return mPinned;
}
#Override
public void setPinned(boolean pinned) {
mPinned = pinned;
}
}
}
RecyclerListViewFragment
public class RecyclerListViewFragment extends Fragment {
private RecyclerView mRecyclerView;
private RecyclerView.LayoutManager mLayoutManager;
private RecyclerView.Adapter mAdapter;
private RecyclerView.Adapter mWrappedAdapter;
private RecyclerViewDragDropManager mRecyclerViewDragDropManager;
private RecyclerViewSwipeManager mRecyclerViewSwipeManager;
private RecyclerViewTouchActionGuardManager mRecyclerViewTouchActionGuardManager;
public RecyclerListViewFragment() {
super();
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_recycler_list_view, container, false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//noinspection ConstantConditions
mRecyclerView = (RecyclerView) getView().findViewById(R.id.recycler_view);
mLayoutManager = new LinearLayoutManager(getContext());
// touch guard manager (this class is required to suppress scrolling while swipe-dismiss animation is running)
mRecyclerViewTouchActionGuardManager = new RecyclerViewTouchActionGuardManager();
mRecyclerViewTouchActionGuardManager.setInterceptVerticalScrollingWhileAnimationRunning(true);
mRecyclerViewTouchActionGuardManager.setEnabled(true);
// drag & drop manager
mRecyclerViewDragDropManager = new RecyclerViewDragDropManager();
mRecyclerViewDragDropManager.setDraggingItemShadowDrawable(
(NinePatchDrawable) ContextCompat.getDrawable(getContext(), R.drawable.material_shadow_z3));
// swipe manager
mRecyclerViewSwipeManager = new RecyclerViewSwipeManager();
//adapter
final MyDraggableSwipeableItemAdapter myItemAdapter = new MyDraggableSwipeableItemAdapter(getDataProvider());
myItemAdapter.setEventListener(new MyDraggableSwipeableItemAdapter.EventListener() {
#Override
public void onItemRemoved(int position) {
((DraggableSwipeableExampleActivity) getActivity()).onItemRemoved(position);
}
#Override
public void onItemViewClicked(View v, boolean pinned) {
onItemViewClick(v, pinned);
}
});
mAdapter = myItemAdapter;
mWrappedAdapter = mRecyclerViewDragDropManager.createWrappedAdapter(myItemAdapter); // wrap for dragging
mWrappedAdapter = mRecyclerViewSwipeManager.createWrappedAdapter(mWrappedAdapter); // wrap for swiping
final GeneralItemAnimator animator = new SwipeDismissItemAnimator();
animator.setSupportsChangeAnimations(false);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mWrappedAdapter); // requires *wrapped* adapter
mRecyclerView.setItemAnimator(animator);
// additional decorations
//noinspection StatementWithEmptyBody
if (supportsViewElevation()) {
// Lollipop or later has native drop shadow feature. ItemShadowDecorator is not required.
} else {
mRecyclerView.addItemDecoration(new ItemShadowDecorator((NinePatchDrawable) ContextCompat.getDrawable(getContext(), R.drawable.material_shadow_z1)));
}
mRecyclerView.addItemDecoration(new SimpleListDividerDecorator(ContextCompat.getDrawable(getContext(), R.drawable.list_divider_h), true));
mRecyclerViewTouchActionGuardManager.attachRecyclerView(mRecyclerView);
mRecyclerViewSwipeManager.attachRecyclerView(mRecyclerView);
mRecyclerViewDragDropManager.attachRecyclerView(mRecyclerView);
}
#Override
public void onPause() {
mRecyclerViewDragDropManager.cancelDrag();
super.onPause();
}
#Override
public void onDestroyView() {
if (mRecyclerViewDragDropManager != null) {
mRecyclerViewDragDropManager.release();
mRecyclerViewDragDropManager = null;
}
if (mRecyclerViewSwipeManager != null) {
mRecyclerViewSwipeManager.release();
mRecyclerViewSwipeManager = null;
}
if (mRecyclerViewTouchActionGuardManager != null) {
mRecyclerViewTouchActionGuardManager.release();
mRecyclerViewTouchActionGuardManager = null;
}
if (mRecyclerView != null) {
mRecyclerView.setItemAnimator(null);
mRecyclerView.setAdapter(null);
mRecyclerView = null;
}
if (mWrappedAdapter != null) {
WrapperAdapterUtils.releaseAll(mWrappedAdapter);
mWrappedAdapter = null;
}
mAdapter = null;
mLayoutManager = null;
super.onDestroyView();
}
private void onItemViewClick(View v, boolean pinned) {
int position = mRecyclerView.getChildAdapterPosition(v);
if (position != RecyclerView.NO_POSITION) {
((DraggableSwipeableExampleActivity) getActivity()).onItemClicked(position);
}
}
public AbstractDataProvider getDataProvider() {
return ((DraggableSwipeableExampleActivity) getActivity()).getDataProvider();
}
public void notifyItemChanged(int position) {
mAdapter.notifyItemChanged(position);
}
public void notifyItemInserted(int position) {
mAdapter.notifyItemInserted(position);
mRecyclerView.scrollToPosition(position);
}
}
To update recyclerView from onPostExecute in a data provider class, your onPostExecute should have access to context where your recyclerView is defined.
Since your FetchExercise async task is defined inside ExerciseDataProvider class, try passing activity context to ExerciseDataProvider's constructor and then pass it on to FetchExercise async task as described here: getting context in AsyncTask
public class MyCustomTask extends AsyncTask<Void, Void, Long> {
private Context mContext;
public MyCustomTask (Context context){
mContext = context;
}
protected void onPostExecute(Long result) {
//use mContext to update recycler view
}
}
}
Use the context to update the recyclerView.
UPDATE
Step 1
Define an interface that will notify your activity of data set change inside a class that initialises your data provider class and pass activity context to constructor of data provider class.
public class ExampleDataProviderFragment extends Fragment {
private AbstractDataProvider mDataProvider;
//Define an interface that will notify your activity of data set change
public interface EventListener {
void onNotifyDataSetChanged();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
//Pass activity context to ExerciseDataProvider
mDataProvider = new ExerciseDataProvider(getActivity());
}
public AbstractDataProvider getDataProvider() {
return mDataProvider;
}
}
Step 2
Add context parameter to ExerciseDataProvider's constructor and use it to notify activity that implements your interface to notify dataset change.
public class ExerciseDataProvider extends AbstractDataProvider {
private List<ConcreteData> mData;
private ConcreteData mLastRemovedData;
private int mLastRemovedPosition = -1;
//Add context parameter to constructor
public ExerciseDataProvider(Context context) {
//Pass context to async task
new FetchExercise(context).execute();
mData = new LinkedList<>();
}
class FetchExercise extends AsyncTask<Void,Void,Integer> {
Context mContext;
public FetchExercise(Context context) {
mContext = context;
}
#Override
protected Integer doInBackground(Void... params) {
...
return 1;
}
#Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
//Typecast context to interface defined above
//and notify dataset changes by calling its method
ExampleDataProviderFragment.EventListener eventListener = (ExampleDataProviderFragment.EventListener)mContext;
eventListener.onNotifyDataSetChanged();
}
}
}
Step 3
Implement above defined interface in your activity class and notify recyclerview adapter inside it
public class DraggableSwipeableExampleActivity extends AppCompatActivity
implements ExampleDataProviderFragment.EventListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
...
}
//implement interface method and notify recyclerview of changes
#Override
public void onNotifyDataSetChanged() {
Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_LIST_VIEW);
// you might need to change visibility of `mWrappedAdapter` in the fragment that defines it or create a getter for it so that you can access it here
((RecyclerListViewFragment) fragment).mWrappedAdapter.notifyDataSetChanged();
}
...
}
I think #random is correct you should be notifying your Recycle view on post execute.
#Override
protected void onPostExecute(Void aVoid) {
mRecyclerViewAdapter.notifyDataSetChanged();
super.onPostExecute(aVoid);
}
or if you have done something in your async task to add/delete something in the data set you would do:
#Override
protected void onPostExecute(Void aVoid) {
mRecyclerViewAdapter.notifyItemRemoved(itemposition); // or item added
mRecyclerViewAdapter.notifyDataSetChanged();
super.onPostExecute(aVoid);
}
Hope it helps !

Categories

Resources