Why recyclerview returns wrong item? - java

I try to open video as I click on itemview. Sometimes it works correctly but sometimes it opens the second video when I click first video and vice versa. What I try to achieve is to open first video when I click first recyclerview item and so on.
UserHomeVideoAdapter.java:
public class UserHomeVideoAdapter extends FirestoreRecyclerAdapter<FollowList, UserHomeVideoAdapter.UserVideoHolder> {
Context context;
final FirebaseFirestore db = FirebaseFirestore.getInstance();
String thumbUrl, videoTitle, videoUrl, videoDesc, videoId, publisherId;
ArrayList<String> videoIdArrayList = new ArrayList<>();
public UserHomeVideoAdapter(#NonNull #NotNull FirestoreRecyclerOptions<FollowList> options, Context context) {
super(options);
this.context = context;
}
#Override
protected void onBindViewHolder(#NonNull #NotNull UserVideoHolder holder, int position, #NonNull #NotNull FollowList model) {
Query query = db.collection("Videos").whereEqualTo("publisherId", model.getUserId());
query.get().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
if (task.getResult() != null) {
for (QueryDocumentSnapshot documentSnapshot : task.getResult()) {
Video video = documentSnapshot.toObject(Video.class);
thumbUrl = video.getThumbUrl();
videoTitle = video.getVideoTitle();
videoUrl = video.getVideoUrl();
videoDesc = video.getVideoDesc();
videoId = video.getVideoId();
publisherId = video.getPublisherId();
videoIdArrayList.add(videoId);
}
if (task.getResult().size() > 0) {
db.collection("Users").document(model.getUserId()).get().addOnSuccessListener(documentSnapshot -> {
if (documentSnapshot != null) {
final User user = documentSnapshot.toObject(User.class);
if (user != null) {
if (user.getUserImageUrl() == null) {
holder.userProfileImage.setImageResource(R.drawable.ic_launcher_background);
} else {
Glide.with(context).load(Uri.parse(user.getUserImageUrl())).into(holder.userProfileImage);
}
}
}
}).addOnFailureListener(e -> Toast.makeText(context, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show());
Glide.with(context).load(thumbUrl).into(holder.videoImageView);
holder.videoTitle.setText(videoTitle);
holder.mainContainerVideo.setVisibility(View.VISIBLE);
} else if (task.getResult().size() == 0) {
holder.mainContainerVideo.getLayoutParams().height = 0;
holder.mainContainerVideo.getLayoutParams().width = 0;
}
}
} else {
Toast.makeText(context, String.valueOf(task.getException()), Toast.LENGTH_SHORT).show();
}
}).addOnFailureListener(e -> Toast.makeText(context, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show());
holder.itemView.setOnClickListener(view -> {
Intent intent = new Intent(context, VideoActivity.class);
intent.putExtra("videoPublisherUserName", model.getUserName());
intent.putExtra("thumbUrl", thumbUrl);
intent.putExtra("videoPublisherEmail", model.getUserEmail());
intent.putExtra("videoUrl", videoUrl);
intent.putExtra("videoId", videoIdArrayList.get(position));
intent.putExtra("videoPublisherFullName", model.getUserFullName());
intent.putExtra("videoPublisherId", publisherId);
context.startActivity(intent);
Log.d("Bax", videoIdArrayList.get(position));
});
}
#NonNull
#NotNull
#Override
public UserVideoHolder onCreateViewHolder(#NonNull #NotNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(context).inflate(R.layout.video_cell, parent, false);
return new UserVideoHolder(v);
}
public static class UserVideoHolder extends RecyclerView.ViewHolder {
RelativeLayout mainContainerVideo;
CircleImageView userProfileImage;
TextView videoTitle;
ImageView videoImageView;
public UserVideoHolder(#NonNull #NotNull View itemView) {
super(itemView);
mainContainerVideo = itemView.findViewById(R.id.mainContainerVideo);
userProfileImage = itemView.findViewById(R.id.userProfileImage);
videoTitle = itemView.findViewById(R.id.videoTitle);
videoImageView = itemView.findViewById(R.id.videoImageView);
}
}
}
I don't understand the problem with my code so any help is appreciated. Thanks

