Create a Notepad and To-do list combined app in Android
DownloadKeywords: TabHost ListView ExpandableListActivity SQLite SimpleCursorAdapter SimpleCursorTreeAdapter PreferenceActivity AlertDialog ContextMenu Spinner Gallery ScrollView Camera
Contents- Overview
- Create a new Eclipse Android project
- Define the Data model
- The Android Manifest file
- The Application class
- Develop the UI
- The Browse tab
- The Manage tab
- The Preferences screen
- The Alert Dialog
- The Context Menu
- The Edit Page
- Image Capture
6. Develop the UI
The main screen is implemented using a TabHost with a custom TabIndicator (without the icon).Here is the main.xml layout file which is set as the content view for this screen.
<?xml version="1.0" encoding="utf-8"?> <TabHost xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/tabhost" android:layout_width="fill_parent" android:layout_height="fill_parent" > <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TabWidget android:id="@android:id/tabs" android:layout_width="fill_parent" android:layout_height="wrap_content" > </TabWidget> <View android:layout_width="fill_parent" android:layout_height="2dip" android:background="#696969" /> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="fill_parent" android:layout_height="0dip" android:background="@drawable/pad_bg_repeat" android:layout_weight="1" > </FrameLayout> </LinearLayout> </TabHost>The background of tabcontent is set as a bitmap drawable with tile mode set to repeat.
<?xml version="1.0" encoding="utf-8"?> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" android:src="@drawable/pattern8_pattern_58c" android:tileMode="repeat" />The MainActivity extends TabActivity and the following lines of code sets up the tabs in onCreate() method.
TabHost tabHost = getTabHost(); TabHost.TabSpec spec; Intent intent; tabHost.getTabWidget().setDividerDrawable(R.drawable.tab_divider); // Browse intent = new Intent().setClass(this, BrowseActivity.class); spec = tabHost.newTabSpec(TAB_BROWSE) .setIndicator(createTabIndicator(getResources().getString(R.string.browse))) .setContent(intent); tabHost.addTab(spec); // Manage intent = new Intent().setClass(this, ManageActivity.class); spec = tabHost.newTabSpec(TAB_MANAGE) .setIndicator(createTabIndicator(getResources().getString(R.string.manage))) .setContent(intent); tabHost.addTab(spec);The createTabIndicator() method creates a custom view which is set as indicator for the tab.
private View createTabIndicator(String label) { View tabIndicator = getLayoutInflater().inflate(R.layout.tabindicator, null); TextView tv = (TextView) tabIndicator.findViewById(R.id.label); tv.setText(label); return tabIndicator; }The tabindicator.xml is a simple layout file with a TextView.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/tab_bg_selector" android:gravity="center_horizontal" > <TextView android:id="@+id/label" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>The active or inactive state of the tab indicator determines its background through a selector drawable.
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="false" android:drawable="@drawable/tab_unselected" /> <item android:state_selected="true" android:drawable="@drawable/tab_selected" /> <item android:drawable="@android:color/transparent" /> </selector>The tab_selected and tab_unselected are shape drawables with different gradient.
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <gradient android:startColor="#A8A8A8" android:centerColor="#7F7F7F" android:endColor="#696969" android:angle="-90" /> </shape>The content of each tab is set to be an intent. We will create two Activity for this purpose and add the folowing lines to the <application> tag in AndroidManifest.xml.
<activity android:name=".BrowseActivity"></activity> <activity android:name=".ManageActivity"></activity>
7. The Browse tab
The Browse tab is implemented using a ListView and a SimpleCursorAdapter used to fetch the list items. Here is a brief outline of BrowseActivity class.public class BrowseActivity extends Activity implements AdapterView.OnItemClickListener { private ListView noteList; private TextView emptyView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tab_browse); noteList = (ListView) findViewById(R.id.note_list); emptyView = (TextView) findViewById(android.R.id.empty); noteList.setOnItemClickListener(this); noteList.setEmptyView(emptyView); } @Override protected void onResume() { super.onResume(); Cursor c = Note.list(SmartPad.db); startManagingCursor(c); SimpleCursorAdapter adapter = new SimpleCursorAdapter( this, R.layout.row_note, c, new String[]{Note.COL_TITLE, Note.COL_TYPE, Note.COL_MODIFIEDTIME}, new int[]{android.R.id.text1, R.id.icon, android.R.id.text2}); noteList.setAdapter(adapter); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // TODO open the note } }Each list item is rendered using the layout row_note.xml.
Here is an excerpt from the file.
<RelativeLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@drawable/note_bg" > <ImageView android:id="@+id/icon" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@android:id/text1" android:layout_toRightOf="@id/icon" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textColor="@android:color/white" android:textSize="18sp" /> <View android:id="@+id/line" android:layout_toRightOf="@id/icon" android:layout_below="@android:id/text1" android:layout_width="fill_parent" android:layout_height="1dip" android:background="#777777" /> <TextView android:id="@android:id/text2" android:layout_alignParentRight="true" android:layout_below="@id/line" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#555555" android:textSize="10sp" /> </RelativeLayout>The translucent and round corner effect is obtained by setting the background using note_bg.xml shape drawable.
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <solid android:color="#99ffffff" /> <corners android:radius="4dp" /> <padding android:left="10dp" android:top="10dp" android:right="10dp" android:bottom="10dp" /> </shape>We have written a separate article which discusses exclusively about how to create some awesome Android UI effects.
The icon next to the title is set programatically by specifying a ViewBinder to the adapter. Here is the lines of code which we need to add in onResume() after creating the SimpleCursorAdapter.
adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() { @Override public boolean setViewValue(View view, Cursor cursor, int columnIndex) { String value = cursor.getString(columnIndex); switch (view.getId()) { case R.id.icon: int icon = 0; if (Note.BASIC.equals(value)) icon = R.drawable.document; else if (Note.CHECKLIST.equals(value)) icon = R.drawable.checklist; else if (Note.SNAPSHOT.equals(value)) icon = R.drawable.pictures; ((ImageView)view).setImageResource(icon); return true; case android.R.id.text2: ((TextView)view).setText("Last edited on " + value); return true; } return false; } });The bottom portion of the screen is a LinearLayout with two ImageButton. The background of the LinearLayout is a bitmap drawable with tile mode set to repeat whereas the background of the ImageButton is an oval shape drawable.
Here is some relevant portion of the tab_browse.xml layout file.
<ListView android:id="@+id/note_list" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" android:cacheColorHint="#00000000" android:divider="@android:color/transparent" android:drawSelectorOnTop="false" /> <TextView android:id="@android:id/empty" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" android:gravity="center" android:text="@string/no_data" /> <View android:layout_width="fill_parent" android:layout_height="2dip" android:background="#333333" /> <LinearLayout android:id="@+id/menubar" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:background="@drawable/bar_bg_repeat" > <ImageButton android:id="@+id/new_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/btn_bg" android:src="@drawable/new_doc" /> <ImageButton android:id="@+id/sort_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/btn_bg" android:src="@drawable/sort" /> </LinearLayout>
We have learned some useful UI stuff in this chapter which can be reused in developing other screens. Next, we will develop the Manage tab.