35 | * <com.uudove.pinyin.widget.PinyinTextView 36 | * android:id="@+id/pinyin_text_view" 37 | * android:layout_width="wrap_content" 38 | * android:layout_height="wrap_content" 39 | * app:horizontalSpacing="10dp" 40 | * app:verticalSpacing="10dp" 41 | * app:textColor="#ff0000" 42 | * app:textSize="20sp"/> 43 | *44 | * 45 | * @author liwenwei 46 | */ 47 | public class PinyinTextView extends View { 48 | /** 49 | * @hide 50 | */ 51 | @IntDef({TYPE_PLAIN_TEXT, TYPE_PINYIN_AND_TEXT, TYPE_PINYIN}) 52 | @Retention(RetentionPolicy.SOURCE) 53 | public @interface PinyinMode { 54 | } 55 | 56 | /** 57 | * draw only plain text 58 | */ 59 | public static final int TYPE_PLAIN_TEXT = 1; 60 | /** 61 | * draw pinyin and text 62 | */ 63 | public static final int TYPE_PINYIN_AND_TEXT = 2; 64 | /** 65 | * draw only pinyin 66 | */ 67 | public static final int TYPE_PINYIN = 3; 68 | /** 69 | * draw type. Must be one value of {@link #TYPE_PINYIN_AND_TEXT} or {@link #TYPE_PLAIN_TEXT} 70 | */ 71 | private int mDrawType = TYPE_PLAIN_TEXT; 72 | 73 | private static final float PINYIN_TEXT_SIZE_RADIO = 0.5F; 74 | 75 | /** 76 | * Text size in pixels
300 | * Def in xml app:textColor=""
301 | *
302 | * @param color text color.
303 | */
304 | public void setTextColor(@ColorInt int color) {
305 | mTextColor = color;
306 | for (Token token : mPinyinTokens) {
307 | token.setTextColor(mTextColor);
308 | }
309 | if (mPinyinTokens.isEmpty()) {
310 | requestLayout();
311 | invalidate();
312 | } else {
313 | setPinyinTextByTokens(mPinyinTokens, mDrawType);
314 | }
315 | }
316 |
317 | /**
318 | * Set pinyin text color.
505 | * Why we calculate the text height by hard code text, not the {@link PinyinTextView#mTextString}
506 | * and {@link PinyinTextView#mPinyinString} ?
507 | *
508 | * Sometimes, we have to align multiple PinyinTextView by horizontal, if we measure different text,
509 | * we get different height, so we have to measure the same text to keep the same height
510 | */
511 | private void calTextHeight() {
512 | // calculate text height
513 | String chinese = "你好";
514 | mPaint.setTextSize(mTextSize);
515 | mPaint.getTextBounds(chinese, 0, chinese.length(), mBounds);
516 | mTextHeight = mBounds.height();
517 |
518 | // calculate pinyin height
519 | String pinyin = "āáǎàaHhJjPpYyGg";
520 | if (mDrawType == TYPE_PINYIN) {
521 | mPaint.setTextSize(mTextSize);
522 | } else {
523 | mPaint.setTextSize(mPinyinTextSize);
524 | }
525 | mPaint.getTextBounds(pinyin, 0, pinyin.length() - 1, mBounds);
526 | mPinyinHeight = mBounds.height();
527 | }
528 |
529 | @Override
530 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
531 | if (mPinyinCompats.isEmpty()) {
532 | measurePlainText(widthMeasureSpec, heightMeasureSpec);
533 | } else {
534 | if (mDrawType == TYPE_PINYIN_AND_TEXT) {
535 | measurePinyinText(widthMeasureSpec, heightMeasureSpec);
536 | } else if (mDrawType == TYPE_PLAIN_TEXT && !TextUtils.isEmpty(mTextString)) {
537 | measurePlainText(widthMeasureSpec, heightMeasureSpec);
538 | } else if (mDrawType == TYPE_PINYIN && !TextUtils.isEmpty(mPinyinString)) {
539 | measurePinyin(widthMeasureSpec, heightMeasureSpec);
540 | } else {
541 | measureDefault(widthMeasureSpec, heightMeasureSpec);
542 | }
543 | }
544 | }
545 |
546 | private void measureDefault(int widthMeasureSpec, int heightMeasureSpec) {
547 |
548 | // max allowed width or height
549 | int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
550 | int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
551 |
552 | // mode
553 | int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
554 | int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
555 |
556 | // measured width and height
557 | int measuredWidth =
558 | modeWidth == MeasureSpec.EXACTLY ? sizeWidth : getPaddingLeft() + getPaddingRight();
559 | int measuredHeight =
560 | modeHeight == MeasureSpec.EXACTLY ? sizeHeight : getPaddingTop() + getPaddingBottom();
561 |
562 | setMeasuredDimension(measuredWidth, measuredHeight);
563 | }
564 |
565 | @SuppressWarnings("PMD")
566 | private void measurePinyinText(int widthMeasureSpec, int heightMeasureSpec) {
567 | int paddingLeft = this.getPaddingLeft();
568 | int paddingRight = this.getPaddingRight();
569 | int paddingTop = this.getPaddingTop();
570 | int paddingBottom = this.getPaddingBottom();
571 |
572 | // max allowed width or height
573 | int sizeWidth = MeasureSpec.getSize(widthMeasureSpec) - paddingLeft - paddingRight;
574 | int sizeHeight = MeasureSpec.getSize(heightMeasureSpec) - paddingTop - paddingBottom;
575 |
576 | // mode
577 | int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
578 | int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
579 |
580 | // measured width and height
581 | int measuredWidth = modeWidth == MeasureSpec.EXACTLY ? sizeWidth : 0;
582 | int measuredHeight = modeHeight == MeasureSpec.EXACTLY ? sizeHeight : 0;
583 |
584 | int line = 0;
585 | int col = 0;
586 | int lineLength = 0;
587 | int baseLine = 0; // top of pinyin
588 | boolean newLine = false;
589 |
590 | for (PinyinCompat compat : mPinyinCompats) {
591 | int textWidth = getTextWidth(compat.text, mTextSize);
592 | int pinyinWidth = getTextWidth(compat.pinyin, mPinyinTextSize);
593 |
594 | int maxWidth = Math.max(textWidth, pinyinWidth);
595 |
596 | if (newLine) {
597 | line++;
598 | col = 0;
599 | newLine = false;
600 | }
601 |
602 | if (lineLength + maxWidth + (col == 0 ? 0 : mHorizontalSpacing) > sizeWidth) { // new row
603 | lineLength = maxWidth;
604 |
605 | baseLine += mTextHeight + mPinyinHeight + mPinyinTextSpacing + mLineSpacing;
606 | // TODO: add the underline vertical space if show underline
607 |
608 | if (modeWidth != MeasureSpec.EXACTLY) {
609 | measuredWidth = sizeWidth;
610 | }
611 |
612 | newLine = true;
613 | } else {
614 | if (col != 0 || line != 0) { // not the first item of first row
615 | lineLength += mHorizontalSpacing;
616 | }
617 | lineLength += maxWidth;
618 |
619 | if (modeWidth != MeasureSpec.EXACTLY && measuredWidth < lineLength) {
620 | measuredWidth = lineLength;
621 | if (measuredWidth > sizeWidth) {
622 | measuredWidth = sizeWidth;
623 | }
624 | }
625 | col++;
626 | }
627 |
628 | // Center the pinyin/text
629 | int pinyinBias = 0;
630 | int textBias = 0;
631 | if (pinyinWidth < textWidth) {
632 | pinyinBias = (textWidth - pinyinWidth) / 2;
633 | } else {
634 | textBias = (pinyinWidth - textWidth) / 2;
635 | }
636 | compat.pinyinRect.left = lineLength - maxWidth + pinyinBias;
637 | compat.pinyinRect.right = compat.pinyinRect.left + pinyinWidth;
638 | compat.pinyinRect.top = baseLine;
639 | compat.pinyinRect.bottom = compat.pinyinRect.top + mPinyinHeight;
640 |
641 | compat.textRect.left = lineLength - maxWidth + textBias;
642 | compat.textRect.right = compat.textRect.left + textWidth;
643 | compat.textRect.top = compat.pinyinRect.bottom + mPinyinTextSpacing;
644 | compat.textRect.bottom = compat.textRect.top + mTextHeight;
645 |
646 | compat.pinyinTextRect.left = lineLength - maxWidth;
647 | compat.pinyinTextRect.right = compat.pinyinRect.left + Math.max(pinyinWidth, textWidth);
648 | compat.pinyinTextRect.top = baseLine;
649 | compat.pinyinTextRect.bottom = compat.pinyinRect.top + mPinyinHeight + mPinyinTextSpacing + mTextHeight;
650 | }
651 |
652 | if (modeHeight != MeasureSpec.EXACTLY) {
653 | measuredHeight = baseLine + mPinyinHeight + mPinyinTextSpacing + mTextHeight + mTextHeight / 4;
654 | }
655 |
656 | setMeasuredDimension(measuredWidth + paddingLeft + paddingRight, measuredHeight + paddingTop + paddingBottom);
657 | }
658 |
659 | private void measurePlainText(int widthMeasureSpec, int heightMeasureSpec) {
660 | measureText(widthMeasureSpec, heightMeasureSpec, mTextString, mTextSize);
661 | }
662 |
663 | private void measurePinyin(int widthMeasureSpec, int heightMeasureSpec) {
664 | measureText(widthMeasureSpec, heightMeasureSpec, mPinyinString, mTextSize);
665 | }
666 |
667 | private void measureText(int widthMeasureSpec, int heightMeasureSpec, String text, float textSize) {
668 | int paddingLeft = this.getPaddingLeft();
669 | int paddingRight = this.getPaddingRight();
670 | int paddingTop = this.getPaddingTop();
671 | int paddingBottom = this.getPaddingBottom();
672 |
673 | // max allowed width or height
674 | int sizeWidth = MeasureSpec.getSize(widthMeasureSpec) - paddingLeft - paddingRight;
675 | int sizeHeight = MeasureSpec.getSize(heightMeasureSpec) - paddingTop - paddingBottom;
676 |
677 | // mode
678 | int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
679 | int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
680 |
681 | // calculate text width and height
682 | mPaint.setColor(mTextColor);
683 | mPaint.setTextSize(textSize);
684 | Spanned htmlSpan = fromHtml(text);
685 | mStaticLayout = new StaticLayout(htmlSpan, mPaint, sizeWidth, Alignment.ALIGN_NORMAL, 1.0f, 0, false);
686 |
687 | // measured width and height
688 | int measuredWidth =
689 | modeWidth == MeasureSpec.EXACTLY
690 | ? sizeWidth
691 | : Math.min(sizeWidth, (int) Math.ceil(Layout.getDesiredWidth(htmlSpan, mPaint)));
692 | int measuredHeight =
693 | modeHeight == MeasureSpec.EXACTLY
694 | ? sizeHeight
695 | : mStaticLayout.getHeight();
696 | if (mUnderline) {
697 | measuredHeight += mUnderlineVerticalSpacing;
698 | }
699 |
700 | setMeasuredDimension(measuredWidth + paddingLeft + paddingRight, measuredHeight + paddingTop + paddingBottom);
701 | }
702 |
703 | private String convertColorHexString(int color) {
704 | return String.format("#%06X", 0xFFFFFF & color);
705 | }
706 |
707 | private String convertTokenToHtml(String text, String textColor) {
708 | return String.format("%s", textColor, text);
709 | }
710 |
711 | private Spanned fromHtml(String html) {
712 | if (html == null) {
713 | html = "";
714 | }
715 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
716 | return Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY);
717 | } else {
718 | return Html.fromHtml(html);
719 | }
720 | }
721 |
722 | @Override
723 | protected void onDraw(Canvas canvas) {
724 | super.onDraw(canvas);
725 | if (this.isInEditMode()) { // eclipse preview mode
726 | return;
727 | }
728 |
729 | if (mPinyinCompats.isEmpty()) {
730 | drawPlainText(canvas);
731 | } else {
732 | if (mDrawType == TYPE_PINYIN_AND_TEXT) {
733 | drawPinyinAndText(canvas);
734 | } else if (mDrawType == TYPE_PLAIN_TEXT) {
735 | drawPlainText(canvas);
736 | } else if (mDrawType == TYPE_PINYIN) {
737 | drawPinyin(canvas);
738 | }
739 | }
740 | }
741 |
742 | private void drawPinyinAndText(Canvas canvas) {
743 | int paddingLeft = this.getPaddingLeft();
744 | int paddingTop = this.getPaddingTop();
745 |
746 | for (int i = 0; i < mPinyinCompats.size(); i++) {
747 | PinyinCompat compat = mPinyinCompats.get(i);
748 |
749 | // draw pinyin
750 | mPaint.setColor(compat.pinyinColor);
751 | mPaint.setTextSize(mPinyinTextSize);
752 | compat.pinyinRect.offset(paddingLeft, paddingTop);
753 | // If the draw mode is TYPE_PINYIN_AND_TEXT, don't draw the pinyin if it's punctuation
754 | if (!isPunctuation(compat.pinyin)) {
755 | canvas.drawText(compat.pinyin, compat.pinyinRect.left, compat.pinyinRect.bottom, mPaint);
756 | }
757 |
758 | // draw text
759 | mPaint.setColor(compat.textColor);
760 | mPaint.setTextSize(mTextSize);
761 | compat.textRect.offset(paddingLeft, paddingTop);
762 | canvas.drawText(compat.text, compat.textRect.left, compat.textRect.bottom, mPaint);
763 |
764 | if (mUnderline && !isPunctuation(compat.text)) {
765 | canvas.drawLine(
766 | compat.pinyinTextRect.left,
767 | compat.pinyinTextRect.bottom + mUnderlineVerticalSpacing,
768 | compat.pinyinTextRect.left + compat.pinyinTextRect.width() + mHorizontalSpacing,
769 | compat.pinyinTextRect.bottom + mUnderlineVerticalSpacing,
770 | mUnderlinePaint);
771 | }
772 |
773 | if (debugDraw) {
774 | mDebugPaint.setColor(mTextColor);
775 | canvas.drawRect(compat.textRect, mDebugPaint);
776 | }
777 |
778 | if (debugDraw) {
779 | mDebugPaint.setColor(mTextColor);
780 | canvas.drawRect(compat.pinyinRect, mDebugPaint);
781 | }
782 |
783 | if (debugDraw) {
784 | mDebugPaint.setColor(mTextColor);
785 | canvas.drawRect(compat.pinyinTextRect, mDebugPaint);
786 | }
787 | }
788 | }
789 |
790 | private void drawPlainText(Canvas canvas) {
791 | drawText(canvas);
792 | }
793 |
794 | // If TYPE_PINYIN or only show Pinyin, we will set the pinyin text color and text size same as the
795 | // plain text (mTextColor, mTextSize)
796 | private void drawPinyin(Canvas canvas) {
797 | drawText(canvas);
798 | }
799 |
800 | private void drawText(Canvas canvas) {
801 | if (mStaticLayout != null) {
802 | int paddingLeft = this.getPaddingLeft();
803 | int paddingTop = this.getPaddingTop();
804 | canvas.translate(paddingLeft, paddingTop);
805 |
806 | mStaticLayout.draw(canvas);
807 |
808 | if (mUnderline && !isPunctuation(mTextString)) {
809 | for (int i = 0; i < mStaticLayout.getLineCount(); i++) {
810 | canvas.drawLine(
811 | mStaticLayout.getLineLeft(i),
812 | mStaticLayout.getLineBottom(i),
813 | mStaticLayout.getLineRight(i),
814 | mStaticLayout.getLineBottom(i),
815 | mUnderlinePaint);
816 | }
817 | }
818 | }
819 | }
820 |
821 | private boolean isPunctuation(String text) {
822 | if (TextUtils.isEmpty(text)) {
823 | return false;
824 | }
825 | text = text.trim();
826 | if (text.length() != 1) {
827 | return false;
828 | }
829 | return StringUtils.isPunctuation(text.charAt(0));
830 | }
831 |
832 | private int getTextWidth(String text, int textSize) {
833 | mPaint.setTextSize(textSize);
834 |
835 | return (int) Math.ceil(Layout.getDesiredWidth(text, mPaint));
836 | }
837 |
838 | static class PinyinCompat {
839 | String text;
840 | @ColorInt
841 | int textColor;
842 | String pinyin;
843 | @ColorInt
844 | int pinyinColor;
845 |
846 | Rect pinyinTextRect;
847 | Rect textRect;
848 | Rect pinyinRect;
849 | }
850 |
851 | public static class Token {
852 | private String text;
853 | private @ColorInt
854 | int textColor = 0;
855 | private String pinyin;
856 | private @ColorInt
857 | int pinyinColor = 0;
858 |
859 | public Token() {
860 |
861 | }
862 |
863 | public Token(String text, @ColorInt int textColor, String pinyin, @ColorInt int pinyinColor) {
864 | this.text = text;
865 | this.textColor = textColor;
866 | this.pinyin = pinyin;
867 | this.pinyinColor = pinyinColor;
868 | }
869 |
870 | public String getText() {
871 | return text;
872 | }
873 |
874 | public void setText(String text) {
875 | this.text = text;
876 | }
877 |
878 | public String getPinyin() {
879 | return pinyin;
880 | }
881 |
882 | public void setPinyin(String pinyin) {
883 | this.pinyin = pinyin;
884 | }
885 |
886 | public int getTextColor() {
887 | return textColor;
888 | }
889 |
890 | public void setTextColor(int textColor) {
891 | this.textColor = textColor;
892 | }
893 |
894 | public int getPinyinColor() {
895 | return pinyinColor;
896 | }
897 |
898 | public void setPinyinColor(int pinyinColor) {
899 | this.pinyinColor = pinyinColor;
900 | }
901 | }
902 | }
903 |
--------------------------------------------------------------------------------
/pinyintextview/src/main/java/com/liwenwei/pinyintextview/StringUtils.java:
--------------------------------------------------------------------------------
1 | package com.liwenwei.pinyintextview;
2 |
3 |
4 | import android.graphics.Color;
5 | import android.text.SpannableString;
6 | import android.text.Spanned;
7 | import android.text.method.LinkMovementMethod;
8 | import android.text.style.ClickableSpan;
9 | import android.widget.TextView;
10 |
11 | import java.util.Locale;
12 |
13 | /**
14 | * StringUtils
15 | * Utils related to {@link String} operations.
16 | */
17 | public class StringUtils {
18 |
19 | /**
20 | * logic about making link inside a TextView
21 | */
22 | public static void makeLinks(TextView textView, String[] links, ClickableSpan[] clickableSpans) {
23 | SpannableString spannableString = new SpannableString(textView.getText());
24 | for (int i = 0; i < links.length; i++) {
25 | ClickableSpan clickableSpan = clickableSpans[i];
26 | String link = links[i];
27 |
28 | int startIndexOfLink = textView.getText().toString().indexOf(link);
29 | spannableString.setSpan(clickableSpan, startIndexOfLink,
30 | startIndexOfLink + link.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
31 | }
32 | textView.setHighlightColor(
33 | Color.TRANSPARENT); // prevent TextView change background when highlight
34 | textView.setMovementMethod(LinkMovementMethod.getInstance());
35 | textView.setText(spannableString, TextView.BufferType.SPANNABLE);
36 | }
37 |
38 | public static boolean isSymbol(char ch) {
39 | if (isCnSymbol(ch)) {
40 | return true;
41 | }
42 | if (isEnSymbol(ch)) {
43 | return true;
44 | }
45 |
46 | if (0x2010 <= ch && ch <= 0x2017) {
47 | return true;
48 | }
49 | if (0x2020 <= ch && ch <= 0x2027) {
50 | return true;
51 | }
52 | if (0x2B00 <= ch && ch <= 0x2BFF) {
53 | return true;
54 | }
55 | if (0xFF03 <= ch && ch <= 0xFF06) {
56 | return true;
57 | }
58 | if (0xFF08 <= ch && ch <= 0xFF0B) {
59 | return true;
60 | }
61 | if (ch == 0xFF0D || ch == 0xFF0F) {
62 | return true;
63 | }
64 | if (0xFF1C <= ch && ch <= 0xFF1E) {
65 | return true;
66 | }
67 | if (ch == 0xFF20 || ch == 0xFF65) {
68 | return true;
69 | }
70 | if (0xFF3B <= ch && ch <= 0xFF40) {
71 | return true;
72 | }
73 | if (0xFF5B <= ch && ch <= 0xFF60) {
74 | return true;
75 | }
76 | if (ch == 0xFF62 || ch == 0xFF63) {
77 | return true;
78 | }
79 | if (ch == 0x0020 || ch == 0x3000) {
80 | return true;
81 | }
82 | return false;
83 |
84 | }
85 |
86 | public static boolean isCnSymbol(char ch) {
87 | if (0x3004 <= ch && ch <= 0x301C) {
88 | return true;
89 | }
90 | if (0x3020 <= ch && ch <= 0x303F) {
91 | return true;
92 | }
93 | return false;
94 | }
95 |
96 | public static boolean isEnSymbol(char ch) {
97 |
98 | if (ch == 0x40) {
99 | return true;
100 | }
101 | if (ch == 0x2D || ch == 0x2F) {
102 | return true;
103 | }
104 | if (0x23 <= ch && ch <= 0x26) {
105 | return true;
106 | }
107 | if (0x28 <= ch && ch <= 0x2B) {
108 | return true;
109 | }
110 | if (0x3C <= ch && ch <= 0x3E) {
111 | return true;
112 | }
113 | if (0x5B <= ch && ch <= 0x60) {
114 | return true;
115 | }
116 | if (0x7B <= ch && ch <= 0x7E) {
117 | return true;
118 | }
119 |
120 | return false;
121 | }
122 |
123 | public static boolean isPunctuation(char ch) {
124 | if (isCjkPunc(ch)) {
125 | return true;
126 | }
127 | if (isEnPunc(ch)) {
128 | return true;
129 | }
130 |
131 | if (0x2018 <= ch && ch <= 0x201F) {
132 | return true;
133 | }
134 | if (ch == 0xFF01 || ch == 0xFF02) {
135 | return true;
136 | }
137 | if (ch == 0xFF07 || ch == 0xFF0C) {
138 | return true;
139 | }
140 | if (ch == 0xFF1A || ch == 0xFF1B) {
141 | return true;
142 | }
143 | if (ch == 0xFF1F || ch == 0xFF61) {
144 | return true;
145 | }
146 | if (ch == 0xFF0E) {
147 | return true;
148 | }
149 | if (ch == 0xFF65) {
150 | return true;
151 | }
152 |
153 | return false;
154 | }
155 |
156 | public static boolean isEnPunc(char ch) {
157 | if (0x21 <= ch && ch <= 0x22) {
158 | return true;
159 | }
160 | if (ch == 0x27 || ch == 0x2C) {
161 | return true;
162 | }
163 | if (ch == 0x2E || ch == 0x3A) {
164 | return true;
165 | }
166 | if (ch == 0x3B || ch == 0x3F) {
167 | return true;
168 | }
169 |
170 | return false;
171 | }
172 |
173 | public static boolean isCjkPunc(char ch) {
174 | if (0x3001 <= ch && ch <= 0x3003) {
175 | return true;
176 | }
177 | if (0x301D <= ch && ch <= 0x301F) {
178 | return true;
179 | }
180 |
181 | return false;
182 | }
183 |
184 | public static String toUpperFirstLetter(String str) {
185 | if (str == null || str.length() == 0) {
186 | return str;
187 | }
188 | return str.substring(0, 1).toUpperCase(Locale.getDefault()) + str.substring(1);
189 | }
190 |
191 | public static boolean isInteger(String s) {
192 | return isInteger(s, 10);
193 | }
194 |
195 | public static boolean isInteger(String s, int radix) {
196 | if (s.isEmpty()) {
197 | return false;
198 | }
199 | for (int i = 0; i < s.length(); i++) {
200 | if (i == 0 && s.charAt(i) == '-') {
201 | if (s.length() == 1) {
202 | return false;
203 | } else {
204 | continue;
205 | }
206 | }
207 | if (Character.digit(s.charAt(i), radix) < 0) {
208 | return false;
209 | }
210 | }
211 | return true;
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/pinyintextview/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
319 | * Def in xml app:pinyinColor=""
320 | *
321 | * @param color pinyin text color.
322 | */
323 | public void setPinyinColor(@ColorInt int color) {
324 | mPinyinColor = color;
325 | for (Token token : mPinyinTokens) {
326 | token.setPinyinColor(mPinyinColor);
327 | }
328 | if (mPinyinTokens.isEmpty()) {
329 | requestLayout();
330 | invalidate();
331 | } else {
332 | setPinyinTextByTokens(mPinyinTokens, mDrawType);
333 | }
334 | }
335 |
336 | /**
337 | * Set line spacing in pixels.
338 | * The same as method {@link #setHorizontalSpacing(int)}
339 | *
340 | * @param px line spacing in pixels.
341 | * @see #setHorizontalSpacing(int)
342 | */
343 | public void setLineSpacing(int px) {
344 | mLineSpacing = px;
345 | requestLayout();
346 | invalidate();
347 | }
348 |
349 | public void setPinyinTextSpacing(int px) {
350 | mPinyinTextSpacing = px;
351 | requestLayout();
352 | invalidate();
353 | }
354 |
355 | public void setUnderlineVerticalSpacing(int px) {
356 | this.mUnderlineVerticalSpacing = px;
357 | }
358 |
359 | public boolean isShowUnderline() {
360 | return this.mUnderline;
361 | }
362 |
363 | public void setUnderline(boolean isShow) {
364 | this.mUnderline = isShow;
365 | invalidate();
366 | }
367 |
368 | /**
369 | * Set horizontal space between two tokens.
370 | * Def in xml app:horizontalSpacing=""
371 | *
372 | * @param px line spacing in pixels.
373 | */
374 | public void setHorizontalSpacing(int px) {
375 | mHorizontalSpacing = px;
376 | mPinyinTextSpacing = mHorizontalSpacing / 2; // half of line spacing
377 | requestLayout();
378 | invalidate();
379 | }
380 |
381 | public void setPinyinText(ListPair.create("你", "nǐ")
, if the
414 | * string is special character, set the pinyin is empty string, like
415 | * Pair.create("!", " ")
416 | * @param mode
417 | */
418 | public void setPinyinTextByTokens(List