is a layout on that gives you adaptable and flexible ways to create views for your apps.

ConstraintLayout, which is now the default layout in Android Studio, gives you many ways to place objects. You can constrain them to their container, to each other or to guidelines. This allows you to create large, , dynamic and responsive views in a flat hierarchy. It even supports animations!

In this , you’ll learn to use a multitude of ConstraintLayout‘s features by building an app for a space travel agency. In the process, you’ll learn how to:

  • Convert from other types of to ConstraintLayout.
  • Dynamically position UI elements onscreen in relation to other elements.
  • Animate your views.

Raze Galactic — An Intergalactic Travel Service

During this tutorial, you’ll build an interface for an intergalactic travel app which lets users book trips between planets, plan weekend space station getaways and make moon rover reservations to get around when they reach their destination.
final project animated

Getting Started

Use the Download Materials button at the top or bottom of this tutorial to download the starter project.

Open the starter project in Android Studio. Build and run the app.
starter project running

There are many elements in this app. You’ll learn how to display them properly using a complex ConstraintLayout in this tutorial.

To start, go to Android Studio and open the layout file for this app, activity_main.xml, in Design view. Notice the structure of the layout is a series of nested LinearLayouts and RelativeLayouts.
Holy Nested Layouts, Batman!

ConstraintLayout is not the best choice for simple layouts, but it’s great for complex layouts like the one in this tutorial.

Converting a Layout to ConstraintLayout

In the Component Tree in Design view, right-click on the top-level LinearLayout and select Convert LinearLayout to ConstraintLayout from the context menu:
convert linear layout to constraint layout

Next, you should get a pop-up dialog with some options:
conversion options dialog

Accept the defaults after reading what they do and click on OK to dismiss the dialog and convert the layout. Android Studio will then attempt to remove all the nested layouts and convert your layout to ConstraintLayout.

At this point, you may need to give Android Studio a moment to do some processing as it tries to figure out the new layout. After a moment, your layout may look like this:
intermediary layout

After another moment, all your views may just jump into the upper left corner of the layout. If this happens, don’t panic!

Note: Make sure to turn off Autoconnect for this tutorial. Find this option in the toolbar of the design editor when you have ConstraintLayout selected.
turn off autoconnect

Removing Inferred Constraints

During the conversion process, Android Studio performs a number of steps. The last one may have been Infer Constraints, whose results might not quite be what you wanted. ;] If that’s the case, simply go to the Edit menu and choose Undo Infer Constraints:
undo infer constraints
Alternatively, you can simply press ⌘-Z on Mac or Control-Z on Windows.

In the Design view of Android Studio, your preview may now look like this:
after undoing infer constraints

It’s close, but not quite where the layout needs to be. You can drag the views around a bit until they look more like the original layout:
original layout

Note: This tutorial uses a Pixel 2 as the device in the preview. If you are using a different device, the views may look different than they do in the screenshots. You can change this setting in the toolbar.

design editor device control

Don’t spend a lot of time trying to get the layout exactly like it was before. At this point, you just want a very rough estimation to get yourself visually oriented. You’ll add all the constraints you need to make it look perfect throughout the rest of this tutorial.

When you are done, your layout may look something like this:
rough layout

If Android Studio added any constraints automatically as you dragged the views around, just click the Clear All Constraints button to get rid of them.
clear all constraints

One last thing before putting theses elements in their final places, change the ID of the root ConstraintLayout to be constraintLayout.

Resizing the Images

Next, fix the image sizes by clicking on each of the icons, spaceStationIcon, flightsIcon, and roverIcon, at the top. Then, in the Attributes panel, change the layout_width and layout_height properties from wrap_content to 30dp.
width and height attributes
You’ll see a bunch of errors listed in the Component Tree. These appear because Android doesn’t have any information from constraints to tell it where to position the UI elements. You’ll start fixing that problem now.

Note: Android Studio offers various ConstraintLayout tools to save you time, but they don’t always do what you expect. It helps to visualize what the constraints should do before you start to add them. That way, if Android Studio’s tools misbehave, you can add individual constraints one at a time to achieve the effect you want.

Keep this in mind as you use the Align menu and other tools in the steps below: if Android Studio doesn’t do what you expect, go back and add the individual constraints yourself.

