Tuesday, December 13, 2022
HomeSocial MediaHighlighting Textual content Enter with Jetpack Compose

Highlighting Textual content Enter with Jetpack Compose


We lately launched a brand new function at Buffer, known as Concepts. With Concepts, you possibly can retailer all of your finest concepts, tweak them till they’re prepared, and drop them straight into your Buffer queue. Now that Concepts has launched in our net and cellular apps, we’ve got a while to share some learnings from the event of this function. On this weblog put up, we’ll dive into how we added assist for URL highlighting to the Concepts Composer on Android, utilizing Jetpack Compose.


We began adopting Jetpack Compose into our app in 2021 – utilizing it as commonplace to construct all our new options, whereas step by step adopting it into present components of our utility. We constructed the entire of the Concepts function utilizing Jetpack Compose – so alongside sooner function improvement and larger predictability throughout the state of our UI, we had loads of alternatives to additional discover Compose and study extra about tips on how to obtain sure necessities in our app.

Throughout the Concepts composer, we assist dynamic hyperlink highlighting. Because of this in case you sort a URL into the textual content space, then the hyperlink will likely be highlighted – tapping on this hyperlink will then present an “Open hyperlink” pop-up, which can launch the hyperlink within the browser when clicked.

On this weblog put up, we’re going to give attention to the hyperlink highlighting implementation and the way this may be achieved in Jetpack Compose utilizing the TextField composable.


For the Concepts composer, we’re utilising the TextField composable to assist textual content entry. This composable accommodates an argument, visualTransformation, which is used to use visible adjustments to the entered textual content.

TextField(
    ...
    visualTransformation = ...
)

This argument requires a VisualTransformation implementation which is used to use the visible transformation to the entered textual content. If we have a look at the supply code for this interface, we’ll discover a filter operate which takes the content material of the TextField and returns a TransformedText reference that accommodates the modified textual content.

@Immutable
enjoyable interface VisualTransformation {
    enjoyable filter(textual content: AnnotatedString): TransformedText
}

With regards to this modified textual content, we’re required to offer the implementation that creates a brand new AnnotatedString reference with our utilized adjustments. This modified content material then will get bundled within the TransformedText sort and returned again to the TextField for composition.

In order that we will outline and apply transformations to the content material of our TextField, we have to begin by creating a brand new implementation of the VisualTransformation interface for which we’ll create a brand new class, UrlTransformation. This class will implement the VisualTransformation argument, together with taking a single argument within the type of a Colour. We outline this argument in order that we will move a theme colour reference to be utilized inside our logic, as we’re going to be outdoors of composable scope and gained’t have entry to our composable theme.

class UrlTransformation(
    val colour: Colour
) : VisualTransformation {

}

With this class outlined, we now have to implement the filter operate from the VisualTransformation interface. Inside this operate we’re going to return an occasion of the TransformedText class – we will leap into the supply code for this class and see that there are two properties required when instantiating this class.

/**
 * The reworked textual content with offset offset mapping
 */
class TransformedText(
    /**
     * The reworked textual content
     */
    val textual content: AnnotatedString,

    /**
     * The map used for bidirectional offset mapping from authentic to reworked textual content.
     */
    val offsetMapping: OffsetMapping
)

Each of those arguments are required, so we’re going to wish to offer a price for every when instantiating the TransformedText class.

  • textual content – this would be the modified model of the textual content that’s supplied to the filter operate
  • offsetMapping – as per the documentation, that is the map used for bidirectional offset mapping from authentic to reworked textual content
class UrlTransformation(
    val colour: Colour
) : VisualTransformation {
    override enjoyable filter(textual content: AnnotatedString): TransformedText {
        return TransformedText(
            ...,
            OffsetMapping.Identification
        )
    }
}

For the offsetMapping argument, we merely move the OffsetMapping.Identification worth – that is the predefined default worth used for the OffsetMapping interface, used for when that can be utilized for the textual content transformation that doesn’t change the character depend. With regards to the textual content argument we’ll want to jot down some logic that can take the present content material, apply the highlighting and return it as a brand new AnnotatedString reference to be handed into our TransformedText reference. For this logic, we’re going to create a brand new operate, buildAnnotatedStringWithUrlHighlighting. That is going to take two arguments – the textual content that’s to be highlighted, together with the colour for use for the highlighting.

enjoyable buildAnnotatedStringWithUrlHighlighting(
    textual content: String, 
    colour: Colour
): AnnotatedString {
    
}

From this operate, we have to return an AnnotatedString reference, which we’ll create utilizing buildAnnotatedString. Inside this operate, we’ll begin through the use of the append operation to set the textual content material of the AnnotatedString.

