Dienstag, 22. Mai 2012

ActionBar

The menu control of Android is an interesting example for how the developers of the system react on experiences with the usage of the system. In the beginning - before 3.0 - the options menu was regarded so important, that there was given a seperate key for it. The menu was realized as a pure popup menu. Even the name speaks for the purpose of the menu as editing settings of the application. Indeed it happened that many applications mis-used the options menu to realize navigation.
With 3.0 the concept was re-worked. Surely it was taken into account, that the purpose of this version was mainly to support tablets and so there was enough space on the screen. The action bar as a totally new element was introduced and the options menu was integrated in this component. The action bar as a given part by default is always visible on the screen (though it may be deactivated). Since Android 4.0 the action bar is also part of the smartphone variant of the operating system. The menu key is not longer a required element of a smartphone.
The action bar is mainly devided into 4 parts ("app icon", "view control", "action buttons" and "action overflow"). The visible appearance is configurable and depends on the space available on the actual device. So the "view control" may be shown in an additional row below the main action bar (as visible in the image) or the "action buttons" may use the space on the bottom of the screen.
The "app icon" (left in the image) serves to identify the running application. It can also be used to navigate "home" or "back".
The "view control" is used to navigate in between the application, for example to activate different pages (fragments) of an activity in form of tabs.
The "action buttons" (right in the image) get close to the options menu used so far. They serve to call context sensitive additional actions. For actions used regularly this can be done directly by icon in this area, less reqularly used actions can be placed in the "action overflow" part. If there is a menu key on the device, the "action overflow" is activated by this key, otherwise there will be shown an appropriate selection list.
Not only relating to content and to handling the actions buttons of Android 3.0 replace the options menu of the earlier versions, but also from the programmers view. The transfer is very transparent.
With 2.x an options menu could be simply declared as following:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
 <item android:id="@+id/mainMenuItem" android:title="@string/main" />
    <item android:id="@+id/layoutDemoMenuItem" android:title="@string/layoutDemo" />
    <item android:id="@+id/layoutDemo2MenuItem" android:title="@string/layoutDemo" />
    <item android:id="@+id/fragmentMenuItem" android:title="@string/fragment" />
</menu>
Methods to add the options menu and to react on the selection of an item are overwritten on the Activity. In this case in form of a BaseActivity, where all the Acivity-Classes that use the options menu may be derived.

public class BaseActivity extends Activity {

     @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  MenuInflater menuInflater = getMenuInflater();
  menuInflater.inflate(R.menu.menu, menu);
  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  switch (item.getItemId()) {
  case R.id.mainMenuItem:
  case android.R.id.home:
   Log.v(getLocalClassName(), "call main");
   Intent intent = new Intent(this, Workshop4Activity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
   startActivity(intent);
   break;
  case R.id.fragmentMenuItem:
   Log.v(getLocalClassName(), "call fragment");
   startActivity(new Intent(this, FragmentActivity.class));
   break;
  case R.id.layoutDemoMenuItem:
   Log.v(getLocalClassName(), "call layoutDemo");
   startActivity(new Intent(this, LayoutDemoActivity.class));
   break;
  case R.id.layoutDemo2MenuItem:
   Log.v(getLocalClassName(), "call layoutDemo");
   startActivity(new Intent(this, LayoutDemoActivity2.class));
   break;
  }
  return true;
 }
}
That's all for the trivial case.
To make the entries of the options menu visible in the action bar on Android 4.0, the corresponding entry of the XML file is given an attribute "android:showAsAction". Possible values are "never", "always", "ifRoom", "withText" and "collapseActionView". Those values may be - if it makes sense - combined with "|".
That's all so far, the BaseActivity does not need to be changed.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
 <item android:id="@+id/mainMenuItem" android:title="@string/main"
  android:showAsAction="ifRoom|withText" />
    <item android:id="@+id/layoutDemoMenuItem" android:title="@string/layoutDemo"
   android:showAsAction="always" />
    <item android:id="@+id/layoutDemo2MenuItem" android:title="@string/layoutDemo"
  android:showAsAction="ifRoom" />
    <item android:id="@+id/fragmentMenuItem" android:title="@string/fragment"
  android:showAsAction="ifRoom" />
</menu>
It is possible to replace the "action buttons" by more complicated controls. The possibilities surely are limited by the rare available space. An ActionProvider may replace the complete logic. "collapseActionView" allows a "short view", from which by click a more detailed view is opened.
The navigation in the "view control" part of the action bar also is not very complicated. The developer documentation describes this very useful (http://developer.android.com/guide/topics/ui/actionbar.html). There is for the case, that the pages to navigate to are realized as Fragments, simply implemented an ActionBar.TabListener. This one instantiiates the concrete Fragments as needed. Each "view control" entry of the action bar is linked with the corresponding Listener. At last only the NavigationMode of the action bar has to be set to "NAVIGATION_MODE_TABS" - in the onCreate-method of the Activity. The ContentView of the Activity has not to be set, only the default container is used. It also is relatively simple, to use a Spinner-control instead of a tab navigation. The elements of the list are defined in an array ressource and the selection of the elements is realized by a SpinnerAdapter and an OnNavigationListener.

ActionBar

Die Menu-Steuerung in Android ist ein interessantes Beispiel dafür, wie die Entwickler des Systems auf Erfahrungen mit der Verwendung des Systems eingehen. Ursprünglich – vor 3.0 - wurde das Options-Menu so wichtig erachtet, dass dafür sogar eine eigene Taste spendiert wurde. Das Menu wurde als reines Popup-Menu realisiert. Schon der Name spricht dafür, dass dieses Menu in erster Linie dazu gedacht war, Einstellungen für die App vorzunehmen. Es stellte sich allerdings heraus, dass viele Anwendungen das Options-Menu dazu missbrauchten, Navigationen zu realisieren.
Ab 3.0 wurde das Konzept überarbeitet – wobei sicherlich auch die Tatsache von Bedeutung war, dass diese Version speziell für Tablets vorgesehen und damit reichlich Platz auf dem Screen vorhanden war. Die Action-Bar als vollkommen neues Element wurde eingeführt und das bisherige Options-Menu in diese Komponente integriert. Die Action-Bar ist als fester Bestandteil per Default immer am Screen sichtbar (kann aber auch deaktiviert werden). Ab Android 4.0 ist die Action-Bar dann auch Bestandteil der Smartphone-Variante des Betriebssystems. Die Menu-Taste ist damit kein Pflichtelement eines Smartphones mehr.
Die ActionBar ist im wesentlichen in 4 Bereiche unterteilt („App-Icon“, „View-Control“, „Action-Buttons“ und „Action-Overflow“), wobei die Darstellung konfigurierbar ist und auch davon abhängig, wie viel Platz auf dem aktuellen Gerät zur Verfügung steht. So kann der „View-Control“-Bereich in einer zusätzlichen Zeile unterhalb der Haupt-ActionBar dargestellt werden (wie im Bild erkennbar) oder die „Action-Buttons“ verwenden den Platz am Fuß des Bildschirms.
Das „App-Icon“ (im Bild ganz links) soll natürlich in erster Linie dazu dienen, die aktuell laufende App zu identifizieren. Es kann auch dazu verwendet werden, zur „Home“-Activity der App oder einfach „Back“ zu navigieren.
Die „View-Control“ wird zur Navigation innerhalb der App verwendet, beispielsweise um in Form von Tabs verschiedene Seiten (Fragmente) einer Activity zu aktivieren.
Die „Action-Buttons“ (im Bild ganz rechts) kommen dem bisherigen Options-Menu noch am nächsten. Sie sollen dazu dienen, kontextabhängig zusätzliche Aktionen aufzurufen. Für häufige Aktionen kann das direkt per Icon in diesem Bereich erfolgen, weniger häufig benötigte Aktionen können im „Action-Overflow“-Bereich liegen. Wenn das Device eine Menu-Taste besitzt, wird dieser „Action-Overflow“-Bereich durch Klick auf die Taste aktiviert, ansonsten wird ein entsprechende Auswahl-Liste dargestellt.
Nicht nur bezüglich Inhalt und Bedienung lösen die Action-Buttons ab Android 3.0 das Options Menu der vorhergehenden Versionen weitestgehend ab, auch aus Sicht der Programmierung. Der Übergang ist auf dieser Ebene sehr transparent:
Unter 2.x konnte ein Options Menu einfach in der folgenden Form deklariert werden:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
 <item android:id="@+id/mainMenuItem" android:title="@string/main" />
    <item android:id="@+id/layoutDemoMenuItem" android:title="@string/layoutDemo" />
    <item android:id="@+id/layoutDemo2MenuItem" android:title="@string/layoutDemo" />
    <item android:id="@+id/fragmentMenuItem" android:title="@string/fragment" />
</menu>
Methoden zum Anhängen des Options Menu und zur Reaktion auf die Auswahl eines Items werden an der Activity überschrieben. Hier in Form einer BaseActivity, von der alle Activities abgeleitet werden können, die das Options Menu bereitstellen sollen:

public class BaseActivity extends Activity {

     @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  MenuInflater menuInflater = getMenuInflater();
  menuInflater.inflate(R.menu.menu, menu);
  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  switch (item.getItemId()) {
  case R.id.mainMenuItem:
  case android.R.id.home:
   Log.v(getLocalClassName(), "call main");
   Intent intent = new Intent(this, Workshop4Activity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
   startActivity(intent);
   break;
  case R.id.fragmentMenuItem:
   Log.v(getLocalClassName(), "call fragment");
   startActivity(new Intent(this, FragmentActivity.class));
   break;
  case R.id.layoutDemoMenuItem:
   Log.v(getLocalClassName(), "call layoutDemo");
   startActivity(new Intent(this, LayoutDemoActivity.class));
   break;
  case R.id.layoutDemo2MenuItem:
   Log.v(getLocalClassName(), "call layoutDemo");
   startActivity(new Intent(this, LayoutDemoActivity2.class));
   break;
  }
  return true;
 }
}
Das ist im Trivialfall schon alles.
Um nun unter Android 4.0 die Einträge des Options Menus in der ActionBar erscheinen zu lassen, wird der entsprechende Eintrag in der XML-Datei einfach mit einem Attribute „android:showAsAction“ versehen. Mögliche Werte sind „never“, „always“, „ifRoom“, „withText“ und „collapseActionView“. Diese können auch – falls sinnvoll – mit „|“ verknüpft werden.
Ansonsten ist das schon alles, die BaseActivity bleibt unverändert.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
 <item android:id="@+id/mainMenuItem" android:title="@string/main"
  android:showAsAction="ifRoom|withText" />
    <item android:id="@+id/layoutDemoMenuItem" android:title="@string/layoutDemo"
   android:showAsAction="always" />
    <item android:id="@+id/layoutDemo2MenuItem" android:title="@string/layoutDemo"
  android:showAsAction="ifRoom" />
    <item android:id="@+id/fragmentMenuItem" android:title="@string/fragment"
  android:showAsAction="ifRoom" />
</menu>
Es ist möglich, die „Action-Buttons“ durch kompliziertere Controls zu ersetzen. Die Möglichkeiten sind natürlich immer durch den äußerst geringen Platz begrenzt. Ein ActionProvider ersetzt sogar die komplette Logik. „collapseActionView“ erlaubt dann eine „Kurzdarstellung“, aus der per Klick eine ausführliche Darstellung aufklappt.
Die Navigation im „View-Control“-Bereich der ActionBar ist auch nicht sonderlich kompliziert. Die Entwicklerdokumentation beschreibt das sehr anschaulich (http://developer.android.com/guide/topics/ui/actionbar.html). Dort wird für den Fall, dass die einzelnen Seiten der Navigation als Fragmente realisiert sind, einfach ein ActionBar.TabListener implementiert. Dieser instantiiert bei Bedarf die entsprechenden Fragmente. Jeder „View-Cotrol“-Eintrag der ActionBar wird dann mit dem entsprechenden Listener verknüpft. Schließlich muss nur noch der NavigationMode der ActionBar auf „NAVIGATION_MODE_TABS“ gesetzt werden – in der onCreate-Methode der Activity. Die ContentView der Activity wird nicht gesetzt, hier wird einfach der Default-Container verwendet. Relativ einfach ist es dann auch, statt einer Tab-Navigation einen Spinner zu verwenden. Die Liste der Elemente muss in der entsprechenden Array-Resource abgelegt werden. Und die Auswahl der Elemente wird über einen SpinnerAdapter und einen OnNavigationListener realisiert.