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
-
Arrange the text vertically.
-
Column and word spacing of text can be set.
-
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.
-
You can add a vertical line to the coordinates of each column of text.
-
Font styles and third-party fonts can be set for text.
Screenshot
Gradle Dependency
- 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.
- <cc.sayaki.widget.PlumbTextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingBottom="30dp"
- android:paddingTop="30dp"
- sayaki:columnSpacing="20dp"
- sayaki:leftLine="true"
- sayaki:leftLineColor="@color/colorAccent"
- sayaki:leftLinePadding="4dp"
- sayaki:letterSpacing="6dp"
- sayaki:regex="[,. ?!]"
- sayaki:text="@string/text"
- sayaki:textColor="@color/colorAccent"
- sayaki:textSize="16sp"
- 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:
-
The single column text exceeds the content height that PlumbTextView can display.
-
Regular expressions are provided, and PlumbTextView replaces columns at the characters contained in the regular expressions.
First measure the height of PlumbTextView itself
- if (heightMode == MeasureSpec.EXACTLY) {
- height = heightSize;
- } else {
- if (!TextUtils.isEmpty(regex)) {
- height = 0;
- String[] texts = text.toString().split(regex);
- for (String s : texts) {
- height = Math.max(height, getDesiredHeight(s));
- }
- height += letterSpacing;
- } else {
- height = getDesiredHeight(text.toString());
- }
- if (height > heightSize) {
- height = heightSize;
- }
- }
Splitting Text Based on Column Swapping Rules
- if (!TextUtils.isEmpty(regex)) {
- String[] texts = text.toString().split(regex);
- for (String s : texts) {
- getFormatTexts(s);
- }
- } else {
- getFormatTexts(text.toString());
- }
- //Get split text
- private void getFormatTexts(String s) {
- int contentHeight = height - getPaddingTop() - getPaddingBottom();
- if (getDesiredHeight(s) > contentHeight) {
- int count = contentHeight / charHeight;
- int i = 0;
- //Some text splitting may still be higher than the display height of the control and need to be splitted again
- for (; i < getDesiredHeight(s) / contentHeight; i++) {
- formatTexts.add(s.substring(i * count, (i + 1) * count));
- }
- //The last column of text does not contain one column
- if (getDesiredHeight(s) % contentHeight != 0) {
- formatTexts.add(s.substring(i * count, s.length()));
- }
- } else {
- formatTexts.add(s);
- }
- }
Measuring the width of PlumbTextView
- if (widthMode == MeasureSpec.EXACTLY) {
- width = widthSize;
- } else {
- if (!TextUtils.isEmpty(regex)) {
- width = columnWidth * formatTexts.size();
- } else {
- width = columnWidth * (getDesiredHeight(text.toString())
- / (height - getPaddingTop() - getPaddingBottom()) + 1);
- }
- if (width > widthSize) {
- width = widthSize;
- }
- }
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.
- @Override
- protected void onDraw(Canvas canvas) {
- float x = width - getPaddingLeft() - getPaddingRight();
- float y = getPaddingTop();
- for (int i = 0; i < formatTexts.size(); i++) {
- //Replacement
- x = i == 0 ? width - columnWidth + columnSpacing : x - columnWidth;
- //Draw each column of text
- for (int j = 0; j < formatTexts.get(i).length(); j++) {
- //Move down the drawing point
- y = j == 0 ? charHeight - letterSpacing + getPaddingTop() : y + charHeight;
- canvas.drawText(formatTexts.get(i), j, j + 1, x, y, textPaint);
- }
- if (leftLine) {
- //Draw vertical lines after each column of text
- canvas.drawLine(x - leftLinePadding, getPaddingTop(),
- x - leftLinePadding, y + letterSpacing, leftLinePaint);
- }
- }
- }