Adding Constraints: Figuring out Alignment

You’ll set your constraints with a top-down approach, starting with the elements at the top of the screen and working your way down to the bottom.

You want the three icons at the top of the screen to line up with each other horizontally. Then you’ll center the labels under each of those icons.

Constraining the First Icon

First, you’ll constrain spaceStationIcon above the word “Space Stations” to the top of the screen.

To do this, click on spaceStationIcon to select it and reveal its constraint anchors. Click on the top anchor and drag it to the top of the view. The icon may slide up to the top of the view. Don’t connect its left constraint yet.

With the spaceStationIcon selected, drag it down from the top so that there’s a little space between the top of the view and the rocket.

Next, switch to Code view and examine the updated XML for the rocket icon. You have added one new constraint, app:layout_constraintTop_toTopOf="parent", and a top margin attribute for the space between the rocket and the top of the view. Update the code to set the margin to 15dp.

The XML for spaceStationIcon should now look like this:


<ImageView
  android:id="@+id/spaceStationIcon"
  android:layout_width="30dp"
  android:layout_height="30dp"
  android:layout_marginTop="15dp"
  android:src="http://www.raywenderlich.com/@drawable/space_station_icon"
  app:layout_constraintTop_toTopOf="parent" />

You can adjust the margin in the Design view as well. To do this, switch to Design view and click on the Attributes tab on the right, if it’s not already visible.

Next, click on the spaceStationIcon to reveal the attributes for that image. After ID, layout_width and layout_height, you’ll see a graphic representation of the margins.

You can pick a new value for the margin by choosing it from the drop-down menu or by clicking on the number and entering a new value.
margin attribute

Aligning the Top Three Icons Horizontally: Using Chains

Next, you want the three icons at the top of the screen to line up in a row with equal spacing between them. To achieve this, you could add a bunch of individual constraints for each icon. However, there’s a much faster way to do this, using chains.

Chains

A chain occurs whenever you have bi-directional constraints. You won’t necessarily see anything special in the XML; the fact that there are mutual constraints in the XML is enough to make a chain.

Whenever you use alignment controls from the menu, such as Align Horizontal Centers, Android Studio is actually applying a chain. You can apply different styles, weights and margins to chains.

Start by switching back to Design view. Shift-click to select all three icons at the top of the screen: spaceStationIcon, flightsIcon and roverIcon. Then right-click to bring up the context menu and select Center ▸ Horizontally. This will automatically create a chain and generate constraints.

In the Design view, you can see that some of the lines representing the constraints look different than others. Some look like squiggly lines, while others resemble a chain.
chains

Exploring Chains

To explore some of the chain modes, click the Cycle Chain Mode button that appears at the bottom of the icons when you select them.
cycle chain mode

The modes are:

  • Packed: The elements display packed together.
  • Spread: The elements spread out over the available space, as shown above.
  • Spread inside: Similar to spread, but the endpoints of the chain are not spread out.

chain modes

Make sure you end with spread as the selected chain mode. You’ll know this is selected one of two ways:

  1. The view will display with the icons spaced as they are in the example screenshot
  2. The attribute app:layout_constraintHorizontal_chainStyle="spread" will be on one of the image views. Updating this attribute is another way to change the chain mode.

Aligning Views

Again, select the three icons. From the tool bar, select Align ▸ Vertical Centers. Android Studio should add constraints to the images to align the bottom and the top of each image to its neighbor.

align vertical centers

Your layout should now look like this:
aligned icons

If your layout doesn’t match this image, check the Text and Design views. If you’ve lost the original constraint between flightsIcon and the top of the view, and if spaceStationIcon didn’t get the constraints you expected, press ⌘ + Z on Mac, or Control + Z on Windows to undo.

Then, manually add the constraints by clicking on the top constraint anchor of spaceStationIcon and dragging it to the top constraint anchor of flightsIcon, and so on, until you have added all of the constraints in the diagram above.

Your three icons should now have the following XML:


<ImageView
  android:id="@+id/spaceStationIcon"
  android:layout_width="30dp"
  android:layout_height="30dp"
  android:layout_marginTop="15dp"
  android:src="http://www.raywenderlich.com/@drawable/space_station_icon"
  app:layout_constraintEnd_toStartOf="@+id/flightsIcon"
  app:layout_constraintHorizontal_chainStyle="spread"
  app:layout_constraintStart_toStartOf="parent"
  app:layout_constraintTop_toTopOf="parent" />

