ViewPager
の中にScrollView
があります。垂直方向だけでなく水平方向にもスクロールできる必要があります。これを達成するために、私のViewPager
がタッチされるたびに垂直スクロールを無効にする必要があり(v.getParent().requestDisallowInterceptTouchEvent(true);
)、水平にスクロールできるようにしました。
しかし同時に、viewPagerをクリックして全画面モードで開くことができるようにする必要があります。
問題は、onTouchがonClickの前に呼び出され、OnClickが呼び出されないことです。
onClickとonClickの両方を実装するにはどうすればよいですか?
viewPager.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("TOUCHED ");
if(event.getAction() == MotionEvent.???){
//open fullscreen activity
}
v.getParent().requestDisallowInterceptTouchEvent(true); //This cannot be removed
return false;
}
});
viewPager.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("CLICKED ");
Intent fullPhotoIntent = new Intent(context, FullPhotoActivity.class);
fullPhotoIntent.putStringArrayListExtra("imageUrls", imageUrls);
startActivity(fullPhotoIntent);
}
});
マソウダダシの答えは私がそれを理解するのに役立ちました。
最終的には次のようになります。
viewPager.setOnTouchListener(new OnTouchListener() {
private int CLICK_ACTION_THRESHOLD = 200;
private float startX;
private float startY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX();
startY = event.getY();
break;
case MotionEvent.ACTION_UP:
float endX = event.getX();
float endY = event.getY();
if (isAClick(startX, endX, startY, endY)) {
launchFullPhotoActivity(imageUrls);// WE HAVE A CLICK!!
}
break;
}
v.getParent().requestDisallowInterceptTouchEvent(true); //specific to my project
return false; //specific to my project
}
private boolean isAClick(float startX, float endX, float startY, float endY) {
float differenceX = Math.abs(startX - endX);
float differenceY = Math.abs(startY - endY);
return !(differenceX > CLICK_ACTION_THRESHOLD/* =5 */ || differenceY > CLICK_ACTION_THRESHOLD);
}
}
ユーザーが画面に触れた時間を記録することで、本当に簡単なことをしました。
private long lastTouchDown;
private static int CLICK_ACTION_THRESHHOLD = 200;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastTouchDown = System.currentTimeMillis();
break;
case MotionEvent.ACTION_UP:
if (System.currentTimeMillis() - lastTouchDown < CLICK_ACTION_THRESHHOLD) {
Log.w("App", "You clicked!");
}
break;
}
return true;
}
GestureDetector にはこれが組み込まれていることにも注意してください。onSingleTapUpを見てください
両方を開発するのは間違った考えです。ユーザーが画面に触れることでさまざまなことを行う可能性がある場合、ユーザーの目的を理解することは少し気の利いたものであり、そのためのコードを開発する必要があります。
2つのソリューション:
1-(より良いアイデア)onTouchイベントチェックでモーションがあるかどうかを確認します。あなたは何かを使用して動きがあるかどうかをチェックすることでそれを行うことができます:
ACTION_UP
ACTION_DOWN
ACTION_MOVE
このようにしてください
if(event.getAction() != MotionEvent.ACTION_MOVE)
画面上のユーザーの指の動きの距離をチェックして、クリック中に誤って動くのではなく、動きが起こったことを確認することもできます。このようにしてください:
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
if(isDown == false)
{
startX = event.getX();
startY = event.getY();
isDown = true;
}
Break;
case MotionEvent.ACTION_UP
{
endX = event.getX();
endY = event.getY();
break;
}
}
上記のいずれも起こらなかった場合はクリックとみなし、クリックでやりたいことを行います。
2)UIと縁がある場合は、ボタンや画像ボタンなどを作成してフルスクリーニングを行い、onClickを設定します。
幸運を
ユーザーのクリックとロングクリックを区別する必要がある場合があります。そうしないと、両方が同じものとして検出されます。私はそれを可能にするためにこれをしました:
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX();
startY = event.getY();
bClick = true;
tmrClick = new Timer();
tmrClick.schedule(new TimerTask() {
public void run() {
if (bClick == true) {
bClick = false;
Log.d(LOG_TAG, "Hey, a long press event!");
//Handle the longpress event.
}
}
}, 500); //500ms is the standard longpress response time. Adjust as you see fit.
return true;
case MotionEvent.ACTION_UP:
endX = event.getX();
endY = event.getY();
diffX = Math.abs(startX - endX);
diffY = Math.abs(startY - endY);
if (diffX <= 5 && diffY <= 5 && bClick == true) {
Log.d(LOG_TAG, "A click event!");
bClick = false;
}
return true;
default:
return false;
}
}
上記の答えは主に時間を覚えています。ただし、MotionEvent
はすでにカバーしています。ここでは、オーバーヘッドの少ないソリューションです。 kotlin
で記述されていますが、それでも理解できるはずです。
private const val ClickThreshold = 100
override fun onTouch(v: View, event: MotionEvent): Boolean {
if(event.action == MotionEvent.ACTION_UP
&& event.eventTime - event.downTime < ClickThreshold) {
v.performClick()
return true // If you don't want to do any more actions
}
// do something in case its not a click
return true // or false, whatever you need here
}
このソリューションは、ほとんどのアプリケーションには十分ですが、クリックと非常に速いスワイプを区別するのにはあまり適していません。
したがって、これを上記の回答と組み合わせて、ポジションも考慮に入れることがおそらく最良の方法です。
ソリューションの時間と位置を組み合わせた方が良いと思います:
private float initialTouchX;
private float initialTouchY;
private long lastTouchDown;
private static int CLICK_ACTION_THRESHHOLD = 100;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastTouchDown = System.currentTimeMillis();
//get the touch location
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
return true;
case MotionEvent.ACTION_UP:
int Xdiff = (int) (event.getRawX() - initialTouchX);
int Ydiff = (int) (event.getRawY() - initialTouchY);
if (System.currentTimeMillis() - lastTouchDown < CLICK_ACTION_THRESHHOLD && (Xdiff < 10 && Ydiff < 10)) {
//clicked!!!
}
return true;
}
return false;
}
});
それを行うエレガントな方法
public class CustomView extends View {
private GestureDetectorCompat mDetector;
public CustomView(Context context) {
super(context);
mDetector = new GestureDetectorCompat(context, new MyGestureListener());
}
@Override
public boolean onTouchEvent(MotionEvent event){
return this.mDetector.onTouchEvent(event);
}
class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {return true;}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
//...... click detected !
return false;
}
}
}
MotionEvent.ACTION_UPは、センサーの最初の時間をタッチすることを意味します
MotionEvent.ACTION_DOWNは、タッチセンサーがもう検出しないことを意味します
たとえば、MotionEvent.ACTION_MOVEは、センサーが移動(スワイプ、スクロールなど)を検出することを意味します
したがって、リスナーの以前のイベントタイプの状態を保存し、最新のイベントと比較して、ユーザーが何をしたかを理解する必要があります。
すべてのビューはその親をカバーします。したがって、ビューにTouchリスナーがある場合、インターセプターのように機能します。 boolean onTouch(View v、MotionEvent event)メソッドからfalseを返す親ビューへのタッチ呼び出しのチェーンを継続することが可能です。
追加:View.performClick()は、OnClickListenerがある場合にのみtrueを返します
yourView.setOnTouchListener(new View.OnTouchListener() {
int lastEvent;
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP && lastEvent == MotionEvent.ACTION_DOWN) {
return ~ /**Click detected. Do something and return result**/
}
lastEvent = event.getAction();
return v.performClick();
}
});
TouchListenerがインターセプトするため、ビューがこのようにタッチイベントを受け取るのを妨げていると思います。あなたはどちらか
v.onTouchEvent(event)
を呼び出して、ToucheListener内でイベントをディスパッチしますViewPager.onTouchEvent(MotionEvent)
をオーバーライドして、イベントをインターセプトしないまた、true
を返すと、イベントを消費しなかったため、次のイベントに邪魔されず、ジェスチャーが完了するまで(つまり、指が離されるまで)次のイベントを受け取りません。再び)。
onTouchClickListenerを使用できます
使用法:
view.setOnTouchListener(new OnTouchClickListener(new OnTouchClickListener.OnClickListener() {
@Override
public void onClick(View v) {
//perform onClick
}
},5));