How to add new item to listview quickly - android

I want to program simple organizer with Notes.
So my solution works, but very bad, because after I create a new note in NewNoteActivity I return to activity with ListView and I see how my list is building again.
I read that listview always will be rebuild, but why it takes so lot of time in my case? For example, in other applications I haven't ever seen the process of drawing list, but in my application I do.
So how to build listview so quickly as it is in good applications? What wrong with my code? I just want to add new item in NewNoteActivity, return to activity with list and see a ready list!
I have a SQLite database with some data as shown below:
_id | time | date | text
1 | 9:45 | 12.01| blabla
2 | 21:01| 13.01| albalb
...| ... | ... | ...
Also I have a class Note:
public class Note {
private int id;
private String time;
private String date;
private String text;
public Note(final int id, final String time, final String date, final String text){
setId(id);
setTime(time);
setDate(date);
setText(text);
}
public int getId(){
return id;
}
public String getTime(){
return time;
}
public String getDate(){
return date;
}
public String getText(){
return text;
}
void setId(final int id){
this.id = id;
}
void setTime(final String time){
this.time = time;
}
void setDate(final String date){
this.date = date;
}
void setText(final String text){
this.text = text;
}
}
And NotesManager:
public class NotesManager {
private static final String TABLE_NAME = "NotesListTable";
private static final String KEY_TIME = "time";
private static final String KEY_DATE = "date";
private static final String KEY_TEXT = "text";
private static final String KEY_ID = "_id";
private final SQLiteDatabase db;
public NotesManager(SQLiteDatabase db){
this.db = db;
}
public void save(final ContentValues cv){
db.insert(TABLE_NAME, null, cv);
}
public void delete(final int id){
db.delete(TABLE_NAME, KEY_ID + "=" + id, null);
}
public Note getNoteById(final int id){
Cursor mCursor = db.query(TABLE_NAME, null, KEY_ID + "=" + id, null, null, null, null, null);
if (mCursor != null) {
mCursor.moveToFirst();
}
return new Note(mCursor.getInt(mCursor.getColumnIndex(KEY_ID)),
mCursor.getString(mCursor.getColumnIndex(KEY_TIME)),
mCursor.getString(mCursor.getColumnIndex(KEY_DATE)),
mCursor.getString(mCursor.getColumnIndex(KEY_TEXT)));
}
public Cursor getAllDataFromDB(){
return db.query(TABLE_NAME, null, null, null, null, null, null);
}
public String[] getKeysArray(){
return new String[] {KEY_ID, KEY_TIME, KEY_DATE, KEY_TEXT};
}
}
I have a fragment with ListView:
It has been generated by Android Studio, nut I made some changes, added SimpleCursorAdapter
public class NotesListFragment extends Fragment implements AbsListView.OnItemClickListener {
private static final String ARG_SECTION_NUMBER = "section_number";
private int mSectionNumber = 0;
private OnFragmentInteractionListener mListener;
private AbsListView mListView;
private SimpleCursorAdapter scAdapter;
private Cursor cursor;
ImageButton deleteButton;
NotesManager notesManager = new NotesManager(OrganizerApp.db);
public static NoesListFragment newInstance(int param1) {
NoesListFragment fragment = new NotesListFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, param1);
fragment.setArguments(args);
return fragment;
}
public NotesListFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mSectionNumber = getArguments().getInt(ARG_SECTION_NUMBER);
}
cursor = NotesManager.getAllDataFromDB();
//TODO: startManagingCursor(cursor)
scAdapter = new SimpleCursorAdapter(getActivity(),
R.layout.note_list_rowlayout,
cursor,
notesManager.getKeysArray(),
new int[]{R.id.note_list_rowlayout_item1,
R.id.note_list_rowlayout_item2,
R.id.note_list_rowlayout_item3,
R.id.note_list_rowlayout_item4 });
deleteButton = (ImageButton) getView().
findViewById(R.id.note_list_rowlayout_deleteButton);
deleteButton.setOnClickListener(onClickDeleteButton);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_note, container, false);
// Set the adapter
mListView = (AbsListView) view.findViewById(android.R.id.list);
mListView.setAdapter(scAdapter);
// Set OnItemClickListener so we can be notified on item clicks
mListView.setOnItemClickListener(this);
return view;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mSectionNumber = getArguments().getInt(ARG_SECTION_NUMBER);
mListener = (OnFragmentInteractionListener) activity;
((MainActivity) activity).onSectionAttached(mSectionNumber);
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (null != mListener) {
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
// mListener.onFragmentInteraction(NotesListContent.ITEMS.get(position).id);
}
}
public void setEmptyText(CharSequence emptyText) { // If list is empty.
View emptyView = mListView.getEmptyView();
if (emptyView instanceof TextView) {
((TextView) emptyView).setText(emptyText);
}
}
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
public void onFragmentInteraction(String id);
}
View.OnClickListener onClickDeleteButton = new View.OnClickListener() {
#Override
public void onClick(View v) {
}
};
}
I have an activity NewNoteActivity in which I create a new note and put it into database:
ContentValues cv = new ContentValues();
cv.put("time", entTime.getText().toString());
cv.put("date", entDate.getText().toString());
cv.put("comment", entComment.getText().toString());
NotesManager.save(cv);

