How to update complex recyclerview item parameters? - java

here is my post item:
public class PostItems {
private String id_wall_post, id_user, text_wall_post, ..........;
public PostItems(String id_wall_post, String id_user, String text_wall_post, ..............) {
this.id_wall_post = id_wall_post;
this.id_user = id_user;
this.text_wall_post = text_wall_post;
}
String get_id_wall_post() {
return id_wall_post;
}
String get_id_user() {
return id_user;
}
String get_text_wall_post() {
return text_wall_post;
}
...
...
.........
...
...
}
Here my adapter:
public class PostAdapter extends RecyclerView.Adapter<PostAdapter.PostViewHolder> {
private List<PostItems> postList;
private Context context;
PostAdapter(List<PostItems> postList, Context context) {
this.postList = postList;
this.context = context;
}
#NonNull
#Override
public PostViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_view_wall_post, parent, false);
return new PostViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull PostViewHolder holder, int position) {
PostItems post_items = postList.get(position);
...
...
.........
...
...
}
#Override
public int getItemCount() {
return postList.size();
}
static class PostViewHolder extends RecyclerView.ViewHolder {
PostViewHolder(#NonNull View itemView) {
super(itemView);
}
}
}
Using retrofit the recyclerview is populated correctly:
postList.addAll(response.body());
postAdapter = new PostAdapter(postList, ProfileActivity.this);
recyclerView.setAdapter(postAdapter);
I also have an Editext where user can update the value of text_wall_post; I want now to update the item parameter text_wall_post of the recyclerView ?
How could I update a recyclerView item specific parameter ?
EDIT:
I have found that:
postList.set(1, new PostItems("1","1176", "some text update here", ..................));
postAdapter.notifyItemChanged(1);
In my recyclerView, I have more than 20 parameters.
Which work but I want a more clean way to update only the needed parameter without updating all the parameters.
Thanks for Any help.

You can change an item without creating a new one.
First make setters (like the getters you made) to change a specific parameter from an object(postItems in this case)
public class PostItems {
private String id_wall_post, id_user, text_wall_post, ..........;
public PostItems(String id_wall_post, String id_user, String text_wall_post, ..............) {
this.id_wall_post = id_wall_post;
this.id_user = id_user;
this.text_wall_post = text_wall_post;
}
String get_id_wall_post() {
return id_wall_post;
}
String get_id_user() {
return id_user;
}
String get_text_wall_post() {
return text_wall_post;
}
public void setIdWallPost(String idWallpost){
this.id_wall_post = idWallpost;
}
public void setIdUser(String idUser){
this.id_user = idWallUser;
}
public void setTextWallPost(String textWallPost){
this.text_wall_post = textWallPost;
}
...
...
.........
...
...
}
You can also make android studio generate them for you by pressing Alt+Inser > Setter
Then get the item with specific position from postList and update the parameter you want
postList.get(position).setTextWallPost("new value here");
And finally call
postAdapter.notifyItemChanged (position)

Related

Passing data from Adapter to Item's ViewModel in Android

