Подтвердить что ты не робот

Конструкция материала: ширина ящика 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?

4b9b3361

Ответ 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); 


            }
        }

    }
    );