Android Tutorial: local storage an app example

In this tutorial we will use the local storage database of the Android device. I will try to explain this by making a small app using local storage of Android devices. Personally I never plan my shopping. I run into a stores to buy a sweater or something else, but this is almost never really planned. A common problem for myself is that I never know my clothing size, which is sometimes a bit problematic when your goal is fly in- and out of the store :). Therefore we will make in this tutorial a small app in which you can set your sizes of different clothing items. When you open the app at a later moment, the previous selected sizes are still remembered and retrieved by the local storage of Android.

Some of the design elements of this app I have already discussed in previous posts. So for the following aspects of this demo app you can browse to:
- Create ActionBar (top header of the app)
- Gradient background
- Overlay instruction when you open the app for the first time.

In the sizematters app (that’s how I named this demo app). There is on the top a settings icon which takes you to a menu where you can set your clothing preferences.

This layout file is very straight forward. If you are wondering what the tag is please see for reference the actionbar tutorial. For the rest we just included some textviews for titles, Spinners for selection and a save button to save your clothing references.

The XML layout for this screen is:

<?xml version="1.0" encoding="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/gradient" >
    
     <include
        android:id="@+id/include1"
        layout="@layout/actionbar" />

    <TextView
        android:id="@+id/tvSettingsheaderwaist"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/include1"
        android:layout_marginTop="18dp"
        android:paddingLeft="@dimen/padding5dp"
        android:text="Select your waist size"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <Spinner
        android:id="@+id/spWaist"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/tvSettingsheaderwaist"
        android:entries="@array/waist_arrays"
        android:prompt="@string/sizewaist_prompt" />

    <TextView
        android:id="@+id/tvSettingsLength"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/tvSettingsheaderwaist"
        android:layout_below="@+id/spWaist"
        android:text="Select your length"
        android:paddingLeft="@dimen/padding5dp"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <Spinner
        android:id="@+id/spLength"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/tvSettingsLength"
        android:entries="@array/length_arrays"
        android:prompt="@string/sizelength_prompt" />

    <TextView
        android:id="@+id/tvSettingsheaderDressShirt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/spLength"
        android:text="Select your dress shirt size"
        android:paddingLeft="@dimen/padding5dp"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <Spinner
        android:id="@+id/spDressshirt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/tvSettingsheaderDressShirt"
        android:entries="@array/dressshirt_arrays"
        android:prompt="@string/sizedressshirt_prompt" />

    <TextView
        android:id="@+id/tvSettingsheaderSweater"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/spDressshirt"
        android:text="Select your sweater size"
        android:paddingLeft="@dimen/padding5dp"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <Spinner
        android:id="@+id/spSweater"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/tvSettingsheaderSweater"
        android:entries="@array/sweater_arrays"
        android:prompt="@string/sizesweater_prompt" />
    
    <TextView
        android:id="@+id/tvSettingsheaderShoe"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/spSweater"
        android:text="Select your shoe size"
        android:paddingLeft="@dimen/padding5dp"
        android:textAppearance="?android:attr/textAppearanceMedium" />
    
    <Spinner
        android:id="@+id/spShoe"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/tvSettingsheaderShoe"
        android:entries="@array/shoe_arrays"
        android:prompt="@string/sizeshoe_prompt" />

    <Button
        android:id="@+id/btnSettingsSave"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/spShoe"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="32dp"
        android:paddingLeft="@dimen/padding5dp"
        android:text="Save for future reference" />

</RelativeLayout>

The values which the user can select in the app, are defined in a strings.xml (in your values folder). Sorry for the long list :).

<resources>
    
    
    <string name="app_name">sizematters</string>
    <string name="hello_world">Hello world!</string>
    <string name="menu_settings">Settings</string>
    <string name="title_activity_main">Size Matters</string>
    
   <!--Below all arrrays with sizes of the different clothing items --> 
    <string name="sizewaist_prompt">Choose your waist</string>
    <string name="sizelength_prompt">Choose your length</string>
    <string name="sizedressshirt_prompt">Choose your size</string>
    <string name="sizesweater_prompt">Choose your size</string>
    <string name="sizeshoe_prompt">Choose your size</string>
 	<string-array name="waist_arrays">
        <item>30</item>
        <item>31</item>
        <item>32</item>
        <item>34</item>
        <item>36</item>
        <item>38</item>
        <item>40</item>
        <item>41</item>
        <item>42</item>
    </string-array>
    
 	<string-array name="length_arrays">
        <item>30</item>
        <item>31</item>
        <item>32</item>
        <item>34</item>
        <item>36</item>
        <item>38</item>
        <item>40</item>
        <item>41</item>
        <item>42</item>
    </string-array>
    
    <string-array name="dressshirt_arrays">
        <item>38</item>
        <item>39</item>
        <item>40</item>
        <item>41</item>
        <item>42</item>
        <item>43</item>
        <item>44</item>
        <item>45</item>
        <item>46</item>
    </string-array>
    
    <string-array name="sweater_arrays">
        <item>XS</item>
        <item>S</item>
        <item>M</item>
        <item>L</item>
        <item>XL</item>
        <item>XXL</item>
    </string-array>
    
    <string-array name="shoe_arrays">
        <item>32</item>
        <item>33</item>
        <item>34</item>
        <item>35</item>
        <item>36</item>
        <item>37</item>
        <item>38</item>
        <item>39</item>
        <item>40</item>
        <item>41</item>
        <item>42</item>
        <item>43</item>
        <item>44</item>
        <item>45</item>
        <item>46</item>
    </string-array>
    

