I am creating a notes app with java, i added room database to my app and when user saves a notes it adds it to database but doesn't shows in recyclerView immediately, when i reloads the app then it shows up, how and where should i insert notifyiteminserted so that recyclerView changes immediately
I have tried onResume Method but that results in app crash. This is my MainActivity.
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
static ArrayList<Notes> arrNotes;
@SuppressLint("StaticFieldLeak")
RecyclerViewAdapter adapter;
RecyclerView.LayoutManager layoutManager;
notesModelView modelView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DatabaseHelper dbHelper = DatabaseHelper.getDatabase(this);
arrNotes = (ArrayList<Notes>) dbHelper.notesDao().getAllNotes();
RecyclerView recyclerView = findViewById(R.id.recycler_view);
adapter = new RecyclerViewAdapter(this, arrNotes);
recyclerView.setAdapter(adapter);
//setting up recycler view
layoutManager = new StaggeredGridLayoutManager(2, LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
//Setting custom Toolbar
Toolbar toolbar1 = findViewById(R.id.toolbar);
setSupportActionBar(toolbar1);
//Moving from MainActivity to add_Notes Activity
Button button = findViewById(R.id.floatingActionButton);
button.setOnClickListener(view -> {
Intent intent = new Intent(MainActivity.this, addActivity.class);
startActivity(intent);
});
}
}
This is my add_Activity.
package com.example.keepnotes;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import java.util.Objects;
public class addActivity extends AppCompatActivity {
MainActivity mainActivity;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add);
Toolbar toolbar_add = findViewById(R.id.toolbar_add_activity);
setSupportActionBar(toolbar_add);
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
toolbar_add.setNavigationIcon(R.drawable.back_button);
toolbar_add.setNavigationOnClickListener(view -> onBackPressed());
EditText titleText = findViewById(R.id.add_activity_title);
EditText bodyText = findViewById(R.id.add_activity_text);
Button saveBtn = findViewById(R.id.button);
DatabaseHelper database = DatabaseHelper.getDatabase(this);
saveBtn.setOnClickListener(view -> {
String titleBody = titleText.getText().toString();
String textBody = bodyText.getText().toString();
if (titleBody.equals("") && textBody.equals("")) {
Toast.makeText(addActivity.this, "Fields can't be empty",
Toast.LENGTH_LONG).show();
} else {
database.notesDao().addNotes(new Notes(titleBody, textBody));
finish();
}
});
}
}
How can i notify adapter the changes on each item add in database.
Here is my MainActivity after update to liveData.
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
static ArrayList<Notes> arrNotes;
@SuppressLint("StaticFieldLeak")
RecyclerViewAdapter adapter;
RecyclerView.LayoutManager layoutManager;
notesModelView modelView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DatabaseHelper dbHelper = DatabaseHelper.getDatabase(this);
modelView = new ViewModelProvider(this).get(notesModelView.class);
modelView.getAllNotes().observe(this, new Observer<List<Notes>>() {
@Override
public void onChanged(List<Notes> notes) {
arrNotes = (ArrayList<Notes>) notes;
}
});
arrNotes = (ArrayList<Notes>) dbHelper.notesDao().getAllNotes();
RecyclerView recyclerView = findViewById(R.id.recycler_view);
adapter = new RecyclerViewAdapter(this, arrNotes);
recyclerView.setAdapter(adapter);
//setting up recycler view
layoutManager = new StaggeredGridLayoutManager(2, LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
//Setting custom Toolbar
Toolbar toolbar1 = findViewById(R.id.toolbar);
setSupportActionBar(toolbar1);
//Moving from MainActivity to add_Notes Activity
Button button = findViewById(R.id.floatingActionButton);
button.setOnClickListener(view -> {
Intent intent = new Intent(MainActivity.this, addActivity.class);
startActivity(intent);
});
}
}
this is Dao.
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import java.util.List;
@Dao
public interface NotesDao {
@Query("SELECT * FROM notesTable")
List<Notes> getAllNotes();
@Query("SELECT * FROM notesTable")
LiveData<List<Notes>> findAllNotes();
@Insert
void addNotes(Notes note);
@Update
void updateNotes(Notes note);
@Delete
void deleteNotes(Notes note);
}
And here is my ViewModel
package com.example.keepnotes;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import java.util.List;
public class notesModelView extends AndroidViewModel {
DatabaseHelper databaseHelper;
public notesModelView(@NonNull Application application) {
super(application);
databaseHelper = DatabaseHelper.getDatabase(application.getApplicationContext());
}
public LiveData<List<Notes>> getAllNotes() {
return databaseHelper.notesDao().findAllNotes();
}
}
Here is my RecyclerView adapter
package com.example.keepnotes;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
Context context;
ArrayList<Notes> arrNotes;
DatabaseHelper databaseHelper;
RecyclerViewAdapter(Context context, ArrayList<Notes> arrNotes, DatabaseHelper databaseHelper) {
this.context = context;
this.arrNotes = arrNotes;
this.databaseHelper = databaseHelper;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.single_view, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, @SuppressLint("RecyclerView") int position) {
holder.title.setText(arrNotes.get(position).title);
holder.body.setText(arrNotes.get(position).text);
holder.index.setText(String.valueOf(position 1));
holder.llView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
AlertDialog.Builder alert = new AlertDialog.Builder(context)
.setTitle("Delete view")
.setMessage("Are you sure to delete")
.setIcon(R.drawable.ic_baseline_delete_24)
.setPositiveButton("yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
databaseHelper.notesDao().deleteNotes(new Notes(arrNotes.get(position).id,arrNotes.get(position).title,arrNotes.get(position).text));
notifyItemRemoved(position);
notifyItemRangeChanged(position, arrNotes.size());
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
});
alert.show();
return true;
}
});
}
@Override
public int getItemCount() {
return arrNotes.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView title, body, index;
CardView llView;
public ViewHolder(View itemView) {
super(itemView);
title = itemView.findViewById(R.id.text_title_view);
body = itemView.findViewById(R.id.text_text_view);
index = itemView.findViewById(R.id.index);
llView = itemView.findViewById(R.id.card_View);
databaseHelper = DatabaseHelper.getDatabase(context);
}
}
}
It deletes the selected notes but also crashes immediately after confirming delete.
and it throws following error
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{535e3b3 position=5 id=-1, oldPos=4, pLpos:4 scrap [attachedScrap] tmpDetached not recyclable(1) no parent} androidx.recyclerview.widget.RecyclerView{11f4816 VFED..... ......ID 31,171-689,1048 #7f090165 app:id/recycler_view}, adapter:com.example.keepnotes.RecyclerViewAdapter@fd652a0, layout:androidx.recyclerview.widget.StaggeredGridLayoutManager@32e1e59, context:com.example.keepnotes.MainActivity@286bccd
CodePudding user response:
Remove this two line
notifyItemRemoved(position);
notifyItemRangeChanged(position, arrNotes.size());
OLD ANSWER
First, if you use liveData you don't need to call the method
arrNotes = (ArrayList<Notes>) dbHelper.notesDao().getAllNotes();
just keep a reference to the adapter instance and whenever there is a change from liveData call the notifyDataSetChanged method.
adapter = new RecyclerViewAdapter(this,new List<Notes>());
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setAdapter(adapter);
modelView.getAllNotes().observe(this, new Observer<List<Notes>>() {
@Override
public void onChanged(List<Notes> notes) {
arrNotes.clear();
arrNotes.addAll(notes);
adapter.notifyDataSetChanged()
}
});
CodePudding user response:
observe a LiveData object as it is not a LifecycleOwner
Use observeForever()
on the LiveData, manually unregistering via removeObserver()
when appropriate (onDestroy()
of the service, if not sooner).
Bear in mind that standard service limitations apply here (e.g., services run for ~1 minute on Android 8.0 unless they are foreground services), so it may be that you need to consider other approaches anyway.