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();
}
}
Related
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.
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.
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.
I am working on an android application, and I am using Firebase database,
this database showing an Items list.
This is my code.
private DatabaseReference root;
ListView elementList;
ArrayAdapter<mdlItem> adapter;
ArrayList<mdlItem> itemsList;
Context context;
private void loadFireDataBase() {
root = FirebaseDatabase.getInstance().getReference().child("Offers");
root.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Add_Chat(dataSnapshot, "Add");
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Add_Chat(dataSnapshot, "Edit");
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Add_Chat(dataSnapshot , "Delete");
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
//Log.i(tag, "onCreateView");
return creatList(inflater, container);
}
private View creatList(LayoutInflater inflater, #Nullable ViewGroup container) {
loadFireDataBase();
context = getActivity();
itemsList = new ArrayList<mdlItem>();
View view;
view = inflater.inflate(R.layout.freg_main_content, container, false);
elementList = (ListView) view.findViewById(R.id.customListView);
Collections.sort(itemsList, new Comparator<mdlItem>() {
#Override
public int compare(mdlItem data1, mdlItem data2) {
if (data1.getOfferIndex() > data2.getOfferIndex())
return 1;
else
return 0;
}
});
adapter = new offersArrayAdapter(context, R.layout.item_list_layout, itemsList);
elementList.setAdapter(adapter);
elementList.setOnItemClickListener(this);
return view;
}
private void Add_Chat(DataSnapshot dataSnapshot, String theCase) {
Map<String, Object> question = null;
try {
String theOfferCode = dataSnapshot.getKey();
question = (Map<String, Object>) dataSnapshot.getValue();
mdlItem mdl = new mdlItem();
mdl.setOfferCode(theOfferCode);
mdl.setRestCode(Integer.parseInt(String.valueOf(question.get("itemCode"))));
mdl.setRestName(question.get("itmeName").toString());
switch (theCase) {
case "Add":
itemsList.add(mdl);
break;
case "Delete":
itemsList.remove(mdl);
break;
case "Edit":
//??
break;
}
adapter.notifyDataSetChanged();
} catch (Exception ex) {
Toast.makeText(context, ex.getMessage(), Toast.LENGTH_LONG).show();
}
}
The problem is I can't get any node index through 'dataSnapshot' object, to control the list when I make Delete and Update.
And another thing how to use Add_Chat method to add the mdl item in the top of other items list.
I already catch the Edit, delete and add in the database root but I can't control how to use these events in the right way
I think I need to get the 'dataSnapshot' object to remove it or set an update on it, or if there is another solution
Can any one help me in this.
Firebase snapshots are not index-based. If you need a mapping from the key in the database to the index in your adapter, you will need to maintain this mapping yourself.
For a simple version of this, have a look at the FirebaseArray class in the FirebaseUI library. It maintains a list of all snapshots that are currently in the array:
private final List<DataSnapshot> mSnapshots = new ArrayList<>();
And then in the onChildAdded methods adds, determines where the new item fits in the list and adds it:
public void onChildAdded(#NonNull DataSnapshot snapshot, #Nullable String previousChildKey) {
int index = 0;
if (previousChildKey != null) {
index = getIndexForKey(previousChildKey) + 1;
}
mSnapshots.add(index, snapshot);
notifyOnChildChanged(ChangeEventType.ADDED, snapshot, index, -1);
}
It does the equivalent for all other onChild... methods, so that it can look up the index for a specific key when needed with its getIndexForKey method:
private int getIndexForKey(#NonNull String key) {
int index = 0;
for (DataSnapshot snapshot : mSnapshots) {
if (snapshot.getKey().equals(key)) {
return index;
} else {
index++;
}
}
throw new IllegalArgumentException("Key not found");
}
Just For recording, This is the correct solution
int index = -1;
for (mdlItem item: itemsList) {
if (item.getIndex() == mdl.getIndex()) {
index = itemsList.indexOf(item);
}
}
I want to implement loadmore with gridview and I am facing problems.
I am able to fetch new data on scroll and I am able to see new views itself. but previous views are disappearing.
I used notifyDataSetChanged().
But still this problem exists help me.
Here is my activity code
setting on scroll method in oncreate
grid.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int lastInScreen = firstVisibleItem + visibleItemCount;
if ((lastInScreen == totalItemCount) && !(loadingMore)) {
if (stopLoadingData == false) {
// FETCH THE NEXT BATCH OF FEEDS
listBikes();
}
}
}
}
listbikes is server call to fetch data on scroll.
In Processlistener method i will set the data to adapter.
bikeList = (CollectionResponseBike) result;
mCursor = bikeList.getNextPageToken();
items = (ArrayList) bikeList.getItems();
Collections.sort(items, new Comparator<Bike>() {
#Override
public int compare(Bike lhs, Bike rhs) {
String id1 = ((Bike) lhs).getTitle();
String id2 = ((Bike) rhs).getTitle();
return id1.compareTo(id2);
}
});
grid.setAdapter(new BikeCustomGrid(BikeGridList.this,items));
loadingMore = false;
progressBar.setVisibility(View.GONE);
Adapter code is here
public BikeCustomGrid(Context c, ArrayList<Bike> mItems) {
mContext = c;
items = mItems;
mLayoutInflater= LayoutInflater.from(c);
VolleySingleton mVolley= VolleySingleton.getInstance(mContext);
mImageLoader=mVolley.getImageLoader();
notifyDataSetChanged();
}
Please let me know the solution for this. thank you in adavance.
Whenever you call your database just add the new data into same list(don't replace it) and don't call the constructor again and again make some method in adapter to provide list and after that just notify the adapter.
Something like this:
mItems.addAll(yourListData coming from server or local db)
adapter.addItems(mItems);
method in adapter:
public addItems(List list){
items.addAll(list)
notifyDataSetChanged();
}