やりたいこと
以下のレイアウトで、TextViewをタッチした時にRelativeLayoutのタッチ処理を呼ぶ方法を調べます。
- RelativeLayout
-
- LinearLayout
-
-
- TextView
-
前提
クリックと同様に、レイアウトの重複部分をタッチした時に実行される処理は一番上のレイアウトの処理です(今回の場合でいえばTextView)。
しかし、クリックと違うところがあります。それは、タッチの場合は親要素の処理を実行するか否かを指定できるという点です。
クリック: 一番上のレイアウトの処理のみ行われる。
タッチ: 親要素に対して処理を実行させるか指定できる。
前提の検証
上の内容だと何を言っているのかわかりづらいですね。笑
実際にソースを書いて処理の動きを見て見たいと思います。
TextView textView = (TextView) findViewById(R.id.textView);
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.linearLayout);
RelativeLayout relativeLayout = (RelativeLayout) findViewById(R.id.relativeLayout);
relativeLayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("RelativeLayout");
return false;
}
});
linearLayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("LinearLayout");
return false;
}
});
textView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("textView");
return false;
}
});
SAMPLE001をクリックするとconsoleには
textView
LinearLayout
RelativeLayout
が表示されました。
次は各処理を以下の通り変更します。
return true;
SAMPLE001をクリックするとconsoleには
textView
のみ表示されました。
つまり、onTouchの返り値によって以下のように処理の仕方が異なっています。
trueの場合は処理を親要素に渡さない。
falseの場合は処理を親要素に渡す。
検証
さっきのソースを以下のように書き換えると、RelativeLayoutのタッチイベントを実行することができました!
textView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("textView");
RelativeLayout grandParent = (RelativeLayout) v.getParent().getParent();
grandParent.onTouchEvent(event);
return true;
}
});
別のやり方
onTouchにはこれ以外にも親要素のtouchイベントを呼び出す方法があります。
それが、ActivityクラスのdispatchTouchEventをOverrideする方法です。
touchイベントって一番子供のレイアウトの処理から始まりますよね。
dispatchTouchEventは子レイアウトに処理を渡すかどうかを決定し、自分のtouchイベント処理を実行させる二つの機能を持っています。
つまり、touchイベントが発生すると。
A(親レイアウト)のdispatchTouchEventが呼ばれる。
↓
・子レイアウトが存在する場合は、子レイアウト(B)のdispatchTouchEventを呼ぶ。
・子レイアウトが存在しない場合は、onTouchイベントを呼ぶ。
なのでActivityクラスのdispatchTouchEventをOverrideすると、子レイアウトのonTouchイベントを呼び出す前に処理を行うことができます。
また、このメソッド内でreturn true;を返すと子レイアウトに処理を行わせないこともできます。
例えば、
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
// RelativeLayoutがクリックされた時に行いたい処理を書く
return false;
}
こうすると、子レイアウトのonTouchが呼ばれる前にRelativeLayoutがタッチされた時に行いたい処理を行うことができます。
と基本的なことを書きました。
今度はもう少し実際に直面した問題に近い内容で書いてみようと思います。