view 클릭 이벤트 처리 과정
view 프로세스
My TextView 계승 TextView를 사용자 정의하고 dispatch Touch Event on Touch Event 인쇄 로그
코드는 다음과 같습니다.
public class MyTextView extends TextView {
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
// Log.e("myTextView", "myTextView dispatchTouchEvent");
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e("myTextView", "myTextView dispatchTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e("myTextView", "myTextView dispatchTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e("myTextView", "myTextView dispatchTouchEvent ACTION_UP");
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e("myTextView", "myTextView onTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e("myTextView", "myTextView onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e("myTextView", "myTextView onTouchEvent ACTION_UP");
break;
}
return super.onTouchEvent(event);
}
}
마지막으로 Activity 코드는 다음과 같습니다.
public class MainActivity extends AppCompatActivity {
private MyTextView myTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myTextView = (MyTextView) findViewById(R.id.tv_show);
myTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.e("activity","activity onclick");
}
});
myTextView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e("activity","activity onTouch ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e("activity","activity onTouch ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e("activity","activity onTouch ACTION_UP");
break;
}
return false;
}
});
}
}
주의 사항
뷰 그룹이 포함되어 있지 않으므로 절차가 간단합니다.view는 단독 요소로 하위 요소가 없기 때문에 이벤트를 아래로 전달할 수 없기 때문에 이벤트를 스스로 처리할 수 있습니다.
MainActivity에서 우리는 My TextView에 On Touch Listener, set On Click Listener라는 감청을 설정해 주었다. 자, View 사건과 관련된 곳은 보통 이 세 군데다. 하나는 온터치 이벤트, 하나는 디스패치 터치 이벤트, 하나는 set On Touch Listener와 set On Click Listener이다.
다음은 를 실행하고 버튼을 클릭하여 로그 출력을 확인합니다.
내가 일부러 눌렀을 때 문질러 놓았는데, 그렇지 않았다면 MOVE를 건드리지 않았을 것이다. 손이 떨리면 MOVE의 일지를 한 무더기 출력할 수 있었다
25372-25372/com.lu.viewdistributionevent E/myTextView: myTextView dispatchTouchEvent ACTION_DOWN
25372-25372/com.lu.viewdistributionevent E/activity: activity onTouch ACTION_DOWN
25372-25372/com.lu.viewdistributionevent E/myTextView: myTextView onTouchEvent ACTION_DOWN
25372-25372/com.lu.viewdistributionevent E/myTextView: myTextView dispatchTouchEvent ACTION_UP
25372-25372/com.lu.viewdistributionevent E/activity: activity onTouch ACTION_UP
25372-25372/com.lu.viewdistributionevent E/myTextView: myTextView onTouchEvent ACTION_UP
25372-25372/com.lu.viewdistributionevent E/activity: activity onclick
25372-25372/com.lu.viewdistributionevent E/myTextView: myTextView dispatchTouchEvent ACTION_DOWN
25372-25372/com.lu.viewdistributionevent E/activity: activity onTouch ACTION_DOWN
25372-25372/com.lu.viewdistributionevent E/myTextView: myTextView onTouchEvent ACTION_DOWN
25372-25372/com.lu.viewdistributionevent E/myTextView: myTextView dispatchTouchEvent ACTION_MOVE
25372-25372/com.lu.viewdistributionevent E/activity: activity onTouch ACTION_MOVE
25372-25372/com.lu.viewdistributionevent E/myTextView: myTextView onTouchEvent ACTION_MOVE
25372-25372/com.lu.viewdistributionevent E/myTextView: myTextView dispatchTouchEvent ACTION_UP
25372-25372/com.lu.viewdistributionevent E/activity: activity onTouch ACTION_UP
25372-25372/com.lu.viewdistributionevent E/myTextView: myTextView onTouchEvent ACTION_UP
25372-25372/com.lu.viewdistributionevent E/activity: activity onclick
보시다시피 DOWN, MOVE, UP은 다음 순서대로 수행됩니다.
다음은 로그의 발자취를 따라 원본 탐색을 시작합니다
/**
* 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)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;// listener, clickListener,touchListener
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED// view
&& li.mOnTouchListener.onTouch(this, event)) {// TouchListener.onTouch ,
result = true;
}
if (!result && onTouchEvent(event)) {// onTouchEvent, onClick/onLongClick
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;
}
Touch Lister 중의 onTouch 방법은true로 되돌아갑니다 제가 의도적으로 눌렀을 때 문질렀습니다. 그렇지 않으면 MOVE를 터치하지 않습니다. 손이 떨리면 MOVE 로그를 출력할 수 있습니다.
17776-17776/com.lu.viewdistributionevent E/myTextView: myTextView dispatchTouchEvent ACTION_DOWN
17776-17776/com.lu.viewdistributionevent E/activity: activity onTouch ACTION_DOWN
17776-17776/com.lu.viewdistributionevent E/myTextView: myTextView dispatchTouchEvent ACTION_MOVE
17776-17776/com.lu.viewdistributionevent E/activity: activity onTouch ACTION_MOVE
17776-17776/com.lu.viewdistributionevent E/myTextView: myTextView dispatchTouchEvent ACTION_MOVE
17776-17776/com.lu.viewdistributionevent E/activity: activity onTouch ACTION_MOVE
17776-17776/com.lu.viewdistributionevent E/myTextView: myTextView dispatchTouchEvent ACTION_UP
17776-17776/com.lu.viewdistributionevent E/activity: activity onTouch ACTION_UP
원본에서 알 수 있듯이 먼저 OnTouch Listener를 설정했는지 판단하고 OnTouch Lister 중의 onTouch 방법이true로 되돌아오면 onTouch Event가 호출되지 않는다는 결론을 내렸다. OnTouch Listener의 우선순위가 onTouch Event보다 높다는 결론을 내렸다.이렇게 하면 외부에서 클릭 사건을 처리하는 데 편리하다는 장점이 있다.동시에 set On Click Listener의 onClick도 호출되지 않은 것을 발견할 수 있습니다.왜 On Touch Lister의 On Touch 방법을 설정해서true로 되돌려 주었는지, set On Click Listener의 On Click도 호출되지 않았습니다.
/**
* Implement this method to handle touch screen motion events.
*
* 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:
*
* - obeying click sound preferences
*
- dispatching OnClickListener calls
*
- handling {@link AccessibilityNodeInfo#ACTION_CLICK ACTION_CLICK} when
* accessibility features are enabled
*
*
* @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();
// View , 。
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, x, y);
}
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;
}
그래서 결론을 얻었다.
View의 LONGCLICKABLE 속성은 기본값false이고 CLICKABLE 속성이false인지 여부는 구체적인view와 관련이 있습니다.정확히 말하면 클릭 가능한view의 CLICKABLE는true이고 클릭 가능한view의 CLICKABLE는false이다.예를 들어 Button은 클릭할 수 있고 TextView는 클릭할 수 없습니다.setClickable 및 setLongClickable을 사용하여 View의 CLICKABLE와 LONG 를 각각 변경할 수 있습니다.CLICKABLE의 속성입니다.또한 setOnClickListener는 자동으로 CLICKABLE를true로 설정하고 setOnLongClickListener는 자동으로 LONGCLICKABLE를 true로 설정합니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.