Welcome 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 [googleplusone]
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; } }
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
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
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 ๐
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.
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!
Thank you very much ๐ I do my best. There must be a little typo some place. Did you copy the code directly from my site? I plan on covering the listview. I’m hoping to cover most everything by the end
Yeah, the typo was in the ID of the TableLayout inside the ScrollView (in the main XML). I Download the source code and now it’s working. thanks…
Great I’m glad you got it working ๐
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
Thank you ๐ It would be best to set the minimum to level 9, which is what I did in the code. Sorry if I glossed over that. I do that some times when I’m trying to crank out so much information
try this,this may work, i’m not sure
preferencesEditor.commit();
Thank goodness. I figured out how to get that error to go away. You have to open up the Android Manifest in the res folder. Then change the minSDK from “8” to “9”!
I’m glad you fixed it ๐ Sorry i couldn’t help more
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!!
You’re very welcome ๐ I’m very happy that I was able to help.
I never did anything Android related in regards to Access, but this article may help http://www.techrepublic.com/blog/tablets/how-to-access-microsoft-office-365-from-an-android-tablet/2650
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 gettingonClick(View v)
, I getonClick(DialogueInterface dialogue, int which)
. Any idea why that’s happening?Thank you for the kind words ๐ I’m not sure about the problem your having. Does everything run after you put in the code?
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.
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
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 ๐
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!?
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)
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
Thank you very much ๐ You’re very kind.
Getting error @ line 245 in MainActivity.java
Intent intent = new Intent(MainActivity.this,StockInfoActivity.class);
“StockInfoActivity” cannot be resolved to a type.
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
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?
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?
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 ๐
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 ๐
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.
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??
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.
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.
What error do you see in the LogCat panel in Eclipse? I also have all the code in package form on this page. Try importing it and see if you get any errors.
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!
Yes I think that would be a good idea. As you said I pretty much write this code out of my head and so it isn’t optimized. I appreciate you taking the time to help ๐
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
ok you can ignore the error for the third code line. should’ve watched the video to the end before posting ๐
Great I’m glad you got the code working ๐
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.
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.
hey Derek, Thanks again man for the wonderful, wonderful tutorials. I actually started with your tutorials in Android Studio, but moved to Eclipse Studio once I completed all the tutorials there.
Now I am using these tutorials to learn the things in Android Studio, hope that I am not messing around, both the IDEs are almost same, and you have already covered a lot of content here.
So, while trying out things designed from something and testing on others, I faced my first show stopper kind of issue. Its difficult to explain in words, but still I’ll try.
So, consider a scenario of table layout where I have added a table row and added 3 components (say a textView, EditText & a button, much like your EnterSymbol, , Enter button). Now I added another table row just below the last one. I added just one component here (say a TextView, much like Stock Symbols and Quotes), this TextView takes up just first cell of the 2nd row (a table will be created with 3 columns on entire screen). I tried your layout_span trick, but it didn’t gave very satisfactory result. On landscape mode, text is not centred.
The main problem is coming with ScrolableText area where everything is trying to fit in just first column. Don’t know what to do.
I know I explained my problem pathetically, but please do help me. I genuinely need your help to sort this out. Please do tell me if you need more clarification on this.
PS. I tried to run your code on Android Studio. Same result, so you can try same on your own. ๐
Thanks a ton buddy in adavance ๐
Hey Mohit,
Sorry about any confusion I caused. I made this video a long time ago. (I’m continuing the Android Studio tutorial next week) I may not have covered making a separate layout for horizontal in this tutorial. When I was making the first Android tutorials I didn’t make complete optimized apps. Either way to fix that issue you’d need to just make a horizontal layout and it would fix everything.
hey Derek, thanks for solution. I’ll try that. however, meanwhile I tried adding tablelayout in table row and creating table rows in inner table layout. not the best solution I guess. I think if I remember well, you have once told in some video that multiple layouts will make ui slower as more code will be there to parse…so anyways I’ll try your approach. not in best of health so may be delayed a bit ๐
Also, I got a request for you. If you can make some tutorial for AsynTask and other methods to interact with UI thread from application thread, that will make a clear a lot of things. Also a background threads like of process.
And another request is for MediaStore and its nested classes. Totally confused by android docs ๐
For now, anxiously waiting for your new video.
Cheers!!! ๐
I covered AsynTask in this tutorial series and will again in my new Android tutorial next week. I’ll do a bunch about threads next week as well. Interfaces will follow that. I hope they help ๐
Great!!!
Will be waiting ๐
Thanks again.