序:最近公司的需求:做一个模拟LED屏的显示控件
中间各种曲折!此文做个记录,本来早就改写完的!各种原因前后隔了两个多月!
文章写的比较详细,熟悉的大佬可以跳过直接看代码!
PS:后面有完整的代码
正文:具体的读字库和点阵显示就不详细写了,可以参考 简书ForeverCy 大神的文章
传送门 https://www.jianshu.com/p/82301771b4eb
问题一:字符间隔太大!
分析原因:图中选择的是12字体大小!读取的12*12点阵字库数据,一个字符占12*2=24个字节,每个字符横向占2个字节也就是16位(绘制出来就是16个点),而英文字母实际上所占用的位置只有5-7位,所以字符间会有点大!当然不只是英文字符,其他字符也有一样的问题
解决思路:我们读取出来的字库字模数据最后是转换成二维布尔数组给显示控件绘制的,能动的就是这个数组了,所以将二维数组竖排取出判断,如果全是flase那就是没有数据的空格了!再做删除处理,当然为了保证程序的适应性,防止存在占满16位的字符(显示出来的效果就是两个字符连在一起)!在删除间隔之前,需在字符之间再添加一位空格!
代码实现:
step1:在字符间插入一位空格
/**
* 在字符间插入一位空格
* */
private void inserAemptyData(){
Log.e("matrix",matrix[0].length+"");
//初始化空值数组
empty_data = new boolean[dots];
for (int i=0;i<dots;i++) {
empty_data[i] = false;
}
ArrayList<boolean[]> tem = new ArrayList<>();
int position = -1;//更改后的数组当前下标
int matrix_index=-1;//更改前的数组当前下标
spaceIndexs = new ArrayList<>();
ArabicIndexs = new ArrayList<>();
for (int i=0;i<str.length();i++){
String indexstr = str.substring(i, i + 1);
String followstr = "";
if (i<str.length()-1){
followstr = str.substring(i+1,i+2);
}
boolean isSpace = indexstr.equals(" ");
boolean isArabic = (!followstr.equals(""))&&ArabicUtils.isArbic(followstr)&&ArabicUtils.isArbic(indexstr);
Log.e("indexstr>>>>",indexstr+"");
for (int j=0;j<16;j++){//无论是12还是16字体 横排都是两个字节 16位
position=position+1;
matrix_index+=1;
boolean[] indx = getstrbycolumn(matrix,matrix_index);//取一竖排
if (isArabic){
ArabicIndexs.add(position);
}
if (isSpace){
spaceIndexs.add(position);
}
tem.add(position,indx);
indx =null;
}
// 连续的两个阿拉伯字符之间不需要插入空格
if ((!followstr.equals(""))&&ArabicUtils.isArbic(followstr)&&ArabicUtils.isArbic(indexstr)){
}else{
Log.e("insert","1");
tem.add(position,empty_data);
position+=1;
}
}
PS:此函数中还考虑了空格(16位空格)和阿拉伯文(字符是连在一起的不需要间隔)
step2:取数组竖排判断并删除多余位空格(只留一个位)
/*
* 2017.12.25 添加
* 消除多余空格
* */
private void fillMatrixEmpty(){
//原则:判断boolean二维数组竖排是否出现连续为Flase的情况 如果是 便舍弃一个 否则添加到新的数组中
//
ArrayList<boolean[]> tem = new ArrayList<>();
int space_number=0;
for (int i = 0;i<matrix[0].length-1;i++){
boolean[] indx = getstrbycolumn(matrix,i);//取一竖排
boolean[] indy = getstrbycolumn(matrix,i+1);
if (i==matrix[0].length-1&&!Arrays.equals(empty_data,indy)){//最后一排加进去
// Log.e(i+">>>>","last_data");
tem.add(indy);
} else if (isSpaceVaules(i)&&isSpaceVaules(i+1)){//如果是空格的位置
space_number+=1;
// Log.e(i+">>>>","空格");
if (space_number<5){//空格位置过长 只取4个点作为空格
tem.add(indx);
}
if (space_number==16){
space_number=0;
}
}
else if (!isSpaceVaules(i)&&Arrays.equals(empty_data,indx)&&Arrays.equals(empty_data,indy)){//如果相邻两列都为空 不保存
// Log.e(i+">>>>","empty_data");
}
else if (isArabicVaules(i)&&!isSpaceVaules(i)&&Arrays.equals(empty_data,indx)){//阿拉伯文 清除所有空格
}
else{
//否则保存
// Log.e(i+">>>>","data");
tem.add(indx);
}
indx = null;
indy = null;
}
boolean[][] temps1 = new boolean[matrix.length][tem.size()];//12*n
for (int i = 0;i<tem.size();i++){
boolean[] pos = tem.get(i);
for (int j=0;j<matrix.length;j++){
temps1[j][i] = pos[j];
}
}
spaceIndexs=null;
ArabicIndexs =null;
matrix = temps1;
// Log.e("matrix>>>>",matrix[0].length+"");
// Log.e("tem>>>>",tem.size()+"");
// Log.e("temps1>>>>",temps1[0].length+"");
}
/**
* 取某一竖排值
* */
public boolean[] getstrbycolumn(boolean[][] strarray, int column){
int columnlength = strarray.length;
boolean[] result = new boolean[strarray.length];
for(int i=0;i<columnlength;i++) {
result[i] = strarray[i][column];
}
return result;
}
此时再看下效果,如下:
很明显间隔ok了,当然既然我们的主题是解决特殊国别的问题,先看下现在的效果如何
从左至右依次是:泰文,希伯来文,阿拉伯文
首先来看下泰文的问题:
问题二:字符上下标(估浅先这么叫)无法正常显示
原因分析:带上下标的文字根本不只是一个字符,但我们读取字模数据的时候是按照字符读取的,所以上下标字符肯定不会出现在对应字符的上标或者下标位置
解决思路:这里先看下 建国雄心 大佬新浪微博上的文章 泰文排版规则 ,可以先了解下泰文的基本字符知识,文章最后给出的那个解决方案,在下才疏学浅没有用得上!浪费了大佬的苦心,抱歉!那就另辟蹊径吧!
仔细看图中泰文的上标字符,其中区域1(暂时叫实部)是有用的,区域2(暂时叫虚部)是不需要的!再用字库软件打开字库文件看下:
所有的上下标的字符的虚部所占的位置都是一样的!既然这样,那在读取字库数据的时候就可以去掉虚部的部分,留下来的 实部再叠加到对应的字符上就行了,至于有哪些字符存在这种情况就需要一个个找了!我这里给我三种国别文字上下标字符的Unicode码,供参考:
//阿拉伯文上下标字符 unicode
static final int[] ArabicSup_Subs = {0x64b,0x64c,0x64d,0x64e,0x64f,0x650,0x651,0x652,0x653,0x654,0x655,0x656,0x657,0x658,0x659,0x65a,0x65b,0x65c,0x65d,0x65e,
0x6d6,0x6d7,0x6d8,0x6d9,0x6da,0x6db,0x6dc,
0x6df,0x6e0,0x6e1,0x6e2,0x6e3,0x6e4,
0x6e7,0x6e8,0x6ea,0x6eb,0x6ec
};
//希伯来文上下标字符 unicode
static final int[] HebrewSup_Subs = {0x591,0x592,0x593,0x594,0x595,0x596,0x597,0x598,0x599,0x59a,0x59b,0x59c,0x59d,0x59e,0x59f,0x5a0,
0x5a1,0x5a2,0x5a3,0x5a4,0x5a5,0x5a6,0x5a7,0x5a8,0x5a9,0x5aa,0x5ab,0x5ac,0x5ad,0x5ae,0x5af,0x5b0,0x5b1,0x5b2,
0x5b3,0x5b4,0x5b5,0x5b6,0x5b7,0x5b8,
0x5bb,0x5bd,0x5bf,0x5c1,0x5c2,0x5c4,0x5c5,0x5c7
};
//泰文 上下标字符 unicode
static final int[] ThaiSup_Subs = {0x0e31,0x0e34,0x0e35,0x0e36,0x0e37,0x0e38,0x0e39,0x0e3a,
0x0e47,0x0e48,0x0e49,0x0e4a,0x0e4b,0x0e4c,0x0e4d,0x0e4e
};
PS:实际上阿拉伯文和希伯来文都存在这种上下标的现象,在这里就一并处理了!后面就不再累述了!
实现代码:
step1:在读取字符字模数据时,去掉虚部并叠加到相应字符
//泰文上下标处理
if (ArabicUtils.isThai(subjectStr)&ArabicUtils.isThai(followStr)){//都为泰文字符
String follow2str = "";
if(index<str.length()-2){
follow2str = str.substring(index+2,index+3);//泰文存在上下标同时存在的情况
}
if (!follow2str.equals("")&&ArabicUtils.isSup_SubThai(follow2str)&&ArabicUtils.isSup_SubThai(followStr)){
byte[] data_follow2 = readAllZiMo(follow2str);
if(data_follow2!=null){//后续字符数据不为空
data = ArabicUtils.adminSup_SubThai(data_follow,data,dots);//将后面字符数据叠加到当前字符数据中
data = ArabicUtils.adminSup_SubThai(data_follow2,data,dots);//将后面字符数据叠加到当前字符数据中
index+=2;
System.arraycopy(data, 0, dataResult, hasDealByte, data.length);
hasDealByte = hasDealByte + data.length;
System.arraycopy(replacedata, 0, dataResult, hasDealByte, replacedata.length);
hasDealByte = hasDealByte + replacedata.length;
System.arraycopy(replacedata, 0, dataResult, hasDealByte, replacedata.length);
hasDealByte = hasDealByte + replacedata.length;
}
}else if (ArabicUtils.isSup_SubThai(followStr)){//后面字符是否为上下标特殊字符
if(data_follow!=null){//后续字符数据不为空
data = ArabicUtils.adminSup_SubThai(data_follow,data,dots);//将后面字符数据叠加到当前字符数据中
index+=1;
System.arraycopy(data, 0, dataResult, hasDealByte, data.length);
hasDealByte = hasDealByte + data.length;
System.arraycopy(replacedata, 0, dataResult, hasDealByte, replacedata.length);
hasDealByte = hasDealByte + replacedata.length;
}
}else {
System.arraycopy(data, 0, dataResult, hasDealByte, data.length);
hasDealByte = hasDealByte + data.length;
}
}else {
System.arraycopy(data, 0, dataResult, hasDealByte, data.length);
hasDealByte = hasDealByte + data.length;
}
上面的代码中还处理了上下标同时存在的情况,处理前后对比图如下(12*12的点阵太小,16*16才能看除效果):
很明显 效果好很多了,上面只给出了泰文的处理方式,其他两种语言也都一样,这里我就不再贴了!如果需要完整代码,后面我会给出链接!
接下来,看阿拉伯文!
问题三:读取的字符混乱,而且反向相反
这里再次感谢 建国雄心的微博:阿拉伯文排版规则 的解惑,
原因分析:引用文章中的一句话:“阿拉伯文的字母没有大写和小写的区分,但有印刷体和书写体的区别,而且除去دذ ر زو五个字母以外,其他23个字母都可以和后面的字母连写,而且因其在词头,词中和词尾的位置不同,字形也有所变化。阿拉伯文字的书写方向和中文不同,它是自右向左横着写”,也就是说,我们看到的是书写体,而字库文件是以印刷体保存的!也因为阿拉伯文是连字体的,所以直接读取出来的数据是不对的,需要重新变形成新的字符串再读取byte数据
解决思路:大神的微博中给出的重排规则,至于方向,如果是纯阿拉伯文字,直接反向就行了!但对于阿拉伯文和中文或者英文混输就需要另外判断了,
实现代码:
/**
* 阿拉伯文排版
* **/
@NonNull
public static String getArbicResult(String str){
StringBuffer stringBuffer = new StringBuffer();
for (int i=0;i<str.length();i++){
//取连续的三个字符判断
String substr = str.substring(i,i+1);
String pre_sub ;
String for_sub ;
if (i==0){
pre_sub = "";
}else {
pre_sub = str.substring(i-1,i);
}
if (i==str.length()-1){
for_sub = "";
}else {
for_sub = str.substring(i+1,i+2);
}
if (isArbic(substr)){ //如果当前字符是阿拉伯文
boolean ispreconnect = false ;
boolean isforconnect = false;
//排版规则1:
// 1.判断是否前连
if (isArbic(pre_sub)&&!pre_sub.equals("")){//如果前一个字符是阿拉伯文,判断是否前连
ispreconnect = getIsPreConnect(pre_sub);
}else{//不需要判断是否前连
}
//2.判断是否后连
if (isArbic(for_sub)&&!for_sub.equals("")){//如果前一个字符是阿拉伯文,判断是否后连
isforconnect = getIsForConnect(for_sub);
}else{//不需要判断是否后连
}
//排版规则2:
//以0x644开头,后面跟的是0x622,0x623,0x625,0x627
if (Integer.parseInt(gbEncoding(substr),16)==0x0644&&!for_sub.equals("")) {//是0x0644
int fors = Integer.parseInt(gbEncoding(for_sub),16);
if (fors==0x0622||fors==0x0623||fors==0x0625||fors==0x0627){//后面接0x622,0x623,0x625,0x627
//这种情况处理后 两个字符合并成一个字符
//判断0x0644前一个字符是否前连
int temp = 0;
if (ispreconnect){//是前连 取arabic_specs数组 1
temp = 1;
}else{//不是 取arabic_specs数组 0
temp = 0;
}
switch (fors){
case 0x0622:
substr = arabic_specs[0][temp]+"";
break;
case 0x0623:
substr = arabic_specs[1][temp]+"";
break;
case 0x0625:
substr = arabic_specs[2][temp]+"";
break;
case 0x0627:
substr = arabic_specs[3][temp]+"";
break;
}
substr = getStrFromUniCode(substr);
i+=1;
}
}else if (isNeedChange(substr)){//不是0x0644,并且在需要变形的数组中
int index = 0;
if(!isforconnect&&ispreconnect){//前连
index = 1;
}
if (isforconnect&&!ispreconnect){//后连
index = 2;
}
if (isforconnect&&ispreconnect){//中间
index = 3;
}
if (!isforconnect&&!ispreconnect){//独立
index = 4;
}
substr = getChangeReturn(substr,index);
substr = getStrFromUniCode(substr);
}
}else{//不是阿拉伯文
}
stringBuffer.append(substr);
}
return stringBuffer.toString();
}
/**
*返回重排后的字符
* */
private static String getChangeReturn(String substr,int index) {
int subunicode = Integer.parseInt(gbEncoding(substr),16);
for (int i=0;i<Arbic_Position.length;i++){
if (Arbic_Position[i][0]==subunicode){
substr = "\\u"+Integer.toHexString(Arbic_Position[i][index]);
}
}
return substr;
}
//阿拉伯文 当前字符是否需要重排
private static boolean isNeedChange(String substr) {
int subunicode = Integer.parseInt(gbEncoding(substr),16);
for (int i=0;i<Arbic_Position.length;i++){
if (Arbic_Position[i][0]==subunicode){
return true;
}
}
return false;
}
//后连
private static boolean getIsForConnect(String for_sub) {
int subunicode = Integer.parseInt(gbEncoding(for_sub),16);
for (int i=0;i<theSet2.length;i++){
if (theSet2[i]==subunicode){
return true;
}
}
return false;
}
//前连
private static boolean getIsPreConnect(String pre_sub) {
int subunicode = Integer.parseInt(gbEncoding(pre_sub),16);
for (int i=0;i<theSet1.length;i++){
if (theSet1[i]==subunicode){
return true;
}
}
return false;
}
下面是需要的Unicode数组:
//阿拉伯文中需要变形字符的unicode码 0x621-0x64a 集合中对应不同位置变形后的unicode码
static final int[][] Arbic_Position = //former first, last, middle, alone
{
{0x621, 0xfe80, 0xfe80, 0xfe80, 0xfe80}, // 0x621
{0x622, 0xfe82, 0xfe81, 0xfe82, 0xfe81},
{ 0x623,0xfe84, 0xfe83, 0xfe84, 0xfe83},
{ 0x624,0xfe86, 0xfe85, 0xfe86, 0xfe85},
{0x625, 0xfe88, 0xfe87, 0xfe88, 0xfe87},
{ 0x626,0xfe8a, 0xfe8b, 0xfe8c, 0xfe89},
{0x627, 0xfe8e, 0xfe8d, 0xfe8e, 0xfe8d},
{0x628, 0xfe90, 0xfe91, 0xfe92, 0xfe8f}, // 0x628
{ 0x629,0xfe94, 0xfe93, 0xfe94, 0xfe93},
{0x62a, 0xfe96, 0xfe97, 0xfe98, 0xfe95}, // 0x62A
{0x62b, 0xfe9a, 0xfe9b, 0xfe9c, 0xfe99},
{0x62c, 0xfe9e, 0xfe9f, 0xfea0, 0xfe9d},
{0x62d, 0xfea2, 0xfea3, 0xfea4, 0xfea1},
{ 0x62e,0xfea6, 0xfea7, 0xfea8, 0xfea5},
{0x62f, 0xfeaa, 0xfea9, 0xfeaa, 0xfea9},
{0x630, 0xfeac, 0xfeab, 0xfeac, 0xfeab}, // 0x630
{0x631, 0xfeae, 0xfead, 0xfeae, 0xfead},
{ 0x632,0xfeb0, 0xfeaf, 0xfeb0, 0xfeaf},
{0x633, 0xfeb2, 0xfeb3, 0xfeb4, 0xfeb1},
{0x634, 0xfeb6, 0xfeb7, 0xfeb8, 0xfeb5},
{ 0x635,0xfeba, 0xfebb, 0xfebc, 0xfeb9},
{0x636, 0xfebe, 0xfebf, 0xfec0, 0xfebd},
{0x637, 0xfec2, 0xfec3, 0xfec4, 0xfec1},
{0x638, 0xfec6, 0xfec7, 0xfec8, 0xfec5}, // 0x638
{0x639, 0xfeca, 0xfecb, 0xfecc, 0xfec9},
{ 0x63a,0xfece, 0xfecf, 0xfed0, 0xfecd}, //0x63A
{0x63b, 0x63b, 0x63b, 0x63b, 0x63b},
{0x63c, 0x63c, 0x63c, 0x63c, 0x63c},
{0x63d, 0x63d, 0x63d, 0x63d, 0x63d},
{0x63e, 0x63e, 0x63e, 0x6