[ad_1]
We just lately launched a brand new characteristic at Buffer, known as Concepts. With Concepts, you may 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 internet and cellular apps, we’ve a while to share some learnings from the event of this characteristic. On this weblog submit, 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 customary to construct all our new options, whereas steadily adopting it into current components of our software. We constructed the entire of the Concepts characteristic utilizing Jetpack Compose – so alongside quicker characteristic growth and better 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.
Inside the Concepts composer, we assist dynamic hyperlink highlighting. Which means that if you happen to kind a URL into the textual content space, then the hyperlink can be highlighted – tapping on this hyperlink will then present an “Open link” pop-up, which can launch the hyperlink within the browser when clicked.
On this weblog submit, we’re going to deal with 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 comprises an argument, visualTransformation
, which is used to use visible modifications 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 take 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 comprises 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 supply the implementation that creates a brand new AnnotatedString
reference with our utilized modifications. This modified content material then will get bundled within the TransformedText
kind and returned again to the TextField
for composition.
In order that we are able to 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 Shade
. We outline this argument in order that we are able to go a theme coloration reference to be utilized inside our logic, as we’re going to be exterior of composable scope and gained’t have entry to our composable theme.
class UrlTransformation(
val coloration: Shade
) : VisualTransformation
With this class outlined, we now must implement the filter operate from the VisualTransformation
interface. Inside this operate we’re going to return an occasion of the TransformedText
class – we are able to bounce 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 unique to reworked textual content.
*/
val offsetMapping: OffsetMapping
)
Each of those arguments are required, so we’re going to want to supply 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 unique to reworked textual content
class UrlTransformation(
val coloration: Shade
) : VisualTransformation
override enjoyable filter(textual content: AnnotatedString): TransformedText
return TransformedText(
...,
OffsetMapping.Id
)
For the offsetMapping
argument, we merely go the OffsetMapping.Id
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 rely. With regards to the textual content argument we’ll want to put in writing 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,
coloration: Shade
): AnnotatedString
From this operate, we have to return an AnnotatedString
reference, which we’ll create utilizing buildAnnotatedString
. Inside this operate, we’ll begin by utilizing the append operation to set the textual content material of the AnnotatedString
.
enjoyable buildAnnotatedStringWithUrlHighlighting(
textual content: String,
coloration: Shade
): AnnotatedString
return buildAnnotatedString
append(textual content)
Subsequent, we’ll must take the contents of our string and apply highlighting to any URLs which are current. Earlier than we are able to do that, we have to determine the URLs within the string. URL detection would possibly range 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 an inventory of URL strings because the consequence.
textual content?.break 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 want to use highlighting to them. That is going to be within the type of an annotated string model, which is utilized utilizing the addStyle operation.
enjoyable addStyle(model: SpanStyle, begin: Int, finish: Int)
When calling this operate, we have to go the SpanStyle
that we want to apply, together with the beginning and finish index that this styling needs to 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?.break 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 by utilizing 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 top index. We will then go these values to the corresponding arguments for the addStyle
operate.
textual content?.break 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 wish to be utilized to the given index vary. Right here we wish to merely spotlight the textual content utilizing the supplied coloration, so we’ll go the colour worth from our operate arguments as the colour argument for the SpanStyle
operate.
textual content?.break up("s+".toRegex())?.filter phrase ->
Patterns.WEB_URL.matcher(phrase).matches()
.forEach
val startIndex = textual content.indexOf(it)
val endIndex = startIndex + it.size
addStyle(
model = SpanStyle(
coloration = coloration
),
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 Shade
reference.
enjoyable buildAnnotatedStringWithUrlHighlighting(
textual content: String,
coloration: Shade
): AnnotatedString
return buildAnnotatedString
append(textual content)
textual content?.break up("s+".toRegex())?.filter phrase ->
Patterns.WEB_URL.matcher(phrase).matches()
.forEach
val startIndex = textual content.indexOf(it)
val endIndex = startIndex + it.size
addStyle(
model = SpanStyle(
coloration = coloration,
textDecoration = TextDecoration.None
),
begin = startIndex, finish = endIndex
)
We’ll then must hop again into our UrlTransformation
class and go the results of the buildAnnotatedStringWithUrlHighlighting
operate name for the TransformedText
argument.
class UrlTransformation(
val coloration: Shade
) : VisualTransformation
override enjoyable filter(textual content: AnnotatedString): TransformedText
return TransformedText(
buildAnnotatedStringWithUrlHighlighting(textual content, coloration),
OffsetMapping.Id
)
Now that our UrlTransformation
implementation is full, we are able to instantiate this and go the reference for the visualTransformation
argument of the TextField
composable. Right here we’re utilizing the specified coloration from our MaterialTheme
reference, which can 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. Which means that now at any time when the consumer inserts a URL into the composer for an Concept, we determine this as a URL by highlighting it utilizing a the secondary coloration from our theme.
On this submit, we’ve learnt how we are able to apply dynamic URL highlighting to the contents of a TextField
composable. Within the subsequent submit, we’ll discover how we added the “Open link” pop-up when a URL is tapped throughout the composer enter space.
[ad_2]
Source link