【问题标题】:Navigation Drawer: set as always opened on tablets导航抽屉:设置为在平板电脑上始终打开
【发布时间】:2013-06-12 13:57:47
【问题描述】:

我正在使用支持库中的导航抽屉模式: http://developer.android.com/training/implementing-navigation/nav-drawer.html

我试图将其设置为始终在平板电脑上打开(作为侧边菜单)

当前的实现是否有可能,或者我们是否必须使用 Listview 创建新布局和新结构,而不是重用相同的代码?

【问题讨论】:

    标签: android android-layout navigation-drawer tablet


    【解决方案1】:

    基于更大的设备可能有不同的布局文件的想法,我创建了以下项目。

    https://github.com/jiahaoliuliu/ABSherlockSlides

    亮点

    由于大型设备的抽屉始终可见,因此不需要抽屉。相反,一个具有两个同名元素的 LinearLayout 就足够了。

    <LinearLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="horizontal">
         <ListView
                 android:id="@+id/listview_drawer"
                 android:layout_width="@dimen/drawer_size"
                 android:layout_height="match_parent"
                 android:layout_gravity="start"
                 android:choiceMode="singleChoice"
                 android:divider="@android:color/transparent"
                 android:dividerHeight="0dp"
                 android:background="@color/drawer_background"/>
        <FrameLayout
                android:id="@+id/content_frame"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginLeft="@dimen/drawer_content_padding"
                />
    </LinearLayout>
    

    因为我们在布局文件中没有抽屉,所以当app试图在布局中查找元素时,它会返回null。因此,不需要额外的布尔值来查看正在使用的布局。

    DrawerLayout mDrawerLayout = (DrawerLayout)findViewById(R.id.drawer_layout);
    
    if (mDrawerLayout != null) {
        // Set a custom shadow that overlays the main content when the drawer opens
        mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
        // Enable ActionBar app icon to behave as action to toggle nav drawer
        getSupportActionBar().setHomeButtonEnabled(true);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    
        // ActionBarDrawerToggle ties together the proper interactions
        // between the sliding drawer and the action bar app icon
        mDrawerToggle = new ActionBarDrawerToggle(
                this,
                mDrawerLayout,
                R.drawable.ic_drawer,
                R.string.drawer_open,
                R.string.drawer_close) {
    
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
            }
    
            public void onDrawerOpened(View drawerView) {
                // Set the title on the action when drawer open
                getSupportActionBar().setTitle(mDrawerTitle);
                super.onDrawerOpened(drawerView);
            }
        };
    
        mDrawerLayout.setDrawerListener(mDrawerToggle);
    }
    

    这是将其用作布尔值的示例。

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        if (mDrawerLayout != null) {
            mDrawerToggle.syncState();
        }
    }
    
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (mDrawerLayout != null) {
            // Pass any configuration change to the drawer toggles
            mDrawerToggle.onConfigurationChanged(newConfig);
        }
    }
    

    【讨论】:

    • 如何确定它是平板设备?使用res/layout-sw600dp-land 足够了吗?
    • 这是一个古老但有趣的问题。您可能会在这里找到答案:stackoverflow.com/questions/11330363/… 请注意,仅通过创建此文件夹并将布局放入其中,并不意味着将使用此布局。如果需要,您可以检测它是否是平板电脑,然后以编程方式强制它处于横向模式。然后这将起作用。祝你好运!
    【解决方案2】:

    根据 CommonsWare 的回答,您可以通过一些调整来做到这一点。首先是设置以下三行:

    drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);
    drawerLayout.setScrimColor(getResources().getColor(R.color.drawerNoShadow));
    isDrawerLocked = true;
    

    drawerNoShadow 颜色只能是非 alpha 颜色(如 0x00000000)。这会让你得到一个没有背景覆盖的开放式抽屉。

    您需要做的第二件事是调整 FrameLayout 的 padding_left 值。为此,您可以设置一个维度来控制它(默认为 0dp) - 在本例中为 R.dimen.drawerContentPadding。您还需要一个 R.dimen.drawerSize 值,该值将是 DrawerLayout 的宽度。

    这允许您检查 FrameLayout 的 paddingLeft 值以调用这些行。

    FrameLayout frameLayout = (FrameLayout)findViewById(R.id.content_frame);
    if(frameLayout.getPaddingLeft() == (int)getResources().getDimension(R.dimen.drawerSize) {
        drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);
        drawerLayout.setScrimColor(getResources().getColor(R.color.drawerNoShadow));
        isDrawerLocked = true;
    }
    

    然后,您可以将所有不想启用的功能包装在 if(!isDrawerLocked) 语句中。这将包括:

    • drawerLayout.setDrawerListener(drawerToggle);
    • getActionBar().setDisplayHomeAsUpEnabled(true);

    最后,您确实需要使用静态抽屉为视图设置备用布局。一个例子是:

    <FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v4.widget.DrawerLayout
    
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <!-- The navigation drawer -->
        <ListView
            android:id="@+id/left_drawer"
            android:layout_width="@dimen/drawerSize"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:choiceMode="singleChoice"
            android:divider="@android:color/transparent"
            android:dividerHeight="0dp"
            android:background="#111"/>
    
    </android.support.v4.widget.DrawerLayout>
    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginLeft="@dimen/drawerContentPadding"/>
    

    这里的美妙之处在于,您可以通过为您想要定位的设备设置备用 dimen.xml 文件来控制所有逻辑,您唯一需要更改的是抽屉内容填充的值并提供修改后的布局。

    注意:我最终使用了 margin_left 而不是 padding_left,因为在新布局中它覆盖了抽屉。在http://derekrwoods.com/2013/09/creating-a-static-navigation-drawer-in-android/查看有关该技术的更深入的博客文章

    【讨论】:

    • 很好的例子!由于您将其更改为 padding_left 而不是边距,因此您无法再使用示例检测哪个布局。但是,如果您在每个布局上设置一个标签,您可以比较该标签以找出正在使用的布局。 *** 还有一件事 - 我发现在将抽屉锁定在向外位置后,当我切换回纵向时,它会打开/关闭滑动动作不再打开抽屉。知道为什么吗?
    • 我也刚刚意识到,从打开的抽屉切换回纵向模式后,我的 onKeyDown 事件停止触发活动!我正在使用兼容性片段,我想知道那里是否有错误......
    • 我通过将此代码移至 onPostCreate() 解决了我的大部分问题,但如果您尝试在抽屉锁定打开时将其滑动关闭,我仍然会崩溃。
    • @methodin 如果您能将完整代码添加到您的博客中,我将不胜感激。
    • @eddy 如果你知道如何运行代码stackoverflow.com/a/28809296/2148631
    【解决方案3】:

    Try setDrawerLockMode() 锁定抽屉在大屏幕设备上打开。

    正如我在评论中指出的那样,我不认为 DrawerLayout 是为您的场景设计的(尽管这不是一个坏主意,恕我直言)。要么使用承载相同ListView 和内容的不同布局,要么下载并修改您自己的DrawerLayout 副本,在大屏幕设备上,打开内容而不是重叠内容。

    【讨论】:

    • 抽屉已打开,但在我的实际布局前面。
    • @Waza_Be:哦,对了。我不认为DrawerLayout 是为您的场景设计的。要么使用承载相同ListView 和内容的不同布局,要么下载并修改您自己的DrawerLayout 副本,在大屏幕设备上,打开时将内容滑过而不是重叠。
    • 我接受了你的回答,但如果你在答案中复制你的评论会很有用;-)
    【解决方案4】:

    只需为平板电脑提供备用布局文件。这样就可以保存NavigationView的所有默认行为。


    第一步

    只需为平板设备创建一个与此类似的备用布局文件,并将其放在layout-w600dp-land 资源目录中。

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v4.widget.DrawerLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:openDrawer="start">
    
        <!--
        NavigationView and the content is placed in a horizontal LinearLayout
        rather than as the direct children of DrawerLayout.
        This makes the NavigationView always visible.
        -->
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <android.support.design.widget.NavigationView
                android:id="@+id/nav"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                app:headerLayout="@layout/nav_header_main"
                app:menu="@menu/activity_main_drawer"/>
            <include
                layout="@layout/app_bar_main"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </LinearLayout>
    
    </android.support.v4.widget.DrawerLayout>
    



    第 2 步

    在此步骤中,我们将进行足够的更改以确保抽屉的打开和关闭仅适用于非平板设备。

    步骤 2.1

    在values目录下新建一个value资源文件,命名为config_ui.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <bool name="isDrawerFixed">false</bool>
    </resources>
    

    这适用于非平板设备。对于平板设备,创建另一个具有相同名称的设备并将其放在values-w600dp-land

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <bool name="isDrawerFixed">true</bool>
    </resources>
    

    在抽屉所属的活动类中创建一个新字段
    private boolean isDrawerFixed;
    并将其初始化为
    isDrawerFixed = getResources().getBoolean(R.bool.isDrawerFixed);.

    现在我们可以像if (isDrawerFixed){} 一样简单地检查设备是平板电脑还是非平板电脑。

    步骤 2.2

    用这样的if 语句包装在操作栏上设置切换按钮的代码。

    if (!isDrawerFixed) {
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();
    }
    

    使用像这样的另一个if 语句包装当单击项目时关闭抽屉的代码。

    if (!isDrawerFixed) {
        drawer.closeDrawer(GravityCompat.START);
    }
    




    最终的结果会有点像这样。

    【讨论】:

    • 这是一个很好的答案。比其他人提供的更容易和明智。谢谢!我使用了RelativeLayout 而不是LinearLayout,我无法仅使用LinearLayout 来显示主要/细节片段。再次感谢。提供屏幕截图以供证明和与我的设备进行比较的额外积分。
    • 最佳答案。简单高效。
    【解决方案5】:

    以前的答案很好,但是我在项目中实施它们时遇到了一些问题,所以我想分享我的解决方案。 首先,我们需要定义一个自定义抽屉:

    public class MyDrawerLayout extends DrawerLayout {
        private boolean m_disallowIntercept;
    
        public MyDrawerLayout (Context context) {
            super(context);
        }
    
        @Override
        public boolean onInterceptTouchEvent(final MotionEvent ev) {
            // as the drawer intercepts all touches when it is opened
            // we need this to let the content beneath the drawer to be touchable
            return !m_disallowIntercept && super.onInterceptTouchEvent(ev);
        }
    
        @Override
        public void setDrawerLockMode(int lockMode) {
            super.setDrawerLockMode(lockMode);
            // if the drawer is locked, then disallow interception
            m_disallowIntercept = (lockMode == LOCK_MODE_LOCKED_OPEN);
        }
    }
    

    然后我们把它放在一个基本的活动布局中(没有以前答案中的任意布局),如下所示:

    <MyDrawerLayout 
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:openDrawer="start">
    
        <!--We must define left padding for content-->
        <FrameLayout
            android:id="@+id/content_frame"
            android:paddingStart="@dimen/content_padding"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
        <android.support.design.widget.NavigationView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:fitsSystemWindows="true"
            app:menu="@menu/menu_nav" />
    
    </MyDrawerLayout>
    

    此处的内容填充在纵向方向为 0dp,在横向方向为 NavigationView 大约 300dp(根据经验计算)。我们在适当的values 文件夹中定义它们:

    values/dimens.xml-

    <dimen name="content_padding">0dp</dimen>
    

    values-land/dimens.xml-

    <dimen name="content_padding">300dp</dimen>
    

    最后,我们将抽屉锁在activity中:

        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);
            mDrawerLayout.setScrimColor(0x00000000); // or Color.TRANSPARENT
            isDrawerLocked = true;
        } else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
            mDrawerLayout.setScrimColor(0x99000000); // default shadow
            isDrawerLocked = false;
        }
    

    【讨论】:

    • 我认为这是迄今为止最好的答案,因为不涉及重复代码,并且它利用 NavigationView API 为导航图让路。干得好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-18
    • 2015-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多