Vertical text control

Posted by whansen02 on Sun, 09 Jun 2019 00:29:35 +0200

Source: https://github.com/lybeat/PlumbTextView

PlumbTextView

PlumbTextView is a vertically arranged text control. You can easily use it to define a variety of vertical text styles.

Feature

  1. Arrange the text vertically.

  2. Column and word spacing of text can be set.

  3. You can add a regular expression to split the text. PlumbTextView replaces columns at the characters contained in the regular expression, and the characters are not displayed in the PlumbTextView.

  4. You can add a vertical line to the coordinates of each column of text.

  5. Font styles and third-party fonts can be set for text.

Screenshot


Gradle Dependency

  1. compile 'cc.sayaki.widget:plumb-textview:1.0.1'

Usage

You can easily use PlumbTextView, just like TextView. All you need to do is set the properties you want in xml or Java code.

  1. <cc.sayaki.widget.PlumbTextView
  2.    android:layout_width="wrap_content"
  3.    android:layout_height="wrap_content"
  4.    android:paddingBottom="30dp"
  5.    android:paddingTop="30dp"
  6.    sayaki:columnSpacing="20dp"
  7.    sayaki:leftLine="true"
  8.    sayaki:leftLineColor="@color/colorAccent"
  9.    sayaki:leftLinePadding="4dp"
  10.    sayaki:letterSpacing="6dp"
  11.    sayaki:regex="[,. ?!]"
  12.    sayaki:text="@string/text"
  13.    sayaki:textColor="@color/colorAccent"
  14.    sayaki:textSize="16sp"
  15.    sayaki:textStyle="bold|italic" />

Thinking

Next, we will talk about the implementation of PlumbTextView.
The idea is simple. After drawing a character, the next drawing position is moved vertically downward to the height of a character (including the height of the character itself and the space between two characters). Vertical text rendering is simple, but column swapping is a troublesome area.

PlumbTextView has two types of column swapping:

  1. The single column text exceeds the content height that PlumbTextView can display.

  2. Regular expressions are provided, and PlumbTextView replaces columns at the characters contained in the regular expressions.

First measure the height of PlumbTextView itself

  1. if (heightMode == MeasureSpec.EXACTLY) {
  2.     height = heightSize;
  3. } else {
  4.     if (!TextUtils.isEmpty(regex)) {
  5.         height = 0;
  6.         String[] texts = text.toString().split(regex);
  7.         for (String s : texts) {
  8.             height = Math.max(height, getDesiredHeight(s));
  9.         }
  10.         height += letterSpacing;
  11.     } else {
  12.         height = getDesiredHeight(text.toString());
  13.     }
  14.     if (height > heightSize) {
  15.         height = heightSize;
  16.     }
  17. }

Splitting Text Based on Column Swapping Rules

  1. if (!TextUtils.isEmpty(regex)) {
  2.     String[] texts = text.toString().split(regex);
  3.     for (String s : texts) {
  4.         getFormatTexts(s);
  5.     }
  6. } else {
  7.     getFormatTexts(text.toString());
  8. }
  9.  
  10. //Get split text
  11. private void getFormatTexts(String s) {
  12.     int contentHeight = height - getPaddingTop() - getPaddingBottom();
  13.     if (getDesiredHeight(s) > contentHeight) {
  14.         int count = contentHeight / charHeight;
  15.         int i = 0;
  16.         //Some text splitting may still be higher than the display height of the control and need to be splitted again
  17.         for (; i < getDesiredHeight(s) / contentHeight; i++) {
  18.             formatTexts.add(s.substring(* count, (+ 1) * count));
  19.         }
  20.         //The last column of text does not contain one column
  21.         if (getDesiredHeight(s) % contentHeight != 0) {
  22.             formatTexts.add(s.substring(* count, s.length()));
  23.         }
  24.     } else {
  25.         formatTexts.add(s);
  26.     }
  27. }

Measuring the width of PlumbTextView

  1. if (widthMode == MeasureSpec.EXACTLY) {
  2.     width = widthSize;
  3. } else {
  4.     if (!TextUtils.isEmpty(regex)) {
  5.         width = columnWidth * formatTexts.size();
  6.     } else {
  7.         width = columnWidth * (getDesiredHeight(text.toString())
  8.                 / (height - getPaddingTop() - getPaddingBottom()) + 1);
  9.     }
  10.     if (width > widthSize) {
  11.         width = widthSize;
  12.     }
  13. }

The above operations are all in the onMeasure method, so in order to avoid repetition of split text caused by multiple measurements, the formatTexts are cleared before each split.

Now that you've got the text split by column, it's easier to draw.

  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3.     float x = width - getPaddingLeft() - getPaddingRight();
  4.     float y = getPaddingTop();
  5.     for (int i = 0; i < formatTexts.size(); i++) {
  6.         //Replacement
  7.         x = i == 0 ? width - columnWidth + columnSpacing : x - columnWidth;
  8.         //Draw each column of text
  9.         for (int j = 0; j < formatTexts.get(i).length(); j++) {
  10.             //Move down the drawing point
  11.             y = j == 0 ? charHeight - letterSpacing + getPaddingTop() : y + charHeight;
  12.             canvas.drawText(formatTexts.get(i), j, j + 1, x, y, textPaint);
  13.         }
  14.         if (leftLine) {
  15.             //Draw vertical lines after each column of text
  16.             canvas.drawLine(- leftLinePadding, getPaddingTop(),
  17.                     x - leftLinePadding, y + letterSpacing, leftLinePaint);
  18.         }
  19.     }
  20. }

Topics: Android github Gradle xml