Author: Lu Lei

Snowball APP is a typical UGC community. In our APP, there are abundant information flows and long articles for users to read. At the same time, a large number of users use the editor to post and send columns every day. In such an Android application with a large reading proportion, the presentation of text content is very important. Elegant typesetting and fast rendering can bring users a very good reading experience. Therefore, it is very important to master all kinds of classes and interfaces provided by Android system and expand these basic class libraries for completing the layout requirements of designers and developing and improving the functions related to snowball community.

This article briefly introduces TextView as the core of the related controls, and summarizes the problems encountered in the development of Snowball APP and the ideas to solve these problems.

Class libraries involved

  • TextView and its subclasses are attached to the utility class

  • CharSequence / Spanned / Spannable / SpannableString

  • CharacterStyle/ParagraphStyle and its various implementations

  • Conversion between Html and Span

How does a TextView draw text

TextView is one of the most commonly used controls in Android application development. It is also one of the most complex controls (and probably one of the most complex). It helps us to use the API and tool classes provided by the system more elegantly to complete the requirements and designs proposed by the students of products and designers.


Coordinate system and display range of text in TextView

First, let’s take a look at the following image to see how to determine how far a line of text should be drawn on the screen and how to position its coordinates.

Those of you who know the drawing process of View know that most of the images displayed on the screen in Android system are drawn by Canvas and Paint (OpenGL is not discussed here). Specifically for TextView, We use a subclass of Paint called TextPaint. For a line of text, the position on the screen is determined by the following five lines:

  • Baseline – Line of text Baseline (center of gravity).

  • Top – The maximum distance above the baseline for the text of the given font.

  • Ascent – Recommended distance for single line text above baseline.

  • Descent – The recommended distance of a single line of text below the baseline.

  • Bottom – The maximum distance of the given font text below the baseline.

For more than one line of text, there is a distance called Leading to ensure the spacing between the lines.

  • Leading – The space between two lines of text.

The Baseline is the center of gravity of a line. The line is always displayed between the Ascent and Decent lines, and the farthest distance is between Top and Bottom. This design allows all types of text to be beautifully drawn on the screen. When the text is more than two lines, Leading plays a more important role, which makes the space between lines more natural (see figure below).

TextView drawing process

The drawing process of View and its subclasses is nothing more than measure, layout and draw. But the drawing of TextView has its own special way. Its drawing is done by Layout and TextLine and their subclasses.

When the APP is developing the setText method, after a series of analyses and calculations, the content displayed in the TextView is determined. BingLayout is responsible for drawing single lines of text, DynamicLayout is responsible for drawing marked text with a Span, and StaticLayout is responsible for drawing multiple lines of plain text without a Span. As mentioned earlier, TextView is a separate control for developers, but if you look more closely, it is composed of lines of text that are computed to show which text is validated for each line, and the final display is drawn by the TextLine. In relation to the above coordinate system, we can understand the importance of a few range lines, such as Baseline, for each line of text, by which TextLine can correctly draw each line of text to the correct position on the screen while keeping the appropriate space between lines. Presentation in the user’s field of vision is elegant reading experience good effect.

Take a look at the code to see how the above process is implemented (there is so much code that some of the key points are highlighted here).

The function of the above code is to ensure that the Layout in the TextView is created and modified correctly, and to calculate the height and width using the Layout. Let’s take a look at how to create different layouts based on the text type at the beginning of this section:

Since the layout process is not special, here skip layout to take a look at the process of DRAW. As mentioned above, TextView is drawn by Layout. Let’s take a look at the code:

In Layout, draw the background in one step and the Text in the other:

After Layout has calculated the first and last lines, it begins to draw text from the TextView. Continuing with the code, you can see that text is drawn line by line using TextLine through a loop. The concern here is the calculation of the beginning and end positions of each line of text. In Andoird systems, a common problem is that some lines of text leave a lot of white space to the right. There is no argument like break all in CSS to align the text left and right. We can actually align the text left and right to the detail code by modifying the start and end arguments in the Textline.

If we look at tl.draw(canvas, x, ltop, lbaseline, lbottom), we can clearly see that each line is drawn with the coordinates top, baseline, and bottom passed to Textline. Help draw the line of text in the correct position.

