Open In App

Android Jetpack Compose – Implement Different Types of Styles and Customization to Text

Last Updated : 26 Apr, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Jetpack Compose is a new UI toolkit from Google used to create native Android UI. It speeds up and simplifies UI development using less code, Kotlin APIs, and powerful tools.

Prerequisites:

  1. Familiar with Kotlin and OOP Concepts as well
  2. Basic understanding of Jetpack Compose
  3. Android Studio Canary Version

A sample video is given below to get an idea about what we are going to do in this article.

Step-by-Step Implementation

Step 1: Create a New Project in Android Studio

To create a new project in Android Studio using Jetpack Compose please refer to: How to Create a New Project in Android Studio Canary Version with Jetpack Compose.

Step 2: Let’s first review the build.gradle(module level)

Now go to the module-level build.gradle file & check on dependencies. If any of the dependencies are missing add them from the below snippet. Remember to double-check this file that everything is included. If something is missing just add those blocks from the below snippets.

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}

android {
    namespace 'com.example.textvariant'
    compileSdk 33

    defaultConfig {
        applicationId "com.example.textvariant"
        minSdk 21
        targetSdk 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary true
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
    buildFeatures {
        compose true
    }
    composeOptions {
        kotlinCompilerExtensionVersion compose_version
    }
    packagingOptions {
        resources {
            excludes += '/META-INF/{AL2.0,LGPL2.1}'
        }
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.9.0'
    implementation "androidx.compose.ui:ui:$compose_version"
    implementation "androidx.compose.material:material:$compose_version"
    implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
    implementation 'androidx.activity:activity-compose:1.6.1'


    implementation 'androidx.appcompat:appcompat:1.5.1'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.4'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
    androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
    debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
    debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0-RC"
    implementation'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'

}

Step 3: Review the build.gradle(project level)

Now go to the project-level build.gradle file & check on the buildscript code block. If any of the requirements are missing then add them from the below snippet.

buildscript {
    ext {
        kotlin_version = '1.0.1-2'
        compose_version = '1.1.0-rc01'
    }
    // Requirements
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" //dependency
    }
}// Top-level build file where you can add configuration options common to all sub-projects/modules.

plugins {
    id 'com.android.application' version '7.3.0-alpha01' apply false
    id 'com.android.library' version '7.3.0-alpha01' apply false
    id 'org.jetbrains.kotlin.android' version '1.6.0' apply false
}

Step 4: Rename MainActivity.kt to CustomTextActivity.kt

We can put the same code to MainActivity.kt as well, but it’s a good idea to create or rename the file to reflect its role. Once you change this we also need to modify the AndroidManifest.xml activity tag to the renamed file since the default is MainActivity. You can refer to the below snippet of AndroidManifest.xml.

XML




<?xml version="1.0" encoding="utf-8"?>
    xmlns:tools="http://schemas.android.com/tools">
 
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AppCompat.Light"
        tools:targetApi="31">
        <activity
            android:name=".CustomTextActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.AppCompat.Light">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>


Step 5: Importing necessary modules

It’s good practice to import only the necessary modules rather than importing all the modules and using only a few. 

Kotlin




// Implementing Different types of
// styles and customization on text
 
// Please replace the name of package
// with your project name
package com.example.textvariant
 
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Divider
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextIndent
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp


Step 6: Implement AppCompatActivity() to class CustomTextActivity

SetContent() block will set the activity component function as the root view of the all activities that we declared. The separate composable functions for each text variant will have their respective logic.

Kotlin




class CustomTextActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // This sets the @Composable function as
        // the root view of the activity.
        // This is meant to replace the .xml
        // file that we would typically
        // set using the setContent(R.id.xml_file) method.
        setContent {
            // We create a ScrollState that's "remember"ed
            // to add proper support for a scrollable component.
            // This allows us to also control the scroll position
            // and other scroll related properties.
 
            // remember calculates the value passed to it only during
            // the first composition. It then
            // returns the same value for every subsequent composition.
            val scrollState = rememberScrollState()
            Column(
                modifier = Modifier.verticalScroll(scrollState)
            ) {
                // This is a custom composable to show different
                // text customization in a column manner
 
                SimpleText()
 
                TextWithColor()
 
                TextWithBiggerFontSize()
 
                BoldText()
 
                ItalicText()
 
                TextWithCustomFontFamily()
 
                TextWithUnderline()
 
                TextWithStrikeThrough()
 
                TextWith1MaxLine()
 
                TextWithShadow()
 
                CenterTextAlign()
                // A pre-defined composable that renders
                // a thin line on the screen that makes it
                // easy to group contents
                Divider(color = Color.Gray)
 
                JustifyTextAlign()
 
                ModifiedTextIntent()
 
                ModifiedLineHeightText()
 
                CustomAnnotatedText()
 
                Divider(color = Color.Gray)
 
                TextWithBackground()
            }
        }
    }


Step 7: Create composable functions for making unique customizations of texts in different styles

Before we go, there are a few things you should be aware of:

  • Composable annotations: The @Composable annotation is used to indicate a Composable function. Composable functions can only be invoked from other composable functions. Consider composable functions to be comparable to Lego blocks in that each composable function is constructed up of smaller composable functions.
    • Reacting to state changes is the core behavior of Compose. We use the state composable that is used for holding a state value in this composable for representing the current value scale(for zooming in the image) & translation(for panning across the image). Any composable that reads the value of the counter will be recomposed any time the value changes.
    • Row: The row is composable and places its children horizontally. It is comparable to a LinearLayout in that it is horizontally oriented. Additionally, we add a modifier to the row
    • Column: The column is composable and places its children vertically. It is comparable to a LinearLayout in that it is vertically oriented. Additionally, we add a modifier to the column.
    • Modifier:  Modifiers serve as examples of the decorator pattern and are used to alter the composable to which they are applied. In this case, we use the Modifier to set the Column up to take up the whole available width and height using the Modifier.fillMaxSize() modifier.
    • Surface: It’s typically used to modify the backdrop color, and add elevation, clip, or form to its children’s composable.
    • Text: Display text on the screen. It allows you to customize its appearance using the style property. 
      • TextStyle: TextStyle allows you to specify styling configuration for a `Text` composable. Types of props TextStyle takes in are given below
        1. color => can change the text color
        2. fontSize => can change the font size of text
        3. fontWeight => can change the font weight of the text
        4. fontStyle => can change font style(eg: Italic)
        5. fontFamily => can change font family(eg:Serif)
        6. textDecoration => can change look of text(eg:underlined text)
        7. shadow => gives shadow to the text
        8. textAlign => alignment of text
        9. textIndent => gives indentation to text
        10. lineHeight => height of text

Kotlin




@Composable
    private fun SimpleText() {
        CustomStyledText(
            "This is the default text style"
        )
    }
 
    @Composable
    private fun TextWithColor() {
        CustomStyledText(
            "This text is blue in color",
            style = TextStyle(
                color = Color.Blue
            )
        )
    }
 
    @Composable
    private fun TextWithBiggerFontSize() {
        CustomStyledText(
            "This text has a bigger font size",
            style = TextStyle(
                fontSize = 30.sp
            )
        )
    }
 
    @Composable
    private fun BoldText() {
        CustomStyledText(
            "This text is bold",
            style = TextStyle(
                fontWeight = FontWeight.W700
            )
        )
    }
 
    @Composable
    private fun ItalicText() {
        CustomStyledText(
            "This text is italic",
            style = TextStyle(
                fontStyle = FontStyle.Italic
            )
        )
    }
 
    @Composable
    private fun TextWithCustomFontFamily() {
        CustomStyledText(
            "This text is using a custom font family",
            style = TextStyle(
                fontFamily = FontFamily.Cursive
            )
        )
    }
 
    @Composable
    private fun TextWithUnderline() {
        CustomStyledText(
            "This text has an underline",
            style = TextStyle(
                textDecoration = TextDecoration.Underline
            )
        )
    }
 
    @Composable
    private fun TextWithStrikeThrough() {
        CustomStyledText(
            "This text has a strikethrough line",
            style = TextStyle(
                textDecoration = TextDecoration.LineThrough
            )
        )
    }
 
    @Composable
    private fun TextWith1MaxLine() {
        CustomStyledText(
            "This text will add an ellipsis to the end " +
                    "of the text if the text is longer than 1 line long.",
            maxLines = 1
        )
    }
 
    @Composable
    private fun TextWithShadow() {
        CustomStyledText(
            "This text has a shadow",
            style = TextStyle(
                shadow = Shadow(
                    color = Color.Black, blurRadius = 10f,
                    offset = Offset(2f, 2f)
                )
            )
        )
    }
 
    @Composable
    private fun CenterTextAlign() {
        Row(modifier = Modifier.fillMaxWidth()) {
            Text(
                text = "This text is center aligned",
                style = TextStyle(
                    textAlign = TextAlign.Center
                ),
                modifier = Modifier.padding(16.dp)
            )
        }
    }
 
    @Composable
    private fun JustifyTextAlign() {
        CustomStyledText(
            "This text will demonstrate how to justify " +
                    "your paragraph to ensure that the text that ends with a soft " +
                    "line break spreads and takes the entire width of the container",
            style = TextStyle(
                textAlign = TextAlign.Justify
            )
        )
    }
 
    @Composable
    private fun ModifiedTextIntent() {
        CustomStyledText(
            "This text will demonstrate how to add " +
                    "indentation to your text. In this example, indentation was only " +
                    "added to the first line. You also have the option to add " +
                    "indentation to the rest of the lines if you'd like",
            style = TextStyle(
                textAlign = TextAlign.Justify,
                textIndent = TextIndent(firstLine = 30.sp)
            )
        )
    }
 
    @Composable
    private fun ModifiedLineHeightText() {
        CustomStyledText(
            "The line height of this text has been " +
                    "increased hence you will be able to see more space between each " +
                    "line in this paragraph.",
            style = TextStyle(
                textAlign = TextAlign.Justify,
                lineHeight = 20.sp
            )
        )
    }
 
    @Composable
    private fun CustomAnnotatedText() {
        val annotatedString = buildAnnotatedString {
            append("This string has style spans")
            addStyle(style = SpanStyle(color = Color.Red), start = 0, end = 4)
            addStyle(style = SpanStyle(color = Color.Green), start = 5, end = 21)
            addStyle(style = SpanStyle(color = Color.Blue), start = 22, end = 27)
        }
        Text(annotatedString, modifier = Modifier.padding(16.dp))
    }
 
    @Composable
    private fun TextWithBackground() {
        Surface(color = Color.Yellow) {
            Text(
                text = "This text has a background color",
                modifier = Modifier.padding(16.dp)
            )
        }
    }
}
 
@Composable
fun CustomStyledText(displayText: String, style: TextStyle? = null, maxLines: Int? = null) {
    Text(
        text = displayText,
        modifier = Modifier.padding(16.dp),
        style = style ?: TextStyle.Default,
        overflow = TextOverflow.Ellipsis,
        maxLines = maxLines ?: Int.MAX_VALUE
    )
    Divider(color = Color.Gray)
}


Step 7: If you want to preview your CustomTextActivity then continue else you can skip it

Significance of @preview and @composable annotations :

  • Instead of needing to download the app to an Android device or emulator, Android Studio allows you to preview your composable functions within the IDE itself. This is an excellent feature since it allows you to preview every one of your components—or composable functions—right inside the IDE
    • The composable function cannot accept any parameters, which is the fundamental constraint. You may just include your component within another composable function that doesn’t take any arguments and calls your composable function with the necessary parameters
    • Also, don’t forget to annotate it with @Preview & @Composable annotations

Kotlin




@Preview
@Composable
fun CustomStyledTextPreview() {
    CustomStyledText(
        "This is preview text",
        maxLines = 2,
        style = TextStyle(
            color = Color.Red,
            fontWeight = FontWeight.W900,
            fontStyle = FontStyle.Italic,
            fontFamily = FontFamily.Serif,
            fontSize = 20.sp,
            textAlign = TextAlign.Justify
        )
    )
}


Step 8: Complete Code Snippet

Kotlin




// Implementing Different types of
// styles and customization on text
 
// Please replace the name of package
// with your project name
package com.example.textvariant
 
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Divider
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextIndent
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
 
class CustomTextActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // This sets the @Composable function as
        // the root view of the activity.
        // This is meant to replace the .xml
        // file that we would typically
        // set using the setContent(R.id.xml_file) method.
        setContent {
            // We create a ScrollState that's "remember"ed
            // to add proper support for a scrollable component.
            // This allows us to also control the scroll position
            // and other scroll related properties.
 
            // remember calculates the value passed to it only during
            // the first composition. It then
            // returns the same value for every subsequent composition.
            val scrollState = rememberScrollState()
            Column(
                modifier = Modifier.verticalScroll(scrollState)
            ) {
                // This is a custom composable to show different
                // text customization in a column manner
 
                SimpleText()
 
                TextWithColor()
 
                TextWithBiggerFontSize()
 
                BoldText()
 
                ItalicText()
 
                TextWithCustomFontFamily()
 
                TextWithUnderline()
 
                TextWithStrikeThrough()
 
                TextWith1MaxLine()
 
                TextWithShadow()
 
                CenterTextAlign()
                // A pre-defined composable that renders
                // a thin line on the screen that makes it
                // easy to group contents
                Divider(color = Color.Gray)
 
                JustifyTextAlign()
 
                ModifiedTextIntent()
 
                ModifiedLineHeightText()
 
                CustomAnnotatedText()
 
                Divider(color = Color.Gray)
 
                TextWithBackground()
            }
        }
    }
 
    @Composable
    private fun SimpleText() {
        CustomStyledText(
            "This is the default text style"
        )
    }
 
    @Composable
    private fun TextWithColor() {
        CustomStyledText(
            "This text is blue in color",
            style = TextStyle(
                color = Color.Blue
            )
        )
    }
 
    @Composable
    private fun TextWithBiggerFontSize() {
        CustomStyledText(
            "This text has a bigger font size",
            style = TextStyle(
                fontSize = 30.sp
            )
        )
    }
 
    @Composable
    private fun BoldText() {
        CustomStyledText(
            "This text is bold",
            style = TextStyle(
                fontWeight = FontWeight.W700
            )
        )
    }
 
    @Composable
    private fun ItalicText() {
        CustomStyledText(
            "This text is italic",
            style = TextStyle(
                fontStyle = FontStyle.Italic
            )
        )
    }
 
    @Composable
    private fun TextWithCustomFontFamily() {
        CustomStyledText(
            "This text is using a custom font family",
            style = TextStyle(
                fontFamily = FontFamily.Cursive
            )
        )
    }
 
    @Composable
    private fun TextWithUnderline() {
        CustomStyledText(
            "This text has an underline",
            style = TextStyle(
                textDecoration = TextDecoration.Underline
            )
        )
    }
 
    @Composable
    private fun TextWithStrikeThrough() {
        CustomStyledText(
            "This text has a strikethrough line",
            style = TextStyle(
                textDecoration = TextDecoration.LineThrough
            )
        )
    }
 
    @Composable
    private fun TextWith1MaxLine() {
        CustomStyledText(
            "This text will add an ellipsis to the end " +
                    "of the text if the text is longer than 1 line long.",
            maxLines = 1
        )
    }
 
    @Composable
    private fun TextWithShadow() {
        CustomStyledText(
            "This text has a shadow",
            style = TextStyle(
                shadow = Shadow(
                    color = Color.Black, blurRadius = 10f,
                    offset = Offset(2f, 2f)
                )
            )
        )
    }
 
    @Composable
    private fun CenterTextAlign() {
        Row(modifier = Modifier.fillMaxWidth()) {
            Text(
                text = "This text is center aligned",
                style = TextStyle(
                    textAlign = TextAlign.Center
                ),
                modifier = Modifier.padding(16.dp)
            )
        }
    }
 
    @Composable
    private fun JustifyTextAlign() {
        CustomStyledText(
            "This text will demonstrate how to justify " +
                    "your paragraph to ensure that the text that ends with a soft " +
                    "line break spreads and takes the entire width of the container",
            style = TextStyle(
                textAlign = TextAlign.Justify
            )
        )
    }
 
    @Composable
    private fun ModifiedTextIntent() {
        CustomStyledText(
            "This text will demonstrate how to add " +
                    "indentation to your text. In this example, indentation was only " +
                    "added to the first line. You also have the option to add " +
                    "indentation to the rest of the lines if you'd like",
            style = TextStyle(
                textAlign = TextAlign.Justify,
                textIndent = TextIndent(firstLine = 30.sp)
            )
        )
    }
 
    @Composable
    private fun ModifiedLineHeightText() {
        CustomStyledText(
            "The line height of this text has been " +
                    "increased hence you will be able to see more space between each " +
                    "line in this paragraph.",
            style = TextStyle(
                textAlign = TextAlign.Justify,
                lineHeight = 20.sp
            )
        )
    }
 
    @Composable
    private fun CustomAnnotatedText() {
        val annotatedString = buildAnnotatedString {
            append("This string has style spans")
            addStyle(style = SpanStyle(color = Color.Red), start = 0, end = 4)
            addStyle(style = SpanStyle(color = Color.Green), start = 5, end = 21)
            addStyle(style = SpanStyle(color = Color.Blue), start = 22, end = 27)
        }
        Text(annotatedString, modifier = Modifier.padding(16.dp))
    }
 
    @Composable
    private fun TextWithBackground() {
        Surface(color = Color.Yellow) {
            Text(
                text = "This text has a background color",
                modifier = Modifier.padding(16.dp)
            )
        }
    }
}
 
@Composable
fun CustomStyledText(displayText: String, style: TextStyle? = null, maxLines: Int? = null) {
    Text(
        text = displayText,
        modifier = Modifier.padding(16.dp),
        style = style ?: TextStyle.Default,
        overflow = TextOverflow.Ellipsis,
        maxLines = maxLines ?: Int.MAX_VALUE
    )
    Divider(color = Color.Gray)
}
 
@Preview
@Composable
fun CustomStyledTextPreview() {
    CustomStyledText(
        "This is preview text",
        maxLines = 2,
        style = TextStyle(
            color = Color.Red,
            fontWeight = FontWeight.W900,
            fontStyle = FontStyle.Italic,
            fontFamily = FontFamily.Serif,
            fontSize = 20.sp,
            textAlign = TextAlign.Justify
        )
    )
}


Output:

As we can see with the help of jetpack compose we can make different customization with their own unique style of formatting.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads