Выдвижная панель ModalNavigationDrawer

Последнее обновление: 02.04.2024

Компонент ModalNavigationDrawer предназначен для создания приложений с выдвижной панелью, где часто располагается какое-нибдуь меню. Этот компонент имеет следующее определение:

@Composable
fun ModalNavigationDrawer(
    drawerContent: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
    gesturesEnabled: Boolean = true,
    scrimColor: Color = DrawerDefaults.scrimColor,
    content: @Composable () -> Unit
): Unit

Параметры компонента:

  • drawerContent: содержимое выдвижной панели

  • modifier: применяемыек компоненты модификаторы

  • drawerState: состояние в виде объекта DrawerState. Для определения состояния применяется функция rememberDrawerState(), в которую передается передается начальное состояние панели с помощью констант перечисления DrawerValue: DrawerValue.Closed (панель закрыта) и DrawerValue.Open (панель открыта) По умолчанию равно rememberDrawerState(DrawerValue.Closed)

  • gesturesEnabled: указывает, может или нет панель управляться касаниями. По умолчанию равно true

  • scrimColor: цвет, который скрывает основное содержимое приложения, когда панель открыта

  • content: остальное содержимое приложения

Первый и последний параметр являются обязательными и в реальности они могут представлять любые компоненты. Например, определим простейшую выдвижную панель:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Text
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.sp

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ModalNavigationDrawer(
                drawerContent = { Text("Drawer", fontSize = 22.sp, color=Color.LightGray)  },
                scrimColor = Color.DarkGray,
                content={Text("Main Content", fontSize = 28.sp)})
        }
    }
}

Здесь и содержимое выдвижной панели, и содержимое основной части приложения представлено компонентом Text. При выдвижении панели основной контент закрашивается сервым цветом - то есть фактически это цвет самой панели. В данном случае по умолчанию панель не видна, и для ее выдвижения надо провести пальцем от левой стороны приложения вправо:

ModalNavigationDrawer в Jetpack Compose и Kotlin на Android

Состояние DrawerState

Выдвижная панель может быть открыта или закрыта. Соответственно компонент может находиться в двух состояниях. Для хранения состояния применяется класс DrawerState. Внутри этого класса для хранения состояния применяется перечисление DrawerValue. В частности, оно имеет два значения: DrawerValue.Closed (панель закрыта) и DrawerValue.Open (панель открыта).

Для создания и управления состояния DrawerState применяется функция rememberDrawerState():

@Composable
fun rememberDrawerState(
    initialValue: DrawerValue,
    confirmStateChange: (DrawerValue) -> Boolean = { true }
): DrawerState

В качестве обязательного параметра она принимает значение DrawerValue, которое указывает, открыта или закрыта панель по умолчанию. Для получения информации о состоянии DrawerState предоставляет ряд свойств:

  • currentValue: текущее состояние в виде значения DrawerValue

  • isClosed: если равно true, то выдвижная панель скрыта

  • isOpen: если равно true, то выдвижная панель раскрыта

Кроме того, DrawerState предоставляет ряд методов для управления состоянием:

  • open(): раскрывает выдвижную панель

  • close(): скрывает выдвижную панель

Обе эти функции являются suspend-функциями. Например, используем эти функции для программного управления состоянием панели:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val drawerState = rememberDrawerState(DrawerValue.Closed)
            val scope = rememberCoroutineScope()
            ModalNavigationDrawer(
                drawerState = drawerState,
                drawerContent = { Text("Menu", fontSize = 22.sp, color=Color.LightGray) },
                scrimColor = Color.DarkGray,
                content={
                    IconButton(onClick = {
                        scope.launch {drawerState.open()} }) {
                        Icon(Icons.Filled.Menu, "Меню")
                    }
                }
            )
        }
    }
}

Сначала определяем контекст DrawerState и контекст корутины для запуска suspend-функций:

val drawerState = rememberDrawerState(DrawerValue.Closed)
val scope = rememberCoroutineScope()

Для связи этого состояния с ModalNavigationDrawer определяем параметр drawerState:

ModalNavigationDrawer(
    drawerState = drawerState,
    ..........................

Затем через параметр content определяем кнопку-иконку меню, по нажатию на которую будет раскрываться панель с помощью выполнения метода drawerState.open():

content={
    IconButton(onClick = {
        scope.launch {drawerState.open()} }) {
        Icon(Icons.Filled.Menu, "Меню")
    }
}
DrawerState и ModalNavigationDrawer в Jetpack Compose и Kotlin на Android

Также здесь мы могли бы сделать более тонкое управление состоянием - открывать панель, когда она скрыта, и скрывать ее, когда она раскрыта:

scope.launch {
    if(drawerState.isClosed) drawerState.open()
    else drawerState.close()
}

Обработка выбора

Нередко на подобных выдвижных панелях размещается некоторое меню. Определим подобное меню и определим обработку выбора его пунктов:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberDrawerState

import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val items = listOf("Home", "Contact", "About")
            val selectedItem = remember { mutableStateOf(items[0]) }
            val drawerState = rememberDrawerState(DrawerValue.Closed)
            val scope = rememberCoroutineScope()
            ModalNavigationDrawer(
                drawerState = drawerState,
                drawerContent = {
                    Column {
                        items.forEach { item ->
                            TextButton(onClick = {
                                    scope.launch { drawerState.close() }
                                    selectedItem.value = item
                                },
                                colors = ButtonDefaults.buttonColors(contentColor = Color.LightGray, containerColor = Color.Transparent)) {
                                Text(item, fontSize = 22.sp)
                            }
                        }
                    }
                },
                scrimColor = Color.DarkGray,
                content={
                    Column {
                        IconButton(onClick = {
                            scope.launch {drawerState.open()}
                        }, content = {
                            Icon(Icons.Filled.Menu, "Меню")
                        })
                        Text(selectedItem.value, fontSize = 28.sp)
                    }
                }
            )
        }
    }
}

В данном случае все пункты меню определены в массиве items. Для хранения выбранного пункта определена переменная selectedItem.

При наполнении выдвижной панели компонентами создаем для каждого элемента в items компонент TextButton. При нажатии на такую кнопку в переменную selectedItem будет передаваться нажатый элемент.

drawerContent = {
    Column {
        items.forEach { item ->
            TextButton(onClick = {
                    scope.launch { drawerState.close() }
                    selectedItem.value = item
                },
                colors = ButtonDefaults.buttonColors(contentColor = Color.LightGray, containerColor = Color.Transparent)) {
                Text(item, fontSize = 22.sp)
            }
        }
    }
},

В основном содержимом приложения отображаем выбранный пункт:

content={Text(selectedItem.value, fontSize = 28.sp)}
меню в ModalNavigationDrawer в Jetpack Compose и Kotlin на Android

ModalDrawerSheet

Для настройки отображения и поведения элементов на выдвижной панели применяется компонент ModalDrawerSheet

@Composable
fun ModalDrawerSheet(
    modifier: Modifier = Modifier,
    drawerShape: Shape = DrawerDefaults.shape,
    drawerContainerColor: Color = DrawerDefaults.modalContainerColor,
    drawerContentColor: Color = contentColorFor(drawerContainerColor),
    drawerTonalElevation: Dp = DrawerDefaults.ModalDrawerElevation,
    windowInsets: WindowInsets = DrawerDefaults.windowInsets,
    content: @Composable ColumnScope.() -> Unit
): Unit

Он использует следующие параметры:

  • modifier: функции модификатора, которые применются к компоненту

  • drawerShape: форма компонента в виде объекта Shape

  • drawerContainerColor: цвет контейнера

  • drawerContentColor: цвет содержимого

  • drawerTonalElevation: эффект анимации при нажатии на элементы компонента

  • windowInsets: отступы от границ контейнера

  • content: содержимое выдвижной панели

Применим ModalDrawerSheet:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Row
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.Text
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberDrawerState

import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val items = listOf("Home", "Contact", "About")
            val selectedItem = remember { mutableStateOf(items[0]) }
            val drawerState = rememberDrawerState(DrawerValue.Closed)
            val scope = rememberCoroutineScope()
            ModalNavigationDrawer(
                drawerState = drawerState,
                drawerContent = {
                    ModalDrawerSheet{
                        items.forEach { item ->
                            TextButton(
                                onClick = {
                                    scope.launch { drawerState.close() }
                                    selectedItem.value = item
                                },
                            ) { Text(item, fontSize = 22.sp) }
                        }
                    }
                },
                content={
                    Row{
                        IconButton(onClick = {scope.launch {drawerState.open()}},
                            content = { Icon(Icons.Filled.Menu, "Меню") }
                        )
                        Text(selectedItem.value, fontSize = 28.sp)
                    }
                }
            )
        }
    }
}
ModalDrawerSheet в ModalNavigationDrawer в Jetpack Compose и Kotlin на Android

Как видно, ModalDrawerSheet имеет некоторую стандартную стилизацию и упрощает создание содержимого. И мы можем помещать в ModalDrawerSheet любые компоненты, как в данном случае компоненты TextButton. Тем не менее для определения элементов в ModalDrawerSheet в Jetpack Compose предназначен специальный компонент - NavigationDrawerItem:

@Composable
fun NavigationDrawerItem(
    label: @Composable () -> Unit,
    selected: Boolean,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    icon: (@Composable () -> Unit)? = null,
    badge: (@Composable () -> Unit)? = null,
    shape: Shape = NavigationDrawerTokens.ActiveIndicatorShape.value,
    colors: NavigationDrawerItemColors = NavigationDrawerItemDefaults.colors(),
    interactionSource: MutableInteractionSource? = null
): Unit

Отмечу основные параметры компонента:

  • label: определяет содержимое

  • selected: указывает, будет ли выделен элемент (при значении true)

  • onClick: обработчик нажатия на компонент

  • colors: устанавливает цвета. Для установки цветов можно использовать функцию NavigationDrawerItemDefaults.colors()

    @Composable
    fun colors(
        selectedContainerColor: Color = NavigationDrawerTokens.ActiveIndicatorColor.value,
        unselectedContainerColor: Color = Color.Transparent,
        selectedIconColor: Color = NavigationDrawerTokens.ActiveIconColor.value,
        unselectedIconColor: Color = NavigationDrawerTokens.InactiveIconColor.value,
        selectedTextColor: Color = NavigationDrawerTokens.ActiveLabelTextColor.value,
        unselectedTextColor: Color = NavigationDrawerTokens.InactiveLabelTextColor.value,
        selectedBadgeColor: Color = selectedTextColor,
        unselectedBadgeColor: Color = unselectedTextColor
    ): NavigationDrawerItemColors
    

    Она устанавливает следующие цвета:

    • selectedContainerColor: цвет фона выбранного элемента

    • unselectedContainerColor: цвет фона невыбранного элемента

    • selectedIconColor: цвет иконки выбранного элемента

    • unselectedIconColor: цвет иконки невыбранного элемента

    • selectedTextColor: цвет текста выбранного элемента

    • unselectedTextColor: цвет текста невыбранного элемента

    • selectedBadgeColor: цвет значка выбранного элемента

    • unselectedBadgeColor: цвет значка невыбранного элемента

Применим NavigationDrawerItem для создания элементов выдвижной панели:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Row
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.Text
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.material3.rememberDrawerState

import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val items = listOf("Home", "Contact", "About")
            val selectedItem = remember { mutableStateOf(items[0]) }
            val drawerState = rememberDrawerState(DrawerValue.Closed)
            val scope = rememberCoroutineScope()
            ModalNavigationDrawer(
                drawerState = drawerState,
                drawerContent = {
                    ModalDrawerSheet{
                        items.forEach { item ->
                            NavigationDrawerItem(
                                label= { Text(item, fontSize = 22.sp) },
                                selected = selectedItem.value==item,
                                onClick = {
                                    scope.launch { drawerState.close() }
                                    selectedItem.value = item
                                }
                            )
                        }
                    }
                },
                content={
                    Row{
                        IconButton(onClick = {scope.launch {drawerState.open()}},
                            content = { Icon(Icons.Filled.Menu, "Меню") }
                        )
                        Text(selectedItem.value, fontSize = 28.sp)
                    }
                }
            )
        }
    }
}
NavigationDrawerItem в ModalNavigationDrawer в Jetpack Compose и Kotlin на Android

Аналогично можно настроить цветовую гамму для выдвижной панели:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Row
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.Text
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.material3.NavigationDrawerItemDefaults
import androidx.compose.material3.rememberDrawerState

import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val items = listOf("Home", "Contact", "About")
            val selectedItem = remember { mutableStateOf(items[0]) }
            val drawerState = rememberDrawerState(DrawerValue.Closed)
            val scope = rememberCoroutineScope()
            ModalNavigationDrawer(
                drawerState = drawerState,
                drawerContent = {
                    ModalDrawerSheet(
                        drawerContainerColor = Color.DarkGray,
                        drawerContentColor =  Color.LightGray
                    ) {
                        items.forEach { item ->
                            NavigationDrawerItem(
                                label= { Text(item, fontSize = 22.sp) },
                                selected = selectedItem.value==item,
                                onClick = {
                                    scope.launch { drawerState.close() }
                                    selectedItem.value = item
                                },
                                colors = NavigationDrawerItemDefaults.colors(
                                    selectedContainerColor = Color.Transparent,
                                    unselectedContainerColor = Color.Transparent,
                                    selectedTextColor = Color.White,
                                    unselectedTextColor = Color.LightGray
                                )
                            )
                        }
                    }
                },
                content={
                    Row{
                        IconButton(onClick = {scope.launch {drawerState.open()}},
                            content = { Icon(Icons.Filled.Menu, "Меню") }
                        )
                        Text(selectedItem.value, fontSize = 28.sp)
                    }
                }
            )
        }
    }
}
цвета в ModalNavigationDrawer в Jetpack Compose и Kotlin на Android
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850