The problem here is your videoIdArrayList.add(videoId) is not being reset. Every time your recyclerview loads, it queries the database and adds videoId in the videoIdArrayList and that keeps adding.
That is messing up the videoId being sent to the next activity.
So, clear your videoIdArrayList Every time the recyclerview loads and add Id's after that.
So, before your for loop in firebase query, just clear the list.
videoIdArrayList.clear()
Should work perfectly.

onBindViewHolder executes for each item in RecyclerView. Not once
Query query = db.collection("Videos").whereEqualTo("publisherId", model.getUserId()); returns a List, not item.
videoIdArrayList.add(videoId); could be executed for first item many times.
String thumbUrl, videoTitle, videoUrl, videoDesc, videoId, publisherId; are setted in onBindViewHolder in Query. Again onBindViewHolder executes for each item in RecyclerView. Not once.
You must refactor your adapter.
execute query over your adapter.
It is not clear, what items receive holders. (for me. if for you it is clear -> skip the step)
instead holder.videoTitle make a method in holder (for example void configView(YourItem item)) and evertyhing regarding to that item execute in holder.

Related

Recyclerview row format changes after search filter

I have a recyclerview that displays a list of contacts. To differentiate between contacts that are also users of my app (let's refer to these as app-contacts) and all other contacts (non-app-contacts), i have made the typeface of all app-contacts bold (Typeface.BOLD), and non-app-contacts normal (Typeface.NORMAL). However, when the recyclerview gets filtered while searching for a contact, and app-contacts get displayed in certain rows (let's say rows 1 and 2) with a bold typeface, then those rows remain in a bold typeface. Even when i change the search, and non-app-contacts (which should be in a normal typeface) now occupy those rows (1 and 2), it’s in a bold typeface. Essentially rows 1 and 2 now remain in a bold typeface regardless of the type of contact being displayed in them.
Here is my recyclerview adapter. the onBindViewHolder is where i change the typeface. "is Suma Contact" boolean means the contact is an app contact.
public class SearchRecipientHintsAdapter extends RecyclerView.Adapter<SearchRecipientHintsAdapter.ViewHolder> {
private Context context;
private List<RecipientsContactItem> contactItems;
private final int SELECT_DROPOFF_REQUEST_CODE = 77;
public SearchRecipientHintsAdapter (Context context, List<RecipientsContactItem> contactItems) {
this.context = context;
this.contactItems = contactItems;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recipients_contact_row,parent,false);
return new ViewHolder(view, context);
}
#Override
public void onBindViewHolder(#NonNull SearchRecipientHintsAdapter.ViewHolder holder, int position) {
RecipientsContactItem contactItem = contactItems.get(position);
holder.name.setText(contactItem.getName());
holder.phoneNumber.setText(contactItem.getPhoneNumber());
if (contactItem.getImage() != null && !contactItem.getImage().isEmpty()) {
try {
Picasso.get().load(contactItem.getImage()).into(holder.image);
} catch (Throwable ignored) { }
} else {
holder.image.setImageDrawable(context.getResources().getDrawable(R.drawable.user_default_img));
}
if (contactItem.isVerified()) {
holder.verificationIcon.setVisibility(View.VISIBLE);
} else {
holder.verificationIcon.setVisibility(View.GONE);
}
if (contactItem.isSumaContact()) {
holder.name.setTypeface(holder.name.getTypeface(), Typeface.BOLD);
switch (contactItem.getPrivacy()) {
case "Public":
holder.publicIcon.setVisibility(View.VISIBLE);
holder.privateIcon.setVisibility(View.GONE);
holder.allowedIcon.setVisibility(View.GONE);
holder.inviteButton.setVisibility(View.GONE);
break;
case "Private":
holder.publicIcon.setVisibility(View.GONE);
holder.privateIcon.setVisibility(View.VISIBLE);
holder.allowedIcon.setVisibility(View.GONE);
holder.inviteButton.setVisibility(View.GONE);
break;
case "Allowed":
holder.publicIcon.setVisibility(View.GONE);
holder.privateIcon.setVisibility(View.GONE);
holder.allowedIcon.setVisibility(View.VISIBLE);
holder.inviteButton.setVisibility(View.GONE);
break;
}
} else {
holder.name.setTypeface(holder.name.getTypeface(), Typeface.NORMAL);
holder.inviteButton.setVisibility(View.VISIBLE);
holder.publicIcon.setVisibility(View.GONE);
holder.privateIcon.setVisibility(View.GONE);
holder.allowedIcon.setVisibility(View.GONE);
}
}
#Override
public int getItemCount() {
return contactItems.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView name;
private TextView phoneNumber;
private ImageView image;
private ImageView verificationIcon;
private Button inviteButton;
private ImageView publicIcon;
private ImageView privateIcon;
private ImageView allowedIcon;
public ViewHolder(#NonNull View itemView, Context ctx) {
super(itemView);
context = ctx;
name = itemView.findViewById(R.id.recipientsCRowNameID);
phoneNumber = itemView.findViewById(R.id.recipientsCRowPhoneID);
image = itemView.findViewById(R.id.recipientsCRowImageID);
verificationIcon = itemView.findViewById(R.id.recipientsCRowVerifiedID);
inviteButton = itemView.findViewById(R.id.recipientsCRowInviteID);
publicIcon = itemView.findViewById(R.id.recipientsCRowPublicID);
privateIcon = itemView.findViewById(R.id.recipientsCRowPrivateID);
allowedIcon = itemView.findViewById(R.id.recipientsCRowAllowedID);
itemView.setOnClickListener(v -> {
//Get position of row
int position = getAdapterPosition();
RecipientsContactItem contactItem = contactItems.get(position);
String uID = contactItem.getUID();
String name = contactItem.getName();
String phoneNumber = contactItem.getPhoneNumber();
String lat = contactItem.getLat();
String lng = contactItem.getLng();
boolean isSumaContact = contactItem.isSumaContact();
if (isSumaContact) {
if (contactItem.getPrivacy().equals("Public") || contactItem.getPrivacy().equals("Allowed")) {
Intent returnRecipientIntent = ((Activity) context).getIntent();
returnRecipientIntent.putExtra("uID", uID);
returnRecipientIntent.putExtra("name", name);
returnRecipientIntent.putExtra("phoneNumber", phoneNumber);
returnRecipientIntent.putExtra("lat", lat);
returnRecipientIntent.putExtra("lng", lng);
returnRecipientIntent.putExtra("isSumaContact", true);
((Activity) context).setResult(Activity.RESULT_OK, returnRecipientIntent);
((Activity) context).finish();
} else {
Toast.makeText(context, R.string.recipients_search_disallowed_toast, Toast.LENGTH_LONG).show();
}
} else {
Intent dropOffSearchIntent = new Intent(context, SelectDropoff.class);
((Activity) context).startActivityForResult(dropOffSearchIntent, SELECT_DROPOFF_REQUEST_CODE);
}
});
inviteButton.setOnClickListener(view -> {
Intent sendInvite = new Intent(android.content.Intent.ACTION_VIEW);
sendInvite.putExtra("address", contactItems.get(getAdapterPosition()).getPhoneNumber());
sendInvite.putExtra("sms_body", context.getResources().getString(R.string.recipients_invite_link));
sendInvite.setType("vnd.android-dir/mms-sms");
try {
context.startActivity(sendInvite);
} catch (Throwable t) {
Toast.makeText(context, "Sorry, invite not working. Please use the invite in your main menu", Toast.LENGTH_LONG).show();
}
});
}
}
#Override
public int getItemViewType(int position) {
return position;
}
public void updateWithSearchFilter (List<RecipientsContactItem> newList) {
contactItems = new LinkedList<>();
contactItems.addAll(newList);
notifyDataSetChanged();
}
}
Here is the onQueryTextChange() in setOnQueryTextListener() where i filter the search and pass the result/new list to the adapter above
public boolean onQueryTextChange(String newText) {
String userInput = newText.toLowerCase();
if (userInput.startsWith("0")) {userInput = userInput.substring(1);}
List<RecipientsContactItem> newList = new LinkedList<>();
for (RecipientsContactItem contactItem : sumaContacts) {
if (contactItem.getName().toLowerCase().contains(userInput) || contactItem.getPhoneNumber().contains(userInput)) {
newList.add(contactItem);
}
}
((SearchRecipientHintsAdapter) searchRHintsAdapter).updateWithSearchFilter(newList);
return true;
}
Shot 1:
the 2 contacts displayed are non-app contacts so their typeface is normal (not bold)
Shot 2. After filtering search to display an app-contact:
the first contact is an contact (bold typeface) and the second is a non-app contact (normal typeface - not bold)
Shot 3. After clearing search filter to display contacts in shot 1:
both contacts are non-app contacts and should be in a normal typeface (not bold). But the first contact is displayed as bold, because an app-contact (which is bold) was briefly displayed there (in shot 2) while filtering search
NB: The problem used to be caused by scrolling too. Till i #Override the getItemViewType() method of the Adapter
Initially, anytime i scroll the recyclerview, the Bold Typeface would be wrongly applied to rows/contacts that shouldn't be bold. Till i found a solution where i had to overrider the getItemViewType() method of the recyclerview adapter like this:
#Override
public int getItemViewType(int position) {
return position;
}
then it was fixed (for scrolling). till i realized that the problem persisted for filtering. So that's what i'm trying to fix now
The problem is
holder.name.setTypeface(holder.name.getTypeface(), Typeface.NORMAL);
When rebinding a viewholder with bold in place, holder.getTypeface() returns the bold typeface that was there earlier. Now, Typeface.NORMAL has the value 0. Here's the setTypeface() implementation from cs.android.com:
public void setTypeface(#Nullable Typeface tf, #Typeface.Style int style) {
if (style > 0) {
if (tf == null) {
tf = Typeface.defaultFromStyle(style);
} else {
tf = Typeface.create(tf, style);
}
setTypeface(tf);
// now compute what (if any) algorithmic styling is needed
int typefaceStyle = tf != null ? tf.getStyle() : 0;
int need = style & ~typefaceStyle;
mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
} else {
mTextPaint.setFakeBoldText(false);
mTextPaint.setTextSkewX(0);
setTypeface(tf);
}
}
Note the if (style > 0) part there. So, passing in Typeface.NORMAL will just set the typeface as-is, without doing any styling on it, so your bold style will stay bold.
To fix that, either pass in a null for typeface if that is appropriate for you, or reset the typeface to a default that fits your needs.
In addition, there's also a perf problem in your
#Override
public int getItemViewType(int position) {
return position;
}
This makes each row have its own specific view type. But you really only have one view type, so you don't need to override this method at all. Or if you do, you can return a constant value.

How to prevent RecyclerView from refreshing all the datas when a newly data has been added?

I'm creating a simple chat app wherein every chatbubbles will be shown in a RecyclerView, now I noticed that every time ill enter a new data coming from Firebase RealTime Database, the old data's / or let's say the old chat bubbles will disappear and reappear once the newly added data has been displayed. I would like the old chat bubbles to not behave just like that, I would like it to remain appeared the whole time.
Here's my method to load every chatbubbles:
private void LoadChat() {
Query orderPosts = ChatRef.orderByChild("servertimestamp");
options = new FirebaseRecyclerOptions.Builder<Chat>().setQuery(orderPosts, Chat.class).build();
adapter = new FirebaseRecyclerAdapter<Chat, MyViewHolder12>(options) {
#Override
protected void onBindViewHolder(#NonNull MyViewHolder12 holder, int position, #NonNull Chat model) {
final String userpower = model.getPower();
final String pow = "Admin";
if (userpower.equals(pow)){
holder.chat_userpower.setVisibility(View.VISIBLE);
holder.chat_userpower.setText(model.getPower());
}
else{
holder.chat_userpower.setVisibility(View.GONE);
}
final String quotedc = model.getQuotedchat();
final String quotedn = model.getQuotedname();
if (quotedc == null){
holder.quotedchatbox.setVisibility(View.GONE);
holder.quotedchatboxlayout.setVisibility(View.GONE);
holder.quotedchatdescription.setVisibility(View.GONE);
}
else{
holder.quotedchatboxlayout.setVisibility(View.VISIBLE);
holder.quotedchatbox.setVisibility(View.VISIBLE);
holder.quotedchatdescription.setVisibility(View.VISIBLE);
holder.quotedchatdescription.setText("Quoted "+ model.getQuotedname() +" " + model.getQuotedchat());
}
holder.chat_usercomment.setText(model.getChat());
Picasso.get().load(model.getProfileimage()).placeholder(R.drawable.profile).into(holder.chat_userimage);
holder.chat_userdep.setText(model.getDep());
holder.chat_date.setText(model.getDate());
holder.chat_username.setText(model.getUsername());
holder.nestedchat_reply.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
quote = true;
quotedname = model.getUsername();
//CommentKey = getRef(holder.getAdapterPosition()).getKey();
quoting.setVisibility(View.VISIBLE);
quotedchat = model.getChat();
quoting.setText("Quoting "+ quotedname + ": " + model.getChat());
quoting.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
quote = false;
quoting.setVisibility(View.GONE);
}
});
}
});
}
#NonNull
#Override
public MyViewHolder12 onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.allchatlayout, parent, false);
return new MyViewHolder12(view);
}
};
adapter.startListening();
allchatlist.setAdapter(adapter);
}
here's my layoutmanager:
LinearLayoutManager lm = new LinearLayoutManager(this);
lm.setReverseLayout(false);
lm.setStackFromEnd(false);
allchatlist.setNestedScrollingEnabled(false);
allchatlist.setLayoutManager(lm);
here's my code calling the method:
ChatRef = FirebaseDatabase.getInstance().getReference().child("Forums").child(ChatRoomNameKey).child("Forum ChatRoom");
ChatRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
if (snapshot.exists()){
LoadChat();
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
To achieve that you will have to use RecyclerView DiffUtill class, more info here:
https://developer.android.com/reference/androidx/recyclerview/widget/DiffUtil
In a nutshell you have to create a diff util class:
class CustomItemDiffUtils(
private val oldList: List<CustomItem>,
private val newList: List<CustomItem>
) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].data == newList[newItemPosition].data
}
}
And use this diff class in your adapter fro example with a method which can be called from the view:
fun updateList(newList: List<CustomItem>) {
val diffResult = DiffUtil.calculateDiff(CustomItemDiffUtils(oldList, newList))
oldList = newList
diffResult.dispatchUpdatesTo(this)
}
Hope this helps.
I fixed the problem by removing the line:
Query orderPosts = ChatRef.orderByChild("servertimestamp");
options = new FirebaseRecyclerOptions.Builder<Chat>().setQuery(orderPosts, Chat.class).build();
Removing that 2 lines of code from that method and putting it somewhere else inside the Activity fixed the blinking problem of my app when a new data has been added.

