Jetpack Compose UI preview

Android development Jetpack_Compose_2 UI preview @Preview

Foreword

Before learning how jetpack compose writes ui, I think we should first understand the UI preview @Preview that works with Android studio. In this way, you can see the UI effect immediately, which is convenient for subsequent learning and verification code.

Required dependencies

 implementation "androidx.compose.ui:ui:1.2.1" //ui base library - important
    implementation "androidx.compose.ui:ui-tooling:1.2.1" //ui tool base library - important
    implementation "androidx.compose.foundation:foundation:1.2.1" //Basic library - important
    implementation "androidx.activity:activity-compose:1.5.1" //Basic library used with activity - important
    implementation "androidx.compose.ui:ui-tooling-preview:1.2.1" //Preview the basic library of ui in Android studio

A simple first look preview

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            //Add MyText to Activity
            MyText()
        }
    }

    @Preview //Adding @Preview to the method indicates that we need to preview the View in this method
    @Composable //Add @Composable to the method. On the surface, this is the ui method of compose. Only after adding it can the View of compose be added to the method
    fun MyText(){
        Text(text = "Hello World", color = Color. White)
    }
}

Preview in Android studio

According to the conditions, select the UI effect running on the Android Studio preview effect and the device

The key to judge in the following code is LocalInspectionMode.current. Used to distinguish the effect on Android Studio or on the device. The actual meaning in the project is to fill in some simulated data to facilitate viewing the preview ui effect. For example, set a placeholder for the Image or fill the list with some simulated data.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyText()
        }
    }

    @Preview
    @Composable
    fun MyText(){
        if (LocalInspectionMode. current){
            //Display the preview ui in Android studio
            Text(text = "android studio preview", color = Color. Red)
        } else {
            //The ui actually displayed on the device
            Text(text = "device run ui", color = Color. Red)
        }
    }
}

Effect picture on Android studio:

Effect picture on the device:

Quick deployment preview

Preview supports rapid deployment preview. The pain point that this function solves is that when we need to preview some pages with deep UI layers, we have to click on the device one by one to jump to the target page to view the UI effect every time. This is particularly time-consuming and labor-intensive. Now you only need to click as shown in the figure below to immediately display the current page on the device.

This function can be said to be half a hot load…

Possible errors

This may be a bug in Android Studio. Now every time you use this function, you need to build a preview. Click

Build and compile and then click on the device icon above.

A handy detail

After you have used the quick deployment preview function once in an activity, a preview file option will be automatically generated as shown in the figure below. In the future, just select a preview of a compose method and click Run to quickly deploy the preview on the device.

Another note! You need to clean up some invalid previews in time, so as to prevent creating a new preview with the same name, but prompting that the file cannot be found when trying to run, as shown in the following figure:

Interactive mode

Interactive mode lets you interact with the preview in a similar way to how you interact on a device. The interactive mode is isolated in a sandboxed environment (isolated from other previews), where you can click elements in the preview and enter user input; the preview can even play animations. Using this pattern, you can quickly test different states and gestures of composable items, such as checking or clearing a checkbox.

The preview interactive mode runs directly in Android Studio, not the emulator, so there are some limitations:

  • can’t access the network
  • can’t access file

The specific opening operation and experience are as follows:

Explanation of attributes of @Preview

name

The meaning of adding name is to increase the name distinction on the preview page. It can be quickly distinguished when there are multiple Compose methods that need to be previewed. In addition, preview @Preview can preview a Compose method with multiple combinations at the same time (such as creating the display effects of tablet devices and mobile devices at the same time, or the display effects of different text scaling ratios), so as to distinguish the preview of the corresponding configuration. The combination preview will be explained in detail below, here is just a reference to emphasize the meaning of the name. Finally, it is recommended that you must add a name to each @Preview.

Refer to the picture:

group

The function of group is to group several Compose methods, and after grouping, you can choose to display only the View of a certain group. Google’s official documentation really doesn’t say anything about this, which makes people speechless.

@Preview(name = "Text 1", group = "Text Group 1")
@Composable
fun MyText1() {
    Text(text = "Text 1", color = Color. White)
}

@Preview(name = "Text 2", group = "Text Group 2")
@Composable
fun MyText2() {
    Text(text = "Text 2", color = Color.Yellow)
}

@Preview(name = "Text 3", group = "Text Group 2")
@Composable
fun MyText3() {
    Text(text = "Text 3", color = Color.Red)
}

The effect is as follows:

widthDp and heightDp

Set the container size of the preview on the Android studio preview ui

@Preview(name = "Text 1", widthDp = 50, heightDp = 100)
@Composable
fun MyText1() {
    Text(text = "Text 1", color = Color. White)
}

@Preview(name = "Text 2", widthDp = 100, heightDp = 50)
@Composable
fun MyText2() {
    Text(text = "Text 2", color = Color. White)
}

Renderings:

locale

Set language-region display preview on Android studio preview ui

@Preview(name = "Text 1", locale = "en")
@Composable
fun MyText1() {
    Text(text = stringResource(id = R. string. hello_world), color = Color. White)
}

@Preview(name = "Text 2", locale = "zh")
@Composable
fun MyText2() {
    Text(text = stringResource(id = R. string. hello_world), color = Color. White)
}

Renderings:

fontScale

Set text scaling on Android studio preview ui

@Preview(name = "Text 1", fontScale = 0.8f)
@Composable
fun MyText1() {
    Text(text = stringResource(id = R. string. hello_world), color = Color. White)
}

@Preview(name = "Text 2", fontScale = 1.5f)
@Composable
fun MyText2() {
    Text(text = stringResource(id = R. string. hello_world), color = Color. White)
}

Renderings:

showSystemUi

The status bar and operation bar of the device are displayed on the Android studio preview ui

@Preview(name = "Text 1", showSystemUi = true)
@Composable
fun MyText1() {
    Text(text = stringResource(id = R. string. hello_world), color = Color. Black)
}

Renderings:

backgroundColor and showBackground

backgroundColor is usually used in combination with showBackground, one is to set the Android studio preview ui background color, and the other is to enable or disable the background display

@Preview(name = "Text 1", showBackground = true, backgroundColor = 0xFFF44336)
@Composable
fun MyText1() {
    Color(0xFFF44336)
    Text(text = stringResource(id = R. string. hello_world), color = Color. White)
}

Effect animation:

uiMode

Set Android studio preview ui mode, such as night mode

 /**
     * UI_MODE_TYPE_UNDEFINED undefined
     * UI_MODE_TYPE_NORMAL normal mode
     * UI_MODE_TYPE_DESK work mode
     * UI_MODE_TYPE_CAR, car mode
     * UI_MODE_TYPE_TELEVISION, TV equipment
     * UI_MODE_TYPE_APPLIANCE, portable device
     * UI_MODE_TYPE_WATCH, watch device
     * UI_MODE_TYPE_VR_HEADSET. VR device
     * UI_MODE_NIGHT_MASK. Night mode
     * UI_MODE_NIGHT_UNDEFINED. Night undefined
     * UI_MODE_NIGHT_NO. Day mode
     * UI_MODE_NIGHT_YES. Night mode
     */
    @Preview(name = "Text 1", uiMode = Configuration.UI_MODE_NIGHT_YES)
    @Composable
    fun MyText1() {
        Text(text = stringResource(id = R. string. hello_world),
            fontSize = 24.sp,
            color = Color.Gray,
            modifier = Modifier. background(Color. Black))
    }

device

Specifies the device style previewed in Android studio

@Preview(name = "Text 1", device = Devices.WEAR_OS_LARGE_ROUND)
@Composable
fun MyText1() {
    Text(
        text = stringResource(id = R. string. hello_world),
        fontSize = 24.sp,
        color = Color.Gray,
        modifier = Modifier. background(Color. Black)
    )
}

Other device parameters

const val DEFAULT = ""

const val NEXUS_7 = "id: Nexus 7"
const val NEXUS_7_2013 = "id: Nexus 7 2013"
const val NEXUS_5 = "id: Nexus 5"
const val NEXUS_6 = "id: Nexus 6"
const val NEXUS_9 = "id: Nexus 9"
const val NEXUS_10 = "name: Nexus 10"
const val NEXUS_5X = "id: Nexus 5X"
const val NEXUS_6P = "id: Nexus 6P"
const val PIXEL_C = "id:pixel_c"
const val PIXEL = "id:pixel"
const val PIXEL_XL = "id:pixel_xl"
const val PIXEL_2 = "id:pixel_2"
const val PIXEL_2_XL = "id:pixel_2_xl"
const val PIXEL_3 = "id:pixel_3"
const val PIXEL_3_XL = "id:pixel_3_xl"
const val PIXEL_3A = "id:pixel_3a"
const val PIXEL_3A_XL = "id:pixel_3a_xl"
const val PIXEL_4 = "id:pixel_4"
const val PIXEL_4_XL = "id:pixel_4_xl"

