Android development primer: Working with Sliding Drawers in Android – Part I

You have to agree that the Sliding Drawer feature of Android is a pretty neat and handy tool to add some text – usually directions for use, help or other important details. Here’s what a Sliding Drawer looks like. The bold black lines are where some text will appear.

sd_close         open

Remember that a Sliding Drawer option is provided in the Palette in the Graphical Layout tab of any xml under the Composite section. However, I like to use a custom Sliding Drawer. It gives me absolute control over what my Sliding Drawer handle looks like, what orientation is it drawn into, what view I want it to have ( a custom one if needed) etc.

So below I provide a sample source code of how to code a Sliding Drawer into you layout. However, I would strongly recommend trying out the Sliding Drawer element from the Palette first, making a few changes to see how it reflects in the layout and then coming back here to look at the source code. This will help you get familiar with the xml.

There are a few things worth noting here :-

  • I have used a frame layout rather than Linear/Relative Layout. I particularly am much more comfortable with it while working with Sliding Drawers. You can read about Frame Layouts here.
  • The Sliding Drawer has attributes namely – android:handle and android:content. Notice that an ImageView is present that has the same id as the value for android:handle and a ScrollView with the same id as the value of android:content.
  • The android:handle attribute defines the handle of your sliding drawer.
  • The android:content attribute defines what your Sliding Drawer holds.
  • Notice that the ImageView which is the handle of the Sliding Drawer has an src attribute of @drawable/help_handle. Recollect from my earlier posts, to use an icon you must paste it in the drawable-ldpi/mdpi/hdpi/xhdpi/xxhdpi folder. I have an image by the name help_handle.png in my drawable folder.
  • Remember that this sort of reference is done in the xml. If you choose to refer to it from your Java code you must use R.drawable.<youriconname>.
  • Notice that the Sliding Drawer has a different handle (Image that works as a handle) when it is closed than from what it has when it is open. If you use the code I provided you with, you will not see this happening. I will discuss how to do it in detail in a later post. For now you can use the image here. Paste it in <eclipseworkspace>/<yourprojectname>/res/drawable-ldpi/mdpi/hdpi/xhdpi/xxhdpi.

Code it. Execute it on the emulator. Once it runs properly, start experimenting!! 🙂

Time Picker in Android

Just like Number Picker provides a neat way to select values from a given range, Android also provides a Time Picker tool. This, as the name suggest is used to pick time. Now, you can use the Time Picker the way you use Number Picker, but this post shows how to use it with the help of a TimePickerDialog.

  • Create a layout for an activity and insert a button. This button will trigger a dialog which will contain the TimePicker.
  • Define the button in the Java file.
  • Set the onClick() method for the button.[java]
    b.setOnClickListener(new OnClickListener() {

    @Override
    public void onClick(View v) {
    showDialog(9999);
    }
    });
    [/java]

  • Create a new method onCreateDialog(). This comes into play when we call a Dialog to appear quite like the one we did above.
    [java]
    protected Dialog onCreateDialog(int id){
    switch(id){
    case 9999:
    return new TimePickerDialog(this, timePickerListener, Calendar.HOUR_OF_DAY, Calendar.MINUTE, false);
    }
    return null;
    }
    [/java]
  • We are setting the current time of the device as the default time every time the dialog appears.
  • Now, we need to define what happens when a time is selected. Here, I am just creating a Toast to show the selected time. Remember that you can do whatever you want. We will override the method onTimeSetListener()
    [java]
    private TimePickerDialog.OnTimeSetListener timePickerListener = new TimePickerDialog.OnTimeSetListener() {

    @Override
    public void onTimeSet(TimePicker view, int newhour, int newminute) {
    Toast.makeText(getApplicationContext(), newhour+":"+newminute, Toast.LENGTH_LONG).show();
    }
    };
    [/java]

  • Save and run the application on an emulator or device.

Tpick1  Tpick2

Remember that the Time is always set in 24 Hour format.

COMPLETE SOURCE CODE

MainActivity.java

[java]
package com.nero.mysecondapp;

import java.util.Calendar;

