簡體   English   中英

如何檢測 onTouch 偵聽器中的點擊?

[英]How can I detect a click in an onTouch listener?

我在ScrollView有一個ViewPager 我需要能夠水平和垂直滾動。 為了實現這一點,必須在我的ViewPager被觸摸時禁用垂直滾動( v.getParent().requestDisallowInterceptTouchEvent(true); ),以便它可以水平滾動。

但同時我需要能夠單擊 viewPager 以全屏模式打開它。

問題是 onTouch 在 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);
    }
});

Masoud Dadashi 的回答幫助我弄清楚了。

這就是它最終的樣子。

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 static int CLICK_THRESHOLD = 100;

@Override
public boolean onTouch(View v, MotionEvent event) {
    long duration = event.getEventTime() - event.getDownTime();

    if (event.getAction() == MotionEvent.ACTION_UP && duration < CLICK_THRESHOLD) {
        Log.w("bla", "you clicked!");
    }

    return false;
}

另外值得注意的是, GestureDetector內置了類似的東西。 看看 onSingleTapUp

兩者都發展是錯誤的想法。 當用戶可以通過觸摸屏幕來做不同的事情時,理解用戶目的有點漂亮,你需要為它開發一段代碼。

兩種解決方案:

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;
    }
}

不要試圖重新發明輪子!

優雅的方法:

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已經涵蓋了您。 這是一個開銷較小的解決方案。 它是用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;
            }
        });

我相信您會阻止您的視圖以這種方式接收觸摸事件,因為您的 TouchListener 會攔截它。 你可以

  • 通過調用v.onTouchEvent(event)在 ToucheListener 中調度事件
  • 改為覆蓋ViewPager.onTouchEvent(MotionEvent)不攔截事件

此外,返回true意味着您沒有消費該事件,並且您對后續事件不感興趣,因此在手勢完成之前您不會收到后續事件(即手指再次向上)。

你可以使用 OnTouchClickListener

https://github.com/hoffergg/BaseLibrary/blob/master/src/main/java/com/dailycation/base/view/listener/OnTouchClickListener.java

用法:

 view.setOnTouchListener(new OnTouchClickListener(new OnTouchClickListener.OnClickListener() { @Override public void onClick(View v) { //perform onClick } },5));

if (event.eventTime - event.downTime < 100 && event.actionMasked == MotionEvent.ACTION_UP) {
     view.performClick()
     return false
}

此代碼將執行觸摸事件和單擊事件。

viewPager.setOnTouchListener(new View.OnTouchListener() {
            private int initialX;
            private int initialY;
            private float initialTouchX;
            private float initialTouchY;


            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:

                        //remember the initial position.
                        initialX = params.x;
                        initialY = params.y;

                        //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);


                        //The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little while clicking.
                        
                        if (Xdiff < 10 && Ydiff < 10) {
                            //So that is click event.
                        }
                        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(mFloatingView, params);
                        return true;
                }
                return false;
            }
        });

這是來源。

我認為你的問題來自於:

v.getParent().requestDisallowInterceptTouchEvent(true); //This cannot be removed

看看文檔 你試過刪除線嗎? 不刪除此行的要求是什么?

考慮到,根據文檔,如果您從 onTouchListener 返回 true,則使用該事件,如果您返回 false,則傳播該事件,因此您可以使用它來傳播該事件。

此外,您應該從以下位置更改您的代碼:

 System.out.println("CLICKED ");

至:

 Log.d("MyApp", "CLICKED ");

在 logcat 中獲得正確的輸出。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM