Android Development Tutorial 7

Android Development TutorialWelcome to part 7 of my Android Development Tutorial! Today I will continue what I started in the last Android tutorial.

I cover how to do all of the following: Pass values between activities : Save key value pairs : Use OnClickListeners : Retrieve saved data : Use ScrollViews : Use Alert Dialog Boxes : Use Intents to Open Applications : Use Intents to Open Activities : Force Close the Keyboard : Much More… Download all the code here

If you like videos like this, it helps to tell Google+ with a click here

Code From the Video

MainActivity.java

package com.newthinktank.stockquotes;

import java.util.Arrays;

import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;

public class MainActivity extends Activity {
	
	// Store a unique message using your package name to avoid conflicts
	// with other apps. This stores the stock symbol I plan on displaying
	public final static String STOCK_SYMBOL = "com.example.myfirstapp.STOCK";
	
	// Manages key valued pairs associated with stock symbols
	private SharedPreferences stockSymbolsEntered;
	
	// Table inside the scroll view that holds stock symbols
	// and buttons
	private TableLayout stockTableScrollView;
	
	// Where the user enters a new stock symbol
	private EditText stockSymbolEditText;
	
	// Button that enters a new stock and another that
	// deletes all of them
	Button enterStockSymbolButton;
	Button deleteStocksButton;

	// Set up the activity
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		// Retrieve saved stocks entered by the user. 
		// MODE_PRIVATE: Only accessible by your app (Most Common)
		// MODE_WORLD_READABLE: Any app can read (Can OR with Following)
		// MODE_WORLD_WRITABLE: Any app can write to this
		stockSymbolsEntered = getSharedPreferences("stockList", MODE_PRIVATE);
		
		// Initialize Components
		stockTableScrollView = (TableLayout) findViewById(R.id.stockTableScrollView);
		stockSymbolEditText = (EditText) findViewById(R.id.stockSymbolEditText);
		enterStockSymbolButton = (Button) findViewById(R.id.enterStockSymbolButton);
		deleteStocksButton = (Button) findViewById(R.id.deleteStocksButton);
		
		// Add ClickListeners to the buttons
		enterStockSymbolButton.setOnClickListener(enterStockButtonListener);
		deleteStocksButton.setOnClickListener(deleteStocksButtonListener);
		
