Ящик для навигации: устанавливается, как всегда открыт на планшетах
Я использую шаблон Navigation Drawer из библиотеки поддержки:
http://developer.android.com/training/implementing-navigation/nav-drawer.html
Я пытался установить его как всегда открытое на планшете (в качестве бокового меню)
![enter image description here]()
Это что-то возможно с текущей реализацией или нам нужно создать новый макет и новую структуру с Listview вместо повторного использования одного и того же кода?
Ответы
Ответ 1
Основываясь на идее, что более крупные устройства могут иметь разные файлы макета, я создал следующий проект.
https://github.com/jiahaoliuliu/ABSherlockSlides
Highlights
Поскольку выдвижной ящик большого устройства всегда виден, нет необходимости иметь ящик. Вместо этого достаточно 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>
Поскольку у меня нет ящика в файле макета, когда приложение пытается найти элемент в макете, оно возвращает 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);
}
Вот пример использования boolean.
@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);
}
}
Ответ 2
На основе ответа CommonsWare вы можете сделать это с помощью нескольких настроек. Первый устанавливает следующие три строки:
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);
drawerLayout.setScrimColor(getResources().getColor(R.color.drawerNoShadow));
isDrawerLocked = true;
Цвет drawerNoShadow может быть просто не-альфа-цветом (например, 0x00000000). Это дает вам открытый ящик без фонового наложения.
Второе, что вам нужно сделать, - это отрегулировать значение padding_left для вашего FrameLayout. Для этого вы можете настроить измерение для управления этим (по умолчанию 0dp) - в этом примере R.dimen.drawerContentPadding. Вам также понадобится значение R.dimen.drawerSize, которое будет шириной DrawerLayout.
Это позволяет вам проверить значение paddingLeft для FrameLayout для вызова этих строк.
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 для устройств, на которые вы хотите настроить таргетинг, и единственное, что вам нужно изменить, это значение drawerContentPadding и предлагать измененные макеты.
ПРИМЕЧАНИЕ. Я закончил использование margin_left вместо padding_left, так как в новом макете он накладывает ящик. См. Более подробное сообщение в блоге об этой технике в http://derekrwoods.com/2013/09/creating-a-static-navigation-drawer-in-android/
Ответ 3
Попробуйте setDrawerLockMode()
, чтобы заблокировать ящик на широкоэкранных устройствах.
Как я уже отмечал в комментарии, я не думаю, что DrawerLayout
предназначен для вашего сценария (хотя это не плохая идея, IMHO). Либо используйте другой макет, на котором размещен один и тот же ListView
и контент, или, возможно, загрузите и измените собственную копию DrawerLayout
, которая на больших экранах будет перемещать контент поверх него, а не перекрывать его.
Ответ 4
Просто предоставьте альтернативный файл макета для планшетов. Таким образом, вы можете сохранить все поведения по умолчанию NavigationView
.
Шаг 1
Просто создайте альтернативный файл макета, аналогичный этому для планшетных устройств, и поместите его в layout-w600dp-land
ресурсов 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
Добавьте следующее содержимое в новый файл ресурсов значений в каталоге значений и назовите его 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);
}
Конечный результат будет выглядеть примерно так.
![output]()
Ответ 5
на основе примера кода @methodin и идеи @CommonsWare
Вот полный код (только для студии Android)
Ответ 6
Предыдущие ответы хороши, но я сталкивался с некоторыми проблемами при их реализации в своем проекте, поэтому хочу поделиться своим решением.
Прежде всего, нам нужно определить пользовательский ящик:
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 в портретной ориентации и около 300dp в ландшафте для NavigationView (вычислено эмпирически). Мы определяем их в соответствующих папках values
:
values/dimens.xml
-
<dimen name="content_padding">0dp</dimen>
values-land/dimens.xml
-
<dimen name="content_padding">300dp</dimen>
Наконец, мы блокируем ящик в активности:
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;
}