LoginSignup
0
1

More than 5 years have passed since last update.

AndroidのタブアクティビティでSupportMapFragmentを利用する

Posted at

タブ・アクティビティはSectionsPagerAdapterを利用したタブによるページ移動を指しています。
移動する各ページを個別のFragmentにし、そのうちの1のFragmentでSupportMapFragment(GoogleMap)を利用したのですが、SupportMapFragment#getMapAsyncを呼び出すタイミングとページ移動時にエラーが発生する事象にハマりました。
上記を回避するための方法を記載します。(これで合っているのかなーという気がしますが...)

まずはタブ・アクティビティでページごとに個別のfragmentを表示する

プロジェクト作成時にタブ・アクティビティを選択すると、MainActivityはAppCompatActivityを継承して作成されます。
Activityのinner classとして定義されるSectionsPagerAdapter#getItemで表示する画面のfragmentを生成しますが、このうちの1つでSupportMapFragmentを利用することにしました。
※UserMapFragmentとします。

public class MainActivity extends AppCompatActivity 
・
・
・
中略
・
・
    public class SectionsPagerAdapter extends FragmentPagerAdapter {

        @Override
        public Fragment getItem(int position) {
            // getItem is called to instantiate the fragment for the given page.
            // Return a TestResultFragment (defined as a static inner class below).

            Fragment fragment = null;

            // ここで選択されたTabの番号に合わせてFragmentを生成
            switch (position) {
                case 0:
                    fragment = UserMapFragment.newInstance(); // Mapを利用するFragment
                    break;
                case 1:
                    fragment = YyyyFragment.newInstance();
                    break;
                default:
                    fragment = ZzzzzFragment.newInstance();
                    break;
            }
            return fragment;
        }

fragmentのonStartでSupportMapFragment#getMapAsyncを実行する。

UserMapFragmentのonStartでgetMapAsyncします。
SupportMapFragment#getMapAsyncするにはActivityが必要になるので、getActivityで取得して渡します。(これでいいのか...その1)

public class UserMapFragment extends Fragment
    @Override
    public void onStart() {

        // Activityの取得とMapの生成
        MainActivity activity = (MainActivity) this.getActivity();

        if (activity.mMap == null) {
            // SupportMapFragmentを取得
            SupportMapFragment mapFragment = (SupportMapFragment) this.getChildFragmentManager().findFragmentById(R.id.fragment_map);
            mapFragment.getMapAsync(activity);
        }
        super.onStart();
    }

そしてgetMapAsyncのcallbackとListenerはActivity側に実装します。
OnMapReadyCallbackはFragmantにimplements出来ない為です。
(これでいいのか...その2)

// implements OnMapReadyCallback, GoogleMap.OnCameraIdleListenerを追加
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback, GoogleMap.OnCameraIdleListener {

    // Mapの定義
    public GoogleMap mMap;

    /**
     * SupportMapFragment#getMapAsyncのcallback
     *
     * @param googleMap
     */
    @Override
    public void onMapReady(GoogleMap googleMap) {
      // Mapの生成
    }

    @Override
    public void onCameraIdle() {
      // Mapを移動したときの処理を実装
    }

ページ移動した際にExceptionが発生するのを防ぐ

これでタブ移動したページにGoogleMapが表示されるのですが、何回かページを移動しているとGoogleMapのあるFragmentのonCreateViewでExceptionが発生します。

厳密なエラーの発生理由は調べていないのですが、どうもページ移動に伴いSupportMapFragment#getMapAsyncが2回以上実行されるとExceptionが発生するようです。

getMapAsyncを複数回実行させないことで対応したいのですが、タブを利用したページ移動は、表示されていない隣のページを同時に生成し、隣のページ以外を破棄してるようです。
SupportMapFragmentを持つFragmentがページ移動により破棄され、再度キャッシュされるときにエラーが発生するようです。

これはページの移動時にSectionsPagerAdapter#getItemが2回実行されるので確認できます。

Tab position:0 ← ここを表示(Fragmentを生成)すると、position:1 もFragmentを生成する
Tab position:1
Tab position:2 ← ここを表示(Fragmentを生成)すると、position:3 のFragmentを生成し、position:0のFragmentを破棄する。
Tab position:3

Fragmentが破棄されないようにすることで回避しました。(これでいいのか...その3)


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

        // ----------- Tabの初期化

        // Create the adapter that will return a fragment for each of the three
        // primary sections of the activity.
        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());


        // Set up the ViewPager with the sections adapter.
        mViewPager = (ViewPager) findViewById(R.id.container);
        mViewPager.setAdapter(mSectionsPagerAdapter);


        // setOffscreenPageLimitに3(Tabの総数)を設定
        mViewPager.setOffscreenPageLimit(3);

setOffscreenPageLimitでキャッシュするページ数が決められるので、3=全てのページをキャッシュすることでページの破棄が発生しなくなりました。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1