In summary, Layout arranges Textline in two steps: one is to set the range and attributes of the text to be displayed, and the other is to tell the coordinates to be drawn.

When entering Textline, its work is complicated and single: after calculation, it is handed over to Canvas for drawing. Because of the complexity of the code here, it is recommended to read the whole process carefully, so it is not necessary to extract the complete code.

summary

After analyzing setText at the code level, the entire control process is very complex from the perspective of the code, but I believe that for this most commonly used control, only a deep understanding of the drawing process can make a better extension to meet the various business needs.

The following diagram summarizes the process in a nutshell, highlighting what each part does.

Tokenable text

As mentioned in the TextView drawing process, we need to use DynamicLayout to draw Spanned text. It is important to realize that classes and their subclasses that implement the Spanned interface are of the Spanned type. They are referred to here as markup text, and their inheritance can be clearly seen in the figure below.

Focus on the following types of Android development:

  • Spanned – An interface used to represent a section of text containing marked objects.

  • Spannable – Used to indicate that a piece of text contains a marked object, which can also be detach.

  • SpannedString – Used to indicate that the content of a piece of text contains a marked object and that the text cannot be changed.

  • SpannableStringBuilder – Used to indicate that a piece of text content and markup can be changed.

  • Editable – indicates that a piece of text and tags can be changed.

As you can see from the inheritance diagram and introduction above, there are three types of tokenable text that the developer actually operates with. For text whose content is fixed, construct SpannedString and set the tag in the appropriate place. For text whose content is changing, use SpannableStringBuilder. Editable is almost exclusively used in EditText, a subclass of TextView, which provides append, DELETE, insert, etc., to change markup text.

They are called tokenable text because we can set a series of tags anywhere on this type of text. For example, we can bold the position from start to end, italicize it, and mark the foreground and background…… for any other place Not only that, we can also make different marks for the same location, such as adding color and changing font. Set the tag uniformly through the following method:

A little example

The results are shown below:

Use multiple spans:

The result is shown below

This small example mainly illustrates three points:

  • Choose the appropriate Spanned text based on business needs.

  • A tag is a specific object. Each tag object can only be marked for one place in the same tokenable text, and repeated use will result in only the last one taking effect.

  • After the Editable object is updated, the UI is updated without resetting.


CharacterStyle / ParagraphStyle

The TextView drawing process and various markable texts below Spannd were mentioned earlier. When we need to mark part of the text with marked text, we need to set specific marks for the corresponding part. All tag types stem from a combination of four types, CharacterStyle, ParagraphStyle, UpdateAppearance, and UpdateLayout.

The scope of influence of the four are as follows:

  • If a Span affects a character-level text format, then CharacterStyle is inherited including our common ForegroundColorSpan, BackgroundColorSpan, and so on.

  • If a Span affects the text format at the paragraph level, implementing ParagraphStyle includes AlignmenttSpan, LeadingMarginSpan, LineBackgroundSpan, and so on.

  • If a Span modifies the appearance of text at the character level, implementing an UpdateAppearance includes ClickableSpan

  • If a Span changes the size of a character-level text metric, the implementation of UpdateLayout includes AbsoluteSizeSpan

Inheritance relationships

The following figure shows the four base types and their corresponding subtypes

Common subclasses

** Let’s start with a quick look at some of the most commonly used tag types:

Figure:

Native conversion of Html text

For most applications, long Html texts are usually rendered and typesetted by Webview. However, for some simple Html texts, TextView can also be used to display them very well, which involves the conversion of Html text and Native. In short, convert a String Html text to Spanned markup text, and convert styles to various Spanned so that the TextView will display correctly.

Transformations are usually handled using the fromHtml method in the Html utility class. View the code:

Take a look at the actual use example below, which is a simple Html text.

As you can see, it contains the usual Html tags: Br, A, Img. Enter the following code processing:

After the conversion, Br becomes \n, A becomes URLSpan, and Img becomes ImageSpan.

Some practices in snowball APP

You’ve seen the TextView drawing process, the tokenable text Span, and common tag types. The three work together to make the TextView display rich text effects. Here is a brief introduction to some of the practices in snowball APP.

