[解決済み】ScrollView内のHorizontalScrollViewのタッチハンドリング
質問
画面全体がスクロールできるように、レイアウト全体を囲むScrollViewを持っています。 このScrollViewの最初の要素は、水平方向にスクロールできる機能を持つHorizontalScrollViewブロックです。 タッチ イベントを処理するために、ontouchlistener を horizontalscrollview に追加し、ACTION_UP イベントで最も近い画像にビューを強制的に "snap"しています。
つまり、アンドロイドの純正ホームスクリーンのように、次から次へとスクロールできて、指を離すと1つの画面に収まるような効果を狙っているのです。
これは、1つの問題を除いてすべてうまくいきます。ACTION_UPを登録するためには、ほぼ完全に水平に左から右へスワイプする必要があります。 最低でも縦にスワイプすると(多くの人が携帯電話で左右にスワイプするときにやりがちだと思いますが)、ACTION_UPの代わりにACTION_CANCELが表示されます。 これは、horizontalscrollviewがscrollview内にあり、scrollviewが垂直スクロールを可能にするために垂直方向のタッチをハイジャックしているからだと私は考えています。
水平スクロールビュー内のスクロールビューのタッチ イベントを無効にし、スクロールビュー内の他の場所で通常の垂直スクロールを可能にするには、どうすればよいですか?
以下は、私のコードのサンプルです。
public class HomeFeatureLayout extends HorizontalScrollView {
private ArrayList<ListItem> items = null;
private GestureDetector gestureDetector;
View.OnTouchListener gestureListener;
private static final int SWIPE_MIN_DISTANCE = 5;
private static final int SWIPE_THRESHOLD_VELOCITY = 300;
private int activeFeature = 0;
public HomeFeatureLayout(Context context, ArrayList<ListItem> items){
super(context);
setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
setFadingEdgeLength(0);
this.setHorizontalScrollBarEnabled(false);
this.setVerticalScrollBarEnabled(false);
LinearLayout internalWrapper = new LinearLayout(context);
internalWrapper.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
internalWrapper.setOrientation(LinearLayout.HORIZONTAL);
addView(internalWrapper);
this.items = items;
for(int i = 0; i< items.size();i++){
LinearLayout featureLayout = (LinearLayout) View.inflate(this.getContext(),R.layout.homefeature,null);
TextView header = (TextView) featureLayout.findViewById(R.id.featureheader);
ImageView image = (ImageView) featureLayout.findViewById(R.id.featureimage);
TextView title = (TextView) featureLayout.findViewById(R.id.featuretitle);
title.setTag(items.get(i).GetLinkURL());
TextView date = (TextView) featureLayout.findViewById(R.id.featuredate);
header.setText("FEATURED");
Image cachedImage = new Image(this.getContext(), items.get(i).GetImageURL());
image.setImageDrawable(cachedImage.getImage());
title.setText(items.get(i).GetTitle());
date.setText(items.get(i).GetDate());
internalWrapper.addView(featureLayout);
}
gestureDetector = new GestureDetector(new MyGestureDetector());
setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
return true;
}
else if(event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL ){
int scrollX = getScrollX();
int featureWidth = getMeasuredWidth();
activeFeature = ((scrollX + (featureWidth/2))/featureWidth);
int scrollTo = activeFeature*featureWidth;
smoothScrollTo(scrollTo, 0);
return true;
}
else{
return false;
}
}
});
}
class MyGestureDetector extends SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
try {
//right to left
if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
activeFeature = (activeFeature < (items.size() - 1))? activeFeature + 1:items.size() -1;
smoothScrollTo(activeFeature*getMeasuredWidth(), 0);
return true;
}
//left to right
else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
activeFeature = (activeFeature > 0)? activeFeature - 1:0;
smoothScrollTo(activeFeature*getMeasuredWidth(), 0);
return true;
}
} catch (Exception e) {
// nothing
}
return false;
}
}
}
解決方法は?
更新:これがわかりました。ScrollViewのonInterceptTouchEventメソッドをオーバーライドして、Yの動きがXの動きと同じ場合にのみタッチイベントをインターセプトする必要がありました。ScrollViewのデフォルトの動作は、任意のYモーションがあるときはいつでもタッチイベントをインターセプトするように思われます。この修正により、ScrollView はユーザーが意図的に Y 方向にスクロールしている場合にのみイベントをインターセプトし、その場合は ACTION_CANCEL を子プロセスに渡します。
以下は、HorizontalScrollView を含む、私の Scroll View クラスのコードです。
public class CustomScrollView extends ScrollView {
private GestureDetector mGestureDetector;
public CustomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
mGestureDetector = new GestureDetector(context, new YScrollDetector());
setFadingEdgeLength(0);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return super.onInterceptTouchEvent(ev) && mGestureDetector.onTouchEvent(ev);
}
// Return false if we're scrolling in the x direction
class YScrollDetector extends SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return Math.abs(distanceY) > Math.abs(distanceX);
}
}
}
関連
-
[解決済み】シンボル 'AppCompatActivity' を解決できない。
-
[解決済み】Android Studio。adbバージョン」の結果を取得できない
-
[解決済み】カメラサービスへの接続に失敗しました。
-
[解決済み】Couldn't load memtrack module Logcat Error
-
[解決済み】Android "ビュー階層を作成した元のスレッドだけが、そのビューに触れることができる"
-
[解決済み] エラー - Android リソースのリンクに失敗しました (AAPT2 27.0.3 Daemon #0)
-
[解決済み] android.intent.action.MAINの意味は何ですか?
-
[解決済み] ScrollView 内の RecyclerView が動作しない。
-
[解決済み] ListViewをScrollViewの中に入れても、崩れないようにするにはどうしたらいいですか?
-
[解決済み】Android。ScrollViewが強制的に下に表示される
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】このアクティビティでは、Theme.AppCompatテーマ(またはその子孫)を使用する必要があります。
-
[解決済み】react-native: コマンドが見つかりません。
-
[解決済み】インストールエラー。インストールエラー:install_failed_older_sdk
-
[解決済み] 現在のテーマでスタイル 'coordinatorLayoutStyle' を見つけることができませんでした。
-
[解決済み】Android Studio AVD - Emulator: 終了コード 1 でプロセスが終了
-
[解決済み】Android Studioの初回起動。Android SDKアドオンリストにアクセスできない
-
[解決済み】IllegalStateException: ViewPager で onSaveInstanceState の後にこのアクションを実行できません。
-
[解決済み] sendUserActionEvent() は null です。
-
[解決済み] Android: @drawable/picture を drawable に変換するのに失敗しました。
-
[解決済み] Gradleのエラーです。イベントディスパッチスレッドからの書き込みアクセスは、Android Studioでのみ許可されます。