====== Chapter 9 ======
The changes to Android Studio 1.5.1 have put this chapter well out of sync with both the "Blank Activity" and "Empty Activity" templates. So while this isn't a very long chapter, there will be a //lot// of corrections/addenda.
It might be a little ambiguous in the Chapter intro, so here's a summary of what you're going to do in the chapter. You will build an app that has an //action bar//. An action bar is space at the top of an app that holds the activity name or other info on the left (in LTR languages) as well as a menu on the right (in LTR languages). The menu will have a number of items; some of those items will be specified to be pulled out of the drop down list and placed on the action bar as //action buttons// alongside the menu.
The action bar is a bit of a clustercuss at the moment. It wasn't to Android added until relatively recently, and if you want to build apps using action bars that target older versions of Android, you'll have to use compatibility libraries. The syntax when compatibility libraries is just different enough from current Android "native" action bars to make things maximally painful to move from one to the other.
Another issue is that the compatibility libraries seem to be buggier than the current Android "native" action bars. So, I think it's best not to mess around with the compatibility libraries at first. Let the fun begin.
===== p. 370: Creating the project =====
When creating the project, set the minimum API level to **21** (not 17).
It will hurt if you don't.
Be sure you specify an **Empty app template** in the new app wizard.
==== menu_main.xml ====
The Empty activity template does not generate a ''menu_main.xml'' file, so you'll have to add one manually. In Android Studio, right click on the ''res'' folder in the Android view, select //New > Menu resource file//, and give the file the name ''menu_main.xml''. Then replace the contents of that file with the following:
and create a string resource Settings
The ''main_menu.xml'' code you entered above will probably show you that there are errors. If this is the case, it means that Android Studio set up your app to use the compatibility libraries. The book tries to show you how to build the app either way, but we're only going to build this //not// using the compatibility libraries.
==== v7 appcompat libraries ====
Way over on p. 385 there is an aside about action items not appearing when using some versions of the v7 appcompat library because of an Android bug. I've been bit by this and it stands as the main reason I suggest not trying to use them for your maiden action bar voyage.
So, start by removing the project's dependency on the v7 appcompat library by using //File > Project Structure...// and selecting //app > Dependencies//, then right clicking on the appropriate entry to remove it.
This will make ''MainActivity.java'' very angry. Make MainActivity extend Activity rather than ActionBarActivity (and fixing imports), and this will make ''MainActivity.java'' a little happier. But you'll probably notice that ''R'' is broken. ''R'' is broken at this point because ''styles.xml'' makes reference to a "Theme.AppCompat.Light.DarkActionBar" value, which no longer exists because we're not using the compatibility libraries any more. We'll fix that shortly.
Isn't this fun?
===== p. 372 =====
Open up ''AndroidManifest.xml'' and addandroid:label="@string/app_name"
to the element. You might want to verify that the ''@string/app_name'' resource exists.
If a label isn't specified for an activity, Android will use the app's label instead. In this case we are setting the activity's label to be the same as the app's label, but this doesn't have to be the case.
===== p. 374 =====
Now let's take care of the problem caused by the "Theme.AppCompat.Light.DarkActionBar" reference. Open ''styles.xml'' and change
to
Also get rid of the items for colorPrimary, colorPrimaryDark, and colorAccent.
If you are seeing errors now, it might be because you forgot to specify a minimum API level of 21 when you create the app. You can fix that now by opening //File > Project Structure...// and selecting //app > Flavors// and for the defaulConfig's "Min Sdk Version", manually type in 21.
Try running the app. It's smoother sailing from here out.
===== p. 379 =====
Let's add an action to the menu. Open '''' and insert the following code before the "action_settings" item:
Be sure to use ''android:showAsAction'' rather than ''app:showAsAction''. The former works with "native" action bars; the latter is the syntax if you're using the compatibility libraries.
Isn't that nice?
Add the "@string/action_create_order" resource with a value of "Create order".
==== The icons ====
The code above references "@drawable/ic_action_new_event", which means Android is expecting to see ''ic_action_new_event.png'' icon files in the ''drawables'' folders. Don't bother looking for the specified icon files in the file you're directed to download in the text. They're not there anymore. For reasons known only to the Google, they've been removed.
Whee.
Instead the authors are now recommending you grab images from the ''drawables-*'' folders at http://tinyurl.com/HeadFirstAndroidNewEventIcons and drop them into folders of the same name in your project. You'll probably have to create the corresponding ''drawables-*'' folders. Or you can grab {{:android_learning:headfirst_android_development_notes:ch9-drawable-folders-with-images.zip|this zip}} that has the folders with images already in them.
===== p. 380 =====
The author's code on this page doesn't need to be modified.
Yay.
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Add menu_main items to the action bar (if present):
getMenuInflater().inflate(R.menu.menu_main, menu);
return super.onCreateOptionsMenu(menu);
}
Use Android Studio's code generation feature to stub out overridden methods. It saves a lot of typing.
===== p. 381 =====
The author's code on this page doesn't need to be modified. But I would suggest you add a Toast to each of the cases to confirm that things are working as expected:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_create_order:
//Code to run when the Create Order item is clicked
Toast.makeText(MainActivity.this, "Create!", Toast.LENGTH_SHORT).show();
return true;
case R.id.action_settings:
//Code to run when the settings item is clicked
Toast.makeText(MainActivity.this, "Settings!", Toast.LENGTH_SHORT).show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
===== p. 382 =====
Be sure to create a new Empty activity. It should automagically generate with a parent class of Activity. Create a menu file for this activity as you did before.
===== p. 383 =====
Add the specified code and take the app for a spin. Everything good?
Notice the name of the new activity is given as "Bits and Pizzas". This is because there is no ''android:label'' entry for this activity in the ''AndroidManifest.xml''. If an activity doesn't have an ''android:label'' in the manifest, Android will show the application's ''android:label'' instead. Change that by adding android:label="@string/activity_order_name"
to the manifest and create an appropriate value for the new resource.
Sweet. The really annoying stuff is over.
===== p. 385 =====
Read the aside about breaky v7 appcompat and be glad that you didn't go down that road now.
===== p. 387 =====
Remember to use ''android:showAsAction'' instead of ''app:showAsAction'':
===== p. 389: Code block =====
Here's the file I ended up with:
package com.hfad.bitsandpizzas;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ShareActionProvider;
import android.widget.Toast;
public class MainActivity extends Activity {
private ShareActionProvider shareActionProvider;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Add menu_main items to the action bar (if present):
getMenuInflater().inflate(R.menu.menu_main, menu);
MenuItem menuItem = menu.findItem(R.id.action_share);
shareActionProvider = (ShareActionProvider) menuItem.getActionProvider();
setIntent("This is example text.");
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_create_order:
//Code to run when the Create Order item is clicked
Intent intent = new Intent(this, OrderActivity.class);
startActivity(intent);
return true;
case R.id.action_settings:
//Code to run when the settings item is clicked
Toast.makeText(MainActivity.this, "Settings!", Toast.LENGTH_SHORT).show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void setIntent(String text)
{
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, text);
shareActionProvider.setShareIntent(intent);
}
}
When you run the app, you may see the Messaging app's icon next to the Share icon, and when you click on the Share icon, nothing seems to happen. If this is the case, it's likely because there is only one app in your emulator that is set up to handle shared messages. To add another app to the heap, open the Email app in the emulator and set it up with an appropriate account. You may have to wait and/or restart the emulator, but eventually you should see an additional icon when you click the Share icon in your app.
===== p. 392 =====
Since this app isn't supporting API 16 and lower, you don't need the ''meta-data'' stuff for the OrderActivity in ''AndroidManifest.xml''.
===== p. 393 =====
Code it like you see it.
Congratulations.