Home > Net >  Notify adapter on item inserted in RoomDatabase and update the recyclerView
Notify adapter on item inserted in RoomDatabase and update the recyclerView

Time:08-11

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.

  • Related