RecyclerView updates view upon object deletion except for last object - java

In my app, I have an activity that displays an ArrayList of students in a RecyclerView. The students are stored in Firebase Realtime Database. The user can add or delete students. When a user deletes a student, the RecyclerView updates and shows the remaining students.
But I have a problem. For example, say I had 5 students. I want to delete all 5 so I delete them one by one. 4 of the students delete correctly and the RecyclerView updates and reflects this. It's only when I delete the last student the RecyclerView doesn't update and the student CardView stays there. I get a toast message confirming the student's deletion and I can see they have been deleted in the database. If I navigate back to the dashboard and go back into the student list, the CardView then disappears. Strange.
I've tried calling onStart again after each deletion but this hasn't helped. Any ideas?
My activity:
public class StudentListActivity extends AppCompatActivity {
//firebase auth
private FirebaseAuth mAuth;
//public variables
public String currentUserAccount;
public String teacherAccountNav = "Teacher";
public String currentUserId;
//recyclerView variables
DatabaseReference ref;
ArrayList<Student> list;
RecyclerView recyclerView;
SearchView searchView;
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_student_list);
//get current user
mAuth = FirebaseAuth.getInstance();
FirebaseUser user = mAuth.getCurrentUser();
//assert current user is not null and get current users id
assert user != null;
currentUserId = user.getUid();
//getting firebase reference of current users students
ref = FirebaseDatabase.getInstance().getReference().child("students").child(currentUserId);
//initialise views
recyclerView = findViewById(R.id.rv);
recyclerView.setHasFixedSize(true);
searchView = findViewById(R.id.searchView);
}
#Override
protected void onStart() {
super.onStart();
if(ref != null){
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
//add students from firebase to an array list
if(dataSnapshot.exists()){
list = new ArrayList<>();
for(DataSnapshot ds : dataSnapshot.getChildren()){
list.add(ds.getValue(Student.class));
}
//sort by name
Collections.sort(list, Student.myName);
//make recycler view
recyclerView.setLayoutManager(new LinearLayoutManager(StudentListActivity.this));
StudentAdapterClass studentAdapterClass = new StudentAdapterClass(list);
recyclerView.setAdapter(studentAdapterClass);
//click listeners for buttons
studentAdapterClass.setOnItemClickListener(new StudentAdapterClass.OnItemClickListener() {
#Override
public void onUpdateClick(int position) {
//handle update click in here
Student student = list.get(position);
//show update dialog here
showUpdateDialog(student.getStudentId(), student.getStudentName(), student.getStudentEmail());
}
#Override
public void onDeleteClick(int position){
//handle delete click in here
Student student = list.get(position);
//show delete dialog here
showDeleteDialog(student.getStudentId());
}
});
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Toast.makeText(StudentListActivity.this, databaseError.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}//if
}
//dialog box for deleting student
private void showDeleteDialog(final String studentId){
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
LayoutInflater inflater = getLayoutInflater();
//dialog XML
final View dialogView = inflater.inflate(R.layout.delete_student_dialog, null);
dialogBuilder.setView(dialogView);
final ImageButton buttonNo = dialogView.findViewById(R.id.imageButtonNo);
final ImageButton buttonYes = dialogView.findViewById(R.id.imageButtonYes);
final AlertDialog alertDialog = dialogBuilder.create();
alertDialog.show();
buttonNo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
alertDialog.dismiss();
}
});
buttonYes.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
deleteStudent(studentId);
alertDialog.dismiss();
}
});
}
//delete student method
private void deleteStudent(String id){
//get current user
mAuth = FirebaseAuth.getInstance();
FirebaseUser user = mAuth.getCurrentUser();
//assert current user is not null and get current users id
assert user != null;
currentUserId = user.getUid();
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference("students").child(currentUserId).child(id);
databaseReference.removeValue();
Toast.makeText(this, "Student Deleted", Toast.LENGTH_LONG).show();
//clear and get new list
onStart();
}
My adapter:
public class StudentAdapterClass extends RecyclerView.Adapter<StudentAdapterClass.MyViewHolder> {
private ArrayList<Student> list;
private OnItemClickListener mListener;
public interface OnItemClickListener{
//click method for update button
void onUpdateClick(int position);
//click method for delete button
void onDeleteClick(int position);
}
public void setOnItemClickListener(OnItemClickListener listener){
mListener = listener;
}
public StudentAdapterClass(ArrayList<Student> list){
this.list = list;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.card_holder, viewGroup, false);
return new MyViewHolder(view, mListener);
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder myViewHolder, int i) {
myViewHolder.name.setText(list.get(i).getStudentName());
myViewHolder.email.setText(list.get(i).getStudentEmail());
}
#Override
public int getItemCount() {
return list.size();
}
static class MyViewHolder extends RecyclerView.ViewHolder {
TextView name, email;
ImageButton update, delete;
MyViewHolder(#NonNull View itemView, final OnItemClickListener listener) {
super(itemView);
name = itemView.findViewById(R.id.studentName);
email = itemView.findViewById(R.id.studentEmail);
update = itemView.findViewById(R.id.updateButton);
delete = itemView.findViewById(R.id.deleteButton);
//update click listener
update.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null){
int position = getAdapterPosition();
if(position != RecyclerView.NO_POSITION){
listener.onUpdateClick(position);
}
}
}
});
delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null){
int position = getAdapterPosition();
if(position != RecyclerView.NO_POSITION){
listener.onDeleteClick(position);
}
}
}
});
}
}