When editors add and remove posts, they always need to delete the good link along with the @ person or insert a hyperlink when it needs to be edited and deleted. In this case, using Span is very convenient.

Let’s first look at inserting a tag text

When adding a hyperlink, as shown in the example in the previous section, we construct Spanned text and insert it directly into the location of Editable. The EditText automatically updates the UI (as explained earlier).

When it is necessary to delete a hyperlink as a whole, the cursor position can be monitored. Once the deletion operation is monitored, it can determine whether the position to be deleted is a marker text to be deleted.

The effect is as follows:

In the figure above, both subclasses of URLSpan get the starting position of the tagged text and delete all. Another use is that when the user cursor moves, it is forbidden to move to the middle of the marked text, and reset the final position of the cursor after moving according to the proximity position.

Tagging special text in the news stream (columns, rewards)

Many times, the type provided by the system does not meet our needs, and we need to extend some base type or implement some interface to meet our expectations. We started by saying that affecting (or modifying) text attributes at different levels requires extending different base types.

One of the most important usage scenarios in snowball information flows is to add a prefix of variable length and content to the text of variable length and content. Specific practices are as follows:

Extend a custom Span to mark text that requires special display. First paste two renderings to view:

The method chosen here is to inherit ReplacementSpan (which inherits from MetricAffectingSpan), as shown in the following code:

We chose ReplacementSpan because we need to change the background color style and text color for the text. All you need to do here is override the onDraw method to set the designer’s style requirements in a specific area.

Tag A in the information flow

In a broad sense, the information flow of Snowball community is not very different from that of weibo and Twitter, which are more widely used. Both users need to quickly find people and topics in the information flow and facilitate interaction. Specific to snowball information flow, what we need to do is: from the information flow to the personal page quickly (a special point in snowball information flow is to separate the paid @ a user to highlight), stock page, topic page, and provide a way to quickly view pictures. As shown in the figure:

Three requirements need to be met when looking at pictures. First, different hyperlinks should be distinguished to display different colors; second, when clicking on hyperlinks, there should be clicking state and clicking state can be corresponding to the color of the connection; third, different hyperlinks will jump after clicking. We chose to extend URLSpan as the type of text tag because URLSpan inherits from ClickableSpan and handles click events. URL can be parsed in URLSpan to display hyperlink color and click status by URL type. The code looks like this:

ICON label in information flow (center)

In snowball application, there is no universal emoji as a small ICON. Instead, the designer designed a set of small ICONS in line with the language environment of financial investment community. For the students of client development, what they need to do is how to make any number of ICONS in any position in a paragraph of text gracefully displayed.

Take a look at what the final display should look like (see the red box below) :

First take a look at the content returned by the back end, as shown in the figure (only the text corresponding to the last few ICONS in the red box in the figure above is captured here).

The first problem I encountered was how to parse these ICONS. There are many ways to handle this. The snowball option is to parse the Img tag, use the local resource file in APK, convert the corresponding Img tag to the corresponding Drawable, and finally display it through ImageSpan.

Compare the changes before and after:

conclusion

TextView (EditText, Button) is a very complex control, and there are dozens of classes related to its functionality in the FrameWork. But as it is the most basic control, APP developers must have a deep understanding of its characteristics, complete grasp of its functions and attributes. The role of this article is only to open TextView this control coat, more profound learning, of course, need to read the relevant source code. It’s a huge amount of code, but I think it’s worth learning.

Of course, in addition to analyzing how TextView draws text, the use and expansion of marketable text, there is still a lot of knowledge related to TextView that is worth discussing, such as how to mix text and text in long articles and how to align left and right (there is no large blank on the right side of each line of TextView). How to do preloading to improve rendering efficiency. I won’t discuss it here for lack of space. Those who are interested can Google the relevant information.

reference

Flavienlaurent.com/blog/2014/0…

Instagram-engineering.tumblr.com/post/114508…

One more thing

Snowball’s engineer team is recruiting Java engineers, operation and maintenance development engineers, test development engineers, algorithm engineers. Interested students can check the original text to see the specific positions and requirements, waiting for you.