import android.os.Bundle;
import android.app.Activity;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.NumberPicker;
import android.widget.TimePicker;
import android.widget.Toast;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button b = (Button) findViewById(R.id.button1);
b.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
showDialog(9999);
}
});
}

protected Dialog onCreateDialog(int id){
switch(id){
case 9999:
return new TimePickerDialog(this, timePickerListener, Calendar.HOUR_OF_DAY, Calendar.MINUTE, false);
}
return null;
}

private TimePickerDialog.OnTimeSetListener timePickerListener = new TimePickerDialog.OnTimeSetListener() {

@Override
public void onTimeSet(TimePicker view, int newhour, int newminute) {
Toast.makeText(getApplicationContext(), newhour+":"+newminute, Toast.LENGTH_LONG).show();
}
};

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

}

[/java]

Fixed: USB Drive Read Only in Ubuntu 13.04

Problem : USB disk/storage devices are auto loaded as Read Only in Ubuntu. When attempting write operation it gives the message like “Read only file system…”.

Reason: Unknown, looks like a bug in the usb disk handling in Ubuntu.

Fix: Run this command to remount as writable disk –

(Pressing tab after /media/<username>/ can help you find the  disk to be provided.)

Number Picker in Android

Often times in our applications ,we are going to need to let the user choose numeric values. Of course we can design a custom Dialog box with an Edit Text and let the user type in the values. However, Android provides a very neat tool to do this with style and ease, the NumberPicker tool.

Complete Source Code is at the bottom.

  • Create a new Project and remember to set the minimum API version to at least 11 i.e. Honeycomb. This is because the support for the Number Picker has been introduces since API 11.
  • Create a layout for an activity. Drag and drop a Number Picker and a button from the palette on the left.
  • Switch over to the Java file and define the button and the Number Picker
    [java]
    Button b = (Button) findViewById(R.id.button1);
    final NumberPicker np = (NumberPicker) findViewById(R.id.numberPicker1);
    [/java]
  • Remember to make the Number Picker final.
  • Set the minimum and maximum values. This defines the range in which the user is supposed to enter the values.
    [java]
    np.setMinValue(1);
    np.setMaxValue(20);
    [/java]
  • Set the onClickListener() attribute for the button to display the value currently selected. We use the getValue() method for retrieving the current value.
  • Save it and execute in a device or an emulator that is running Android Honeycomb or higher.

NPick

COMPLETE SOURCE CODE

MainActivity.java

[java]
package com.nero.mysecondapp;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.NumberPicker;
import android.widget.Toast;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button b = (Button) findViewById(R.id.button1);
final NumberPicker np = (NumberPicker) findViewById(R.id.numberPicker1);
np.setMinValue(1);
np.setMaxValue(20);
b.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), np.getValue()+"", Toast.LENGTH_SHORT).show();
}
});
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
[/java]

Alarm Manager in Android

AlarmManager as the name suggests is used to schedule actions in an Android system. Do not be misled by the name that it is used to schedule the alarms. It can be used to schedule alarms, notifications, broadcasts etc.