Every time RecyclerView data changed, you have to call the adapter method notifyDataSetChanged(), otherwise, RecyclerView cannot refresh correctly.
You can call it inside deleteStudent() method like this.
recyclerView.getAdapter().notifyDataSetChanged()

Related

Repeated values using TextView in TableRow?

I'm facing a problem when I add values in firebase and tried to read and view these valued in tableRow using textView, but I get repeated values and when I navigate between fragments and come back to this fragment the repeated values disappear, how can I solve this problem? how can I view these values without the repeated values?
this is the code
public class RoomFragment extends Fragment {
TableLayout stk;
TableRow tbrow0;
Context thiscontext;
Button addNewRoom;
ArrayList<Rooms> rooms;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
thiscontext = container.getContext();
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_room,
container, false);
addNewRoom = (Button) rootView.findViewById(R.id.addNewRoom);
rooms = new ArrayList<Rooms>();
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Add Room
addNewRoom.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
updateDetail();
}
});
return rootView;
}
public void updateDetail() {
Intent intent = new Intent(getActivity(), add_room.class);
startActivity(intent);
}
#Override
//
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
addTable();
}
// This event is triggered soon after onCreateView().
// Any view setup should occur here. E.g., view lookups and attaching view listeners.
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// Setup any handles to view objects here
// EditText etFoo = (EditText) view.findViewById(R.id.etFoo);
stk=(TableLayout)view.findViewById(R.id.table_main);//table layout
}
public void addTable(){
// fetch reference database
DatabaseReference mDbRef = FirebaseDatabase.getInstance().getReference().child("Rooms");
// Read from the database
mDbRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()){
// Storing DB values in class
Rooms room = ds.getValue(Rooms.class);
rooms.add(room);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Display Room numbers
String RNum = ds.child("number").getValue().toString();
TableRow tbrow=new TableRow(thiscontext);// row
TextView t2v=new TextView(thiscontext);
t2v.setText(RNum);
t2v.setPadding(0,0,30,10);
t2v.setTextColor(Color.parseColor("#2171C1"));
t2v.setGravity(Gravity.LEFT);
t2v.setTextSize(20);
tbrow.addView(t2v);
TextView t3v=new TextView(thiscontext);
t3v.setText(" ");
t3v.setPadding(30,0,30,10);
tbrow.addView(t3v);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Modify a Room
ImageView editimg = new ImageView(thiscontext);
editimg.setBackgroundResource(R.drawable.ic_edit);
editimg.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//**Send Room number
Intent intent = new Intent(getActivity(), modify_room.class);
String No = ds.getKey();
intent.putExtra("No", No );
intent.putExtra("number", room.getNumber() );
intent.putExtra("employee", room.getEmployee());
intent.putExtra("status", room.getStatus());
// intent.putExtra("department", room.getDepartment());
// intent.putExtra("type", room.getType());
startActivity(intent);
}
});
tbrow.addView(editimg);
TextView t4v=new TextView(thiscontext);
t4v.setText(" ");
t4v.setPadding(30,0,30,10);
tbrow.addView(t4v);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Delete a Room
ImageView deleteimg = new ImageView(thiscontext);
deleteimg.setBackgroundResource(R.drawable.ic_delete);
deleteimg.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// DatabaseReference ref =ds.getRef();
// ref.removeValue();
ds.getRef().removeValue();
stk.removeView(tbrow);
Toast.makeText(getContext(),"The Room has been deleted successfully from the system",Toast.LENGTH_LONG).show();
}
});
tbrow.addView(deleteimg);
stk.addView(tbrow);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#Override
public void onCancelled(DatabaseError error) {
// Failed to read value
//Log.w(TAG, "Failed to read value.", error.toException());
}
});
}
}
You can just clear the list of rooms for each call of onDataChange() to guarantee non-duplicate values.
mDbRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
rooms.clear(); // Clearing the list
for (DataSnapshot ds : dataSnapshot.getChildren()){
// Storing DB values in class
Rooms room = ds.getValue(Rooms.class);
rooms.add(room);
// rest of code

Problem deleting entry from Recycler View

This issue is driving me crazy. I've tried 20 different things by now. So let's see if someone here can help me.
I am making a ToDo list APP using RecyclerView that stores the data in Firebase.
The app has a TasksActivity where all the tasks appear in the RecyclerView. I have a button that goes to a Task Creation dialogue. And I can create tasks, they appear then in the RecyclerView (Tasks Activity) and are updated in the firebase with no issue. I can close the APP and come back later and everything works well, all the entries appear in the app again when I load it. I can also swipe to delete an entry, and the entry also gets eliminated from the Database in firebase.
The problem, is when I create a TASK and without closing the app, I try to delete the task that I just created. It doesn't allow me to do that. When I create a new task, and instantly delete it before closing the app, it appears again. But If I then close the app, and load it again, that entry can be deleted normally, but if I am in the same session in which I created the entry there's no way I can delete it.
I was using some Log.d arguments to see how it changes. I think for various reasons that the problem is OnDataChange(). But so far I haven't been able to come to the root of the issue. This is the TaskActivity class, and after this I will paste the TasksCreation (I don't think pasting the Adapter is necessary).
public class TasksActivity extends AppCompatActivity {
DatabaseReference reference;
RecyclerView myTasks;
ArrayList<TaskItems> myTasksList;
TasksAdapter tasksAdapter;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tasks);
myTasks = findViewById(R.id.my_tasks); // RecyclerView that I defined as part of the layout. This is the id of it
myTasks.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));
myTasksList = new ArrayList<>();
Button openCreateTask = findViewById(R.id.openCreateTask);
tasksAdapter = new TasksAdapter(this,myTasksList);
myTasks.setAdapter(tasksAdapter);
new ItemTouchHelper(itemTouchHelper).attachToRecyclerView(myTasks);
openCreateTask.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent_task = new Intent(getApplicationContext(), TasksCreation.class);
startActivity(intent_task);
}
});
reference = FirebaseDatabase.getInstance().getReference().child("MotApp"); // Name of the App in the database .child("MotApp")
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) { // It gets the info from the database
Log.d("data Changed called", "onDataChange: is called");
Log.d("whatever", "onDataChange BEGIN Array of myTasksList size is "+myTasksList.size());
myTasksList.clear(); // Added later to avoid duplication
for(DataSnapshot elements: dataSnapshot.getChildren()){
TaskItems p = elements.getValue(TaskItems.class);
myTasksList.add(p);
}
tasksAdapter.notifyDataSetChanged(); // If this is put outside of onDataChange, it displays a blank list.
Log.d("whatever", "onDataChange END Array of myTasksList size is "+myTasksList.size());
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Toast.makeText(getApplicationContext(), "No data", Toast.LENGTH_SHORT).show();
}
});
}
ItemTouchHelper.SimpleCallback itemTouchHelper = new ItemTouchHelper.SimpleCallback(0,ItemTouchHelper.RIGHT) {
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
Log.d("ARRAY SIZE", "onSwiped BEGIN Array of myTasksList size is "+myTasksList.size());
String key = myTasksList.get(position).getKey();
reference= FirebaseDatabase.getInstance().getReference().child("MotApp").child(key);
Toast.makeText(getApplicationContext(),"This is key "+key,Toast.LENGTH_LONG).show();
reference.removeValue();
myTasksList.remove(position);
tasksAdapter.notifyItemRemoved(position);
Log.d("ARRAY SIZE", "onSwiped END Array of myTasksList size is "+myTasksList.size());
}
};
}
And this is for TasksCreation Activity:
public class TasksCreation extends AppCompatActivity {
DatabaseReference referenceCreation;
ArrayList<String> list;
EditText taskName;
EditText taskDescr;
Button selectDates;
TextView taskDate;
Button createTask;
Button cancel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tasks_creation);
taskName = findViewById(R.id.et_TaskName);
taskDescr = findViewById(R.id.et_TaskDescr);
selectDates = findViewById(R.id.selectDates);
taskDate= findViewById(R.id.tv_Dates);
createTask = findViewById(R.id.createTask);
cancel = findViewById(R.id.cancelButton);
createTask.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!TextUtils.isEmpty(taskName.getText()) && !TextUtils.isEmpty(taskDate.getText())) {
referenceCreation = FirebaseDatabase.getInstance().getReference().child("MotApp").push(); //saves it with custom key created by Firebase
final String key = referenceCreation.getKey();
referenceCreation.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
dataSnapshot.getRef().child("key").setValue(key);
dataSnapshot.getRef().child("taskTitle").setValue(taskName.getText().toString());
dataSnapshot.getRef().child("taskDescription").setValue(taskDescr.getText().toString());
dataSnapshot.getRef().child("taskDate").setValue(taskDate.getText().toString());
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
Intent intent = new Intent(getApplicationContext(), TasksActivity.class);
startActivity(intent);
} else{
Toast.makeText(getApplicationContext(), "You haven't filled all the fields", Toast.LENGTH_SHORT).show();
}
}
});
cancel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), TasksActivity.class);
startActivity(intent);
}
});
selectDates.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
createAlertDialogue();
}
});
}
private void createAlertDialogue(){
list = new ArrayList<String>();
AlertDialog.Builder builder = new AlertDialog.Builder(this,R.style.MyDialogTheme);
builder.setTitle("Select days");
builder.setMultiChoiceItems(R.array.Days, null, new DialogInterface.OnMultiChoiceClickListener() {
#Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
String arr[] = getResources().getStringArray(R.array.Days);
if(isChecked){
list.add(arr[which]);
}else if(list.contains(arr[which])){
list.remove(arr[which]);
}
}
});
builder.setPositiveButton("Save", new DialogInterface.OnClickListener() {
String data = "";
#Override
public void onClick(DialogInterface dialog, int which) {
for(String elements: list){
elements = elements.substring(0,3);
data= elements+" "+data;
}
taskDate.setText(data);
}
});
builder.create();
builder.show();
}
}
I really appreciate any light to the issue.
Thanks a lot
P:S: This is the code for the TaskAdapter (Ignore the dayoftheweek part is just for a part of the code that I commented )
public class TasksAdapter extends RecyclerView.Adapter<TasksAdapter.MyViewHolder> { // V 1.3 added OnClickListener
Context context;
ArrayList<TaskItems> tasks;
DatabaseReference reference;
SimpleDateFormat sdf = new SimpleDateFormat("EEEE");
Date d = new Date();
final String dayOfTheWeek = sdf.format(d).substring(0,3);
public TasksAdapter(Context context, ArrayList<TaskItems> tasks) {
this.context = context;
this.tasks = tasks;
}
public class MyViewHolder extends RecyclerView.ViewHolder { // V1.3 Added implements View.OnClickListener
TextView taskTitle;
TextView taskDate;
TextView taskDescription;
CheckBox taskCheckBox;
ConstraintLayout constraintLayout; // Added to change background of each RecyclerView item.
public MyViewHolder(#NonNull View itemView) {
super(itemView);
taskTitle = itemView.findViewById(R.id.taskTitle);
taskDate = itemView.findViewById(R.id.taskDate);
taskDescription = itemView.findViewById(R.id.taskDescription);
taskCheckBox = itemView.findViewById(R.id.taskCheckBox);
constraintLayout = (ConstraintLayout) itemView.findViewById(R.id.item_task_layout);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int pos = getAdapterPosition();
// if(pos != RecyclerView.NO_POSITION){//Checks if item still exists
TaskItems clickedDataItem = tasks.get(pos);
Toast.makeText(v.getContext(), "You clicked " + clickedDataItem.getTaskTitle(), Toast.LENGTH_SHORT).show();
// }
}
});
}
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) { // standard code for onCreateViewHolder
return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_task,parent,false));
}
#Override
public void onBindViewHolder(final #NonNull MyViewHolder holder, final int position) { // This method is called once for each item on the list.
holder.taskTitle.setText(tasks.get(position).getTaskTitle());
holder.taskDescription.setText(tasks.get(position).getTaskDescription());
holder.taskDate.setText(tasks.get(position).getTaskDate());
holder.taskCheckBox.setChecked(tasks.get(position).isChecked());
holder.taskCheckBox.setTag(tasks.get(position).getKey());
holder.taskCheckBox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FirebaseDatabase database = FirebaseDatabase.getInstance();
String post = (String) holder.taskCheckBox.getTag();
Toast.makeText(context,"This is checkbox number: "+post,Toast.LENGTH_SHORT).show(); // Working Well. //change for new logic
reference = database.getReference("MotApp").child(post);
boolean checkboxStatus = holder.taskCheckBox.isChecked();
Log.d("Checked", "onClick: The taskcheckbox checked is "+holder.taskCheckBox.isChecked());
TaskItems value = new TaskItems(tasks.get(position).getKey(),tasks.get(position).getTaskTitle(), tasks.get(position).getTaskDate(),tasks.get(position).getTaskDescription(),checkboxStatus);
reference.setValue(value);
Toast.makeText(context,"This is a checkbox belonging to item "+tasks.get(position).getTaskTitle(),Toast.LENGTH_LONG).show();
}
});
}
#Override
public int getItemCount() { //tells the adapter the size . if it's zero then it won't create anything
return tasks.size();
}
}
You need to rebuild the RecyclerView data in your TasksActivity when you return back from the TasksCreation; that is because the onCreate() callback of the TasksActivity is called only when you open your app; and that is why the deletion works only when you close your app and reopen it.
And onCreate() is not called when you back from the the TasksCreate, because when you transfer from TasksActivity to TasksCreation, the TasksActivity is not destroyed, but just stopped, and therefore when you come back to TasksActivity it will start, and resumed; so transfer your code on the onCreate() to onResume() in order to allow the list to be updated with the recent changes.
So change your TasksActivity to the below
public class TasksActivity extends AppCompatActivity {
DatabaseReference reference;
RecyclerView myTasks;
ArrayList<TaskItems> myTasksList;
TasksAdapter tasksAdapter;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tasks);
}
#Override
protected void onResume() {
super.onResume();
myTasks = findViewById(R.id.my_tasks); // RecyclerView that I defined as part of the layout. This is the id of it
myTasks.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));
myTasksList = new ArrayList<>();
Button openCreateTask = findViewById(R.id.openCreateTask);
tasksAdapter = new TasksAdapter(this,myTasksList);
myTasks.setAdapter(tasksAdapter);
new ItemTouchHelper(itemTouchHelper).attachToRecyclerView(myTasks);
openCreateTask.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent_task = new Intent(getApplicationContext(), TasksCreation.class);
startActivity(intent_task);
}
});
reference = FirebaseDatabase.getInstance().getReference().child("MotApp"); // Name of the App in the database .child("MotApp")
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) { // It gets the info from the database
Log.d("data Changed called", "onDataChange: is called");
Log.d("whatever", "onDataChange BEGIN Array of myTasksList size is "+myTasksList.size());
myTasksList.clear(); // Added later to avoid duplication
for(DataSnapshot elements: dataSnapshot.getChildren()){
TaskItems p = elements.getValue(TaskItems.class);
myTasksList.add(p);
}
tasksAdapter.notifyDataSetChanged(); // If this is put outside of onDataChange, it displays a blank list.
Log.d("whatever", "onDataChange END Array of myTasksList size is "+myTasksList.size());
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Toast.makeText(getApplicationContext(), "No data", Toast.LENGTH_SHORT).show();
}
});
}
ItemTouchHelper.SimpleCallback itemTouchHelper = new ItemTouchHelper.SimpleCallback(0,ItemTouchHelper.RIGHT) {
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
Log.d("ARRAY SIZE", "onSwiped BEGIN Array of myTasksList size is "+myTasksList.size());
String key = myTasksList.get(position).getKey();
reference= FirebaseDatabase.getInstance().getReference().child("MotApp").child(key);
Toast.makeText(getApplicationContext(),"This is key "+key,Toast.LENGTH_LONG).show();
reference.removeValue();
myTasksList.remove(position);
tasksAdapter.notifyItemRemoved(position);
Log.d("ARRAY SIZE", "onSwiped END Array of myTasksList size is "+myTasksList.size());
}
};
}
I finally found how to solve this.
First I changed all addValueEventListener with addListenerforSingleValueEvent in TaskActivity and TaskCreation. With addValueEventListener I realized that it was making several loops everytime a new task was created which caused all kind of problems.
And I also changed the way the startIntent was set up in TaskCreation. I had set it up initially after the Listener block. But the asynchronous nature of onDataChange was making that everytime I loaded the intent the updated information wasn't set up with the new task that was just added.
By putting it inside the onDataChange it was solved. I have made several tests and now it's working flawlessly.
I wasted many days trying to solve this. But I learned a lot of lessons in the process. So I guess they weren't wasted days :) .
Thanks to all who chipped in to help me with this issue.

