古い API を使用する実装を作成する

このレッスンでは、古いデバイスをサポートしつつ新しい API を反映する実装を作成する方法について説明します。

代替ソリューションを決定する

下位互換性を維持しつつ新しい UI 機能を使用する場合の最も困難なタスクは、古いプラットフォーム バージョン用の古い(代替)ソリューションを決定して実装することです。多くの場合は、古い UI フレームワーク機能により、新しい UI コンポーネントの目的を達成できます。次に例を示します。

  • アクションバーは、カスタム タイトルバーまたはアクティビティ レイアウトのビューとして、画像ボタンを含む水平 LinearLayout を使用することで実装できます。オーバーフロー アクションは、デバイスのメニューボタンの下に表示できます。

  • アクションバー タブは、ボタンを含む水平 LinearLayout か、TabWidget UI 要素を使用して実装できます。

  • NumberPicker ウィジェットと Switch ウィジェットは、それぞれ Spinner ウィジェットと ToggleButton ウィジェットを使用して実装できます。

  • ListPopupWindow ウィジェットと PopupMenu ウィジェットは PopupWindow ウィジェットを使用して実装できます。

一般的に、新しい UI コンポーネントを古いデバイスにバックポートするための万能なソリューションはありません。ユーザー エクスペリエンスに注意する必要があります。古いデバイスのユーザーは、新しいデザイン パターンと UI コンポーネントに馴染めない可能性があります。どうすればユーザーが使い慣れた要素で同じ機能を実現できるかを考えましょう。アプリのエコシステム(アクションバーなど)で新しい UI コンポーネントが目立っていたり、対話モデルが非常にシンプルで直感的であったりすれば(ViewPager を使用したビューのスワイプなど)、多くの場合、それほど問題は生じません。

古い API を使用するタブを実装する

アクションバー タブの古い実装を作成するには、TabWidgetTabHost を使用できます(代わりに、水平方向に置かれた Button ウィジェットも使用できます)。これは、TabHelperEclair および CompatTabEclair という名前のクラスに実装します。この実装では、Android 2.0(Eclair)より前に導入された API を使用するからです。

タブの Eclair 実装のクラスの図

図 1. タブの Eclair 実装のクラスの図

CompatTabEclair 実装は、タブテキストやアイコンなどのタブプロパティをインスタンス変数に格納します。これは、このストレージの処理に使用できる ActionBar.Tab オブジェクトがないためです。

Kotlin

class CompatTabEclair internal constructor(val activity: FragmentActivity, tag: String) :
        CompatTab(tag) {

    // Store these properties in the instance,
    // as there is no ActionBar.Tab object.
    private var text: CharSequence? = null
    ...

    override fun setText(resId: Int): CompatTab {
        // Our older implementation simply stores this
        // information in the object instance.
        text = activity.resources.getText(resId)
        return this
    }

    ...
    // Do the same for other properties (icon, callback, etc.)
}

Java

public class CompatTabEclair extends CompatTab {
    // Store these properties in the instance,
    // as there is no ActionBar.Tab object.
    private CharSequence text;
    ...

    public CompatTab setText(int resId) {
        // Our older implementation simply stores this
        // information in the object instance.
        text = activity.getResources().getText(resId);
        return this;
    }

    ...
    // Do the same for other properties (icon, callback, etc.)
}

TabHelperEclair の実装では、TabHost ウィジェットのメソッドを使用して、TabHost.TabSpec オブジェクトとタブ インジケーターを作成します。

Kotlin

class TabHelperEclair internal constructor(activity: FragmentActivity) : TabHelper(activity) {

    private var tabHost: TabHost? = null
    ...

    override fun setUp() {
        // Our activity layout for pre-Honeycomb devices
        // must contain a TabHost.
        tabHost = tabHost ?: mActivity.findViewById<TabHost>(android.R.id.tabhost).apply {
            setup()
        }
    }

    override fun addTab(tab: CompatTab) {
        ...
        tabHost?.newTabSpec(tab.tag)?.run {
            setIndicator(tab.getText()) // And optional icon
            ...
            tabHost?.addTab(this)
        }
    }
    // The other important method, newTab() is part of
    // the base implementation.
}

Java

public class TabHelperEclair extends TabHelper {
    private TabHost tabHost;
    ...

    protected void setUp() {
        if (tabHost == null) {
            // Our activity layout for pre-Honeycomb devices
            // must contain a TabHost.
            tabHost = (TabHost) mActivity.findViewById(
                    android.R.id.tabhost);
            tabHost.setup();
        }
    }

    public void addTab(CompatTab tab) {
        ...
        TabSpec spec = tabHost
                .newTabSpec(tag)
                .setIndicator(tab.getText()); // And optional icon
        ...
        tabHost.addTab(spec);
    }

    // The other important method, newTab() is part of
    // the base implementation.
}

これで、CompatTabTabHelper の 2 つの実装を用意できました。1 つは Android 3.0 以降を実行するデバイスで動作し、新しい API を使用します。もう 1 つは Android 2.0 以降を実行するデバイスで動作し、古い API を使用します。次のレッスンでは、これらの実装をアプリで使用する方法について説明します。