My development environment is jdk1.7, android 4.2, and eclipse 4.2.1 (Juno). It's the first Android application I created. Precisely, I created it referring to the sample that Android provides in the Android SDK and the Notepad lesson that Android developers have provided. For more details about the Notepad lesson, here's the link.
** 2013/4/1 - For those who are having bugs on return from NoteEdit activity to NoteList activity. Tried to download again for update to new version. I have updated the latest version to link. Before debugging the latest version to Android, the old version must be uninstalled first.
Here's how it looks like:
Left side is the note list and the right side is the note editor. It's not really difficult to write this program if you have certain experience to Android apps program. Basically, what I have used on this program mostly is fundamentally things like intent, SQLite database, options menu, list activity and of course life-cycle. If you are already tried the Notepad exercise that I mentioned above, basically you can understand what I'm writing about.
I'm going to show what I have written on this project.
Here's the Java code for note list:
package com.example.note;
import android.os.Bundle;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import android.widget.AdapterView.AdapterContextMenuInfo;
public class NoteList extends ListActivity {
private static final int DELETE_ID = Menu.FIRST;
private int mNoteNumber = 1;
private NotesDbAdapter mDbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.notelist);
mDbHelper = new NotesDbAdapter (this);
mDbHelper.open();
fillData();
registerForContextMenu(getListView());
Button addnote = (Button)findViewById(R.id.addnotebutton);
addnote.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
createNote();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.notelist_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_about:
AlertDialog.Builder dialog = new AlertDialog.Builder(NoteList.this);
dialog.setTitle("About");
dialog.setMessage("Hello! I'm Heng, the creator of this application.
This application is created based on learning." +
" Used it on trading or any others activity that is related
to business is strictly forbidden."
+"If there is any bug is found please freely e-mail me. "+
"\n\tedisonthk@gmail.com"
);
dialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
dialog.show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void createNote() {
Intent i = new Intent(this, NoteEdit.class);
startActivityForResult(i, ACTIVITY_CREATE);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Intent i = new Intent(this, NoteEdit.class);
i.putExtra(NotesDbAdapter.KEY_ROWID, id);
startActivityForResult(i, ACTIVITY_EDIT);
}
private void fillData() {
Cursor notesCursor = mDbHelper.fetchAllNotes();
startManagingCursor(notesCursor);
String[] from = new String[] { NotesDbAdapter.KEY_TITLE ,NotesDbAdapter.KEY_DATE};
int[] to = new int[] { R.id.text1 ,R.id.date_row};
SimpleCursorAdapter notes =
new SimpleCursorAdapter(this, R.layout.notes_row, notesCursor, from, to);
setListAdapter(notes);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(0, DELETE_ID, 0, R.string.menu_delete);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
switch(item.getItemId()) {
case DELETE_ID:
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
mDbHelper.deleteNote(info.id);
fillData();
return true;
}
return super.onContextItemSelected(item);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
fillData();
}
}
Here's the XML code for note list:
="1.0"="utf-8"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/notelist">
<TextView
android:id="@+id/textViewTop"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:paddingBottom="35dp" />
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/btmLayout"
android:layout_below="@+id/textViewTop"
android:background="#FFF9C8"
android:divider="#D3D3D3"
android:dividerHeight="1sp"
android:footerDividersEnabled="true" />
<LinearLayout
android:id="@+id/btmLayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:layout_alignParentBottom="true"
android:layout_marginBottom="3dp"
android:layout_marginTop="10dp">
<Button
android:id="@+id/addnotebutton"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:background="@drawable/create_note" />
</LinearLayout>
</RelativeLayout>
The name written on background for instance notelist and create_note
is the png image files. You can download it at the top of this blog.
Here's the XML code for list row:
="1.0"="utf-8"
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginLeft="10sp"
android:textSize="25sp"
android:hint="@string/no_title"
android:id="@+id/text1"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize = "25sp"
android:id="@+id/date_row"
android:layout_alignParentRight="true"
android:layout_marginRight="10sp"/>
</RelativeLayout>
Here's the Java code for note edit:
package com.example.note;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class NoteEdit extends Activity {
public static int numTitle = 1;
public static String curDate = "";
public static String curText = "";
private EditText mTitleText;
private EditText mBodyText;
private TextView mDateText;
private Long mRowId;
private Cursor note;
private NotesDbAdapter mDbHelper;
public static class LineEditText extends EditText{
public LineEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(Color.BLUE);
}
private Rect mRect;
private Paint mPaint;
@Override
protected void onDraw(Canvas canvas) {
int height = getHeight();
int line_height = getLineHeight();
int count = height / line_height;
if (getLineCount() > count)
count = getLineCount();
Rect r = mRect;
Paint paint = mPaint;
int baseline = getLineBounds(0, r);
for (int i = 0; i < count; i++) {
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
baseline += getLineHeight();
super.onDraw(canvas);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDbHelper = new NotesDbAdapter(this);
mDbHelper.open();
setContentView(R.layout.note_edit);
setTitle(R.string.app_name);
mTitleText = (EditText) findViewById(R.id.title);
mBodyText = (EditText) findViewById(R.id.body);
mDateText = (TextView) findViewById(R.id.notelist_date);
long msTime = System.currentTimeMillis();
Date curDateTime = new Date(msTime);
SimpleDateFormat formatter = new SimpleDateFormat("d'/'M'/'y");
curDate = formatter.format(curDateTime);
mDateText.setText(""+curDate);
mRowId = (savedInstanceState == null) ? null :
(Long) savedInstanceState.getSerializable(NotesDbAdapter.KEY_ROWID);
if (mRowId == null) {
Bundle extras = getIntent().getExtras();
mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
: null;
}
populateFields();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
saveState();
outState.putSerializable(NotesDbAdapter.KEY_ROWID, mRowId);
}
@Override
protected void onPause() {
super.onPause();
saveState();
}
@Override
protected void onResume() {
super.onResume();
populateFields();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.noteedit_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_about:
AlertDialog.Builder dialog = new AlertDialog.Builder(NoteEdit.this);
dialog.setTitle("About");
dialog.setMessage("Hello! I'm Heng, the creator of this application.
This application is created for learning." +
" Using it on trading or any others activity that is related
to business is strictly forbidden."
+"If there is any bug is found please freely e-mail me. "+
"\n\tedisonthk@gmail.com"
);
dialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
dialog.show();
return true;
case R.id.menu_delete:
if(note != null){
note.close();
note = null;
}
if(mRowId != null){
mDbHelper.deleteNote(mRowId);
}
finish();
return true;
case R.id.menu_save:
saveState();
finish();
default:
return super.onOptionsItemSelected(item);
}
}
private void saveState() {
String title = mTitleText.getText().toString();
String body = mBodyText.getText().toString();
if(mRowId == null){
long id = mDbHelper.createNote(title, body, curDate);
if(id > 0){ mRowId = id; }
}else{
mDbHelper.updateNote(mRowId, title, body, curDate);
}
}
private void populateFields() {
if (mRowId != null) {
note = mDbHelper.fetchNote(mRowId);
startManagingCursor(note);
mTitleText.setText(note.getString(
note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
mBodyText.setText(note.getString(
note.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
curText = note.getString(
note.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY));
}
}
}
Here's the XML code for note edit:
="1.0"="utf-8"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFF9C8">
<RelativeLayout
android:id="@+id/toplayout"
android:background="@drawable/notetop"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:paddingBottom="5dp"
android:paddingLeft="5dp"
android:paddingTop="5dp" >
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="@string/title"
android:id="@+id/title_text1" />
<EditText android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:hint="@string/no_title"
android:layout_toRightOf="@+id/title_text1"
android:background="@android:color/transparent"
android:layout_marginLeft="5dp"
android:singleLine="true"
android:imeOptions="actionNext"/>
<TextView
android:id="@+id/notelist_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:paddingRight="10sp"
android:textSize="18sp" />
</RelativeLayout>
<view
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/body"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/toplayout"
class="com.example.note.NoteEdit$LineEditText"
android:background="@android:color/transparent"
android:capitalize="sentences"
android:fadingEdge="vertical"
android:gravity="top"
android:padding="5dp"
android:scrollbars="vertical"
android:textSize="22sp" />
</RelativeLayout>
Here's the Java code for note adapters that is provided by Android developers. It's almost the same but I did some edit on it that I added the one more parameter for date.
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class NotesDbAdapter {
public static final String KEY_TITLE = "title";
public static final String KEY_DATE = "date";
public static final String KEY_BODY = "body";
public static final String KEY_ROWID = "_id";
private static final String TAG = "NotesDbAdapter";
private DatabaseHelper mDbHelper;
private SQLiteDatabase mDb;
private static final String DATABASE_CREATE =
"create table notes (_id integer primary key autoincrement, "
+ "title text not null, body text not null, date text not null);";
private static final String DATABASE_NAME = "data";
private static final String DATABASE_TABLE = "notes";
private static final int DATABASE_VERSION = 2;
private final Context mCtx;
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS notes");
onCreate(db);
}
}
public NotesDbAdapter(Context ctx) {
this.mCtx = ctx;
}
public NotesDbAdapter open() throws SQLException {
mDbHelper = new DatabaseHelper(mCtx);
mDb = mDbHelper.getWritableDatabase();
return this;
}
public void close() {
mDbHelper.close();
}
public long createNote(String title, String body, String date) {
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_TITLE, title);
initialValues.put(KEY_BODY, body);
initialValues.put(KEY_DATE, date);
return mDb.insert(DATABASE_TABLE, null, initialValues);
}
public boolean deleteNote(long rowId) {
return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
}
public Cursor fetchAllNotes() {
return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE,
KEY_BODY,KEY_DATE}, null, null, null, null, null);
}
public Cursor fetchNote(long rowId) throws SQLException {
Cursor mCursor =
mDb.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
KEY_TITLE, KEY_BODY,KEY_DATE}, KEY_ROWID + "=" + rowId, null,
null, null, null, null);
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
public boolean updateNote(long rowId, String title, String body,String date) {
ContentValues args = new ContentValues();
args.put(KEY_TITLE, title);
args.put(KEY_BODY, body);
args.put(KEY_DATE, date);
return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
}
}
Here's the manisfest code for this project:
="1.0"="utf-8"
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.note"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="16" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.note.NoteList"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.example.note
.NoteEdit"
android:label="@string/app_name"
android:windowSoftInputMode="adjustUnspecified"/>
</application>
</manifest>
The code above shown is not completely all of this project. There are some files I didn't put on here as I want to show the main files of this project.