enjoyable buildAnnotatedStringWithUrlHighlighting(
    textual content: String, 
    colour: Colour
): AnnotatedString {
    return buildAnnotatedString {
        append(textual content)
    }
}

Subsequent, we’ll have to take the contents of our string and apply highlighting to any URLs which might be current. Earlier than we will do that, we have to determine the URLs within the string. URL detection would possibly fluctuate relying on the use case, so to maintain issues easy let’s write some instance code that can discover the URLs in a given piece of textual content. This code will take the given string and filter the URLs, offering a listing of URL strings because the consequence.

textual content?.cut up("s+".toRegex())?.filter { phrase ->
    Patterns.WEB_URL.matcher(phrase).matches()
}

Now that we all know what URLs are within the string, we’re going to wish to use highlighting to them. That is going to be within the type of an annotated string type, which is utilized utilizing the addStyle operation.

enjoyable addStyle(type: SpanStyle, begin: Int, finish: Int)

When calling this operate, we have to move the SpanStyle that we want to apply, together with the beginning and finish index that this styling must be utilized to. We’re going to start out by calculating this begin and finish index  – to maintain issues easy, we’re going to imagine there are solely distinctive URLs in our string.

textual content?.cut up("s+".toRegex())?.filter { phrase ->
    Patterns.WEB_URL.matcher(phrase).matches()
}.forEach {
    val startIndex = textual content.indexOf(it)
    val endIndex = startIndex + it.size
}

Right here we find the beginning index through the use of the indexOf operate, which can give us the beginning index of the given URL. We’ll then use this begin index and the size of the URL to calculate the tip index. We are able to then move these values to the corresponding arguments for the addStyle operate.

textual content?.cut up("s+".toRegex())?.filter { phrase ->
    Patterns.WEB_URL.matcher(phrase).matches()
}.forEach {
    val startIndex = textual content.indexOf(it)
    val endIndex = startIndex + it.size
    addStyle(
        begin = startIndex, 
        finish = endIndex
    )
}

Subsequent, we have to present the SpanStyle that we need to be utilized to the given index vary. Right here we need to merely spotlight the textual content utilizing the supplied colour, so we’ll move the colour worth from our operate arguments as the colour argument for the SpanStyle operate.

textual content?.cut up("s+".toRegex())?.filter { phrase ->
    Patterns.WEB_URL.matcher(phrase).matches()
}.forEach {
    val startIndex = textual content.indexOf(it)
    val endIndex = startIndex + it.size
    addStyle(
        type = SpanStyle(
            colour = colour
        ),
        begin = startIndex, 
        finish = endIndex
    )
}

With this in place, we now have an entire operate that can take the supplied textual content and spotlight any URLs utilizing the supplied Colour reference.

enjoyable buildAnnotatedStringWithUrlHighlighting(
    textual content: String, 
    colour: Colour
): AnnotatedString {
    return buildAnnotatedString {
        append(textual content)
        textual content?.cut up("s+".toRegex())?.filter { phrase ->
            Patterns.WEB_URL.matcher(phrase).matches()
        }.forEach {
            val startIndex = textual content.indexOf(it)
            val endIndex = startIndex + it.size
            addStyle(
                type = SpanStyle(
                    colour = colour,
                    textDecoration = TextDecoration.None
                ),
                begin = startIndex, finish = endIndex
            )
        }
    }
}

We’ll then have to hop again into our UrlTransformation class and move the results of the buildAnnotatedStringWithUrlHighlighting operate name for the TransformedText argument.

class UrlTransformation(
    val colour: Colour
) : VisualTransformation {
    override enjoyable filter(textual content: AnnotatedString): TransformedText {
        return TransformedText(
            buildAnnotatedStringWithUrlHighlighting(textual content, colour),
            OffsetMapping.Identification
        )
    }
}

Now that our UrlTransformation implementation is full, we will instantiate this and move the reference for the visualTransformation  argument of the TextField composable. Right here we’re utilizing the specified colour from our MaterialTheme reference, which will likely be used when highlighting the URLs in our TextField content material.

TextField(
    ...
    visualTransformation = UrlTransformation(
        MaterialTheme.colours.secondary)
)

With the above in place, we now have dynamic URL highlighting assist inside our TextField composable. Because of this now every time the consumer inserts a URL into the composer for an Concept, we determine this as a URL by highlighting it utilizing a the secondary colour from our theme.

On this put up, we’ve learnt how we will apply dynamic URL highlighting to the contents of a TextField composable. Within the subsequent put up, we’ll discover how we added the “Open hyperlink” pop-up when a URL is tapped throughout the composer enter space.



RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments