diff --git a/docs/html/images/training/backward-compatible-ui-classes-eclair.png b/docs/html/images/training/backward-compatible-ui-classes-eclair.png new file mode 100644 index 0000000000000..febba5b379e4d Binary files /dev/null and b/docs/html/images/training/backward-compatible-ui-classes-eclair.png differ diff --git a/docs/html/images/training/backward-compatible-ui-classes-honeycomb.png b/docs/html/images/training/backward-compatible-ui-classes-honeycomb.png new file mode 100644 index 0000000000000..ba14252d93b33 Binary files /dev/null and b/docs/html/images/training/backward-compatible-ui-classes-honeycomb.png differ diff --git a/docs/html/images/training/backward-compatible-ui-classes.png b/docs/html/images/training/backward-compatible-ui-classes.png new file mode 100644 index 0000000000000..c5a3cd8080616 Binary files /dev/null and b/docs/html/images/training/backward-compatible-ui-classes.png differ diff --git a/docs/html/images/training/backward-compatible-ui-gb.png b/docs/html/images/training/backward-compatible-ui-gb.png new file mode 100644 index 0000000000000..621ee63c49767 Binary files /dev/null and b/docs/html/images/training/backward-compatible-ui-gb.png differ diff --git a/docs/html/images/training/backward-compatible-ui-ics.png b/docs/html/images/training/backward-compatible-ui-ics.png new file mode 100644 index 0000000000000..646055448f6a1 Binary files /dev/null and b/docs/html/images/training/backward-compatible-ui-ics.png differ diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs index 6fe36fab71197..686bde356ef84 100644 --- a/docs/html/resources/resources_toc.cs +++ b/docs/html/resources/resources_toc.cs @@ -227,7 +227,31 @@ class="new"> new! - + +
TabCompat.zip
+Suppose you want to use action bar tabs as the primary form of top-level navigation in your application. Unfortunately, the {@link android.app.ActionBar} APIs are only available in Android 3.0 or later (API level 11+). Thus, if you want to distribute your application to devices running earlier versions of the platform, you need to provide an implementation that supports the newer API while providing a fallback mechanism that uses older APIs.
+ +In this class, you build a tabbed user interface (UI) component that uses abstract classes with version-specific implementations to provide backward-compatibility. This lesson describes how to create an abstraction layer for the new tab APIs as the first step toward building the tab component.
+ +Abstraction in the Java programming language involves the creation of one or more interfaces or abstract classes to hide implementation details. In the case of newer Android APIs, you can use abstraction to build version-aware components that use the current APIs on newer devices, and fallback to older, more compatible APIs on older devices.
+ +When using this approach, you first determine what newer classes you want to be able to use in a backward compatible way, then create abstract classes, based on the public interfaces of the newer classes. In defining the abstraction interfaces, you should mirror the newer API as much as possible. This maximizes forward-compatibility and makes it easier to drop the abstraction layer in the future when it is no longer necessary.
+ +After creating abstract classes for these new APIs, any number of implementations can be created and chosen at runtime. For the purposes of backward-compatibility, these implementations can vary by required API level. Thus, one implementation may use recently released APIs, while others can use older APIs.
+ +In order to create a backward-compatible version of tabs, you should first determine which features and specific APIs your application requires. In the case of top-level section tabs, suppose you have the following functional requirements:
+ +Preparing these requirements in advance allows you to control the scope of your abstraction layer. This means that you can spend less time creating multiple implementations of your abstraction layer and begin using your new backward-compatible implementation sooner.
+ +The key APIs for tabs are in {@link android.app.ActionBar} and {@link android.app.ActionBar.Tab ActionBar.Tab}. These are the APIs to abstract in order to make your tabs version-aware. The requirements for this example project call for compatibility back to Eclair (API level 5) while taking advantage of the new tab features in Honeycomb (API Level 11). A diagram of the class structure to support these two implementations and their abstract base classes (or interfaces) is shown below.
+ +
+
+Figure 1. Class diagram of abstract base classes and version-specific implementations.
+ +Get started on building your tab abstraction layer by creating an abstract class representing a tab, that mirrors the {@link android.app.ActionBar.Tab ActionBar.Tab} interface:
+ +
+public abstract class CompatTab {
+ ...
+ public abstract CompatTab setText(int resId);
+ public abstract CompatTab setIcon(int resId);
+ public abstract CompatTab setTabListener(
+ CompatTabListener callback);
+ public abstract CompatTab setFragment(Fragment fragment);
+
+ public abstract CharSequence getText();
+ public abstract Drawable getIcon();
+ public abstract CompatTabListener getCallback();
+ public abstract Fragment getFragment();
+ ...
+}
+
+
+You can use an abstract class instead of an interface here to simplify the implementation of common features such as association of tab objects with activities (not shown in the code snippet).
+ +Next, define an abstract class that allows you to create and add tabs to an activity, like {@link android.app.ActionBar#newTab ActionBar.newTab()} and {@link android.app.ActionBar#addTab ActionBar.addTab()}:
+ +
+public abstract class TabHelper {
+ ...
+
+ public CompatTab newTab(String tag) {
+ // This method is implemented in a later lesson.
+ }
+
+ public abstract void addTab(CompatTab tab);
+
+ ...
+}
+
+
+In the next lessons, you create implementations for TabHelper and CompatTab that work across both older and newer platform versions.
TabCompat.zip
+This class demonstrates how to use UI components and APIs available in newer versions of Android in a backward-compatible way, ensuring that your application still runs on previous versions of the platform.
+ +Throughout this class, the new Action Bar Tabs feature introduced in Android 3.0 (API level 11) serves as the guiding example, but you can apply these techniques to other UI components and API features.
+ +TabCompat.zip
+This lesson shows you how to subclass the CompatTab and TabHelper abstract classes and use new APIs. Your application can use this implementation on devices running a platform version that supports them.
The concrete classes for CompatTab and TabHelper that use newer APIs are a proxy implementation. Since the abstract classes defined in the previous lesson mirror the new APIs (class structure, method signatures, etc.), the concrete classes that use these newer APIs simply proxy method calls and their results.
You can directly use newer APIs in these concrete classes—and not crash on earlier devices—because of lazy class loading. Classes are loaded and initialized on first access—instantiating the class or accessing one of its static fields or methods for the first time. Thus, as long as you don't instantiate the Honeycomb-specific implementations on pre-Honeycomb devices, the Dalvik VM won't throw any {@link java.lang.VerifyError} exceptions.
+ +A good naming convention for this implementation is to append the API level or platform version code name corresponding to the APIs required by the concrete classes. For example, the native tab implementation can be provided by CompatTabHoneycomb and TabHelperHoneycomb classes, since they rely on APIs available in Android 3.0 (API level 11) or later.
+
+Figure 1. Class diagram for the Honeycomb implementation of tabs.
+ +CompatTabHoneycomb is the implementation of the CompatTab abstract class that TabHelperHoneycomb uses to reference individual tabs. CompatTabHoneycomb simply proxies all method calls to its contained {@link android.app.ActionBar.Tab} object.
Begin implementing CompatTabHoneycomb using the new {@link android.app.ActionBar.Tab ActionBar.Tab} APIs:
+public class CompatTabHoneycomb extends CompatTab {
+ // The native tab object that this CompatTab acts as a proxy for.
+ ActionBar.Tab mTab;
+ ...
+
+ protected CompatTabHoneycomb(FragmentActivity activity, String tag) {
+ ...
+ // Proxy to new ActionBar.newTab API
+ mTab = activity.getActionBar().newTab();
+ }
+
+ public CompatTab setText(int resId) {
+ // Proxy to new ActionBar.Tab.setText API
+ mTab.setText(resId);
+ return this;
+ }
+
+ ...
+ // Do the same for other properties (icon, callback, etc.)
+}
+
+
+TabHelperHoneycomb is the implementation of the TabHelper abstract class that proxies method calls to an actual {@link android.app.ActionBar}, obtained from its contained {@link android.app.Activity}.
Implement TabHelperHoneycomb, proxying method calls to the {@link android.app.ActionBar} API:
+public class TabHelperHoneycomb extends TabHelper {
+ ActionBar mActionBar;
+ ...
+
+ protected void setUp() {
+ if (mActionBar == null) {
+ mActionBar = mActivity.getActionBar();
+ mActionBar.setNavigationMode(
+ ActionBar.NAVIGATION_MODE_TABS);
+ }
+ }
+
+ public void addTab(CompatTab tab) {
+ ...
+ // Tab is a CompatTabHoneycomb instance, so its
+ // native tab object is an ActionBar.Tab.
+ mActionBar.addTab((ActionBar.Tab) tab.getTab());
+ }
+
+ // The other important method, newTab() is part of
+ // the base implementation.
+}
+
\ No newline at end of file
diff --git a/docs/html/training/backward-compatible-ui/older-implementation.jd b/docs/html/training/backward-compatible-ui/older-implementation.jd
new file mode 100644
index 0000000000000..5006123371c07
--- /dev/null
+++ b/docs/html/training/backward-compatible-ui/older-implementation.jd
@@ -0,0 +1,126 @@
+page.title=Creating an Implementation with Older APIs
+parent.title=Creating Backward-Compatible UIs
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Proxying to the New APIs
+previous.link=new-implementation.html
+next.title=Using the Version-Aware Component
+next.link=using-component.html
+
+@jd:body
+
+This lesson discusses how to create an implementation that mirrors newer APIs yet supports older devices.
+ +The most challenging task in using newer UI features in a backward-compatible way is deciding on and implementing an older (fallback) solution for older platform versions. In many cases, it's possible to fulfill the purpose of these newer UI components using older UI framework features. For example:
+ +Action bars can be implemented using a horizontal {@link android.widget.LinearLayout} containing image buttons, either as custom title bars or as views in your activity layout. Overflow actions can be presented under the device Menu button.
+Action bar tabs can be implemented using a horizontal {@link android.widget.LinearLayout} containing buttons, or using the {@link android.widget.TabWidget} UI element.
+{@link android.widget.NumberPicker} and {@link android.widget.Switch} widgets can be implemented using {@link android.widget.Spinner} and {@link android.widget.ToggleButton} widgets, respectively.
+{@link android.widget.ListPopupWindow} and {@link android.widget.PopupMenu} widgets can be implemented using {@link android.widget.PopupWindow} widgets.
+There generally isn't a one-size-fits-all solution for backporting newer UI components to older devices. Be mindful of the user experience: on older devices, users may not be familiar with newer design patterns and UI components. Give some thought as to how the same functionality can be delivered using familiar elements. In many cases this is less of a concern—if newer UI components are prominent in the application ecosystem (such as the action bar), or where the interaction model is extremely simple and intuitive (such as swipe views using a {@link android.support.v4.view.ViewPager}).
+ +To create an older implementation of action bar tabs, you can use a {@link android.widget.TabWidget} and {@link android.widget.TabHost} (although one can alternatively use horizontally laid-out {@link android.widget.Button} widgets). Implement this in classes called TabHelperEclair and CompatTabEclair, since this implementation uses APIs introduced no later than Android 2.0 (Eclair).
+
+Figure 1. Class diagram for the Eclair implementation of tabs.
+ +The CompatTabEclair implementation stores tab properties such as the tab text and icon in instance variables, since there isn't an {@link android.app.ActionBar.Tab ActionBar.Tab} object available to handle this storage:
+public class CompatTabEclair extends CompatTab {
+ // Store these properties in the instance,
+ // as there is no ActionBar.Tab object.
+ private CharSequence mText;
+ ...
+
+ public CompatTab setText(int resId) {
+ // Our older implementation simply stores this
+ // information in the object instance.
+ mText = mActivity.getResources().getText(resId);
+ return this;
+ }
+
+ ...
+ // Do the same for other properties (icon, callback, etc.)
+}
+
+
+The TabHelperEclair implementation makes use of methods on the
+{@link android.widget.TabHost} widget for creating {@link android.widget.TabHost.TabSpec}
+objects and tab indicators:
+public class TabHelperEclair extends TabHelper {
+ private TabHost mTabHost;
+ ...
+
+ protected void setUp() {
+ if (mTabHost == null) {
+ // Our activity layout for pre-Honeycomb devices
+ // must contain a TabHost.
+ mTabHost = (TabHost) mActivity.findViewById(
+ android.R.id.tabhost);
+ mTabHost.setup();
+ }
+ }
+
+ public void addTab(CompatTab tab) {
+ ...
+ TabSpec spec = mTabHost
+ .newTabSpec(tag)
+ .setIndicator(tab.getText()); // And optional icon
+ ...
+ mTabHost.addTab(spec);
+ }
+
+ // The other important method, newTab() is part of
+ // the base implementation.
+}
+
+
+You now have two implementations of CompatTab and TabHelper: one that works on devices running Android 3.0 or later and uses new APIs, and another that works on devices running Android 2.0 or later and uses older APIs. The next lesson discusses using these implementations in your application.
TabCompat.zip
+Now that you have two implementations of TabHelper and CompatTab—one for Android 3.0 and later and one for earlier versions of the platform—it's time to do something with these implementations. This lesson discusses creating the logic for switching between these implementations, creating version-aware layouts, and finally using the backward-compatible UI component.
The TabHelper abstract class acts as a factory for creating version-appropriate TabHelper and CompatTab instances, based on the current device's platform version:
+public abstract class TabHelper {
+ ...
+ // Usage is TabHelper.createInstance(activity)
+ public static TabHelper createInstance(FragmentActivity activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ return new TabHelperHoneycomb(activity);
+ } else {
+ return new TabHelperEclair(activity);
+ }
+ }
+
+ // Usage is mTabHelper.newTab("tag")
+ public CompatTab newTab(String tag) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ return new CompatTabHoneycomb(mActivity, tag);
+ } else {
+ return new CompatTabEclair(mActivity, tag);
+ }
+ }
+ ...
+}
+
+
+The next step is to provide layouts for your activity that can support the two tab implementations. For the older implementation (TabHelperEclair), you need to ensure that your activity layout contains a {@link android.widget.TabWidget} and {@link android.widget.TabHost}, along with a container for tab contents:
res/layout/main.xml:
+ ++<!-- This layout is for API level 5-10 only. --> +<TabHost xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/tabhost" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="5dp"> + + <TabWidget + android:id="@android:id/tabs" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <FrameLayout + android:id="@android:id/tabcontent" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" /> + + </LinearLayout> +</TabHost> ++ +
For the TabHelperHoneycomb implementation, all you need is a {@link android.widget.FrameLayout} to contain the tab contents, since the tab indicators are provided by the {@link android.app.ActionBar}:
res/layout-v11/main.xml:
+ ++<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/tabcontent" + android:layout_width="match_parent" + android:layout_height="match_parent" /> ++ +
At runtime, Android will decide which version of the main.xml layout to inflate depending on the platform version. This is the same logic shown in the previous section to determine which TabHelper implementation to use.
In your activity's {@link android.app.Activity#onCreate onCreate()} method, you can obtain a TabHelper object and add tabs with the following code:
+{@literal @}Override
+public void onCreate(Bundle savedInstanceState) {
+ setContentView(R.layout.main);
+
+ TabHelper tabHelper = TabHelper.createInstance(this);
+ tabHelper.setUp();
+
+ CompatTab photosTab = tabHelper
+ .newTab("photos")
+ .setText(R.string.tab_photos);
+ tabHelper.addTab(photosTab);
+
+ CompatTab videosTab = tabHelper
+ .newTab("videos")
+ .setText(R.string.tab_videos);
+ tabHelper.addTab(videosTab);
+}
+
+
+When running the application, this code inflates the correct activity layout and instantiates either a TabHelperHoneycomb or TabHelperEclair object. The concrete class that's actually used is opaque to the activity, since they share the common TabHelper interface.
Below are two screenshots of this implementation running on an Android 2.3 and Android 4.0 device.
+ +
+
+
+
+Figure 1. Example screenshots of backward-compatible tabs running on an Android 2.3 device (using TabHelperEclair) and an Android 4.0 device (using TabHelperHoneycomb).