5

After I save the note in my Android app, the note (or the ListView) of the note/s doesn't appear in the MainActivity. The MainActivity class of my app is:

package com.twitter.i_droidi.mynotes;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import java.util.List;

public class MainActivity extends ActionBarActivity implements AdapterView.OnItemClickListener {

    ListView lv;
    NotesDataSource nDS;
    List<NotesModel> notesList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        nDS = new NotesDataSource(this);
        lv = (ListView) findViewById(R.id.lv);

        nDS.open();
        notesList = nDS.getAllNotes();
        nDS.close();

        String[] notes = new String[notesList.size()];

        for (int i = 0; i < notesList.size(); i++) {
            notes[i] = notesList.get(i).getTitle();
        }

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1,
                android.R.id.text1, notes);
        lv.setAdapter(adapter);

        registerForContextMenu(lv);
        lv.setOnItemClickListener(this);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Intent nView = new Intent(this, Second.class);
        nView.putExtra("id", notesList.get(position).getId()); // Check...!!!
        startActivity(nView);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_delete, menu);
        super.onCreateContextMenu(menu, v, menuInfo);
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.delete:
                nDS.open();
                nDS.deleteNote(lv.getId()); // Check...!!!
                nDS.close();
                Toast nDelete = Toast.makeText(this, "Deleted.", Toast.LENGTH_LONG);
                nDelete.show();
                return true;
        }
        return super.onContextItemSelected(item);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.mainMenuNewNote:
                Intent nNote = new Intent(this, Second.class);
                startActivity(nNote);
                return true;

            case R.id.mainMenuAbout:
                AlertDialog.Builder aboutDialog = new AlertDialog.Builder(this);
                aboutDialog.setTitle("About the app");
                aboutDialog.setMessage("The Simplest app for notes!\n\n" +
                        "Developed by: Abdulaziz\n" +
                        "Twitter: @i_Droidi\n" +
                        "Telegram: MrGlitch\n\n" +
                        "Special Thanks to who tested the app before upload it on Play Store and to who use it now! :)");
                aboutDialog.setIcon(R.mipmap.ic_launcher);
                aboutDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface aboutDialog, int witch) {
                        // Do Not Do Anything.
                    }
                });

                aboutDialog.show();
                return true;

            case R.id.mainMenuExit:
                AlertDialog.Builder exDialog = new AlertDialog.Builder(this);
                exDialog.setTitle("Exit?");
                exDialog.setMessage("Are you sure to exit?");
                exDialog.setIcon(R.mipmap.ic_launcher);
                exDialog.setNegativeButton("Yes", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface exDialog, int which) {
                        finish();
                    }
                });
                exDialog.setPositiveButton("No", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface exDialog, int which) {
                        // Do Not Do Anything.
                    }
                });

                exDialog.show();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

The activity_main (xml/layout file) of my app is:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <ListView
        android:id="@+id/lv"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"></ListView>

</LinearLayout>

The Second class of my app is:

package com.twitter.i_droidi.mynotes;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.EditText;
import android.widget.Toast;

public class Second extends ActionBarActivity {

    NotesDataSource nDS;
    EditText noteTitle;
    EditText noteBody;
    int id;
    DB db;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.second);

        Intent in = getIntent();
        id = in.getIntExtra("id", 0);

        noteTitle = (EditText) findViewById(R.id.note_title);
        noteBody = (EditText) findViewById(R.id.note);
        nDS = new NotesDataSource(this);

        nDS.open();
        NotesModel note = nDS.getNote(id);
        nDS.close();

        noteTitle.setText(note.getTitle());
        noteBody.setText(note.getBody());
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_second, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.secondMenuSave:
                if (!noteTitle.getText().toString().isEmpty() && !noteBody.getText().toString().isEmpty()) {
                    nDS.open();
                    nDS.updateNote(id, noteTitle.getText().toString(), noteBody.getText().toString());
                    nDS.close();
                    Toast nSave = Toast.makeText(this, "Saved.", Toast.LENGTH_LONG);
                    nSave.show();
                    finish();
                } else {
                    Toast notSave = Toast.makeText(this, "The title and content of the note CANNOT be empty!", Toast.LENGTH_LONG);
                    notSave.show();
                }
                return true;

            case R.id.secondMenuBack:
                AlertDialog.Builder baDialog = new AlertDialog.Builder(this);
                baDialog.setTitle("Back?");
                baDialog.setMessage("Do you want to back to the main page before saving the note?");
                baDialog.setIcon(R.mipmap.ic_launcher);
                baDialog.setNegativeButton("Yes", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface baDialog, int which) {
                        finish();
                    }
                });
                baDialog.setPositiveButton("No", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface baDialog, int which) {
                        // Do Not Do Anything.
                    }
                });

                baDialog.show();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