How to remove values from Firebase when item is selected in recyclerview?

My app is displaying user's classes in a recylerview. When a user selects a specific class, it opens up another activity that displays the details and there is a button where a user can remove the class. When that button is clicked, i want the id of the class that is saved under the user's list of classes to be removed. I also want the user's uid to be removed from the list of students under the class.Also, I accidentally saved my class id as variable name uid, which shouldn't be confused with the user's uid.Let me know if you have any questions!
Here is my database structure:
{
"Classes" : {
"-LiGRe3e1YF_HFrHKMgi" : {
"Students" : {
"-LiId4URjJ8SWLAP4f4i" : "UhTQgJ4jugMqFtEZK8CsIkTqQl22",
"-LiIeT5t1T7JpcUee_MJ" : "UhTQgJ4jugMqFtEZK8CsIkTqQl22"
},
"class_info" : {
"date_clasname" : "mm",
"room_number" : "jjhh",
"subject" : "Science",
"teacher" : "mm",
"uid" : "-LiGRe3e1YF_HFrHKMgi"
}
}
},
"Users" : {
"UhTQgJ4jugMqFtEZK8CsIkTqQl22" : {
"Classes" : {
"-LiIeT5slTjC0B3lEqNJ" : "-LiGRe3e1YF_HFrHKMgi"
"-LiIeT5slTjC0B3lEqNJ" : "-LiGRe3e1YF_HFrHKMgi"
},
"User_info" : {
"email" : "samuelford48#gmail.com",
"grade" : "12",
"name" : "Samuel"
}
}
}
}
Here is the code of my activity that shows the classes and gets the data from Firebase:
public class home_fragment extends Fragment implements View.OnClickListener {
private Button button;
//DatabaseReference dref;
//ListView listview2;
//ArrayList<String> list=new ArrayList<>();
private FirebaseDatabase database;
private DatabaseReference myRef;
private List<Listdata> list;
private RecyclerView recyclerview;
public home_fragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.home_fragment, container, false);
//FirebaseUser fbUser = FirebaseAuth.getInstance().getCurrentUser();
//if(fbUser == null) { Intent intent = new Intent(getContext(), LoginActivity.class);
// startActivity(intent);}
button = (Button) view.findViewById(R.id.button);
button.setOnClickListener(this);
recyclerview = (RecyclerView) view.findViewById(R.id.rview);
database = FirebaseDatabase.getInstance();
myRef = database.getReference("Users")
.child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child("Classes");
myRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, String s) {
list = new ArrayList<>();
final adapter_user_remove_class recycler = new adapter_user_remove_class(list);
// final RecyclerviewAdapter2 recycler = new RecyclerviewAdapter2(list);
//String postkey2 = dataSnapshot.getKey();
String class_id = dataSnapshot.getValue(String.class);
myRef = database.getReference("Classes").child(class_id).child("class_info");
// StringBuffer stringbuffer = new StringBuffer();
myRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot)
{
Class_model new_class = dataSnapshot.getValue(Class_model.class);
assert new_class != null;
String nameofclass = new_class.getDate_clasname();
String teacherofclass = new_class.getTeacher();
String roomnumberofclass = new_class.getRoom_number();
String class_key = new_class.getUid();
Listdata listdata = new Listdata(nameofclass, teacherofclass, roomnumberofclass, class_key);
//String name = userdetails.getName();
//String email = userdetails.getEmail();
//String address = userdetails.getAddress();
listdata.setDate_class(nameofclass);
listdata.setTeacher(teacherofclass);
listdata.setRnumber(roomnumberofclass);
// recycler.notifyDataSetChanged();
list.add(listdata);
recycler.notifyDataSetChanged();
//recycler.notifyDataSetChanged();// Toast.makeText(MainActivity.this,""+name,Toast.LENGTH_LONG).show();
}
#Override
public void onCancelled(DatabaseError error) {
AlertDialog alertDialog = new AlertDialog.Builder(getContext()).create();
alertDialog.setTitle("Error");
alertDialog.setMessage("Check your connection! If, problem persists please email svhsdev#vigoschools.org!");
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
alertDialog.show();
}
});
RecyclerView.LayoutManager layoutmanager = new LinearLayoutManager(getContext());
recyclerview.setLayoutManager(layoutmanager);
recyclerview.setItemAnimator(new DefaultItemAnimator());
recyclerview.setAdapter(recycler);
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onCancelled(DatabaseError error) {
AlertDialog alertDialog = new AlertDialog.Builder(getContext()).create();
alertDialog.setTitle("Error");
alertDialog.setMessage("Check your connection! If, problem persists please email svhsdev#vigoschools.org!");
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
alertDialog.show();
}
});
return view;
}
Here is the code for my recycler adapter:
public class adapter_user_remove_class extends RecyclerView.Adapter<adapter_user_remove_class.MyHolder>{
List<Listdata> listdata;
public adapter_user_remove_class(List<Listdata> listdata) {
this.listdata = listdata;
}
#Override
public adapter_user_remove_class.MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cardview_class_model,parent,false);
adapter_user_remove_class.MyHolder myHolder = new adapter_user_remove_class.MyHolder(view);
return myHolder;
}
public void onBindViewHolder(adapter_user_remove_class.MyHolder holder, final int position) {
final Listdata data = listdata.get(position);
holder.vdate_class.setText(data.getDate_class());
holder.vteacher.setText(data.getTeacher());
holder.vrnumber.setText(data.getRnumber());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick( final View view) {
Context context = view.getContext();
Intent intent = new Intent(context, user_remove_class.class);
intent.putExtra("date_class", listdata.get(position).getDate_class());
intent.putExtra("teacher", listdata.get(position).getTeacher());
intent.putExtra("room_number", listdata.get(position).getRnumber());
intent.putExtra("post_key", listdata.get(position).getUid());
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return listdata.size();
}
class MyHolder extends RecyclerView.ViewHolder{
TextView vdate_class , vteacher,vrnumber;
public MyHolder(View itemView) {
super(itemView);
vdate_class = (TextView) itemView.findViewById(R.id.date_class_name);
vteacher = (TextView) itemView.findViewById(R.id.teacher);
vrnumber = (TextView) itemView.findViewById(R.id.room_number);
}
}
}
Here is the code for the activity that shows the details and handles the removal of data from Firebase:
public class user_remove_class extends AppCompatActivity {
private Button remove_class;
private FirebaseDatabase database;
private DatabaseReference myRef, myRef2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_remove_class);
final String date_class2 = getIntent().getStringExtra("date_class");
final String teacher = getIntent().getStringExtra("teacher");
final String room_number = getIntent().getStringExtra("room_number");
final String post_key = getIntent().getStringExtra("post_key");
TextView display_class_name = (TextView) findViewById(R.id.date_tv);
display_class_name.setText(date_class2);
TextView display_teacher = (TextView) findViewById(R.id.teacher_tv);
display_teacher.setText(teacher);
TextView display_room_number = (TextView) findViewById(R.id.rn_tv);
display_room_number.setText(room_number);
database = FirebaseDatabase.getInstance();
myRef = database.getReference("Users").child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child("Classes");
remove_class = (Button) findViewById(R.id.add_class_2);
// final Query query = myRef.orderByChild("classes").equalTo(post_key);
//System.out.println(query);
remove_class.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
myRef = database.getReference("Users").child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child("Classes");
}
});
}
}
You can remove it by :
remove_class.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
myRef = database.getReference("Users").child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child("Classes").child(post_key);
myRef.removeValue();
}
});
You can Retrieve the Random key by this method .
MyRef.orderByChild("email").equalTo("blabla#blabla.com").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child: dataSnapshot.getChildren()) {
Log.d("User key", child.getKey());
Log.d("User ref", child.getRef().toString());
Log.d("User val", child.getValue().toString());
}
}
Note that you have to pass the email by putExtra method and then match it with equalTo(); method . If email is exits in the random key it will retrive the ramdom key simple . :)
Hope it will work. If it works don't forget to Accept the question.