I'll drop a few hints in here.
As you are using a DB to load data, my thoughts go first to the idea you have a very big number of records you are fetching when creating the list.
Multiply it by 4, the number of views in each listView's item, it may be a reason for the slowdown.
Generally, you'd want to start fetching the data from another Thread, aiming to keep the MainThread light and free(meaning your app will be responsive while the heavy loading takes place).
This can be done via AsyncThread. I'd suggest to pop up a ProgressBar view while you are loading and displaying data, to notify the user your app has work in progress.
Another GREAT way to make your listView faster (during scrolling at least, not sure if it can help you on the loading phase), is to implement the ViewHolder pattern in your Adapter.
Each time you scroll, Android calls the getView() method of the Adapter, generally creating a new View each time an item is found(remember, an item has 4 views inside!). This makes it incredibly slow when you handle many items. The idea behind ViewHolder pattern is to re-use views which are no longer visible rather than adding new ones.
You can find more about it here.
If this still doesn't help, I'd give a try to another way to load.
Having like 1000+ rows from the DB may be an overload of work for what you first need to show.
As an example, let's imagine you have 1000 records to insert in the listView. Loading them all may take some time, and when this is done and the listView is filled, you'll never be able to look at them, simply because your listView has a certain space and on the display you'd be able to see just a little portion of the data, let's say 10.
My point is: why keep the phone busy loading 1000 records on view creation, when you can load let's say 50(thinking over to an early-scroll), and when you know the listView is created and you are able to interact with the app load the rest in a background thread(still, AsyncTask). Once you are done, notify the adapter of the data change.
Hope to have been understandable ^_^"

Related

Get a DB-Record ID from a ListView item (LV with CursorAdapter and DB Helper)

