質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

解決済

1回答

2928閲覧

Android Kotlin 横スワイプによる画面遷移について

RgrayLemon

総合スコア2

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

0クリップ

投稿2021/07/19 02:04

編集2021/07/19 09:52

前提・実現したいこと

Kotlinを使用してAndroidアプリの作成を行っています。
このアプリには4つの機能(画面)があり、それぞれの機能を横スワイプで切り替えられるようにしたいと思っています。
また各機能の中でアクティビティの移動、画面遷移を行うものがあります。
横スワイプによる画面移動はviewPajerとフラグメントを利用して機能のトップ画面については作成することが出来ました。

現在困っている点は、機能内でアクティビティを移動するとスワイプで移動ができなくなることです。viewPagerで設定しているフラグメントではなくなるのでそれ自体は当然だとは思うのですが、どういったものを利用すれば意図した動きが可能になるでしょうか?
そもそもそういったことは可能なのでしょうか?

試したこと

アクティビティではなくフラグメントに移動するものも試しましたが遷移が上手くいかないようです。
子フラグメントやnavigationなるものが利用できるのかな?と考えたりしたのですがよく分かりませんでした。
同一のフラグメントで中身の表示・非表示も考えたのですが、機能によっては大きく画面が変わるので少し厳しい気もします。

補足情報(FW/ツールのバージョンなど)

Android Studio 4.2.2
Android 11.0(API 30)

###コード
画面遷移に関係する部分のみ記載します。

フラグメント1とアクティビティ1が機能1、フラグメント2は機能2に該当します。
アプリ起動時にフラグメント1が表示され、アクティビティ1に遷移する前はフラグメント2と横スワイプで移動できます。しかしアクティビティ1に遷移するとフラグメント2にスワイプで遷移できなくなります。

本質問はアクティビティ1に遷移したあともフラグメント2にスワイプで遷移できるようにするにはどういった改善が必要か、ということです。

アダプター

Kotlin

