{"id":30863,"date":"2023-05-20T12:23:10","date_gmt":"2023-05-20T04:23:10","guid":{"rendered":"https:\/\/docs.pingcode.com\/?p=30863"},"modified":"2023-07-31T01:51:38","modified_gmt":"2023-07-30T17:51:38","slug":"action_cancel%e5%88%b0%e5%ba%95%e4%bd%95%e6%97%b6%e8%a7%a6%e5%8f%91%ef%bc%8c%e6%bb%91%e5%87%ba%e5%ad%90view%e8%8c%83%e5%9b%b4%e4%bc%9a%e5%8f%91%e7%94%9f%e4%bb%80%e4%b9%88","status":"publish","type":"post","link":"https:\/\/docs.pingcode.com\/ask\/30863.html","title":{"rendered":"ACTION_CANCEL\u5230\u5e95\u4f55\u65f6\u89e6\u53d1\uff0c\u6ed1\u51fa\u5b50View\u8303\u56f4\u4f1a\u53d1\u751f\u4ec0\u4e48"},"content":{"rendered":"<blockquote class=\"wp-block-quote\">\n<p>ACTION_CANCEL\u5728\u8fd9\u4e9b\u65f6\u5019\u4f1a\u89e6\u53d1\uff1a1\u3001\u7236view\u62e6\u622a\u4e8b\u4ef6\uff1b2\u3001ACTION_DOWN\u521d\u59cb\u5316\u64cd\u4f5c\uff1b3\u3001\u5728\u5b50View\u5904\u7406\u4e8b\u4ef6\u7684\u8fc7\u7a0b\u4e2d\u88ab\u4ece\u7236View\u4e2d\u79fb\u9664\u65f6\u7b49\u3002\u7236view\u62e6\u622a\u4e8b\u4ef6\u662f\u6307\u5728\u5b50View\u5904\u7406\u4e8b\u4ef6\u7684\u8fc7\u7a0b\u4e2d\uff0c\u7236View\u5bf9\u4e8b\u4ef6\u62e6\u622a\u3002<\/p>\n<\/blockquote>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img fetchpriority=\"high\" alt=\"\" class=\"wp-image-30864\" decoding=\"async\" height=\"576\" sizes=\"(max-width: 1024px) 100vw, 1024px\" src=\"https:\/\/cdn-docs.pingcode.com\/wp-content\/uploads\/2023\/05\/\u6570\u636e\u7ed3\u6784\u9002\u7528\u4e8e\u89e3\u51b3\u4ec0\u4e48\u95ee\u9898-26-1024x576.png?x-oss-process=image\/auto-orient,1\/format,webp\" srcset=\"https:\/\/cdn-docs.pingcode.com\/wp-content\/uploads\/2023\/05\/\u6570\u636e\u7ed3\u6784\u9002\u7528\u4e8e\u89e3\u51b3\u4ec0\u4e48\u95ee\u9898-26-1024x576.png?x-oss-process=image\/auto-orient,1\/format,webp 1024w, https:\/\/cdn-docs.pingcode.com\/wp-content\/uploads\/2023\/05\/\u6570\u636e\u7ed3\u6784\u9002\u7528\u4e8e\u89e3\u51b3\u4ec0\u4e48\u95ee\u9898-26-300x169.png 300w, https:\/\/cdn-docs.pingcode.com\/wp-content\/uploads\/2023\/05\/\u6570\u636e\u7ed3\u6784\u9002\u7528\u4e8e\u89e3\u51b3\u4ec0\u4e48\u95ee\u9898-26-768x432.png 768w, https:\/\/cdn-docs.pingcode.com\/wp-content\/uploads\/2023\/05\/\u6570\u636e\u7ed3\u6784\u9002\u7528\u4e8e\u89e3\u51b3\u4ec0\u4e48\u95ee\u9898-26-270x152.png 270w, https:\/\/cdn-docs.pingcode.com\/wp-content\/uploads\/2023\/05\/\u6570\u636e\u7ed3\u6784\u9002\u7528\u4e8e\u89e3\u51b3\u4ec0\u4e48\u95ee\u9898-26-1536x865.png 1536w, https:\/\/cdn-docs.pingcode.com\/wp-content\/uploads\/2023\/05\/\u6570\u636e\u7ed3\u6784\u9002\u7528\u4e8e\u89e3\u51b3\u4ec0\u4e48\u95ee\u9898-26.png 1993w\" width=\"1024\"\/><\/figure>\n<\/div>\n<h2 class=\"wp-block-heading\">\u4e00\u3001ACTION_CANCEL\u5728\u8fd9\u4e9b\u65f6\u5019\u4f1a\u89e6\u53d1<\/h2>\n<h3 class=\"wp-block-heading\">1\u3001\u7236view\u62e6\u622a\u4e8b\u4ef6<\/h3>\n<p>\u9996\u5148\u8981\u4e86\u89e3ViewGroup\u4ec0\u4e48\u60c5\u51b5\u4e0b\u4f1a\u62e6\u622a\u4e8b\u4ef6\uff0c\u8bf7\u770b\u4e0b\u9762\u4e00\u6bb5\u4ee3\u7801\uff1a<\/p>\n<pre class=\"wp-block-code\"><code>@Override\npublic boolean dispatchTouchEvent(MotionEvent ev) \n\t...\n\n    boolean handled = false;\n    if (onFilterTouchEventForSecurity(ev)) \n        final int action = ev.getAction();\n        final int actionMasked = action &amp; MotionEvent.ACTION_MASK;\n\t\t...\n        \/\/ Check for interception.\n        final boolean intercepted;\n        \/\/ \u5224\u65ad\u6761\u4ef6\u4e00\n        if (actionMasked == MotionEvent.ACTION_DOWN\n                || mFirstTouchTarget != null) \n            final boolean disallowIntercept = (mGroupFlags &amp; FLAG_DISALLOW_INTERCEPT) != 0;\n            \/\/ \u5224\u65ad\u6761\u4ef6\u4e8c\n            if (!disallowIntercept) \n                intercepted = onInterceptTouchEvent(ev);\n                ev.setAction(action); \/\/ restore action in case it was changed\n             else \n                intercepted = false;\n            \n         else \n            \/\/ There are no touch targets and this action is not an initial down\n            \/\/ so this view group continues to intercept touches.\n            intercepted = true;\n        \n        ...\n    \n    ...<\/code><\/pre>\n<p>\u53ef\u4ee5\u770b\u51fa\u6709\u4e24\u4e2a\u6761\u4ef6\uff1a<\/p>\n<ul>\n<li>MotionEvent.ACTION_DOWN\u4e8b\u4ef6\u6216\u8005mFirstTouchTarget\u975e\u7a7a\u4e5f\u5c31\u662f\u6709\u5b50view\u5728\u5904\u7406\u4e8b\u4ef6<\/li>\n<li>\u5b50view\u6ca1\u6709\u505a\u62e6\u622a\uff0c\u4e5f\u5c31\u662f\u6ca1\u6709\u8c03\u7528ViewParent#requestDisallowInterceptTouchEvent(true)<\/li>\n<\/ul>\n<p>\u5982\u679c\u6ee1\u8db3\u4e0a\u9762\u7684\u4e24\u4e2a\u6761\u4ef6\u624d\u4f1a\u6267\u884conInterceptTouchEvent(ev)\u3002\u5982\u679cViewGroup\u62e6\u622a\u4e86\u4e8b\u4ef6\uff0c\u5219intercepted\u53d8\u91cf\u4e3atrue\uff0c\u63a5\u7740\u5f80\u4e0b\u770b\uff1a<\/p>\n<pre class=\"wp-block-code\"><code>@Override\npublic boolean dispatchTouchEvent(MotionEvent ev) \n    \n    boolean handled = false;\n    if (onFilterTouchEventForSecurity(ev)) \n        ...\n\n        \/\/ Check for interception.\n        final boolean intercepted;\n        if (actionMasked == MotionEvent.ACTION_DOWN\n                || mFirstTouchTarget != null) \n            final boolean disallowIntercept = (mGroupFlags &amp; FLAG_DISALLOW_INTERCEPT) != 0;\n            if (!disallowIntercept) \n                \/\/ \u5f53mFirstTouchTarget != null\uff0c\u4e5f\u5c31\u662f\u5b50view\u5904\u7406\u4e86\u4e8b\u4ef6\n                \/\/ \u6b64\u65f6\u5982\u679c\u7236ViewGroup\u62e6\u622a\u4e86\u4e8b\u4ef6\uff0cintercepted==true\n                intercepted = onInterceptTouchEvent(ev);\n                ev.setAction(action); \/\/ restore action in case it was changed\n             else \n                intercepted = false;\n            \n         else \n            \/\/ There are no touch targets and this action is not an initial down\n            \/\/ so this view group continues to intercept touches.\n            intercepted = true;\n        \n\n        ...\n\n        \/\/ Dispatch to touch targets.\n        if (mFirstTouchTarget == null) \n            ...\n         else \n            \/\/ Dispatch to touch targets, excluding the new touch target if we already\n            \/\/ dispatched to it.  Cancel touch targets if necessary.\n            TouchTarget predecessor = null;\n            TouchTarget target = mFirstTouchTarget;\n            while (target != null) \n                final TouchTarget next = target.next;\n                if (alreadyDispatchedToNewTouchTarget &amp;&amp; target == newTouchTarget) \n                    ...\n                 else \n                    \/\/ \u5224\u65ad\u4e00\uff1a\u6b64\u65f6cancelChild == true\n                    final boolean cancelChild = resetCancelNextUpFlag(target.child)\n                            || intercepted;\n\n\t\t\t\t\t\/\/ \u5224\u65ad\u4e8c\uff1a\u7ed9child\u53d1\u9001cancel\u4e8b\u4ef6\n                    if (dispatchTransformedTouchEvent(ev, cancelChild,\n                            target.child, target.pointerIdBits)) \n                        handled = true;\n                    \n                    ...\n            \n    ...\n    return handled;<\/code><\/pre>\n<p>\u4ee5\u4e0a\u5224\u65ad\u4e00\u5904cancelChild\u4e3atrue\uff0c\u7136\u540e\u8fdb\u5165\u5224\u65ad\u4e8c\u4e2d\u4e00\u770b\u7a76\u7adf\uff1a<\/p>\n<pre class=\"wp-block-code\"><code>private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,\n            View child, int desiredPointerIdBits) \n    final boolean handled;\n\n    \/\/ Canceling motions is a special case.  We don't need to perform any transformations\n    \/\/ or filtering.  The important part is the action, not the contents.\n    final int oldAction = event.getAction();\n    if (cancel || oldAction == MotionEvent.ACTION_CANCEL) \n        \/\/ \u5c06event\u8bbe\u7f6e\u6210ACTION_CANCEL\n        event.setAction(MotionEvent.ACTION_CANCEL);\n        if (child == null) \n            ...\n         else \n            \/\/ \u5206\u53d1\u7ed9child\n            handled = child.dispatchTouchEvent(event);\n        \n        event.setAction(oldAction);\n        return handled;<\/code><\/pre>\n<p>\u5f53\u53c2\u6570cancel\u4e3ature\u65f6\u4f1a\u5c06event\u8bbe\u7f6e\u4e3aMotionEvent.ACTION_CANCEL\uff0c\u7136\u540e\u5206\u53d1\u7ed9child\u3002<\/p>\n<h3 class=\"wp-block-heading\">2\u3001ACTION_DOWN\u521d\u59cb\u5316\u64cd\u4f5c<\/h3>\n<p>\u9996\u5148\u8bf7\u770b\u4e00\u6bb5\u4ee3\u7801\uff1a<\/p>\n<pre class=\"wp-block-code\"><code>public boolean dispatchTouchEvent(MotionEvent ev) \n\n    boolean handled = false;\n    if (onFilterTouchEventForSecurity(ev)) \n        final int action = ev.getAction();\n        final int actionMasked = action &amp; MotionEvent.ACTION_MASK;\n\n        \/\/ Handle an initial down.\n        if (actionMasked == MotionEvent.ACTION_DOWN) \n            \/\/ Throw away all previous state when starting a new touch gesture.\n            \/\/ The framework may have dropped the up or cancel event for the previous gesture\n            \/\/ due to an app switch, ANR, or some other state change.\n            \/\/ \u53d6\u6d88\u5e76\u6e05\u9664\u6240\u6709\u7684Touch\u76ee\u6807\n            cancelAndClearTouchTargets(ev);\n            resetTouchState();\n    \t\n    \t...\n    \n    ...<\/code><\/pre>\n<p>\u7cfb\u7edf\u53ef\u80fd\u4f1a\u7531\u4e8eApp\u5207\u6362\u3001ANR\u7b49\u539f\u56e0\u4e22\u5931\u4e86up\uff0ccancel\u4e8b\u4ef6\u3002\u56e0\u6b64\u9700\u8981\u5728ACTION_DOWN\u65f6\u4e22\u5f03\u6389\u6240\u6709\u524d\u9762\u7684\u72b6\u6001\uff0c\u5177\u4f53\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n<pre class=\"wp-block-code\"><code>private void cancelAndClearTouchTargets(MotionEvent event) \n    if (mFirstTouchTarget != null) \n        boolean syntheticEvent = false;\n        if (event == null) \n            final long now = SystemClock.uptimeMillis();\n            event = MotionEvent.obtain(now, now,\n                    MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);\n            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);\n            syntheticEvent = true;\n        \n\n        for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) \n            resetCancelNextUpFlag(target.child);\n            \/\/ \u5206\u53d1\u4e8b\u4ef6\u540c\u60c5\u51b5\u4e00\n            dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);\n        \n        ...<\/code><\/pre>\n<p>PS\uff1a\u5728dispatchDetachedFromWindow()\u4e2d\u4e5f\u4f1a\u8c03\u7528cancelAndClearTouchTargets()<\/p>\n<h3 class=\"wp-block-heading\">3\u3001\u5728\u5b50View\u5904\u7406\u4e8b\u4ef6\u7684\u8fc7\u7a0b\u4e2d\u88ab\u4ece\u7236View\u4e2d\u79fb\u9664\u65f6<\/h3>\n<p>\u8bf7\u770b\u4e0b\u9762\u8fd9\u6bb5\u4ee3\u7801\uff1a<\/p>\n<pre class=\"wp-block-code\"><code>public void removeView(View view) \n    if (removeViewInternal(view)) \n        requestLayout();\n        invalidate(true);\n    \n\n\nprivate boolean removeViewInternal(View view) \n    final int index = indexOfChild(view);\n    if (index &gt;= 0) \n        removeViewInternal(index, view);\n        return true;\n    \n    return false;\n\n\nprivate void removeViewInternal(int index, View view) \n\n    ...\n    cancelTouchTarget(view);\n\t...\n\n\nprivate void cancelTouchTarget(View view) \n    TouchTarget predecessor = null;\n    TouchTarget target = mFirstTouchTarget;\n    while (target != null) \n        final TouchTarget next = target.next;\n        if (target.child == view) \n            ...\n            \/\/ \u521b\u5efaACTION_CANCEL\u4e8b\u4ef6\n            MotionEvent event = MotionEvent.obtain(now, now,\n                    MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);\n            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);\n            \u5206\u53d1\u7ed9\u76ee\u6807view\n            view.dispatchTouchEvent(event);\n            event.recycle();\n            return;\n        \n        predecessor = target;\n        target = next;<\/code><\/pre>\n<h3 class=\"wp-block-heading\">4\u3001\u5b50View\u88ab\u8bbe\u7f6e\u4e86PFLAG_CANCEL_NEXT_UP_EVENT\u6807\u8bb0\u65f6<\/h3>\n<p>\u8bf7\u770b\u4e0b\u9762\u8fd9\u6bb5\u4ee3\u7801\uff0c\u5728\u60c5\u51b5\u4e00\u79cd\u7684\u4e24\u4e2a\u5224\u65ad\u5904\uff1a<\/p>\n<pre class=\"wp-block-code\"><code>\/\/ \u5224\u65ad\u4e00\uff1a\u6b64\u65f6cancelChild == true\nfinal boolean cancelChild = resetCancelNextUpFlag(target.child)\n|| intercepted;\n\n\/\/ \u5224\u65ad\u4e8c\uff1a\u7ed9child\u53d1\u9001cancel\u4e8b\u4ef6\nif (dispatchTransformedTouchEvent(ev, cancelChild,\n    target.child, target.pointerIdBits)) \n    handled = true;<\/code><\/pre>\n<p>\u5f53\u00a0resetCancelNextUpFlag(target.child)\u00a0\u4e3atrue\u65f6\u540c\u6837\u4e5f\u4f1a\u5bfc\u81f4cancel\uff0c\u67e5\u770b\u4ee3\u7801\uff1a<\/p>\n<pre class=\"wp-block-code\"><code>\/**\n * Indicates whether the view is temporarily detached.\n *\n * @hide\n *\/\nstatic final int PFLAG_CANCEL_NEXT_UP_EVENT        = 0x04000000;\n\nprivate static boolean resetCancelNextUpFlag(View view) \n    if ((view.mPrivateFlags &amp; PFLAG_CANCEL_NEXT_UP_EVENT) != 0) \n        view.mPrivateFlags &amp;= ~PFLAG_CANCEL_NEXT_UP_EVENT;\n        return true;\n    \n    return false;<\/code><\/pre>\n<h2 class=\"wp-block-heading\">\u4e8c\u3001\u6ed1\u51fa\u5b50View\u8303\u56f4\u4f1a\u53d1\u751f\u4ec0\u4e48<\/h2>\n<p>\u901a\u5e38\u6765\u8bf4\uff0c\u6ed1\u51fa\u5b50View\u8303\u56f4\u4ec0\u4e48\u4e5f\u4e0d\u4f1a\u53d1\u751f\u3002\u5982\u679c\u624b\u6307\u79fb\u51fa\u4e86\u5b50View\u4e4b\u5916\uff0c\u4ece\u800c\u5bfc\u81f4\u4e8b\u4ef6\u5e8f\u5217\u88ab\u53d6\u6d88\uff0c\u90a3\u4e48\u901a\u5e38\u4e0d\u4f1a\u6709\u592a\u591a\u4e8b\u60c5\u53d1\u751f\u3002\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u5c06\u4f1a\u6536\u5230\u4e00\u4e2aACTION_CANCEL\u4e8b\u4ef6\uff0c\u4f46\u662f\u7531\u4e8e\u4e8b\u4ef6\u5df2\u7ecf\u88ab\u53d6\u6d88\uff0c\u60a8\u65e0\u6cd5\u6267\u884c\u4efb\u4f55\u8fdb\u4e00\u6b65\u7684\u64cd\u4f5c\u3002\u5982\u679c\u60a8\u5e0c\u671b\u907f\u514d\u8fd9\u79cd\u60c5\u51b5\u53d1\u751f\uff0c\u60a8\u53ef\u4ee5\u5c1d\u8bd5\u4f7f\u7528requestDisallowInterceptTouchEvent()\u65b9\u6cd5\u6765\u9632\u6b62\u89e6\u6478\u4e8b\u4ef6\u5e8f\u5217\u88ab\u62e6\u622a\uff0c\u6216\u8005\u91cd\u65b0\u8bbe\u8ba1\u60a8\u7684UI\u4ee5\u786e\u4fdd\u7528\u6237\u4e0d\u4f1a\u610f\u5916\u5730\u79fb\u52a8\u624b\u6307\u5230View\u7684\u8303\u56f4\u5916\u3002<\/p>\n<p><strong><em>\u5ef6\u4f38\u9605\u8bfb1\uff1aACTION_CANCEL\u4f5c\u7528<\/em><\/strong><\/p>\n<p>\u6211\u4eec\u77e5\u9053\u5982\u679c\u67d0\u4e00\u4e2a\u5b50View\u5904\u7406\u4e86Down\u4e8b\u4ef6\uff0c\u90a3\u4e48\u968f\u4e4b\u800c\u6765\u7684Move\u548cUp\u4e8b\u4ef6\u4e5f\u4f1a\u4ea4\u7ed9\u5b83\u5904\u7406\u3002\u4f46\u662f\u4ea4\u7ed9\u5b83\u5904\u7406\u4e4b\u524d\uff0c\u7236View\u8fd8\u662f\u53ef\u4ee5\u62e6\u622a\u4e8b\u4ef6\u7684\uff0c\u5982\u679c\u62e6\u622a\u4e86\u4e8b\u4ef6\uff0c\u90a3\u4e48\u5b50View\u5c31\u4f1a\u6536\u5230\u4e00\u4e2aCancel\u4e8b\u4ef6\uff0c\u5e76\u4e14\u4e0d\u4f1a\u6536\u5230\u540e\u7eed\u7684Move\u548cUp\u4e8b\u4ef6\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"ACTION_CANCEL\u5728\u8fd9\u4e9b\u65f6\u5019\u4f1a\u89e6\u53d1\uff1a1\u3001\u7236view\u62e6\u622a\u4e8b\u4ef6\uff1b2\u3001ACTION_DOWN\u521d\u59cb\u5316\u64cd\u4f5c\uff1b3\u3001 [&hellip;]","protected":false},"author":1,"featured_media":30864,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[37],"tags":[119],"acf":[],"_links":{"self":[{"href":"https:\/\/docs.pingcode.com\/wp-json\/wp\/v2\/posts\/30863"}],"collection":[{"href":"https:\/\/docs.pingcode.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/docs.pingcode.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/docs.pingcode.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/docs.pingcode.com\/wp-json\/wp\/v2\/comments?post=30863"}],"version-history":[{"count":0,"href":"https:\/\/docs.pingcode.com\/wp-json\/wp\/v2\/posts\/30863\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/docs.pingcode.com\/wp-json\/wp\/v2\/media\/30864"}],"wp:attachment":[{"href":"https:\/\/docs.pingcode.com\/wp-json\/wp\/v2\/media?parent=30863"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/docs.pingcode.com\/wp-json\/wp\/v2\/categories?post=30863"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/docs.pingcode.com\/wp-json\/wp\/v2\/tags?post=30863"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}