I am currently having an error in my app. I have a recyclerview that loads posts from a firebase database and displays them. When I switch fragments and activities I do not get any errors. When I click the home button or tabs button on my phone and then click back into my app there is a crash with the error message,
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{777f444 position=1
id=-1, oldPos=0, pLpos:0 scrap [attachedScrap] tmpDetached no parent}
android.support.v7.widget.RecyclerView{efb45c8 VFED..... .F......
0,0-1080,1497 #7f0900c2 app:id/postList},
adapter:com.sender.hp.sender.DashboardFragment$2#efd661,
layout:android.support.v7.widget.LinearLayoutManager#9760586,
context:com.sender.hp.sender.MainActivity#720f0fa
Whenever I hit the back button on my phone and my app gets closed, If I go into my tabs and hit back into my app it works fine. I don't have an onBackPressed listener.
Here is my code,
firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<postsGetInfo, postsGetInfoViewHolder>(postsGetInfo.class, R.layout.posts_layout,postsGetInfoViewHolder.class,postRef) {
#Override
protected void populateViewHolder(final postsGetInfoViewHolder viewHolder, postsGetInfo model, int position) {
TextView txtUpvote = (TextView) viewHolder.myView.findViewById(R.id.txtUpvoteCount);
final String postKey = getRef(position).getKey();
viewHolder.setUsername(model.getUsername());
viewHolder.setTime(model.getTime());
viewHolder.setDate(model.getDate());
viewHolder.setDescription(model.getDescription());
viewHolder.setProfileImage(model.getProfileimage());
viewHolder.setImage(model.getImage());
viewHolder.setDisplayvotes(model.getDisplayvotes());
viewHolder.myView.findViewById(R.id.imgDownvote).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
UpdateTheDisplayVotes(postKey);
++getDownvotes;
--getDisplayVotes;
String txtDisplayVotes = Integer.toString(getDisplayVotes);
postRef.child(postKey).child("downvotes").setValue(getDownvotes);
postRef.child(postKey).child("displayvotes").setValue(txtDisplayVotes);
}
});
viewHolder.myView.findViewById(R.id.imgUpvote).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
UpdateTheDisplayVotes(postKey);
++getUpvotes;
++getDisplayVotes;
String txtDisplayVotes = Integer.toString(getDisplayVotes);
postRef.child(postKey).child("upvotes").setValue(getUpvotes);
postRef.child(postKey).child("displayvotes").setValue(txtDisplayVotes);
}
});
viewHolder.myView.findViewById(R.id.imgPost).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent editIntent = new Intent((getActivity()),postEdit.class);
editIntent.putExtra("PostKey",postKey);
startActivity(editIntent);
}
});
}
};
postList.setAdapter(firebaseRecyclerAdapter);
}
private void UpdateTheDisplayVotes(final String key) {
VoteListener = postRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
if (dataSnapshot.hasChild("displayvotes")){
displayvotes = dataSnapshot.child(key).child("displayvotes").getValue().toString();
getDisplayVotes = Integer.parseInt(displayvotes);
}
if (dataSnapshot.hasChild("upvotes")){
upvotes = dataSnapshot.child(key).child("upvotes").getValue().toString();
getUpvotes = Integer.parseInt(upvotes);
}
if (dataSnapshot.hasChild("downvotes")){
downvotes = dataSnapshot.child(key).child("downvotes").getValue().toString();
getDownvotes = Integer.parseInt(downvotes);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onStart() {
super.onStart();
firebaseRecyclerAdapter.startListening();
}
#Override
public void onStop() {
super.onStop();
if (firebaseRecyclerAdapter != null){
firebaseRecyclerAdapter.cleanup();
}
}
Anyone know how to fix? Thanks for any answers!
This error occurs when the adapter or list in the adapter is cleared.
Call notifyDataSetChanged() when adapter is cleared.
#Override
public void onStop() {
super.onStop();
if (firebaseRecyclerAdapter != null){
firebaseRecyclerAdapter.cleanup();
firebaseRecyclerAdapter.notifyDataSetChanged();
}
}
Related
I've just recently started to learn some coding, a little python and java.
I'm trying to make a whatsapp clone as a test, and I've hit a wall.
Basically I have a UID showing as the name of a group chat, and I want to swap it for a groupId I have saved in firebase at the same level as the Uid.
FirebaseDatabase.getInstance().getReference().child("chat").child(chatId).child("info").child("groupId)
I've been following simcoder youtube video and there's a lot of checks and balances happening between user and chat UIDs and I'm getting lost in the middle of all that.
I've tried adding groupId to the adapter and the chatObject, and I can see the data in debug, but I just can't get it to populate the recycler view. I've had a search of the forums here, but can't quite get it to work.
I'm hoping someone can help me out, many thanks
private RecyclerView mChatList;
private RecyclerView.Adapter mChatListAdapter;
private RecyclerView.LayoutManager mChatListLayoutManager;
ArrayList<ChatObject> chatList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_group_chat);
OneSignal.startInit(this).init();
OneSignal.setSubscription(true);
OneSignal.idsAvailable(new OneSignal.IdsAvailableHandler() {
#Override
public void idsAvailable(String userId, String registrationId) {
FirebaseDatabase.getInstance().getReference().child("user").child(FirebaseAuth.getInstance().getUid()).child("notificationKey").setValue(userId);
}
});
OneSignal.setInFocusDisplaying(OneSignal.OSInFocusDisplayOption.Notification);
Button mLogout = findViewById(R.id.logout);
Button mFindUser = findViewById(R.id.finduser);
mFindUser.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(getApplicationContext(), FindUserActivity.class));
}
});
mLogout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
OneSignal.setSubscription(false);
FirebaseAuth.getInstance().signOut();
Intent intent = new Intent(getApplicationContext(), LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
return;
}
});
getPermissions();
initializeRecyclerView();
getUserChatList();
}
private void getUserChatList(){
DatabaseReference mUserChatDB = FirebaseDatabase.getInstance().getReference().child("user").child(FirebaseAuth.getInstance().getUid()).child("chat");
mUserChatDB.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()){
for (DataSnapshot childSnapshot : dataSnapshot.getChildren()){
ChatObject mChat = new ChatObject(childSnapshot.getKey());
boolean exists = false;
for (ChatObject mChatIterator : chatList){
if(mChatIterator.getChatId().equals(mChat.getChatId()))
exists = true;
}
if (exists)
continue;
chatList.add(mChat);
getChatData(mChat.getChatId());
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void getChatData(String chatId) {
DatabaseReference mChatDB = FirebaseDatabase.getInstance().getReference().child("chat").child(chatId).child("info");
mChatDB.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()){
String chatId = "";
if(dataSnapshot.child("id").getValue() != null)
chatId = dataSnapshot.child("id").getValue().toString();
for(DataSnapshot userSnapshot : dataSnapshot.child("users").getChildren()){
for(ChatObject mChat : chatList)
if(mChat.getChatId().equals(chatId)){
UserObject mUser = new UserObject(userSnapshot.getKey());
mChat.addUserToArrayList(mUser);
getUserData(mUser);
}
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void getUserData(UserObject mUser) {
DatabaseReference mUserDb = FirebaseDatabase.getInstance().getReference().child("user").child(mUser.getUid());
mUserDb.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
UserObject mUser = new UserObject(dataSnapshot.getKey());
if(dataSnapshot.child("notificationKey").getValue() != null)
mUser.setNotificationKey(dataSnapshot.child("notificationKey").getValue().toString());
for(ChatObject mChat : chatList)
for(UserObject mUserIt : mChat.getUserObjectArrayList()){
if(mUserIt.getUid().equals(mUser.getUid())){
mUserIt.setNotificationKey(mUser.getNotificationKey());
}
}
mChatListAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void initializeRecyclerView() {
chatList = new ArrayList<>();
mChatList = findViewById(R.id.chatList);
mChatList.setNestedScrollingEnabled(false);
mChatList.setHasFixedSize(false);
mChatListLayoutManager = new LinearLayoutManager(getApplicationContext(), RecyclerView.VERTICAL,false);
mChatList.setLayoutManager(mChatListLayoutManager);
mChatListAdapter = new ChatListAdapter(chatList);
mChatList.setAdapter(mChatListAdapter);
}
From what I see you are passing to the adapter an empty list so that's why there's not data to be displayed.
And also, where you are using mChatListAdapter.notifyDataSetChanged();, you don't set before a populated list to the adapter.
I'm not sure which is the data you want to display but you have to collect in a list from the firebase all the information you want to display, for example, if UserObject is what you want, you have to make an ArrayList<UserObject>, to add there all the objects, pass it to the adapter and then call adapter.notifyDataSetChanged() .
You can make function in the adapter, like :
public void setData(ArrayList<YourObject> list){
yourAdapterList = list
this.notifyDataSetChanged()
}
And then call it from your activity/fragment like adapter.setData( /* your populated list */)
I want to get data of card on right swipe and on left i want to remove it from list but not to delete it from firebase how can i achieve it ? Here is my code
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
if(direction==ItemTouchHelper.RIGHT)
{
Toast.makeText(home.this, uplodinfo2.getBreed() +"/"+ uplodinfo2.getPrice()+"Right Swipe", Toast.LENGTH_SHORT).show();
}
if(direction==ItemTouchHelper.LEFT)
{
}
}
};
protected void onStart()
{ super.onStart();
FirebaseRecyclerAdapter<PetData,PetAdapter> firebaseRecyclerAdapter=new FirebaseRecyclerAdapter<PetData, PetAdapter>(PetData.class,R.layout.swipelayout,PetAdapter.class,query) {
#Override
protected void populateViewHolder(PetAdapter categoryviewHoder, final PetData uplodinfo, int i) {
uplodinfo2=uplodinfo;
categoryviewHoder.setDetail(getApplicationContext(),"Price: " + uplodinfo.getPrice() + "$","Breed Name: "+uplodinfo.getBreed(),uplodinfo.getImageUrl());
msg= uplodinfo.getName();
// txtcat.setText(Category);
categoryviewHoder.view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i = new Intent(home.this,rightview.class);
startActivity(i);
}
});
}
};
recyclerHome.setAdapter(firebaseRecyclerAdapter);
}
I have a problem with Recyclerview item click. I fetch data to list() method and add via addItem() method in recyclerview custom adapter when scroll down in addOnScrollListener. I get item position with click interface on Fragment. First fetch data work perfectly but when fetch loadmore, can't retrive item positon to new data with onButtonLClick() method.
// in onBindViewHolder;
holder.lnl.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try {
rcylviewItemNotify.onButtonLClick(position);
}catch (Throwable e){
//interface can be null
}
}
});
// addItem() method in adapter;
public void addItem(List<Image> img) {
for (Image im : img) {
arrayList.add(im);
}
notifyDataSetChanged();
}
// interface code;
public interface RcylviewItemNotify {
void onButtonLClick(int position);
}
// in Fragment code;
public void list() {
GetServices service = RetrofitInstance.getRetrofitInstance().create(GetServices.class);
Call<Images> call = service.getImages();
call.enqueue(new Callback<Images>() {
#Override
public void onResponse(Call<Images> call, Response<Images> response) {
Images body = response.body();
records = body.getImages();
adapter.addItem(records);
}
#Override
public void onFailure(Call<Images> call, Throwable t) {
Toast.makeText(getActivity(), "Network hatası onFailure", Toast.LENGTH_SHORT).show();
reflesh.setRefreshing(false);
}
});
}
#Override
public void onButtonLClick(int position) {
final String clickId = String.valueOf(records.get(position).getID());
Toast.makeText(getActivity(), "ID: " + clickId, Toast.LENGTH_SHORT).show();
}
// recycler settings;
public void loadView() {
layoutManager = new GridLayoutManager(getActivity(), 2);
recyclerView.setLayoutManager(layoutManager);
Collections.reverse(records);
adapter = new RecyclerViewAdapter(this,(ArrayList<Image>) records, getActivity());
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
reflesh.setRefreshing(false);
}
I'm not sure if this is your issue but you should be using the ViewHolder to get the position. Inside of your onBindViewHolder:
#Override
public void onClick(View view){
int itemPosition = holder.getAdapterPosition();
// Then do whatever you need to with the position
}
I am using Firebase Recycler Adapter in my app to display items from my database, but I want a situation whereby newer items will be at the top of the fragment so i tried to use orderByChild() like this:
Query conversationQuery = mConvDatabase.orderByChild("timestamp");
but that didn't solve the problem.
What i really want to do is to update chat fragment anytime a user receives a new message to move the new conversation to the top just like every other messenger app.
this is my ChatFragment
#Override
public void onStart() {
super.onStart();
Query conversationQuery = mConvDatabase.orderByChild("timestamp");
FirebaseRecyclerAdapter<Conv, ConvViewHolder> firebaseConvAdapter = new FirebaseRecyclerAdapter<Conv, ConvViewHolder>(
Conv.class,
R.layout.users_layout,
ConvViewHolder.class,
conversationQuery
) {
#Override
protected void populateViewHolder(final ConvViewHolder convViewHolder, final Conv conv, int i) {
final String list_user_id = getRef(i).getKey();
// Query MessageQuery = mMessageDatabase.child(list_user_id);
Query lastMessageQuery = mMessageDatabase.child(list_user_id).limitToLast(1);
lastMessageQuery.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String data = dataSnapshot.child("message").getValue().toString();
String type_image = dataSnapshot.child("type").getValue().toString();
boolean seen = Boolean.parseBoolean(dataSnapshot.getKey());
if (type_image.equals("image")){
convViewHolder.setMessage("image",conv.isSeen());
}else {
convViewHolder.setMessage(data, conv.isSeen());
}}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
mUsersDatabase.child(list_user_id).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final String userName = dataSnapshot.child("name").getValue().toString();
String userThumb = dataSnapshot.child("thumb_image").getValue().toString();
if(dataSnapshot.hasChild("online")) {
String userOnline = dataSnapshot.child("online").getValue().toString();
convViewHolder.setUserOnline(userOnline, getActivity());
}
mConvList.scrollToPosition(0);
convViewHolder.setName(userName);
convViewHolder.setUserImage(userThumb, getContext());
convViewHolder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent chatIntent = new Intent(getContext(), ChatActivity.class);
chatIntent.putExtra("user_id", list_user_id);
chatIntent.putExtra("user_name", userName);
startActivity(chatIntent);
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
};
mConvList.setAdapter(firebaseConvAdapter);
firebaseConvAdapter.notifyDataSetChanged();
}
Is there a way i can manipulate the time stamp sent to database so it can be later retrieved in an appropriate manner? or can i just change the arrangement locally in my code as data is been loaded?
You can do something like this.
//Display
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
linearLayoutManager.setStackFromEnd(true);
linearLayoutManager.setReverseLayout(true);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(firebaseRecyclerAdapter);
firebaseRecyclerAdapter.startListening();
firebaseRecyclerAdapter.notifyDataSetChanged();
I am new to firebase and I am in love with this real-time database thing.
However, I am not sure how to display nested data in a recycled view.
Following is my database structure :-
I want to display the data at 0,1,2...The data consist of information about different books.
This is what I have tried so far:-
private void getSelectedBooks() {
FirebaseRecyclerAdapter<BooksInfo, BooksViewHolder> adapter = new FirebaseRecyclerAdapter<BooksInfo, BooksViewHolder>(
BooksInfo.class,
R.layout.book_list,
BooksViewHolder.class,
nextOrder.child(String.valueOf(index))
) {
#Override
protected void populateViewHolder(BooksViewHolder viewHolder, final BooksInfo model, final int position) {
Log.d("BOOKDETAILS",model.getTitle());
// viewHolder.setTitle(model.getTitle());
// viewHolder.setISBN(model.getISBN());
// viewHolder.setDiscount(model.get());
// viewHolder.setQty(model.getQuantity());
// viewHolder.setPrice(model.getPrice());
// viewHolder.setGrossValue(model.get);
// viewHolder.setNetValue(model.get);
viewHolder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
DatabaseReference ref = getRef(position);
String key = ref.getKey();
Toast.makeText(AddOrderActivity.this, key, Toast.LENGTH_SHORT).show();
// getRetailerInfo(key);
}
});
}
};
selectedBookRecyclerView.setAdapter(adapter);
mDatabase.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.child(String.valueOf(index)).exists()) {
// run some code
// DisableProgress();
} else {
// DisableProgress();
// Toast.makeText(RetailerActivity.this, "No retailers to display", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Here DatabaseReference nextOrder = mDatabase.child(userId).child("order_details").child(String.valueOf(index));
Any help or suggestion is appreciated.Thank you.