Thursday, January 17, 2013

Planning: Saving and loading the list. Extension of functional

Planning: Saving and loading the list. Extension of functional
In the last article, we developed a custom ListView, which is filled with local data. In this article we expand the functionality of our application and implement the ability to save and load data.


Practice

In this article I will show you how to work with files on the sd-card (saving and loading). And as we expand the possible actions with our list by using the main and context menu. The result of the program you can see in the screenshots, below.

    

Proceed directly to the practice. 

1. Save / Load the list
To access the SD-card uses the class Environment. This class provides access to environment variables. We use the method getExternalStorageDirectory().GetAbsolutePath() to get the path to the SD-card device.
In class Utils add line:
// Directory on the SD-card where stores the list
 public static File cacheDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/customlistview");
To save and load list, use the following idea: save the object (ArrayList) wholly in the binary. For correct operation the previous article, we have added an interface Serializable for the class ToDoItem.
  /**
  * Save the list on the SD-card
  */
 private synchronized void saveList() {
  try {
   File infoFile = new File(Utils.cacheDir, "cache");
   infoFile.createNewFile();
   ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(infoFile));
   out.writeObject(list);
   out.close();
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 /**
  * Load a list of SD-Card
  */
 @SuppressWarnings("unchecked")
 public boolean loadList() {
  try {
   Utils.cacheDir.mkdir();
   File infoFile = new File(Utils.cacheDir, "cache");
   infoFile.createNewFile();
   ObjectInputStream in = new ObjectInputStream(new FileInputStream(infoFile));
   list = (List<ToDoItem>) in.readObject();
   in.close();
   return true;
  } catch (Exception e) {
   e.printStackTrace();
   return false;
  }
 }
Make changes to the existing algorithms:
In the method onCreate() instead initList() will use loadList().
It is also necessary to add saving a list on change status for element. This happens in Handler.
  private Handler handler = new Handler() {
  public void handleMessage(Message msg) {

   switch (msg.what)
   {
   ...
   case MSG_CHANGE_ITEM: // Do / not do case
    ToDoItem item = list.get(msg.arg1);
    item.setCheck(!item.isCheck());
    Utils.sorting(list, 0);
    saveList();
    adapter.notifyDataSetChanged();
    setCountPurchaseProduct();
    break; 
   }
  }
 };

2. Development of the main menu
The main menu appears in the application when you press the menu button on the device.
With our main menu application can perform the following functions:
  • Add an element;
  • Remove all the elements;
  • To mark all the elements;
  • Remove marks from all the elements.
First, add all the string resources in the file strings.xml.
      <string name="menu_item_add">Add</string>
      <string name="menu_item_remove">Remove</string>
      <string name="menu_item_rename">Rename</string>
      <string name="menu_item_remove_all">Remove all</string>
      <string name="menu_item_check_all">Check all</string>
      <string name="menu_item_uncheck_all">Remove all checks</string>
     
      <string name="btn_yes">Yes</string>
      <string name="btn_no">No</string>
     
      <string name="msg_empty">The name can not be empty</string>

Now, designed layout for the menu. Need to create a directory menu in res folder. There create a file menu.xml

Source code (menu.xml):
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/menu_item_add"
        android:title="@string/menu_item_add"/>
    <item
        android:id="@+id/menu_item_remove_all"
        android:title="@string/menu_item_remove_all"/>
    <item
        android:id="@+id/menu_item_check_all"
        android:title="@string/menu_item_check_all"/>
    <item
        android:id="@+id/menu_item_uncheck_all"
        android:title="@string/menu_item_uncheck_all"/>
</menu>

Now it is necessary to develop methods that will be executed when selecting menu items.
  • Add Item (showAddToDoItemDialog)
This method will create a dialog box with an input field name. After you click "Yes" to the list to add a new element.
  /**
  * Open dialog box to add an item
  */
 private void showAddToDoItemDialog()
 {
  AlertDialog.Builder builder = new AlertDialog.Builder(this);
  builder.setTitle(getString(R.string.menu_item_add));
  final Context context = this;
  final EditText input = new EditText(this);  
        builder.setView(input);
        
        builder.setPositiveButton(getString(R.string.btn_yes), new DialogInterface.OnClickListener() {
         
            @Override
            public void onClick(DialogInterface dialog, int whichButton) {
                String value = input.getText().toString();
                if (value.equals(""))
                 Toast.makeText(context, getString(R.string.msg_empty), Toast.LENGTH_SHORT).show();
                else 
                {
                 list.add(new ToDoItem(value, getIndexFromList() + 1));
                 Utils.sorting(list, 0);
                 saveList();
                 getHandler().sendEmptyMessage(MainActivity.MSG_UPDATE_ADAPTER);
                }

            }
        });
 
        builder.setNegativeButton(getString(R.string.btn_no), new DialogInterface.OnClickListener() {
 
            @Override
            public void onClick(DialogInterface dialog, int which) {
                return;
            }
        });
  AlertDialog alert = builder.create();
  alert.show();
 }
  • Remove all the elements (removeAll)
This method will be fully clear the list.
  /**
  * Removing all items from the list
  */
 private void removeAll() {
  list.clear();
  saveList();
  getHandler().sendEmptyMessage(MainActivity.MSG_UPDATE_ADAPTER);
 }
  • Checking / unchecking all items (setCheckAll)
This method, depending on the parameter sets / clears selection of each item in the list.
  /**
  * Do / not do all cases
  */
 private void setCheckAll(boolean check)
 {
  Iterator<ToDoItem> it = list.iterator();
  while (it.hasNext())
  {
   ToDoItem item = it.next();
   item.setCheck(check);
  }
  Utils.sorting(list, 0);
  saveList();
  getHandler().sendEmptyMessage(MainActivity.MSG_UPDATE_ADAPTER);
 }
  • Get the last index in the list (getIndexFromList)
This method returns the last index of in the list.
  /**
  * Get the last index in the list
  */
 private int getIndexFromList()
 {
  int index = 0;
  Iterator<ToDoItem> it = list.iterator();
  while (it.hasNext())
  {
   ToDoItem item = it.next();
   if (item.getIndex() > index)
    index = item.getIndex();
  }
  return index;
 }

Now add the menu in Activity.
  /**
  * Creating the main menu
  */
 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  getMenuInflater().inflate(R.menu.menu, menu);
  return true;
 }
   
 /**
  * Processing clicking on an item of the main menu
  */
 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  int id = item.getItemId();
  switch (id)
  {
  case R.id.menu_item_add: // Adding an item
   showAddToDoItemDialog();
   return true;
  case R.id.menu_item_remove_all: // Removing all items
   removeAll();
   return true;
  case R.id.menu_item_check_all: // Do all cases
   setCheckAll(true);
   return true;
  case R.id.menu_item_uncheck_all: // Redo list to its original state
   setCheckAll(false);
   return true;
  default:
   return super.onOptionsItemSelected(item);
  }  
 }

3. Development of the context menu
The context menu for the ListView appears when you long press on an item. Registered by the method registerForContextMenu(). The context menu of our application will be able to perform the following functions:
  • Delete the item;
  • Rename the item.
First developed a method for the context menu.
  • Delete selected item (removeItem) 
Removes the specified item from the list. After removal of the list is re-indexed.
  /**
  * Removing all items from the list
  */
 private void removeItem(int index) {
  list.remove(index);
  reindexList();
  saveList();
  getHandler().sendEmptyMessage(MainActivity.MSG_UPDATE_ADAPTER);
 }
  • Rename the selected item (showRenameDialog)
  /**
  * Open a dialog box to rename an item
  */
 private void showRenameDialog(final int index)
 {
  AlertDialog.Builder builder = new AlertDialog.Builder(this);
  builder.setTitle(getString(R.string.menu_item_rename));
  final Context context = this;
  final EditText input = new EditText(this);  
  input.setText(list.get(index).getName());
        builder.setView(input);
        
        builder.setPositiveButton(getString(R.string.btn_yes), new DialogInterface.OnClickListener() {
         
            @Override
            public void onClick(DialogInterface dialog, int whichButton) {
                String value = input.getText().toString();
                if (value.equals(""))
                 Toast.makeText(context, getString(R.string.msg_empty), Toast.LENGTH_SHORT).show();
                else 
                {
                 list.get(index).setName(value);
                 saveList();
                 getHandler().sendEmptyMessage(MainActivity.MSG_UPDATE_ADAPTER);
                }

            }
        });
 
        builder.setNegativeButton(getString(R.string.btn_no), new DialogInterface.OnClickListener() {
 
            @Override
            public void onClick(DialogInterface dialog, int which) {
                return;
            }
        });
  AlertDialog alert = builder.create();
  alert.show();
 }
  • Reindexing list (reindexList) 
This method orders index in the list after removing an element.
  /**
  * Reallocate indexes for items in the list
  */
 private void reindexList()
 {
  int index = 1;
  Utils.sorting(list, 1);
  Iterator<ToDoItem> it = list.iterator();
  while (it.hasNext())
  {
   ToDoItem item = it.next();
   item.setIndex(index);
   index++;
  }
  Utils.sorting(list, 0);
 }

Now let's add a context menu in Activity.
Register the menu to ListView in method onCreate():
registerForContextMenu(listview); // Register the context menu for ListView

Add methods in Activity:
  /**
  * Creating context menu for ListView
  */
 @Override
 public void onCreateContextMenu(ContextMenu menu, View v,
   ContextMenuInfo menuInfo) {
  int index = ((AdapterView.AdapterContextMenuInfo)menuInfo).position;
  if (index == list.size() + 1 || index == 0)
   return;
  super.onCreateContextMenu(menu, v, menuInfo);
  menu.add(0, MSG_REMOVE_ITEM, Menu.NONE, R.string.menu_item_remove);
  menu.add(0, MSG_RENAME_ITEM, Menu.NONE, R.string.menu_item_rename);
 }
 
 /**
  * Processing clicking on an item context menu
  */
 @Override
 public boolean onContextItemSelected(MenuItem item) {

  super.onContextItemSelected(item);
  AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
  int index = menuInfo.position - 1;
  
  switch (item.getItemId())
  {
  case MSG_REMOVE_ITEM:  // Remove an item
   removeItem(index);
   return true;
  case MSG_RENAME_ITEM: // Rename an item  
   showRenameDialog(index);
   return true;
  }
  return false;
 } 

Links