I'm trying to build a simple app to serve as a students list.
MainActivity consists of ListView, EditText boxes and an 'add' Button.
My ListView connects with a custom CursorAdapter, and the app uses a custom SQLiteOpenHelper DB.
Upon clicking on the 'add' button ->
New record is inserted to DB ->
DB-Record-ID Of the newly inserted record is updated on the ListView item that was added (through bindView)
What I'm trying to do, is to pass the DB-Record ID of a ListView Item, when clicking on it, to a new activity, using Intent.putExtra, but not sure how to get the child-text-view in the ListView Item that was clicked? (In onItemClick, in order to get its text and pass it with the new Intent to the new activity)
PURPOSE Is to have the DB-Record-ID in the new activity, because the new activity allows the user to UPDATE the grade param in the record (Thus, I need the DB-Record-ID in order to update it on my DB as well upon update)
Can someone suggest a solution? Here's the needed code -
MainActivity:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
/* Our DB model to store student objects */
private SqlDbHelper mDB;
/* Custom SQL-Adapter to connect our SQL DB to the ListView */
private SQLAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* Init fields & needed views */
mDB = new SqlDbHelper(getApplicationContext());
mAdapter = new SQLAdapter(getApplicationContext(), mDB.getAllRows(), false);
final ListView lvStudents = findViewById(R.id.lv_students);
final EditText etName = findViewById(R.id.et_name);
final EditText etGrade = findViewById(R.id.et_grade);
/* Listen to ADD BUTTON clicks */
findViewById(R.id.button_add).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
insertNewRecord(etName.getText().toString(), etGrade.getText().toString());
etName.setText("");
etGrade.setText("");
}
});
/* Set adapter to LV, Listen to list item clicks */
lvStudents.setAdapter(mAdapter);
lvStudents.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent listItemIntent = new Intent(getApplicationContext(), ListItemActivity.class);
// ????????????
// listItemIntent.putExtra("_ID",
// "Get textview.text containing the param in the CLICKED list item);
startActivity(listItemIntent);
}
});
}
/**
* Creates a list item for a student name + grade
* #param name
* #param grade
* #return the new student id in the DB
*/
private long insertNewRecord(final String name, final String grade) {
int gradeInt = -1;
try {
gradeInt = Integer.parseInt(grade);
} catch (NumberFormatException e) {
Log.w(TAG, "Invalid grade entered: " + grade);
}
/* Add the new student to the DB */
final long id = mDB.insertNewStudent(name, gradeInt);
mAdapter.changeCursor(mDB.getAllRows());
return id;
}
}
Not sure if needed, but just in case -
SQLAdapter:
final class SQLAdapter extends CursorAdapter {
private LayoutInflater mInflater;
public SQLAdapter(Context context, Cursor c, boolean autoRequery) {
super(context, c, autoRequery);
mInflater = LayoutInflater.from(context);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
return mInflater.inflate(R.layout.lv_line, viewGroup, false);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
final String name = cursor.getString(cursor.getColumnIndex(SqlDbHelper.KEY_NAME));
final int grade = cursor.getInt(cursor.getColumnIndex(SqlDbHelper.KEY_GRADE));
final long id = cursor.getLong(cursor.getColumnIndex(SqlDbHelper.KEY_ID));
view.findViewById(R.id.ll_line).setBackgroundColor(Color.RED));
((TextView)view.findViewById(R.id.tv_name)).setText(name);
((TextView)view.findViewById(R.id.tv_grade)).setText(String.valueOf(grade));
((TextView)view.findViewById(R.id.tv_id)).setText(String.valueOf(id));
}
}
SqlDbHelper:
final class SqlDbHelper extends SQLiteOpenHelper {
private static final String TAG = "SqlDbHelper";
/* Database version */
public static final int VERSION = 1;
/* Relevant string names, keys represent columns */
public static final String DB_NAME = "StudentsDB";
public static final String TABLE_NAME = "students";
public static final String KEY_ID = "_id";
public static final String KEY_NAME = "Name";
public static final String KEY_GRADE = "Grade";
public SqlDbHelper(Context context) {
super(context, DB_NAME, null, VERSION);
}
#Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
/* Create sql command to create new table */
StringBuilder sql = new StringBuilder();
sql.append("CREATE TABLE " + TABLE_NAME + " (")
.append(KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,")
.append(KEY_NAME + " TEXT,")
.append(KEY_GRADE + " INT")
.append(")");
/* Execute our sql command:
* CREATE TABLE StudentsDB(_id INTEGER PRIMARY KEY, Name TEXT, Grade INT) */
Log.d(TAG, "Query to form table: " + sql.toString());
sqLiteDatabase.execSQL(sql.toString());
}
#Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {}
public long insertNewStudent(final String name, final int grade) {
ContentValues cv = new ContentValues();
cv.put(KEY_NAME, name);
cv.put(KEY_GRADE, grade);
return getWritableDatabase().insert(TABLE_NAME, null, cv);
}
public Cursor getAllRows() {
return getReadableDatabase().
rawQuery("SELECT * FROM " + TABLE_NAME, null);
}
}
The 4th parameter long l passed to onItemClick, for a CursorAdapter, is the id (i.e _id as per the cursor).
So just do listItemIntent.putExtra("_ID",l);
e.g. :-
lvStudents.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent listItemIntent = new Intent(getApplicationContext(), ListItemActivity.class);
listItemIntent.putExtra("_ID",l); // ID as per _id in the cursor.
startActivity(listItemIntent);
}
});
Edit after comment :-
Thanks, and for the sake of the question - how to get any other
text-view text from the specific list item, in the onItemClick? will
view.findViewById(id_of_the_text_box) will get me the correct text?
Yes, if you want to access a a view from the clicked item then use (assuming TextView, otherwsie whatever is appropriate for the View) :-
view.findViewById(R.id.your_view_id).getText().toString;
So applying this you could have:-
lvStudents.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent listItemIntent = new Intent(getApplicationContext(), ListItemActivity.class);
listItemIntent.putExtra("_ID",l); // ID as per _id in the cursor.
listItemIntent.putExtra("_IDFROMVIEW,
view.findViewByID(R.id.tv_id).getText().toString();
startActivity(listItemIntent);
}
});
i.e. it finds the View within the clicked Item's list of views (a specific subset/branch of the ListView's (aka adapterView) list of views).

How to refresh rating in adapter using the Cursor Adapter

