In case you haven’t read the Compose introduction yet, I suggest you read this article
As promised, here I am with a second article about Jetpack Compose. We now start to get a little more technical by seeing the first components made available by the framework.
At the base of every single app we have the Labels and the Buttons, the simplest components that can be used to compose the UI of our app.
Let’s start with the Label.
Who among you has never found yourself having to add a TextView
to an xml to display a label? The steps, on the agenda for millions of developers worldwide, would be: Inserting a TextView in the xml, associate it with an id, retrieve the associated view via findViewById
and, if necessary, store it in our Fragment/Activity/Custom View. Also remembering the need to inflate the layout. Now, though, you can say goodbye to all of that and add a label in two or three lines of code.
First of all, a premise must be made: Each graphic component you want to create or use must be “contained” in a method annotated with @Composable
for example
@Composable fun MyFirstComposeLabel() { //Your code here }
By doing so, the compiler and the Linter are able to recognize the Compose workspace by suggesting the usable methods. As will also be suggested by the Linter, the name of a method annotated with @Composable
must have the first letter capitalized while the name of a method, which however returns any object (a view, a string, etc), must have the first lowercase letter for example:
@Composable fun myFirstAnnotatedString(): AnnotatedString { //Your code here }
Returning to our labels; Let’s create a method that will be used to render our first label
@Composable fun MyFirstComposeLabel() { //Your code here }
and inside it we are going to add the component called Text
equivalent to the old TextView
@Composable fun MyFirstComposeLabel() { Text() }
At this point Android Studio will suggest different ways of using this component such as using it through a classic String
or an AnnotatedString
, with which you have a high level of customization (font size, color, etc.) for the entire text or for only a few portions.
We start by using a normal String
and by declaration we use the text
parameter assigning it the desired string
@Composable fun MyFirstComposeLabel() { Text( text = "Hello World" ) }
In case the string has been inserted in a resource file, it is possible to use the stringResource
method provided by the framework
@Composable fun MyFirstComposeLabel() { Text(text = stringResource(R.string.name_of_desired_string)) }
As for the AnnotatedString
the code gets complicated by a few lines to write.
As I mentioned earlier, the AnnotatedString
are the equivalent of the old SpannableString
with which you can get a high level of customization of the strings: text portions in bold, clickable portions and much more.
So let’s create our usual method and call the builder for an AnnotatedString
using the buildAnnotatedString
method.
@Composable fun MyFirstComposeLabel() { val myAnnotatedString = buildAnnotatedString { } Text(text = myAnnotatedString) }
This way, within our builder, we will have the possibility to use methods like append
, withStyle
and withAnnotation
.
In this example we want to highlight the portion of text consisting of “privacy policy” in the sentence “By registering you accept the conditions described in our corporate privacy policy” using the append
and withStyle
methods.
Strings are not retrieved from resources for simplicity, but even when creating an AnnotatedString
you can get your resource using the stringResource
method.
The result will be:
@Composable fun MyFirstComposeLabel() { val myAnnotatedString = buildAnnotatedString { append(“By registering you accept the conditions described in our corporate ”) append(“ “) //Let’s add a blank space between our strings added via `append` withStyle( style = SpanStyle(fontWeight = FontWeight.Bold) ) { append(“privacy policy”) } } Text(text = myAnnotatedString) }
which printed on the screen will be: By registering you accept the conditions described in our corporate privacy policy.
Making a small comparison, to get a label on a screen with the old framework the steps would have been
- Adding TextView to xml layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id=”id_text_view” android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/string_name" /> </LinearLayout>
- Inflate of the layout (for example in a fragment):
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? = inflater.inflate(R.layout.layout_id, container, false)
- In case you want to modify the text added directly with the resource in the XML file:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { view.findViewById<TextView>(R.id.id_text_view).text = "New text" }
With a significant increase in lines of code for very complex layout. Obviously over the years Google has introduced new methods to try to simplify these steps, for example introducing the ViewBinding/DataBinding (with a concept of updating the ui very similar to the state management present in Compose which we will discuss in the next articles).
Now let’s move on to the Buttons!
The basic clickable button of the framework is called Button
and has several parameters to customize it including: shape, colors, border and many others.
There are also different types of Button
such as TextButton
and OutlinedButton
that already include a theme that follows the guideline of the Material Design, so as not to have to create the button from scratch every time. 🥳
In our example we will use a standard Button
, so let’s create our usual method and insert the Button
.
@Composable fun MyFirstComposeButton() { Button(onClick = { /*TODO*/ }) { //Content to add (for example a Label) } }
By default, the Button
provides a “content” parameter for the addition of text that is annotated internally with @Composable
, in order to allow you to add a label of your choice. The method provides you a “RowScope”
, resulting in the possibility of putting multiple elements on a single line such as Icon (left) + Text, Text only, Text + Icon (right), etc.
Thus obtaining a method of this type:
@Composable fun MyFirstComposeButton() { Button(onClick = { /*TODO*/ }) { Text(text = "Click Me!") Icon(painter = painterResource(id = R.drawable.resource_name), contentDescription = "") } }
Now we have a button with a text and an icon of your choice to the right of the text, but it will not perform any operation. How to do that? Simple, just invoke the method or perform the action of your choice inside the lambda generated by the onClick
parameter of our button, as you can see below:
@Composable fun MyFirstComposeButton() { var counter = 0 Button(onClick = { counter++ }) { Text(text = "Click Me!") Icon(painter = painterResource(id = R.drawable.resource_name), contentDescription = "") } }
Wanting to make a comparison as done for the label, in order to add a button with the old framework and manage its click, the steps would have been:
- Adding Button to a XML layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <Button android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Click Me!"/> </LinearLayout>
- Inflate of the layout (for example in a fragment):
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? = inflater.inflate(R.layout.layout_id, container, false)
- Handle button click:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { var counter = 0 view.findViewById<Button>(R.id.id_button).setOnClickListener { counter++ } }
As you can see from the two comparisons I have proposed, you can appreciate the huge savings in lines of code and the simplicity of writing an app using Compose. 😉
Now it’s your turn, try using Text
and Button
and have fun customising them as much as possible! 😎
Greetings to all of you and see you again in the next “chapter”! 🧑🏻💻🤫