联想ET980小屏应用及输入法的设计与开发
这篇文章中讲述的内容来自于前几个月我的一个中途夭折的项目,项目的内容是为联想的ET980手机开发小屏应用和键盘输入法。该手机基于Windows Mobile 5.0操作系统。下面是该手机的图片:
可以看到,该手机有一个折叠的键盘,键盘合上后遮住了一半屏幕,剩下的一半即是所谓的小屏:) 。
一. 开发目标
开发的目标是为该小屏开发一个好用的、可扩展的小屏应用系统,并配上一个好用的键盘输入法。
二. 架构设计
看看这两张处于半成品状态的截图:
下部黑色区域是被键盘遮住的部分我们不讨论,就看上面部分,从上往下分为:状态栏区,其中显示的状态图标可扩展增加,目前图中可以看到的有时间和输入法状态,将来可以想到的有手机信号、新短信等等;主窗口,分为三类,应用程序中心也就相当于是主菜单,是各个应用程序的入口,应用程序窗口和屏幕保护;输入法选字区;左右两软键显示区。除此之外,还考虑到可能有不显示任何界面的应用程序在运行,这类应用程序可能在需要的时候显示各对话框什么的,但一般都只在后台运行。
那么为了要实现系统的可扩展性,系统的架构是如何设计的呢,请看下面的类图:
这张类图最关键的部分是左下角的这一块,通过一些继承,将前述所说的几块区域将要承载的控件进行了描述。其中输入法的接口也在这里得到了描述。
这样,作为第三方的开发人员,如果要开发相关的应用,如状态栏程序、主窗口程序、应用程序中心、屏幕保护、输入法,就只需要从相应的类继承即可。
类图左上方的那些类则是用于对前述这些应用进行描述的,第三方开发人员开发好自己的程序之后需要遵循相应的规则在注册表中进行注册,这样,小屏系统就可以发现你的程序了。
三. 一些技术细节
1. 如何监测小键盘的开合?其实是通过监测注册表实现的,软件上查看注册表项Software/Lenovo/KeypadStatus,该项为0则表示盖已翻开,对应GPIO11中断。
2.如何设置背景图?我们知道.net CF的控件是不支持背景图的,那么怎么办呢,我用如下的代码:
首先重写了Panel控件,这是提供整个屏幕的底图:
protected override void OnPaintBackground(PaintEventArgs e)
首先重写了Panel控件,这是提供整个屏幕的底图:
protected override void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground(e);
Image backgroudImage = this.BackgroundImage;
if (backgroudImage != null)
{
Bitmap backImage = new Bitmap(backgroudImage);
e.Graphics.DrawImage(backImage, this.ClientRectangle,
new Rectangle(0, 0, backgroudImage.Width, backgroudImage.Height),
GraphicsUnit.Pixel);
}
}
但是这样还不够,因为上面放置的控件会将这个Panel遮住,所以我们就在ControlBase (这个类在上面的类图中可以找到)中写入下的代码:
protected
override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (useBackgroudImage == true)
{
Image backgroundImage = null;
int top = e.ClipRectangle.Top;
int left = e.ClipRectangle.Left;
Control control = this;
while ((control is MainPnl) == false && control.Parent != null)
{
top = top + control.Top;
left = left + control.Left;
control = control.Parent;
}
if (control is MainPnl)
{
backgroundImage = ((MainPnl)control).BackgroundImage;
}
Rectangle srcRect = new Rectangle(left, top, e.ClipRectangle.Width, e.ClipRectangle.Height);
if (backgroundImage != null)
{
Bitmap backImage = new Bitmap(backgroundImage);
e.Graphics.DrawImage(backImage, e.ClipRectangle, srcRect, GraphicsUnit.Pixel);
} }
}
这样就可以了
3
.……
四. 小键盘输入法
这个小键盘输入法是拼音输入法,实现了整句输入、自动联想、词频自动调整、自造词、魔法1键等功能,具体的特性可以参看中文之星的职能狂拼,因为我在开发的时候基本上就是比照它来写的。
1. 算法上不多说,其实就是一个树搜索。
2. 在候选框里如何把侯选字显示出来?其实也就是重写控件:
private
void pnlLettersQueue_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(new Pen(this.ForeColor), e.ClipRectangle);
int drawedLeft = this.ClientRectangle.Left + 2;
int stringwidth = 0;
//
首先绘制已选好的字符串
if (this.buildedString.Length > 0)
{
stringwidth = (int)e.Graphics.MeasureString(this.buildedString,this.Font).Width;
RectangleF rectfBuiled = new RectangleF(drawedLeft, e.ClipRectangle.Top, stringwidth, e.ClipRectangle.Height);
e.Graphics.DrawString(this.buildedString, this.Font, new SolidBrush(Color.Blue), rectfBuiled);
drawedLeft += (stringwidth + 4);
}
//
绘制光标前的未选定的字符串
if (this.displayStringBeforeKeysPtr.Length > 0)
{
stringwidth = (int)e.Graphics.MeasureString(this.displayStringBeforeKeysPtr,this.Font).Width;
RectangleF rectfBeforeKeysPtr = new RectangleF(drawedLeft, e.ClipRectangle.Top, stringwidth, e.ClipRectangle.Height);
e.Graphics.DrawString(this.displayStringBeforeKeysPtr, this.Font, new SolidBrush(Color.Black), rectfBeforeKeysPtr);
drawedLeft += (stringwidth + 4);
}
//
绘制光标指向的文字
if (this.displayStringInKeysPtr.Length > 0)
{
string stringToDraw = this.displayStringInKeysPtr.Substring(0, this.displayStringLengthInKeysPtr);
stringwidth = (int)e.Graphics.MeasureString(stringToDraw,this.Font).Width;
RectangleF rectfInKeysPtr = new RectangleF(drawedLeft, e.ClipRectangle.Top, stringwidth, e.ClipRectangle.Height);
e.Graphics.DrawString(stringToDraw, this.Font, new SolidBrush(Color.Red), rectfInKeysPtr);
drawedLeft += (stringwidth);
}
//
绘制光标线
e.Graphics.DrawLine(new Pen(Color.Red), (int)drawedLeft, e.ClipRectangle.Top + 2, (int)drawedLeft, e.ClipRectangle.Bottom - 2);
drawedLeft += 2;
if (this.displayStringInKeysPtr.Length > 0)
{
string stringToDraw = displayStringInKeysPtr.Substring(this.displayStringLengthInKeysPtr, this.displayStringInKeysPtr.Length - this.displayStringLengthInKeysPtr);
stringwidth = (int)e.Graphics.MeasureString(stringToDraw,this.Font).Width;
RectangleF rectfInKeysPtr1 = new RectangleF(drawedLeft, e.ClipRectangle.Top, stringwidth, e.ClipRectangle.Height);
e.Graphics.DrawString(stringToDraw, this.Font, new SolidBrush(Color.Gray), rectfInKeysPtr1);
drawedLeft += (stringwidth + 4);
}
//
绘制光标后的文字
if (this.displayStringAfterKeysPtr.Length > 0)
{
stringwidth = (int)e.Graphics.MeasureString(displayStringAfterKeysPtr,this.Font).Width;
RectangleF rectfAfterKeysPtr = new RectangleF(drawedLeft, e.ClipRectangle.Top, stringwidth, e.ClipRectangle.Height);
e.Graphics.DrawString(this.displayStringAfterKeysPtr, this.Font, new SolidBrush(Color.Black), rectfAfterKeysPtr);
}
}
private void pnlPhrases_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(new Pen(this.ForeColor), e.ClipRectangle);
int drawedLeft = this.ClientRectangle.Left + 1;
int stringwidth = 0;
if (this.candidatePhraseses.Count > 0)
{
Font numberFont = new Font(this.Font.Name, this.Font.Size - 3, this.Font.Style);
for (int i = 0; i < this.candidatePhraseses[currentPageDisplayPtr].Count; i++)
{
int numberWidth = (int)e.Graphics.MeasureString((i + 1).ToString(), numberFont).Width;
stringwidth = (int)e.Graphics.MeasureString(this.candidatePhraseses[currentPageDisplayPtr][i].String, this.Font).Width;
if (i == this.currentCandidatePhrasesPtr)
{
Rectangle rect = new Rectangle(drawedLeft, e.ClipRectangle.Top, numberWidth + stringwidth, e.ClipRectangle.Height);
e.Graphics.FillRectangle(new SolidBrush(Color.Blue), rect);
}
//
绘制数字
RectangleF rectfNumber = new RectangleF(drawedLeft, e.ClipRectangle.Top, numberWidth, e.ClipRectangle.Height);
e.Graphics.DrawString((i + 1).ToString(), numberFont, new SolidBrush(Color.Red), rectfNumber);
drawedLeft += (numberWidth);
//
绘制文字
RectangleF rectfString = new RectangleF(drawedLeft, e.ClipRectangle.Top, stringwidth, e.ClipRectangle.Height);
Color stringColor = (i == this.currentCandidatePhrasesPtr) ? Color.White : Color.Black;
e.Graphics.DrawString(this.candidatePhraseses[currentPageDisplayPtr][i].String, this.Font, new SolidBrush(stringColor), rectfString);
drawedLeft += (stringwidth + 4);
}
}
}
最后得到的效果可以看最前面的图。
好了,就说这么多了。如果哪位达人有兴趣,可以与我深入探讨,我可以把源代码公开出来:)。