I have a view pager with an adapter that has a number of items fetched from the cloud. I add the pages to the adapter through the fragment.
I am trying to pass a variable to the Item's view model every time a new page is instantiated from the adapter so as to notify one of the views that depend on this view model. I have tried a few workarounds but nothing has been successful so far. Would anyone please advice how I may pass this data.
Below is my adapter
public class HelpPagerAdapter extends PagerAdapter {
private final List<HelpPage> mHelpPages;
private int currentPage = 0;
public HelpPagerAdapter(Context context) {
mPages = new ArrayList<>();
}
#Override
public int getCount() {
return mHelpPages.size();
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
HelpPage helpPage = mHelpPages.get(position);
currentPage = position;
LayoutInflater inflater = (LayoutInflater) container.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewDataBinding binding = DataBindingUtil.inflate(inflater, helpPage.getLayout(), container, false);
binding.setVariable(helpPage.getViewModelBindingId(), helpPage.getViewModel());
container.addView(binding.getRoot());
return binding.getRoot();
}
public void addPage(#LayoutRes int layout, Object viewModel, int viewModelBindingId) {
mHelpPages.add(new HelpPage(layout, viewModel, viewModelBindingId));
}
private class HelpPage {
private final #LayoutRes int mLayout;
private final Object mViewModel;
private final int mViewModelBindingId;
HelpPage(#LayoutRes int layout, Object viewModel, int viewModelBindingId) {
mLayout = layout;
mViewModel = viewModel;
mViewModelBindingId = viewModelBindingId;
}
#LayoutRes int getLayout() {
return mLayout;
}
Object getViewModel() {
return mViewModel;
}
int getViewModelBindingId() {
return mViewModelBindingId;
}
}
}
Part of my fragment code
#Override
public void onAttach(Context context) {
super.onAttach(context);
Bundle bundle = getArguments();
if (bundle == null || (!bundle.containsKey(ARG_STEPS) && !bundle.containsKey(ARG_VIEWS))) {
throw new IllegalStateException("HelpFragment cannot be initialised without views");
}
HelpPagerAdapter adapter = new HelpPagerAdapter(getContext());
if (bundle.containsKey(ARG_STEPS)) {
List<Step> steps = Parcels.unwrap(bundle.getParcelable(ARG_STEPS));
adapter = addHelpPages(steps, adapter);
}
mHelpViewModel = new HelpViewModel(adapter);
}
private HelpPagerAdapter addHelpPages(List<Step> steps, HelpPagerAdapter adapter) {
for (Step step : steps) {
adapter.addPage(R.layout.layout_help, new HelpItemViewModel(step), BR.viewModel);
}
return adapter;
}
Part of my ViewModel
public class HelpItemViewModel {
private final String mText;
private final String mVideoId;
private final String mImageUrl;
public HelpItemViewModel(Step step) {
mText = step.getText();
mHelpId = step.getHelpId();
mImageUrl = step.getImage();
}
}

How to make RecyclerView Multiple ViewTypes With Retrofit and ViewModel

Recently I have created RecyclerView with Multiple ViewTypes followed this video. In the video the lecturer followed the traditional method of sending data using ArrayList In MainAvtivity. But when I used ViewModel and Retrofit it is not passing any data and nothing show in the screen.
And here an expiation of what I have done.
First:
I have 3 diffrents types of views so I created a 3 model classes for each view called (FirstImageModel,LiveModel,DailyImageModel).
FirstImageModel class -- All other 2 classes have same stretcher.
public class FirstImageModel {
#SerializedName("firstImageUrl")
private String firstImageUrl;
public FirstImageModel(String firstImageUrl) {
this.firstImageUrl = firstImageUrl;
}
public String getFirstImageUrl() {
return firstImageUrl;
}
public void setFirstImageUrl(String firstImageUrl) {
this.firstImageUrl = firstImageUrl;
}
and I have created a fourth class called ItemModel that have type int and Object object.
public class ItemModel {
private int type;
private Object object;
public ItemModel(int type) {
this.type = type;
this.object = object;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
}
Second:
I have created Adapter class called HomeAdapter that extends from RecyclerView and I made 3 classes for eatch view inside the same class HomeAdapter.
public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<ItemModel> items;
public HomeAdapter(List<ItemModel> items) {
this.items = items;
}
public void setHomeList(List<ItemModel> items) {
this.items = items;
notifyDataSetChanged();
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
// Here is types are: 0-FirstImage, 1-Live, 2-DailyImage
if (viewType == 0) {
return new FirstImageViewHolder(
LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_home_container_firstimage,
parent,
false
)
);
} else if (viewType == 1) {
return new LiveViewHolder(
LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_home_container_live,
parent,
false
)
);
} else {
return new DailyImageViewHolder(
LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_home_container_dailyimage,
parent,
false
)
);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
if (getItemViewType(position) == 0) {
FirstImageModel firstImageModel = (FirstImageModel) items.get(position).getObject();
((FirstImageViewHolder) holder).setFirstImageData(firstImageModel);
} else if (getItemViewType(position) == 1) {
LiveModel liveModel = (LiveModel) items.get(position).getObject();
((LiveViewHolder) holder).setLiveImageData(liveModel);
} else {
DailyImageModel dailyImageModel = (DailyImageModel) items.get(position).getObject();
((DailyImageViewHolder) holder).setDailyImageData(dailyImageModel);
}
}
#Override
public int getItemCount() {
if(this.items != null) {
return this.items.size();
}
return 0;
}
#Override
public int getItemViewType(int position) {
return items.get(position).getType();
}
/* firstImage Adapter */
static class FirstImageViewHolder extends RecyclerView.ViewHolder {
private ImageView firstImage;
FirstImageViewHolder(#NonNull View itemView) {
super(itemView);
firstImage = itemView.findViewById(R.id.image_home_firstImage);
}
void setFirstImageData(FirstImageModel firstImageModel) {
Glide.with(itemView.getContext())
.load(firstImageModel.getFirstImageUrl())
.into(firstImage);
}
}
/* Live Adapter */
static class LiveViewHolder extends RecyclerView.ViewHolder {
private ImageView liveImage;
LiveViewHolder(#NonNull View itemView) {
super(itemView);
liveImage = itemView.findViewById(R.id.image_home_liveImage);
}
void setLiveImageData(LiveModel liveModel) {
Glide.with(itemView.getContext())
.load(liveModel.getImageLiveInfoUrl())
.into(liveImage);
}
}
/* DailyImage Adapter */
static class DailyImageViewHolder extends RecyclerView.ViewHolder {
private ImageView dailyImage;
DailyImageViewHolder(#NonNull View itemView) {
super(itemView);
dailyImage = itemView.findViewById(R.id.image_home_dailyImage);
}
void setDailyImageData(DailyImageModel dailyImageModel) {
Glide.with(itemView.getContext())
.load(dailyImageModel.getDailyImageUrl())
.into(dailyImage);
}
}
My Problem:
When I use Retrofit to load the data, I don't Know how to pass the 3 views types data to the RecyclerView using ViewModel. My ViewModel class.
public class HomeViewModel extends ViewModel {
private MutableLiveData<List<ItemModel>> homeObjectsList;
private Call<List<ItemModel>> call;
public HomeViewModel(){
homeObjectsList = new MutableLiveData<>();
}
public MutableLiveData<List<ItemModel>> getHomeItemsListObserver() {
return homeObjectsList;
}
public void makeApiCallHome() {
APIServiceHome apiServiceHome = RetroInstanceHome.getRetroClientHome().create(APIServiceHome.class);
call = apiServiceHome.getHomeObjectsList();
call.enqueue(new Callback<List<ItemModel>>() {
#Override
public void onResponse(Call<List<ItemModel>> call, Response<List<ItemModel>> response) {
homeObjectsList.postValue(response.body());
}
#Override
public void onFailure(Call<List<ItemModel>> call, Throwable t) {
homeObjectsList.postValue(null);
}
});
}
Also here's my fragment that should dislay the data in RecyclerView.
public class HomeFragment extends Fragment {
View rootView;
RecyclerView recyclerView;
private List<ItemModel> itemModelList;
private HomeViewModel viewModel;
private HomeAdapter adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_home, container, false);
initializeContent();
initializeViewModel();
return rootView;
}
private void initializeContent() {
recyclerView = rootView.findViewById(R.id.recyclerView_home);
recyclerView.setAdapter(new HomeAdapter(itemModelList));
}
private void initializeViewModel() {
ProgressBar progressBar = rootView.findViewById(R.id.progress_bar_home);
viewModel = ViewModelProviders.of(this).get(HomeViewModel.class);
viewModel.getHomeItemsListObserver().observe(getActivity(), new Observer<List<ItemModel>>() {
#Override
public void onChanged(List<ItemModel> itemModels) {
if(itemModels != null) {
progressBar.setVisibility(View.GONE);
itemModelList = itemModels;
adapter.setHomeList(itemModels);
}
}
});
viewModel.makeApiCallHome();
}
I hope this explanation was clear to show the problem. Waiting for your answer

RecyclerView index=-1 when scrolling fast to the bottom

I have created a RecyclerView to display items and everything shows and works fine except to one scenario: when I scroll fast to the bottom of the RecyclerView.
If I do so, I get the following error:
java.lang.ArrayIndexOutOfBoundsException: length=15; index=-1
It directs me to this part of my code (specifically to checkIfBorrowed.get( getAdapterPosition() )):
public class itemtestAdapter extends RecyclerView.Adapter<itemtestAdapter.testViewHolder> {
private List<Discoveritems> items;
private List<Boolean> checkIftested;
interface OnItemCheckListener {
void onItemCheck(Discoveritems items);
void onItemUncheck(Discoveritems items);
}
#NonNull
private OnItemCheckListener onItemCheckListener;
public itemtestAdapter(List<Discoveritems> items, List<Boolean> checkIftested, #NonNull OnItemCheckListener onItemCheckListener) {
this.items = items;
this.onItemCheckListener = onItemCheckListener;
this.checkIftested = checkIftested;
}
#Override
public testViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
view = LayoutInflater.from(parent.getContext()).inflate( R.layout.item_item_test_list, parent, false );
return new testViewHolder(view);
}
#Override
public void onBindViewHolder(testViewHolder holder, int position) {
holder.bind(items.get(position));
final Discoveritems currentItem = items.get(position);
holder.setOnClickListener( v -> {
holder.Cb_test.setChecked(
!holder.Cb_test.isChecked());
if (holder.Cb_test.isChecked()) {
onItemCheckListener.onItemCheck(currentItem);
} else {
onItemCheckListener.onItemUncheck(currentItem);
}
} );
}
#Override
public int getItemCount() {
return items.size();
}
class testViewHolder extends RecyclerView.ViewHolder {
ImageView Iv_test,itemPic;
TextView itemName;
CheckBox Cb_test;
Discoveritems items;
public testViewHolder(View itemView) {
super(itemView);
Iv_testitemPic = itemView.findViewById(R.id.iv_itemCover);
Tv_testitemName = itemView.findViewById( R.id.tv_testeditemName);
Cb_test = itemView.findViewById( R.id.cb_testitem );
Cb_test.setClickable( false );
}
public void setOnClickListener(View.OnClickListener onClickListener) {
itemView.setOnClickListener(onClickListener);
}
public void bind(Discoveritems items) {
this.items = items;
String itemID = items.getitemID();
MyitemClient client = new MyitemClient();
client.getitems(itemID, new JsonHttpResponseHandler(){
#Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response){
if(response!=null){
final Myitem items = Myitem.fromJson(response);
if (checkIftested.get( getAdapterPosition() )){
itemView.setEnabled(false);
Tv_itemName.setText("my" + items.getTitle());
} else {
Tv_itemName.setText( items.getTitle() );
}
}
}
});
}
}
}
Thing is that inside bind I can't figure out how to get the position of the item without using getAdapterPosition and I also cant understand why it only happens when I scroll fast.
Also it is weird that it says length = 15 while checkIfBorowed's size is 12 only.
Thank you
Change holder.bind(items.get(position)); to holder.bind(items.get(position), position); and then change viewholders bind method to accept the new position argument i.e. bind(Discoveritems items, int position) and use position instead of getAdapterPosition().
The issue is you are not using the position the adapter gave your viewholder which can change when views are recycled and thus they dont macth. Please see here for more https://stackoverflow.com/a/38970513/1133011

RecyclerView is duplicating the first three items on a list

Im fairly new to android and i am trying to use a RecyclerView to display content hosted on firebase, but when it comes up the first three items are duplicated.
I have tried a few solutions around but none seem to work, any help would be great!
DiscountRecyclerAdapter.java
public class DiscountRecyclerAdapter extends RecyclerView.Adapter<com.cianod.comharapp.DiscountRecyclerAdapter.ViewHolder> {
public List<Discounts> discountsList;
public Context context;
private ImageView discountImageView;
public DiscountRecyclerAdapter(List<Discounts> discountsList){
this.discountsList = discountsList;
}
#Override
public com.cianod.comharapp.DiscountRecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.discount_list_item, parent, false);
context = parent.getContext();
return new com.cianod.comharapp.DiscountRecyclerAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(final com.cianod.comharapp.DiscountRecyclerAdapter.ViewHolder holder, int position) {
holder.setIsRecyclable(false);
String discountName = discountsList.get(position).getDiscount_name();
holder.setDiscount_Name(discountName);
String discountDescription = discountsList.get(position).getDiscount_description();
holder.setDiscount_Description(discountDescription);
String discountValue = discountsList.get(position).getDiscount_value();
holder.setDiscount_Value(discountValue);
String image_url = discountsList.get(position).getDiscount_image();
String thumbUri = discountsList.get(position).getDiscount_image();
holder.setDiscount_Image(image_url, thumbUri);
}
#Override
public int getItemCount() {
if(discountsList != null) {
return discountsList.size();
} else {
return 0;
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
private View mView;
private TextView discount_name;
public ViewHolder(View itemView) {
super(itemView);
mView = itemView;
}
public void setDiscount_Name(String message){
discount_name = mView.findViewById(R.id.discount_name);
discount_name.setText(message);
}
public void setDiscount_Description(String message){
discount_name = mView.findViewById(R.id.discount_description);
discount_name.setText(message);
}
public void setDiscount_Value(String message){
discount_name = mView.findViewById(R.id.discount_value);
discount_name.setText(message);
}
public void setDiscount_Image(String downloadUri, String thumbUri){
discountImageView = mView.findViewById(R.id.discount_image);
RequestOptions requestOptions = new RequestOptions();
requestOptions.placeholder(R.drawable.image_placeholder);
Glide.with(context).applyDefaultRequestOptions(requestOptions).load(downloadUri).thumbnail(
Glide.with(context).load(thumbUri)
).into(discountImageView);
}
}
}
I believe the problem is coming from the discount adapter but I cant say for sure. The RecyclerView is displayed on a fragment if that could be influencing it I can attach other pieces if they are necessary.
There is nothing wrong with your code.So maybe you should try to override those two methods in your adapter class
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}

Display data from JSON inside RecyclerView(CardView)

I have a JSON node wich is displayed in logcat and look like this :
jsonData[{"numarProduse":"4","pretTotal":"1138","data":"11-27-2017","numeVanzator":"Clau","numarClient":0,"detaliiCos":{"metodaPlata":"CASH"}}]
But I want from this JSON response to display in another activity just 3 of them "numarProduse":"4", "pretTotal":"1138", "numarClient":0.
I tried something else, to upload data to Firebase and then retrive data from Firebase.
Here is my RecyclerView Adapter:
public class CardArrayAdapter extends RecyclerView.Adapter<CardArrayAdapter.ViewHolder> {
private List<Card> listdata;
public CardArrayAdapter(List<Card> listdata) {
this.listdata = listdata;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_card, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Card vanzatorProduse = listdata.get(position);
holder.txtViewNumarCumparator.setText(vanzatorProduse.getProdusId());
holder.listaProduse.setText(vanzatorProduse.getQty());
holder.sumaProduse.setText(vanzatorProduse.getSubTotal());
}
#Override
public int getItemCount() {
return listdata.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView txtViewNumarCumparator;
public TextView listaProduse;
public TextView sumaProduse;
public ViewHolder(View itemView) {
super(itemView);
txtViewNumarCumparator = (TextView) itemView.findViewById(R.id.txtViewNumarCumparator);
listaProduse = (TextView) itemView.findViewById(R.id.listaProduse);
sumaProduse = (TextView) itemView.findViewById(R.id.sumaProduse);
}
}
}
I made my layout for CardView, and object class with getters and setters.
public class Card implements Serializable {
public String produsId;
public String subTotal;
public String qty;
public Card(){}
public Card(String produsId, String subTotal, String qty) {
this.produsId = produsId;
this.subTotal = subTotal;
this.qty = qty;
}
public String getProdusId() {
return produsId;
}
public void setProdusId(String produsId) {
this.produsId = produsId;
}
public String getSubTotal() {
return subTotal;
}
public void setSubTotal(String subTotal) {
this.subTotal = subTotal;
}
public String getQty() {
return qty;
}
public void setQty(String qty) {
this.qty = qty;
}
}
And here is where I tried to retrive data from Firebase:
myRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
list = new ArrayList<>();
for (DataSnapshot snapshot : dataSnapshot.getChildren()){
VanzatorProduse produse = snapshot.getValue(VanzatorProduse.class);
Card listdata = new Card();
String id = produse.getProdusId();
String qty = String.valueOf(produse.getQty());
String suma = String.valueOf(produse.getSubTotal());
listdata.setProdusId(id);
listdata.setQty(qty);
listdata.setSubTotal(suma);
list.add(listdata);
}
CardArrayAdapter recycler = new CardArrayAdapter(list);
RecyclerView.LayoutManager layoutmanager = new LinearLayoutManager(VanzatorActivity.this);
recyclerview.setLayoutManager(layoutmanager);
recyclerview.setItemAnimator( new DefaultItemAnimator());
recyclerview.setAdapter(recycler);
}
But is not working, I get two columns where is 0 displayed. I have no idea what to do next, here I'm blocked.
You can create a bean class with setter getter for your List Items and the bean class should implement parcelable so that you can send the bean object to other activity. For recycler View you use adapter of same bean type and set that adapter once data is loaded from server.

Categories

Resources