Development Tip

Android에서 길게 누르기 감지

yourdevel 2020. 11. 2. 19:54
반응형

Android에서 길게 누르기 감지


나는 현재 사용하고 있습니다

onTouchEvent(MotionEvent event){
}

사용자가 내 glSurfaceView를 누를 때를 감지하는 방법은 긴 클릭이 발생했을 때 감지하는 방법이 있습니다. 개발 문서에서 많은 것을 찾을 수 없다면 방법에 대한 일종의 해결 방법이 될 것이라고 생각합니다. ACTION_DOWN을 등록하고 ACTION_UP까지 얼마나 걸리는지 확인하는 것과 같은 것입니다.

OpenGL-es를 사용하여 Android에서 길게 누르는 것을 어떻게 감지합니까?


이 시도:

final GestureDetector gestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
    public void onLongPress(MotionEvent e) {
        Log.e("", "Longpress detected");
    }
});

public boolean onTouchEvent(MotionEvent event) {
    return gestureDetector.onTouchEvent(event);
};

GestureDetector 가 최고의 솔루션입니다.

흥미로운 대안이 있습니다. 에서 onTouchEvent 모든에 ACTION_DOWN의 일정의 Runnable은 1 초에 실행합니다. 모든 ACTION_UP 또는 ACTION_MOVE 에서 예약 된 Runnable을 취소합니다. ACTION_DOWN 이벤트 에서 1 초 이내에 취소가 발생하면 Runnable이 실행되지 않습니다.

final Handler handler = new Handler(); 
Runnable mLongPressed = new Runnable() { 
    public void run() { 
        Log.i("", "Long press!");
    }   
};

@Override
public boolean onTouchEvent(MotionEvent event, MapView mapView){
    if(event.getAction() == MotionEvent.ACTION_DOWN)
        handler.postDelayed(mLongPressed, ViewConfiguration.getLongPressTimeout());
    if((event.getAction() == MotionEvent.ACTION_MOVE)||(event.getAction() == MotionEvent.ACTION_UP))
        handler.removeCallbacks(mLongPressed);
    return super.onTouchEvent(event, mapView);
}

클릭, 긴 클릭 및 움직임을 감지하는 코드가 있습니다. 위에 주어진 답변과 모든 문서 페이지를 들여다 보면서 변경 한 내용이 상당히 조합되어 있습니다.

//Declare this flag globally
boolean goneFlag = false;

//Put this into the class
final Handler handler = new Handler(); 
    Runnable mLongPressed = new Runnable() { 
        public void run() { 
            goneFlag = true;
            //Code for long click
        }   
    };

//onTouch code
@Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {    
        case MotionEvent.ACTION_DOWN:
            handler.postDelayed(mLongPressed, 1000);
            //This is where my code for movement is initialized to get original location.
            break;
        case MotionEvent.ACTION_UP:
            handler.removeCallbacks(mLongPressed);
            if(Math.abs(event.getRawX() - initialTouchX) <= 2 && !goneFlag) {
                //Code for single click
                return false;
            }
            break;
        case MotionEvent.ACTION_MOVE:
            handler.removeCallbacks(mLongPressed);
            //Code for movement here. This may include using a window manager to update the view
            break;
        }
        return true;
    }

내 응용 프로그램에서 사용한 것처럼 작동하는지 확인합니다.


사용자가 누르는 것을 의미하는 것은 클릭을 의미합니까? 클릭은 사용자가 아래로 누른 다음 즉시 손가락을 떼는 것입니다. 따라서 두 개의 onTouch 이벤트를 포함합니다. 초기 터치 또는 릴리스 이후에 발생하는 작업에 대해서는 onTouchEvent 사용을 저장해야합니다.

따라서 클릭 인 경우에는 onClickListener를 사용해야합니다.

귀하의 대답은 유사합니다. onLongClickListener를 사용하십시오.


실제 View 소스에서 영감을 얻은 스 니펫 을 만들어 사용자 지정 지연으로 긴 클릭 / 누름을 안정적으로 감지합니다. 하지만 Kotlin에 있습니다.

val LONG_PRESS_DELAY = 500

val handler = Handler()
var boundaries: Rect? = null

var onTap = Runnable {
    handler.postDelayed(onLongPress, LONG_PRESS_DELAY - ViewConfiguration.getTapTimeout().toLong())
}

var onLongPress = Runnable {

    // Long Press
}

