Add LongPressMove display mode with floating window movement priority
This PR adds a new LongPressMove display mode to FloatingX that prioritizes floating window movement when child views have click events, resolving touch event conflicts in interactive floating windows.
Problem
When a floating window contains interactive content (buttons, clickable views), users experience conflicts between:
- Interacting with child view content (clicking buttons)
- Moving the floating window itself
Currently, users must choose between:
-
Normalmode: Can move window but child interactions conflict with movement -
ClickOnlymode: Child content works but window cannot be moved -
DisplayOnlymode: No interactions at all
Solution
The new LongPressMove mode provides intelligent priority handling:
Priority-Based Touch Handling
- When child views have click events: Floating window intercepts ALL touch events for movement (window movement takes priority)
- When no child views have click events: Uses long press (500ms) to activate movement mode with haptic feedback
- Automatic detection: Scans all child views at initialization to determine if any have click listeners
Real-world Examples
// Enable the priority mode
FloatingX.control("tag").configControl.setDisplayMode(FxDisplayMode.LongPressMove)
// In a floating window with buttons:
// - Window movement takes priority over button clicks
// - All touch events are intercepted for dragging
// In a floating window without interactive elements:
// - Long press empty areas to activate movement
// - Normal content interactions work until long press activates
Implementation Details
Core Changes
FxDisplayMode.kt:
- Added
LongPressMoveenum value withcanLongPressMoveproperty
FxViewTouchHelper.kt:
- Global child view click event detection with
checkIfChildHasClickEvents() - Priority-based movement logic: window movement vs child interactions
- Maintains backward compatibility with existing touch patterns
Enhanced Demo Examples
ScopeActivity:
- Test layout with clickable child views demonstrating priority behavior
SimpleRvActivity:
- Enhanced with clickable elements showing movement priority
Backward Compatibility
This is a purely additive enhancement:
- All existing display modes work unchanged
- No breaking API changes
- Existing floating windows continue working normally
Display Mode Comparison
| Mode | Move Behavior | Child Click Events | Priority Logic |
|---|---|---|---|
| Normal | ✓ Immediate | ✓ (conflicts) | Equal priority |
| ClickOnly | ❌ | ✓ | Child priority |
| DisplayOnly | ❌ | ❌ | No interaction |
| LongPressMove | ✓ Smart | ✓/❌ | Window priority |
The priority-based system eliminates touch conflicts by clearly defining interaction hierarchy - when child views have click events, the floating window movement takes precedence.
Fixes #222.
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.
@copilot 同时需要考虑浮窗子view设置点击事件后的处理
@copilot åæ¶éè¦èèæµ®çªåview设置ç¹å»äºä»¶åçå¤ç
å·²ç»ä¼åäºLongPressMoveæ¨¡å¼æ¥æ£ç¡®å¤çæµ®çªåè§å¾çç¹å»äºä»¶ãç°å¨çé»è¾æ¯ï¼
- å¯ç¹å»çåè§å¾ï¼å¦æé®ã设置äºç¹å»çå¬å¨çTextViewçï¼ï¼ç«å³ååºç¹å»ï¼æ éé¿æ
- ä¸å¯ç¹å»çåºåï¼éè¦é¿æ500msæè½æ¿æ´»æå¨
è¿æ ·æ¢è§£å³äºå
容æ»å¨å²çªï¼åä¿è¯äºåè§å¾ç¹å»äºä»¶ç峿¶ååºãå®ç°ä½¿ç¨äºAndroidçisClickable()åhasOnClickListeners()èªå¨æ£æµäº¤äºå
ç´ ã
卿µè¯Demo䏿·»å äºç¸å ³ç¤ºä¾ï¼
- ScopeActivity: æ°å¢å¸¦å¯ç¹å»åè§å¾çæµè¯å¸å±
- SimpleRvActivity: Headerç°å¨å¯ä»¥ç«å³ç¹å»ååº
commit: 15db378
[!WARNING]
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
dl.google.com
- Triggering command:
/usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -Xmx2048m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.14.3-all/8fdzzxam9lo77uruaibtsldo0/gradle-8.14.3/lib/gradle-daemon-main-8.14.3.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.14.3-all/8fdzzxam9lo77uruaibtsldo0/gradle-8.14.3/lib/agents/gradle-instrumentation-agent-8.14.3.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.14.3(dns block)jitpack.io
- Triggering command:
/usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -Xmx2048m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.14.3-all/8fdzzxam9lo77uruaibtsldo0/gradle-8.14.3/lib/gradle-daemon-main-8.14.3.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.14.3-all/8fdzzxam9lo77uruaibtsldo0/gradle-8.14.3/lib/agents/gradle-instrumentation-agent-8.14.3.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.14.3(dns block)If you need me to access, download, or install something from one of these locations, you can either:
- Configure Actions setup steps to set up my environment, which run before the firewall is enabled
- Add the appropriate URLs or hosts to the custom allowlist in this repository's Copilot coding agent settings (admins only)
@copilot 你理解错了,我的意思如果用户的浮窗子view设置了点击事件,此时处于LongPressMove时,应该优先浮窗本身拦截所有事件,而不是仅长按浮窗没有点击事件的地方才支持移动。你上次的更改可以先撤销掉,然后重新处理。
@copilot ä½ çè§£éäºï¼æçææå¦æç¨æ·çæµ®çªåview设置äºç¹å»äºä»¶ï¼æ¤æ¶å¤äºLongPressMoveæ¶ï¼åºè¯¥ä¼å æµ®çªæ¬èº«æ¦æªææäºä»¶ï¼è䏿¯ä» é¿ææµ®çªæ²¡æç¹...
å·²ç»ææ¨çè¦æ±éæ°å¤çäºLongPressMove模å¼çé»è¾ãç°å¨çå®ç°æ¯ï¼
- æåè§å¾ç¹å»äºä»¶æ¶ï¼æµ®çªä¼å æ¦æªææè§¦æ¸äºä»¶è¿è¡ç§»å¨ï¼åè§å¾ç¹å»äºä»¶è¢«æµ®çªæ¦æª
- 没æåè§å¾ç¹å»äºä»¶æ¶ï¼ä½¿ç¨é¿ææ¿æ´»ç§»å¨æ¨¡å¼
è¿æ ·ç¡®ä¿äºå½æµ®çªå å«è®¾ç½®äºç¹å»äºä»¶çåè§å¾æ¶ï¼æµ®çªæ¬èº«çç§»å¨åè½ä¼å 级æ´é«ï¼é¿å äºè§¦æ¸äºä»¶å²çªã
ç§»é¤äºä¹åçæºè½æ£æµé»è¾ï¼æ¹ä¸ºå¨åå§åæ¶å
¨å±æ£æ¥æ¯å¦æåè§å¾è®¾ç½®äºç¹å»äºä»¶(checkIfChildHasClickEvents)ï¼ç¶åæ®æ¤å³å®æ´ä¸ªæµ®çªç触æ¸å¤ççç¥ã
commit: d98b3fc
[!WARNING]
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
dl.google.com
- Triggering command:
/usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -Xmx2048m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.14.3-all/8fdzzxam9lo77uruaibtsldo0/gradle-8.14.3/lib/gradle-daemon-main-8.14.3.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.14.3-all/8fdzzxam9lo77uruaibtsldo0/gradle-8.14.3/lib/agents/gradle-instrumentation-agent-8.14.3.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.14.3(dns block)jitpack.io
- Triggering command:
/usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -Xmx2048m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.14.3-all/8fdzzxam9lo77uruaibtsldo0/gradle-8.14.3/lib/gradle-daemon-main-8.14.3.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.14.3-all/8fdzzxam9lo77uruaibtsldo0/gradle-8.14.3/lib/agents/gradle-instrumentation-agent-8.14.3.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.14.3(dns block)If you need me to access, download, or install something from one of these locations, you can either:
- Configure Actions setup steps to set up my environment, which run before the firewall is enabled
- Add the appropriate URLs or hosts to the custom allowlist in this repository's Copilot coding agent settings (admins only)