In this post I will show you how to schedule a notification from your application. We will make use of Alarm Manager through which we will send a broadcast, and a Broadcast Receiver using which we will receive the broadcast and use it to create a notification. Complete Source Code is at the bottom.

  • Set up a layout for your activity.
  • Switch to the Java file and create a global Alarm Manager object.
  • Define the object inside the onCreate() method.
    [java]
    am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    [/java]
  • Create a new method setAlarm(). Write the following lines inside it.
    [java]
    Intent intent = new Intent(this, AlarmReceiver.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    am.cancel(pendingIntent);
    GregorianCalendar alarmtime = new GregorianCalendar();
    alarmtime.set(GregorianCalendar.HOUR_OF_DAY, 6);
    alarmtime.set(GregorianCalendar.MINUTE, 0);
    alarmtime.set(GregorianCalendar.SECOND, 0);
    alarmtime.set(GregorianCalendar.MILLISECOND, 0);
    if(alarmtime.before(new GregorianCalendar()))alarmtime.add(GregorianCalendar.DAY_OF_MONTH, 1);
    am.set(AlarmManager.RTC_WAKEUP, alarmtime.getTimeInMillis(), pendingIntent);
    [/java]
  • Call this method inside the onCreate() method.
  • Create a new Java class that extends BroadcastReceiver class. Name it AlarmReceiver.java. Note that we have already used this name in the method setAlarm() above.
  • Inside the onReceive() method write the following lines. I will explain these line in a moment.
    [java]
    notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    Intent i = new Intent(context, Second.class);
    PendingIntent pi = PendingIntent.getActivity(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);

    AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    PendingIntent pi2 = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + AlarmManager.INTERVAL_DAY, pi2);

    CharSequence from = "Nero";
    CharSequence message = "Test Notification";
    notification = new Notification(R.drawable.ic_launcher,"Attention",System.currentTimeMillis());
    notification.setLatestEventInfo(context, from, message, pi);
    notification.defaults|= Notification.DEFAULT_SOUND;
    notification.defaults|= Notification.DEFAULT_LIGHTS;
    notification.defaults|= Notification.DEFAULT_VIBRATE;
    notificationManager.notify(1, notification);
    return;
    [/java]

  • Add an entry in your manifest file
  • Save it and execute it on a device or an emulator. Remember that we have scheduled a notification ar 6 am. You might need to change the date and time to see that the notification appears.

Understanding the Code

  • GregorianCalendar is a form of calendar present in the Android system. You can use Calendar too. Google it.
  • PendingIntent – It should be quite enough for you to know that we are creating an Intent that has to be fulfilled. The Broadcast receiver does that.
  • RTC_WAKEUP is used so that if your device is switched off, it starts up at the scheduled time and performs the action.
  • In the AlarmReceiver we are creating another PendingIntent that creates the intent for the alarm to fire the next day exactly at the same time.
  • ALARM_SERVICE is a service of Android system that we are calling inside the Broadcast Receiver.
  • FLAG_CANCEL_CURRENT is used to cancel any outstanding Intents. This is done so that at a time only a single Intent is active.
  • notification.defaults|= Notification.DEFAULT_SOUND – This provides for the Sound attributes of the notification. Same is the case with vibrate and Lights.

COMPLETE SOURCE CODE

AlarmReceiver.java

[java]
package com.nero.myfirstapp;

import java.text.SimpleDateFormat;
import java.util.Date;

import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.util.Log;

public class AlarmReceiver extends BroadcastReceiver {

NotificationManager notificationManager;
Notification notification;
@Override
public void onReceive(Context context, Intent intent) {
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent i = new Intent(context, Second.class);
PendingIntent pi = PendingIntent.getActivity(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);

AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pi2 = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + AlarmManager.INTERVAL_DAY, pi2);

CharSequence from = "Nero";
CharSequence message = "Test Notification";
notification = new Notification(R.drawable.ic_launcher,"Attention",System.currentTimeMillis());
notification.setLatestEventInfo(context, from, message, pi);
notification.defaults|= Notification.DEFAULT_SOUND;
notification.defaults|= Notification.DEFAULT_LIGHTS;
notification.defaults|= Notification.DEFAULT_VIBRATE;
notificationManager.notify(1, notification);
return;
}
}

[/java]

MainActivity.java

[java]
package com.nero.myfirstapp;

import java.util.ArrayList;
import java.util.GregorianCalendar;

import com.psyduck.myfirstapp.AlarmReceiver;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

public class Main extends Activity {
AlarmManager am;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
setAlarm();
}

public void setAlarm(){
Intent intent = new Intent(this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
am.cancel(pendingIntent);
GregorianCalendar alarmtime = new GregorianCalendar();
alarmtime.set(GregorianCalendar.HOUR_OF_DAY, 6);
alarmtime.set(GregorianCalendar.MINUTE, 0);
alarmtime.set(GregorianCalendar.SECOND, 0);
alarmtime.set(GregorianCalendar.MILLISECOND, 0);
if(alarmtime.before(new GregorianCalendar()))alarmtime.add(GregorianCalendar.DAY_OF_MONTH, 1);
am.set(AlarmManager.RTC_WAKEUP, alarmtime.getTimeInMillis(), pendingIntent);

}
}
[/java]

Custom Data Adapters with Checked List Views – Part II

