--- _db_id: 415 available_flavours: - kotlin content_type: project prerequisites: hard: - projects/kotlin/project-1/user-activity - topics/kotlin/grid-layout soft: [] ready: true submission_type: repo title: Constraint Layout Using Layout Editor --- ## App overview The ColorMyViews app is inspired by the Dutch artist, Piet Mondrian. He invented a style of painting style called neoplasticism, which uses only vertical and horizontal lines and rectangular shapes in black, white, gray, and primary colors. ![](7fdda7ddbbb92f51.png) ![](957c810298666482.png) Although paintings are static, your app will be interactive! The app consists of clickable text views that change color when tapped, and button views in a `constraintLayout`. ![](6978e2b5aad2536e.png) ![](2c563e2d49e54588.png) ![](41400880c88d3db7.png) ### ConstraintLayout A `ConstraintLayout`t is a `ViewGroup` that allows you to position and size child views in a flexible way. A constraint layout allows you to create large, complex layouts with flat view hierarchies (no nested view groups). To build a constraint layout, you can use the Layout Editor to add constraints, and to drag-and-drop views. You don't need to edit the XML. Note: `ConstraintLayout` is available as a support library, which is available in API level 9 and higher. ### Constraints A `constraint` is a connection or alignment between two UI elements. Each constraint connects or aligns one view to another view, to the parent layout, or to an invisible guideline. In a constraint layout, you position a view by defining at least one horizontal and one vertical constraint. ![](be3c775dcb40cfe.png) 1 - Horizontal constraint: B is constrained to stay to the right of A. (In a finished app, B would need at least one vertical constraint in addition to this horizontal constraint.) 2 - Vertical constraint: C is constrained to stay below A. (In a finished app, C would need at least one horizontal constraint in addition to this vertical constraint.) ## Task: Create the ColorMyViews project 1 - Open Android Studio, if it's not already open, and create a new project with the following parameters: | Attribute | Value | | -------------------------------------- | ----------------------------------------------------- | | Template | Empty Activity in the Phone and Tablet tab | | Application Name | ColorMyViews | | Company Name android | com.android.example.colormyviews (or your own domain) | | Language | Kotlin | | Minimum API level | API 19: Android 4.4 (KitKat) | | This project will support instant apps | (Leave this box cleared) | | Use AndroidX artifacts | Select this box. | The Empty Activity template creates a single empty activity in the `Mainactivity.kt` file. The template also creates a layout file called `activity_main.xml`. The layout uses `ConstraintLayout` as its root view group, with a single `TextView` as the layout's content. 1 - Wait for Android Studio to finish the Gradle build. If you see any errors, select Build > Rebuild Project. 2 - Run the app and wait for a few seconds for the build to complete. You should see a screen with "Hello World!" in the middle of it. ![](b91290c0a4d213ba.png) ## Task: Use Layout Editor to build a constraint layout In this task, you use the Android Studio Layout Editor to build a constraint layout for your app. ### Step 1: Set up your Android Studio work area 1 - Open the `activity_main.xml` file and click the **Design** tab. 2 - You'll add constraints manually, so you want autoconnect turned off. In the toolbar, locate the **Turn Off/On Autoconnect** toggle button, which is shown below. (If you can't see the toolbar, click inside the design editor area of the Layout Editor.) Make sure autoconnect is off. ![](f66ff1b834a69947.png) Autoconnect is on. ![](dd30e456c4858de3.png) Autoconnect is off—this is what you want for this project. 3 - Use the toolbar to set the default margins to `16dp`. (The default is `8dp`.) ![](c7debadc31243335.png) When you set the default margin to `16dp`, new constraints are created with this margin, so you don't have to add the margin each time you add a constraint. 4 - Zoom in using the **+** icon ![](e6686dccd99f45c6.png) on the right side of the toolbar, until the Hello World text is visible inside its text view. 5 - Double-click on the Hello World text view to open the Attributes pane. ![](1b5c3f9767bdbf26.gif) ### The view inspector The view inspector, shown in the screenshot below, is a part of the Attributes pane. The view inspector includes controls for layout attributes such as constraints, constraint types, constraint bias, and view margins. ![](fd4bb323c90d70ea.png) Tip: The view inspector is available only for views that are inside a `ConstraintLayout`. ### Constraint bias Constraint bias positions the view element along the horizontal and vertical axes. By default, the view is centered between the two constraints with a bias of 50%. To adjust the bias, you can drag the bias sliders ![](14f369688bbc92a0.png) in the view inspector. Dragging a bias slider changes the view's position along the axis. ### Step 2: Add margins for the Hello World text view 1 - Notice that in the view inspector, the left, right, top, and bottom margins for the text view are `0`. The default margin was not automatically added, because this view was created before you changed the default margin. 2 - For the left, right, and top margins, select 16dp from the drop-down menu in the view inspector. For example, in the following screenshot you are adding `layout_marginEnd` `(layout_marginRight)`. ![](98f63d9564c42f5d.png) ### Step 3: Adjust constraints and margins for the text view 1 - In the view inspector, the arrows ![](f0877ac04bffb878.png) inside the square represents the type of the constraint: - ![](f0877ac04bffb878.png) **Wrap Content**: The view expands only as much as needed to contain its contents. - ![](217e6866e21dff0d.png) **Fixed**: You can specify a dimension as the view margin in the text box next to the fixed-constraint arrows. - ![](9ca6bced19ddc559.png) **Match Constraints**: The view expands as much as possible to meet the constraints on each side, after accounting for the view's own margins. This constraint is very flexible, because it allows the layout to adapt to different screen sizes and orientations. By letting the view match the constraints, you need fewer layouts for the app you're building. 1 - In the view inspector, change the left and right constraints to **Match Constraints** ![](9ca6bced19ddc559.png) (Click the arrow symbol to toggle between the constraint types.) ![](cee5c4478192c328.png) 2 - In the view inspector, click the Delete Bottom Constraint dot ![](8b8e6d915b2b4a9c.png) on the square to delete the bottom constraint. 3 - Switch to the Text tab. Extract the dimension resource for `layout_marginStart`, and set the Resource name to `margin_wide`. ![](1fb03cec8f053edf.png) 4 - Set the same dimension resource, `@dimen/margin_wide`, for the top and end margins. ``` android:layout_marginStart="@dimen/margin_wide" android:layout_marginTop="@dimen/margin_wide" android:layout_marginEnd="@dimen/margin_wide" ``` ## Task: Style the TextView ### Step 1: Add a font 1 - In the Attributes pane, search for `fontFamily` and select the drop-down arrow next to it. Scroll down to More Fonts and select it. The Resources dialog opens. ![](bd10601e8201e4ea.png) 2 - In the Resources dialog, search for roboto. 3 - Click Roboto and select Regular in the Preview list. 4 - Select the Add font to project radio button. 5 - Click OK. ![](34d3fac8fd09786b.png) This adds a `res/font` folder that contains a `roboto.ttf` font file. Also, the `@font/roboto` attribute is set for your text view. ### Step 2: Add a style 1 - Open `res/values/dimens.xml`, and add the following dimension resource for the font size. ``` 24sp ``` 2 - Open `res/values/styles.xml` and add the following style, which you will use for the text view. ``` ``` In this style, the background color and the text color are set to default Android color resources. The font is set to Roboto. The text is center aligned and bolded, and the text size is set to `box_text_size`. ### Step 3: Add a string resource for the text view 1 - In the Attributes pane, find the text attribute. (You want the one without the wrench icon.) 2 - Click the ... (three dots) next to the text attribute to open the Resources dialog. 3 - In the Resources dialog, select Add new resource > New string Value. Set the resource name to `box_one`, and set the value to `Box One`. 4 - Click OK. ![](59d31cc58f827c9.png) ### Step 4: Finish setting attributes for the text view 1 - In the Attributes pane, set the `id` of the text view to `box_one_text`. 2 - Set the `style` to `@style/whiteBox`. 3 - To clean up the code, switch to the Text tab and remove the `android:fontFamily="@font/roboto"` attribute, because this font is present in the `whiteBox` style. 4 - Switch back to the Design tab. At the top of the design editor, click the Device for preview (D) button. A list of device types with different screen configurations is displayed. The default device is Pixel. ![](d8bf1815c1e74f69.png) 5 - Select different devices from the list and see how the `TextView` adapts to the different screen configurations. 6 - Run your app. You see a styled green text view with the text "Box One". ![](7ab575ecda8ea251.png) ## Task: Add a second TextView and add constraints In this task, you add another text view below the `box_one_text`. You constrain the new text view to `box_one_text` and the layout's parent element. ### Step 1: Add a new text view 1 - Open the `activity_main.xml` file and switch to the Design tab. 2 - Drag a `TextView` from the Palette pane directly into the design editor preview, as shown below. Place the text view below the `box_one_text`, aligned with the left margin. ![](fbfcfd3b50143beb.gif) 3 - In the design editor, click the new text view, then hold the pointer over the dot on the top side of the text view. This dot, shown below, is called a _constraint handle_. ![](e6d06f567bfd56e.png) Notice that when you hold the pointer over the constraint handle, the handle turns green and blinks. ### Step 2: Add constraints to the new text view Create a constraint that connects the top of the new text view to the bottom of the Box One text view: 1 - Hold the pointer over the top constraint handle on the new text view. 2 - Click the top constraint handle of the view and drag it up. A constraint line appears. Connect the constraint line to the bottom of the Box One textview, as shown below. ![](75bc5ad6c0003376.png) As you release the click, the constraint is created, and the new text view jumps to within 16dp of the bottom of the Box One. (The new text view has a top margin of 16dp because that's the default you set earlier.) #### Now create a left constraint: 1 - Click the constraint handle on the left side of the new view. 2 - Drag the constraint line to the left edge of the layout. ![](954c246ce6b8bd29.png) **Tip**: You can also create constraints using the view inspector. For example, to create a left constraint on the new text box: 1 - In the preview, click the new text box to select it. 2 - In the view inspector, click the + icon on the left side of the box, as shown below. ![](b684fbf661f2bf3d.png) When you create a constraint this way, the constraint is attached to the parent or to a view closer to it. ### Step 3: Set attributes for the new text view 1 - Open `res/values/strings.xml`. Add a new string resource with the following code: `Box Two` 2 - Open `activity_main.xml` and click the Design tab. Use the Attributes pane to set the following attributes on the new text view: | Attribute | value | | ------------- | --------------- | | id | box_two_text | | layout_height | 130dp | | layout_width | 130dp | | style | @style/whiteBox | | text | @string/box_two | In this case, you're assigning fixed sizes for the height and width of the text view. Assign fixed sizes for height and width only if your view should always have a fixed size on all devices and layouts. Important: When developing real-world apps, use flexible constraints for the height and width of your UI elements, whenever possible. For example, use `match_constraint` or `wrap_content`. The more fixed-size UI elements you have in your app, the less adaptive your layout is for different screen configurations. 3 - Run your app. You should see two green `TextView` views, one above the other, similar to the following screenshot: ![](ca60a1275d7a4cc.png) ### Task: Create a chain of TextView views In this task, you add three `TextView` views. The text views are vertically aligned with each other, and horizontally aligned with the Box Two text view. The views will be in a chain. #### Chains A _chain_ is a group of views that are linked to each other with bidirectional constraints. The views within a chain can be distributed either vertically or horizontally. For example, the following diagram shows two views that are constrained to each other, which creates a horizontal chain. ![](690fa1708662a4a9.png) #### Head of the chain The first view in a chain is called the head of the chain. The attributes that are set on the head of the chain control, position, and distribute all the views in the chain. For horizontal chains, the head is the left-most view. For vertical chains, the head is the top-most view. In each of the two diagrams below, "A" is the head of the chain. ![](e822baed8a72bf3a.png) ![](559c8ae4c80cc90f.png) #### Chain styles Chain styles define the way the chained views are spread out and aligned. You style a chain by assigning a chain style attribute, adding weight, or setting bias on the views. There are three chains styles: - **Spread**: This is the default style. Views are evenly spread in the available space, after margins are accounted for. ![](d57e8cdbe225181f.png) - **Spread inside**: The first and the last views are attached to the parent on each end of the chain. The rest of the views are evenly spread in the available space. ![](8ee14c6b5164afef.png) - **Packed**: The views are packed together, after margins are accounted for. You can then adjust the position of the whole chain by changing the bias of the chain's head view. ![Packed Chain](16bb057b065865c6.png) Packed Chain. ![](c893437f3a9c3f06.png) Packed chain with bias. - **Weighted**: The views are resized to fill up all the space, based on the values set in the `layout_constraintHorizontal_weight` or `layout_constraintVertical_weight` attributes. For example, imagine a chain containing three views, A, B, and C. View A uses a weight of 1. Views B and C each use a weight of 2. The space occupied by views B and C is twice that of view A, as shown below. ![](91ca5b204a0141ed.png) To add a chain style to a chain, set the `layout_constraintHorizontal_chainStyle` or the `layout_constraintVertical_chainStyle` attribute for the head of the chain. You can add chain styles in the Layout Editor, which you learn in this task. Alternatively, you can add chain styles in the XML code. For example: ``` // Horizontal spread chain app:layout_constraintHorizontal_chainStyle="spread" // Vertical spread inside chain app:layout_constraintVertical_chainStyle="spread_inside" // Horizontal packed chain app:layout_constraintHorizontal_chainStyle="packed" ``` ### Step 1: Add three text views and create a vertical chain 1 - Open the `activity_main.xml` file in the Design tab. Drag three `TextView` views from the Palette pane into the design editor. Put all three new text views to the right of the Box Two text view, as shown below. ![](36b4f0d7ebb2f39d.png) 2 - In the `strings.xml` file, add the following string resources for the names of the new text views: ``` Box Three Box Four Box Five ``` 3 - Set the following attributes for the new text views: | Attribute | Top text view | Middle text view | Bottom text view | | --------- | ----------------- | --------------------------------- | ---------------- | | ID | box_three_text | box_four_text | box_five_text | | text | @string/box_three | @string/box_four\@string/box_five | | style | @style/whiteBox | @style/whiteBox | @style/whiteBox | ![](1a3dd819d3f985a5.png) In the Component Tree, you see errors about missing constraints. You fix these errors later. ### Step 2: Create a chain and constrain it to the height of Box Two 1 - Select all three new text views, right-click, and select Chains > Create Vertical Chain. ![](1303fa08f51e3a78.png) This creates a vertical chain that extends from Box One to the end of the layout. 2 - Add a constraint that extends from the top of Box Three to the top of Box Two. This removes the existing top constraint and replaces it with the new constraint. You don't have to delete the constraint explicitly. ![](8bf2cf78d0557691.png) 3 - Add a constraint from the bottom of Box Five to the bottom of Box Two. ![](5b7cff6d32a2526f.png) Observe that the three text views are now constrained to the top and bottom of Box Two. ### Step 3: Add left and right constraints 1 - Constrain the left side of Box Three to the right side of Box Two. Repeat for Box Four and Box Five, constraining the left side of each to the right side of Box Two. ![](f4864c716bf57f67.png) 2 - Constrain the right side of each of the three text views to the right side of the layout. ![](9d69129fc6d543de.png) 3 - For each of the three text views, change the `layout_width` attribute `0dp`, which is equivalent to changing the constraint type to Match Constraints. ![](fa084fcb1c6cc47d.png) ### Step 4: Add margin Use the **Attributes** pane to set **Layout_margin** attributes on the three text views to add spacing between them. 1 - For Box Three, use `@dimen/margin_wide` for the start and end margins. Remove the other margins. 2 - For Box Four, use `@dimen/margin_wide` for the start, end, top, and bottom margins. Remove the other margins. 3 - For Box Five, use `@dimen/margin_wide` for the start and end margins. Remove the other margins. 4 - To see how the text views in your app adapt to device-configuration changes, change the orientation of the preview. To do this, click the Orientation for Preview (O) icon ![](2eb21a66f61c9a94.png) in the toolbar and select Landscape. ![](6d7021d538791e6.png) 5 - Run the app. You should see five styled `TextView` views. To see how the constraints behave on a wider screen, try running the app on a larger device or emulator, such as a Nexus 10. ![](6e967fca39323004.png) ![](e5e9e3765492579b.png) ### Task: Add clickHandlers to the text views In this task, you make the ColorMyViews app a little more colorful. First you change the color of all the text views to white. Then you add a click handler that changes the view's color and the layout background color when the user taps it. 1 - In `styles.xml`, inside the `whiteBox` style, change the background color to white. The text views will start out white with white font, then change colors when the user taps them. `@android:color/white` 2 - In `MainActivity.kt`, after the `onCreate()` function, add a function called `makeColored()`. Use View as the function's parameter. This view is the one whose color will change. ``` private fun makeColored(view: View) { } ``` Every view has a resource ID. The resource ID is the value assigned to the view's `id` attribute in the layout file, `activity_main.xml`. To set a color, the code will switch using a `when` statement on the view's resource ID. It's a common pattern to use one click-handler function for many views when the click action is the same. 3 - Implement the `makeColored()` function: Add a `when` block to check the view's resource ID. Call the `setBackgroundColor()` function on each view's id to change the view's background color using the `Color` class **constants**. To fix the code indentation, choose **Code > Reformat code**. ``` private fun makeColored(view: View) { when (view.id) { // Boxes using Color class colors for the background R.id.box_one_text -> view.setBackgroundColor(Color.DKGRAY) R.id.box_two_text -> view.setBackgroundColor(Color.GRAY) R.id.box_three_text -> view.setBackgroundColor(Color.BLUE) R.id.box_four_text -> view.setBackgroundColor(Color.MAGENTA) R.id.box_five_text -> view.setBackgroundColor(Color.BLUE) } } ``` 4 - To run, the code that you just added needs the `android.graphics.Color` library. If Android Studio hasn't imported this library automatically, use an `import` statement to add the library before the `MainActivity` class definition. 5 - If the user taps the background, you want the background color to change to light gray. A light background will reveal the outlines of the views and give the user a hint about where to tap next. If the `id` doesn't match any of the views, you know that the user has tapped the background. At the end of the `when` statement, add an `else` statement. Inside the `else`, set the background color to light gray. `else -> view.setBackgroundColor(Color.LTGRAY)` 6 - In `activity_main.xml`, add an id to the root constraint layout. The Android system needs an identifier in order to change its color. `android:id="@+id/constraint_layout"` 7 - In `MainActivity.kt`, add a function called `setListeners()` to set the click-listener function, `makeColored()`, on each view. Use `findViewByID` to get a reference for each text view, and for the root layout. Assign each reference to a variable. ``` private fun setListeners() { val boxOneText = findViewById(R.id.box_one_text) val boxTwoText = findViewById(R.id.box_two_text) val boxThreeText = findViewById(R.id.box_three_text) val boxFourText = findViewById(R.id.box_four_text) val boxFiveText = findViewById(R.id.box_five_text) val rootConstraintLayout = findViewById(R.id.constraint_layout) } ``` For this code to run, it needs the `android.widget.TextView` library. If Android Studio doesn't import this library automatically, use an `import` statement to add the library before the `MainActivity` class definition. 8 - At the end of `setListeners()` function, define a `List` of views. Name the list `clickableViews` and add all the view instances to the list. ``` fun setListeners() { ... val clickableViews: List = listOf(boxOneText, boxTwoText, boxThreeText, boxFourText, boxFiveText, rootConstraintLayout) } ``` At the end of the `setListeners()` function, set the listener for each view. Use a `for` loop and the `setOnClickListener()` function. ``` for (item in clickableViews) { item.setOnClickListener { makeColored(it) } ``` 10 - In `MainActivity.kt`, at the end of the `onCreate()` function, make a call to `setListeners()`. ``` override fun onCreate(savedInstanceState: Bundle?) { ... setListeners() } ``` Run your app. At first you see a blank screen. Tap the screen to reveal the boxes and the background. Go ahead and experiment more with more views and colors on your own. ![](bb3e8d9160561b5.png) ![](7bca2553a09819ae.png) ## Coding challenge (this is important) Use images instead of colors and text as backgrounds for all the views. The app should reveal the images when the user taps the text views. **Hint**: Add images to the app as drawable resources. Use the `setBackgroundResource()` function to set an image as a view's background. Example: `R.id.box_two_text -> view.setBackgroundResource(R.drawable.image_two)` ## Task: Add a baseline constraint ### Baseline constraint The baseline constraint aligns the baseline of a view's text with the baseline of another view's text. Aligning views that contain text can be a challenge, especially if the fonts are differently sized. Baseline constraint does the alignment work for you. ![](7eca5c45d4b85a9.png) You can create baseline constraints in the Layout Editor by using the **Edit Baseline** ![](f6f12698635fad5a.png) icon, which is displayed below the view when you hold the pointer over it. The equivalent XML code for the baseline constraint has the constraint layout attribute, `layout_constraintBaseline_toBaselineOf`. Sample XML code for the baseline constraint: ```