The NotesDataSource class of my app is:

package com.twitter.i_droidi.mynotes;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;

public class NotesDataSource {

    DB myDB;
    SQLiteDatabase sql;

    String[] getAllColumns = new String[]{DB.ID, DB.TITLE, DB.BODY};

    public NotesDataSource(Context context) {
        myDB = new DB(context);
    }

    public void open() {
        try {
            sql = myDB.getWritableDatabase();
        } catch (Exception ex) {
            Log.d("Error in your database!", ex.getMessage());
        }
    }

    public void close() {
        sql.close();
    }

    public void createNote(String title, String body) {
        ContentValues note = new ContentValues();
        note.put(myDB.TITLE, title);
        note.put(myDB.BODY, body);
        sql.insert(myDB.TABLE_NAME, null, note);
    }

    public NotesModel getNote(int id) {
        NotesModel note = new NotesModel();

        Cursor cursor = sql.rawQuery("SELECT * FROM " + DB.TABLE_NAME + " WHERE " + DB.ID + " = ?", new String[]{id + ""});

        if (cursor.getCount() > 0) {
            cursor.moveToFirst();
            note.setId(cursor.getInt(0));
            note.setTitle(cursor.getString(1));
            note.setBody(cursor.getString(2));
            cursor.close();
        }
        return note;
    }

    public void updateNote(int id, String title, String body) {
        ContentValues note = new ContentValues();
        note.put(myDB.TITLE, title);
        note.put(myDB.BODY, body);
        sql.update(myDB.TABLE_NAME, note, myDB.ID + " = " + id, null);
    }

    public void deleteNote(Object id) {
        sql.delete(myDB.TABLE_NAME, myDB.ID + " = " + id, null);
    }

    public List<NotesModel> getAllNotes() {
        List<NotesModel> notesList = new ArrayList<NotesModel>();

        Cursor cursor = sql.query(myDB.TABLE_NAME, getAllColumns, null, null, null, null, null);
        cursor.moveToFirst();

        while (!cursor.isAfterLast()) {
            NotesModel notes = new NotesModel();
            notes.setId(cursor.getInt(0));
            notes.setTitle(cursor.getString(1));
            notes.setBody(cursor.getString(2));

            notesList.add(notes);
            cursor.moveToNext();
        }

        cursor.close();
        return notesList;
    }
}

The DB class of my app is:

package com.twitter.i_droidi.mynotes;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DB extends SQLiteOpenHelper {

    private static final String DB_NAME = "MyNotes";
    private static final int DB_VERSION = 1;

    public static final String TABLE_NAME = "MyNotes";
    public static final String ID = "id";
    public static final String TITLE = "title";
    public static final String BODY = "body";

    private static final String DB_CREATE = "create table " + TABLE_NAME + " (" + ID + " integer primary key autoincrement, " +
            TITLE + " text not null, " + BODY + " text not null)";

    public DB(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(DB_CREATE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        onCreate(db);
    }
}

A screenshot of my app (MainActivity), after saving the note (Nothing shows!):

How I can solve this problem?!

Or anyone can write the correct code?!

Thanks for helping me.

ChrisF
  • 134,786
  • 31
  • 255
  • 325
  • I'm not sure if I completely understand what is going on but I think you need to call `notifyDataSetChanged()` on the listview adapter after you've added a note. – DigitalNinja Jun 13 '15 at 00:40
  • Okay, so let me see if I understand. Your typing in/adding a note to your list of notes. Then when you're done and it's "saved", and you're returned to the main screen the note is not there. Is that correct? Is is it in your array of notes (`notes[i] = notesList.get(i).getTitle();`)? Do you see it there? If so, then I think it still goes back to simply updating the listview adapter with the new set of data (notes[]). After you save it to your list call `notifyDataSetChanged()` on the adapter that holds the listview. – DigitalNinja Jun 13 '15 at 01:01
  • Did you check that the notes array actually contains data? – ᴘᴀɴᴀʏɪᴏᴛɪs Jun 13 '15 at 01:06
  • Same problem after I adding this line `adapter.notifyDataSetChanged();` –  Jun 13 '15 at 01:35
  • Post the code of `NotesDataSource` – Vasiliy Jun 13 '15 at 16:26
  • I posted it, check it out. –  Jun 13 '15 at 16:30
  • Anyone can help me?! –  Jun 14 '15 at 18:47

3 Answers3

0

The problem is that the onCreate is only called once during the lifecycle. When you switch activities, the Main is simply hidden and paused. When you are returned to the activity, the onCreate does not fire again.

Override the onStart method and set your list adapter in there. It will be called right after the onCreate() and when you are returned to the view. Here is the Android lifecycle:

http://developer.android.com/training/basics/activity-lifecycle/starting.html

Tyler Kiser
  • 921
  • 8
  • 14
  • Same problem! Maybe I did a mistake while I using the `onStart` method. Can you please modify my code and write that method in it?! –  Jun 13 '15 at 13:21
  • 1
    This answer is absolutely incorrect - assigning the `Adapter` to the list in `onStart()` is just wrong. – Vasiliy Jun 13 '15 at 13:35
  • So, can I use the `onResume()` method and inside it an adapter?! –  Jun 13 '15 at 13:47
-1

This answer assumes that your implementation of Second activity adds a new note to some global model and that the state of this model is reflected in the nDS object that you've already created in MainActivity (since onCreate() of MainActivity usually will not be called when you finish() Second).

You should definitely test that the above assumption is correct. If it is not, you could employ some form of Singleton design pattern for storing the data.

In order to reflect the changes into the ListView modify/add the following code in your MainActivity:

public class MainActivity extends ActionBarActivity implements AdapterView.OnItemClickListener {

ArrayAdapter<String> adapter;
NotesDataSource nDS;
List < NotesModel > notesList;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    nDS = new NotesDataSource(this);
    ListView lv = (ListView) findViewById(R.id.lv);

    nDS.open();
    notesList = nDS.getAllNotes();
    nDS.close();

    String[] notes = new String[notesList.size()];

    for (int i = 0; i < notesList.size(); i++) {
        notes[i] = notesList.get(i).getTitle();
    }

    adapter = new ArrayAdapter < String > (MainActivity.this, android.R.layout.simple_list_item_1,
    android.R.id.text1, notes);
    lv.setAdapter(adapter);

    registerForContextMenu(lv);
    lv.setOnItemClickListener(this);
}



@Override
protected void onResume() {
    super.onResume();

    nDS.open();
    notesList = nDS.getAllNotes();
    nDS.close();

    String[] notes = new String[notesList.size()];

    for (int i = 0; i < notesList.size(); i++) {
        notes[i] = notesList.get(i).getTitle();
    }

    adapter.clear();
    adapter.addAll(notes);
    adapter.notifyDataSetChanged();
}

// No changes in other methods ...

}

Additional note: it is very inefficient and error prone to maintain ArrayAdapter<String> and List<NotesModel>. It will be way better if you implement a custom ListAdapter. You can use something like this:

class CustomAdapter extends ArrayAdapter<NotesModel> {
    ...
}

Good tutorial on writing custom adapters can be found here.

Community
  • 1
  • 1
Vasiliy
  • 16,221
  • 11
  • 71
  • 127
  • Thanks for helping me! Still the same problem! I will update the post with the Second class. And, please see the (Check...!!!) comments in all the classes, maybe you will find an error related to our main problem. –  Jun 13 '15 at 15:01
  • @MrGlitch, I assume that `DB` class subclasses `SQLiteOpenHelper`, but I don't see any code that adds new notes to the SQLite DB... Do you call `NotesDataSource.createNote()` somewhere? – Vasiliy Jun 14 '15 at 19:10
  • I updated the post with `DB` class, check it out please. –  Jun 14 '15 at 19:22
  • @MrGlitch, I don't see any call to `NotesDataSource.createNote()` method. Have you checked that your SQLite DB contains any information at all? – Vasiliy Jun 14 '15 at 19:27
  • I added this line `nDS.createNote(noteTitle.getText().toString(), noteBody.getText().toString());` in **Second class** inside `case R.id.secondMenuSave:` after opening the database by using `nDS.open();` but still the same problem! Maybe there is a problem with closing the database by using `nDS.close();`?! Please, check it out. Thanks. –  Jun 14 '15 at 21:37
  • @MrGlitch, I think you got all the help you need: the code in this answer will make sure that your list is updated when you get back to `MainActivity`. I also pointed out that your DB might be empty. It is your job to test it and fix it in case it is the case. There are many questions on SO concerning debugging SQLite DBs. If your DB is updated, then try to see whether `getAllNotes()` return correct values. – Vasiliy Jun 15 '15 at 05:35
  • It is very much unfortunate to use listview and arrayadapter in the year of 2017. It is pretty much obselete and sign of backdated coding style. Also I have noticed you have called local db in both on create and in in resume which is pretty much bad practice because on activity creation both oncreate and onresume gets called one after another. Also actionbar activity is very much backdated one. – amit Mar 29 '19 at 03:26
-1

I think there are two problem present in your code

1) First replace

public List<NotesModel> getAllNotes() {
        List<NotesModel> notesList = new ArrayList<NotesModel>();

        Cursor cursor = sql.query(myDB.TABLE_NAME, getAllColumns, null, null, null, null, null);
        cursor.moveToFirst();

        while (!cursor.isAfterLast()) {
            NotesModel notes = new NotesModel();
            notes.setId(cursor.getInt(0));
            notes.setTitle(cursor.getString(1));
            notes.setBody(cursor.getString(2));

            notesList.add(notes);
            cursor.moveToNext();
        }

        cursor.close();
        return notesList;
    }

with these

public List<NotesModel> getAllNotes() {
        List<NotesModel> notesList = new ArrayList<NotesModel>();

StringBuffer selectQuery = new StringBuffer();
selectQuery.append("SELECT * FROM "+myDB.TABLE_NAME+"");

        Cursor cursor = sql.rawQuery(selectQuery.toString(),null);

if (cursor != null && cursor.moveToFirst()) {
            do {
               NotesModel notes = new NotesModel();
            notes.setId(cursor.getInt(0));
            notes.setTitle(cursor.getString(1));
            notes.setBody(cursor.getString(2));

            notesList.add(notes);

            } while (cursor.moveToNext());
        }


        cursor.close();
        return notesList;
    }

2) Goto second class on list item click like this--

Intent nView = new Intent(this, Second.class);
        nView.putExtra("id", notesList.get(position).getId()); // Check...!!!
nView.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(nView);

and after adding note return back to MainActivity like this----

Intent nView = new Intent(this, MainActivity.class);
        nView.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(nView);

dont't use finish because it will end current task only and take out previous screen in stack without refreshing

amit
  • 171
  • 1
  • 9
  • Thanks, man! The problem has been solved! Still another small problem, if you can solve it.. How I can delete the note from the DB and from the list?! Please see **MainActivity class** at `onContextItemSelected` method. –  Jun 16 '15 at 21:54
  • Although OP confirmed that this answer solved his issue, I wouldn't recommend this approach to anybody. First of all using `rawQuery()` instead of `query()` is not justified - the functionality is the same, but it is `query()` method which is "Android way" of doing things. It is a matter of coding style standard... – Vasiliy Jun 19 '15 at 06:53
  • ...The second issue with this answer is way more serious. This statement it totally wrong: `dont't use finish because it will end current task only and take out previous screen in stack without refreshing`. When the previous `Activity` is taken from the stack both `onStart()` and `onResume()` methods will be called. You can and should "refresh" the activity's look in these methods instead of using `FLAG_ACTIVITY_CLEAR_TOP` – Vasiliy Jun 19 '15 at 07:00
  • @Vasily first of all don't undervote any answer without proper reviewing them. There can be 100 solutions on these problem. Not every coder knows all solution. I can give them a complex solution of back navigation but they will not understand them properly. So whats the point of helping others if they dont understands them. So i took easy one solution. – amit Jun 19 '15 at 09:33
  • @amit Can you please help me with the code of deleting the note?! –  Jun 20 '15 at 02:51
  • @amit, the first part of your answer is irrelevant to the questions and suggests to use bad coding style. The second part of your answer provides a bad solution and justifies it with incorrect information. I reviewed your answer properly - it is a bad answer (even though it resolved OP's issue). – Vasiliy Jun 20 '15 at 09:59