我在
SlidingPaneLayout
中有两个片段,片段 A 和片段 B。片段 A 是主片段,片段 B 是可以滑入的窗格。在无法同时容纳片段 A 和片段 B 的设备上,有一个奇怪的问题行为。如果用户打开片段 B 并尝试点击片段 B 左边缘的任何视图,则触摸会被 SlidingPaneLayout
消耗。这实质上使片段 B 的整个左边缘成为死区,并干扰某些 UI 元素,例如工具栏的向上按钮,因为它位于左边缘附近。它还会干扰底部工具栏的操作按钮。有谁知道为什么会发生这种情况以及如何禁用这种行为?
我找出了这个问题的原因,或者至少我找出了导致这种情况发生的代码。我仍然不知道为什么会出现导致此问题的代码。
看起来如果窗格打开
SlidingPaneLayout
会启用ViewDragHelper
上的边缘跟踪。如果视图是 LTR,则启用左侧边缘跟踪。如果视图是 RTL,则它会启用右侧的边缘跟踪。我不知道为什么 SlidingPaneLayout
会这样做,但我可以在它的源代码中看到这个逻辑。
从
SlidingPaneLayout
的源代码来看,似乎没有一种简单的方法可以切换此行为。它本质上是硬编码为始终开启的。
为了解决这个问题,我必须延长
SlidingPaneLayout
。然后我覆盖 onInterceptTouchEvent()
并调用 SlidingPaneLayout
的 onInterceptTouchEvent()
。如果返回 false,则返回 false。如果返回 true,我会检查窗格是否打开。如果是,我检查触摸事件是否发生在左边缘(如果是 LTR)或右边缘(如果是 RTL)。如果触摸发生在边缘,那么我返回 false。否则我返回 true。
这有效地防止了
SlidingPaneLayout
吃掉边缘触摸事件。
固定
SlidingPaneLayout
的完整代码在这里:
class FixedSlidingPaneLayout : SlidingPaneLayout {
companion object {
private val sEdgeSizeUsingSystemGestureInsets = Build.VERSION.SDK_INT >= 29
}
// We only construct a drag helper to get the width of the drag region.
private val dragHelper = ViewDragHelper.create(
this, 0.5f,
object : ViewDragHelper.Callback() {
override fun tryCaptureView(child: View, pointerId: Int): Boolean {
return false
}
},
)
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(
context,
attrs,
defStyle,
)
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
val intercept = super.onInterceptTouchEvent(ev)
if (ev == null) {
return intercept
}
if (!intercept) {
return false
}
if (!isOpen || !isSlideable) {
return true
}
val gestureInsets = getSystemGestureInsets()
?: return true
val edgeSize = max(dragHelper.defaultEdgeSize, gestureInsets.left)
val isLayoutRtl = isLayoutRtlSupport()
if (isLayoutRtl) {
if (ev.x > edgeSize) {
return false
}
} else {
if (ev.x < edgeSize) {
return false
}
}
return true
}
private fun getSystemGestureInsets(): Insets? {
var gestureInsets: Insets? = null
if (sEdgeSizeUsingSystemGestureInsets) {
val rootInsetsCompat = ViewCompat.getRootWindowInsets(this)
if (rootInsetsCompat != null) {
gestureInsets = rootInsetsCompat.systemGestureInsets
}
}
return gestureInsets
}
private fun isLayoutRtlSupport(): Boolean {
return ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL
}
}