Всичко, което трябва да знаете, за да отключите Jetpack Compose Previews Потенциал като експерт

Jetpack Compose улеснява създаването на екрани с приложения за Android. Една от страхотните му функции е анотацията @Preview. Това ни позволява бързо да видим как изглеждат нашите проекти, без да стартираме пълното приложение. Но понякога трябва да виждаме дизайните си по различни начини, например в тъмен или светъл режим. Повтарянето на едни и същи настройки за всеки компонент може да доведе до объркване. Това е мястото, където идват персонализираните анотации.

1- Контекстно съобразени визуализации: светла и тъмна тема

Например повечето приложения в днешно време поддържат както светли, така и тъмни теми. Визуализацията на компонент в двете теми обикновено изглежда така:

@Preview(
    name = "Dark Mode",
    showBackground = true,
    uiMode = UI_MODE_NIGHT_YES
)
@Preview(
    name = "Light Mode",
    showBackground = true,
    uiMode = UI_MODE_NIGHT_NO
)
@Composable
fun PreviewListItem() {
    // Your Composable here
}

Това е част от кода, особено ако трябва да копирате това в множество компоненти. Нека капсулираме това с персонализирана анотация:

@Preview(name = "Dark Mode", showBackground = true, uiMode = UI_MODE_NIGHT_YES)
@Preview(name = "Light Mode", showBackground = true, uiMode = UI_MODE_NIGHT_NO)
annotation class ThemePreviews

@ThemePreviews
@Composable
fun PreviewsListItem() {
    MaterialTheme {
        Surface {
            PortraitListItem(
                imageResource = painterResource(id = R.drawable.task),
                text = "Write the `Preview` Medium article",
                isChecked = false,
                onCheckedChange = { }
            )
        }
    }
}

Можете да получите кода за PortraitListItem тук:
https://gist.github.com/mo0rti/260c35752a3718d54e774ab3ccbc0179

С @ThemePreviews визуализацията ви изглежда така:

2- Визуализации на ориентациите на устройството: портрет и пейзаж

Jetpack Compose обикновено показва визуализации в портретен стил нагоре-надолу. Но можете да направите своя компонент адаптивен както към Пейзаж, така и към Портрет. Можем да използваме LocalConfiguration контекст във функцията @Composable и да създадем компонент за адаптивно композиране.

@Composable
fun AdaptiveListItem(
    imageResource: Painter,
    text: String,
    isChecked: Boolean,
    onCheckedChange: (Boolean) -> Unit
) {
    val configuration = LocalConfiguration.current
    if (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        LandscapeListItem(imageResource, text, isChecked, onCheckedChange)
    } else {
        PortraitListItem(imageResource, text, isChecked, onCheckedChange)
    }
}

За следващата стъпка можете да създадете следната персонализирана анотация:

@Preview(name = "Landscape Mode", showBackground = true, device = Devices.AUTOMOTIVE_1024p, widthDp = 640)
@Preview(name = "Portrait Mode", showBackground = true, device = Devices.PIXEL_4)
annotation class OrientationPreviews

@OrientationPreviews
@Composable
fun OrientationPreviewsListItem() {
    MaterialTheme {
        Surface {
            AdaptiveListItem(
                imageResource = painterResource(id = R.drawable.task),
                text = "Write the `Preview` Medium article",
                isChecked = false,
                onCheckedChange = { }
            )
        }
    }
}

Използвах Devices.PIXEL_4 за показване в портретен режим и това е лично предпочитание. За пейзажен режим използвам Devices.AUTOMOTIVE_1024p и ограничена ширина от 640 DP, така че визуализацията ми може лесно да се види в инструментите за визуализация на Android Studio.

3- Прегледи на мащабиране на шрифта: Различни настройки за достъпност

@Preview(name = "Default Font Size", fontScale = 1f)
@Preview(name = "Large Font Size", fontScale = 1.5f)
annotation class FontScalePreviews

@FontScalePreviews
@Composable
fun FontScalePreviewsListItem() {
    // Your Composable here
}

Можете също така да комбинирате тази персонализирана анотация с нови параметри за визуализация:

@Preview(
    name = "Extra Large Font Size",
    fontScale = 2f
)
@FontScalePreviews
annotation class NewFontScalePreviews

@NewFontScalePreviews
@Composable
fun NewFontScalePreviewsListItem() {
    // Your Composable here
}

4- Предварителни прегледи на указания за оформление: LTR и RTL поддръжка за международни приложения

@Preview(name = "Left-To-Right", locale = "en")
@Preview(name = "Right-To-Left", locale = "ar")
annotation class LayoutDirectionPreviews

@LayoutDirectionPreviews
@Composable
fun LayoutDirectionPreviewsListItem() {
    // Your Composable here
}

5- Интерактивност с Preview

Интерактивният режим на Jetpack Compose ви позволява да взаимодействате с компоненти във визуализацията. Това е невероятно ценно за тестване на малки взаимодействия без внедряване на приложението:

@Composable
fun ListItemPreview() {
    var checkedState by remember { mutableStateOf(false) }

    PortraitListItem(
        imageResource = painterResource(id = R.drawable.task),
        text = "Write the `Preview` Medium article",
        isChecked = checkedState,
        onCheckedChange = { newState ->
            checkedState = newState
        }
    )
}

@Preview(showBackground = true, name = "Interactivity Preview")
@Composable
fun PreviewListItem() {
    ListItemPreview()
}

За да използвате интерактивен режим, първо трябва да стартирате режима:

След като сте в интерактивния режим, можете да визуализирате вашия компонент в различни състояния.

Забележка:Имайте предвид, че винаги трябва да спирате интерактивния режим, когато приключите с тестването, в противен случай няма да видите новите си промени във визуализацията. Можете да го намерите в горния ляв ъгъл на прозореца за визуализация.

6- Различни визуализации с различни данни

Визуализирането на това как вашите компоненти на потребителския интерфейс се адаптират към различни сценарии с данни е жизненоважно. С @PreviewParameter на Jetpack Compose тази задача става лесна. Нека разгледаме някои практически примери за това как можете да се възползвате максимално от тази функция!

а. Изпробване на различни дължини на текст

Някога чудили ли сте се как изглежда малък етикет или абзац, дълъг като есе, във вашия дизайн? Нека разберем.

class TextPreviewProvider : PreviewParameterProvider<String> {
    override val values = sequenceOf(
        "Short Text",
        "A bit longer text.",
        "This one is really, really long. Like, really long!"
    )
}

@Composable
@Preview
fun DifferentTextPreviewsListItem(
    @PreviewParameter(TextPreviewProvider::class) text: String
) {
    MaterialTheme {
        Surface {
            PortraitListItem(
                imageResource = painterResource(id = R.drawable.task),
                text = text,
                isChecked = false,
                onCheckedChange = { }
            )
        }
    }
}

Той генерира три визуализации поради последователността от низови стойности и визуализацията се появява, както следва:

b. Преглед на различни изображения

Изображенията се предлагат във всякакви форми и размери. Нека видим как различните изображения се вписват в нашия дизайн.

class ImagePreviewProvider : PreviewParameterProvider<Int> {
    override val values = sequenceOf(
        R.drawable.pic1,
        R.drawable.pic2,
        R.drawable.pic3
    )
}

° С. Превключване на теми

За приложения, предлагащи светъл и тъмен режим, можете да видите и двата в действие един до друг.

class ThemePreviewProvider : PreviewParameterProvider<Colors> {
    override val values = sequenceOf(LightColorPalette, DarkColorPalette)
}

д. Различни състояния на компоненти

От активно до зареждане, визуализирайте всяко състояние, в което вашият компонент може да бъде.

enum class ButtonState {
    Active, Disabled, Loading
}

@Composable
fun MyComposeButton(
    text: String,
    buttonState: ButtonState,
    onClick: () -> Unit
) {
    // ... Your code for the button goes here...
}

// You can preview the button with different states
class ButtonStateProvider : PreviewParameterProvider<ButtonState> {
    override val values = sequenceOf(
        ButtonState.Active,
        ButtonState.Disabled,
        ButtonState.Loading
    )
}

