Android 에서 onTouch 이벤트 전달 메커니즘 상세 분석
ontach 는 Android 시스템 의 전체 이벤트 메커니즘 의 기초 입 니 다.Android 의 다른 이벤트,예 를 들 어 onClick,onLongClick 등 은 모두 onTach 를 바탕 으로 합 니 다.
onTach 는 손가락 으로 눌 러 서 핸드폰 화면 을 떠 나 는 전체 과정 을 포함 하고 미시적 인 형식 에서 action 로 구체 적 으로 나타난다.down、action_move 와 actionup 등 과정.
onTach 두 가지 주요 정의 형식 은 다음 과 같다.
1.사용자 정의 컨트롤 에서 흔히 볼 수 있 는 재 작성
onTouchEvent(MotionEvent ev)
방법 이 있 습 니 다.개발 중 에 재 작성 한 onTouchEvent 방법 을 자주 볼 수 있다 면,그리고 그 중 에는 서로 다른 미시적 표현(actiondown、action_move 와 actionup 등)상응하는 판단 을 내 려 논 리 를 집행 하고 서로 다른 불 값 을 되 돌려 줄 수 있다.
2.코드 에서 기 존 컨트롤 에 setOnTouchListener 모니터 를 직접 설정 합 니 다.모니터 의 onTouch 방법 을 다시 씁 니 다.onTouch 리 셋 함수 에는 view 와 MotionEvent 가 있 습 니 다.
onTouch 이벤트 전달 메커니즘
일반적으로 우리 가 사용 하 는 UI 컨트롤 은 공 통 된 부류 인 View 를 계승 하 는 것 으로 알려 져 있다.그래서 View 라 는 종 류 는 onTouch 사건 의 관련 처 리 를 관리 해 야 합 니 다.그러면 View 에서 Touch 와 관련 된 방법 을 찾 아 보 자.그 중 하 나 는 우리 의 주 의 를 끌 기 쉽다.
dispatchTouchEvent(MotionEvent event)
방법 명 에 따 르 면 터치 사건 을 배포 하 는 것 을 책임 져 야 한 다 는 뜻 으로 소스 코드 를 제시 했다.
/**
* Pass the touch screen motion event down to the target view, or this
* view if it is the target.
*
* @param event The motion event to be dispatched.
* @return True if the event was handled by the view, false otherwise.
*/
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
if (event.isTargetAccessibilityFocus()) {
// We don't have focus or no virtual descendant has it, do not handle the event.
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
// We have focus and got the event, then use normal event dispatch.
event.setTargetAccessibilityFocus(false);
}
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Defensive cleanup for new gesture
stopNestedScroll();
}
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
// Clean up after nested scrolls if this is the end of a gesture;
// also cancel it if we tried an ACTION_DOWN but we didn't want the rest
// of the gesture.
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}
return result;
}
소스 코드 가 좀 길 지만,우 리 는 모든 줄 을 다 볼 필요 가 없다.먼저 dispatchTouchEvent 의 반환 값 이 boolean 형식 이라는 것 을 알 게 되 었 습 니 다.주석 에 있 는 설명:@return True if the event was handled by the view, false otherwise.
은 이 터치 이벤트 가 이 View 에 소비 되면 true 로 돌아 가 고 그렇지 않 으 면 false 로 돌아 가 는 것 입 니 다.방법 에 서 는 이 이벤트 가 초점 을 맞 췄 는 지 여 부 를 먼저 판단 하고 초점 을 받 지 못 하면 false 로 되 돌아 갑 니 다.그 다음 에 우 리 는if (li != null && li.mOnTouchListener != null&& (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event))
이 부분 에 시선 을 돌 렸 습 니 다.여기 에는 li 라 는 부분 변수 가 있 는데 Listener Info 류 에 속 하고 mListener Info 부 를 통 해 얻 을 수 있 습 니 다.Listener Info 는 포장 류 일 뿐 이 고 그 안에 대량의 모니터 가 밀봉 되 어 있다.View 클래스 에서 mListener Info 를 찾 으 면 다음 코드 를 볼 수 있 습 니 다.
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
따라서 우 리 는 mListener Info 가 비어 있 지 않다 는 것 을 알 수 있 습 니 다.그래서 li 도 비어 있 지 않 습 니 다.첫 번 째 판단 은 true 입 니 다.그리고 li.mOnTouch Listener 를 보 았 습 니 다.앞에서 Listener Info 는 모니터 의 패 키 징 류 라 고 말 했 기 때문에 우 리 는 mOnTouch Listener 를 추적 합 니 다.
/**
* Register a callback to be invoked when a touch event is sent to this view.
* @param l the touch listener to attach to this view
*/
public void setOnTouchListener(OnTouchListener l) {
getListenerInfo().mOnTouchListener = l;
}
바로 위의 방법 을 통 해 mOnTouchListener 를 설정 한 것 입 니 다.위의 방법 은 모두 가 잘 알 고 있 을 것 이 라 고 생각 합 니 다.바로 우리 가 평소에 자주 사용 하 는 xxx.setOnTouchListener 입 니 다.자,이 를 통 해 OnTouchListener 를 설정 하면 두 번 째 판단 도 true 이 고 세 번 째 판단 은 이 View 가 enable 인지,기본 값 은 enable 인지 알 수 있 기 때문에 똑 같이 true 입 니 다.마지막 이 남 았 습 니 다.li.mOnTouchListener.onTouch(this, event)
두 번 째 판단 에서 모니터 의onTouch()
방법 을 바 꾼 것 이 분명 합 니 다.onTouch()
방법 이 true 로 돌아 가면 위의 네 가지 판단 이 모두 true 이 고dispatchTouchEvent()
방법 은 true 로 돌아 가 며if (!result && onTouchEvent(event))
이 판단 을 집행 하지 않 습 니 다.이 판단 에서 우 리 는 익숙 한 방법 을 보 았 다.onTouchEvent()
그래서 onTouchEvent 를 실행 하려 면 위의 네 가지 판단 에서 최소한 false 가 있어 야 합 니 다.그러면 우리 가
onTouch()
방법 에서 false 로 돌 아 왔 다 고 가정 하면 onTouch Event 를 순조롭게 실 행 했 습 니 다.그러면 onTouch Event 의 소스 코드 를 보 세 요.
/**
* Implement this method to handle touch screen motion events.
* <p>
* If this method is used to detect click actions, it is recommended that
* the actions be performed by implementing and calling
* {@link #performClick()}. This will ensure consistent system behavior,
* including:
* <ul>
* <li>obeying click sound preferences
* <li>dispatching OnClickListener calls
* <li>handling {@link AccessibilityNodeInfo#ACTION_CLICK ACTION_CLICK} when
* accessibility features are enabled
* </ul>
*
* @param event The motion event.
* @return True if the event was handled, false otherwise.
*/
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return (((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure
// the user sees it.
setPressed(true, x, y);
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_DOWN:
mHasPerformedLongPress = false;
if (performButtonActionOnTouchDown(event)) {
break;
}
// Walk up the hierarchy to determine if we're inside a scrolling container.
boolean isInScrollingContainer = isInScrollingContainer();
// For views inside a scrolling container, delay the pressed feedback for
// a short period in case this is a scroll.
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
checkForLongClick(0);
}
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_MOVE:
drawableHotspotChanged(x, y);
// Be lenient about moving outside of buttons
if (!pointInView(x, y, mTouchSlop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback();
setPressed(false);
}
}
break;
}
return true;
}
return false;
}
이 소스 코드 는 dispatch Touch Event 보다 더 길 지만 똑 같이 우리 가 중점 을 두 고 보 자.if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE)
이 말 을 보면 이 view 가 클릭 가능 한 지 여 부 를 판단 하 는 것 임 을 알 수 있 습 니 다.클릭 할 수 있 으 면 계속 실행 합 니 다.그렇지 않 으 면 false 로 돌아 갑 니 다.if 에서 switch 로 어떤 터치 사건 인지 판단 할 수 있 지만 마지막 에는 true 로 돌아 갑 니 다.그리고 주의해 야 할 것 은:ACTION 에서UP 에서 실행
performClick()
방법:
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
위의li.mOnClickListener.onClick(this);
를 볼 수 있 습 니 다.맞아요.우 리 는 또 새로운 발견 을 한 것 같 습 니 다.위의 경험 에 따 르 면 이 코드 는 우리 가 설정 한 클릭 이벤트 모니터 를 되 돌려 줍 니 다.우리 가 평소에 쓰 는 거 야xxx.setOnClickListener(listener);
/**
* Register a callback to be invoked when this view is clicked. If this view is not
* clickable, it becomes clickable.
*
* @param l The callback that will run
*
* @see #setClickable(boolean)
*/
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
위의 방법 설정 이 바로 mListener Info 의 클릭 모니터 로 위의 추측 을 검증 한 것 을 볼 수 있 습 니 다.여기까지 온 터치 사건 의 전달 메커니즘 은 기본적으로 분석 이 끝 났 고 일 단락 된 셈 이다.자,이제 시작 문 제 를 해결 할 수 있 습 니 다.그리고 다시 한 번 요약 하 겠 습 니 다.dispatchTouchEvent 에서 OnTouchListener 가 설정 되 어 있 고 View 가 enable 이 라면 먼저 OnTouchListener 중의
onTouch(View v, MotionEvent event)
가 실 행 됩 니 다.onTouch 가 true 로 돌아 가면 dispatchTouch 이벤트 가 더 이상 실행 되 지 않 고 true 로 돌아 갑 니 다.그렇지 않 으 면 onTouchEvent 를 실행 합 니 다.onTouchEvent 에서 View 가 클릭 할 수 있 으 면 true 로 돌아 갑 니 다.그렇지 않 으 면 false 입 니 다.그리고 onTouchEvent 에서 View 가 클릭 가능 하고 현재 터치 이벤트 가 ACTION 이면UP,실행performClick()
,OnClickListener 의 onClick 방법 을 되 돌려 줍 니 다.다음은 내 가 그린 스케치 이다.
또 한 가지 주의 할 점 은 현재 사건 이 ACTION 이 라면DOWN,dispatchTouchEvent 가 true 로 돌아 와 야 다음 ACTION 을 받 을 수 있 습 니 다.MOVE,ACTION_UP 사건,즉 사건 이 소 비 돼 야 다음 사건 을 받 아들 일 수 있다 는 것 이다.
총결산
이상 은 안 드 로 이 드 에서 onTouch 이벤트 전달 체제 에 대한 상세 한 분석 입 니 다.안 드 로 이 드 개발 자 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 십시오.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.