override fun onTouch(view: View, event: MotionEvent): Boolean {
    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            boundaries = Rect(view.left, view.top, view.right, view.bottom)
            handler.postDelayed(onTap, ViewConfiguration.getTapTimeout().toLong())
        }
        MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
            handler.removeCallbacks(onLongPress)
            handler.removeCallbacks(onTap)
        }
        MotionEvent.ACTION_MOVE -> {
            if (!boundaries!!.contains(view.left + event.x.toInt(), view.top + event.y.toInt())) {
                handler.removeCallbacks(onLongPress)
                handler.removeCallbacks(onTap)
            }
        }
    }
    return true
}

MSquare의 솔루션은 특정 픽셀을 보유한 경우에만 작동하지만 최종 사용자가 마우스를 사용하지 않는 한 (그렇지 않고 손가락을 사용하는 경우) 이는 불합리한 기대입니다.

그래서 중간에 MOVE 액션이있는 경우 DOWN 액션과 UP 액션 사이의 거리에 대한 약간의 임계 값을 추가했습니다.

final Handler longPressHandler = new Handler();
Runnable longPressedRunnable = new Runnable() {
    public void run() {
        Log.e(TAG, "Long press detected in long press Handler!");
        isLongPressHandlerActivated = true;
    }
};

private boolean isLongPressHandlerActivated = false;

private boolean isActionMoveEventStored = false;
private float lastActionMoveEventBeforeUpX;
private float lastActionMoveEventBeforeUpY;

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    if(event.getAction() == MotionEvent.ACTION_DOWN) {
        longPressHandler.postDelayed(longPressedRunnable, 1000);
    }
    if(event.getAction() == MotionEvent.ACTION_MOVE || event.getAction() == MotionEvent.ACTION_HOVER_MOVE) {
        if(!isActionMoveEventStored) {
            isActionMoveEventStored = true;
            lastActionMoveEventBeforeUpX = event.getX();
            lastActionMoveEventBeforeUpY = event.getY();
        } else {
            float currentX = event.getX();
            float currentY = event.getY();
            float firstX = lastActionMoveEventBeforeUpX;
            float firstY = lastActionMoveEventBeforeUpY;
            double distance = Math.sqrt(
                    (currentY - firstY) * (currentY - firstY) + ((currentX - firstX) * (currentX - firstX)));
            if(distance > 20) {
                longPressHandler.removeCallbacks(longPressedRunnable);
            }
        }
    }
    if(event.getAction() == MotionEvent.ACTION_UP) {
        isActionMoveEventStored = false;
        longPressHandler.removeCallbacks(longPressedRunnable);
        if(isLongPressHandlerActivated) {
            Log.d(TAG, "Long Press detected; halting propagation of motion event");
            isLongPressHandlerActivated = false;
            return false;
        }
    }
    return super.dispatchTouchEvent(event);
}

아이디어는 Runnable향후 실행을위한 긴 클릭을 생성하는 것이지만 클릭 또는 이동으로 인해이 실행을 취소 할 수 있습니다.

긴 클릭이 언제 소모되었는지, 손가락이 너무 많이 움직여서 언제 취소되었는지도 알아야합니다. 사용자가 각면에 5 픽셀 씩 10 픽셀의 정사각형 영역을 이탈하는지 확인 하기 위해 initialTouchX& initialTouchY사용 합니다.

여기에 위임 내 전체 코드 를 클릭 & LongClick을CellListViewActivity와 함께 OnTouchListener:

    ClickDelegate delegate; 
    boolean goneFlag = false;
    float initialTouchX;
    float initialTouchY;
    final Handler handler = new Handler();
    Runnable mLongPressed = new Runnable() {
        public void run() {
            Log.i("TOUCH_EVENT", "Long press!");
            if (delegate != null) {
                goneFlag = delegate.onItemLongClick(index);
            } else {
                goneFlag = true;
            }
        }
    };

    @OnTouch({R.id.layout})
    public boolean onTouch (View view, MotionEvent motionEvent) {
        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_DOWN:
                handler.postDelayed(mLongPressed, ViewConfiguration.getLongPressTimeout());
                initialTouchX = motionEvent.getRawX();
                initialTouchY = motionEvent.getRawY();
                return true;
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_CANCEL:
                if (Math.abs(motionEvent.getRawX() - initialTouchX) > 5 || Math.abs(motionEvent.getRawY() - initialTouchY) > 5) {
                    handler.removeCallbacks(mLongPressed);
                    return true;
                }
                return false;
            case MotionEvent.ACTION_UP:
                handler.removeCallbacks(mLongPressed);
                if (goneFlag || Math.abs(motionEvent.getRawX() - initialTouchX) > 5 || Math.abs(motionEvent.getRawY() - initialTouchY) > 5) {
                    goneFlag = false;
                    return true;
                }
                break;
        }
        Log.i("TOUCH_EVENT", "Short press!");
        if (delegate != null) {
            if (delegate.onItemClick(index)) {
                return false;
            }
        }
        return false;
    }

