Add Static Fragment to Layout in Android using Kotlin

This tutorial will demonstrate how to add two fragments to an activity with their own UI and separate functionality. You’ll create a simple counter class that increments and decrements a number and a styling class that changes the style applied programmatically to some Hello World text. Perform the following steps:

1. Create an application in Android Studio with an empty activity called Fragment Intro. Then replace the content with the following strings required for the exercise in the res | values | strings.xml file:

< resources > < string name = "app_name" > Fragment Intro < /string>    Hello World
< string name = "bold_text" > Bold < /string>    Italic
< string name = "reset" > Reset < /string>    0
< string name = "plus" > + < /string>    - < string name = "counter_text" > Counter < /string>

These strings are used in both the counter fragment as well as the style fragment, which you will create next.

2. Add a new blank fragment by going to File | New | Fragment (Blank) called CounterFragment with layout name fragment_counter

3. Now make changes to the fragment_counter.xml file. To add the fields, you’ll need to create the counter in the Fragment class. The code below is truncated for space:fragment_counter.xml

< TextView10 android: id = "@+id/counter_text"
android: layout_width = "wrap_content"
android: layout_height = "wrap_content"
android: text = "@string/counter_text"
android: paddingTop = "10dp"
android: textSize = "44sp"
app: layout_constraintEnd_toEndOf = "parent"
app: layout_constraintStart_toStartOf = "parent"
app: layout_constraintTop_toTopOf = "parent" / > < TextView21 android: id = "@+id/counter"
android: layout_width = "wrap_content"
android: layout_height = "wrap_content"
android: text = "@string/zero"
android: textSize = "54sp"
android: textStyle = "bold"
app: layout_constraintEnd_toEndOf = "parent"
app: layout_constraintStart_toStartOf = "parent"
app: layout_constraintTop_toBottomOf = "@id/counter_text"
app: layout_constraintBottom_toTopOf = "@id/plus" / >

We are using a simple ConstraintLayout file that has TextViews set up for the header @+id/counter_text and the value of the android:id=”@+id/counter” (with a default of @string/zero), which will be changed by the android:id=”@+id/plus” and android:id=”@+id/minus” buttons.NoteFor a simple example like this, you are not going to set individual styles on the views with style=”@some_style” notation, which would be best practice to avoid repeating these values on each view.

4. Now open the CounterFragment and override the onViewCreated function. You will also need to add the following imports:

import android.widget.Buttonimport android.widget.TextViewoverride fun onViewCreated(view: View, savedInstanceState: Bundle ? ) {
  super.onViewCreated(view, savedInstanceState) val counter = view.findViewById < TextView > (R.id.counter) view.findViewById < Button > (R.id.plus).setOnClickListener {
    var counterValue = counter.text.toString().toInt() counter.text = (++counterValue).toString()
  }
  view.findViewById < Button > (R.id.minus).setOnClickListener {
    var counterValue = counter.text.toString().toInt() if (counterValue > 0) counter.text = (--counterValue).toString()
  }
}

We’ve added onViewCreated, which is the callback run when the layout has been applied to your fragment. The onCreateView callback, which creates the view, was run when the fragment itself was created. The buttons you’ve specified in the preceding fragment have click listeners set up on them to increment and decrement the value of the counter view.

Firstly, with this line, you are retrieving the current value of the counter as an integer:var counterValue = counter.text.toString().toInt()

Then with the following line, you are incrementing the value by 1 with the ++ notation:counter.text = (++counterValue).toString()As this is done by adding the ++ before the counterValue, it increments the integer value before it is cast to a string. If you didn’t do it this way, but instead did a post increment with counter++, the value would only be available the next time you used this value in a statement, which would reset the counter to the same value.

The line within the minus button click listener does a similar thing to the add click listener but decrements the value by 1:if (counterValue > 0) counter.text = (–counterValue).toString()You only do the operation if the value is greater than 0 so that no negative numbers are set.

5. You have not added the fragment to the MainActivity layout. To do this, go into the activity_main.xml and add the following code:

< ? xml version = "1.0"
encoding = "utf-8" ? > < LinearLayout xmlns : android = "http://schemas.android.com/apk/res/ android"
xmlns: tools = "http://schemas.android.com/tools"
android: layout_width = "match_parent"
android: layout_height = "match_parent"
android: orientation = "vertical"
tools: context = ".MainActivity" > < fragment android: id = "@+id/counter_fragment"
android: name = "com.example.fragmentintro.CounterFragment"
android: layout_width = "match_parent"
android: layout_height = "match_parent" / > < /LinearLayout>

6. You are going to change the layout from a FrameLayout to a LinearLayout as you will need this to put one fragment above the other when you add the next fragment. You specify the fragment to be used within the fragment XML element by the name attribute with the fully qualified package name used for the class: android:name=”com.example.fragmentintro.CounterFragment. If you used a different package name when you created the app, then this must refer to the CounterFragment you created. The important thing to grasp here is that you have added a fragment to your main activity layout and the fragment also has a layout. This shows some of the power of using fragments as you can encapsulate the functionality of one feature of your app, complete with a layout file and fragment class, and add it to multiple activities. Once you’ve done this, run the fragment in the virtual device as in Figure 3.6:Figure 3.6: App displaying the counter fragment
Figure 3.6: App displaying the counter fragment you have created a simple counter. The basic functionality works as expected, incrementing and decrementing a counter value.

In the next step, you are going to add another fragment to the bottom half of the screen. This demonstrates the versatility of fragments. You can have encapsulated pieces of UI with functionality and features in different areas of the screen.

7. Now create a new fragment using the earlier steps for creating the CounterFragment called StyleFragment with layout name fragment_style.

8. Next, open up the fragment_style.xml file that has been created and replace the contents with the code at the link below. The snippet shown below is truncated – see the link for the full code:fragment_style.xml

<TextView
    android:id="@+id/hello_world"
    android:layout_width="wrap_content"
    android:layout_height="0dp"
    android:textSize="34sp"
    android:paddingBottom="12dp"
    android:text="@string/hello_world"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />


<Button
    android:id="@+id/bold_button"
    android:layout_width="wrap_content"
    android:layout_height="0dp"
    android:textSize="24sp"
    android:text="@string/bold_text"
    app:layout_constraintEnd_toStartOf="@+id/italic_button"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/hello_world" />

The layout adds a TextView with three buttons. The TextView text and the text for all the buttons are set as string resources (@string).

9. Next, go into the activity_main.xml file and add the StyleFragment below the CounterFragment inside the LinearLayout:

< ? xml version = "1.0"
encoding = "utf-8" ? > < LinearLayout xmlns : android = "http://schemas.android.com/apk/res/ android"
xmlns: tools = "http://schemas.android.com/tools"
android: layout_width = "match_parent"
android: layout_height = "match_parent"
android: orientation = "vertical"
tools: context = ".MainActivity" > < fragment android: id = "@+id/counter_fragment"
android: name = "com.example.fragmentintro.CounterFragment"
android: layout_width = "match_parent"
android: layout_height = "match_parent" / > < fragment android: id = "@+id/style_fragment"
android: name = "com.example.fragmentintro.StyleFragment"
android: layout_width = "match_parent"
android: layout_height = "match_parent" / > < /LinearLayout>

When you run the app, you will see that the StyleFragment is not visible, as in Figure 3.7:Figure 3.7: App shown without the StyleFragment displayed 
Figure 3.7: App shown without the StyleFragment displayedYou’ve included the StyleFragment in the layout, but because the CounterFragment has its width and height set to match its parent (android:layout_width=”match_parent android:layout_height=”match_parent”) and it is the first view in the layout, it takes up all the space.What you need is some way to specify the proportion of the height that each fragment should occupy. The LinearLayout orientation is set to vertical so the fragments will appear one on top of the other when the layout_height is not set to match_parent. In order to define the proportion of this height, you need to add another attribute layout_weight to each fragment in the activity_main.xml layout file. When you use layout_weight to determine this proportional height, the fragments should occupy you set the layout_height of the fragments to 0dp.

10. Update the activity_main.xml layout with the following changes setting the layout_height of both fragments to 0dp and adding the layout_weight attributes with the values below:

< ? xml version = "1.0"
encoding = "utf-8" ? > < LinearLayout xmlns : android = "http://schemas.android.com/apk/res/ android"
xmlns: tools = "http://schemas.android.com/tools"
android: layout_width = "match_parent"
android: layout_height = "match_parent"
android: orientation = "vertical"
tools: context = ".MainActivity" > < fragment android: id = "@+id/counter_fragment"
android: name = "com.example.fragmentintro.CounterFragment"
android: layout_width = "match_parent"
android: layout_height = "0dp"
android: layout_weight = "2" / > < fragment android: id = "@+id/style_fragment"
android: name = "com.example.fragmentintro.StyleFragment"
android: layout_width = "match_parent"
android: layout_height = "0dp"
android: layout_weight = "1" / > < /LinearLayout>

These changes make the CounterFragment occupy twice the height of the StyleFragment, as shown in Figure 3.8:Figure 3.8: CounterFragment with twice the amount of vertical space allocated
Figure 3.8: CounterFragment with twice the amount of vertical space allocated you can experiment by changing the weight values to see the differences you can make to the display of the layout.

11. At this point, pressing the styling buttons Bold and Italic will have no effect on the text Hello World. The button actions have not been specified. The next step involves adding interactivity to the buttons to make changes to the style of the Hello World text. Add the following onViewCreated function, which overrides its parent to add behavior to the fragment after the layout view has been set up. You will also need to add the following widgets and the typeface import to change the style for the text:

import android.widget.Buttonimport android.widget.TextViewimport android.graphics.Typefaceoverride fun onViewCreated(view: View, savedInstanceState: Bundle ? ) {
  super.onViewCreated(view, savedInstanceState) val boldButton = view.findViewById < Button > (R.id.bold_button) val italicButton = view.findViewById < Button > (R.id.italic_button) val resetButton = view.findViewById < Button > (R.id.reset_button) val helloWorldTextView = view.findViewById < TextView > (R.id.hello_world) boldButton.setOnClickListener {
    if (helloWorldTextView.typeface?.isItalic == true) helloWorldTextView.setTypeface(helloWorldTextView.typeface, Typeface.BOLD_ITALIC)
    else helloWorldTextView.setTypeface(null, Typeface.BOLD)
  }
  italicButton.setOnClickListener {
    if (helloWorldTextView.typeface?.isBold == true) helloWorldTextView.setTypeface(helloWorldTextView.typeface, Typeface.BOLD_ITALIC)
    else helloWorldTextView.setTypeface(null, Typeface.ITALIC)
  }
  resetButton.setOnClickListener {
    helloWorldTextView.setTypeface(null, Typeface.NORMAL)
  }
}

Here, you add click listeners to each button defined in the layout and set the Hello World text to the desired Typeface. (In this context, Typeface refers to the style which will be applied to the text and not a font). The conditional statement for the bold_button checks whether the italic Typeface is set and if it is, to make the text bold and italic, and if not, just make the text bold. This logic works the opposite way for the italic_button, checking the state of the Typeface and making the corresponding changes to the Typeface, initially setting it to italic if no TypeFace is defined.

12. Finally, the reset_button clears the Typeface and sets it back to normal. Run the app up and click the ITALIC and BOLD buttons. You should see a display as in Figure 3.9:Figure 3.9: StyleFragment setting text to bold and italic

Figure 3.9: StyleFragment setting text to bold and italic

This tutroial, although simple, has demonstrated some fundamental concepts of using fragments. The features of your app that the user can interact with can be developed independently and not rely on bundling two or more features into one layout and activity. This makes fragments reusable and means you can focus your attention when developing your app on adding well-defined UI, logic, and features into a single fragment.

Written by

XR Developer responsible for end-to-end development of XR solutions spanning multiple domains, by using various XR and WebXR libraries.

Leave a Reply