		// Add saved stocks to the Stock Scrollview
		updateSavedStockList(null);
	}
	
	// Either adds a new stock or if null is entered the stock
	// list is updated with saved stocks
	private void updateSavedStockList(String newStockSymbol){
		
		// Get the saved stocks
		String[] stocks = stockSymbolsEntered.getAll().keySet().toArray(new String[0]);
		
		// Sort the stocks in alphabetical order
		Arrays.sort(stocks, String.CASE_INSENSITIVE_ORDER);
		
		// If the attribute sent to this method isn't null
		if(newStockSymbol != null){
			
			// Enter the new stock in sorted order into the array
			insertStockInScrollView(newStockSymbol, Arrays.binarySearch(stocks, newStockSymbol));
			
		} else {
			
			// Display saved stock list
			for(int i = 0; i < stocks.length; ++i){
				
				insertStockInScrollView(stocks[i], i);
				
			}
			
		}
		
	}
	
	private void saveStockSymbol(String newStock){
		
		// Used to check if this is a new stock
		String isTheStockNew = stockSymbolsEntered.getString(newStock, null);
		
		// Editor is used to store a key / value pair
		// I'm using the stock symbol for both, but I could have used company
		// name or something else
		SharedPreferences.Editor preferencesEditor = stockSymbolsEntered.edit();
		preferencesEditor.putString(newStock, newStock);
		preferencesEditor.apply();
		
		// If this is a new stock add its components
		if(isTheStockNew == null){
			updateSavedStockList(newStock);
		}
		
	}
	
	private void insertStockInScrollView(String stock, int arrayIndex){
		
		// Get the LayoutInflator service
		LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		
		// Use the inflater to inflate a stock row from stock_quote_row.xml
		View newStockRow = inflater.inflate(R.layout.stock_quote_row, null);
		
		// Create the TextView for the ScrollView Row
		TextView newStockTextView = (TextView) newStockRow.findViewById(R.id.stockSymbolTextView);
		
		// Add the stock symbol to the TextView
		newStockTextView.setText(stock);
		
		Button stockQuoteButton = (Button) newStockRow.findViewById(R.id.stockQuoteButton);
		stockQuoteButton.setOnClickListener(getStockActivityListener);
		
		Button quoteFromWebButton = (Button) newStockRow.findViewById(R.id.quoteFromWebButton);
		quoteFromWebButton.setOnClickListener(getStockFromWebsiteListener);
		
		// Add the new components for the stock to the TableLayout
		stockTableScrollView.addView(newStockRow, arrayIndex);
		
	}
	
	public OnClickListener enterStockButtonListener = new OnClickListener(){

		@Override
		public void onClick(View theView) {
			
			// If there is a stock symbol entered into the EditText
			// field
			if(stockSymbolEditText.getText().length() > 0){
				
				// Save the new stock and add its components
				saveStockSymbol(stockSymbolEditText.getText().toString());
				
				stockSymbolEditText.setText(""); // Clear EditText box
				
				// Force the keyboard to close
				InputMethodManager imm = (InputMethodManager)getSystemService(
					      Context.INPUT_METHOD_SERVICE);
					imm.hideSoftInputFromWindow(stockSymbolEditText.getWindowToken(), 0);
			} else {
				
				// Create an alert dialog box
				AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
				
				// Set alert title 
				builder.setTitle(R.string.invalid_stock_symbol);
				
				// Set the value for the positive reaction from the user
				// You can also set a listener to call when it is pressed
				builder.setPositiveButton(R.string.ok, null);
				
				// The message
				builder.setMessage(R.string.missing_stock_symbol);
				
				// Create the alert dialog and display it
				AlertDialog theAlertDialog = builder.create();
				theAlertDialog.show();
				
			}
			
		}
		
	};
	
	private void deleteAllStocks(){
		
		// Delete all the stocks stored in the TableLayout
		stockTableScrollView.removeAllViews();
		
	}
	
	public OnClickListener deleteStocksButtonListener = new OnClickListener(){


		public void onClick(View v) {
			
			deleteAllStocks();
			
			// Editor is used to store a key / value pairs
			
			SharedPreferences.Editor preferencesEditor = stockSymbolsEntered.edit();
			
			// Here I'm deleting the key / value pairs
			preferencesEditor.clear();
			preferencesEditor.apply();
			
		}
		
	};

	public OnClickListener getStockFromWebsiteListener = new OnClickListener(){

		public void onClick(View v) {
			
			// Get the text saved in the TextView next to the clicked button
			// with the id stockSymbolTextView

			TableRow tableRow = (TableRow) v.getParent();
            TextView stockTextView = (TextView) tableRow.findViewById(R.id.stockSymbolTextView);
            String stockSymbol = stockTextView.getText().toString();
            
            // The URL specific for the stock symbol
            String stockURL = getString(R.string.yahoo_stock_url) + stockSymbol;
            
            Intent getStockWebPage = new Intent(Intent.ACTION_VIEW, Uri.parse(stockURL));
            
            startActivity(getStockWebPage);
			
		}
		
	};
	
	public OnClickListener getStockActivityListener = new OnClickListener(){

		public void onClick(View v) {
			
			// Get the text saved in the TextView next to the clicked button
			// with the id stockSymbolTextView

			TableRow tableRow = (TableRow) v.getParent();
            TextView stockTextView = (TextView) tableRow.findViewById(R.id.stockSymbolTextView);
            String stockSymbol = stockTextView.getText().toString();
            
            // An intent is an object that can be used to start another activity
            Intent intent = new Intent(MainActivity.this, StockInfoActivity.class);
            
            // Add the stock symbol to the intent
            intent.putExtra(STOCK_SYMBOL, stockSymbol);
            
            startActivity(intent);
			
		}
		
	};
	
	@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;
	}

}

43 Responses to “Android Development Tutorial 7”

  1. Pradeep says:

    Awesome Derek, No words for all your tutorials, I have already followed your Design Patterns and Object Oriented Design,

    It is really amazing a person expert on each and every area.

    I am perfectly following very closely Android Tutorial, and waiting for rest of them also.

    Also Derek, is it possible for you, to teach us on any ESB such as, Mule-ESB

    Thanks
    Pradeep

    • Derek Banas says:

      Thank you very much for the kind words. I do the best I can to make the videos quick and to cover as much information as possible. Sorry, but I have never used Mule-ESB, so I can’t help with it

  2. Dorothy says:

    I’m stopping mid tutorial to tell you how awesome you are! I love your tutorials. This is exactly the stuff I need to know right now – so lucky I found you. You’re fast and thorough. The second a question pops in my head, you answer it. Best tutorials I’ve ever seen and normally I can’t sit through five minutes :)

    • Derek Banas says:

      Thank you very much :) I greatly appreciate you taking the time to say such nice things. I do my best to constantly improve. The input I have received from my community has really helped a lot.

  3. Oren says:

    Great tutorials, I’ve been flowing along the Java tutorials and now the Android ones and I really appreciate the effort you put into them.

    I tried to compile the code, but it crushed on the line -
    stockTableScrollView = (TableLayout) findViewById(R.id.stockTableScrollView);

    I looked for – stockTableScrollView ID in the previous tutorial, but didn’t find it.

    One more thing, I think it’s better to use the list view and an adapter instead of the scroll view. That way each view will be recycle once it’s not on the screen.

    Thank you for all the great tutorials!

  4. Matt says:

    Hey Derek,

    I’m really enjoying the videos and I really love the methods of teaching that you have adopted. While following along with this tutorial I noticed I was getting an error with the

    preferencesEditor.apply();

    and the error reads

    Call requires API level 9 (current min is 8): android.content.SharedPreferences.Editor#apply

    I noticed there are several suppress warnings that could be applied.

    What would be the best way to deal with this? Or how would one go about updating the min API level?

    With great appreciation,

    Matt

  5. Chi says:

    Your tutorials have really helped me with my school projects, Thanks you very much. Please would it be possible to provide advice on how to send details entered by a user from an android app to a Microsoft Access database, a tutorial on it would be great!!

  6. Thaddeus says:

    These are the best tutorials for android development. I am running into a problem though. I when I use add unimplemented methods for the OnClickListener the parameters for the onClick() method are completely different from whats in the tutorial. Instead of getting onClick(View v), I get onClick(DialogueInterface dialogue, int which). Any idea why that’s happening?

    • Derek Banas says:

      Thank you for the kind words :) I’m not sure about the problem your having. Does everything run after you put in the code?

    • Jeff says:

      I had the same issue, eclipse imported the wrong item. Check your imports for any onclicklisteners and delete them and add import android.view.View.OnClickListener;
      Mine was pulling a listener for the Dialog interface package.

  7. Richard Lucas says:

    At 4:06, what is the difference between these two lines. I know they both allow for the saving and restoring of data when the app is closed, but how are they different?

    public final static String STOCK_SYMBOL = “com.example.myfirstapp.STOCK”;

    private SharedPreferences stockSymbolsEntered;

    At 10:06, can you better explain what each method call on this line is doing, I especially do not understand that newString[0] part:

    String[] stocks = stockSymbolsEntered.getAll().keySet().toArray(new String[0]);

    Thank you very much Derek

    • Derek Banas says:

      SharedPreferences store many key / valued pairs of data that can be accessed by all activities. You can put many values in it. STOCK_SYMBOL on the other hand will only store one value.

      stockSymbolsEntered is of type SharedPreferences. getAll() retrieves all values that are stored. keySet() returns all keys from the map. toArray() puts them in an Array. String[0] gets the very first key in the Array.

      I hope that helps :)

      • Nadeem says:

        I still can’t figure out one thing in this line:

        stockSymbolsEntered = getSharedPreferences(“stockList”, MODE_PRIVATE);

        “stockList” – you’re using this list name, but I don’t know where did you declare it? It seems it just came from nowhere!?

        • Derek Banas says:

          stockList is just a name that is actually being created by getSharedPreferences.

          public abstract SharedPreferences getSharedPreferences (String name, int mode)

          Retrieve and hold the contents of the preferences file ‘name’, returning a SharedPreferences through which you can retrieve and modify its values. Only one instance of the SharedPreferences object is returned to any callers for the same name, meaning they will see each other’s edits as soon as they are made.

          Parameters

          name Desired preferences file. If a preferences file by this name does not exist, it will be created when you retrieve an editor (SharedPreferences.edit()) and then commit changes (Editor.commit()).

          mode Operating mode. Use 0 or MODE_PRIVATE for the default operation, MODE_WORLD_READABLE and MODE_WORLD_WRITEABLE to control permissions. The bit MODE_MULTI_PROCESS can also be used if multiple processes are mutating the same SharedPreferences file. MODE_MULTI_PROCESS is always on in apps targetting Gingerbread (Android 2.3) and below, and off by default in later versions.

          Returns
          The single SharedPreferences instance that can be used to retrieve and modify the preference values.

          Source : http://developer.android.com/reference/android/content/Context.html#getSharedPreferences(java.lang.String, int)

  8. Cbin says:

    Your videos are awesome,Before watching your videos i was following a lot of other tutorial videos.let me tell you the truth you are the best

  9. Shasha says:

    Getting error @ line 245 in MainActivity.java

    Intent intent = new Intent(MainActivity.this,StockInfoActivity.class);

    “StockInfoActivity” cannot be resolved to a type.

    • Derek Banas says:

      Make sure you try my exact code and it will work. This is normally an error that comes up when the package isn’t imported. The first line should be import package

      • Aldi says:

        Say, i encountered the exact same error and when i hover to it, the only possible fix is create class,enum,interface and fix project setup, no import.

        Maybe i’m wrong but is StockInfoActivity related to stock_quote_row?

  10. One question to you: In which cases is better to use ListView than TableLayout with TableRows, for example if you need to make a schedule of data, What you would use the list view or table view to present data?

    • Derek Banas says:

      It really depends on what you want to do.

      You could create a scrolling list of items with a LinearLayout in a ScrollView. You could also use a ListView with an Adapter.

      You’d get similar results with a TableLayout inside a ScrollView. You could do the same with a GridView with an Adapter.

      LinearLayout and TableLayout require an XML layout definition. ListView and GridView require an Adapter to bind data to them.

      I hope that helps :)

  11. Domenico says:

    Hi Derek,
    In your finished app there is an annoying behavior I don’t know how to fix.
    If I rotate the screen, the TextViews are randomly repopulated, or maybe, as I noticed, all the stocks in the list became like the last stock that was in the list prior to rotating the device.
    I guess this has something to do with the ‘Bundle savedInstance’ you covered in the previous part of the tutorial, but I am not sure.
    Also, if click the various buttons, the browser page is the same.
    However, if I put the app in background, and then I reopen it, all seem as the confusion between the TextViews was never happened.
    What do you think I should look into, to solve this kind of problem?
    What’s happening when I rotate my device?

    Thank you for this awesome series, good luck :)

    • Derek Banas says:

      I should have made a horizontal layout for the app. I cover how to do that later in the tutorial. My approach in this series was to cover as much as possible, but not to necessarily create a perfect app. If I did that it would take forever to make just one app and it would be very overwhelming. Just move to the next tutorial and I cover everything you need if you want to go back and fix it up later. Sorry about any confusion I caused.

  12. Sothearith Sreang says:

    Hi Derek, awesome tutorials. You are a really good explainer. Anw, I have a question for the last part. I quite don’t get how the Intent object, getStockWebPage is able to go to a web browser to link to the url. To be specific, how does this object know that it has to start the web browsing activity? by Intent.ACTION_VIEW??

    • Derek Banas says:

      Thank you :) Yes ACTION_VIEW is used when you want to perform an action on data, which in this case is to parse the provided URL. I cover Intents in great detail as the tutorial continues.

  13. DewaldvS says:

    I followed your tutorials and have not yet encountered an issue up an till now.
    So first off thanks for the great work.

    I have tried to follow each step and even looked at your source files but for some reason when I try and run the StockQuote app on the device I get the message Unfortunately, Get Stock Quotes has stopped.

    I am very new to mobile development.
    But I cant figure out if this is an environment setting or a code issue causing the app to crash?

    Thank you for your help.

  14. Walter Reyes says:

    Hi, Derek

    I just want to ask you something

    In the method: saveStockSymbol
    Would not it be better, in terms of performance, if we put the string newStock in the SharedPreference after we check if it already exists?

    I mean something like this:


    private void saveStockSymbol(String newStock){

    String isTheStockNew = stockSymbolsEntered.getString(newStock,null);

    if(isTheStockNew == null){

    SharedPreferences.Editor preferencesEditor = stockSymbolsEntered.edit();

    preferencesEditor.putString(newStock,newStock);
    preferencesEditor.apply();

    updateSavedStockList(newStock);
    }

    }

    Instead of:


    private void saveStockSymbol(String newStock){

    String isTheStockNew = stockSymbolsEntered.getString(newStock,null);

    SharedPreferences.Editor preferencesEditor = stockSymbolsEntered.edit();

    preferencesEditor.putString(newStock,newStock);
    preferencesEditor.apply();
    if(isTheStockNew == null){

    updateSavedStockList(newStock);
    }

    }

    I know it does not affects the funcionality because it just overwrites the key.

    I understand you do not stop in performance matters because you cover a lot!
    But i just wanted to ask (=

    Regards, and thanks for the awesome tutorials!

  15. Mel says:

    Hey Derek,

    i have problems at the following code lines:

    stockTableScrollView = (TableLayout) findViewById(R.id.stockTableScrollView);
    stockSymbolEditText = (EditText) findViewById(R.id.stockSymbolEditText);

    Intent intent = new Intent(MainActivity.this, StockInfoActivity.class);

    the first two lines give error because there seem to be no stockTableScrollView and stockSymbolEditText in R. and the last code line complains that the class StockInfoActivity hasn’t been created.

    thanks in advance! great work btw.

    Mel

  16. Sangram Gupta says:

    Hey Derek,

    I am big fan of yours and really like your tutorials. Most succinct tutorials on Android online.

    Also, you helping each and replying each person even through your PR staff shows the humility you posses. Seriously you have earned my earnest respect.

    Thanks.

    • Derek Banas says:

      Hey Sangram,

      I wish I had a PR staff :) I do my best. Thank you very much for taking your time to show your appreciation. The new Android tutorials will come out very soon.

  17. Derek Banas says:

    Thank you :) the app wasn’t made to pull in multiple quotes at one time. You can only get one at a time. Actually it isn’t meant to be a professional app. I wanted to teach as many skills as possible in the least amount of time. To do that I avoided error handling here and there. I hope that makes sense?

Leave a Reply

Your email address will not be published.

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

Google+