1class SamplePagerAdapter(fm: FragmentManager, private val fragmentList: List<Fragment>) : 2FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { 3 4 // 表示するフラグメントを制御する 5 override fun getItem(position: Int): Fragment { 6 return fragmentList[position] 7 } 8 9 // viewPagerにセットするコンテンツ(フラグメントリスト)のサイズ 10 override fun getCount(): Int { 11 return fragmentList.size 12 } 13}

MainActivity

Kotlin

1class MainActivity : AppCompatActivity() { 2 override fun onCreate(savedInstanceState: Bundle?) { 3 4 super.onCreate(savedInstanceState) 5 setContentView(R.layout.activity_main) 6 7 val fragmentList = arrayListOf<Fragment>( 8 view1Fragment(), 9 view2Fragment(), 10 ) 11 12 /// adapterのインスタンス生成 13 val adapter = SamplePagerAdapter(supportFragmentManager, fragmentList) 14 /// adapterをセット 15 viewPager.adapter = adapter 16 } 17 18 fun moving(view: View){ 19 val intent = Intent(this,Activity1::class.java) 20 startActivity(intent) 21 overridePendingTransition(0,0) 22 } 23}

MainActivity レイアウト

Kotlin

1 2<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 android:background="#f9f0e2"> 8 9<androidx.viewpager.widget.ViewPager 10 android:id="@+id/viewPager" 11 android:layout_width="match_parent" 12 android:layout_height="match_parent" 13 app:layout_constraintBottom_toBottomOf="parent" 14 app:layout_constraintEnd_toEndOf="parent" 15 app:layout_constraintStart_toStartOf="parent" 16 app:layout_constraintTop_toTopOf="parent" /> 17</RelativeLayout>

fragment1

Kotlin

1class view1Fragment : Fragment() { 2 override fun onCreateView( 3 inflater: LayoutInflater, container: ViewGroup?, 4 savedInstanceState: Bundle? 5 ): View? { 6 // Inflate the layout for this fragment 7 return inflater.inflate(R.layout.fragment_view1, container, false) 8 } 9} 10

fragment1レイアウト

Kotlin

1<androidx.constraintlayout.widget.ConstraintLayout 2 xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent"> 7 8<Button 9 android:id="@+id/first_button" 10 android:layout_width="wrap_content" 11 android:layout_height="wrap_content" 12 android:onClick="moving"/> 13 14</androidx.constraintlayout.widget.ConstraintLayout>

fragment2

Kotlin

1class view2Fragment : Fragment() { 2 override fun onCreateView( 3 inflater: LayoutInflater, container: ViewGroup?, 4 savedInstanceState: Bundle? 5 ): View? { 6 // Inflate the layout for this fragment 7 val view = inflater.inflate(R.layout.fragment_view2, container, false) 8 9 return view 10 } 11}

フラグメント2のレイアウトとアクティビティ1については何も無い単なる画面ということで割愛します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2021/07/19 03:03

言葉だけで希望する仕様や現状を説明されてもとても分かり難いです。 可能な限り現状を再現できるコードをご提示の上、どの部分での操作でどうしたいのにどうなってしまうのかという具体的なレベルにご質問を修正願えますか。
RgrayLemon

2021/07/19 04:08

ご指摘ありがとうございます。 画面遷移に関わると思われる部分のコードを追記いたしました。 スマホからの投稿になってしまったため、改行等が十分にできておらず見づらく申し訳ないですが、よろしくお願いします。
jimbe

2021/07/19 07:56

ちなみに、機能1のアクティビティ1というは、なぜアクティビティなのでしょうか。
RgrayLemon

2021/07/19 09:55

機能1を作成時まだAndroidアプリ作成の知識がほとんどなく、fragmentへの画面遷移やfindViewByIdの利用方法が分からなかったためアクティビティで作成しました。 アクティビティでなければならない理由はないです。
jimbe

2021/07/19 10:40

なるほど。 実際フラグメントにしようとされたそうですし、そのほうがよさそうですね。 それで、 > アクティビティではなくフラグメントに移動するものも試しましたが遷移が上手くいかない とありますが、どのように上手く行かなかったのでしょう。
RgrayLemon

2021/07/19 11:03

移動先のフラグメントをフラグメント3とします。 ボタンを押してフラグメントを移動させたかったので、fragmentManager.beginTransactionを利用してreplaceで移動させようと考えました。 その時、replace(R.id.viewPager,fragment3)としていたのですが、ボタンを押しても移動先のfragmentの画面が表示されませんでした。 何も表示されていないもののスクロールは可能で、フラグメント1、2に相当する分左右にスワイプできたのですが、フラグメント2についてもフラグメント3に移動後は画面が表示されませんでした。
jimbe

2021/07/19 11:14

全体の構造や操作としましては(本来は4機能/画面の所をテストのため2機能分に減らしたとして)、MainActivity 上でViewPager により機能1の Fragment1 と機能2の Fragment2 を左右スワイプで切り替えが出来るようにし、 Fragment1 にはボタンが有ってそれを押すと機能1の次の画面 Fragment3 が(Fragment1を置き換えて)表示される、と。 その状態で左右スワイプすると、Fragment3 と Fragment2 が切り替えられることになる、という感じでしょうか。
RgrayLemon

2021/07/19 11:20

そういうことです! 別アクティビティ/フラグメントへ遷移後も隣の機能にスワイプで切り替えができるようにしたいということです。
jimbe

2021/07/19 13:07

もうしわけありません、私の kotlin 能力の不足の所為で、"kotlinらしい" サンプルが出来ません。 少し試して出来た方法としましては、最初 SamplePagerAdapter を fragment1 と fragment2 の配列で作ってアダプタとして ViewPager に渡していますが、moving ボタンが押された時には新しい SamplePagerAdapter を fragment3 と fragment2 の配列で作って ViewPager に渡す方法です。 (この場合、fragment2 は共通ですので、別々に作らず共有したほうがよさそうです。) ただこの方法だと、例えば機能が4つあってそれぞれが機能1のように複数のフラグメントを切り替えられるようだったりすると組み合わせ爆発になってしまうので、少なくとも良い方法では無いでしょうねorz それで、ちゃんとするとすれば、fragment1 と fragment3 を載せるためのフラグメント(例えば) fragment4 をつくり、ViewPager には fragment4 と fragment2 を設定してスワイプで切り替えるようにし、fragment4 の中で fragment1 と fragment3 を切り替えられるようにするのが形としては良いように思います。 そして、それなら fragment4 の中に 1 と 3 のどちらを表示するのかを ViewModel で管理するようにして…等と考えましたら、kotlin "らしく" 書くにはどうなるんだ・・・となってしまいました。
guest

回答1

0

ベストアンサー

結構恥ずかしいコードですが、一応動いたので載せておきます。
なお、build.gradle の dependencies に以下の追加が必要でした。

implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation "androidx.fragment:fragment-ktx:1.3.5"

ViewPager には fragment4 と fragment2 を載せてスワイプさせ、 fragment4 に fragment1 か fragment3 を載せます。
ViewModel の func1_mode が 1 なら fragment1, 3 なら fragment3 を表示するとし、fragment1 のボタンが押されたら Fragment1 が func1_mode を 3 にしています。

MainActivity.kt

kotlin

1import androidx.appcompat.app.AppCompatActivity 2import android.os.Bundle 3import androidx.fragment.app.Fragment 4import androidx.lifecycle.ViewModelProviders 5import kotlinx.android.synthetic.main.activity_main.* 6 7class MainActivity : AppCompatActivity() { 8 override fun onCreate(savedInstanceState: Bundle?) { 9 super.onCreate(savedInstanceState) 10 setContentView(R.layout.activity_main) 11 12 var viewModel = ViewModelProviders.of(this).get(SampleViewModel::class.java) 13 14 val fragmentList = arrayListOf<Fragment>( 15 Fragment4(), 16 Fragment2(), 17 ) 18 19 /// adapterのインスタンス生成 20 val adapter = SamplePagerAdapter(supportFragmentManager, fragmentList) /// adapterをセット 21 viewPager.adapter = adapter 22 } 23}

SamplePagerAdapter.kt

kotlin

1import androidx.fragment.app.Fragment 2import androidx.fragment.app.FragmentManager 3import androidx.fragment.app.FragmentStatePagerAdapter 4 5class SamplePagerAdapter(fm: FragmentManager, private val fragmentList: List<Fragment>) : 6 FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { 7 8 // 表示するフラグメントを制御する 9 override fun getItem(position: Int): Fragment { 10 return fragmentList[position] 11 } 12 13 // viewPagerにセットするコンテンツ(フラグメントリスト)のサイズ 14 override fun getCount(): Int { 15 return fragmentList.size 16 } 17}

SampleViewModel.kt

kotlin

1import androidx.lifecycle.MutableLiveData 2import androidx.lifecycle.ViewModel 3 4class SampleViewModel : ViewModel() { 5 val func1_mode = MutableLiveData<Int>(1) 6 7 val func1_text = MutableLiveData<String>("dummy") 8}

Fragment1.kt

kotlin

1import android.os.Bundle 2import android.view.LayoutInflater 3import android.view.View 4import android.view.ViewGroup 5import androidx.fragment.app.Fragment 6import androidx.fragment.app.activityViewModels 7import kotlinx.android.synthetic.main.fragment1.* 8 9class Fragment1 : Fragment() { 10 override fun onCreateView( 11 inflater: LayoutInflater, container: ViewGroup?, 12 savedInstanceState: Bundle? 13 ): View? { 14 return inflater.inflate(R.layout.fragment1, container, false) 15 } 16 17 private val viewModel: SampleViewModel by activityViewModels() 18 19 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 20 super.onViewCreated(view, savedInstanceState) 21 22 first_button.setOnClickListener { 23 viewModel.func1_mode.value = 3 24 } 25 viewModel.func1_text.observe(viewLifecycleOwner) { text -> 26 func1_text.text = text 27 } 28 } 29}

Fragment2.kt

kotlin

1import android.os.Bundle 2import android.view.LayoutInflater 3import android.view.View 4import android.view.ViewGroup 5import androidx.fragment.app.Fragment 6 7class Fragment2 : Fragment() { 8 override fun onCreateView( 9 inflater: LayoutInflater, container: ViewGroup?, 10 savedInstanceState: Bundle? 11 ): View? { 12 return inflater.inflate(R.layout.fragment2, container, false) 13 } 14}

Fragment3.kt

kotlin

1import android.os.Bundle 2import android.view.LayoutInflater 3import android.view.View 4import android.view.ViewGroup 5import androidx.fragment.app.Fragment 6import androidx.fragment.app.activityViewModels 7import kotlinx.android.synthetic.main.fragment3.* 8 9class Fragment3 : Fragment() { 10 override fun onCreateView( 11 inflater: LayoutInflater, container: ViewGroup?, 12 savedInstanceState: Bundle? 13 ): View? { 14 return inflater.inflate(R.layout.fragment3, container, false) 15 } 16 17 private val viewModel: SampleViewModel by activityViewModels() 18 19 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 20 super.onViewCreated(view, savedInstanceState) 21 22 second_button.setOnClickListener { 23 viewModel.func1_text.value = func1_edit.text.toString() 24 viewModel.func1_mode.value = 1 25 } 26 } 27}

Fragment4.kt

kotlin

1import android.os.Bundle 2import android.view.LayoutInflater 3import android.view.View 4import android.view.ViewGroup 5import androidx.fragment.app.Fragment 6import androidx.fragment.app.activityViewModels 7 8class Fragment4 : Fragment() { 9 override fun onCreateView( 10 inflater: LayoutInflater, container: ViewGroup?, 11 savedInstanceState: Bundle? 12 ): View? { 13 return inflater.inflate(R.layout.fragment4, container, false) 14 } 15 16 private val model: SampleViewModel by activityViewModels() 17 private var fragment1 = Fragment1() 18 private var fragment3 = Fragment3() 19 20 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 21 super.onViewCreated(view, savedInstanceState) 22 23 model.func1_mode.observe(viewLifecycleOwner) { mode -> 24 var f:Fragment = fragment1 25 if(mode != 1) f = fragment3 26 childFragmentManager.beginTransaction().replace(R.id.function1_container, f).commit() 27 } 28 } 29}

以下レイアウト
activity_main.xml

xml

1<?xml version="1.0" encoding="utf-8"?> 2<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 tools:context=".MainActivity" 8 android:background="#f9f0e2"> 9 10 <androidx.viewpager.widget.ViewPager 11 android:id="@+id/viewPager" 12 android:layout_width="match_parent" 13 android:layout_height="match_parent" 14 app:layout_constraintBottom_toBottomOf="parent" 15 app:layout_constraintEnd_toEndOf="parent" 16 app:layout_constraintStart_toStartOf="parent" 17 app:layout_constraintTop_toTopOf="parent" /> 18</RelativeLayout>

fragment1.xml

xml

1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 xmlns:app="http://schemas.android.com/apk/res-auto" 5 xmlns:tools="http://schemas.android.com/tools" 6 android:layout_width="match_parent" 7 android:layout_height="match_parent" 8 tools:context=".Fragment1"> 9 10 <Button 11 android:id="@+id/first_button" 12 android:layout_width="wrap_content" 13 android:layout_height="wrap_content" 14 android:text="to Fragment3" 15 app:layout_constraintBottom_toBottomOf="parent" 16 app:layout_constraintEnd_toEndOf="parent" 17 app:layout_constraintStart_toStartOf="parent" 18 app:layout_constraintTop_toBottomOf="@id/func1_text" /> 19 20 <TextView 21 android:id="@+id/func1_text" 22 android:layout_width="0dp" 23 android:layout_height="wrap_content" 24 android:layout_marginStart="16dp" 25 android:layout_marginEnd="16dp" 26 app:layout_constraintBottom_toBottomOf="parent" 27 app:layout_constraintEnd_toEndOf="parent" 28 app:layout_constraintStart_toStartOf="parent" 29 app:layout_constraintTop_toTopOf="parent" /> 30 31</androidx.constraintlayout.widget.ConstraintLayout>

fragment2.xml

xml

1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 tools:context=".Fragment2"> 8 <TextView 9 android:layout_width="match_parent" 10 android:layout_height="match_parent" 11 android:text="Fragment2" /> 12</androidx.constraintlayout.widget.ConstraintLayout>

fragment3.xml

xml

1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 xmlns:app="http://schemas.android.com/apk/res-auto" 5 xmlns:tools="http://schemas.android.com/tools" 6 android:layout_width="match_parent" 7 android:layout_height="match_parent" 8 tools:context=".Fragment3"> 9 10 <EditText 11 android:id="@+id/func1_edit" 12 android:layout_width="0dp" 13 android:layout_height="wrap_content" 14 android:layout_marginStart="16dp" 15 android:layout_marginEnd="16dp" 16 android:ems="10" 17 android:text="Fragment3" 18 app:layout_constraintBottom_toBottomOf="parent" 19 app:layout_constraintEnd_toEndOf="parent" 20 app:layout_constraintStart_toStartOf="parent" 21 app:layout_constraintTop_toTopOf="parent" /> 22 <Button 23 android:id="@+id/second_button" 24 android:layout_width="wrap_content" 25 android:layout_height="wrap_content" 26 android:text="set Text" 27 app:layout_constraintBottom_toBottomOf="parent" 28 app:layout_constraintEnd_toEndOf="parent" 29 app:layout_constraintStart_toStartOf="parent" 30 app:layout_constraintTop_toBottomOf="@id/func1_edit" /> 31 <TextView 32 android:id="@+id/textView" 33 android:layout_width="wrap_content" 34 android:layout_height="wrap_content" 35 android:text="Input Text:" 36 app:layout_constraintStart_toStartOf="@id/func1_edit" 37 app:layout_constraintBottom_toTopOf="@id/func1_edit" /> 38</androidx.constraintlayout.widget.ConstraintLayout>

fragment4.xml

xml

1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 android:id="@+id/function1_container" 8 tools:context=".Fragment4"> 9</androidx.constraintlayout.widget.ConstraintLayout>

投稿2021/07/19 14:24

編集2021/07/20 12:17
jimbe

総合スコア13209

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

RgrayLemon

2021/07/20 04:41

回答ありがとうございます! 記載のコードを実行したところ、期待していた動きが得られました! ちなみにフラグメント1とフラグメント3の間で値の受け渡しは可能なのでしょうか?
jimbe

2021/07/20 05:02 編集

ViewModel が アプリケーションを通じて存在し、各画面間のデータを保持できます。 というより、モデル(ViewModel) が各データを保持し、ビュー(フラグメント等)がその中から必要なデータに接触して表示する…という設計(?)がありまして、その為に ViewModel というものが作られています。(MVVM や MVC といったキーワードで検索すると色々なモノが出てくると思います。) フラグメント間で、どこかで値を設定したら他の所に表示する、というようなことであれば、ViewModel に”保存する変数”を作り、あるフラグメントでそこに値を設定し、表示するフラグメントのほうではその変数にオブザーバとして”その変数の値を表示するコード”を登録しておけば、変数に値が設定されたら表示するコードが呼ばれますので、別途同期を気にする必要はありません。
jimbe

2021/07/20 12:21

fragment1 と fragment3 のコードとレイアウトおよび SampleVewModel を修正してみました。 fragment3 でテキストを入力して ViewModel に保存し mode を 1 にすると、fragment1 に戻って、 ViewModel に保存した文字列が表示されます。 スワイプ等に影響はないはずです。
RgrayLemon

2021/07/21 02:35

加筆修正ありがとうございます! 調べてもviewmodelによるデータ受け渡しの理解が及ばず悩んでいたので大変助かりました! おかげさまで無事目的物を作成することが出来ました! 大変お手数お掛けいたしました。
jimbe

2021/07/21 05:00 編集

ViewModel 等を真面目に(理論的に?)やろうとすると、(ソフトウェア全般的に考察される)デザインパターンを、数ある OS の一つである Android でどう実装するか(したか)というようなレベルまで話が進み易いので、それなりに大変かと思います。 が、プログラムのあちこちでそれぞれにデータ保持したりそれを渡し合ったりするのではなく、データの管理や(表示以外の)処理を担当するオブジェクトを置いて、表示側はそのオブジェクトを通じてデータを得る…という形式は(カタチや言葉は違っても)使われていると思います。 お時間がある時にでも書籍やネットでお調べになられては如何でしょうか。 おつかれさまでした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問