Конструкция материала: ширина ящика Nav
В соответствии с спецификациями Material Design ширина навигационного ящика на мобильных устройствах должна быть
side nav width = ширина экрана - высота строки приложения
Как это реализовать на android?
У меня есть два частичных решения. Во-первых, это хакерский способ: в содержательной деятельности я помещаю этот код:
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR1) {
final Display display = getWindowManager().getDefaultDisplay();
final Point size = new Point();
display.getSize(size);
final ViewGroup.LayoutParams params = mDrawerFragment.getView().getLayoutParams();
params.width = size.x - getResources().getDimensionPixelSize(
R.dimen.abc_action_bar_default_height_material
);
mFragmentUserList.getView().setLayoutParams(params);
}
Это, однако, вызывает второй цикл компоновки и не работает в пряниках: это не оптимально.
Второе решение включает в себя добавление пробела между фрагментом и drawerLayout. Однако он смещает тень и место, где пользователь может нажать, чтобы вернуться в основное приложение. Он также падает, когда нажимается значок "hamburguer". Не оптимально.
Есть ли лучшее решение, желательно одно, которое включает стили и xml?
Ответы
Ответ 1
При помощи Android Design Support Library теперь очень просто реализовать навигационный ящик, включая правильный размер. Используйте NavigationView и используйте его возможность сделать вывод из ресурса меню (пример здесь) или вы можете просто обернуть его вокруг вида, которое вы в настоящее время используете для отображения списка ящиков (например, ListView, RecyclerView). Затем NavigationView позаботится о размере ящика для вас.
Вот пример того, как я использую NavigationView, обернутый вокруг ListView:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/navdrawer_layout"
android:fitsSystemWindows="true">
<!-- Layout where content is shown -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include android:id="@+id/toolbar"
layout="@layout/toolbar" />
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/toolbar" />
<!-- Toolbar shadow for pre-lollipop -->
<View style="@style/ToolbarDropshadow"
android:layout_width="match_parent"
android:layout_height="3dp"
android:layout_below="@id/toolbar" />
</RelativeLayout>
<android.support.design.widget.NavigationView
android:id="@+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start">
<ListView
android:id="@+id/navdrawer_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:choiceMode="singleChoice"
android:dividerHeight="0dp"
android:divider="@null"/>
</android.support.design.widget.NavigationView>
</android.support.v4.widget.DrawerLayout>
Таким образом вы можете использовать настройку NavigationViews и по-прежнему использовать свой собственный список ящиков. Хотя намного проще сделать список ящиков из ресурсов меню (пример здесь), вы не можете использовать пользовательские представления для элементов списка.
Ответ 2
Мне удалось создать решение, используя объявления стиля XML, но оно немного взломанно. Мой подход состоял в том, чтобы использовать поля вместо применения заданной ширины, чтобы избежать написания какого-либо кода для вычисления макета вручную. Я создал основное правило стиля, чтобы подчеркнуть, как это сделать.
К сожалению, DrawerLayout в настоящее время применяет минимальный запас 64dp. Для этого подхода к работе нам необходимо компенсировать это значение с отрицательными полями, чтобы мы могли получить желаемую ширину для навигационного ящика. Надеюсь, это может быть разрешено в будущем (Кто-то задал вопрос об этом), поэтому мы можем просто ссылаться на ссылку измерения abc_action_bar_default_height_material
для поля.
Выполните следующие действия:
-
Добавьте следующие определения размеров и стилей:
значения/dimens.xml
<!-- Match 56dp default ActionBar height on portrait orientation -->
<dimen name="nav_drawer_margin_offset">-8dp</dimen>
значения-земля/dimens.xml
<!-- Match 48dp default ActionBar height on landscape orientation -->
<dimen name="nav_drawer_margin_offset">-16dp</dimen>
значения/styles.xml
<!-- Nav drawer style to set width specified by Material Design specification -->
<style name="NavDrawer">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_marginRight">@dimen/nav_drawer_margin_offset</item>
</style>
значения-sw600dp/styles.xml
<!-- Margin already matches ActionBar height on tablets, just modify width -->
<style name="NavDrawer">
<item name="android:layout_width">320dp</item>
<item name="android:layout_marginRight">0dp</item>
</style>
-
После того, как вы добавили вышеприведенные правила в свой проект, вы можете ссылаться на стиль NavDrawer в представлении ящика навигации:
layout/navigation_drawer.xml(или другое подходящее представление, используемое для вашего навигационного ящика)
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/navigation_drawer"
style="@style/NavDrawer"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="@android:color/transparent"
android:dividerHeight="0dp"
android:background="#FFF"
/>
Ответ 3
После поиска более простого решения я нашел очень разъясняющую статью: Размер рамки материала для навигации.
Здесь:
Экран Nexus 5 - это хороший 640 x 360 dp (xxhdpi) и панель приложений на это 56 dp tall. Таким образом, навигационный ящик должен быть:
[ширина в dp] = 360dp - 56dp = 304dp
A Nexus 4 поддерживает 640 x 384 dp (xhdpi) экран вместо. Та же самая высота панели приложения 56dp. Его навигационный ящик?
[ширина в dp] = 384dp - 56dp = 328dp
Итак, как дизайнеры Google придумали 288dp и 304dp, соответственно? Понятия не имею.
Приложения Google, на с другой стороны, похоже, согласен с моей математикой. О, и вы знаете, что Самое забавное во всем этом? IPhone (который имеет другой экран высота, но постоянная ширина 320 дп) правильно помечена как имеющая 264dp nav drawer.
В основном, это показывает, что некоторые рекомендации относительно ящика навигации противоречат сами себе и что вы можете использовать следующее правило, чтобы избежать вычислений:
В основном вы можете использовать 304dp на -sw360dp
и 320dp на -sw384dp для вашего навигационного ящика, и вы получите все правильно.
Ответ 4
Они уже обновили спецификации, теперь ширина ящика навигации:
Math.min(screenWidth — actionBarSize, 6 * actionBarSize);
Ответ 5
Ну, мне это очень сложно понять и реализовать. К сожалению, решение Мэтью сделало мой навигационный ящик слишком широким для пейзажа, и это противоречило практике Google, то есть ширина навигатора определяется наименьшей шириной устройства. В любом случае это не сработало бы в моем случае, поскольку я отключил конфигурацию в своем манифесте. Поэтому я решил изменить динамическую ширину навигатора следующим кодом.
Следует отметить, что мое приложение предназначено только для телефонов, и я установил максимальную ширину 320dp. Именно поэтому я установил высоту панели инструментов 56dp для обеих ориентаций.
Надеюсь, что это поможет кому-то, и что они могут избежать ненужного стресса, который он вызвал.
navDrawLayout.post(new Runnable()
{
@Override
public void run() {
Resources r = getResources();
DisplayMetrics metrics = new DisplayMetrics();
if(r.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int height = metrics.heightPixels;
float screenWidth = height / r.getDisplayMetrics().density;
float navWidth = (screenWidth - 56);
navWidth = Math.min(navWidth, 320);
int newWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, navWidth, r.getDisplayMetrics());
DrawerLayout.LayoutParams params = (DrawerLayout.LayoutParams) navDrawLayout.getLayoutParams();
params.width = newWidth;
navDrawLayout.setLayoutParams(params);
}
if(r.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int width = metrics.widthPixels;
float screenWidth = width / r.getDisplayMetrics().density;
float navWidth = screenWidth - 56;
navWidth = Math.min(navWidth, 320);
int newWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, navWidth, r.getDisplayMetrics());
DrawerLayout.LayoutParams params = (DrawerLayout.LayoutParams) navDrawLayout.getLayoutParams();
params.width = newWidth;
navDrawLayout.setLayoutParams(params);
}
}
}
);
Ответ 6
Ширина навигационного ящика зависит от наименьшей ширины, ориентации и версии Android.
Ознакомьтесь с этой статьей о размерном навигационном ящике.