ClickDelegateinterface같은 핸들러 클래스에 클릭 이벤트를 보내는Activity

    public interface ClickDelegate {
        boolean onItemClick(int position);
        boolean onItemLongClick(int position);
    }

그리고 필요한 것은 행동을 위임해야 할 경우 Activity부모 또는 부모 에서 구현하는 것입니다 View.

public class MyActivity extends Activity implements ClickDelegate {

    //code...
    //in some place of you code like onCreate, 
    //you need to set the delegate like this:
    SomeArrayAdapter.delegate = this;
    //or:
    SomeViewHolder.delegate = this;
    //or:
    SomeCustomView.delegate = this;

    @Override
    public boolean onItemClick(int position) {
        Object obj = list.get(position);
        if (obj) {
            return true; //if you handle click
        } else {
            return false; //if not, it could be another event
        }
    }

    @Override
    public boolean onItemLongClick(int position) {
        Object obj = list.get(position);
        if (obj) {
            return true; //if you handle long click
        } else {
            return false; //if not, it's a click
        }
    }
}

setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {

                int action = MotionEventCompat.getActionMasked(event);


                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        longClick = false;
                        x1 = event.getX();
                        break;

                    case MotionEvent.ACTION_MOVE:
                        if (event.getEventTime() - event.getDownTime() > 500 && Math.abs(event.getX() - x1) < MIN_DISTANCE) {
                            longClick = true;
                        }
                        break;

                    case MotionEvent.ACTION_UP:
                                if (longClick) {
                                    Toast.makeText(activity, "Long preess", Toast.LENGTH_SHORT).show();
                                } 
                }
                return true;
            }
        });

다음은 MSquare의 버튼을 길게 누르는 것을 감지하기위한 좋은 아이디어를 기반으로 한 접근 방식으로, 추가 기능이 있습니다. 긴 누르기에 대한 응답으로 수행되는 작업 일뿐만 아니라 MotionEvent.ACTION_UP 메시지가 나타날 때까지 작업이 반복됩니다. 받았습니다. 이 경우 길게 누르기 및 짧게 누르기 동작은 동일하지만 다를 수 있습니다.

다른 사람들이보고했듯이 MotionEvent.ACTION_MOVE 메시지에 대한 응답으로 콜백을 제거하면 손가락을 충분히 가만히 둘 수 없기 때문에 콜백이 실행되지 않습니다. 나는 그 메시지를 무시함으로써 그 문제를 해결했다.

private void setIncrementButton() {
    final Button btn = (Button) findViewById(R.id.btn);
    final Runnable repeater = new Runnable() {
        @Override
        public void run() {
            increment();
            final int milliseconds = 100;
            btn.postDelayed(this, milliseconds);
        }
    };
    btn.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent e) {
            if (e.getAction() == MotionEvent.ACTION_DOWN) {
                increment();
                v.postDelayed(repeater, ViewConfiguration.getLongPressTimeout());
            } else if (e.getAction() == MotionEvent.ACTION_UP) {
                v.removeCallbacks(repeater);
            }
            return true;
        }
    });
}

private void increment() {
    Log.v("Long Press Example", "TODO: implement increment operation");   
}

I found one solution and it does not require to define runnable or other things and it's working fine.

    var lastTouchTime: Long = 0

    // ( ViewConfiguration.#.DEFAULT_LONG_PRESS_TIMEOUT =500)
    val longPressTime = 500

    var lastTouchX = 0f
    var lastTouchY = 0f

    view.setOnTouchListener { v, event ->

        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                lastTouchTime = SystemClock.elapsedRealtime()
                lastTouchX = event.x
                lastTouchY = event.y
                return@setOnTouchListener true
            }
            MotionEvent.ACTION_UP -> {
                if (SystemClock.elapsedRealtime() - lastTouchTime > longPressTime
                        && Math.abs(event.x - lastTouchX) < 3
                        && Math.abs(event.y - lastTouchY) < 3) {
                    Log.d(TAG, "Long press")
                }
                return@setOnTouchListener true
            }
            else -> {
                return@setOnTouchListener false
            }
        }

    }

참고URL : https://stackoverflow.com/questions/7919865/detecting-a-long-press-with-android

반응형