const val AUTOMOTIVE_1024p = "id:automotive_1024p_landscape"

const val WEAR_OS_LARGE_ROUND = "id:wearos_large_round"
const val WEAR_OS_SMALL_ROUND = "id:wearos_small_round"
const val WEAR_OS_SQUARE = "id:wearos_square"
const val WEAR_OS_RECT = "id:wearos_rect"

// Reference devices
const val PHONE = "spec:id=reference_phone,shape=Normal,width=411,height=891,unit=dp,dpi=420"
const val FOLDABLE = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480"
const val TABLET = "spec:shape=Normal,width=1280,height=800,unit=dp,dpi=420"
const val DESKTOP = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420"

Multipreview custom preview annotation

Note: This feature is available starting with Android Studio Dolphin and Jetpack Compose 1.2.0-beta01

Simply put, this function is actually a combination of multiple @Preview customizations, so that you can configure @Preview that meets your needs. And you can choose to meet the ui preview under different conditions.

class DeploymentActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyText1()
        }
    }

    @LandscapeAndPortraitPreviews
    @Composable
    fun MyText1() {
        Text(
            text = stringResource(id = R. string. hello_world),
            fontSize = 24.sp,
            color = Color. White,
            modifier = Modifier. background(Color. Black)
        )
    }
}

@Preview(
    name = "horizontal screen",
    group = "horizontal screen group",
    fontScale = 0.5f,
    widthDp = 1280,
    heightDp = 720
)
@Preview(
    name = "vertical screen",
    group = "vertical screen group",
    fontScale = 1.5f,
    widthDp = 720,
    heightDp = 1280
)
annotation class LandscapeAndPortraitPreviews

Renderings:

@PreviewParameter preview with parameters

In normal development, we often need to pass in data parameters to the Composable method (note that the parameters here refer to the data type, if you pass in some parameters of Composable such as Modifier, there is no problem). But after we add the preview, the following error message will appear:

Here you need to add the @PreviewParameter annotation to the String parameter name.

First, we need to create a PreviewParameterProvider to provide preview data. Please note here that the class created here cannot be an internal class, otherwise there may be problems that cannot be previewed.

class NameProvider : PreviewParameterProvider<String> {
    override val values: Sequence<String> get() = listOf("apple","banana","watermelon","grape").asSequence()
}

Add PreviewParameterProvider to Composable method

@Preview(device = Devices.AUTOMOTIVE_1024p)
@Composable
fun TextDemo(@PreviewParameter(NameProvider::class, 1) name: String) {
    Text(text = name)
}

Preview renderings:

The parameter limit of @PreviewParameter

In the above example, you can see that after passing in the NameProvider class, an Int value is also passed in. This value is related to the data collection of the NameProvider we implemented. Modifying the limit will automatically produce a preview of other data in the collection. The reference animation is as follows:

@PreviewParameter collection data preview

As an example of a slightly more complex use case

PreviewParameterProvider data code:

class ImageProvider : PreviewParameterProvider<SnapshotStateList<Int>> {
    override val values: Sequence<SnapshotStateList<Int>>
        get() = listOf(
            mutableStateListOf(
                R.mipmap.copybook_1,
                R.mipmap.copybook_1,
                R.mipmap.copybook_1
            )
        ).asSequence()
}

Composable method to add preview data

@Preview(device = Devices.AUTOMOTIVE_1024p)
@Composable
private fun ImageList(@PreviewParameter(ImageProvider::class, 1) list: SnapshotStateList<Int>) {
    Column {
        LazyVerticalGrid(
            columns = GridCells. Adaptive(minSize = 200.dp),
            verticalArrangement = Arrangement.spacedBy(20.dp),
            horizontalArrangement = Arrangement.spacedBy(20.dp),
            contentPadding = PaddingValues(top = 20.dp, start = 20.dp, end = 20.dp)
        ) {
            items(list. size) { index ->
                Image(painter = painterResource(list[index]), contentDescription = null)
            }
        }
    }
}

Preview renderings:

end.