Fragment 間の遷移 (1) プロジェクト作成

2019年12月15日日曜日

Android Fragment Kotlin

t f B! P L

概要

 Fragment間の画面遷移を知る。Android Studioでいつのまにか (ver 3.3から?)、プロジェクト生成画面で Fragment + ViewModelの雛形が選択できるやうになつてゐたのでそれも試す。ただし、ViewModelらしきことは一切しない。
 まづ、Fragmentを2つ用意し、それぞれにボタンを持たせる。ボタンを押したら Fragment を切り替へる。第一のFragmentのボタンを押したら、Activityでイベントを受け取るまでを作る。その続きはあとで。
 使ふているバージョンは次。

 Android Studio : 3.5.2
 Kotlin : 1.3.61


プロジェクトの作成

プロジェクトを生成する
  1. Choose your project画面で Fragment + ViewModel を選択
  2. Configure your project画面で、Name: TestFragmentVMとし、Use androidx.* artifacts を選択

生成されるファイル

 以下の5つのファイルが生成されてゐることを確認する。

ファイル 説明
main_activity.xml Activityのレイアウト。idはcontainer。fragment要素は無い。
main_fragment.xml  fragmentのレイアウト。idはmain。 文字列MainFragmentを表示するTextViewが1つある
MainActivity.kt Activityのコード。onCreate()の中で MainFragmentクラスのインスタンスをActivityのレイアウトに表示させてゐる
MainFragment.kt レイアウト main_fragment.xmlを レイアウト containerに吹き込んでゐる(inflate)。 ViewModelのオブジェクトも生成済。
MainViewModel.kt VieModelクラス派生のクラスMainViewModelを生成済


ビルドする。 TextViewに MainFragmentと表示されるアプリが起動することを確認する。

フラグメントの追加

 2つ目のFragmentを追加する。SecondFragmentとする。

  1. Android Studio 画面左側のproject tool windowで、app javacom.example.testfragment を選び右クリック
  2. NewFragment Fragment (Blank) を選択
  3. Configure Component ダイアローグが現れる
  4. そのダイアローグで次の設定をする
    • Fragment Name:  SecondFragment
    • Create lyaout XML? : チェックつける
    • Fragment Layout Name: fragment_second
    • include fragment factory methods? : チェックはずす
    • include interface callback? : チェックはずす
    • 言語はKotlin
  5. Finish
 次の2つのファイルができてゐることを確認する。
  • fragment_second.xml - レイアウトファイル
  • SecondFragment.kt - クラス SecnodFragmentができてゐる。 Fragment()の派生として。 onCreateView( )の中で、レイアウトファイル fragment_second.xmlを吹き込んでゐる。つまり、クラスとレイアウトとの関係づけは既にできてゐる。

 ビルドする。まだ何も実装してゐないので何も変わらない。

MainFragmentにボタン追加

 まづ、レイアウトファイル main_fragment.xmlにボタンを追加する。つぎに、そのボタンを押したら、SecondFragmentに遷移するためのイベントリスナーを登録するために、MainFragment.ktを変更する。

ボタン コンポーネントの追加 - main_fragment.xml

main_fragment.xmlのデザインモードで次をする。
  1. PaletteからButtonコンポーネントを選択してイメージビューにドロップ。
  2. Buttonの配置を適当に制約づけする。たとへばMainFragmentのTextViewの下あたり。
  3. そのButtonコンポーネントのAttributesをつぎのやうにする
    • id: buttonMain
    • text: To Second Fragment

ボタン クリックのイベントリスナー登録 - MainFragment.kt

 Fragment間は相互に通信しないことが基本。このため、画面遷移のイベントはすべてActivityに任せる。Fragmentがやることは、次のことだけ。
  • ボタンクリックのイベントを受けて行う処理の interfaceだけを用意すること。(処理本体の実装はActivityが行う)
  • ボタンクリックのイベントを受けること (リスナーを登録する)
  • イベントを受けて行うActivityの処理本体を登録すること。

interfaceの用意

クラス MainFragmentで定義する interface は次のやう。

   interface OnButtonClickListener {
        fun onButtonClicked()
    }

ボタンクリックのリスナー登録とActivityの処理本体の登録

関数 onCreateView( )の中で次を書く。


val view = inflater.inflate(R.layout.main_fragment, container, false)
val button = view.findViewById<Button>(R.id.buttonMain)
button.setOnClickListener {
            val listener = context as? OnButtonClickListener
            listener?.onButtonClicked()
}
return view

  • 1行目:
    • Android Studioが準備したコード部分。 元々は、main_fragment.xmlレイアウトを吹き込んだ(inflateした) Viewをreturnしてゐるが、そのViewを使うので変数viewに格納。
  • 2行目:
    • レイアウトからbuttonMain部品を取得。
  • 3行目:
    • そのbuttonMain部品が持つメンバー関数の setOnClickListnerを使って、ボタンがクリックされた時のこのFragmentでの処理を登録する。
  • 4行目:
    • contextは、この MainFragmentを受け持つActivityのこと。それを、 OnButtonClickListnerに型変換して取りだしてゐる。上位のクラスから下位のクラスへの型変換なので、明示的に as を使ふ。 OnButtonClickListenerが実装されていないかもしれないので、null許容するために、asに ?をつける。
      OnButtonClickListenerは、さきほど、このMainFragmentで interface定義したもの。
      ここで、contextを使ふのは、どうもきれいではないやうに見える。グローバル変数を使かふているやうで。 Android Developers Guideを見ると、Activityが thisを引数にして、Fragmentの関数を呼び出すことでイベントリスナーを登録する例になつてゐる。そのはうが、ActivityとFragmentとが分離されているやうであるが、コードが増える。あかんかなあ。
  • 5行目:
    • interfaceとして定義したOnButtonClickListenerの中の関数を呼び出す。contextの中の、つまり、Activityの中のonButtonClicked()を実行することになる。
  • 6行目、7行目:
    • viewを returnしている。(そのままや)
ここまでで、ManFragment.ktの編集はおしまい。4行目あかんかなあ。

Activityでボタンイベントを受ける - MainActivity.kt


まずは、MainFragmentで定義したinterfaceの実装をする。コードは次のやう。onButtonClicked ()の中で Fragmentの切り替えを実装するがそれはあとまはし。


class MainActivity : AppCompatActivity(), MainFragment.OnButtonClickListener {
  〜 中略 〜
    override fun onButtonClicked() {
        Log.i("MainActivity", "onButtonClicked")
    }
}

  • 1行目:
    • MainFragment.OnButtonClickListenerからの派生。
  • 3行目〜5行目:
    • interfaceの実装。ログを表示させてイベントが到達していることを確認するだけ。

ここまででビルドする。動作させてみる。ボタンを押したら、Logcatに、onButtonClickedが表示されてゐることがわかる。 MainFragmentのボタンを押して、Activityに通知されてゐる。

つづく。

ここまでのコード

MainFragment.kt


class MainFragment : Fragment() {

    companion object {
        fun newInstance() = MainFragment()
    }

    private lateinit var viewModel: MainViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        val view = inflater.inflate(R.layout.main_fragment, container, false)
        val button = view.findViewById<Button>(R.id.buttonMain)
        button.setOnClickListener {
            val listener = context as? OnButtonClickListener
            listener?.onButtonClicked()
        }
        return view
    }

    interface OnButtonClickListener {
        fun onButtonClicked()
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
        // TODO: Use the ViewModel
    }

}


MainActivity.kt


class MainActivity : AppCompatActivity(), MainFragment.OnButtonClickListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .replace(R.id.container, MainFragment.newInstance())
                .commitNow()
        }
    }

    override fun onButtonClicked() {
        Log.i("MainActivity", "onButtonClicked")
    }

}


(了)

アーカイブ