</resources>

Ok, now that we have set the layout for our settings menu we will make the main layout of the sizematters app. The MainActivity consists out of two parts. (1) the actual activity and secondly the layout which is only showed the first time with the overlay instruction. When your interested in this overlay instruction. Please see this tutorial. These 2 layout are combined in a FrameLayout. Again very straightforward, some textviews for the titles and some imageviews to make the app a bit more appealing.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/main_layout" >

    <!--Below activity widgets when the transparent layout is gone -->

<RelativeLayout 
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:background="@drawable/gradient">

    <include
        android:id="@+id/include1"
        layout="@layout/actionbar" />

    <ImageView
        android:id="@+id/ivPants"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="80dp"
        android:paddingLeft="@dimen/padding10dp"
        android:src="@drawable/pants" />

    <ImageView
        android:id="@+id/ivDressshirt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tvLength"
        android:layout_marginTop="55dp"
        android:paddingLeft="@dimen/padding10dp"
        android:src="@drawable/shirt" />

    <ImageView
        android:id="@+id/ivSweater"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/ivDressshirt"
        android:layout_below="@+id/ivDressshirt"
        android:layout_marginTop="57dp"
        android:paddingLeft="@dimen/padding10dp"
        android:src="@drawable/sweater" />
    
      <ImageView
          android:id="@+id/ivShoe"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_alignLeft="@+id/ivSweater"
          android:layout_below="@+id/ivSweater"
          android:layout_marginTop="57dp"
          android:paddingLeft="@dimen/padding10dp"
          android:src="@drawable/shoe" />
    
    <TextView
        android:id="@+id/tvWaist"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/ivPants"
        android:layout_toRightOf="@+id/ivSweater"
        android:paddingLeft="@dimen/padding10dp"
        android:text="Waist"
        android:textSize="26sp" />

    <TextView
        android:id="@+id/tvLength"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/ivPants"
        android:layout_toRightOf="@+id/ivSweater"
        android:paddingLeft="@dimen/padding10dp"
        android:text="Length"
        android:textSize="26sp" />

    <TextView
        android:id="@+id/tvDressshirt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/ivDressshirt"
        android:layout_toRightOf="@+id/ivSweater"
        android:paddingLeft="@dimen/padding10dp"
        android:text="Dressshirt"
        android:textSize="26sp" />

    <TextView
        android:id="@+id/tvSweater"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/tvDressshirt"
        android:layout_alignTop="@+id/ivSweater"
        android:paddingLeft="@dimen/padding10dp"
        android:text="Sweater"
        android:textSize="26sp" />
    
    <TextView
        android:id="@+id/tvShoe"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/tvSweater"
        android:layout_alignTop="@+id/ivShoe"
        android:paddingLeft="@dimen/padding10dp"
        android:text="Shoe"
        android:textSize="26sp" />

</RelativeLayout>
 	


	<!--Below is the transparent layout positioned at startup -->
<RelativeLayout
    	android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#88666666"
        android:id="@+id/top_layout">

    <ImageView
        android:id="@+id/ivInstruction"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
      	android:paddingTop="25dp"
        android:layout_marginRight="15dp"
        android:clickable="false"
        android:paddingLeft="20dip"
        android:scaleType="center"
        android:src="@drawable/help" />
    
    </RelativeLayout>
    
</FrameLayout>

Now lets start with the java :). First we will create the settings.java. This is the java class which describes the settings menu of the sizematters app. In this class we will make the references to your android widgets and write the settings to the local storage of the device.

I will not explain every piece, but I made comments in the code for you so you know what it stands for. In summary:
- As the user is in the settings menu we make the settings icon invisible.
- We use the ArrayAdapter of Android to bind the strings.xml to the Spinners
- We set the references of the textviews, save button and spinners.

When the user selects the sizes we want the save button to save the selected data to the local storage. This has been defined in the onClick method. First we use toString() to get the values from the spinners and set them to a String value. Than we define eol (end of line) which we will use to save each item of the spinners on a new line in the local storage. You can see this in the snippet below. We get each value of the spinner and add an eol. With other words, write each selected Spinner item to the txt file jeansizes but seperated with a line break.

	bw.write(waist + eol); 
	bw.write(length + eol);
	bw.write(dressshirt + eol);
	bw.write(sweater + eol);
	bw.write(shoe + eol);
	bw.close();

You can see if the txt file has been saved by the device by running the emulator, go in Eclipse to DDMS- FileExplorer- data- data -select your android package name – files. If you did everything right, you should see a text file with the save data. As you can see the data has been saved with a line separator between each selected item.

To make sure a users always selects an item from the Spinners I check if any of the widgets have no value (“”) otherwise we show a toast that the data has been saved and use an Intent to direct the user back to the MainActivity.

The complete settings.java class:

package com.christianpeeters.sizematters;

import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStreamWriter;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.Toast;


public class settings extends Activity {
	
	
	Spinner spWaist, spLength, spDressshirt, spSweater, spShoe;
	Button btnSettingsSave, btnSettings;
	String lineitem;
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
	    setContentView(R.layout.settings);
	    
	    //remove the actual settings icon in the header in the settings menu
	    btnSettings = (Button)findViewById(R.id.btnsettings);
	    btnSettings.setVisibility(View.GONE);
	   
	    
	    ////set arrays for all clothing sizes
	    // set waist array
	    ArrayAdapter<CharSequence> adapterwaist = ArrayAdapter.createFromResource(
	    this, R.array.waist_arrays, android.R.layout.simple_spinner_item );
	    adapterwaist.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item );
	    
	    //set length array
	    ArrayAdapter<CharSequence> adapterlength = ArrayAdapter.createFromResource(
	    this, R.array.length_arrays, android.R.layout.simple_spinner_item );
	    adapterlength.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item );
	    
	  //set length array
	    ArrayAdapter<CharSequence> adapterdressshirt = ArrayAdapter.createFromResource(
	    this, R.array.dressshirt_arrays, android.R.layout.simple_spinner_item );
	    adapterdressshirt.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item );
	    
	    
	    //set sweater array
	    ArrayAdapter<CharSequence> adaptersweater = ArrayAdapter.createFromResource(
	    this, R.array.sweater_arrays, android.R.layout.simple_spinner_item );
	    adaptersweater.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item );
	    
	    //set sweater array
	    ArrayAdapter<CharSequence> adaptershoe = ArrayAdapter.createFromResource(
	    this, R.array.shoe_arrays, android.R.layout.simple_spinner_item );
	    adaptersweater.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item );
	    
	    
	    //spinner references
	    spWaist = (Spinner)findViewById(R.id.spWaist );
	    spWaist.setAdapter( adapterwaist );
	    
	    spLength = (Spinner)findViewById(R.id.spLength);
	    spLength.setAdapter(adapterlength);
	    
	    spDressshirt = (Spinner)findViewById(R.id.spDressshirt);
	    spDressshirt.setAdapter(adapterdressshirt);
	    
	    spSweater = (Spinner)findViewById(R.id.spSweater);
	    spSweater.setAdapter(adaptersweater);
	    
	    spShoe = (Spinner)findViewById(R.id.spShoe);
	    spShoe.setAdapter(adaptershoe);
	    		

	    //reference button to save sizes
	   btnSettingsSave = (Button)findViewById(R.id.btnSettingsSave);
	   // methods what happens when clicked on the save button
	    btnSettingsSave.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				
				//get spinner values
				String waist = spWaist.getSelectedItem().toString();
				String length = spLength.getSelectedItem().toString();
				String dressshirt = spDressshirt.getSelectedItem().toString();
				String sweater = spSweater.getSelectedItem().toString();
				String shoe = spShoe.getSelectedItem().toString();
				
				//create a line separator
				String eol = System.getProperty("line.separator");
				BufferedWriter bw = null;
				
				// write the info to a file called jeanssizes
				try {
					bw = new BufferedWriter(new OutputStreamWriter(openFileOutput("jeanssizes", MODE_WORLD_WRITEABLE)));
					bw.write(waist + eol); 
					bw.write(length + eol);
					bw.write(dressshirt + eol);
					bw.write(sweater + eol);
					bw.write(shoe + eol);
					bw.close();
				} catch (FileNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();	
				}
					//ensure that the user always enter all sizes. 
				if(waist.equals("") || length.equals("") || dressshirt.equals("") || sweater.equals("") || shoe.equals(""))
					{
					Toast.makeText(getApplicationContext(), "Oops, you did not select all of your sizes", Toast.LENGTH_LONG).show();
					}
				
				else{
					Toast.makeText(getApplicationContext(), "Yeah, your sizes are saved", Toast.LENGTH_LONG).show();

					
				}
			
				//go back to mainactivity
				Intent intent = new Intent(getApplicationContext(),MainActivity.class);
				startActivity(intent);
			}
	    });
	       
    }
  
	
	

			
		
       
}

Now that we have finished the settings.java class we still need to build our MainActivity. You can see in the MainActivity a method called isFirstTime(). This method is for the instruction overlay. You can read more about it in this tutorial.

As you can see I made a custom font in the app. I will not explain much about this for now, in case you have questions please leave a comment on the bottom of this post. As you can see we call a method called retrieveJeansize(). This method is the actual retrieval of the stored data in the jeansizes.txt which we call in retrieveJeansizes(). The BufferedReader is called to read the txt file. Then we do a While loop to read the txt file. Two variables are important to be aware of. The first is the lineitem. This is each line in the txt file representing one of the values stored. Secondly is linecounter starting at zero, which is the line of the txt file. So the while loop runs through the txt file and retrieves the value (text) and set it to the different android widgets (TextViews). Everytime when the value has set we go to the next line by calling linecounter++

The complete MainActivity.class

package com.christianpeeters.sizematters;


import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;

import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Typeface;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import com.christianpeeters.sizematters.settings;

public class MainActivity extends Activity {
	
	Button btnSettings;
	TextView tvWaist, tvLength, tvDressshirt, tvSweater, tvShoe;
	View topLevelLayout;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        topLevelLayout = findViewById(R.id.top_layout);
        
        
        
       if (isFirstTime()) {
        	topLevelLayout.setVisibility(View.INVISIBLE);
        }
       	
       	//create custom font
        Typeface font = Typeface.createFromAsset(getAssets(), "CanarsieSlab.ttf");

        tvWaist = (TextView)findViewById(R.id.tvWaist);
        tvLength = (TextView)findViewById(R.id.tvLength);
        tvDressshirt = (TextView)findViewById(R.id.tvDressshirt);
        tvSweater = (TextView)findViewById(R.id.tvSweater);
        tvShoe = (TextView)findViewById(R.id.tvShoe);
       
        //set textviews to new custom font
	    tvWaist.setTypeface(font);
	    tvLength.setTypeface(font);
	    tvDressshirt.setTypeface(font);
	    tvSweater.setTypeface(font);
	    tvShoe.setTypeface(font);

    
        		
      
        
        btnSettings = (Button)findViewById(R.id.btnsettings);
        btnSettings.setBackgroundResource(R.drawable.settings);
        
       
     
        btnSettings.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v){
				Intent intentSettings = new Intent(getApplicationContext(), settings.class);
				startActivity(intentSettings);
				
				
			}
		});
   
    }
    
    public void retrievJeansize() {
		
		String eol = System.getProperty("line.separator");
		BufferedReader br = null;
		
		try {
			br = new BufferedReader(new InputStreamReader(openFileInput("jeanssizes")));
			String lineitem;
			int linecounter =0;
		while((lineitem = br.readLine())!=null)
			{
			switch(linecounter){
			case 0:
			tvWaist.setText("W: "+ lineitem);
			linecounter++;
			break;
			case 1:
			tvLength.setText("L: "+ lineitem);
			linecounter++;
			break;
			case 2:
			tvDressshirt.setText(lineitem);
			linecounter++;
			break;
			case 3:
			tvSweater.setText(lineitem);
			linecounter++;
			break;
			case 4:
			tvShoe.setText(lineitem);
			break;
	
			}
		
			}
	
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
    }
    
    private boolean isFirstTime()
    	{
        SharedPreferences preferences = getPreferences(MODE_PRIVATE);
        boolean ranBefore = preferences.getBoolean("RanBefore", false);
        if (!ranBefore) {
        
            SharedPreferences.Editor editor = preferences.edit();
            editor.putBoolean("RanBefore", true);
            editor.commit();
            topLevelLayout.setVisibility(View.VISIBLE);
            topLevelLayout.setOnTouchListener(new View.OnTouchListener(){

		@Override
		public boolean onTouch(View v, MotionEvent event) {
		topLevelLayout.setVisibility(View.INVISIBLE);
		return false;
		}
		            
		            });
        
  
		    }
		return ranBefore;
		    
		}
		
		    
		 
		}

This is all, if you liked the post feel free to leave a comment.

2 Responses

  1. nabil September 4, 2013 / 12:27 am

    Really good simple codes.
    But still it d be good if you put more comments and explanation .as it’s for beginners :-)

  2. ├Ânder June 18, 2014 / 11:21 am

    eclipse with android pull data from local storage,
    The summary I;
    vebview file / / inside, take the key and value value

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

*