<ImageView
  android:id="@+id/flightsIcon"
  android:layout_width="30dp"
  android:layout_height="30dp"
  android:src="http://www.raywenderlich.com/@drawable/rocket_icon"
  app:layout_constraintBottom_toBottomOf="@+id/spaceStationIcon"
  app:layout_constraintEnd_toStartOf="@+id/roverIcon"
  app:layout_constraintStart_toEndOf="@+id/spaceStationIcon"
  app:layout_constraintTop_toTopOf="@+id/spaceStationIcon" />

<ImageView
  android:id="@+id/roverIcon"
  android:layout_width="30dp"
  android:layout_height="30dp"
  android:src="@drawable/rover_icon"
  app:layout_constraintBottom_toBottomOf="@+id/flightsIcon"
  app:layout_constraintEnd_toEndOf="parent"
  app:layout_constraintStart_toEndOf="@+id/flightsIcon"
  app:layout_constraintTop_toTopOf="@+id/flightsIcon" />

Aligning the Text for Each of the Icons

Now that the icons are in place, you’ll need to set their text fields to appear in their proper places.

Select the TextView labeled Space Stations to reveal its constraint anchors. Constrain the left side of the Space Stations TextView to the left side of the space station icon and the right side of the Space Stations TextView to the right side of the space station icon. This centers it vertically with the icon.

Then change the default margins in the tool bar to 15dp and just drag from the top anchor of the label to the bottom anchor of the icon, which will set both the constraint and the margin in a single step. Do the same for the other labels to align them to their icons.

labels animation

Now, the constraint errors for the top two rows of UI elements should be gone. The XML for the top three images and labels should look like this:


<TextView
  android:id="@+id/roverLabel"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_marginTop="15dp"
  android:text="@string/rovers"
  app:layout_constraintEnd_toEndOf="@+id/roverIcon"
  app:layout_constraintStart_toStartOf="@+id/roverIcon"
  app:layout_constraintTop_toBottomOf="@+id/roverIcon" />

<TextView
  android:id="@+id/flightsLabel"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_marginTop="15dp"
  android:text="@string/flights"
  app:layout_constraintEnd_toEndOf="@+id/flightsIcon"
  app:layout_constraintStart_toStartOf="@+id/flightsIcon"
  app:layout_constraintTop_toBottomOf="@+id/flightsIcon" />

<TextView
  android:id="@+id/spaceStationLabel"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_marginTop="15dp"
  android:text="@string/space_stations"
  app:layout_constraintEnd_toEndOf="@+id/spaceStationIcon"
  app:layout_constraintStart_toStartOf="@+id/spaceStationIcon"
  app:layout_constraintTop_toBottomOf="@+id/spaceStationIcon" />

Using Guidelines

So far, you’ve constrained UI elements to their parent containers and to each other. Another option you have is to add invisible guidelines to the layout and constrain UI elements to those guidelines.

Recall that in the final layout, the double arrows image should be centered and should overlap the two green views.
overlapping views

Setting the Horizontal and Vertical Guidelines

Select the double arrows icon and set the height and width to 60dp. Then right-click on it and choose Center ▸ Horizontally in Parent from the context menu.
double arrows center
For each of the green TextViews, you’ll now set the width to 124dp and the height to 98dp.

To make the double arrows icon overlap the two green TextViews, you’ll constrain the right side of the left TextView to the right side of the double arrows icon and set the right margin to 40dp.

Similarly, constrain the left side of the right TextView to the left side of the double arrows icon and set the left margin to 40dp.

Lastly, constrain the top and bottom of the TextViews to the top and bottom of doubleArrowsIcon.
overlapping views
Next, click on the Guidelines menu in the toolbar and select Add Horizontal Guideline.
add horizontal guideline

This will add a horizontal dashed line to the layout.

Select the horizontal guideline using the Component Tree in Design view. In the attributes inspector, change the ID of the guideline to guideline1. Note the guideline properties: layout_constraintGuide_begin and layout_constraintGuide_percent.

For the horizontal guideline, set layout_constraintGuide_begin to 200dp.
horizontal guideline 200dp

