Touch event monitoring at different levels of Android
In APP development, you often encounter actions related to gesture processing, such as swiping right to return to the previous page. Regarding the handling of touch events, we can roughly handle them at different levels.
Activity layer: It can be regarded as the top level of touch event acquisition
ViewGroup layer: The ViewGroup layer can automatically control whether subViews can get touch events
View layer: You can decide whether you really consume touch events, and if you don't consume, you can throw them to the upper layer ViewGroup
Activity-level gesture monitoring: (swipe right to return to the upper interface)
Use scenarios for Activity-level gesture monitoring: Generally used when there are not too many gestures in the current page that need to be processed, at most click events exist. For the requirement of sliding right to return to the upper layer interface, it can be defined in a BaseActivity. If the sub-Activity needs to be implemented, it can be turned on through a certain switch.
Notes:
1. Activity layer, use dispatch to grab all events.
2. For sliding, set a distance threshold mDistanceGat to mark whether the gesture is valid and pay attention to the processing of sliding backwards.
3. If there is a click Item at the bottom, in order to prevent discoloration during sliding, you can block the touch event in a timely manner: manually construct the Cancle event to be issued actively, which is to be compatible with the most basic click effect. However, before satisfying the gesture of clicking, the Move event must be issued normally. The specific implementation is as follows:
@Override public boolean dispatchTouchEvent(MotionEvent event) { case MotionEvent.ACTION_MOVE: if ((() - down_X) > 10 && flagDirection == ) { MotionEvent e = ((), (), MotionEvent.ACTION_CANCEL, (), (), 0); (e); } else { (event);//Do not meet the conditions and issuance is normal }
4. To prevent the gesture from sliding back, it is best to use GestureDectetor to judge. If there is sliding back, the gesture will be invalid. The usage method is as follows:
mDetector = new GestureDetector(this, new () { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { if (!slideReturnFlag && distanceX > 5) { slideReturnFlag = true; }}
5. How to deal with Up events: Whether dispatch is distributed downward. The specific approach is to determine whether the gesture is effective or not. If the gesture is invalid, then Up will definitely need to be distributed downwards. If valid, it will be done according to subsequent operations, because sometimes it is necessary to prevent the subView from getting unnecessary click events. The specific implementation is as follows
@Override public boolean dispatchTouchEvent(MotionEvent event) { case MotionEvent.ACTION_UP: if (mGestureListener != null && !slideReturnFlag && flagDirection == ) { if (stateMotion == ) { (); } } else { (event); //Invalid gesture } flagDirection = ; stateMotion = ; slideReturnFlag=false; break;
6. It is best to record down_X and down_Y in disPatch, for subsequent processing and judgment, because dispatch can best ensure that you get the event. At the same time, the dispatch of the Dispatch event must be ensured.
Second: Parent container-level gesture monitoring
Note: Container-level listening should at least make the current container force the focus of the gesture. As for how to get the focus, you can write the onTouch event yourself and make it true. However, we put the judgment processing in the dispatch to ensure that the event is fully acquired. Because, if the underlying consumption of events, onTouch cannot obtain events intact, but we have enough ability to ensure that dispatch obtains complete events. Whether it is consumed at this level onTouch or at the bottom level, dispatch is used to avoid missing. For gesture containers, it is best to use padding instead of Magin. Why? Because Margin is not inside the container.
1. Use scenarios for parent container monitoring
- In the container, does there exist interactive events in the subview and whether there is sliding
- Is there a possibility of intercepting events in the upper container, such as SrollView
2. Realize
There is no interactive event for the subview:
This type of container can be implemented using Dispatch, but it requires forced focus acquisition and timely release focus. The specific implementation is as follows:
How to ensure that this layer will definitely receive subsequent events in Down. The Down event of dispatch can return True.
How to ensure that this layer is not blocked by chance, just use getParent().requestDisallowInterceptTouchEvent(true). Of course, if there is a forced acquisition, it must be released in time. When the gesture is determined to be invalid, it must be released. The specific implementation is as follows:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { getParent().requestDisallowInterceptTouchEvent(true);</strong></span> (ev); switch (()) { case MotionEvent.ACTION_DOWN: down_X = (); down_Y = (); slideReturnFlag = false; break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_MOVE: if ((down_X - ()) < (down_Y - ()) && (() - down_Y) > mDistanceGate / 2) { getParent().requestDisallowInterceptTouchEvent(false);</span></strong> } default: break; } return (ev); }
There are interactive events for subView: There are interactive events for subView, so it must be used in conjunction with dispatch and onTouch. In order to judge the effectiveness of the gesture, dispatch is necessary to force focus to obtain, so it is not important to force focus to obtain focus at the bottom layer. If there is no consumption Down here, it means that the underlying View has been consumed. At the same time, it must be compatible with the release of forced focus acquisition by invalid gestures to prevent uploading and scrolling Views. The specific implementation is as follows:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { (ev); switch (()) { case MotionEvent.ACTION_DOWN: down_X = (); down_Y = (); slideReturnFlag = false; break; default: break; } return (ev); }
The response event is handled in onTouch, mainly to prevent the upper layer from processing after the underlying layer is acquired.
// ACTION_CANCEL nested such as other scrollViews may be blocked@Override public boolean onTouchEvent(MotionEvent ev) { switch (()) { case MotionEvent.ACTION_DOWN:
// ACTION_CANCEL nested such as other scrollViews may be blocked @Override public boolean onTouchEvent(MotionEvent ev) { switch (()) { case MotionEvent.ACTION_DOWN: getParent().requestDisallowInterceptTouchEvent(true); return true; case MotionEvent.ACTION_CANCEL: return true; case MotionEvent.ACTION_UP: if ((down_X - ()) > (down_Y - ()) && !slideReturnFlag && () - down_X > mDistanceGate) { // Return to the previous Activity, or it may be the previous Fragment FragmentActivity mContext = null; if (getContext() instanceof FragmentActivity) { mContext = (FragmentActivity)getContext(); FragmentManager fm = (); if (() > 0) { (); } else { (); } } } return true; case MotionEvent.ACTION_MOVE: if ((down_X - ()) < (down_Y - ()) && (() - down_Y) > mDistanceGate / 2) { getParent().requestDisallowInterceptTouchEvent(false); } return true; default: break; } return (ev); }
3. Intercepting the parent container gesture. Sometimes, the child View has a click event, and clicks change color. After giving a certain fault tolerance space, force intercept the event. dispatch returns true to ensure that the event is uploaded, so don't worry
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (() == MotionEvent.ACTION_MOVE && (down_X - ()) > 20) return true; return (ev); }
Fourth: In the edge state of HorizontalScrollView, the monitoring of sliding gestures is implemented as follows, mainly gesture judgment at the edge.
@Override public boolean dispatchTouchEvent(MotionEvent ev) { getParent().requestDisallowInterceptTouchEvent(true); (ev); switch (()) { case MotionEvent.ACTION_DOWN: slideReturnFlag = false; down_X = (); down_Y = (); oldScrollX = getScrollX(); break; case MotionEvent.ACTION_UP: if ((down_X - ()) > (down_Y - ()) && () - down_X > mDistanceGate && !slideReturnFlag && oldScrollX == 0) { // Return to the previous Activity, or it may be the previous Fragment FragmentActivity mContext = null; if (getContext() instanceof FragmentActivity) { mContext = (FragmentActivity)getContext(); FragmentManager fm = (); if (() > 0) { (); } else { (); } } } break; case MotionEvent.ACTION_MOVE: if ((down_X - ()) < (down_Y - ()) && (() - down_Y) > mDistanceGate / 2) { getParent().requestDisallowInterceptTouchEvent(false); } default: break; } return (ev); }
Fifth: Prevent premature blocking events of ScrollView from vertical scrolling: just rewrite the intercept function:
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { if ((() - down_Y) < getResources().getDimensionPixelSize(.slide_gesture_vertical_gate)) { (ev); return false; } return (ev); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (()) { case MotionEvent.ACTION_DOWN: down_X = (); down_Y = (); break;
Sixth: Viewpager first page swipe gesture;
1. Prevent premature hindrance
@Override public boolean dispatchTouchEvent(MotionEvent ev) { getParent().requestDisallowInterceptTouchEvent(true); (ev); switch (()) { case MotionEvent.ACTION_DOWN: down_X = (); down_Y=(); slideReturnFlag=false; break; case MotionEvent.ACTION_MOVE: if ((down_X - ()) < (down_Y - ()) && (() - down_Y) > mDistanceGate / 2) { getParent().requestDisallowInterceptTouchEvent(false); } break; default: break; } return (ev); }
2. Prevent sliding backwards, etc.
/* * To handle touch events, we need to determine whether the ViewPager is not slideable. */ @Override public boolean onTouchEvent(MotionEvent arg0) { // Prevent jumps boolean ret = (arg0); switch (()) { case MotionEvent.ACTION_DOWN: ("lishang", "down"); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: ("lishang", "up"); if (slideDirection == ) { if (slideReturnFlag || getCurrentItem() != 0 || () - down_X < mDistanceGate || mPercent > 0.01f) break; } else if (slideDirection == ) { if (getAdapter() != null) { if (slideReturnFlag||getCurrentItem() != getAdapter().getCount() - 1 || down_X - () < mDistanceGate || mPercent > 0.01f) break; } } else {
seventh:getParent().requestDisallowInterceptTouchEvent
The function of this function can only ensure that the event is not blocked. However, if the dispatch of this layer returns false when down, the event processing will be invalid, even if the focus is forced to be obtained
Thank you for reading, I hope it can help you. Thank you for your support for this site!