タッチイベントとボタンのクリックイベントを処理しようとしています。私は次のことを行います:
button.setOnClickListener(clickListener);
button.setOnTouchListener(touchListener);
リスナーが1つ登録されている場合は問題なく動作しますが、リスナーを使用しようとすると、両方のタッチイベントのみが発生します。回避策はありますか?私は何を間違えていますか?
ClickListener
とTouchListener
には微妙ですが、非常に重要な違いがあります。 TouchListener
は、ビューがイベントに応答する前に実行されます。 ClickListener
は、ビューが処理した後にのみイベントを受け取ります。
したがって、画面に触れると、TouchListener
が最初に実行され、イベントにtrue
を返すと、ClickListener
はそれを取得しません。ただし、デバイスのトラックボールを押すと、ClickListener
が応答しないため、TouchListener
が起動されます。
少し注意が必要です。
onTouchListener
を設定した場合、ACTION_DOWN
_でtrue
を返す必要があります。これは、システムにイベントを消費したことを通知し、他のリスナーにトリクルダウンしません。
ただし、OnClickListener
は実行されません。
だからあなたは思うかもしれません、私はそこで自分のことをして、false
を返すので、クリックも受け取ることができます。そうすれば動作しますが、他の今後のタッチイベント(_ACTION_MOVE
_、_ACTION_UP
_)にサブスクライブされません。したがって、唯一のオプションはtrue
を返すことです。ただし、前述のようにクリックイベントは発生しません。
したがって、view.performClick()
を使用して_ACTION_UP
_で手動でクリックを実行する必要があります
これは動作します。
すばらしい回答をありがとう@urSusに感謝
ただし、その場合、すべてのタッチがクリックを実行します。ACTION_MOVEmove
イベントとclick
イベントを分離したい場合、ちょっとしたトリックを使用できますboolean
フィールドを定義し、次のように使用します。
@Override
public boolean onTouch(View view, MotionEvent motionEvent)
{
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
shouldClick = true;
.
.
break;
case MotionEvent.ACTION_UP:
if (shouldClick)
view.performClick();
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
//Do your stuff
shouldClick = false;
break;
}
rootLayout.invalidate();
return true;
}
OnTouchListener
でfalseを返す必要があります。そうすると、OnClickListener
も処理されます。
true
でOnTouchListener
を返していると思いますか?それはイベントを消費するので、それ以上の処理のために送信されることはありません。
サイドノートで-クリックとタッチの両方のリスナーを持つことのポイントは何ですか?
上記の答えはすべて、できないsetOnTouchListener
とsetOnClickListener
の両方を処理すると言われています。
しかし、私はできると思いますhandle both by return false
in setOnTouchListener
例
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button = findViewById(R.id.button)
button.setOnClickListener {
Log.i("TAG", "onClick")
}
button.setOnTouchListener { v, event ->
Log.i("TAG", "onTouch " + event.action)
false
}
}
Button
をクリックすると、logcatは次のように表示されます
I/TAG: onTouch 0
I/TAG: onTouch 1
I/TAG: onClick
button.setOnTouchListener(this);
ここにインターフェイスとコードを実装します。
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (view.getId()) {
case R.id.send:
switch(motionEvent.getAction()){
case MotionEvent.ACTION_DOWN:
//when the user has pressed the button
//do the needful here
break;
case MotionEvent.ACTION_UP:
//when the user releases the button
//do the needful here
break;
}
break;
}
return false;
}
## Exact working solution for both click action and touch listener(dragging) ##
private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;
private float CLICK_ACTION_THRESHOLD = 0.5f;
private float startX;
private float startY;
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (view.getId()) {
case R.id.chat_head_profile_iv:
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//remember the initial position.
initialX = params.x;
initialY = params.y;
startX = event.getX();
startY = event.getY();
//get the touch location
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
return true;
case MotionEvent.ACTION_UP:
float endX = event.getX();
float endY = event.getY();
if (shouldClickActionWork(startX, endX, startY, endY)) {
openScreen();// WE HAVE A CLICK!!
}
return true;
case MotionEvent.ACTION_MOVE:
//Calculate the X and Y coordinates of the view.
params.x = initialX + (int) (event.getRawX() - initialTouchX);
params.y = initialY + (int) (event.getRawY() - initialTouchY);
//Update the layout with new X & Y coordinate
mWindowManager.updateViewLayout(mChatHeadView, params);
return true;
}
break;
}
return true;
}
private boolean shouldClickActionWork(float startX, float endX, float startY, float endY) {
float differenceX = Math.abs(startX - endX);
float differenceY = Math.abs(startY - endY);
if ((CLICK_ACTION_THRESHOLD > differenceX) && (CLICK_ACTION_THRESHOLD > differenceY))
return true;
else
return false;
}
これは、ClickListenerをシャドウしないTouchListenerの例です。
import Android.graphics.PointF
import Android.view.MotionEvent
import Android.view.MotionEvent.*
import Android.view.View
import kotlin.math.abs
object TouchAndClickListener : View.OnTouchListener {
/** Those are factors you can change as you prefer */
private const val touchMoveFactor = 10
private const val touchTimeFactor = 200
private var actionDownPoint = PointF(0f, 0f)
private var previousPoint = PointF(0f, 0f)
private var touchDownTime = 0L
override fun onTouch(v: View, event: MotionEvent) = when (event.action) {
ACTION_DOWN -> PointF(event.x, event.y).let {
actionDownPoint = it // Store it to compare position when ACTION_UP
previousPoint = it // Store it to compare position when ACTION_MOVE
touchDownTime = now() // Store it to compare time when ACTION_UP
/* Do other stuff related to ACTION_DOWN you may whant here */
true
}
ACTION_UP -> PointF(event.x, event.y).let {
val isTouchDuration = now() - touchDownTime < touchTimeFactor // short time should mean this is a click
val isTouchLength = abs(it.x - actionDownPoint.x) + abs(it.y - actionDownPoint.y) < touchMoveFactor // short length should mean this is a click
val shouldClick = isTouchLength && isTouchDuration // Check both
if (shouldClick) yourView.performClick() //Previously define setOnClickListener{ } on yourView, then performClick() will call it
/* Do other stuff related to ACTION_UP you may whant here */
true
}
ACTION_MOVE -> PointF(event.x, event.y).let {
/* Do other stuff related to ACTION_MOVE you may whant here */
previousPoint = it
true
}
else -> false // Nothing particular with other event
}
private fun now() = System.currentTimeMillis()
}
Gridviewで両方のイベントを可能にするには、次のようにタッチリスナーの戻り値を「false」にするだけで、これはうまくいきました。
**GridView myView = findViewById(R.id.grid_view);
myView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// ... Respond to touch events
return false;
}
});**
このようにして、両方のイベントを達成できます