List is returning item after deleting all the items [duplicate]

My List is returning an item after clearing all the items by deleting ,On app fresh install its returing null which is good but after adding item and then by deleting all, this happens when go back from that activity and come again, list.size() is returning 1 and an item is remaing ,i don't know if it is loading from cache object instance here is my code of adapter class
[please look to the image attached ,list is empty but still counter 1 counter = cartModelList.size()]i have a list of cart itemsprivate List<CartModel> cartModelList;
It's returning null on app fresh install which is good but when i add item to the cart and then remove all the items then its returning 1.
I mean cartmodelList.size() is returning as far I know it's returning some items from cached objects or some thing like that.
The question is how to remove that List object cached or any alternative?
I tried on delete button but still cached coming
public static double p = 0;
private List<CartModel> cartModelList;
Database db;
Context context;
public CartAdapter(Context context, List<CartModel> cartModelList) {
this.cartModelList = cartModelList;
this.context = context;
db = new Database(context);
}
#NonNull
#Override
public Viewholder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cart_layout_item, parent, false);
return new Viewholder(view);
}
#Override
public void onBindViewHolder(#NonNull final Viewholder holder, final int position) {
String namee = cartModelList.get(position).getName();
String manufacturere = cartModelList.get(position).getManufacturer();
String availabilitye = cartModelList.get(position).getAvailability();
String e_parte = cartModelList.get(position).getE_part();
String m_parte = cartModelList.get(position).getM_part();
String floatprice = cartModelList.get(position).getUnit_();
String int_quantity = cartModelList.get(position).getQuantity();
String float_line_total = cartModelList.get(position).getLine_total();
holder.setItemDetails(namee, manufacturere, availabilitye, e_parte, m_parte, floatprice, int_quantity, float_line_total);
int checker = SharedPrefManager.getInstance(context).cartcount().getCounter();
if (checker <= 0){
cartModelList.clear();
}
holder.btn_delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (db.deleteProduct(cartModelList.get(position).getID())) {
cartModelList.remove(position);
notifyDataSetChanged();
Toast.makeText(context, "Product deleted from cart", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "Product not deleted from cart", Toast.LENGTH_LONG).show();
}
CartList user111 = new CartList(--COUNTER_BADGE);
// Toast.makeText(context, "else", Toast.LENGTH_SHORT).show();
SharedPrefManager.getInstance(context).cartList(user111);
((Activity)context).invalidateOptionsMenu();
((Activity)context).finish();
Intent intent = new Intent(context, CartActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
context.startActivity(intent);
}
});
#Override
public int getItemCount() {
return cartModelList.size();
}
class Viewholder extends RecyclerView.ViewHolder {
private TextView name;
private TextView manufacturer;
private TextView availability;
private TextView e_part;
private TextView m_part;
private TextView price;
private EditText quantity;
private TextView linetotal;
private Button btn_delete;
private Button btn_update;
private Button adapter_livestock;
public SpinKitView progressbar;
public Viewholder(#NonNull View itemView) {
super(itemView);
name = itemView.findViewById(R.id.name);
manufacturer = itemView.findViewById(R.id.manufacturer);
availability = itemView.findViewById(R.id.availability);
e_part = itemView.findViewById(R.id.e_part);
m_part = itemView.findViewById(R.id.m_part);
price = itemView.findViewById(R.id.price);
quantity = itemView.findViewById(R.id.quantity);
linetotal = itemView.findViewById(R.id.linetotal);
btn_delete = itemView.findViewById(R.id.btn_delete);
btn_update = itemView.findViewById(R.id.btn_update);
adapter_livestock = itemView.findViewById(R.id.adapter_livestock);
progressbar = itemView.findViewById(R.id.adapterrprogresslivestockprogress);
}
private void setItemDetails(String namee, String manufacturere, String availabilitye, String e_parte, String m_parte, String floatprice, String int_quantity, String float_line_total) {
name.setText(namee);
manufacturer.setText(manufacturere);
availability.setText(availabilitye);
e_part.setText(e_parte);
m_part.setText(m_parte);
price.setText("£"+floatprice);
quantity.setText(int_quantity);
linetotal.setText("£"+float_line_total);
}
}
[https://i.stack.imgur.com/PxDTZ.jpg]
Okay... The first.
if (db.deleteProduct(cartModelList.get(position).getID()))
will not delete your item from cartModelList, you need to do it manually. Like this:
if (db.deleteProduct(cartModelList.get(position).getID())) {
cartModelList.remove(position)
And the second. You have to call notifyDataSetChanged() or itemChanged or itemRemoved etc. only in the end of your deletion method. Please, tell me, if it worked.
P.S. Your items do not cached. The problem is in your code order.
Edit 1. Also, you need to check your db.deleteProduct method. Is it worked? Is your if statement worked?
Edit 2. Try this.
holder.btn_delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (db.deleteProduct(cartModelList.get(position).getID())) {
cartModelList.remove(position);
notifyItemRemoved(position);
Toast.makeText(context, "Product deleted from cart", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "Product not deleted from cart", Toast.LENGTH_LONG).show();
}
CartList user111 = new CartList(cartModelList.size());
// Toast.makeText(context, "else", Toast.LENGTH_SHORT).show();
SharedPrefManager.getInstance(context).cartList(user111);
((Activity)context).invalidateOptionsMenu();
((Activity)context).finish();
Intent intent = new Intent(context, CartActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
context.startActivity(intent);
}
});
my problem is solved by putting cartModelList.clear on delete button when cartModelList.size() == 1 , so after deleting the last item it will clear the list.

