Modified your code and made some improvements so that it gives us the expected results. Let's see what's changed and why step by step.
Item Design
The goal here is to combine two different classes into the one list. To make this possible we need to add a BaseItem class which will contain the all common properties and functions of the NoteHelper and ChecklistHelper classes so that we extend them from the BaseItem class.Note that it has an abstract method called getViewType so that the inherited classes can implement this method and provide us their view types.
BaseItem.java
public abstract class BaseItem {
int id;
String content;
long date;
public BaseItem() {
content = "";
}
public BaseItem(int id, String content, long date) {
this.id = id;
this.content = content;
this.date = date;
}
public abstract int getViewType();
public int getId() {
return id;
}
public String getContent() {
return content;
}
}
Now if we see the NoteHelper and ChecklistHelper classes; each class has its own definition of view type and implemets the getViewType class which we will use it for our convenience in the adaptor class.
NoteHelper.java
public class NoteHelper extends BaseItem {
public static final int VIEW_TYPE = 0;
public NoteHelper() {
}
public NoteHelper(int id, String content, long date) {
super(id, content, date);
}
@Override
public int getViewType() {
return VIEW_TYPE;
}
}
ChecklistHelper.java
public class ChecklistHelper extends BaseItem {
public static final int VIEW_TYPE = 1;
public ChecklistHelper(){
}
public ChecklistHelper(int id, String content, long date){
super(id, content, date);
}
@Override
public int getViewType() {
return VIEW_TYPE;
}
}
Combinating Two Items Into One List
The next step is design the combination logic. As an example I designed the combination based on creation date. But it can be done based on any criteria similar to this. In order to implement the date based combination logic we will need a long type comparator since we save the creation date as a time stamp using System.currentMillis().
ItemLongDateComparator.java
public class ItemLongDateComparator implements Comparator<BaseItem> {
@Override
public int compare(BaseItem baseItem, BaseItem t1) {
return Long.compare(baseItem.date, t1.date);
}
}
Redesigning the Databese Helper
Making database queries within the main thread is not a good practice since it can cause UI glitches as the data gets bigger. To prevent UI issues we execute the queries in a background thread using an executor service and deliver the result to the main thread. But before we deliver the result we combine the data in background thread by the creation timestamp criteria, then we send the result to main thread using an interface.
DbHelper.java
public class DbHelper extends SQLiteOpenHelper {
// This background executor is only for database operations
private final Executor dbexecutor;
public static final String DATABASE_NAME = "NOTES.DB";
public static final int DATABASE_VERSION = 1;
public static final String NOTES_TABLE = "NOTES_TABLE";
public static final String CHECKLIST_TABLE = "CHECKLIST_TABLE";
public static final String COLUMN_ID = "ID";
public static final String COLUMN_CONTENT = "CONTENT";
public static final String COLUMN_DATE = "DATE";
public DbHelper(@Nullable Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
dbexecutor = Executors.newSingleThreadExecutor();
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
String query = " CREATE TABLE " + NOTES_TABLE + " ( " + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT ," +
COLUMN_CONTENT + " TEXT, " + COLUMN_DATE + " DATE " + " ) ";
sqLiteDatabase.execSQL(query);
String query2 = "CREATE TABLE " + CHECKLIST_TABLE + " ( " + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COLUMN_CONTENT + " TEXT, " + COLUMN_DATE + " DATE " + " ) ";
sqLiteDatabase.execSQL(query2);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
if (oldVersion>newVersion) {
sqLiteDatabase.execSQL(" DROP TABLE IF EXISTS " + NOTES_TABLE);
sqLiteDatabase.execSQL(" DROP TABLE IF EXISTS " + CHECKLIST_TABLE);
}
onCreate(sqLiteDatabase);
}
public void insertNote(String content, long date){
dbexecutor.execute(()-> {
SQLiteDatabase sqLiteDatabase;
sqLiteDatabase = getWritableDatabase();
ContentValues c = new ContentValues();
c.put(COLUMN_CONTENT, content);
c.put(COLUMN_DATE, date);
sqLiteDatabase.insert(NOTES_TABLE, null, c);
});
}
public void insertChecklist(String content, long date){
dbexecutor.execute(()-> {
SQLiteDatabase sqLiteDatabase;
sqLiteDatabase = getWritableDatabase();
ContentValues c = new ContentValues();
c.put(COLUMN_CONTENT, content);
c.put(COLUMN_DATE, date);
sqLiteDatabase.insert(CHECKLIST_TABLE, null, c);
});
}
private List<NoteHelper> getNotes() {
List<NoteHelper> notesList = new ArrayList<>();
SQLiteDatabase sqLiteDatabase = this.getReadableDatabase();
String query = " SELECT * FROM " + NOTES_TABLE;
Cursor cursor = sqLiteDatabase.rawQuery(query, null);
if (cursor.moveToFirst()) {
do {
int id = cursor.getInt(0);
String content = cursor.getString(1);
long date = cursor.getLong(2);
NoteHelper noteHelper = new NoteHelper(id, content, date);
notesList.add(noteHelper);
}
while (cursor.moveToNext());
} else {
sqLiteDatabase.close();
cursor.close();
}
return notesList;
}
private List<ChecklistHelper> getChecklists(){
List<ChecklistHelper> checklistsList = new ArrayList<>();
SQLiteDatabase sqLiteDatabase = this.getReadableDatabase();
String query = "SELECT * FROM " + CHECKLIST_TABLE;
Cursor cursor = sqLiteDatabase.rawQuery(query, null);
if (cursor.moveToFirst()){
do{
int id = cursor.getInt(0);
String content = cursor.getString(1);
long date = cursor.getLong(2);
ChecklistHelper checklistHelper = new ChecklistHelper(id, content, date);
checklistsList.add(checklistHelper);
}
while (cursor.moveToNext());
}
else {
cursor.close();
sqLiteDatabase.close();
}
return checklistsList;
}
public void getNotesAndChecklists(OnContentReadyListener listener) {
if(listener == null) return; // Don't proceed if the listener is null
dbexecutor.execute(()-> {
List<NoteHelper> notes = getNotes();
List<ChecklistHelper> checks = getChecklists();
// Merge lists and sort items by creation date
List<BaseItem> items = new ArrayList<>();
items.addAll(notes);
items.addAll(checks);
Collections.sort(items, new ItemLongDateComparator());
new Handler(Looper.getMainLooper()).post(()-> listener.onContentReady(items));
});
}
@MainThread
public interface OnContentReadyListener {
void onContentReady(List<BaseItem> combinedItems);
}
}
Connecting the Data to the UI
Now that we've done with the data organisation, we can adapt this design easily to the UI API. In the adapter class we simply use a list of BaseItem
class since this list will accept all derived classes from the BaseItem.
It will be easier to manage only one list instead of two.
AdapterClass.java
public class AdapterClass extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List<BaseItem> itemsList;
public AdapterClass() {
itemsList = new ArrayList<>();
}
@Override
public int getItemViewType(int position) {
return itemsList.get(position).getViewType();
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
if(viewType == NoteHelper.VIEW_TYPE){
View view = layoutInflater.inflate(R.layout.layout_note, parent,false);
return new NoteViewHolder(view);
}
if(viewType == ChecklistHelper.VIEW_TYPE){
View view = layoutInflater.inflate(R.layout.layout_checklist, parent, false);
return new ChecklistViewHolder(view);
}
return null;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
int vtype = getItemViewType(position);
BaseItem bi = itemsList.get(position);
if(vtype == NoteHelper.VIEW_TYPE) {
NoteViewHolder nvh = (NoteViewHolder) holder;
TextView tv = nvh.itemView.findViewById(R.id.tv_note);
tv.setText(bi.getContent());
}
else if(vtype == ChecklistHelper.VIEW_TYPE) {
ChecklistViewHolder cvh = (ChecklistViewHolder) holder;
TextView tv = cvh.itemView.findViewById(R.id.tv_checklist);
tv.setText(bi.getContent());
}
}
@Override
public int getItemCount() {
return itemsList.size();
}
public void update(List<BaseItem> combinedList) {
itemsList.clear();
itemsList.addAll(combinedList);
notifyItemRangeChanged(0, itemsList.size());
}
static class NoteViewHolder extends RecyclerView.ViewHolder{
public NoteViewHolder(@NonNull View itemView) {
super(itemView);
}
}
static class ChecklistViewHolder extends RecyclerView.ViewHolder{
public ChecklistViewHolder(@NonNull View itemView) {
super(itemView);
}
}
}
Reflecting the Data to the UI
Finally we can reflect the data to the UI parts. Remember when I mentioned about finishing activities instead of restarting the main activity again and again? Look at the adding activities how simple they are now.
activity_add_note.java
public class activity_add_note extends AppCompatActivity {
Button btnSaveNote;
EditText etNote;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_note);
btnSaveNote = findViewById(R.id.btn_save_note);
etNote = findViewById(R.id.et_note);
btnSaveNote.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String note = etNote.getText().toString();
DbHelper dbHelper = new DbHelper(getApplicationContext());
dbHelper.insertNote(note, System.currentTimeMillis());
// Finish this activity
new Handler(Looper.getMainLooper()).postDelayed(() -> finish(), 500);
}
});
}
}
activity_add_checklist.java
public class activity_add_checklist extends AppCompatActivity {
Button btnSaveChecklist;
EditText etChecklist;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_checklist);
btnSaveChecklist = findViewById(R.id.btn_save_checklist);
etChecklist = findViewById(R.id.et_checklist);
btnSaveChecklist.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String checklist = etChecklist.getText().toString();
DbHelper dbHelper = new DbHelper(getApplicationContext());
dbHelper.insertChecklist(checklist, System.currentTimeMillis());
// Finish this activity
new Handler(Looper.getMainLooper()).postDelayed(() -> finish(), 500);
}
});
}
}
One important thing to note in the main activity is that we query the items every time it resumes. Thus when we return from other activities the newly added items will appear in the list.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
RecyclerView recyclerView;
AdapterClass adapterClass;
Button btnNote, btnChecklist;
DbHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recycler_view);
btnNote = findViewById(R.id.btn_add_note);
btnChecklist = findViewById(R.id.btn_add_checklist);
recyclerView = findViewById(R.id.recycler_view);
dbHelper = new DbHelper(this);
adapterClass = new AdapterClass();
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapterClass);
btnNote.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this,activity_add_note.class);
startActivity(intent);
}
});
btnChecklist.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, activity_add_checklist.class);
startActivity(intent);
}
});
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume: ");
if(dbHelper == null) {
dbHelper = new DbHelper(this);
}
dbHelper.getNotesAndChecklists(new DbHelper.OnContentReadyListener() {
@Override
public void onContentReady(List<BaseItem> combinedItems) {
adapterClass.update(combinedItems);
}
});
}
}
Get the whole project: your app