Finally, add a vertical guideline, ensure that you’ve set its ID to guideline2 and set its layout_constraintGuide_percent to 0.05. This positions guideline2 to 5% of the screen width from the left.

Positioning the Guidelines

You can position guidelines using one of these three attributes:

  • layout_constraintGuide_begin: positions a guideline with a specific number of dp from the left (for vertical guides) or the top (for horizontal guides) of its parent.
  • layout_constraintGuide_end: positions a guideline a specific number of dp from the right or bottom of its parent.
  • layout_constraintGuide_percent: places a guideline at a percentage of the width or height of its parent.

After you constrain elements to the guidelines, you can constrain other elements to them. That way, if the guideline changes position, everything constrained to the guideline, or to the other elements fixed to the guideline, will adjust its position.

Hint: Later, you’ll use this feature to create some cool animations!

Adding Constraints to Guidelines

Now that your guidelines are set up, you can start adding constraints to them.

First, for the double arrows icon:

  • Constrain the bottom to the horizontal guideline.
  • Set the bottom margin to 40dp.

For the switch:

  • Set the width to 160dp.
  • Constrain the left side to the vertical guideline.
  • Constrain the top to the parent (top of the screen).
  • Set the margin at the top to 200dp.

For the label beneath the switch listing the number of travelers:

  • Constrain the left side to the vertical guideline.
  • Constrain the top to the bottom of the switch.

For the galaxy icon (id is galaxyIcon):

  • Set the width and height to 90dp.
  • Constrain the top to the horizontal guideline.
  • Constrain the bottom to the bottom of the parent (bottom of the screen). This will center it between the horizontal guideline and the bottom of the screen.
  • Center it horizontally in the parent view.

For the rocket icon to the left of the galaxy icon (ID is rocketIcon):

  • Set the width and height to 30dp.
  • Constrain the rocket icon’s top, bottom, and right sides to the top, bottom, and left sides of the galaxy icon, respectively.

Finally, for the DEPART button at the bottom:

  • Change the width from wrap_content to match_parent.
  • Constrain its bottom to the bottom of the parent (bottom of the screen).

At this point, you should have set all the constraints Android Studio needs to figure out the layout; there should be no errors in the Component Tree. Your layout should now look similar to this:
final static layout

Build and run the app:
final static layout in emulator

Your layout looks great now! But clicking the button doesn’t do anything… so it’s time to add some pizzaz with a few simple animations!

Circular Position Constraints

In addition to the methods that you’ve already learned, you can also constrain UI elements relative to each other using distance and an angle. This allows you to position them on a circle, where one UI element is at the center of the circle and the other is on the perimeter.

circular constraints

To do this, select the rocket icon next to the galaxy icon and update its code in Code view as follows:


<ImageView
  android:id="@+id/rocketIcon"
  android:layout_width="30dp"
  android:layout_height="30dp"
  android:src="http://www.raywenderlich.com/@drawable/rocket_icon"
  app:layout_constraintCircle="@id/galaxyIcon"
  app:layout_constraintCircleAngle="270"
  app:layout_constraintCircleRadius="100dp" />

The first constraint attribute, layout_constraintCircle, indicates the ID of the UI element that will be on the center of the circle. The other two attributes indicate the angle and radius.

Why would you want to use such an unusual type of constraint, you ask? Stay tuned, in a moment you’ll use this technique to animate the rocket to fly around the screen!

Note: You can ignore the error in Component Tree for the view using circular constraint. Android Studio doesn’t seem to recognize circular constraint yet.

Build and run the app. Everything should still appear properly positioned onscreen:
circular constraint

Animating the UI Elements on the Screen

Now that you’re a master of laying things out onscreen using ConstraintLayout, it’s time to add some rocket fuel into the mix and take off to the next level!

In this section, you’ll start with the complex layout you created and add some cool UI animations in just a few steps.

Constraint Sets

Using ConstraintLayouts, you can use Keyframe Animations to animate your views. To do this, you’ll provide a pared-down copy of your layout file, known as a ConstraintSet. A ConstraintSet only needs to contain the constraints, margins and padding of the elements within a given ConstraintLayout.

In your Kotlin code, you can then apply ConstraintSet to your ConstraintLayout to update its layout.

To build an animation, you need to specify a single layout file and a ConstraintSet to act as the starting and ending keyframes. You can also apply transitions to make your animations a bit fancier.

Setting up the Starting Layout for Your Animation

In your project, duplicate your layout file and name the duplicate keyframe1.xml. You’re going to need to alter the positions of elements in this new layout and set this new layout as the starting layout for the app.

To start, open keyframe1.xml and change the layout_constraintGuide_begin property of guideline1 from 200dp to 0dp. This moves the guide, the elements constrained to the guide, and all elements constrained to them higher up, so that some of them are are now offscreen.

Then change the layout_constraintGuide_percent property of guideline2 from .05 to 1. This moves the guide and the elements constrained to it to the far right so that they are offscreen as well.

Now, we’ve changed the layout by just moving a couple of guides, but we still need to make this new layout the starting layout for the app. To do this, open MainActivity.kt and modify the setContentView() call in the onCreate() function to pass in R.layout.keyframe1 instead of R.layout.activity_main:


setContentView(R.layout.keyframe1)

Build and run your app. The orange switch and label and the arrival and destination space ports no longer appear on the screen. Additionally, the rocket and universe icons have moved up:
altered guidelines

Animating the View

Change the following import statement in your MainActivity.kt Kotlin class:

import import kotlinx.android.synthetic.main.activity_main.*

to the following:

import kotlinx.android.synthetic.main.keyframe1.*

This allows you to reference UI elements in the new layout XML without any findViewById() craziness from the pre-historic days of Android development. :]

Next, add the following private properties to the class. You may need to add the android.support.constraint.ConstraintSet import:

private val constraintSet1 = ConstraintSet()
private val constraintSet2 = ConstraintSet()

private var isOffscreen = true

The first two properties are the constraint sets that you’ll use to animate your view. You will use the boolean to keep track of the layout state.

Transition Manager

You can use the Transition Manager class to handle transitioning from one keyframe to another. To create a layout animation, you simply provide Transition Manager with the ConstraintSet you want to animate and it will handle the rest. Optionally, you can provide it with custom animations to perform.

Now, add the following to the onCreate() function, importing TransitionManager:


constraintSet1.clone(constraintLayout) //1
constraintSet2.clone(this, R.layout.activity_main) //2

departButton.setOnClickListener { //3
  //apply the transition
  TransitionManager.beginDelayedTransition(constraintLayout) //4
  val constraint = if (!isOffscreen) constraintSet1 else constraintSet2
  isOffscreen = !isOffscreen
  constraint.applyTo(constraintLayout) //5
}
  1. This pulls the layout information from the initial layout into one of the constraint sets, constraintSet1. Since you added an ID to the ConstraintLayout earlier, you can refer to it directly from code now.
  2. This pulls the layout information from the final layout into constraintSet2. Since you are creating a ConstraintSet and you never actually inflate the second layout file, you avoid the overhead and performance hit of dealing with a second layout.
  3. This adds the animation in the listener for the button, for now, so that you can trigger the animation whenever it’s toggled.
  4. This calls Transition Manager’s beingDelayedTransition function.
  5. This applies the new ConstraintSet to the currently displayed ConstraintLayout.

Build and run the app. Click the button at the bottom of the screen repeatedly to see how the animation works.

Voila! The app loads with a bunch of elements offscreen. When you tap the button, the guide positions animate, which causes everything constrained to them to animate as well.
guidelines animated

Animating the Bounds of a View

Not only can you change the position of elements onscreen by affecting their constraints, but you can also change their size.

Open keyframe1.xml and select the galaxy icon, whose ID is galaxyIcon. Change the layout_height property from 90dp to 10dp.

Note: In activity_main.xml, the height is still set to 90dp.

Build and run the app and tap the button at the bottom repeatedly. Now you can witness the expansion of the galaxy in action! :]
view bounds animated

Using Custom Transitions to Make Animation Easier

You now have a couple of animations tied to the switch, but wouldn’t it be nice for the view to animate automatically when it first loads?

You’ll do that next, but first you’ll create a custom animation instead of using the default animation, and you’ll also customize the animation’s timing.

Add the following function to MainActivity.kt, adding the import for android.transition.AutoTransition if it isn’t added automatically:


