LoginSignup
19
11

More than 5 years have passed since last update.

ActivityとFragment、両方向の操作

Last updated at Posted at 2018-01-09

前回実装したタブ(TabLayoutとViewPagerを利用したタブの実装 - Qiita)をさらに拡張していきます。今回はActivityからViewPager内のFragmentのメソッドを呼ぶ方法と、ViewPager内のFragmentからActivityのメソッドを呼ぶ方法を紹介します。

ActivityからFragmentを操作する

使用例

ActivityのToolbarに作成したメニューがクリックされた時に、Fragment内のEditTextに入力された文字列を保存して、Activityを終了する。

流れ

1.FragmentにToolbarのメニューがクリックする時に呼ばれるpublicなメソッドを作成する。
2.Toolbarのメニューがクリックされた時、ViewPagerでどのFragmentが表示されているかを取得する。
3.取得されたFragmentによって、1で作成したメソッドを呼ぶ。
4.Activityを終了する。

コード

使用例に沿ったものを作っていきます。今回、文字列の保存はSharedPreferencesで行います。

とりあえずこんな感じのLayoutにしました。EditTextを1つ配置しただけです。

layout/fragment_main2.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context="...Main2Fragment">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="fragment2" />

    <EditText
        android:id="@+id/editText2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="text" />
</LinearLayout>

Fragmentを変更します。closeAndSave()メソッドをToolbarのメニューがクリックされた時に呼びます。
onViewCreated()メソッドで保存してある文字列をEditTextにセットするようにしました。これで、実際にFragmentのメソッドがActivityから呼ばれているかが確認できます。

Main2Fragment.java
package ...;

import ...;

public class Main2Fragment extends Fragment {

    private EditText editText;
    private SharedPreferences preferences;

    ...

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        editText = view.findViewById(R.id.editText2);

        preferences = getContext().getSharedPreferences("data", Context.MODE_PRIVATE);
        editText.setText(preferences.getString("text", ""));
    }

    public void closeAndSave() {
        SharedPreferences.Editor editor = preferences.edit();
        editor.putString("text", editText.getText().toString());
        editor.apply();
    }
}

次にAdapterを変更します。これはActivityが、ViewPagerがどのFragmentを表示しているかをAdapterから取得する必要があるためです。
新たに、setPrimaryItem()というオーバーライドメソッドを表記しました。このメソッドはViewPagerのページが変え始められた時と、表示するページが確定した時に呼ばれます。要するにViewPagerをスワイプして右のページを表示した時、スワイプし始めた瞬間に1回呼ばれ、指を離して右のページの表示が完了した時にもう1回呼ばれます(logで確認してみて下さい)。ここで、positionとfragmentにそれぞれ引数を代入していますが、今回ViewPagerのページ切り替え中には特に何もしないので、1回のページ切り替えで2回呼ばれるメソッドのうち、基本的にページが確定した時に代入された値のみを使うことになります。
引数のObjectはViewPagerで表示されているもの、すなわちこの場合はFragmentなので型を変えて代入できます。

OriginalFragmentPagerAdapter.java
package ...;

import ...;

public class OriginalFragmentPagerAdapter extends FragmentPagerAdapter {

    private int position;
    private Fragment fragment;

    ...

    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        super.setPrimaryItem(container, position, object);

        this.position = position;
        fragment = (Fragment) object;
    }

    public int getPosition() {
        return position;
    }

    public Fragment getFragment() {
        return fragment;
    }
}

最後にActivityです。Toolbarにメニューを表示するところから書きます。
メニューがクリックされた時にclose()メソッドが呼ばれ流ようにしました。まずpositionをAdapterから取得しています。今、Toolbarのメニューをクリックして呼びたいメソッドを書いたのはMain2Fragmentだけなので、Main2Fragmentが表示されている、すなわちpositionが1の時のみMain2FragmentのcloseAndSave()メソッドが呼ばれるようにしています。よって、現段階ではMain1Fragmentが表示されているときにメニューをクリックしてもActivityが終了するだけです。
また、onMenuItemClick()メソッドの最後のfinish()というコードはActivityを終了するコード(メソッド)です。よって、close()メソッド内のFragmentから呼ばれたメソッドが終了したタイミングでActivityが終了します。

MainActivity.java
package ...;

import ...;

public class MainActivity extends AppCompatActivity {

    private OriginalFragmentPagerAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        ...

        Toolbar toolbar = findViewById(R.id.toolbar);
        toolbar.inflateMenu(R.menu.menu_main);
        toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.menu_save:
                        close();
                        break;
                }
                finish();
                return false;
            }
        });
    }

    private void close() {
        int position = adapter.getPosition();
        switch (position) {
            case 0:
                break;
            case 1:
                ((Main2Fragment) adapter.getFragment()).closeAndSave();
                break;
        }
    }
}
menu/menu_main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/menu_save"
        android:icon="@drawable/ic_done_white_24dp"
        android:title="Save"
        app:showAsAction="always" />
</menu>

FragmentからActivityを操作する

使用例

あまり、使用する機会があるのかわかりませんがとりあえず。。。
EditTextで入力された文字列をリアルタイムで(入力と同時に)Activity上のToolbarのタイトルに反映させる。

流れ

1.Activityに、引数で受け取った文字列をToolbarのタイトルにセットするパブリックなメソッドを作る。
2.EditTextに入力を検知するリスナーをセットして、文字列が変更されるたびに1で作成したメソッドをよび、文字列を渡す。

コード

今度はMain1Fragmentに実装します。
Layoutは前回のまま使用できるので、まずはActivityにToolbarにタイトルをセットするメソッドを作ります。

MainActivity.java
package ...;

import ...;

public class MainActivity extends AppCompatActivity {

    ...

    private Toolbar toolbar;

    private void setToolbarTitl(String title) {
        toolbar.setTitle(title);
    }
}

ActivityからFragmentを操作する時と異なるのは、今回は操作対象の親であるActivityが1つしかないので判別する必要がないということです。よって次にFragmentを編集するだけで、これは実装できます。

Main1Fragment.java
package ...;

import ...;

public class Main1Fragment extends Fragment {

    ...

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {

        ...

        editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                ((MainActivity) getContext()).setToolbarTitle(s.toString());
            }

            @Override
            public void afterTextChanged(Editable s) {
            }
        });
    }
}

最後に

このようにActivityとFragmentは両方向の操作が可能です。

19
11
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
19
11