Range of click events captured by the Android control.
The position of clicking an event in the View Tween animation process does not change because the layout position does not actually change during the animation process, therefore, we once thought that a View's Click Event (not just a click event, including all touch events) is triggered by the left, top, right, bottom. This is not exactly the case today. It is because the code is not carefully read at ordinary times, which leads to incomplete understanding of the problem.
Here we will record the process from Problem Discovery to problem handling.
You can customize a ViewGroup with two linear layout. The left-side LinearLayout overwrites the full screen, and the right-side LinearLayout is hidden outside the screen. Then observe the position information of the Button and the second linear layout during the display of the second LinearLayout during the Sliding Process:
It can be seen that the position of the second linear distribution is not changed during the process of sliding to the left. Here, the position is indicated by getLeft (), getTop (), getRight (), location obtained by getBottom (), which is determined by layout.
Since the position has not changed, the second linear layout Click Event and the button click event are also responded at this time, which means that the location of the captured click event is not exactly in layout. Because I didn't stretch my hand out of the screen and click...
Let's look back at the ViewGroup # dispatchTouchEvent method when distributing touch events:
for (int i = count - 1; i >= 0; i--) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { child.getHitRect(frame); if (frame.contains(scrolledXInt, scrolledYInt)) { // offset the event to the view's coordinate system final float xc = scrolledXFloat - child.mLeft; final float yc = scrolledYFloat - child.mTop; ev.setLocation(xc, yc); child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; if (child.dispatchTouchEvent(ev)) { // Event handled, we have a target now. mMotionTarget = child; return true; } }}
The frame. contains (scrolledXInt, scrolledYInt) function is used to determine whether the point (scrolledXInt, scrolledYInt) is in the frame rectangle. This rectangular frame is obtained by child. getHitRect (frame:
public void getHitRect(Rect outRect) { outRect.set(mLeft, mTop, mRight, mBottom);}
Obviously, this rectangle is determined by the Layout parameter of the Child View. However, the parameters scrolledXInt and scrolledYInt are not the locations where our fingers CLICK:
final int action = ev.getAction();final float xf = ev.getX();final float yf = ev.getY();final float scrolledXFloat = xf + mScrollX;final float scrolledYFloat = yf + mScrollY;……final int scrolledXInt = (int) scrolledXFloat;final int scrolledYInt = (int) scrolledYFloat;
It can be seen that when judging whether this point is included in the sub-View, this point is not the coordinate clicked by the finger, but the coordinate clicked by the finger plus the mScrollX and mScrollY, then, you can determine whether the Sub-View is in the range of the sub-View.
In the process of moving to the left, although the location of the second linear layout is not changed, the parameter location of layout is: mLeft: 720, mTop: 0, mRight: 1440, mBottom: 1134.
However, the mScrollX of his parent View has changed, and it slides to the left to slide the mScrollX to be greater than 0. This is the second linear layout clicked by hand. The position clicked by the hand is added with the value of mScrollX, this will fall into the range of layout in the second linear layout.
Test code:
Custom MyViewGroup:
Public class MyViewGroup extends ViewGroup {public static final String TAG = "MyViewGroup"; private int childCount; private GestureDetector detector; private Button btn; private LinearLayout ll2; public MyViewGroup (Context context, attributeSet attrs, int defStyle) {super (context, attrs, defStyle); init (context);} public MyViewGroup (Context context, AttributeSet attrs) {super (context, attrs ); init (context);} public MyViewGroup (Context context) {super (context); init (context);} private void init (final Context context) {detector = new GestureDetector (context, new MyOnGestureListener (); LinearLayout ll1 = new LinearLayout (context); ll1.setBackgroundColor (Color. BLUE); ll2 = new LinearLayout (context); ll2.setBackgroundColor (Color. RED); btn = new Button (context); btn. setText ("click"); ll2.addView (btn); addView (ll1); addView (ll2); setOnTouchListener (new MyTouchEvent (); ll2.setOnClickListener (new OnClickListener () {@ Overridepublic void onClick (View v) {Toast. makeText (context, "click linear layout 2", 0 ). show () ;}}); btn. setOnClickListener (new OnClickListener () {@ Overridepublic void onClick (View v) {Toast. makeText (context, "click Button", 0 ). show () ;}}) ;}@ Overrideprotected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {super. onMeasure (widthMeasureSpec, heightMeasureSpec); childCount = getChildCount (); for (int I = 0; I <childCount; I ++) {View child = getChildAt (I); child. measure (widthMeasureSpec, heightMeasureSpec) ;}@overrideprotected void onLayout (boolean changed, int l, int t, int r, int B) {for (int I = 0; I <childCount; I ++) {View child = getChildAt (I); child. layout (0 + I * getWidth (), 0, (I + 1) * getWidth (), getHeight () ;}} private class MyTouchEvent implements View. onTouchListener {@ Overridepublic boolean onTouch (View v, MotionEvent event) {detector. onTouchEvent (event); return true;} private class extends SimpleOnGestureListener {@ Overridepublic boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {scrollBy (int) distanceX, 0); if (getScrollX () % 10 = 0) {Log. I (TAG, "Button top left bottom position:" + btn. getLeft () + "/" + btn. getTop () + "/" + btn. getRight () + "/" + btn. getBottom (); Log. I (TAG, "top left and bottom right of linear layout 2:" + ll2.getLeft () + "/" + ll2.getTop () + "/" + ll2.getRight () + "/" + ll2.getBottom (); Log. I (TAG, "MyViewGroup's mScrollX:" + getScrollX ();} return super. onScroll (e1, e2, distanceX, distanceY );}}}
Then in the Activity:
public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(new MyViewGroup(this));}}