How to implement OnClick item using firebase search query result item

I've created a firebase search query and it works perfectly.But now I can't seem to figure out how to do the onlick and get item position. Below is my firebase search activity:
mUserDatabase=FirebaseDatabase.getInstance().getReference("Users").child("UserProfile";
mSearchField = findViewById(R.id.search_field);
mSearchBtn = findViewById(R.id.search_btn);
mResultList = findViewById(R.id.result_list);
mResultList.setHasFixedSize(true);
mResultList.setLayoutManager(new LinearLayoutManager(this));
mSearchBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String searchText = mSearchField.getText().toString();
firebaseUserSearch(searchText);
}
});
}
private void firebaseUserSearch(String searchText) {
Toast.makeText(SearchActivity.this, "Searching......", Toast.LENGTH_LONG).show();
Query firebaseSearchQuery = mUserDatabase.orderByChild("profession").startAt(searchText).endAt(searchText + "\uf8ff");
FirebaseRecyclerAdapter<Users, UsersViewHolder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Users, UsersViewHolder>(
Users.class,
R.layout.search_list_layout,
UsersViewHolder.class,
firebaseSearchQuery
) {
#Override
protected void populateViewHolder(UsersViewHolder viewHolder, Users model, int position) {
viewHolder.setDetails(getApplicationContext(), model.getUsername(), model.getSummary(), model.getImageurl());
}
};
mResultList.setAdapter(firebaseRecyclerAdapter);
}
public static class UsersViewHolder extends RecyclerView.ViewHolder {
View mView;
private TextView user_name;
private TextView user_summary;
private ImageView user_image;
public UsersViewHolder(View itemView) {
super(itemView);
mView = itemView;
}
public void setDetails(Context ctx, String userName,String userSummary,String userImage){
user_name = mView.findViewById(R.id.name_text);
user_summary = mView.findViewById(R.id.summary_text);
user_image = mView.findViewById(R.id.profile_image);
user_name.setText(userName);
user_summary.setText(userSummary);
Glide.with(ctx).load(userImage).into(user_image);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int pos = getAdapterPosition();
Snackbar.make(v, "Hello,How may i help you", Snackbar.LENGTH_LONG).setAction("Action", null).show();
}
});
}
How do i go about displaying the content in a details activity when a single item from the search result is clicked.
Based on Parag Pawar's suggestion, I tried the following, but it's not working>
#Override
protected void populateViewHolder(UsersViewHolder viewHolder, Users model, int position) {
viewHolder.setDetails(getApplicationContext(), model.getUsername(), model.getSummary(), model.getImageurl());
final String each_key = getRef(position).getKey();
}
};
Then in my UserViewHolder i initialized the onclick:
mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent pDetail = new Intent(SearchActivity.this, ProfileDetail.class);
Users each_key = each_key.get(getAdapterPosition());
pDetail.putExtra(ProfileDetail.EXTRA_POSITION, each_key.getUserId());
startActivity(pDetail);
}
});
But I am getting the error variable each_key might not have been initialized
Fixed my mistake according top advice from Parag Pawar and implemented he onclick inside populateView`holder as follows:
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
/*Here use intent.putExtra() to send each_key on other activity and fetch data there*/
Toast.makeText(SearchActivity.this, "u clicked......" +position, Toast.LENGTH_LONG).show();
Intent each_key = new Intent(SearchActivity.this, ProfileDetail.class);
Users user = mResultList.get(position);
each_key.putExtra(ProfileDetail.EXTRA_POSITION, user.getUserId());
startActivity(each_key);
} });
but I am getting the error here:
Users user = mResultList.get(position);
Cannot resolve method 'get(int)'
mReultList is the name of my Recyclerview and I initialized it as follows:
private RecyclerView mResultList;
but I don't know if I'm doing this correctly. I know I am supposed to attach the results to an adapter, but I don't know how to do that. So I tried:
listItems = new ArrayList<>();
firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<UsersList>(listItems,this);
But this doesn't seem to work either
Updated Code
I created an array:
private List<UsersList> listItems;//UsersList is my modal class
Then added it to my onCreate:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
listItems = new ArrayList<>();
I then Implemented putExtra as follows using this array as follows:
Intent each_key = new Intent(TestSearch.this, ProfileDetail.class);
UsersList user = listItems.get(position);
//Here use intent.putExtra() to send each_key on other activity and fetch data there
Toast.makeText(TestSearch.this, "u clicked......" +position, Toast.LENGTH_LONG).show();
each_key.putExtra(ProfileDetail.EXTRA_POSITION, user.getUserId());
startActivity(each_key);
But getting this error when i click an item:
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.get(ArrayList.java:411)
at com.ecard.ecard.search.TestSearch$2$1.onClick(TestSearch.java:94)
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22429)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
To get key from each item, in populateViewholder do
Example :-
final String each_key = getRef(position).getKey();
To set Click Listeners on each item, in populateViewholder do
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
/*Here use intent.putExtra() to send each_key on other activity and fetch data there*/
} }); }
In the activity that contains your current
items that you want to click on:
Create a String to reference from in your populateVieHolder:
final String single_view = getRef(position).getKey();
Then attach an onclick to your ViewHolder:
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
And in your OnClick place your intents and putExtras:
#Override
public void onClick(View view) {
//give your intent a name to reference from("moreDetail" in this case)
Intent moreDetail = new Intent(SearchActivity.this,MoreDetail.class);
//attach the intent reference to access each item and key
moreDetail.putExtra(MoreDetail.EXTRA_POSITION,single_view);
startActivity(moreDetail);//Start the new activity
Then in your more details activity:
Add the ExtraPosition String
String single_view = getIntent().getExtras().getString(MoreDetail.EXTRA_POSITION);//get extra position reference previous view
From here on,you need to display your data from firebase
For accessing Single Values from Firebase
firebaseRef.child(single_view).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//For single items
// for example:String user_name = (String) dataSnapshot.child("username").getValue();
Or
For accessing Values attached to your adapter using a Listview
firebaseRef.child(single_view).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
ListItem data = dataSnapshot.getValue(ListItem.class);
listItems.add(data);
adapter.notifyDataSetChanged();
//if you are accessing your data from a ListView
There are so many ways to do this.Another way to accomplish this is to create an interface.declare an onclick listener and contex in your recycler adapter:
OnItemClickListener callback;
private Context mContext;
Then implement an Onclick listener to your view holder
public static class UsersViewHolder extends RecyclerView.ViewHolder implements OnItemClickListener {
add an onclick to access a position,to your View Holder using callback:
#Override
public void onClick(View v) {
callback.onItemClick(getAdapterPosition());
}
Then create an interface:
public interface OnItemClickListener {
void onItemClick(int pos);
}
Based on your context:
In your more main activity:
#Override
public void onItemClick(int pos) {
Intent pDetail = new Intent(this.getContext(), ProfileDetail.class);//get the context
ListItem listItem = listItems.get(pos);//get the position
//Then your puEtxra here followed by start activity
startActivity(pDetail);
}
And finally call EXTRA_POSITION in your detail view followed by your datasnapshots.
You can find more details from the firebase documentation and firebase quick start samples on github:
GitHub
FirebaseDocs
I said both the things go in populateViewHolder()
You are getting not initialized error because you're using each_key in the Viewholder class.
Copy this
final String each_key = getRef(position).getKey();
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
/*Here use intent.putExtra() to send each_key on other activity and fetch data there*/
} }); }
and paste it in the populateViewHolder method

Firebase and Android - Chat Message and OnClickListener

I learn Firebase and I found this project: https://github.com/firebase/FirebaseUI-Android/tree/0.3.1/codelabs/chat
I'm trying to expand this project for setOnClickListener method.
I would like to click on every item of this chat message(chat listview) and see
this in another activity.
For example: I have this chat in: ChatMessageActivity. And I would like show this in ChatMessageDetailsActivity.
Like this:
private Firebase firebaseRef;
private static final String FIREBASE_URL = "https://my-project.firebaseio.com/chat";
FirebaseListAdapter listAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat_message);
Firebase.setAndroidContext(this);
firebaseRef = new Firebase(FIREBASE_URL);
final EditText mText = (EditText) findViewById(R.id.messageText);
Button sendButton = (Button) findViewById(R.id.sendButton);
sendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String text = mText.getText().toString();
ChatMessage message = new ChatMessage("Android User", text);
firebaseRef.push().setValue(message);
mText.setText("");
}
});
final ListView listView = (ListView) this.findViewById(R.id.list);
listAdapter = new FirebaseListAdapter<ChatMessage>(this, ChatMessage.class,
android.R.layout.two_line_list_item, firebaseRef) {
#Override
protected void populateView(View v, ChatMessage model, int position) {
((TextView)v.findViewById(android.R.id.text1)).setText(model.getName());
((TextView)v.findViewById(android.R.id.text2)).setText(model.getText());
}
};
listView.setAdapter(listAdapter);
firebaseRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
}
public void onChildChanged(DataSnapshot dataSnapshot, String s) { }
public void onChildRemoved(DataSnapshot dataSnapshot) { }
public void onChildMoved(DataSnapshot dataSnapshot, String s) { }
public void onCancelled(FirebaseError firebaseError) { }
});
}
#Override
public View getView(View convertView, ViewGroup parent) {
View row = convertView;
ViewHolder holder = null;
if ( row == null || (row.getTag() == null)) {
holder = new ViewHolder();
holder.chatName = (TextView) row.findViewById(R.id.detsName);
holder.chatText = (TextView) row.findViewById(R.id.detsText);
row.setTag(holder);
}else {
holder = (ViewHolder) row.getTag();
}
holder.chatName.setText(holder.chatMessage.getName());
holder.chatText.setText(holder.chatMessage.getText());
final ViewHolder finalHolder = holder;
row.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(ChatMessage.this, ChatMessageDetails.class);
startActivity(i);
}
});
return row;
}
public class ViewHolder {
ChatMessage chatMessage;
TextView chatName;
TextView chatText;
}
#Override
protected void onDestroy() {
super.onDestroy();
listAdapter.cleanup();
}
}
But getView(at the bottom) method doesn't work. I had this in my old project without Firebase and internet connection. It worked. But now I'm trying to implement this in Chat Project but without positive results.
Is in Firebase some inside method to do this?
Does somebody has some preposition how to develop this listView with SetOnClickListener method with Firebase in AndroidStudio?

Categories

Resources