It's not duplicate. I saw all answers is Stack.
My problem: I have CommentActivity, where I get Cursor. In CommentCursorAdapter I get values from Database.
In adapter I have two images: Like and Dislike. When I click Like - in database rating incremented.
TextView rating should show a new rating after pressing. How to do it correctly?
CommentActivity
public class CommentActivity extends AppCompatActivity {
private String idComment;
private ListView listComments;
private CommentCursorAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comment);
listComments = (ListView) findViewById(R.id.list_comments);
idComment = getIntent().getStringExtra(MainActivity.ID_KEY);
getCursorData();
}
private void getCursorData() {
adapter = new CommentCursorAdapter(this, new CursorLoader(App.getInstance(), idComment).loadInBackground(), 0);
listComments.setAdapter(adapter);
}
private static class CursorLoader extends SimpleCursorAdapter {
private String idComment;
public CursorLoader(Context context, String idComment) {
super(context);
this.idComment = idComment;
}
#Override
public Cursor loadInBackground() {
return App.getInstance().getDb().rawQuery(
"SELECT comment._id AS _id, comment.text AS text, user.email AS email, comment.rate FROM comment JOIN user ON comment.userId = user._id WHERE comment.postId = ? ORDER BY _id ASC", new String[]{
idComment
});
}
}
}
CommentCursorAdapter
public class CommentCursorAdapter extends CursorAdapter {
private TextView commentEmail;
private TextView commentText;
private TextView ratingText;
private ImageView imageViewLike;
private ImageView imageViewDislike;
private static final String ID = "_id";
private static final String EMAIL = "email";
private static final String TEXT = "text";
private static final String RATE = "rate";
public CommentCursorAdapter(Context context, Cursor c, int flags) {
super(context, c, 0);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
return LayoutInflater.from(context).inflate(R.layout.adapter_comment, viewGroup, false);
}
#Override
public void bindView(View view, Context context, final Cursor cursor) {
commentEmail = (TextView) view.findViewById(R.id.comment_email);
commentText = (TextView) view.findViewById(R.id.comment_text);
ratingText = (TextView) view.findViewById(R.id.rating_text);
imageViewLike = (ImageView) view.findViewById(R.id.image_like);
imageViewDislike = (ImageView) view.findViewById(R.id.image_dislike);
imageViewLike.setTag(cursor.getString(cursor.getColumnIndexOrThrow(ID)));
imageViewDislike.setTag(cursor.getString(cursor.getColumnIndexOrThrow(ID)));
String email = cursor.getString(cursor.getColumnIndexOrThrow(EMAIL));
String text = cursor.getString(cursor.getColumnIndexOrThrow(TEXT));
String rating = cursor.getString(cursor.getColumnIndexOrThrow(RATE));
commentEmail.setText(email);
commentText.setText(text);
ratingText.setText(rating);
imageViewLike.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.e("like", "click");
SQLiteStatement statement = App.getInstance().getDb().compileStatement(
"UPDATE comment SET rate = rate + 1 WHERE comment._id = ?"
);
statement.bindString(1, (String) view.getTag());
try {
statement.execute();
} finally {
statement.close();
}
}
});
imageViewDislike.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.e("dislike", "click");
SQLiteStatement statement = App.getInstance().getDb().compileStatement(
"UPDATE comment SET rate = rate - 1 WHERE comment._id = ?"
);
statement.bindString(1, (String) view.getTag());
try {
statement.execute();
} finally {
statement.close();
}
//swapCursor(cursor);
//notifyDataSetChanged();
}
});
}
}
There are a some options you can choose from:
After updating the rating in the database, you can just reload all data using your loader.
You can migrate your database logic to using a ContentProvider. If you do it correctly, the provider can notify all loaders that their underlying data has been changed, so they are automatically reloaded.
In your CustomAdapter, you need to put this.notifyDataSetChanged(); where you are performing operation of the LIKE and COMMENT.
It's great that you have used CursorAdapter - that listens for update notifications and reloads the content automatically.
Hope it will help you.

Good way to create a dynamic listview with data from SQLite?

