JavaME UI设计之Bitmap字体

 
当我们使用 MIDP 的 Graphics 类,可以调用其 setFont() 来指定字体,然后调用该字体的一种 drawChar() 或 drawString() 方法来在 canvas 或后台图像上呈现字体。MIDP 仅提供了一套有限的字体选项,但是:字体可以是等宽的或成比例的,大小可以是小、中或大,并且样式可以是平铺或粗体、斜体和下划线的任意组合。运行库实现工具可能没有满足您的标准的字体,它会将自由返回其确定的最相近的字体,而各种各样的应用程序通常需要使用各种符合应用风格的字体,这时我们就需要使用自定义字体,即需要自己来实现一个字体类;自定义字体通常有两种,分别是:Bitmap字体和点阵字体,本文我们主要分析如何实现一个Bitmap字体类,下一篇文章我们将介绍在J2ME上的点阵字实现。
Bitmap 字体的绘制指令是位到图形环境中的位置的逐一映射。bitmap 字体中的每一个字符对应于从内存拷贝到屏幕的像素排列。换一种说法,每一个字符是通过拷贝到屏幕的一个后台图像来绘制的。从这个角度看,立即就能得到字体的定义(包含用于呈现字体的指令的文件)本身就可以是一个压缩格式(如 PNG 或 JPEG)的图像文件。我们可以把这个图像读到内存中,然后在需要绘制我们的字体的字符时,将它的各有关部分拷贝到屏幕。
那么要实现一个Bitmap 字体类的第一步就是要创建一个这样的图像。高度应当就是字体所需的高度,并且足够宽来容纳要呈现的字符集中的每一个字符。那是多宽呢?Java 技术通常采用 Unicode Character Standard。Unicode 字符集有 95,000 多个字符,比我们能提供的要多,并远远超出我们的需要!因此 MIDTerm 的 Telnet 协议的实现方式是基于 7 位 ASCII 字符集的基础上,该字符集只包含 128 个字符。因为 MIDTerm 需要等宽字体,文档宽度应为一个字符宽度的 128 倍。例如,如果每一个字符高为 12 像素宽为 10 像素,为容纳所有的 128 个字符,则需要高为 12 宽为 1,280 的图像。
现在有了用于放置对应于 ASCII 代码 0 到 127 的 128 个位置。为了能查明哪个字符放到哪个空位中,可能需要 ASCII 表 。请注意前 31 个字符是“控制字符”并且不能看到,可以保留为空白。
本文的演示示例中我们使用了如下图片,作为Bitmap 字体的图像。
大家可以看到该图片的前一部分是空的,什么也没有,原因前面已经解释过,这里就不在重复,下面我们开始构建这个自定义字体类。首先定义一些成员变量,代码如下所示:
public class BitmapFont {
       private int style;/*字体类型*/
       private int size;/*字体大小*/
       private int baseline;/*基线*/
       private int height;/*单个字符高度*/
       private int width;/*单个字符宽度*/
       private int color;
       private int bgcolor;
       private Image image;/* 返回一个自定义字体的对象 */
       private Image srcimage;
       //省略不分...
}
字体的类型我们同样可以使用系统提供的Font中的一个风格,主要包括粗体、斜体等,然后开始构建我们自定义的字体,这里可以采用和Font类一样的静态方法,代码如下:
public static BitmapFont getFont(String inName, int inStyle, int inSize){
       Image i;
       String filename = inName;
       try{
              i = Image.createImage(filename);
       }catch(Throwable t){}
       return new BitmapFont(i, inStyle, inSize);
}
private BitmapFont(Image inImage, int inStyle, int inSize){
       image = inImage;
       style = inStyle;
       size = inSize;
       /*计算字符需要显示的高度和宽度,图片的假设一共有128个字符*/
       try{
       srcimage=null;
       srcimage = Image.createImage(inImage);
       height = image.getHeight();
       width = image.getWidth() / 128;
       /*计算字符需要显示的最下面非背景色的坐标位置*/
       baseline = calculateBaseline();
       }catch(Throwable t){}
}
getFont函数的第一个参数就是我们所准备Bitmap字体的图像文件,创建好图片之后,需要通过calculateBaseline()函数来计算字符需要显示的最下面非背景色的坐标位置,首先假设图片上有128个字符,具体的计算实现代码如下:
/*计算字符需要显示的最下面非背景色的坐标位置*/
private int calculateBaseline(){
       int result = height;
       int imageWidth = image.getWidth();
       int max = 0;
       int total;
       int[] row = new int[imageWidth];
       int background;
       /*确定背景色,假设(0, 0)坐标的像素为北京颜色*/
       image.getRGB(row, 0, 1, 0, 0, 1, 1);
       bgcolor = background = row[0];
       /*按每行搜索像素*/
       for(int y = height / 2;y < height;y++){
              total = 0;
              image.getRGB(row, 0, imageWidth, 0, y, imageWidth, 1);
              for(int x = 0;x < imageWidth;x++){
                     if (row[x] != background){
                            total ++;
                            color = row[x];
                     }
              }
              if (total > max){
                     max = total;
                     result = y;
              }
       }
       return result;
}
到这里就构建好了该字体类的,下面我们就可以开始实现字符的绘制了,绘制单个字符实际上就是使用setClip来设置裁剪区域,然后绘制将对应的字符在字体图像上的区域绘制在制定的裁剪区域内,具体绘制代码如下:
private void drawCharInternal(Graphics g, char character, int x, int y, int anchor){
       /*绘制倾斜类型的字体*/
       if((style & Font.STYLE_ITALIC) != 0){
              g.setClip(x + 1, y, width, height / 2);
              g.drawImage(image, x - width * character + 1, y, anchor);
              g.setClip(x, y + height / 2, width, height / 2);
              g.drawImage(image, x - width * character, y, anchor);
              /*绘制加粗类型的字体*/
              if((style & Font.STYLE_BOLD) != 0){
                     g.setClip(x, y, width, height / 2);
                     g.drawImage(image, x - width * character + 2, y, anchor);
                     g.setClip(x, y + height / 2, width, height / 2);
                     g.drawImage(image, x - width * character + 1, y, anchor);
              }
       }else {/*绘制正常类型的字体*/
              g.setClip(x, y, width, height);
              g.drawImage(image, x - width * character, y, anchor);
              if((style & Font.STYLE_BOLD) != 0){
                     g.drawImage(image, x - width * character + 1, y, anchor);
              }
       }
       /*绘制下划线的字体*/
       if((style & Font.STYLE_UNDERLINED) != 0){
              g.drawLine(x, y + baseline + 2, x + width, y + baseline + 2);
       }
}
绘制过程会根据Font.STYLE_ITALIC、Font.STYLE_BOLD、Font.STYLE_UNDERLINED等常量来确定字体的风格,包括粗体、斜体、下划线,绘制斜体可以通过绘制两次相同的字符并将其绘制的x坐标偏移一个像素来实现,粗体则需要将两次绘制的x,y坐标都偏移一个像素,下划线风格则更加简单,在字体显示之后再绘制一条直线即可。通过drawCharInternal函数,我们就可以实现类似于Font中的drawchar、drawchars、drawString等函数了,下面我们列举出一个绘制字符和字符数组的函数,如下:
public void drawChar(Graphics g, char character, int x, int y, int anchor){
       int clipX = g.getClipX();
       int clipY = g.getClipY();
       int clipW = g.getClipWidth();
       int clipH = g.getClipHeight();
       /*使用setClip方式绘制单个字符到屏幕上*/
       drawCharInternal(g, character, x, y, anchor);
       g.setClip(clipX, clipY, clipW, clipH);
}
/*画字符数组*/
public void drawChars(Graphics g, char[] data, int offset, int length, int x, int y, int anchor){
       if((anchor & Graphics.RIGHT) != 0){
              x -= charsWidth(data, offset, length);
       }else if((anchor & Graphics.HCENTER) != 0){
              x -= (charsWidth(data, offset, length) / 2);
       }
       /*判断需要绘制的字体位置*/
       if((anchor & Graphics.BOTTOM) != 0){
              y -= height;
       }else if((anchor & Graphics.VCENTER) != 0){
              y -= height / 2;
       }
       /*保存原来的裁减区数据*/
       int clipX = g.getClipX();
       int clipY = g.getClipY();
       int clipW = g.getClipWidth();
       int clipH = g.getClipHeight();
       char c;
       for(int i = 0;i < length;i++){
              c = data[offset + i];
              drawCharInternal(g, c, x, y, Graphics.TOP | Graphics.LEFT);
              x += width;
       }
       /*在裁减区绘制*/
       g.setClip(clipX, clipY, clipW, clipH);
}
原理很简单了,直接通过循环调用drawCharInternal函数来绘制出每一个字符即可,drawString就只需要将字符串通过toCharArray函数将字符串转换为字符数组即可。
最后测试一下该自定义自体类的使用,代码如下:
BitmapFont ft = BitmapFont.getFont("/font.png", Font.STYLE_ITALIC, 256);
ft.setColor(0xffff0000);
ft.drawString(g,str, 0, 0, Graphics.LEFT|Graphics.BOTTOM);
效果如下:
 

 
到这里一个自定义字体类基本完成,当然,稍作扩展还可以设置字体的颜色,背景颜色等,由于篇幅关系,这里不能将所有代码都贴出来了!另外,这种字体仅适合于绘制英文的字符串,要是做英文应用,可以很好的使用,但是并不支持中文,下一篇文章我们将介绍另一种自定义字体的实现,它将能支持中文的显示。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值