override fun onEnterAnimationComplete() { //1
  super.onEnterAnimationComplete()

  constraintSet2.clone(this, R.layout.activity_main) //2

  //apply the transition
  val transition = AutoTransition() //3
  transition.duration = 1000 //4
  TransitionManager.beginDelayedTransition(constraintLayout, transition) //5

  constraintSet2.applyTo(constraintLayout) //6
}
  1. Activities can’t draw anything while the view is animating. onEnterAnimationComplete() is the point in the app life cycle where the view animation has completed and it’s safe to call on drawing code.
  2. This pulls the layout information from your final layout into constraintSet2.
  3. This creates a custom transition. In this case, you are using a built-in transition, AutoTransition(), which first fades out disappearing targets, then moves and resizes existing targets, and finally fades in appearing targets.
  4. This sets a duration of 1,000 milliseconds for the animation, so that it’s slow enough to be seen.
  5. This calls Transition Manager’s beingDelayedTransition function, but this time you also supply your custom transition.
  6. This applies the new ConstraintSet to the currently-displayed ConstraintLayout.

Build and run the app. Now, all of the animations occur as soon as the view loads.
animations on load

Animating the Circular Constraint

Remember that funny circular constraint you added earlier? Time to add the grand finale animation by flying the rocket around the galaxy!

To animate the rocket around the galaxy, you have to alter two properties: the angle of the circular constraint, which moves the position of the rocket around the circle, and the rotation of the rocket to complete the illusion. You also check the One Way / Round Trip switch value to determine whether the rocket should fly half a circle or one full circle.

Replace the click listener for the DEPART button in onCreate() as follows:


departButton.setOnClickListener {
  //1
  val layoutParams = rocketIcon.layoutParams as ConstraintLayout.LayoutParams
  val startAngle = layoutParams.circleAngle
  val endAngle = startAngle + (if (switch1.isChecked) 360 else 180)

  //2
  val anim = ValueAnimator.ofFloat(startAngle, endAngle)
  anim.addUpdateListener { valueAnimator ->
    
    //3
    val animatedValue = valueAnimator.animatedValue as Float
    val layoutParams = rocketIcon.layoutParams as ConstraintLayout.LayoutParams
    layoutParams.circleAngle = animatedValue
    rocketIcon.layoutParams = layoutParams

    //4
    rocketIcon.rotation = (animatedValue % 360 - 270)
  }
  //5
  anim.duration = if (switch1.isChecked) 2000 else 1000
  
  //6
  anim.interpolator = LinearInterpolator()
  anim.start()
}
  1. Set startAngle to the current angle of the rocket before animation start. Depending on One Way / Round Trip switch, endAngle is either 180 or 360 degree in addition to startAngle value.
  2. ValueAnimator class provides a simple timing engine for running animations between two values. Here you provide startAngle and endAngle to create the instance of ValueAnimator.
  3. Inside update listener of ValueAnimator instance, obtain the animated value and assign it to the rocket’s circleAngle in layoutParams.
  4. Rotate the rocket with animated value. This will make the rocket fly in more natural direction.
  5. This is quite straightforward. One way animation takes 1 second, while round trip animation takes 2 seconds.
  6. Choose LinearInterpolator to make sure passengers have pleasant flight. You can try AnticipateOvershootInterpolator to see what will happen :] Last but not least, start the animation!

Build and run the app. Click the DEPART button and toggle the switch to make the rocket fly around the galaxy:
fly around the galaxy

Congratulations! You’ve built a complex UI layout for a space travel Android app, and you’ve added some cool animations to it with just a few lines of code.

Where to Go From Here?

You can download the final version of this project using the Download Materials button at the top or bottom of this tutorial.

If you enjoyed building the view animations, you might like to try the more sophisticated animation possibilities provided by MotionLayout, which is in alpha as of this writing. As always, you can also look at the documentation for ConstraintLayout.

For more on MotionLayout see this presentation: New Features in ConstraintLayout 2.0: Designing With Constraints — Sean McQuillan — Android Summit 2018 — YouTube.

Hopefully, you’ve enjoyed this tutorial. If you have any comments, feel free to hop into the forum below!



Source link https://www.raywenderlich.com/9475-constraintlayout-tutorial-for-android-complex-layouts

LEAVE A REPLY

Please enter your comment!
Please enter your name here