I want to program simple organizer with Notes.
I have a SQLite database with some data as shown below:
_id | time | date | text
1 | 9:45 | 12.01| blabla
2 | 21:01| 13.01| albalb
...| ... | ... | ...
Also I have a class Note:
public class Note {
private int id;
private String time;
private String date;
private String text;
public Note(final int id, final String time, final String date, final String text){
setId(id);
setTime(time);
setDate(date);
setText(text);
}
public int getId(){
return id;
}
public String getTime(){
return time;
}
public String getDate(){
return date;
}
public String getText(){
return text;
}
void setId(final int id){
this.id = id;
}
void setTime(final String time){
this.time = time;
}
void setDate(final String date){
this.date = date;
}
void setText(final String text){
this.text = text;
}
}
And NotesManager:
public class NotesManager {
private static final String TABLE_NAME = "NotesListTable";
private static final String KEY_TIME = "time";
private static final String KEY_DATE = "date";
private static final String KEY_TEXT = "text";
private static final String KEY_ID = "_id";
private final SQLiteDatabase db;
public NotesManager(SQLiteDatabase db){
this.db = db;
}
public void save(final ContentValues cv){
db.insert(TABLE_NAME, null, cv);
}
public void delete(final int id){
db.delete(TABLE_NAME, KEY_ID + "=" + id, null);
}
public Note getNoteById(final int id){
Cursor mCursor = db.query(TABLE_NAME, null, KEY_ID + "=" + id, null, null, null, null, null);
if (mCursor != null) {
mCursor.moveToFirst();
}
return new Note(mCursor.getInt(mCursor.getColumnIndex(KEY_ID)),
mCursor.getString(mCursor.getColumnIndex(KEY_TIME)),
mCursor.getString(mCursor.getColumnIndex(KEY_DATE)),
mCursor.getString(mCursor.getColumnIndex(KEY_TEXT)));
}
public Cursor getAllDataFromDB(){
return db.query(TABLE_NAME, null, null, null, null, null, null);
}
public String[] getKeysArray(){
return new String[] {KEY_ID, KEY_TIME, KEY_DATE, KEY_TEXT};
}
}
I have a fragment with ListView:
It has been generated by Android Studio, nut I made some changes, added SimpleCursorAdapter
public class NotesListFragment extends Fragment implements AbsListView.OnItemClickListener {
private static final String ARG_SECTION_NUMBER = "section_number";
private int mSectionNumber = 0;
private OnFragmentInteractionListener mListener;
private AbsListView mListView;
private SimpleCursorAdapter scAdapter;
private Cursor cursor;
ImageButton deleteButton;
NotesManager notesManager = new NotesManager(OrganizerApp.db);
public static NoesListFragment newInstance(int param1) {
NoesListFragment fragment = new NotesListFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, param1);
fragment.setArguments(args);
return fragment;
}
public NotesListFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mSectionNumber = getArguments().getInt(ARG_SECTION_NUMBER);
}
cursor = NotesManager.getAllDataFromDB();
//TODO: startManagingCursor(cursor)
//mAdapter = new ArrayAdapter<NotesListContent.NotesItem>(getActivity(),
// android.R.layout.simple_list_item_1, android.R.id.text1, NotesListContent.ITEMS);
scAdapter = new SimpleCursorAdapter(getActivity(),
R.layout.note_list_rowlayout,
cursor,
notesManager.getKeysArray(),
new int[]{R.id.note_list_rowlayout_item1,
R.id.note_list_rowlayout_item2,
R.id.note_list_rowlayout_item3,
R.id.note_list_rowlayout_item4 });
deleteButton = (ImageButton) getView().
findViewById(R.id.note_list_rowlayout_deleteButton);
deleteButton.setOnClickListener(onClickDeleteButton);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_note, container, false);
// Set the adapter
mListView = (AbsListView) view.findViewById(android.R.id.list);
mListView.setAdapter(scAdapter);
//((AdapterView<ListAdapter>) mListView).setAdapter(mAdapter);
// Set OnItemClickListener so we can be notified on item clicks
mListView.setOnItemClickListener(this);
return view;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mSectionNumber = getArguments().getInt(ARG_SECTION_NUMBER);
mListener = (OnFragmentInteractionListener) activity;
((MainActivity) activity).onSectionAttached(mSectionNumber);
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (null != mListener) {
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
// mListener.onFragmentInteraction(NotesListContent.ITEMS.get(position).id);
}
}
public void setEmptyText(CharSequence emptyText) { // If list is empty.
View emptyView = mListView.getEmptyView();
if (emptyView instanceof TextView) {
((TextView) emptyView).setText(emptyText);
}
}
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
public void onFragmentInteraction(String id);
}
View.OnClickListener onClickDeleteButton = new View.OnClickListener() {
#Override
public void onClick(View v) {
}
};
}
Android studio also generated NotesListContent.java:
public class NotesListContent {
public static List<Note> ITEMS = new ArrayList<Note>();
//public static Map<String, Note> ITEM_MAP = new HashMap<String, Note>();
private static void addItem(Note item) {
ITEMS.add(item);
//ITEM_MAP.put(item.id, item);
}
/**
* A dummy item representing a piece of content.
public static class NoteItem {
public String id;
public String content;
public NoteItem(String id, String content) {
this.id = id;
this.content = content;
}
#Override
public String toString() {
return content;
}
}*/
}
So my solution works, but I think that it is bad.
For what I need a NotesListContent.java? How can I use it?
How can I use ListView without deprecated simpleCursorAdapter?
How to delete and add items without refresh all ListView?
Especially this code seems to be very unconvenient:
scAdapter = new SimpleCursorAdapter(getActivity(),
R.layout.note_list_rowlayout,
cursor,
notesManager.getKeysArray(),
new int[]{R.id.note_list_rowlayout_item1,
R.id.note_list_rowlayout_item2,
R.id.note_list_rowlayout_item3,
R.id.note_list_rowlayout_item4 });
I've done notes manager of my own so I'll try to answer Your questions.
For what I need a NotesListContent.java? How can I use it?
This is somewhat MVC pattern, separation of data from view. Try to think about it as an entity, or better as a single note entry description.
How can I use ListView without deprecated simpleCursorAdapter?
a) since when is simpleCursorAdapter depreciated? Only one of it's constructor is.
b) You can use second constructor, or extend some adapter class (for example ArrayAdapter) Yourself
How to delete and add items without refresh all ListView?
You add data to Your dataAdapter, then set dataAdapter as an adapter for ListView (listview.setAdapter(adapter)).
If You do not call adapter.notifyDataSetChanged() listview's view will not be updated.
Especially this code seems to be very unconvenient (...)
What's so wrong about it? But if so, feel free to use sth like this:
String[] columns = new String[] { // The desired columns to be bound
"timestamp",
"title",
"content",
};
// the XML defined views which the data will be bound to
int[] map_to = new int[] {
R.id.timestamp,
R.id.title,
R.id.content,
};
dataAdapter = new SimpleCursorAdapter(
this, R.layout.some_xml_here,
db.getAllItems(),
columns,
map_to,
0);

Trying to assign database information to ArrayList and then ListView

So please bear with me on this one... I am trying to get all information from my database table and output that information into a nice custom ListView (which I have already built).
MySQLiteHelper.java (What I am using to scrape the information)
...
public List<String> getAllLogs() {
List<String> List = new ArrayList<String>();
String selectQuery = "SELECT * FROM " + TABLE_GASLOG;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
if (cursor.moveToFirst()){
do {
List.add(cursor.getString(1));
} while (cursor.moveToNext());
}
return List;
}
...
gasLog.java (What I am using to get/set all of my information)
...
public class gasLog {
private int id;
private double pricePerGallon;
private double gallons;
private double odometer;
private String date;
private String filledOrNot; //This will be a 0 or 1 value.
private String comments;
public gasLog(){}
public gasLog(double pricePerGallon, double gallons, double odometer, String date, String filledOrNot, String comments){
super();
this.id = id;
this.pricePerGallon = pricePerGallon;
this.gallons = gallons;
this.odometer = odometer;
this.date = date;
this.filledOrNot = filledOrNot;
this.comments = comments;
}
//getters & setters
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getPricePerGallon() {
return pricePerGallon;
}
public void setPricePerGallon(double pricePerGallon) {
this.pricePerGallon = pricePerGallon;
}
public double getGallons(){
return gallons;
}
public void setGallons(double gallons){
this.gallons = gallons;
}
public double getOdometer(){
return odometer;
}
public void setOdometer(double odometer){
this.odometer = odometer;
}
public String getDate(){
return date;
}
public void setDate(String date){
this.date = date;
}
public String getFilledOrNot(){
return filledOrNot;
}
public void setFilledOrNot(String filledOrNot){
this.filledOrNot = filledOrNot;
}
public String getComments(){
return comments;
}
public void setComments(String comments){
this.comments = comments;
}
public String toString() {
return "Date: " + date + ", Price: " + pricePerGallon + ", " + gallons + " Gallons, " +
", Odometer Reading: " + odometer +
", Full fill: " + filledOrNot;
}
}
history.java (Where I am inflating the view and calling all the information to).
...
public class history extends ListActivity {
// Log table name
private static final String TABLE_GASLOG = "gasLog";
// Log table columns names
private static final String KEY_ID = "id";
private static final String KEY_PRICE_PER_GALLON = "pricePerGallon";
private static final String KEY_GALLONS = "gallons";
private static final String KEY_ODOMETER = "odometer";
private static final String KEY_DATE = "date";
private static final String KEY_FILLED_OR_NOT = "filledOrNot";
private static final String KEY_COMMENTS = "comments";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.history);
Typeface tf = Typeface.createFromAsset(getAssets(),"Roboto-Light.ttf");
ListView listContent = (ListView) findViewById(android.R.id.list);
TextView history = (TextView) findViewById(R.id.history);
history.setTypeface(tf);
MySQLiteHelper db = new MySQLiteHelper(this);
List<gasLog> list = new ArrayList<gasLog>();
list = db.getAllLogs();
ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, list);
listContent.setAdapter(adapter);
}
}
So I know I need more in the bottom section of history.java Something similar to..
// THE DESIRED COLUMNS TO BE BOUND
String[] columns = new String[] { People.NAME, People.NUMBER };
// THE XML DEFINED VIEWS WHICH THE DATA WILL BE BOUND TO
int[] to = new int[] { R.id.name_entry, R.id.number_entry };
But I am not quite sure what everything should be. I had tried using a getContentResolver but im not sure how to set the URI (or get the URI for my database) or if that is even the proper method to go.
my history.xml contains a listview and i have a bg.xml file that has the layout for each record in the listview. Right now I can only get it to return the messy looking String toString() at the bottom of gasLog.java
Any help would be greatly appreciated, if someone could give me some direction and also maybe why? Looking forward to learning something on this that I can apply down the road. Thank you so much! Sorry for being such a newbie!
EDIT:
Just want to make sure I am clear on this part.
in GasCursorAdapter.java I will setup bindView like this:
public void bindView(View v, Context context, Cursor cursor){
TextView cardDate = (TextView) v.findViewById(R.id.cardDate);
int date = cursor.getColumnIndexOrThrow(MySQLiteHelper.KEY_DATE);
cardDate.setText(date);
}
and will do that for each view/db field (then that will assign the values to the view) (I think?!)
as far as this part goes, im not quite sure where to put that..
MySQLiteHelper db = new MySQLiteHelper(this);
Cursor cursor = db.getAllLogs();
GasCursorAdapter adapter = new GasCursorAdapter(content, cursor, 0);
setAdapter(adapter);
right now i have that in history.java but i am getting content cannot be resolved to a variable
Other than that i think i have it figured out thanks to you!!!
Thanks again for your help!
I suggest you use a CursorAdapter, which will skip a few of the steps and run much smoother. I was doing something similar to you, and ran into a lot of performance issues, but I switched to a cursorAdapter, and my code is much easier to follow and quicker as a result. There's a few parts to making this happen, which I'll show you below. The first step is to simply return the cursor, instead of trying to process it in your database call.
public Cursor getAllLogs() {
List<String> List = new ArrayList<String>();
String selectQuery = "SELECT * FROM " + TABLE_GASLOG;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
return cursor;
}
This will actually pass the query in a way that you can better utilize it. The way I managed my program was to do something like this from here.
Define what a row of output of the database should look like, and place in an XML file.
Set up a CursorAdapter that looks something like the code below.
Create your query, and then pass it to a new GasCursorAdapter.
Here's GasCursorAdapter
public class GasCursorAdapter extends CursorAdapter {
private LayoutInflater mInflater=null;
public GasCursorAdapter (Context context, Cursor c,int flags) {
super(context, c, flags);
mInflater=(LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return mInflater.inflate(R.layout.gasLayout,parent,false);
}
#Override
public void bindView(View v,Context context, Cursor cursor) {
//View is the view created by newView, take it and find your views and populate it, given the Cursor
}
}
And creating it looks like:
Cursor cursor=getAllLogs();
GasCursorAdapter adapter=new GasCursorAdapter(context, cursor,0);
setAdapter(adapter);

Android error onTextChanged updating sqlite database

I am building an app that displays items in a ListFragment. Right now each item displays the title and creation date. There are two other fragments. One that creates an item and has a EditText field where i can edit the title. Another simply displays an individual items contents.
The issue I am having is that every time I enter a character in the EditText field the app closes. The error messages indicate that the error occurs at onTextChanged in the TextChangedListener. Since I had this feature working when I was storing everything as a JSON file the error must occur because of the way i am updating the database and updating my model layer.
This file performs all the database operations and creates a custom Cursor.
public class SnapDatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "FeedFragment";
private static final String DB_NAME = "snap.sqlite";
private static final int VERSION = 1;
private static final String TABLE_SNAP = "snap";
private static final String COLUMN_SNAP_ID = "_id";
private static final String COLUMN_SNAP_DATE = "snap_date";
private static final String COLUMN_SNAP_UUID = "snap_uuid";
private static final String COLUMN_SNAP_TITLE = "snap_title";
public SnapDatabaseHelper(Context context){
super(context, DB_NAME, null, VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
// Create SNAP table
db.execSQL("create table snap(" +
"_id integer primary key autoincrement, " +
//"snap_uuid text, " +
"snap_date integer, " +
"snap_title text) ");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//
}
public long insertSnap(Snap snap){
ContentValues cv = new ContentValues();
//cv.put(COLUMN_SNAP_UUID, snap.getUniqueId().toString());
cv.put(COLUMN_SNAP_DATE, snap.getDate().getTime());
cv.put(COLUMN_SNAP_TITLE, "");
return getWritableDatabase().insert(TABLE_SNAP, null, cv);
}
public boolean updateTitle(long snapId, String text)
{
ContentValues cv = new ContentValues();
cv.put(COLUMN_SNAP_ID, snapId);
cv.put(COLUMN_SNAP_TITLE, text);
int i= getWritableDatabase().update(TABLE_SNAP, cv, COLUMN_SNAP_ID+ "=" + snapId, null);
return i>0;
}
public SnapCursor querySnap(long id) {
Cursor wrapped = getReadableDatabase().query(TABLE_SNAP,
null, // all columns
COLUMN_SNAP_ID + " = ?", // look for a run ID
new String[]{ String.valueOf(id) }, // with this value
null, // group by
null, // order by
null, // having
"1"); // limit 1 row
return new SnapCursor(wrapped);
}
public SnapCursor querySnaps() {
// equivalent to "select * from run order by start_date asc"
Cursor wrapped = getReadableDatabase().query(TABLE_SNAP,
null, null, null, null, null, COLUMN_SNAP_DATE + " asc");
return new SnapCursor(wrapped);
}
public static class SnapCursor extends CursorWrapper{
public SnapCursor(Cursor c){
super(c);
}
public Snap getSnap() {
if (isBeforeFirst() || isAfterLast())
return null;
Snap s = new Snap();
s.setId(getLong(getColumnIndex(COLUMN_SNAP_ID)));
//s.setUniqueId(UUID(getString(getColumnIndex(COLUMN_SNAP_UUID))));
s.setDate(new Date(getLong(getColumnIndex(COLUMN_SNAP_DATE))));
s.setTitle(getString(getColumnIndex(COLUMN_SNAP_TITLE)));
return s;
}
}
}
This file links the fragments to the DatabaseHelper.
public class SnapLab {
private static SnapLab sSnapLab;
private Context mAppContext;
private SnapDatabaseHelper mHelper;
// private constructor
private SnapLab(Context appContext){
mAppContext = appContext;
mHelper = new SnapDatabaseHelper(mAppContext);
}
public static SnapLab get(Context c){
if(sSnapLab == null){
sSnapLab = new SnapLab(c.getApplicationContext());
}
return sSnapLab;
}
public Snap insertSnap() {
Snap s = new Snap();
s.setId(mHelper.insertSnap(s));
return s;
}
public boolean updateTitle(long snapId, String text){
return mHelper.updateTitle(snapId, text);
}
public SnapCursor querySnaps() {
return mHelper.querySnaps();
}
public Snap getSnap(long id) {
Snap s = null;
SnapCursor cursor = mHelper.querySnap(id);
cursor.moveToFirst();
// if we got a row, get a run
if (!cursor.isAfterLast())
s = cursor.getSnap();
cursor.close();
return s;
}
}
Here is the fragment with the EditText field
public class EditPageFragment extends Fragment {
private static final String TAG = "EditPageFragment";
public static final String EXTRA_SNAP_ID = "SNAP_ID";
private SnapLab mSnapLab;
private Snap mSnap;
private SnapDatabaseHelper mHelper;
private EditText mSnapText;
private Button mUploadButton;
private TextView mDateText;
private Long snapId;
public static EditPageFragment newInstance(Long snapId){
Bundle args = new Bundle();
args.putLong(EXTRA_SNAP_ID, snapId);
EditPageFragment fragment = new EditPageFragment();
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mSnapLab = SnapLab.get(getActivity());
Bundle args = getArguments();
if (args != null){
long snapId = args.getLong(EXTRA_SNAP_ID, -1);
if (snapId != -1){
mSnap = mSnapLab.getSnap(snapId);
}
}
mSnap = new Snap();
mSnap = mSnapLab.insertSnap();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState){
View v = inflater.inflate(R.layout.edit_fragment, parent, false);
mDateText = (TextView)v.findViewById(R.id.edit_dateText);
mDateText.setText(mSnap.getDate().toString());
mSnapText = (EditText)v.findViewById(R.id.edit_snapText);
mSnapText.addTextChangedListener(new TextWatcher(){
#Override
public void afterTextChanged(Editable s) {
//leave blank for now
}
#Override
public void beforeTextChanged(CharSequence c, int start, int count,
int after) {
//leave blank for now
}
#Override
public void onTextChanged(CharSequence c, int start, int before,
int count) {
mSnap.setTitle(c.toString());
mSnapLab.updateTitle(snapId, c.toString());
Log.i(TAG, "text saved");
}
});
return v;
}
}
The import bits of code are the updateTitle() functions. What could I be doing wrong. Do you have a suggestion on how to better update a database. Everything works great except for the updating of the title. I appreciate any bit of help.
Looks like snapId is not assigned
private Long snapId; //field
few lines later
long snapId = args.getLong(EXTRA_SNAP_ID, -1); //local variable
few lines later
mSnapLab.updateTitle(snapId, c.toString()); //field
Please add stacktrace next time.

Categories

Resources