一年多以前,我一直在寻找同样该死的东西。并最终由我自己实施。
线的宽度是符号间空间(字距调整)、符号之间的空白宽度、视图边界宽度的总和。
符号间空间(紧缩)对于您要使用的每对符号都不同。如果你的字体不是 MONOSPACE,WA 和 DA 有不同的空间。
whitespaces 相同,每对符号不同。
左右边界相同,每个符号不同。
所以我做了什么 - 为您的应用程序准备一个包含每个可能符号的数组(不是全部,而是那些可能出现在您提供给视图的文本中的符号)然后计算每个符号的宽度。
HashMap<String, Integer[]> charMap = new HashMap<>();
String defaultSet = "ҢңӨөөҮүёЁQWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm\"'[]{}\\./`~!@#$%^&**()_+`12\uFEFF3456«;789»0—-=:|<>?йцуке…нгшщзхъфывапролджэячсмитьбюЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,,–";
String[] charArray = defaultSet.split("");
Rect bounds = new Rect();
for (String mChar : charArray) {
if (mChar.length() == 0) {
continue;
}
//textView.setText(mChar);
textPaint.getTextBounds(String.valueOf(mChar), 0, 1, bounds);
int height = bounds.height();
int width = bounds.width();
Integer[] size = {width, height};
charMap.put(mChar, size);
}
接下来是获取每对符号的字距调整、空格和边界值。每个方法中的最后一个参数是您在第一个代码框中计算的 charMap。
/**
* Returns Bounds map for each symbol
* @param charArray
* @param textPaint
* @param mCharMap
* @return
*/
private HashMap<String, Integer> generateBoundsLeftMap(String[] charArray, TextPaint textPaint, HashMap<String, Integer[]> mCharMap){
//individual letterspacing, and whitespace width.
HashMap<String, Integer> boundsMap= new HashMap<>();
Rect bounds = new Rect();
for(int i=1;i<charArray.length;i++){
String mKeyChar=charArray[i];
String testLine="skajd fnфыв";
textPaint.getTextBounds(testLine,0,11,bounds);
int testwidth=bounds.width();
String checkLine= mKeyChar.concat(testLine).concat(mKeyChar);
textPaint.getTextBounds(checkLine, 0, 13, bounds);
boundsMap.put(charArray[i], bounds.left);
MLog.sout("boundsleft", "" + bounds.left, true);
MLog.sout("measuretext",""+textPaint.measureText(checkLine),true);
MLog.sout("boundsright",""+(textPaint.measureText(checkLine)-testwidth-(mCharMap.get(mKeyChar)[0]*2)-bounds.left),true);
}
return boundsMap;
}
/**
* Returns Bounds map for each symbol
* @param charArray
* @param textPaint
* @param mCharMap
* @return
*/
private HashMap<String, Integer> generateBoundsRightMap(String[] charArray, TextPaint textPaint,HashMap<String, Integer[]> mCharMap){
//individual letterspacing, and whitespace width.
HashMap<String, Integer> boundsMap= new HashMap<>();
Rect bounds=new Rect();
for(int i=1;i<charArray.length;i++){
String mKeyChar=charArray[i];
String testLine="aba";
String checkLine= mKeyChar.concat(testLine).concat(mKeyChar);
textPaint.getTextBounds(checkLine, 0, 5, bounds);
int testwidth=bounds.width();
int boundsright =(int) textPaint.measureText(checkLine)-testwidth-bounds.left;
boundsMap.put(charArray[i], boundsright);
}
return boundsMap;
}
/**
* returns Pairs map. HashMap<String, HashMap<String, Integer > >
* char1 char2 letterspacing
* @param charArray
* @param textPaint
* @return
*/
private HashMap<String, HashMap<String, Integer>> generateKerningPairs(String[] charArray, TextPaint textPaint, HashMap<String,Integer[]> mCharMap){
//individual letterspacing, and whitespace width.
HashMap<String, HashMap<String, Integer> > pairMap= new HashMap<>();
Rect bounds=new Rect();
int lineWidth;
int interspaceWidth;
for(int i=1;i<charArray.length;i++){
String mKeyChar=charArray[i];
HashMap<String , Integer> spaceMap=new HashMap<>();
for(int z=1; z<charArray.length;z++){
//getletterspacing
String checkLine=mKeyChar.concat(charArray[z]);
textPaint.getTextBounds(checkLine,0,2,bounds);
lineWidth = bounds.width();
interspaceWidth=lineWidth-(mCharMap.get(mKeyChar))[0] - (mCharMap.get(charArray[z]))[0];
spaceMap.put(charArray[z], interspaceWidth);
}
pairMap.put(mKeyChar,spaceMap);
}
return pairMap;
}
/**
* returns Pairs map. HashMap<String, HashMap<String, Integer > >
* char1 char2 letterspacing
* @param charArray
* @param textPaint
* @return
*/
private HashMap<String, HashMap<String, Integer>> generateSpacePairs(String[] charArray, TextPaint textPaint, HashMap<String,Integer[]> mCharMap){
//individual letterspacing, and whitespace width.
HashMap<String, HashMap<String, Integer> > pairMap= new HashMap<>();
Rect bounds=new Rect();
int lineWidth;
int spaceWidth;
for(int i=1;i<charArray.length;i++){
String mKeyChar=charArray[i];
HashMap<String , Integer> spaceMap=new HashMap<>();
for(int z=1; z<charArray.length;z++){
//getletterspacing
String checkLine=mKeyChar.concat(" ").concat(charArray[z]);
textPaint.getTextBounds(checkLine,0,3,bounds);
lineWidth = bounds.width();
spaceWidth=lineWidth-(mCharMap.get(mKeyChar))[0] - (mCharMap.get(charArray[z]))[0];
spaceMap.put(charArray[z], spaceWidth);
}
pairMap.put(mKeyChar,spaceMap);
}
return pairMap;
}
计算这些值需要很长时间,所以我为每个 DPI 做了一次(使用 genymotion 和更改屏幕密度)并保存到 txt 文件中的 JSON 中。
内核映射.txt
空间地图.txt
...
等等。
你把这些文件放到你的assets文件夹里,结构是这样的
assets/dpi/160/kernmap.txt
...
资产/dpi/240/kernmap.txt
...
等等。
从这一点开始 - 你不能使用你在开始时没有放在默认设置字符串中的符号
String defaultSet = "ҢңӨөөҮүёЁQWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm\"'[]{}\\./`~!@#$%^&**()_+`12\uFEFF3456«;789»0—-=:|<>?йцуке…нгшщзхъфывапролджэячсмитьбюЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,,–";
即使准备好所有地图,您也会遇到性能问题。我使用 jni 来计算页面长度。
接下来呢?
你得到你的字符串,让我们说它
"This is a test line provided by blabla"
取“T”和“h”(前 2 个字符)
linewidth = boundsLeftmap.get("T") (你有它在地图中)+ widthofchars.get("T") + kerningbetween.get("T").get("h") + 等等.
使用 JNI 需要 3-4 秒来处理一个 80000 个字符的字符串并将其分解为完全适合您视图的页面。