Android 自定义带拼音的TextView

编程入门 行业动态 更新时间:2024-10-05 05:25:06

Android <a href=https://www.elefans.com/category/jswz/34/1771438.html style=自定义带拼音的TextView"/>

Android 自定义带拼音的TextView

主要功能:

1. 汉字带拼音

2. 关键词不同颜色显示

示例:

Note:此代码为作者为特定项目而使用的,如果想要复用请根据实际情况修改! 

 Java 自定义TextView代码:

package com.example.test.pinyin;import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;import com.example.test.R;import java.util.ArrayList;public class PinyinTextView extends androidx.appcompat.widget.AppCompatTextView {private int fontSize;private String[] pinyin;private String[] hanzi;private int color;private int keyWordColor = Color.rgb(0, 0, 255);private String keyWord;private int keyWordIndex = -1;private TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);//`TextPaint`是Android中的一个类,它继承自`Paint`类,并提供了一些额外的方法和属性,专门用于在绘制文本时进行设置和处理。////`TextPaint`类提供了一些与文本相关的功能,例如:////1. 文本大小和样式:`TextPaint`提供了`setTextSize()`方法来设置文本的大小,`setTypeface()`方法来设置文本的字体样式(如粗体、斜体等)。////2. 文本颜色和效果:`TextPaint`提供了`setColor()`方法来设置文本的颜色,`setUnderlineText()`和`setStrikeThruText()`方法来设置文本的下划线和删除线效果。////3. 文本测量和布局:`TextPaint`提供了`measureText()`方法来测量文本的宽度,`breakText()`方法来根据给定的宽度截断文本,以及一些其他的文本布局相关方法。private Paint.FontMetrics fontMetrics;//`Paint.FontMetrics`是Android中的一个类,它用于描述文本绘制时的字体度量信息。通过`Paint`类的`getFontMetrics()`方法,可以获取一个`Paint.FontMetrics`对象,该对象包含了关于字体的各种度量信息。////`Paint.FontMetrics`类提供了以下属性:////1. `ascent`:文本基线以上的最大距离,包括文本的上坡度和上方的空间。////2. `descent`:文本基线以下的最大距离,包括文本的下坡度和下方的空间。////3. `top`:文本的最高点到基线的距离,包括上坡度和上方的空间。////4. `bottom`:文本的最低点到基线的距离,包括下坡度和下方的空间。////5. `leading`:推荐的行间距,即行与行之间的额外空间。////这些度量信息可以用于调整和布局文本绘制的位置和间距。例如,可以使用`ascent`和`descent`的差值来计算文本的高度,或者使用`leading`来设置行间距。private final int paddingTop = 0;private final int minHeight = 144;private ArrayList<Integer> indexList = new ArrayList<>();    // 存储每行首个String位置int line = 1;float density;public PinyinTextView(Context context) {this(context, null);}public PinyinTextView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public PinyinTextView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PinyinTextView);color = typedArray.getColor(R.styleable.PinyinTextView_customColor, getCurrentTextColor());fontSize = (int) typedArray.getDimension(R.styleable.PinyinTextView_customTextSize, getTextSize());//回收资源typedArray.recycle();initTextPaint();}public void initTextPaint() {textPaint.setColor(color);density = getResources().getDisplayMetrics().density;//返回dpi单位textPaint.setStrokeWidth(density * 2);textPaint.setTextSize(fontSize);fontMetrics = textPaint.getFontMetrics();//1. `textPaint.getFontMetrics()`返回的是`FontMetrics`对象,其中包含了一些浮点数值,表示文本绘制// 时的字体度量信息。`FontMetrics`的字段包括`top`、`ascent`、`descent`、`bottom`和`leading`,// 它们分别表示字体的顶部、上坡度、下坡度、底部和行间距。////2. `textPaint.getFontMetricsInt()`返回的是`FontMetricsInt`对象,其中包含了一些整型数值,// 表示文本绘制时的字体度量信息。`FontMetricsInt`的字段包括`top`、`ascent`、`descent`、`bottom`和// `leading`,它们的精度相对较高,可以直接用于像素级别的计算。}public void setPinyin(String[] pinyin) {this.pinyin = pinyin;}public void setHanzi(String[] hanzi) {this.hanzi = hanzi;}public void setColor(int color) {this.color = color;if (textPaint != null) {textPaint.setColor(color);}}public void setFontSize(int size) {this.fontSize = size;if (textPaint != null) {textPaint.setTextSize(size);}}public void setKeyWordColor(int color) {this.keyWordColor = color;}public void setKeyWord(String keyWord) {this.keyWord = keyWord;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//`onMeasure(int widthMeasureSpec, int heightMeasureSpec)`是`TextView`类中的一个方法,用于测量`TextView`的尺寸。////在Android中,测量视图的尺寸是在视图绘制过程中的一个重要步骤。当`TextView`的尺寸需要确定时,系统会调用`onMeasure()`方法来测量其宽度和高度。////`onMeasure()`方法接收两个参数:`widthMeasureSpec`和`heightMeasureSpec`,它们是32位的整数值,包含了测量模式和测量尺寸的信息。////测量模式有三种://- `MeasureSpec.EXACTLY`:表示精确的尺寸要求,通常是使用固定的数值或`match_parent`属性指定的尺寸。//- `MeasureSpec.AT_MOST`:表示最大尺寸限制,通常是使用`wrap_content`属性指定的尺寸。//- `MeasureSpec.UNSPECIFIED`:表示没有限制,通常在父容器不对子视图施加限制时使用。////测量尺寸是一个32位整数值,可以通过`MeasureSpec.getSize()`方法获取实际的尺寸值。////在`onMeasure()`方法中,开发者需要根据测量模式和测量尺寸来计算并设置`TextView`的宽度和高度。通常,可以使用`MeasureSpec.getMode()`方法获取测量模式,然后根据不同的模式进行相应的处理。////例如,当测量模式为`MeasureSpec.EXACTLY`时,可以直接使用`MeasureSpec.getSize()`来设置`TextView`的尺寸;当测量模式为`MeasureSpec.AT_MOST`时,可以根据内容的大小来动态计算尺寸;当测量模式为`MeasureSpec.UNSPECIFIED`时,可以根据自身的需要来设置尺寸。////最后,在`onMeasure()`方法中,使用`setMeasuredDimension()`方法来设置测量后的宽度和高度,以便系统可以根据这些尺寸来绘制和布局`TextView`。// 需要根据文本测量高度int widthMode, heightMode;int width = 0, height = 0;indexList.clear();widthMode = MeasureSpec.getMode(widthMeasureSpec);heightMode = MeasureSpec.getMode(heightMeasureSpec);//计算宽度if (widthMode == MeasureSpec.EXACTLY) {width = MeasureSpec.getSize(widthMeasureSpec);Log.e("MyTest", "exactly " + width);} else if (widthMode == MeasureSpec.AT_MOST) {width = 700;Log.e("MyTest", "at_most " + width);} else {width = 700;Log.e("MyTest", "unspecified " + width);}//计算高度if (heightMode == MeasureSpec.EXACTLY) {height = MeasureSpec.getSize(heightMeasureSpec);} else if (heightMode == MeasureSpec.AT_MOST) {if (textPaint != null) {if (pinyin != null && pinyin.length != 0) {height = (int) ((pinyin.length / 10 + 1) * 1.6 * (fontMetrics.bottom - fontMetrics.top) + paddingTop);//设置height为行数*行高+paddingTop} else if (hanzi != null) {height = (int) ((fontMetrics.bottom - fontMetrics.top) + paddingTop);//设置height为行高+paddingTop}}} else {//如果为 MeasureSpec.UNSPECIFIEDif (textPaint != null) {if (pinyin != null && pinyin.length != 0) {float pinyinWidth = 0;int lineCount = 1;for (int index = 0; index < pinyin.length; index++) {if (TextUtils.equals(pinyin[index], "null")) {pinyinWidth = pinyinWidth + textPaint.measureText(hanzi[index]);} else {pinyinWidth = pinyinWidth + textPaint.measureText(pinyin[index]);}if (pinyinWidth > width) {indexList.add(index);lineCount++;pinyinWidth = textPaint.measureText(pinyin[index]);}}height = (int) Math.ceil((lineCount * 2) * (textPaint.getFontSpacing() + density * 1));//向上取整 getFontSpacing() 返回以像素为单位的行间距} else if (hanzi != null) {height = (int) textPaint.getFontSpacing();}}}height = Math.max(height, minHeight);setMeasuredDimension(width, height);//`onMeasure()` 方法是用来测量视图的宽度和高度的。在自定义 `TextView` 中,您可以根据自己的需求重写 `onMeasure()` 方法,并在其中调用 `setMeasuredDimension(width, height)` 来设置测量后的宽度和高度。}private float pinyinWidth = 0;private boolean isCenterHorizontal = false;@Overrideprotected void onDraw(Canvas canvas) {//keywordkeyWordIndex = getKeyWordIndex(keyWord, hanzi);float widthMeasure = 0f;if (indexList.isEmpty()) {// 单行数据处理if (isCenterHorizontal) {if (pinyin != null && pinyin.length > 0) {widthMeasure = (getWidth() - textPaint.measureText(combinePinyinEnd(0, pinyin.length))) / 2;//得到剩余宽度的一半Log.e("MyTest", "widthMeasure1: " + widthMeasure);} else if (hanzi != null && hanzi.length > 0) {widthMeasure = (getWidth() - textPaint.measureText(combineHanziEnd(0, hanzi.length))) / 2;}}}pinyinWidth = 0;line = 1;int trimIndex = 0;if (pinyin != null && pinyin.length > 0) {for (int index = 0; index < pinyin.length; index++) {if (!TextUtils.equals(pinyin[index], "null") && !TextUtils.equals(pinyin[index], " ")) {pinyinWidth = widthMeasure + textPaint.measureText(pinyin[index]);if (pinyinWidth > getWidth()) {line++;widthMeasure = 0;trimIndex = index;}Log.e("MyTest", "widthMeasure2: " + index + widthMeasure);if (keyWordIndex != -1 && keyWord != null) {if (keyWordIndex <= index && index <= keyWordIndex + keyWord.length() - 1) {textPaint.setColor(keyWordColor);} else {textPaint.setColor(color);}}if (index == trimIndex) {String trimmed = pinyin[index].replaceFirst("^\\s+", "");canvas.drawText(trimmed, widthMeasure, (line * 2 - 1) * (textPaint.getFontSpacing()), textPaint);canvas.drawText(hanzi[index], widthMeasure + (textPaint.measureText(trimmed) - textPaint.measureText(hanzi[index])) / 2 - moveHalfIfNeed(trimmed, textPaint), (line * 2) * (textPaint.getFontSpacing()), textPaint);widthMeasure = widthMeasure + textPaint.measureText(trimmed);} else {canvas.drawText(pinyin[index], widthMeasure, (line * 2 - 1) * (textPaint.getFontSpacing()), textPaint);canvas.drawText(hanzi[index], widthMeasure + (textPaint.measureText(pinyin[index]) - textPaint.measureText(hanzi[index])) / 2 - moveHalfIfNeed(pinyin[index], textPaint), (line * 2) * (textPaint.getFontSpacing()), textPaint);widthMeasure = widthMeasure + textPaint.measureText(pinyin[index]);}} else if (TextUtils.equals(pinyin[index], "null")) {float hanziWidth = widthMeasure + textPaint.measureText(hanzi[index]);if (hanziWidth > getWidth()) {line++;widthMeasure = 0;}canvas.drawText(hanzi[index], widthMeasure, (line * 2) * textPaint.getFontSpacing(), textPaint);widthMeasure = widthMeasure + textPaint.measureText(hanzi[index]);}}}super.onDraw(canvas);}private float moveHalfIfNeed(String pinyinUnit, TextPaint paint) {if (pinyinUnit.trim().length() % 2 == 0) {return paint.measureText(" ") / 2;} else {return 0;}}private String combinePinyinEnd(int index, int length) {StringBuilder sb = new StringBuilder();for (int subIndex = index; subIndex < length; subIndex++) {String pendString = pinyin[subIndex];sb.append(pendString);}return sb.toString();}private String combineHanziEnd(int index, int length) {StringBuilder sb = new StringBuilder();for (int subIndex = index; subIndex < length; subIndex++) {sb.append(hanzi[subIndex]);}return sb.toString();}public static String[] splitHanziString(String str) {ArrayList<String> arrayList = new ArrayList<>();int i = 0;while (i < str.length()) {char c = str.charAt(i);if (Character.isDigit(c) || (Character.isLetter(c) && Character.UnicodeBlock.of(c) == Character.UnicodeBlock.BASIC_LATIN)) {StringBuilder sb = new StringBuilder();sb.append(c);while (i + 1 < str.length()) {char nextChar = str.charAt(i + 1);if ((Character.isDigit(c) && Character.isDigit(nextChar)) || ((Character.isLetter(c) && Character.UnicodeBlock.of(c) == Character.UnicodeBlock.BASIC_LATIN) && (Character.isLetter(nextChar) && Character.UnicodeBlock.of(nextChar) == Character.UnicodeBlock.BASIC_LATIN))) {sb.append(nextChar);i++;} else {break;}}arrayList.add(sb.toString());} else {arrayList.add(String.valueOf(c));}i++;}return arrayList.toArray(new String[0]);}public static String[] splitPinyin(String str) {String[] s = str.trim().split(" ");ArrayList<String> arrayList = new ArrayList<>();for (String s1 : s) {if (!s1.equals(" ") && !s1.equals("")) {arrayList.add(s1);}}return arrayList.toArray(new String[0]);}public static String[] getFormattedPinyin(String pinyin, String[] hanzi) {String[] splitPinyin = splitPinyin(pinyin);ArrayList<String> newPinyin = new ArrayList<>();ArrayList<String> formattedPinyin = new ArrayList<>();int i = 0;for (String item : hanzi) {if (isChineseCharacter(item)) {newPinyin.add(splitPinyin[i]);i++;} else {newPinyin.add("null");}}for (String s : newPinyin) {if (!s.equals("null")) {formattedPinyin.add(formatUnit(s));} else {formattedPinyin.add("null");}}return formattedPinyin.toArray(new String[0]);}private static boolean isChineseCharacter(String str) {if (str.length() == 1) {char c = str.charAt(0);return isChineseChar(c);}return false;}private static boolean isChineseChar(char c) {Character.UnicodeBlock block = Character.UnicodeBlock.of(c);return block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS|| block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A|| block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B|| block == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS|| block == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT;}private static String formatUnit(String unit) {String result = unit;switch (unit.length()) {case 1:result = "  " + result + "   ";break;case 2:result = "  " + result + "  ";break;case 3:result = " " + result + "  ";break;case 4:result = " " + result + " ";break;case 5:result = result + " ";break;}return result;}private static int getKeyWordIndex(String keyword, String[] hanzi) {for (int i = 0; i < hanzi.length; i++) {int flag = 0;for (int j = 0; j < keyword.length(); j++) {if (i + j < hanzi.length) {if (hanzi[i + j].equals(String.valueOf(keyword.charAt(j)))) {flag++;}}}if (flag == keyword.length()) {return i;}}return -1;}
}

java 设置代码

String text = "一年有365天,我在IT行业工作,8乘以8等于64。";
String[] hanzi=PinyinTextView.splitHanziString(text);
String[] pinyin=PinyinTextView.getFormattedPinyin("yī nián yǒu tiān wǒ zài háng yè gōng zuò chéng yǐ děng yú",hanzi);textView.setHanzi(hanzi);
textView.setColor(Color.rgb(0,255,0));
textView.setFontSize(35);
textView.setPinyin(pinyin);
textView.setKeyWord("行业");
textView.setKeyWordColor(Color.rgb(205,5,255));

更多推荐

Android 自定义带拼音的TextView

本文发布于:2024-02-27 23:37:51,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1767134.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:自定义   拼音   Android   TextView

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!