2015年4月16日木曜日

Navigation DrawerとFragmentをAPI level 11未満で使いたい


ちょっといまさらですが、NavigationDrawerについて調べてみました。

NavigationDrawerを実装するには、サポートライブラリのandroid.support.v4.widget.DrawerLayoutを使います。

NavigationDrawerに限らず、画面を切り替えたりするのにFragmentを使うと便利なのですが、API level 11以上です。

API level11というとAndroid 3.0以降ということになりますが、Android 2.3.3 - 2.3.7がまだ1割弱は現存しているため、割り切ってしまうのもどうかなぁという気もします。

そこで方法がないか調べてみたところ、同じくサポートライブラリのandroid.support.v4.app.Fragmentを使えば可能でした。


ということで今回は、android.support.v4.widget.DrawerLayoutとandroid.support.v4.app.Fragmentを使って、NavigationDrawerを実装してみます。

NavigationDrawerから設定画面と詳細設定画面を切り替えるサンプルを作成します。

まずは、パーツを作成します。
設定画面と詳細設定画面のFragment用のレイアウトをそれぞれ作成します。

fragment_settings.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content_settings"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Settings"
        android:id="@+id/textView1" />
</LinearLayout>

fragment_advanced_settings.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content_advanced_settings"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Advanced Settings"
        android:id="@+id/textView2" />
</LinearLayout>

何の変哲も無いレイアウトです。

次に、android.support.v4.app.Fragmentを継承したクラスをそれぞれ作成します。

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class SettingsFragment extends Fragment{

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_settings, container, false);
    }
}

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class AdvancedSettingsFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_advanced_settings, container, false);
    }
}

onCreateViewメソッドをオーバーライドし、先ほどのxmlレイアウトファイルをそれぞれ紐付けます。

これでパーツは出来たので、次にメイン画面を作成します。

activity_main.xml

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </FrameLayout>

    <LinearLayout
        android:id="@+id/drawer"
        android:orientation="vertical"
        android:layout_width="320dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="#ffffffff">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="Menu1"
            android:id="@+id/menuText1"
            android:textColor="#ff505050" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="Menu2"
            android:id="@+id/menuText2"
            android:textColor="#ff505050" />
    </LinearLayout>

</android.support.v4.widget.DrawerLayout>

android.support.v4.widget.DrawerLayoutをルートに配置し、一つ目の子がコンテンツ用、二つ目の子がNavigationDrawer用です。

コンテンツはFragmentを使って実装し、NavigationDrawerは今回はそのままここに書いてしまいます。

それでは組み立てます。

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;

public class ActivityMain extends ActionBarActivity {

    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mDrawerToggle;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initDrawer();
        initDrawerMenu();

        changeFragment(new SettingsFragment());
    }

    private void initDrawer() {
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.app_name, R.string.app_name);
        mDrawerToggle.setDrawerIndicatorEnabled(true);
        mDrawerLayout.setDrawerListener(mDrawerToggle);

        getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_drawer);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setDisplayShowHomeEnabled(true);
    }

    private void initDrawerMenu() {
        TextView menu1 = (TextView) findViewById(R.id.menuText1);
        menu1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                changeFragment(new SettingsFragment());
                setTitle("Settings");
            }
        });
        TextView menu2 = (TextView) findViewById(R.id.menuText2);
        menu2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                changeFragment(new AdvancedSettingsFragment());
                setTitle("Advanced Settings");
            }
        });
    }

    private void changeFragment(Fragment f) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction()
                .replace(R.id.content_frame, f)
                .commit();
        mDrawerLayout.closeDrawers();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (mDrawerToggle.onOptionsItemSelected(item)) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

今時っぽくするためにActionBarを使いたいので、またもやサポートライブラリからActionBarActivityを継承してクラスを作成します。

initDrawer()メソッドの中で、NavigationDrawerとActionBarの設定を行っています。

getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_drawer)で左上の三本線のアイコンを設定しています。
これ、なぜだか分かりませんが、あんまりネットで参考になる日本語サイトがなく、結構ハマりました。

アイコン画像はここで拾って来て、drawableに放り込んでおきます。

アイコンからNavigationDrawerを呼べるように、onOptionsItemSelected()内でActionBarDrawerToggleのonOptionsItemSelected()を呼びます。

initDrawerMenu()メソッドの中で、NavigationDrawerのメニューの設定を行っています。

今回はそれぞれOnClickListenerを設定し、コンテンツの変更とついでにActionBarのタイトルの変更を行っています。


実際のコンテンツの変更については、changeFragment()メソッドの中で実装しています。

FragmentManagerを利用してコンテンツを変更し、NavigationDrawerをクローズする処理を行っています。


出来ました。

まぁメニュー二つとかそんなシンプルな構成であれば、NavigationDrawerを使う必要もないのですが、とりあえず最近のアプリっぽくなりました。

0 件のコメント:

コメントを投稿