【发布时间】:2015-03-05 12:37:05
【问题描述】:
我知道以前可能有人问过这个问题,但由于我没有找到任何令人信服的东西,所以我需要再问一次。
当您还需要提供某种导航面板(例如Navigation Drawer)时,如何设计一款支持平板电脑和手机的应用?
我想我知道如何在手机和平板电脑上纵向使用导航抽屉,因为在这些情况下,我当时只显示一个窗格(片段)。但是对于横向的平板电脑,情况就完全不同了,因为您有足够的空间来显示至少两个窗格(片段),大多数情况下这些窗格(片段)将以一种“主要细节”的方式关联。
编辑#1
只是为了提供一些关于我的应用程序的上下文以及为什么我认为我需要在导航列表之外有两个窗格。
我的应用程序的主要目标是帮助服务员接受订单,这种情况在以下情况下发挥作用:
服务员从左侧菜单(最好是导航抽屉)中选择“接受订单”选项
在我称之为“内容部分”的第一部分(左窗格)中,服务员可以在他们提供的所有食物类别中进行选择(假设它是顶部的列表)并根据选择的类别,下面的列表显示了该特定类别下的所有菜肴。
服务员点击菜品后,会自动将其添加到“内容部分”的第二部分(右窗格)
知道导航抽屉的基本结构是这样的:
<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 来处理抽屉的所有功能,并且我的所有活动都可以从它继承。
这是我目前得到的:
基础活动
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(drawer) 的 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,但对ListView 使用include 标签
<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);
}
欢迎任何想法和建议。 谢谢。
附:我找到了this question,其中说不建议使用导航抽屉。
【问题讨论】:
-
“你如何处理“横向平板电脑”场景?”——嗯,用水平的
LinearLayout替换FrameLayout来容纳你的两个窗格?我看不到导航抽屉如何影响问题。您会以与没有导航抽屉相同的方式实现主/详细信息。您甚至可以将LinearLayout放在一个单独的XML 文件中,然后使用<include>将其与DrawerLayout一起放在整体布局中,如果它有助于您可视化问题。 -
“服务员从左侧菜单中选择“接受订单”选项(最好是导航抽屉)”——根据 Google 的设计指南,导航抽屉中的条目应该是名词,而不是动词/命令. “我想我必须使用导航列表之外的两个窗格”——是的,但是这些窗格可以包含在水平的
LinearLayout中。DrawerLayout是什么限制你在 UI 中除了抽屉之外有一个“单一”的东西,但它没有说明那个“单一”的东西里面有什么。如果你想要两个FrameLayouts,去吧。 -
当您不需要时,您可以随时通过
setVisibilitity(View.GONE)让其中一个窗格消失。如果您正确使用权重,则另一个窗格将展开以填充所有可用空间。 -
“那么最终将 DrawerLayout 用于手机和平板电脑(任何方向)是否完全可行?” - 当然。 “你会用那种方法吗?” - 不适用于您描述的用法,因为我会坚持使用更符合 Google 设计指南(名词,而不是动词)的抽屉。但是,除此之外,当然。
-
我怀疑很多抽屉式导航应用都采用“单一活动”模型,其中一切都只是来来往往的片段,而不是在活动之间转换。话虽如此,如果您的方法令您满意,我认为您的方法没有特别的问题。
标签: android android-fragments navigation-drawer android-ui landscape-portrait