0
0

スクロールとスワイプの共存

Last updated at Posted at 2024-08-14

 AndroidStudioでGoogleカレンダーを模したアプリを作成していたところ、問題発生。

スワイプで前後の日週月に移動させたいが、日のみ縦スクロールが優先されてスワイプできない

 検索する限り様々対策されている方はいるようだが、自分のケースにあった解決法が見当たらなかったため、ChatGPTと相談しながら原因特定→問題解決を図った。が、なかなかいい解決方法が出てこないので、最終的には自分がこれ、と思った解決方法が当たった。

問題発生時の状況

 メイン画面(MainActivity)で上部タブにより、月・週・日の表示が切り替え可能。日のみ縦にスクロールして24時間分を表示させる。(GoogleCalendarの上にタブがあると思ってください)
 横スワイプでは前後の月・週・日付に移動させる(GestureDetectorを使用)が、日のみスワイプできない。

原因

 ScrollViewが優先されてGestureDetectorが効かなくなっている。

改善策

 スワイプ方向に応じてScrollViewを無効にする必要があるが、最初はこれをMainActivity.javaで実施しようとしていたが、うまくいかない。そこで、日の画面だけはMainActivity.javaでGestureDetectorのタッチリスナーを無効にし、日画面作成時にactivity_mainのコンテキストを引き継いだうえでGestureDetectorを実装。また、ScrollViewのタッチリスナーで横スワイプを無効に設定。

コード抜粋

 文章では分かりづらいため、コードの抜粋を以下に示す。

MainActivity.java
public class MainActivity extends AppCompatActivity {
   @Override
 protected void onCreate(Bundle savedInstanceState) {
   
   // GestureDetectorの設定
   gestureDetector = new GestureDetector(this, new GestureListener());

   // タブのクリックリスナー設定
   dateTypeTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
     @Override
     public void onTabSelected(@NonNull TabLayout.Tab tab) {
       // タブが選択されたときに、タブの位置に応じて画面を表示
       int tabPosition = tab.getPosition();

       // タブのテキストを更新
       updateTextViewBasedOnDate(showingDate);

       // タブの位置に応じて適切なフラグメントを作成
       displayFragmentForTab(tabPosition);

//        タブがDayタブ以外の場合、スワイプジェスチャーを有効にする(DayのスワイプはDayFragmentで設定)
       if (tabPosition != 2) { // Dayタブ以外が選択された場合
         findViewById(R.id.toDoDisplay).setOnTouchListener((v, event) -> {
           gestureDetector.onTouchEvent(event);
           return true;
         });
       } else {
         findViewById(R.id.toDoDisplay).setOnTouchListener(null);
       }
     }
     @Override
     public void onTabUnselected(@NonNull TabLayout.Tab tab) {
       // タブが非選択になったときの処理(必要なら追加)
     }

     @Override
     public void onTabReselected(@NonNull TabLayout.Tab tab) {
       // タブが再選択されたときの処理(必要なら追加)
     }
   });
 }

 public void displayFragmentForTab(int tabPosition) {
   Fragment fragment;

   switch (tabPosition) {
     case 0:
       fragment = MonthFragment.newInstance(showingDate);
       break;
     case 1:
       fragment = WeekFragment.newInstance(showingDate);
       break;
     case 2:
       fragment = DayFragment.newInstance(showingDate);
       break;
     default:
       fragment = DayFragment.newInstance(showingDate); // デフォルトのフラグメント
       break;
   }

   // フラグメントを表示
   getSupportFragmentManager().beginTransaction()
       .replace(R.id.toDoDisplay, fragment)
       .commit();
 }
}
DayFragment.xml
public class DayFragment extends Fragment {
 private MainActivity mainActivity;

 private static final String ARG_DATE = "showingDate";
 private Date showingDate;

 private TabLayout dateTypeTabLayout;
 private GestureDetector gestureDetector;

 public DayFragment() {
 }

 public static DayFragment newInstance(Date showingDate) {
   DayFragment fragment = new DayFragment();
   Bundle args = new Bundle();
   args.putSerializable(ARG_DATE, showingDate);
   fragment.setArguments(args);
   return fragment;
 }

 @Override
 public void onAttach(Context context) {
   super.onAttach(context);
   if (context instanceof MainActivity) {
     mainActivity = (MainActivity) context; // contextをMainActivityにキャスト
   } else {
     throw new RuntimeException(context.toString()
         + " must be an instance of MainActivity");
   }
 }

 @Override
 public void onCreate(@Nullable Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   if (getArguments() != null) {
     showingDate = (Date) getArguments().getSerializable(ARG_DATE);
   }
 }

 @Override
 public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
   View rootView = inflater.inflate(R.layout.fragment_day, container, false);

   gestureDetector = new GestureDetector(getContext(), mainActivity.new GestureListener());
   ScrollView scrollView = rootView.findViewById(R.id.dayScrollView);

   // ScrollView に OnTouchListener を設定
   scrollView.setOnTouchListener((v, event) -> {
     if (gestureDetector.onTouchEvent(event)) {
       // 横スワイプを検出した場合、ScrollView のスクロールを無効にする
       return true;
     } else {
       // 縦スワイプの場合、ScrollView のデフォルトのスクロールを許可する
       return v.onTouchEvent(event);
     }
   });

   return rootView;
 }
}
0
0
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
0