为Android Navigation Drawer设置拖动边距

34

我正在使用我的应用程序中的新导航抽屉。它非常好,但我还没有找到一个简单的方法来设置抽屉是如何打开的。默认行为是从屏幕左边缘拖动打开。这很好,除非手机/平板电脑上有盖子,无法触摸到手机的边缘。我希望允许用户从屏幕左侧的边距处触摸并拖动。这在其他导航抽屉库(Sliding lib)中很容易设置。不幸的是,我还没有看到Google的库中有任何内容。

导航布局中有一个onTouchEvent()方法,可以使用它。当然,您可以监听所有触摸事件并触发openDrawer,但希望有一种更简单的方式减少代码量。有什么想法吗?


+1 我也很想弄清楚这个。 - bbedward
2个回答

70

我通过反射找到了一个解决方案。

DrawerLayout mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
Field mDragger = mDrawerLayout.getClass().getDeclaredField(
        "mLeftDragger");//mRightDragger for right obviously
mDragger.setAccessible(true);
ViewDragHelper draggerObj = (ViewDragHelper) mDragger
        .get(mDrawerLayout);

Field mEdgeSize = draggerObj.getClass().getDeclaredField(
        "mEdgeSize");
mEdgeSize.setAccessible(true);
int edge = mEdgeSize.getInt(draggerObj);

mEdgeSize.setInt(draggerObj, edge * 5); //optimal value as for me, you may set any constant in dp

1
黑客并不真正起作用,特别是当它的内容是ViewPager时。当向左滑动时,使用NavigationDrawer,ViewPager不会从视图0更改为视图1。所有事件都被NavigationDrawer消耗掉了。 - Cheok Yan Cheng
3
@CheokYanCheng 这很明显。你不能两全其美 :) - Saket
@Maxim:我在 Android 5.0 上测试,发现它运行不太流畅。当滑动到屏幕中间附近时,左侧屏幕无法向右移动! - Phuong
默认边缘大小为20 dp,您可以查看ViewDragHelper.java的源代码, final ViewConfiguration vc = ViewConfiguration.get(context); final float density = context.getResources().getDisplayMetrics().density; mEdgeSize = (int) (EDGE_SIZE * density + 0.5f); - user1611552
1
有没有直接的API可以做到这一点? - rupesh
1
它与「androidx.drawerlayout:drawerlayout:1.0.0」一起工作,但与「androidx.drawerlayout:drawerlayout:1.1.1」不兼容。 - Yahia

2

这是一个非常古老的问题,但我最近遇到了这个问题。我尝试了原始解决方案,但它对我没有起作用。

经过数小时的尝试后,我发现问题不在解决方案上,而是由于库版本更改引起的。

被接受的答案适用于android.drawerlayout:drawerlayout:1.0.0

但对我而言,我使用的是com.google.android.material:material:1.5.0,它依赖于drawerlayout:drawerlayout的更高版本。

Android团队在onLayout中添加了一些逻辑来设置边缘大小。

所以我使用了原始解决方案,但我没有在活动中使用它,而是基于DrawerLayout创建了一个自定义视图。

这是我的自定义类:

class CustomDrawerLayout : DrawerLayout {
    var swipeEdgeSize = 0

    constructor(context: Context) : this(context, null, 0)
    constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        super.onLayout(changed, l, t, r, b)
        var leftDraggerField = DrawerLayout::class.java.getDeclaredField("mLeftDragger")
        leftDraggerField.isAccessible = true
        val viewDragHelper = leftDraggerField[this] as ViewDragHelper

        val edgeSizeField = ViewDragHelper::class.java.getDeclaredField("mEdgeSize")
        edgeSizeField.isAccessible = true
        val origEdgeSize = edgeSizeField[viewDragHelper] as Int
        edgeSizeField.setInt(viewDragHelper, max(swipeEdgeSize, origEdgeSize))
    }
}

在这个活动中,我将使用类似这样的东西。
class MainActivity :....{
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(....)
        setSideMenuAreaRatio(0.8)
        ....
    }

private fun setSideMenuAreaRatio(ration: Double) {
        val displaySize = Point()
        windowManager.defaultDisplay.getSize(displaySize)
        binding.drawerLayout.swipeEdgeSize = (displaySize.x * ration).toInt()
    }
}

如果你正在开始一个新项目,使用 jetpack compose。它没有这个问题。


另外要确保该解决方案仅适用于左/开始抽屉而不是右/结束抽屉


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接