Retrieving data from Cloud Firestore with AsynkTaskLoader in Android

I'm new to Android and Firebase and I'm having this weird bug I don't understand. I have a RecyclerView that should display a list of Strings that have dates followed by a number. Now, I want to retrieve the data from Cloud Firestore and display it. For this purpose, I use an AsyncTaskLoader and in loadInBackground() I retrieve the data from Cloud Firestore. Now, when I start the activity, it shows the error message (and it will keep behaving this way no matter how many times I Hit the refresh button). However, if I turn off the screen and then turn it on, it shows the data the way I wish. The following is my code
public class MeasureListActivity extends AppCompatActivity implements
MeasuresAdapter.MeasuresAdapterOnClickHandler,
LoaderManager.LoaderCallbacks<String[]> {
private RecyclerView mRecyclerView;
private FirebaseAuth mAuth;
private MeasuresAdapter mMeasuresAdapter;
private TextView mErrorMessageDisplay;
private ProgressBar mLoadingIndicator;
private static final int MEASURES_LOADER_ID = 0;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
/* Use AppCompatActivity's method getMenuInflater to get a handle on the menu inflater */
MenuInflater inflater = getMenuInflater();
/* Use the inflater's inflate method to inflate our menu layout to this menu */
inflater.inflate(R.menu.measures_list, menu);
/* Return true so that the menu is displayed in the Toolbar */
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_refresh) {
invalidateData();
getSupportLoaderManager().restartLoader(MEASURES_LOADER_ID, null, this);
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_measure_list);
mAuth = FirebaseAuth.getInstance();
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview_measures);
mErrorMessageDisplay = (TextView) findViewById(R.id.tv_error_message_display);
LinearLayoutManager layoutManager
= new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setHasFixedSize(true);
mMeasuresAdapter = new MeasuresAdapter(this);
mRecyclerView.setAdapter(mMeasuresAdapter);
mLoadingIndicator = (ProgressBar) findViewById(R.id.pb_loading_indicator);
int loaderId = MEASURES_LOADER_ID;
LoaderManager.LoaderCallbacks<String[]> callback = MeasureListActivity.this;
Bundle bundleForLoader = null;
getSupportLoaderManager().initLoader(loaderId, bundleForLoader, callback);
}
#Override
public Loader<String[]> onCreateLoader(int id, final Bundle loaderArgs) {
return new AsyncTaskLoader<String[]>(this) {
String[] mWMeasuresData = null;
#Override
protected void onStartLoading() {
if (mWMeasuresData != null) {
deliverResult(mWMeasuresData);
} else {
mLoadingIndicator.setVisibility(View.VISIBLE);
forceLoad();
}
}
private String[] aux;
#Override
public String[] loadInBackground() {
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("medidas").whereEqualTo("id_user", "3Aq3g0czkarT8GIbyESV").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
QuerySnapshot snapshot = task.getResult();
int tam = snapshot.getDocuments().size();
aux = new String[tam];
for (int i = 0; i < tam; i++) {
String temp = "";
DocumentSnapshot doc = snapshot.getDocuments().get(i);
temp += doc.get("fecha") + " ";
temp += doc.get("valor");
aux[i] = temp;
}
}
}
});
return aux;
}
public void deliverResult(String[] data) {
mWMeasuresData = data;
super.deliverResult(data);
}
};
}
#Override
public void onLoadFinished(Loader<String[]> loader, String[] data) {
mLoadingIndicator.setVisibility(View.INVISIBLE);
mMeasuresAdapter.setMeasuresData(data);
if (null == data) {
showErrorMessage();
} else {
showMeasuresDataView();
}
}
#Override
public void onLoaderReset(Loader<String[]> loader) {
}
private void invalidateData() {
mMeasuresAdapter.setMeasuresData(null);
}
private void showMeasuresDataView() {
/* First, make sure the error is invisible */
mErrorMessageDisplay.setVisibility(View.INVISIBLE);
/* Then, make sure the weather data is visible */
mRecyclerView.setVisibility(View.VISIBLE);
}
private void showErrorMessage() {
/* First, hide the currently visible data */
mRecyclerView.setVisibility(View.INVISIBLE);
/* Then, show the error */
mErrorMessageDisplay.setVisibility(View.VISIBLE);
}
#Override
public void onClick(String measure) {
Context context = this;
Class destinationClass = DetailMeasureActivity.class;
Intent intentToStartDetailActivity = new Intent(context, destinationClass);
intentToStartDetailActivity.putExtra(Intent.EXTRA_TEXT, measure);
startActivity(intentToStartDetailActivity);
}
}
Can you help me with this?...thanks in advance
First of all, I think that when you call this block of code:
mLoadingIndicator.setVisibility(View.VISIBLE);
forceLoad();
You should also hide the error message view.
Also, on a more serious note, you do not need AsyncTaskLoader to get your data from firestore, firebase firestore already handles background work for you.
To be able to understand fully where the problem is from, you need to understand you activity lifecycle and add logs to mark critical events that will help you to know which block of code is called at any point in time. So I will encourage you to add logs for when the data loading starts, finishes and when you are hiding a view or showing a view just to be sure that all methods are called and also add logs to your activity lifecycles.

Invert ListView Order and Display Without Scrolling

I am trying to invert a ListView so that the most recent items appear first. I have seen results that state to modify the getItem() method, however that requires me scrolling down and scrolling back up to see the new item. Is there a way to have the item appear at the top of the list without requiring the need to scroll?
public class ListAdapter extends ArrayAdapter<Comments> {
Firebase BaseRef = new Firebase(FIREBASE_URL);
Firebase PollsRef = mBaseRef.child(POLLS_LABEL);
Firebase UpdateRef = mPollsRef.child(mCurrentDateString).child(String.valueOf(mPollIndex + 1));
Firebase CommentsRef = mUpdateRef.child(COMMENTS_LABEL);
int pollCommentCount;
public ListAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
public ListAdapter(Context context, int resource, List<Comments> items) {
super(context, resource, items);
}
#Override
public int getCount() {
CommentsRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
pollCommentCount = (int) dataSnapshot.getChildrenCount();
Log.v("POLL_COMMENT_COUNT", "The poll comment count is " + pollCommentCount);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
return pollCommentCount;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi;
vi = LayoutInflater.from(getContext());
v = vi.inflate(R.layout.individual_comment, null);
}
Comments p = getItem(position);
if (p != null) {
TextView userID = (TextView) v.findViewById(R.id.user_ID);
TextView userComment = (TextView) v.findViewById(R.id.user_comment);
if (userID != null) {
userID.setText(p.getUserID());
}
if (userComment != null) {
userComment.setText(p.getUserComment());
}
}
return v;
}
}
You can sort the Comment list before creating your adapter. This way they are already in the order you want them to be in. I don't know what variable the Comment object contains that lets you know when it was modified, but assuming it is a date, you can sort the list like this:
Collections.sort(commentsList, new Comparator<Comment>() {
public int compare(Comment c1, Comment c2) {
return c1.getDate().compareTo(c2.getDate());
}
});
You can also simply reverse the list with Collections.reverse(commentList)
Calling notifyDataSetChanged() should update the list.
I realized that the .add() method actually inserts the item at a specific index. If I am always adding new items to index(0), then the items will naturally appear in reverse order.
I thought Google would have been more intuitive with the Android code and allowed for an insert() method, but the add() method at index(o) serves the purpose:
mUpdateRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
setImage(dataSnapshot);
setQuestion(dataSnapshot);
createInitialCommentIDArray(dataSnapshot);
mNumberOfCommentsAtPoll = (int) dataSnapshot.child(COMMENTS_LABEL).getChildrenCount();
for (int i = 0; i < mNumberOfCommentsAtPoll; i++) {
String commentID = (String) dataSnapshot.child(COMMENTS_LABEL).child(mCommentIDArrayList.get(i)).child("COMMENT").getValue();
Log.v("COMMENT_ID", "The comment ID is " + commentID);
String userID = (String) dataSnapshot.child(COMMENTS_LABEL).child(mCommentIDArrayList.get(i)).child("USER_ID").getValue();
Log.v("USER_ID", "The user ID is " + userID);
mCommentArrayList.add(0 , new Comments(mUserAvatar, userID, commentID));
mCommentAdapter.notifyDataSetChanged();
}
}

Categories