-
The first part of this series covered a lot of zoomed-out Android concepts, as well as the general structure of Android projects. In this part of the tutorial, you’ll learn more “on the ground” Android: how a layout file works and the specifics of Android layouts and views.
By the time you’re done with this section, you’ll have an app with:
- An image from a PNG file;
- An editable text field for writing messages;
- A button to submit your input;
- A text view that shows your most recent message;
- A list that displays all your messages;
- An option to share your message through Facebook, Twitter, SMS or email; and
- A greeting that saves and retrieves your name each time you open the app.
You should already be at the point where you have a “Hello World” app running on your emulator or device. At the end of the last section, you changed the text so that it greets you personally, like mine:
It’s great that you’ve come this far — but now it’s time to take it to the next level! There’s a lot to do, so let’s get to it!
Getting Started
Looking ahead, the first thing you should do is check that you’re making your app as simple as possible. You don’t want to introduce additional complexity unless it’s needed since extra complexity in how something is implemented means that it takes more time and requires more work if you were to later modify the bits with the extra complexity.
First, open app/res/layout/activity_main.xml. After opening the file, you may have to switch the editor to Textmode if you can’t see the raw XML. Click the appropriate tab at the bottom of the editor pane as shown below.
Note: From now on, this tutorial will always interact with XML files in text mode.The only thing you need to remove here are the padding attributes that Android Studio automatically generates in your .xml layout. They look something like this (your values may vary slightly):
android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin"
Delete these lines from your layout, your activity_main.xml file should now look like this:
<RelativeLayout xmlns:android="http://bit.ly/1I9HKuD" xmlns:tools="http://bit.ly/1IWE4KU" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:text="@string/hello_world" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>Now, double-click on MainActivity.java on the left pane of Android Studio to take a look at your first piece of Android code.
Pro Tip: A quick way to navigate to any file in your project using OSX is to type Shift + Command + O. This is the same shortcut used in Xcode!The only lines you need to remove here are the following:
@Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); }
Be careful not to remove the extra curly brace at the bottom that closes the end of your class.
Now that you’ve finished that bit of cleaning up, you’re all set to begin. It’s time to bring your
Activityto life!XML Layout Basics
Android layouts are in XML format, in the form of a tree with a single root and a hierarchy of views. The hierarchy is very strict and straightforward: each view in the tree is termed the parent of the views it contains and the child of the view that contains it.
Open res/layout/activity_main.xml. You can see the XML for your activity here. There is a parent
RelativeLayoutand a childTextView.Look at the
TextViewspecifically. You can see that it contains three attributes. Of those, two are present in every view you’ll ever put into your Android layouts:layout_widthandlayout_height. The values for these attributes can take several forms:wrap_content: This constant value specifies that the view will be just large enough to fit whatever is inside it, whether that’s an image, text or child view.match_parent: This constant sets the view to be as big as its parent.- Explicit values: You could set the dimension to a specific number of pixels (Ex:
5px), but it is usually wiser to use density independent pixels (Ex:5dp). Adpis a pixel on a “medium-density” (mdpi) device, and the number of actual pixels automatically scales for devices designated as low-density (ldpi), high-density (hdpi), extra-high-density (xhdpi), etc.
In other words, using straight-up pixels would result in your views being all sorts of crazy sizes, depending on whether a device has 160 pixels per inch or 300 pixels per inch, or what have you. Who knows! Let the Android system take care of the scaling and just use
dp.Note: Designations like
mdpiandhdpiare only general categories. Actual pixel densities are even more variable, but they are all given the same scaling factor regardless. Sodpscaling, while convenient, is not an exact science.iOS Developers should be familiar with a similar practice of density independence, using “points” instead of pixels in their layouts to account for early iPhone screens not having Retina displays.
The final attribute of the
TextViewis simplytext, in which you specify the text to be displayed. This attribute is a good example of how different views respond to different attributes. Adding atextattribute to aRelativeLayoutor aSpacewouldn’t accomplish anything because, unlike theTextView, they wouldn’t know what to do with it.But the value of the attribute,
@string/hello_world, isn’t what’s displaying, is it? What you specify in your layout file is not the actual string to be displayed but rather a string resource ID identifying the actual text. That way, all your app’s copy can be in one place – res/values/strings.xml.You can command click on the resource ID to be brought directly to the definition in your resource file.Now let’s look at the parent node in the XML:
RelativeLayout. What’s going on there?Relative Layouts
The layouts in iOS apps used to be in purely absolute terms, like: “Place View X at pixels (x,y)”, but now iOS developers have AutoLayout. Android developers have always needed to keep device screen sizes in mind. Layout files are very well-suited for this consideration.
The default project Studio created for you sets you up with a useful layout: a
RelativeLayout. It is currently the parent layout and theTextViewelement is its child.A RelativeLayout is an intuitive and powerful thing. It holds a bunch of child views and positions them in relation to each other. Here are three examples of what you can easily do with a
RelativeLayout:Example 1: Use
layout_alignParentBottomand the similar attributes for top, left and right to line up a view’s edge with the corresponding edge of theRelativeLayout, which may or may not also be the edge of the screen.Example 2: You can use
layout_toRightOfand the analogous attributes for left, above and below to position oneViewrelative to another.Example 3: You can use
layout_alignRightand the analogous attributes to align a side of oneViewwith another.You can see how that could be useful! For now, though, let’s move on to the layout type you’ll be using for this tutorial.
Linear Layouts
A LinearLayout needs to have an orientation specified, either
horizontalorvertical. Then it lines up its children in that orientation, in the order in which they are specified in your XML.The children of
LinearLayoutsdon’t respond to attributes likelayout_toRightOf, but they do respond to two other attributes:layout_weightandlayout_gravity.Specifying a
layout_weightexpands the view to a proportion of its parent so that the parent weight is the sum of all child view weights.The
layout_weightofViewX
———————————————————————-
The sum of all weights ofViewX and its siblingsConfused? Perhaps the following image might help explain it better.
Notice how the full height of the parent view is split up between the child views based on the layout weight assigned to each child view.
Assigning a
layout_gravityto a view sets its horizontal and vertical positions within its parentLinearLayout. For example, a view might have alayout_gravityattribute with a value likeleft,right, andcenter_vertical. The previous values can also be combined, like this:top|center_horizontal.Then, there’s
gravity, not to be confused withlayout_gravity. Whilelayout_gravityis about where you place the view itself, thegravityattribute defines how you place the content of a view within itself. If you want your text to be left or center justified, usegravity.The following example shows how
layout_gravityandgravitywork in a verticalLinearLayout. Note that the top three have alayout_widthofwrap_contentwhile for the bottom three it’s set tomatch_parent:One handy trick, which you’ll see in just a bit, is that you can nest layouts inside each other. Cue the theme music from Inception!
You don’t want your layouts to be as multi-layered as a Christopher Nolan movie, though. So, if you start to see your nested
LinearLayouts scheme getting out of hand, consider switching to aRelativeLayout.Note: There are also performance reasons for considering a simple layout design. Layout weights require the layout inflater — the class which turns the XML into realViews— to do a tiny calculation to put the view together.Now, modern devices won’t have any trouble rendering your basic layouts. But if you were to make an indefinitely longListView— for instance, one full of cells making copious use of nestedLinearLayouts andlayout_weights — all those extra tiny calculations could add up.Before you move on to the next section, open res/layout/activity_main.xml and change the root node from a
RelativeLayout— what Android Studio gave you as a default — to aLinearLayout.To do that, you should replace these lines:
<RelativeLayout xmlns:android="http://bit.ly/1I9HKuD" xmlns:tools="http://bit.ly/1IWE4KU" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity">And this one at the very end of the file:
</RelativeLayout>
With This:
<LinearLayout xmlns:android="http://bit.ly/1I9HKuD" xmlns:tools="http://bit.ly/1IWE4KU" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity">And this:
</LinearLayout>
Accessing Views From Within Java
Layouts are primarily the domain of your XML. But there are plenty of visual elements you will want to create, destroy, change, and trigger from within your Java code!
So first, edit the
TextViewin activity_main.xml to match the following:<TextView android:id="@+id/main_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="20dp" android:text="@string/hello_world"/>Notice the addition of the
idattribute. Using this tag (or attribute, if you prefer) allows you to access that specificViewfrom within your code, so you can thereafter manipulate theViewvia code.There’s also a change to make in the
texttag. The name of the string resourcehello_worldis a bit outdated now, don’t you think? Right-click on the @string/hello_world part of the line and then choose Refactor > Rename.Then, type in
textviewand click Refactor.This not only changes the name of the resource ID in your layout file, it also changes the original resource ID in your
strings.xmlfile. It also renames the resource ID wherever else it might be used in the project. This is a useful trick to remember when renaming something that appears all over your project!Now open MainActivity.java and add the following line above the
onCreatemethod but below the MainActivity class declaration:TextView mainTextView;
Android Studio will throw an error at you when you leave your cursor on this line that will look like this:
The
TextViewclass hasn’t been imported into MainActivity.java yet so it doesn’t know what aTextViewis. Android Studio can quickly fix that for you. Just tap Alt-Enter on your keyboard while this error popup is present to automatically importTextView.Note: It can get tiresome very quickly having to manually import every single component of an Android App you want to use in a class. Fortunately you can automate this in the IDE settings window, which you can access by going to Android Studio > Preferences (for Mac) or File > Settings (for Windows & Linux) and then clicking Editor > Auto Import and ticking Optimize imports on the fly and Add unambiguous imports on the fly.
Next, add the following code to
onCreateafter the two existing lines of code:// 1. Access the TextView defined in layout XML // and then set its text mainTextView = (TextView) findViewById(R.id.main_textview); mainTextView.setText("Set in Java!");Your MainActivity.java file should now look like this:
public class MainActivity extends ActionBarActivity { TextView mainTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 1. Access the TextView defined in layout XML // and then set its text mainTextView = (TextView) findViewById(R.id.main_textview); mainTextView.setText("Set in Java!"); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } }Finally, run your app and look at the results!
The text set via Java code now appears on screen. What are the steps you just took to make that happen?
- You added an
idattribute to theViewin XML. - You used the
idto access theViewvia your code. - You called a method on the
Viewto change its text value.
Note: You added the code to access and set the text of your
TextViewin theonCreatemethod of yourActivity, meaning that the app runs all the code in that block right away when it first creates theActivity. Activities have strict lifecycles they must follow and it’s thanks to this that you can write code that runs at specific points of an Activities life. You can read more about theActivitylifecycle here.Buttons and Listeners
It’s time to build on your
TextViewand get more interactive! Next up is aButton.Add a
Buttonto activity_main.xml, directly after yourTextView:<!-- Set OnClickListener to trigger results when pressed --> <Button android:id="@+id/main_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:layout_marginLeft="20dp" android:text="@string/button" />Notice there’s an XML comment above the
Button, a reminder of how to trigger results.The
layout_marginattributes simply add 20 density-independent pixels of space above and to the left of theButtonto keep your layout from looking cramped. Remember that the value of 20 will be scaled by the screen density of the device to get an actual pixel value.You’ve probably noticed
@string/buttonunder the button text property appears in red. If you hover over it, you’ll see that the symbol cannot be resolved – and that’s because you haven’t yet defined it.Open strings.xml and add the following line to the bottom to resolve this:
<string name="button">Update The TextView</string>
Next, open
MainActivity.javaand add the following right below the previous line you added to include aTextViewvariable:Button mainButton;
Now add the following code to the end of
onCreate, after the code you added earlier:// 2. Access the Button defined in layout XML // and listen for it here mainButton = (Button) findViewById(R.id.main_button); mainButton.setOnClickListener(this);
Again, you see the same three steps as when you added code to access the
TextView:- You add an
idto theViewin XML. Or, in this case, you add a view with anidattribute. - You access that
Viewin code by using theid. - You call methods on that
View.
This time, the method you called on the
ButtonissetOnClickListener. What you put in the parentheses of that method becomes the answer to this question: WhichObjectis going to respond when thisButtongets pressed?To answer that question with simply the word
thisseems a little curt and unspecific, but Java knows that it meansMainActivityitself is your intended listener.This means that
MainActivityhas to implement theView.OnClickListenerinterface. If that sentence doesn’t make much sense to you, I suggest finding an intro on what an interface is and how to create one, like this one.Note: An interface is like part of a job description. If I’m a young, ambitious Java object, and I want to be able to put a particular certification on my resume, there are a few methods I have to be comfortable and capable of performing. The interface is like the checklist I need to pass, or “implement.”If have an iOS/Objective-C background, an interface is comparable to a protocol. In fact, in object oriented programming the words protocol and interface are used interchangeably.Android Studio is smart and can help you do the implementation. Simply single-click on
this, which is underlined in red, indicating an issue (in this case the fact thatMainActivitycurrently does not support the necessary interface). Then, when a red light bulb appears at the beginning of the line, click on it and select Make ‘MainActivity’ implement ‘android.view.View.OnClickListener’.Simply click OK on the next dialog, which lets you know which method(s) Studio will automatically create for you.
Studio then generates the code necessary to make your
MainActivityqualify as a union-certifiedOnClickListener.First, it added a bit to the class declaration indicating that the
Activityimplements a specific interface:public class MainActivity extends ActionBarActivity implements View.OnClickListener
Second, Studio added a stub for a method you need to implement in order to get your
OnClickListenerlicense (other interfaces may require more than one method to be implemented):onClick. This method fires when yourButtongets pressed.@Override public void onClick(View v) { }
The method currently does nothing. So add the following code to
onClickto make it do something:// Test the Button mainTextView.setText("Button pressed!");Can you tell from the code what should happen? Run your app and see if you’re right…
The app now changes the text in the
TextViewwhen you press theButton. Cool! You’ll be putting thisButtonto even better use later — to submit input.Adding a Visual and Nested Layouts
It’s always fun to include images in your UI. So how about adding an
ImageViewto show a little icon? Along the way, you’ll also get to see how a nestedLinearLayoutworks.First off, what image will you show? Well, it’s easiest to start with the image you’re given by default. It’s already in your project and here’s where to find it.
Use the Project Navigator to expand the
res/drawabledirectory:You can see a folder with multiple copies of the same image within
res. Notice that the file names have brackets at the end that look like they contain screen density abbreviations.Indeed, those abbreviations correspond to the pixel density buckets used to classify Android devices in dots per inch (dpi):
mdpi:mediumhdpi:highxhdpi:extra highxxhdpi:extra extra high
You can even create your own
xxxhdpifolder where you can place images for devices with even higher pixel densities. Personally, I think they should use Roman numerals for the next level up and call itxlhdpi, but on second thought that would probably be a terribly confusing way to go…Look inside the
drawabledirectories. You’ll see a file named ic_launcher.png. This is simply the default launch image you’re given, at several different sizes for different screens. The Android system will pick the right one for the device.Now head back to activity_main.xml and replace the following section:
<!-- Set OnClickListener to trigger results when pressed --> <Button android:id="@+id/main_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:layout_marginLeft="20dp" android:text="@string/button" />With this:
<!-- This nested layout contains views of its own --> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <!-- Set OnClickListener to trigger results when pressed --> <Button android:id="@+id/main_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:layout_marginLeft="20dp" android:text="@string/button" /> <!-- Shows an image from your drawable resources --> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:layout_marginLeft="20dp" android:src="@drawable/ic_launcher" /> <!-- Closing tag for the horizontal nested layout --> </LinearLayout>You just added a new
LinearLayoutinside the existing rootLinearLayoutlayout, directly underneath theTextViewas its new sibling. You also moved the existingButtoninto the nested layout and added a newImageView, as well.By wrapping your
Buttonin a second, horizontalLinearLayout, you are able to place aButtonand anImageViewside-by-side horizontally, even as the root layout has a vertical orientation.As for the
ImageViewitself, the important attribute issrc, to which you give your drawable image resource. Note the format you use to reference the drawable image. You need to prefix the file name of your image (minus the file type) with@drawable/.Run the app, and you’ll see the new image right beside the button!
Involving the Keyboard
Now it’s time to get some user input… by introducing an
EditText. This is a special subclass ofTextViewthat opens the keyboard and displays what the user types as its content.Add the
EditTextXML to activity_main.xml as a sibling to theTextViewand the horizontalLinearLayout. Be careful not to get it caught inside the nested layout! Instead, add it right after the closing for the embedded linear layout and just before the closing for the root linear layout.<!-- Displays keyboard when touched --> <EditText android:id="@+id/main_edittext" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:layout_marginLeft="20dp" android:hint="@string/hint" />Notice the special attribute,
hint. You’re using this text as a placeholder in the input field. The app will overwrite it once the user starts typing.As usual, you need to define the string resource for your hint in res/values/strings.xml:
<string name="hint">A Name</string>
Now open MainActivity.java and add a new variable for the
EditText(below the other two existing variables):EditText mainEditText;
Next, add the following code to the end of
onCreate:// 3. Access the EditText defined in layout XML mainEditText = (EditText) findViewById(R.id.main_edittext);
The above code, similar to the previous code, simply gets a reference to the
EditTextcontrol and saves it in the assigned variable.Now that you have a reference to the
EditTextcontrol, you need to do something with user input. Replace the current contents ofonClickwith the following:// Take what was typed into the EditText // and use in TextView mainTextView.setText(mainEditText.getText().toString() + " is learning Android development!");When
mainButtonis clicked, themainTextViewwill now be set to display a string including the contents ofmainEditTextconcatenated with ” is learning Android Development!”.Run your app, and test this out!
Now you receive user input with an
EditText, submit it with aButton, and display it in aTextView. Very nice! But how about visualizing more than one piece of data at a time?The ListView
The
ListViewis a useful control that visualizes a list of items. It’s analogous to aUITableViewin iOS.You define a
ListViewjust as you would any other view in your XML. Add one to activity_main.xml as a sibling to theTextView, the horizontalLinearLayout, and theEditTextby adding the following lines after the lines for theEditTextcontrol:<!-- List whose dataset is defined in code with an adapter --> <ListView android:id="@+id/main_listview" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:layout_marginTop="20dp"/>Wait… what? How in the world could setting
layout_heightto0dpbe a good idea? After all, no matter what screen you’re on,0is always going to scale to0.Well, take a look at what directly follows: a
layout_weight. Since you haven’t given anything else in your layout a weight yet, theListViewis going to expand to fill as much space as possible, no matter what value you give thelayout_height.The general practice, then, is to use a value of
0so the layout inflater has one fewer dimension to think about and can get the job done a bit quicker.Now open MainActivity.java and, add the following variables below the ones you’ve already added above the
onCreatemethod:The one for the
ListViewmakes sense. But what about the others? The others are for supplying theListViewwith data to display. All will be explained in a bit :]But first, add the following code to the end of
onCreate:// 4. Access the ListView mainListView = (ListView) findViewById(R.id.main_listview); // Create an ArrayAdapter for the ListView mArrayAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, mNameList); // Set the ListView to use the ArrayAdapter mainListView.setAdapter(mArrayAdapter);
Some of that looks familiar by now: finding the
ListViewusing itsid. But what else is going on?mArrayAdapteris an example of an adapter, which is basically a go-between so yourListViewcan get the data it needs.Note: I think of aListViewas being a picky sort, as far asObjectsgo. It’s great at what it does but doesn’t want to get its hands dirty with any real data. It’s all got to be prepared for it or else it’ll throw a fit.TheAdapter, then, is the enterprisingObjectthat is able to code-switch between the rough language of the datasource and the refined dialect of theListView.When you create
mArrayAdapter, you have to specify theContext, the target XML view for the data (simple_list_item_1), and the datasource (mNameList).But hang on, you didn’t write anything with an
idofsimple_list_item_1! So where is that coming from? Also what exactly is aContext?Notice the
android.R.layoutpart beforesimple_list_item_1. There are several important concepts here, but let’s look at theRbit first.R(or,R.java, if you prefer) is a dynamically created class which gives you access to the resources in your project. If interested, you can read more about accessing resources via theRclass, here.As the linked article above explains, you can use the
Rclass to get a resource ID by specifying a resource type and a resource name. The resource type would be something likestring,drawable, orlayout– matching the various resource types you see in your project. And thus, thelayoutpart inandroid.R.layout.simple_list_item_1simply specifies that you are referring to a layout resource.But what about the
androidprefix? Why is it there? It is an indicator that you didn’t create the view; it’s already part of the Android platform. It represents a simpleTextViewthat a default list cell can use.The Context is an object that represents the current state of your App. Do you need to access a specific service for your App to use? Context is your guy. Do you need your App to show a specific
VieworActivity? Context is the mastermind behind it.In this instance, Context is used to create the Views used within your
ListView. Remember that layout resource you are referring to? This is the layout the context takes and converts into a view, the adapter then populates each view with a value from its datasource.The datasource in this case is
mNameList, which is simply a list ofStrings. It’s initialized, but empty. So the next step is to add some data that theListViewcan display.Add the following code to the end of
onClick:// Also add that value to the list shown in the ListView mNameList.add(mainEditText.getText().toString()); mArrayAdapter.notifyDataSetChanged();
You simply add whatever the user typed into the
EditTextto the list of names and then shoot a signal to the adapter to update what’s shown in theListView.Now run your app.
You should be able to type a name into the
EditText, then see the name used in theTextViewand added to a new row in theListViewwhen you press theButton. Cool!Detecting List Selections
Looking at the items in a list is cool, but this is an app, and interactivity is even better! So next you’re going to set up a way to detect user selections from the list.
First, modify the class definition in MainActivity.java to add support for another interface. To do this, modify this line:
public class MainActivity extends ActionBarActivity implements View.OnClickListener {
To look like this:
public class MainActivity extends ActionBarActivity implements View.OnClickListener, AdapterView.OnItemClickListener {
All you’ve really done is add support for a new interface –
AdapterView.OnItemClickListener, which, as the name suggests, listens for item selections from aListView.MainActivityis really stacking up the credentials!You should also have a red line highlighting the line where you’ve just added your new interface, Android Studio is letting you know that you haven’t actually implemented the interface yet. You can fix this easily by clicking the highlighted line, pressing Alt-Enter,clicking Implement Methods and finally clicking Ok.
Next, add the following code to the end of
onCreate:// 5. Set this activity to react to list items being pressed mainListView.setOnItemClickListener(this);
The above code sets
MainActivityas the listener for any item clicks onmainListView.Now replace the
onItemClickMethod that was automatically generated for you with the following:@Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // Log the item's position and contents // to the console in Debug Log.d("omg android", position + ": " + mNameList.get(position)); }
You may need to use your old friend Alt-Enter to import and use
Log.Well done! Your
MainActivityClass has implementedonItemClickand can live up to the title of being a card-carryingOnItemClickListener.But what’s happening inside
onItemClick? There’s a weirdLog.din there, and then something with aget(position)…Take a look at what you’re passing along to
onItemClick. In particular, look atint position, which is an integer equal to the index of the item the user pressed on the list (counting up from 0).You take that position, as well as the item at that index in your list of names, and log them. Logging is a very basic, but very useful debugging technique.
Run your app, enter a few values and add them to the list, just as before. Then select an item. There’s no visible effect for the moment.
With the app still running, look at the bottom section of Android Studio:
The bottom left section of the window contains information about your Device or Emulator, what processes are running and logs of what is currently happening within those processes. These logs appear in a console called
logcat. It will read off tons of stuff from your emulator or device, the majority of which is not of much interest to you at this point. The log statements you generated with your selections are here, but there’s too much noise to see them.Here are some useful ways to filter out the noise and see only what you want:
Notice the option for Log level in a dropdown at the top of the console. When you put your
Logcommand into code, it specifically was theLog.dcommand. Thedis for “debug” level. The levels are:v: Verbosed: Debugi: Infow: Warninge: Error
When you select a log level for
logcat, it will only show messages at that level or higher. And the levels start at verbose and go up to error, in the same order as listed above. So, if you select the log level as Warning, then you’ll see all warnings and errors but nothing else.Meanwhile, you can use the text box to the right of the log level drop-down to apply a filter and show only those messages that contain the text you typed in.
Now that you know this, set the log level to Debug and type omg android into the filter text box.
Great! You now have a clean feed of log statements and you can detect when a certain item gets selected in your list. The ability to log will come in handy as you create more complicated apps and want to stay informed of the inner workings of your code.
The Action Bar
Your app has several different views now, and it’s time to think about other ways to add functionality. Older Android devices used to have a Menu device button that would display a bunch of options depending on the situation, but since Honeycomb in early 2011, Android has used the Action Bar to display any options for the current view.
The Action Bar provides a familiar base for your users. Since it’s present across apps, making good use of the Action Bar means a significant part of your app’s functionality will be immediately intuitive to an Android user. Conversely, neglecting the Action Bar would confuse all your users who expect it to work — and that would be weird!
The Action Bar is already in your app — it just has no options attached to it yet. That will be your first order of business next!
Sharing
Soon you’ll have a chance to show off the fact that you’re learning Android, from within your own app! You’ll do this using an intersection of the Android concept of the
Intentand the Action Bar, known as aShareActionProvider.One of the advantages of an
Intentis that you can construct it in either a specific (explicit) or generic (implicit) manner. You used an example of the explicit type when you specifically defined theIntentthat launches your app, which the manifest then identifies asMainActivity. Now you’ll see an example of the implicit type.A generic
Intentionreally helps in this case. After all, some of us prefer to share things with the entire world, and others with just a few friends. Rather than wondering what a potential user’s favorite social network might be and integrating them one by one, you can politely tell the Android device that you’d very much like to share a bit of content (thus expressing anIntent), and Android will graciously take it from there!Navigate to res/menu/menu_main.xml and open it.
You’ll note that there’s some auto-generated XML in there, but you don’t need it. Replace the whole thing with this:
<!-- Defines the menu item that will appear on the Action Bar in MainActivity --> <menu xmlns:android="http://bit.ly/1I9HKuD" xmlns:omgandroid="http://bit.ly/1KgLl9m"> <!-- Share item --> <item android:id="@+id/menu_item_share" android:title="Share" omgandroid:showAsAction="ifRoom" omgandroid:actionProviderClass= "android.support.v7.widget.ShareActionProvider" /> </menu>Because your App is running on versions of Android lower than Lollipop, it’s often the case that you need to use features that don’t exist on previous versions of Android. This means you ether build your own functionality to ensure users have a seamless experience across multiple versions or you provide it using 3rd party libraries.
Google provide various App Compatibility libraries to try and reduce this fragmentation issue. In the XML above, you can see you are making use of the android.support.v7 libraries in your XML. Making use of the support library now means the code you are about to implement will work all the way down to Android v7. You can read more about the support libraries here.
Now head over to MainActivity.java and add the following variable underneath the rest of your variables:
ShareActionProvider mShareActionProvider;
Note: Studio may be confused about whichShareActionProvideryou mean, so if it asks, useandroid.support.v7.widget.ShareActionProvider. If you have enabled auto imports then double check it has imported the right one.Next, you need to add the following two methods to the class – the first one,
onCreateOptionsMenu, might already be implemented in the class. If it is, simply replace the existing implementation with the new one.@Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu. // Adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); // Access the Share Item defined in menu XML MenuItem shareItem = menu.findItem(R.id.menu_item_share); // Access the object responsible for // putting together the sharing submenu if (shareItem != null) { mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(shareItem); } // Create an Intent to share your content setShareIntent(); return true; } private void setShareIntent() { if (mShareActionProvider != null) { // create an Intent with the contents of the TextView Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("text/plain"); shareIntent.putExtra(Intent.EXTRA_SUBJECT, "Android Development"); shareIntent.putExtra(Intent.EXTRA_TEXT, mainTextView.getText()); // Make sure the provider knows // it should work with that Intent mShareActionProvider.setShareIntent(shareIntent); } }
Important: If there’s an implementation ofonOptionsItemSelectedin the class, remove it.Add your imports so they are recognised by your
Activity.onCreateOptionsMenugets called once, when the activity first starts. Similar to how you specified which layout XML file you wanted to use for the activity inonCreate, you now direct the menu inflater to look atmenu_main.xml for the menu items that go on the Action Bar.From there, you can access the menu item you defined in XML by its
id,menu_item_share, and then you can access its action provider. Previously, you specified that this item’s action provider was aShareActionProvider. So, you can safely cast to that type in your code and hang onto a reference to it via themShareActionProvidervariable.Then, you call
setShareIntent. This method creates anIntent, but not just anyIntent. It creates anIntentwhose action you’ve set toACTION_SEND. It’s truly as generic as it looks: you’re going to tell Android you want to take the action of sending something.From there, you set the
Intent‘s content type, subject — used by email programs and the like, as the subject header of the message —, and text. The text matches whatever is currently in yourTextView. After you’ve packed up everything theIntentneeds to know, you pair it withmShareActionProvider.This code will work, but only “kind of.” As-is, you only call
setShareIntentonce, at the creation of the menu. It would be much better to update theIntentwhenever theTextViewchanges — otherwise you’re stuck with the initial message forever!Add the following code to the end of
onClick:// 6. The text you'd like to share has changed, // and you need to update setShareIntent();
Here, you simply make sure that the share intent is always up-to-date.
Run the app, and try out the new sharing feature – tapping the share icon on the Action Bar should reveal a number of choices, depending on what is installed on your emulator or device.
The
ShareActionProviderautomatically puts together an array of possible avenues for sharing content based on the apps you have installed on a given device. This array of options will differ from device to device. The emulator will most likely have far fewer options for sharing, whereas you may have apps like Twitter andFacebook on an actual device and could share through those networks, too.Remembering Your Name
Everything you’ve done so far with regard to user input only persists while the app is running. But what about between sessions? Let’s see some data persistence in action, with a new feature that will record and remember your name each time you open the app.
There are a few good options on Android to persist data, and the simplest one is
SharedPreferences.SharedPreferencesstores data in key-value pairs, meaning that you specify a name (the key) for a piece of data (the value) when you save it, and you can retrieve it later by using the original key.Let’s see how it works in action, shall we?
First, add the following constants and variable to MainActivity.java (to the same place as the previous variables):
private static final String PREFS = "prefs"; private static final String PREF_NAME = "name"; SharedPreferences mSharedPreferences;
The above sets
PREFandPREF_NAMEat the top of the class. You’ll usePREFas a filename to keep yourSharedPreferencesin a single location. You’ll usePREF_NAMEas the key for storing your name in shared preferences.Note: It’s a good practice to use variables forStringsthat you’ll need/refer to multiple times. That way, if you need to change the string value later, you can do it in one place. You’ll also never have to worry about some weird spelling error creating bugs in your code.The final line adds a variable named
mSharedPreferencesfor storing a reference to the shared preferences class. You only need to access it in a few places, but it will be useful to hang onto it. Add the import into your Class if you haven’t already.Next, add the following lines to the end of
onCreate:// 7. Greet the user, or ask for their name if new displayWelcome();
The new code calls a new method,
displayWelcome. So implement that by adding the following method at the end of the class:public void displayWelcome() { // Access the device's key-value storage mSharedPreferences = getSharedPreferences(PREFS, MODE_PRIVATE); // Read the user's name, // or an empty string if nothing found String name = mSharedPreferences.getString(PREF_NAME, ""); if (name.length() > 0) { // If the name is valid, display a Toast welcoming them Toast.makeText(this, "Welcome back, " + name + "!", Toast.LENGTH_LONG).show(); } }Note: While you could conceivably place all the code fromdisplayWelcomedirectly intoonCreateand it would still work, many people, your humble author included, prefer to keep method lengths reasonable by calling separate methods for a specific task.This also means that if the same task needs to be performed from elsewhere in code later on, you already have a handy method in place for it :]In the new method, the first thing you do is access
SharedPreferences, withMODE_PRIVATEmeaning that only your OMGAndroid app can access the data stored here. This means that your saved data will not get overwritten by another application which might have used the same key as you.Then you simply ask the preferences object for whatever value is stored using the key
PREF_NAME. The second parameter for the method can be used to set a default value to be returned in case there is no value stored using the key you provide. So, you use an emptyStringas the default value here.Finally, you check to see if the retrieved
Stringactually has any content, and display a message if so. Your message takes the form of aToast, which is a short-lived pop-up message that appears for a bit and then fades away. Give theToasta message it should display, specify one of its built-in lengths to remain on the screen and then simply tell it toshow. Easy!Displaying the Name Dialog
What you’ve set up so far will show your name if the application can retrieve it out of the preferences. But obviously, that’s no use to you yet since you have no mechanism in place to save your name in the first place!
You’ll use a
Dialogto achieve that.Dialogsare small windows that alert the user. They may contain ways for the user to provide input or make choices. You’re going to use anAlertDialog, specifically.Add the following code to the end of
displayWelcome, creating anelsebranch for theifcondition that’s already there:} else { // otherwise, show a dialog to ask for their name AlertDialog.Builder alert = new AlertDialog.Builder(this); alert.setTitle("Hello!"); alert.setMessage("What is your name?"); // Create EditText for entry final EditText input = new EditText(this); alert.setView(input); // Make an "OK" button to save the name alert.setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Grab the EditText's input String inputName = input.getText().toString(); // Put it into memory (don't forget to commit!) SharedPreferences.Editor e = mSharedPreferences.edit(); e.putString(PREF_NAME, inputName); e.commit(); // Welcome the new user Toast.makeText(getApplicationContext(), "Welcome, " + inputName + "!", Toast.LENGTH_LONG).show(); } }); // Make a "Cancel" button // that simply dismisses the alert alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) {} }); alert.show(); }The app will reach this
elsecondition when there is no valid name saved using thePREF_NAMEkey. You use anAlertDialog.Builderto give yourAlertDialoga title, a message, and anEditTextin the center for the user to type in their name.Then, you add two buttons to the
AlertDialog: a positive and a negative button. The first thing you define for each is the text displayed on the button – “OK” and “Cancel” are pretty standard choices. The second thing you define for each button is anOnClickListener.This time your
OnClickListeners are specificallyDialogInterface.OnClickListeners, and you are defining them right away. Notice how the parameters foronClickare slightly different.For the positive button’s listener,
onClickdoes quite a bit. First, it reads the name that the user typed into the dialog’sEditText.It then saves that name into
SharedPreferencesusing a helper called aSharedPreferences.Editor. You simply tell the editor what to save and where, tell it to commit the changes, and that’s it!Finally, it displays a
Toastidentical to the other welcoming one.The negative button’s listener is far simpler: it does nothing! Nothing!
Run your app and check out your
Dialog.Type in your name, press OK and see the
Toastgreeting. From now on, your app will remember your name and greet you each time you launch it!Where to Go From Here?
You covered a lot of UI concepts in this part of the tutorial! Take a few minutes to play around with your app a little, maybe sharing something with a friend!
You can get the full source code for this part of the tutorial on GitHub or as a .zip.
Those looking for a challenge should try:
- Setting up the
EditTextto expect names as its input so that it capitalizes first letters. - Dismissing the keyboard associated with the
EditText. - Making the Done button on the keyboard do the same thing as the Update TextView button.
- Modifying font sizes on the various views.
Hopefully you have found this helpful! If you have any comments or questions, feel free to leave them below. And of course, don’t miss Part Three, in which you set up your app to interact with data online!
The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.
The post Android Tutorial for Beginners: Part 2 appeared first on Codzcook.
We are Learncodz.
Posts
Comments
The Team
Blog Codz Author
Popular Codz Article
-
I rewrite this tutorial from forum.xda-developers.com : The users of Micromax A116 Canvas Hd can now update their handsets to Android 5.0 Lo...
-
Build Your Own URL Shortener With YOURLS
What You'll Be Creating In this tutorial, I'll show you how to install your own open source, PHP-based URL shortener, called YOU... -
Wifi Hacking – WEP – Kali Linux Aircrack-ng suite
Alright, this post is written assuming you have Kali Linux up and running on your computer. If not, here is a post on hacking with kali linu... -
Top 4 Affordable Android One Smartphones from Rs. 6000
If you’re looking for an affordable smartphone to buy this holiday season and aren’t too keen on a Windows Phone, look no further than the A... -
[MOD][4.1 4.2] Extend Phone Storage 1.5, 2.5GB A116 (Other MT6589 Devices on request)
READ EVERYTHING CAREFULLY I Will Not Responsible For Any Brick or Any Problem So Do It At Your Own Risk. This Will Extend your Device's ... -
want to build an chat application..(parse)
Introduction The Parse platform provides a complete backend solution for your mobile application. Our goal is to totally eliminate the nee... -
Distributing iOS Apps With iTunes Connect
Once you've developed your iOS or OS X app, it's time to submit it to Apple for release in the App Store. This process is done throu...
Portfolio
- 2015 at 02:00AM
- 2015 at 02:03AM
- 2015 at 02:07AM
- 2015 at 02:09AM
- 2015 at 03:51AM
- 2015 at 03:57AM
- 2015 at 04:03AM
- 2015 at 04:08AM
- 2015 at 06:38AM
- 2015 at 08:03PM
- 2015 at 08:09PM
- 2015 at 08:13PM
- 2015 at 08:18PM
- 2015 at 08:23PM
- 2015 at 08:32PM
- 2015 at 08:33PM
- 2015 at 08:42PM
- 2015 at 08:50PM
- 2015 at 09:08AM
- 2015 at 09:12AM
- 2015 at 09:20AM
- 2015 at 09:22AM
- 2015 at 09:25AM
- 2015 at 09:27PM
- 2015 at 09:28AM
- 2015 at 09:31PM
- 2015 at 09:34AM
- 2015 at 09:58AM
- 2015 at 10:31AM
- 2015 at 10:45AM
- 2015 at 10:46PM
- 2015 at 10:50AM
- 2015 at 10:57AM
- 2015 at 10:57PM
- 2015 at 10:58AM
- 2015 at 11:04PM
- 2015 at 11:07AM
- 2015 at 11:17AM
- 2015 at 11:20AM
- 2015 at 11:31AM
- 2015 at 11:32AM
- 2015 at 11:33AM
- 2015 at 11:39AM
- 2015 at 11:44AM
- 2015 at 11:45AM
- 2015 at 11:45PM
- 2015 at 11:46PM
- 2015 at 11:50PM
- 2015 at 11:51PM
- 2015 at 11:52AM
- 2015 at 11:57AM
- 2015 at 11:58PM
- 2015 at 12:02PM
- 2015 at 12:04AM
- 2015 at 12:08PM
- 3d maxx
- and
- Android
- android developer
- android developr
- android labels
- android sdk
- android studio
- android tutorial
- android tutorials
- apk
- apple
- application
- apps
- April 19
- April 20
- April 21
- April 22
- April 23
- April 24
- April 25
- At its simplest
- bac
- backtrack
- battery
- biotechnology is technology based on biology - biotechnology harnesses cellular and biomolecular processes to develop technologies and products that help improve our lives and the hea
- Blog
- blogger
- browser
- build an chat application
- C
- chrome app
- command prompt
- designing
- Developing App
- earn money
- games
- hack
- hack Facebook
- Hacking
- help
- How To
- htc
- IFTTT
- intel xdk
- Ios
- ios 8
- ios8
- ips 8
- Java
- Javascript
- lenovo
- mac
- Magento
- Mysql
- new launch
- nexus
- operating system
- OS
- phone
- Photoshop
- Php
- pivotal tracker
- Review
- Reviews
- roms
- root
- Ruby On Rails
- samsung
- sdk
- security
- swift
- Tech. News
- Tools:Tips
- tutorial
- Uncategorized
- unity
- update
- Visual Studio
- windows phone
- Wordpress
- wordpress tutorial