In the last post, I showed you how to make the front end for the List View with check box using Custom Data Adapters. This post will deal with the coding of the custom Data Adapters. This one coupled with the last post, will complete the procedure.

The custom Data Adapter class is named Adapter.java and extends the ArrayAdapter<String> class. Quite obviously, this is because if you recall the lost post, you can see that I’ve used an ArrayList to populate the List View. Had I used a database, we could have used a CursorAdapter.

This code is complicated and hence I provide the explanations below.

Understanding the Code

  • A HashMap is a Java class that allows for key-value pair mapping. We are going to map each item with a boolean value that identifies if it is checked or unchecked.
  • We initialize all the items to unchecked in the constructor.
  • The toggle() function serves the purpose of checking or unchecking the item once it is clicked, depending on it’s previous state.
  • getCheckedItemPosition() serves the purpose of returning the indices of items that are checked.
  • getCheckedItems() serves the purpose of returning the items that are checked.
  • Remember that all these methods make use of the basic functions of the HashMap class of Java. If you face any difficulty understanding them, I suggest you read the Java HashMap documentation here.

CDA

COMPLETE SOURCE CODE

row_item.xml

activity_main.xml

Adapter.java

[java]
package com.nero.myfirstapp;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import android.R.color;
import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckedTextView;
import android.widget.Toast;

public class Adapter extends ArrayAdapter {

HashMap<Integer, Boolean> checked = new HashMap<Integer, Boolean>();
ArrayListweekdays;
Context context;
private int[] colors = new int[] { Color.parseColor("#D0D0D0"), Color.parseColor("#D8D8D8") };
public Adapter(Context context, int resource, int textViewResourceId, ArrayList weekdays) {
super(context, resource, textViewResourceId, weekdays);
this.context=context;
this.weekdays=weekdays;
for(int i=0;i<weekdays.size();i++)checked.put(i, false);
}

public Adapter(Context context, int resource, int textViewResourceId, ArrayList weekdays, ArrayListoldweekdays) {
super(context, resource, textViewResourceId, weekdays);
this.context=context;
this.weekdays=weekdays;
for(int i=0;i<weekdays.size();i++)checked.put(i, false);
for(int i=0;i<oldweekdays.size();i++)checked.put(oldweekdays.get(i),true);
}

public void toggle(int position){
if(checked.get(position))checked.put(position, false);
else checked.put(position, true);
notifyDataSetChanged();
}

public ArrayList getCheckedItemPosition(){
ArrayListcheck = new ArrayList();
for(int i=0;i<checked.size();i++){
if(checked.get(i))check.add(i);
}
return check;
}

public ArrayList getCheckedItems(){
ArrayList check = new ArrayList();
for(int i=0;i<checked.size();i++){
if(checked.get(i))check.add(weekdays.get(i));
}
return check;
}

public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;

if(row == null){
LayoutInflater vi = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = vi.inflate(R.layout.row_item, null);
}

CheckedTextView checkedTextView = (CheckedTextView)row.findViewById(R.id.checkedtextview);
checkedTextView.setText(weekdays.get(position));
row.setBackgroundColor(colors[position % colors.length]);
return row;
}
}

[/java]

MainActivity.java

[java]
package com.nero.myfirstapp;

import java.util.ArrayList;

import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

public class Main extends Activity {
ArrayList weekdays = new ArrayList();
ArrayList ret = new ArrayList();
ListView lv;
EditText coursename;
Button addcourse;
Adapter adapter;
String c;
long courseid;

public void initialize(){
weekdays.add("Monday");weekdays.add("Tuesday");weekdays.add("Wednesday");
weekdays.add("Thursday");weekdays.add("Friday");weekdays.add("Saturday");
}

public void getoldweekdays(long id){
ret.clear();
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ret.clear();
initialize();
lv = (ListView) findViewById(R.id.listView1);
coursename = (EditText) findViewById(R.id.coursenameet);
addcourse = (Button) findViewById(R.id.addcourse);
lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

courseid = getIntent().getLongExtra("courseid", -1);
adapter = new Adapter(this, R.layout.row_item, R.id.checkedtextview, weekdays);
lv.setAdapter(adapter);
for(int i=0;i<ret.size();i++)lv.setItemChecked(ret.get(i), true);

lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
adapter.toggle(position);
}
});

