如何为支持平板电脑和手机的应用提供导航

时间:2015-03-05 12:37:05

标签: android android-fragments navigation-drawer android-ui landscape-portrait

我知道之前可能会问这个问题,但由于我没有找到任何令人信服的信息,我需要再次提问。

当您还需要提供某种排序导航面板(例如Navigation Drawer)时,如何设计支持平板电脑和手机的应用?

我想我知道如何使用导航抽屉与纵向方向的手机和平板电脑,因为在那些情况下我当时只显示一个窗格(片段)。但是由于你有足够的空间来展示至少两个窗格(片段),因此平板电脑在风景中是完全不同的,大部分时间都会以某种“主体 - 细节”方式相关联。

编辑#1


只是提供一些关于我的应用程序的上下文以及为什么我认为除导航列表之外还需要两个窗格。

我的应用程序的主要目标是帮助服务员接受订单,这种情况在以下情况发挥作用:

  1. 服务员从左侧菜单(理想情况下是导航抽屉)中选择“接订单”选项

  2. 在我称之为“内容部分”的第一部分(左侧窗格)中,服务员可以从他们所服务的所有食物类别中选择(假装它是顶部的列表)并根据选择的类别,下面的列表显示该特定类别下的所有菜肴。

  3. 一旦服务员点击一个盘子,它会自动添加到“内容部分”的第二部分(右侧窗格)

  4. enter image description here


    知道导航抽屉的基本结构与此相似:

    <android.support.v4.widget.DrawerLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <!-- The main content view -->
        <FrameLayout
            android:id="@+id/content_frame"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
        <!-- The navigation drawer -->
        <ListView android:id="@+id/left_drawer"
            android:layout_width="240dp"
            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>
    

    您如何处理“面向横向的平板电脑”方案?您是否在多窗格布局中使用嵌套片段(在这种情况下可能需要使用)?(听起来非常复杂)您是否使用自定义库?你知道在Git中实现这个的任何开源应用程序,我可以用作参考吗?

    编辑#2

    在尝试了一些想法后,我意识到为多个活动实现相同导航抽屉的最方便的方法是创建一个BaseActivity来处理所有抽屉功能,我的所有活动都可以从中继承。

    这是我到目前为止所得到的:

    BaseActivity

    import android.app.Activity;
    import android.content.res.Configuration;
    import android.os.Bundle;
    import android.support.v7.app.ActionBarDrawerToggle;
    import android.support.v4.widget.DrawerLayout;
    import android.util.Log;
    import android.view.MenuItem;
    import android.view.View;
    import android.widget.AdapterView;
    import android.widget.ArrayAdapter;
    import android.widget.LinearLayout;
    import android.widget.ListView;
    
    public class BaseActivity extends Activity {
    
        private DrawerLayout mDrawerLayout;
        private ActionBarDrawerToggle mDrawerToggle;
        private ListView mDrawerListView;
    
        protected void onCreate(Bundle savedInstanceState, int resLayoutID) {
    
            setContentView(resLayoutID);
            super.onCreate(savedInstanceState);
            setupNavDrawer();
        }
    
        private void setupNavDrawer() {
            mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
            mDrawerListView = (ListView) findViewById(R.id.drawer);
            mDrawerListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    
            String[] rows = getResources().getStringArray(R.array.drawer_rows);
            mDrawerListView.setAdapter(new ArrayAdapter<String>(
                    this, R.layout.drawer_row, rows));
            mDrawerToggle =
                    new ActionBarDrawerToggle(this, mDrawerLayout,
                            R.string.drawer_open,
                            R.string.drawer_close);
    
            mDrawerLayout.setDrawerListener(mDrawerToggle);
            mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> listView, View row,
                                        int position, long id) {
                    if (position == 0) {
                        Log.d("Menu", "European Union");
                    } else {
                        Log.d("Menu", "Other Option");
                    }
                    mDrawerLayout.closeDrawers();
                }
    
            });
            getActionBar().setDisplayHomeAsUpEnabled(true);
            getActionBar().setHomeButtonEnabled(true);
        }
    
        @Override
        protected void onPostCreate(Bundle savedInstanceState) {
            super.onPostCreate(savedInstanceState);
            mDrawerToggle.syncState();
        }
    
    
        @Override
        public void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            mDrawerToggle.onConfigurationChanged(newConfig);
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            if (mDrawerToggle.onOptionsItemSelected(item)) {
                return true;
            }
            return super.onOptionsItemSelected(item);
        }
    }
    

    现在需要拥有导航抽屉的每个活动都需要扩展BaseActivity

    EuropeanUnionActivity

    public class EuropeanUnionActivity extends BaseActivity{
        //...
    }
    

    此外,每个活动布局都需要在其代码中包含android.support.v4.widget.DrawerLayout元素,如下所示:

    activity_europeanunion

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <include layout="@layout/main" />
        <ListView
            android:id="@+id/drawer"
            android:layout_width="240dp"
            android:layout_height="match_parent"
            android:layout_gravity="left"
            android:background="#111"
            android:choiceMode="singleChoice"
            android:divider="@android:color/transparent"
            android:dividerHeight="0dp" />
    </android.support.v4.widget.DrawerLayout>
    

    值得一提的是,DrawerLayout(drawer_layout)和ListView(抽屉)的ID在所有具有导航抽屉的活动布局中需要相同,因为这些ID由BaseActivity使用。

    为了简洁和快速测试这种方法,我使用了@CommonsWare的this example(我希望他不介意)。在那里,您可以找到@layout/main的实现。

    现在,虽然几乎所有内容都按预期运行,但我无法理解为什么我将ListView的代码放在一个单独的文件中 listview_options 导航抽屉根本不会关闭并点击ActionBarDrawerToggle只会让应用程序崩溃并在LogCat中显示:

    FATAL EXCEPTION: main
        Process: com.idealsolution.simplenavigationdrawer, PID: 17725
        java.lang.IllegalArgumentException: No drawer view found with gravity LEFT
                at android.support.v4.widget.DrawerLayout.openDrawer(DrawerLayout.java:1293)
                at android.support.v7.app.ActionBarDrawerToggle.toggle(ActionBarDrawerToggle.java:290)
                at android.support.v7.app.ActionBarDrawerToggle.onOptionsItemSelected(ActionBarDrawerToggle.java:280)
                at com.idealsolution.simplenavigationdrawer.BaseActivity.onOptionsItemSelected(BaseActivity.java:86)
                at android.app.Activity.onMenuItemSelected(Activity.java:2608)
                at com.android.internal.widget.ActionBarView$3.onClick(ActionBarView.java:167)
                at android.view.View.performClick(View.java:4456)
                at android.view.View$PerformClick.run(View.java:18465)
                at android.os.Handler.handleCallback(Handler.java:733)
                at android.os.Handler.dispatchMessage(Handler.java:95)
                at android.os.Looper.loop(Looper.java:136)
                at android.app.ActivityThread.main(ActivityThread.java:5086)
                at java.lang.reflect.Method.invokeNative(Native Method)
                at java.lang.reflect.Method.invoke(Method.java:515)
                at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
                at dalvik.system.NativeStart.main(Native Method)
    

    这是第86行:

    public boolean onOptionsItemSelected(MenuItem item) {
    
        if (mDrawerToggle.onOptionsItemSelected(item)) { //Line 86
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    

        

    这是相同的代码 activity_europeanunion ,但为include使用ListView标记

    <include layout="@layout/main" />
    <include layout="@layout/listview_options" />
    

    listview_options.xml 的代码:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/menu_layout"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ListView
            android:id="@+id/drawer"
            android:layout_width="240dp"
            android:layout_height="match_parent"
            android:layout_gravity="left"
            android:background="#111"
            android:choiceMode="singleChoice"
            android:divider="@android:color/transparent"
            android:dividerHeight="0dp" />
    </LinearLayout>
    

    这是我在BaseActivity中尝试检索ListView的代码,但这显然无效:

        private void setupNavDrawer() {
                mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    
    
               //Please pay special attention to the following two lines
                mDrawerLinearLayout = (LinearLayout) findViewById(R.id.menu_layout);
                mDrawerListView = (ListView) mDrawerLinearLayout.findViewById(R.id.drawer);
    
    
                mDrawerListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    
                String[] rows = getResources().getStringArray(R.array.drawer_rows);
                mDrawerListView.setAdapter(new ArrayAdapter<String>(
                        this, R.layout.drawer_row, rows));
                mDrawerToggle =
                        new ActionBarDrawerToggle(this, mDrawerLayout,
                                R.string.drawer_open,
                                R.string.drawer_close);
                mDrawerLayout.setDrawerListener(mDrawerToggle);
                mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                    @Override
                    public void onItemClick(AdapterView<?> listView, View row,
                                            int position, long id) {
                        //...
                        mDrawerLayout.closeDrawers();
                    }
    
                });
                getActionBar().setDisplayHomeAsUpEnabled(true);
                getActionBar().setHomeButtonEnabled(true);
            }
    

    欢迎任何想法和建议。 感谢。

    P.S。我找到了this question,据说使用导航抽屉是不可取的。

1 个答案:

答案 0 :(得分:2)

对于手机和平板电脑肖像模式,您可以使用以下布局:

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <include layout="@layout/fragments_layout" />
    <include layout="@layout/drawer_list" />
</android.support.v4.widget.DrawerLayout>

这可能是平板电脑横向模式的布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <include layout="@layout/drawer_list" />
    <include layout="@layout/fragments_layout" />
</LinearLayout>

这是drawer_list:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="260dp"
    android:layout_height="match_parent"
    android:layout_gravity="start" >

    <ListView
        android:id="@+id/drawer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:choiceMode="singleChoice"
        android:background="#111"
        android:divider="@android:color/darker_gray"
        android:dividerHeight="0dp" />

</RelativeLayout>

和fragments_layout:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <fragment android:id="+@id/master_fragment" />
    <fragment android:id="+@id/detail_fragment" />
</FrameLayout>

在活动中,您必须检查自己是否处于平板电脑横向模式并显示两个片段。否则,您将首先显示主片段,隐藏细节

如果导航抽屉在所有活动中都可见,请声明一个负责抽屉的抽象活动。然后,所有活动都继承自此抽象活动。您发布的BaseActivity是一个很好的例子