@Composable
@Preview
fun MyButtonPreview(
    @PreviewParameter(ButtonStateProvider::class) buttonState: ButtonState
) {
    MaterialTheme {
        Surface {
            MyComposeButton(
                text = "My Button",
                buttonState = buttonState,
                onClick = { }
            )
        }
    }
}

д. Фалшиви API данни

Въпреки че не можем да извлечем данни на живо във визуализации, можете да имитирате данни от своя API. По този начин вие винаги сте готови за това, което вашият API ви хвърля.

data class UserProfile(val name: String, val bio: String, val avatar: Int)

class UserProfileProvider : PreviewParameterProvider<UserProfile> {
    override val values = sequenceOf(
        UserProfile("Alice", "Loves hiking and coffee", R.drawable.avatar1),
        UserProfile("Bob", "Avid reader and tech enthusiast", R.drawable.avatar2),
        UserProfile("Charlie", "Just here for the memes", R.drawable.avatar3)
    )
}

@Composable
fun UserProfileComposable(profile: UserProfile) {
    // Your compose code is here
}

@Composable
@Preview
fun UserProfilePreview(
    @PreviewParameter(UserProfileProvider::class) userProfile: UserProfile
) {
    MaterialTheme {
        Surface {
            UserProfileComposable(profile = userProfile)
        }
    }
}

И визуализацията ви изглежда така:

Можете да ограничите количеството данни, които искате да показвате във визуализацията, като използвате параметъра limit:

@Composable
@Preview
fun LimitedUserProfilePreview(
    @PreviewParameter(UserProfileProvider::class, limit = 2) userProfile: UserProfile
) {
    MaterialTheme {
        Surface {
            UserProfileComposable(profile = userProfile)
        }
    }
}

f. Посока на оформлението:

Докато изграждате компоненти на потребителския интерфейс, важно е да визуализирате как биха изглеждали в двата режима Ltr и Rtl.

object DirectionPreviews : PreviewParameterProvider<LayoutDirection> {
    override val values: Sequence<LayoutDirection> = sequenceOf(
        LayoutDirection.Ltr,
        LayoutDirection.Rtl
    )
}

@Preview
@Composable
fun PreviewWithDirection(
    @PreviewParameter(DirectionPreviews::class) direction: LayoutDirection
) {
    CompositionLocalProvider(LocalLayoutDirection provides direction) {
        MaterialTheme {
            Surface {
                PortraitListItem(
                    imageResource = painterResource(id = R.drawable.task),
                    text = "Write the `Preview` Medium article",
                    isChecked = false,
                    onCheckedChange = { }
                )
            }
        }
    }
}

Последен случай на употреба: Нека комбинираме всички заедно

Да предположим, че създавате приложение, което трябва да се погрижи за множество ориентации на екрана, и бихте искали да имате визуализация с динамичен списък с данни, за да симулирате използване в реалния свят. С комбинация от OrientationPreviews и PreviewParameter,можете да постигнете това.

@Composable
@OrientationPreviews
fun DifferentTextPreviewsListItem(
    @PreviewParameter(TextPreviewProvider::class) text: String
) {
    MaterialTheme {
        Surface {
            PortraitListItem(
                imageResource = painterResource(id = R.drawable.baseline_assignment_turned_in_24),
                text = text,
                isChecked = false,
                onCheckedChange = { }
            )
        }
    }
}

Визуализацията ви изглежда така:

Можете да правите по-сложни визуализации, като комбинирате различни анотации за визуализация:

@Composable
@OrientationPreviews
@LayoutDirectionPreviews
fun DifferentTextPreviewsListItem(
    @PreviewParameter(TextPreviewProvider::class) text: String
) {
    // Your preview code here
}

Обзалагам се, че можете да познаете резултата сега 😄

Това е опаковка! С Jetpack Compose Preview и @PreviewParameter, вие сте готови да създавате перфектни като пиксел дизайни, независимо от данните.

Хареса ли ви тази статия? Не забравяйте да споделите!

Приятно композиране!