addcourse.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {
ArrayList checkeditem = adapter.getCheckedItems();
finish();
}
});
}
}
[/java]

Custom Data Adapters with Checked List Views – Part I

We have been using ArrayAdapter and SimpleCursorAdapter while populating our List View with elements from an array and a database respectively. It is worth knowing that both these classes are subclasses of the DataAdapter class. The DataAdapter class is the primary class that allows for such adaptation and display of data.
Also, we have used simple List Views. On many occasions you would have seen List Views with checkboxes, that allow for multiple options to be selected.

This post along with the next, will show you how to do this. We will be using a custom Data Adapter with a List View containing checkbox. Remember that in Part I of this post I will show you how to create the Front End of the Activity, i.e. how to program the layout and the corresponding Java file for this activity. In the next post I will show you how to create the Custom Adapter in order for the Activity to work as decided. This can be a little complicated and is the sole reason I will be breaking it into two different posts. Complete Source Code is at the bottom.

  • Create a layout of the type CheckedTextView and paste the code below, there.
  • Create a layout for the Activity and insert 1 Edit Text and 7 Text Views in it. Also insert a button at the bottom. We will have the weekdays as our List View Items.
  • The button will trigger whatever you want to do with the selected items – whether you want to switch over to the next activity or finish the activity etc.
  • We will be creating something resembling a course schedule.
  • Switch over to the Java file and copy-paste the code from the Complete Source Code below.
  • Since the source code is complicated, I provide explanations below. If you fail to understand something, the next post will answer your questions.

Understanding the Code

  • I have used an ArrayList instead of a simple array. For those who are unfamiliar with this class, it provides a linked implementation for Double Dimensional arrays.
  • The initialize() method adds weekdays to the ArrayList.
  • lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE) – This statement is responsible for the checkboxes. If we wanted radio buttons, all we need to do is replace CHOICE_MODE_MULTIPLE with CHOICE_MODE_SINGLE.
  • We then, set the Adapter and define the onClick() method for the button.

COMPLETE SOURCE CODE

row_item.xml

activity_main.xml

MainActivity.java

[java]
package com.nero.myfirstapp;

import java.util.ArrayList;

import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

public class Main extends Activity {
ArrayList weekdays = new ArrayList();
ArrayList ret = new ArrayList();
ListView lv;
EditText coursename;
Button addcourse;
Adapter adapter;
String c;
long courseid;

public void initialize(){
weekdays.add("Monday");weekdays.add("Tuesday");weekdays.add("Wednesday");
weekdays.add("Thursday");weekdays.add("Friday");weekdays.add("Saturday");
}

public void getoldweekdays(long id){
ret.clear();
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ret.clear();
initialize();
lv = (ListView) findViewById(R.id.listView1);
coursename = (EditText) findViewById(R.id.coursenameet);
addcourse = (Button) findViewById(R.id.addcourse);
lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

courseid = getIntent().getLongExtra("courseid", -1);
adapter = new Adapter(this, R.layout.row_item, R.id.checkedtextview, weekdays);
lv.setAdapter(adapter);
for(int i=0;i<ret.size();i++)lv.setItemChecked(ret.get(i), true);

lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
adapter.toggle(position);
}
});

addcourse.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {
ArrayList checkeditem = adapter.getCheckedItems();
finish();
}
});
}
}
[/java]

Context Menus is List View using Simple Cursor Adapter

You have already seen how Context Menus work with ArrayAdapter. In case of SimpleCursorAdapter there are a few methods that can be used in order to retrieve the data about the item that is being clicked. This process is a bit different from the one we have already seen. I provide below the Source Code for doing the same.

Again, the Source Code is changed only in the MainActivity.java file. The rest of the codes namely DatabaseHelper.java, activity_main.xml, row_item.xml remain the same.

MainActivity.java

[java]
package com.nero.myfirstapp;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.provider.ContactsContract.PhoneLookup;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.v4.widget.SimpleCursorAdapter;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class Main extends ListActivity {
DatabaseHelper db;
SimpleCursorAdapter dataAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = new DatabaseHelper(Main.this);
displayList();
}

public void displayList(){
db.InsertValues();
Cursor cursor=db.GetAllData();
String from [] = new String[]{db.colName,db.colAge};
int to [] = new int[] {R.id.textView1,R.id.textView2};
dataAdapter = new SimpleCursorAdapter(this, R.layout.row_item, cursor, from, to, 0);
db.close();

ListView lv = getListView();
lv.setAdapter(dataAdapter);
registerForContextMenu(lv);
}

public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo){
ListView lv = (ListView) v;
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
Cursor cursor = (Cursor) lv.getItemAtPosition(info.position);
final int title = cursor.getInt(cursor.getColumnIndex("_id"));
menu.setHeaderTitle(title);
menu.add(0, v.getId(), 0, "Edit");
menu.add(0, v.getId(), 0, "Delete");
}

public boolean onContextItemSelected(MenuItem item){
if(item.getTitle()=="Edit")Toast.makeText(getApplicationContext(), "Edit Clicked", Toast.LENGTH_LONG).show();
if(item.getTitle()=="Delete")Toast.makeText(getApplicationContext(), "Delete Clicked", Toast.LENGTH_LONG).show();
return true;
}
}
[/java]

List View using Simple Cursor Adapter – Part II

In the last post, I showed you how to populate a ListView from the data present at any time in a database. We used a separate XML for defining the layout of a single row in ListView. However, in the last post the exact application of this XML file was not shown.

In this post I’ll provide you with the Source code to have more than one row inside a single row in a ListView. Here I’ve used two. You can use as many as you like.
The Source code has a few changes, but they can be easily understood. Also the activity_main.xml and DatabaseHelper.java file do not contain any changes.

Cursor

row_item.xml

MainActivity.java

[java]
package com.nero.myfirstapp;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.provider.ContactsContract.PhoneLookup;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.v4.widget.SimpleCursorAdapter;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class Main extends ListActivity {
DatabaseHelper db;
SimpleCursorAdapter dataAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = new DatabaseHelper(Main.this);
displayList();
}

public void displayList(){
db.InsertValues();
Cursor cursor=db.GetAllData();
String from [] = new String[]{db.colName,db.colAge};
int to [] = new int[] {R.id.textView1,R.id.textView2};
dataAdapter = new SimpleCursorAdapter(this, R.layout.row_item, cursor, from, to, 0);
db.close();

ListView lv = getListView();
lv.setAdapter(dataAdapter);
}
}
[/java]

List View using SimpleCursorAdapter – Part I

Till now we have been populating our List View with a predefined String array using the ArrayAdapter class of Java. Practically, a predefined array is hardly, if ever, used. Most of the times you would want to populate the List View using the data in your Databases. This is precisely what you see in the Contacts list of your phone. Once you add a contact, it is stored in a database and when you open the Contacts list, the list is populated from the data in the database.

Since, here we will be extracting data from our Database, we definitely ill not be using ArrayAdapter. For this we will use SimpleCursorAdapter class. SimpleCursorAdapter just like ArrayAdapter  helps in bridging the data repository to the UI. Remember that as I said earlier there are many ways to populate the ListView. Here I am going to show you one of the most customizable ways around.

So start off an activity and follow along. Complete Source Code is at the bottom.

  • Create the XML layout for the activity and insert a ListView in it.
  • Create another XML named row_item.xml. We will define the layout for a single row in the ListView. Insert a single TextView here and nothing more.
  • Remember that this is one method of doing things. This keeps your ListView customizable to the the single row.
  • Create a new class in your application. Name it DatabaseHelper.java. It will extend SQLiteOpenHelper and will contain all required methods. If you do not know how to do this refer to one of my previous posts here.
  • Also create two static string variables named colName and colAge with values “_id” and “Age” respectively. These will be the names of the columns in our table. (See Source Code for details.)
  • Switch over to the Java file. We will be extending ListActivity.
  • Declare an object of the DatabaseHelper class and SimpleCursorAdapter class globally i.e. inside the class and outside all the methods.
  • Create a new method displayList(). Within this method we need to insert the values in the Database and retrieve all the data from the database.  Write the following lines to do this:
    [java]
    db.InsertValues();
    Cursor cursor=db.GetAllData();
    [/java]
  • Once the data is retrieved, we need to define the source and destination of all the data items. Write the below lines.
    [java]
    String from [] = new String[]{db.colName};
    int to [] = new int[] {R.id.textView1};
    [/java]
  • Now that we have our source and destination for each data item, we need to define the SimpleCursorAdapter.
    [java]
    dataAdapter = new SimpleCursorAdapter(this, R.layout.row_item, cursor, from, to, 0);
    [/java]
  • Now define the ListView and assign the dataAdapter to the ListView.
    [java]
    ListView lv = getListView();
    lv.setAdapter(dataAdapter);
    [/java]
  • Save your work and execute it on an emulator or device.

Array_Adapter3  Array_Adapter2

Understanding the Code

  • The object of the DatabaseHelper class is required because we will need to call the methods and refer to the static variables of that class. Obviously, it cannot be done without objects.
  • Cursor, as I have said in my earlier posts are analogous to a pointer which points to the beginning of the rows returned from a query.
  • The from[ ] and the to[ ] arrays are used to define the source and destination of the data items. It tells Android how and where I want data from my database to be displayed. The actual importance of these will be reflected when we are using more than one row inside a single row of the ListView. I will discuss this in a separate post.
  • The constructor of the SimpleCursorAdapter class used here is
    [java]
    SimpleCursorAdapter(Context context, int layout, Cursor cursor, String from[], int to[], int flags)
    [/java]
  • The above constructor is already comprehensive. We then finally assign the SimpleCursorAdapter to the ListView.

COMPLETE SOURCE CODE

row_item.xml

activity_main.xml

DatabaseHelper.java

[java]
package com.nero.myfirstapp;

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

public class DatabaseHelper extends SQLiteOpenHelper {

static String DatabaseName="NeroDB";
static String colName="_id";
static String colAge="Age";

public DatabaseHelper(Context context) {
super(context, DatabaseName, null, 4);
}

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS NeroTable(_id VARCHAR, Age INT(3));");
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE NeroTable;");
db.execSQL("CREATE TABLE IF NOT EXISTS NeroTable(_id VARCHAR, Age INT(3));");
}

public void InsertValues()
{
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("INSERT INTO NeroTable VALUES(‘Vergil’, 20);");
db.execSQL("INSERT INTO NeroTable VALUES(‘Dante’, 20);");
db.execSQL("INSERT INTO NeroTable VALUES(‘Nero’, 18);");
db.execSQL("INSERT INTO NeroTable VALUES(‘Sparda’, 40);");
db.execSQL("INSERT INTO NeroTable VALUES(‘Arkham’, 38);");
db.execSQL("INSERT INTO NeroTable VALUES(‘Agni’, 35);");
db.execSQL("INSERT INTO NeroTable VALUES(‘Rudra’, 35);");
db.execSQL("INSERT INTO NeroTable VALUES(‘Beowulf’, 60);");
db.execSQL("INSERT INTO NeroTable VALUES(‘Nevan’, 26);");
db.close();
}

public Cursor GetAllData()
{
SQLiteDatabase db = this.getReadableDatabase();
Cursor c = db.rawQuery("SELECT * FROM NeroTable;", null);
return c;
}

public void DeleteTable()
{
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("DROP TABLE NeroTable;");
}
}

[/java]

MainActivity.java

[java]
package com.nero.myfirstapp;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.provider.ContactsContract.PhoneLookup;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.v4.widget.SimpleCursorAdapter;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class Main extends ListActivity {
DatabaseHelper db;
SimpleCursorAdapter dataAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = new DatabaseHelper(Main.this);
displayList();
}

public void displayList(){
db.InsertValues();
Cursor cursor=db.GetAllData();
String from [] = new String[]{db.colName};
int to [] = new int[] {R.id.textView1};
dataAdapter = new SimpleCursorAdapter(this, R.layout.row_item, cursor, from, to, 0);
db.close();

ListView lv = getListView();
lv.setAdapter(dataAdapter);
}
}
[/java]