免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
论坛 程序设计 C/C++ c++
12345
最近访问板块 发新帖
楼主: clzhana
打印 上一主题 下一主题

[C++] c++ [复制链接]

论坛徽章:
0
41 [报告]
发表于 2011-05-18 23:19 |只看该作者
开发者学习笔记——View&Canvas
BitMap、View以及Canvas是我们Ophone程序中常用到的类。本日以feisky的学习笔记呈现,通过实例讲解View&Canvas等等。
1. 从资源中获取位图
可以使用BitmapDrawable或者BitmapFactory来获取资源中的位图。

当然,首先需要获取资源:

Resources res=getResources();

使用BitmapDrawable获取位图

使用BitmapDrawable (InputStream is)构造一个BitmapDrawable;
使用BitmapDrawable类的getBitmap()获取得到位图;
// 读取InputStream并得到位图
InputStream is=res.openRawResource(R.drawable.pic180);
BitmapDrawable bmpDraw=new BitmapDrawable(is);
Bitmap bmp=bmpDraw.getBitmap();
或者采用下面的方式:

BitmapDrawable bmpDraw=(BitmapDrawable)res.getDrawable(R.drawable.pic180);
Bitmap bmp=bmpDraw.getBitmap();
使用BitmapFactory获取位图
(Creates Bitmap objects from various sources, including files, streams, and byte-arrays.)
使用BitmapFactory类decodeStream(InputStream is)解码位图资源,获取位图。
Bitmap bmp=BitmapFactory.decodeResource(res, R.drawable.pic180);
BitmapFactory的所有函数都是static,这个辅助类可以通过资源ID、路径、文件、数据流等方式来获取位图。
以上方法在编程的时候可以自由选择,在Android SDK中说明可以支持的图片格式如下:png (preferred), jpg (acceptable), gif (discouraged),和bmp(Android SDK Support Media Format)。
2. 获取位图的信息
要获取位图信息,比如位图大小、像素、density、透明度、颜色格式等,获取得到Bitmap就迎刃而解了,这些信息在Bitmap的手册中,这里只是辅助说明以下2点:

在Bitmap中对RGB颜色格式使用Bitmap.Config定义,仅包括ALPHA_8、ARGB_4444、ARGB_8888、RGB_565,缺少了一些其他的,比如说RGB_555,在开发中可能需要注意这个小问题;
Bitmap还提供了compress()接口来压缩图片,不过AndroidSAK只支持PNG、JPG格式的压缩;其他格式的需要Android开发人员自己补充了。
3. 显示位图
显示位图可以使用核心类Canvas,通过Canvas类的drawBirmap()显示位图,或者借助于BitmapDrawable来将Bitmap绘制到Canvas。当然,也可以通过BitmapDrawable将位图显示到View中。

转换为BitmapDrawable对象显示位图

// 获取位图
Bitmap bmp=BitmapFactory.decodeResource(res, R.drawable.pic180);
// 转换为BitmapDrawable对象
BitmapDrawable bmpDraw=new BitmapDrawable(bmp);
// 显示位图
ImageView iv2 = (ImageView)findViewById(R.id.ImageView02);
iv2.setImageDrawable(bmpDraw);

使用Canvas类显示位图

这儿采用一个继承自View的子类Panel,在子类的OnDraw中显示

public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new Panel(this));
}

class Panel extends View{
public Panel(Context context) {
super(context);
}
public void onDraw(Canvas canvas){
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.pic180);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(bmp, 10, 10, null);
}
}
}
4. 位图缩放
(1)将一个位图按照需求重画一遍,画后的位图就是我们需要的了,与位图的显示几乎一样:drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)。

(2)在原有位图的基础上,缩放原位图,创建一个新的位图:CreateBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)

(3)借助Canvas的scale(float sx, float sy) (Preconcat the current matrix with the specified scale.),不过要注意此时整个画布都缩放了。

(4)借助Matrix:

Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.pic180);
Matrix matrix=new Matrix();
matrix.postScale(0.2f, 0.2f);
Bitmap dstbmp=Bitmap.createBitmap(bmp,0,0,bmp.getWidth(),
bmp.getHeight(),matrix,true);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(dstbmp, 10, 10, null);
5.位图旋转
同样,位图的旋转也可以借助Matrix或者Canvas来实现。

Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.pic180);
Matrix matrix=new Matrix();
matrix.postScale(0.8f, 0.8f);
matrix.postRotate(45);
Bitmap dstbmp=Bitmap.createBitmap(bmp,0,0,bmp.getWidth(),
bmp.getHeight(),matrix,true);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(dstbmp, 10, 10, null);
旋转效果:

6.图片水印的生成方法
生成水印的过程。其实分为三个环节:第一,载入原始图片;第二,载入水印图片;第三,保存新的图片。

/**
* create the bitmap from a byte array
*
* @param src the bitmap object you want proecss
* @param watermark the water mark above the src
* @return return a bitmap object ,if paramter's length is 0,return null
*/
private Bitmap createBitmap( Bitmap src, Bitmap watermark )
{
String tag = "createBitmap";
Log.d( tag, "create a new bitmap" );
if( src == null )
{
return null;
}

int w = src.getWidth();
int h = src.getHeight();
int ww = watermark.getWidth();
int wh = watermark.getHeight();
//create the new blank bitmap
Bitmap newb = Bitmap.createBitmap( w, h, Config.ARGB_8888 );//创建一个新的和SRC长度宽度一样的位图
Canvas cv = new Canvas( newb );
//draw src into cv.drawBitmap( src, 0, 0, null );//在 0,0坐标开始画入src
//draw watermark into
cv.drawBitmap( watermark, w - ww + 5, h - wh + 5, null );//在src的右下角画入水印
//save all clip
cv.save( Canvas.ALL_SAVE_FLAG );//保存
//store
cv.restore();//存储
return newb;
}
6.图片水印的生成方法
生成水印的过程。其实分为三个环节:第一,载入原始图片;第二,载入水印图片;第三,保存新的图片。

/**
* create the bitmap from a byte array
*
* @param src the bitmap object you want proecss
* @param watermark the water mark above the src
* @return return a bitmap object ,if paramter's length is 0,return null
*/
private Bitmap createBitmap( Bitmap src, Bitmap watermark )
{
String tag = "createBitmap";
Log.d( tag, "create a new bitmap" );
if( src == null )
{
return null;
}

int w = src.getWidth();
int h = src.getHeight();
int ww = watermark.getWidth();
int wh = watermark.getHeight();
//create the new blank bitmap
Bitmap newb = Bitmap.createBitmap( w, h, Config.ARGB_8888 );//创建一个新的和SRC长度宽度一样的位图
Canvas cv = new Canvas( newb );
//draw src into cv.drawBitmap( src, 0, 0, null );//在 0,0坐标开始画入src
//draw watermark into
cv.drawBitmap( watermark, w - ww + 5, h - wh + 5, null );//在src的右下角画入水印
//save all clip
cv.save( Canvas.ALL_SAVE_FLAG );//保存
//store
cv.restore();//存储
return newb;
}
7.Canvas的save和restore
onDraw方法会传入一个Canvas对象,它是你用来绘制控件视觉界面的画布。
在onDraw方法里,我们经常会看到调用save和restore方法,它们到底是干什么用的呢?
❑ save:用来保存Canvas的状态。save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作。
❑ restore:用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响。
save和restore要配对使用(restore可以比save少,但不能多),如果restore调用次数比save多,会引发Error。save和restore之间,往往夹杂的是对Canvas的特殊操作。
例如:我们先想在画布上绘制一个右向的三角箭头,当然,我们可以直接绘制,另外,我们也可以先把画布旋转90°,画一个向上的箭头,然后再旋转回来(这种旋转操作对于画圆周上的标记非常有用)。然后,我们想在右下角有个20像素的圆,那么,onDraw中的核心代码是:
int px = getMeasuredWidth();
int py = getMeasuredWidth();
// Draw background
canvas.drawRect(0, 0, px, py, backgroundPaint);
canvas.save();
canvas.rotate(90, px/2, py/2);
// Draw up arrow
canvas.drawLine(px / 2, 0, 0, py / 2, linePaint);
canvas.drawLine(px / 2, 0, px, py / 2, linePaint);
canvas.drawLine(px / 2, 0, px / 2, py, linePaint);
canvas.restore();
// Draw circle
canvas.drawCircle(px - 10, py - 10, 10, linePaint);
图的圆圈位置的明显差异。不进行Canvas的save和restore操作的话,所有的图像都是在画布旋转90°后的画布上绘制的。当执行完onDraw方法,系统自动将画布恢复回来。save和restore操作执行的时机不同,就能造成绘制的图形不同。

论坛徽章:
0
42 [报告]
发表于 2011-05-18 23:23 |只看该作者
\其他
论坛,其实对于开发和学习都是很重要的资源。毕竟,所有的资料都是死的,只有人是活得,能够最大限度的因地制宜解决问题。
只不过,标准的官方论坛,放在Google Group上,已经惆怅的被盾了。中文论坛方面,没有特别优秀和活跃的,这一方面是由于Android的发展现状还不算很磅礴,另一方面是由于Android的开发相对于Symbian而言,奇技淫巧少了很多,没有那么多好问的。也许你可以去去csdn这样的传统论坛,或者eoe这样专门的论坛。有的时候,还是多少能获得一些帮助的。
书籍方面,真没有什么推荐,豆瓣上搜索一下,你可以看到,目前的书籍,基本上还是集中在SDK使用层面上,很少有解析的很透彻,做的很深入的。而SDK的使用,看SDK的文档就足够了,如果实在对e文不感冒,买一两本评价不太差的中文书籍,放着翻翻也还是挺好。
更进一步,也许可以读读一些经验性的文档,去Google Code上搜索一些代码回来看看。比如,SDK文档中,有个经验性文档的集合:resources/articles/index.html,就可以翻看一下。

最后,更多的一切还需要自己在工程和思考中,慢慢总结。相信,好的代码,会垂青一个勤于动手和思考的人。

Android组件
横看成岭侧成峰,远近高低各不同。 -- 《题西林壁》
组件(Component),在谈及所谓架构和重用的时候,是一个重要的事情。很多时候都会说基于组件的软件架构,指的是期望把程序做乐高似的,有一堆接口标准封装完整的组件放在哪里,想用的时候取上几个一搭配,整个程序就构建完成了。
在开篇的时候就在说,Android是一个为组件化而搭建的平台,它引入所谓Mash-Up的概念,这使得你在应用的最上层,想做的不组件化都是很困难的一件事情(底层逻辑,好吧,管不了...)。具体说来,Android有四大组件四喜丸子:Activity、Service、Broadcast Receiver、Content Provider。

Activity
做一个完整的Android程序,不想用到Activity,真的是比较困难的一件事情,除非是想做绿叶想疯了。因为Activity是Android程序与用户交互的窗口,在我看来,从这个层面的视角来看,Android的Activity特像网站的页面。
首先,一个网站,如果一张页面都没有,那...,真是一颗奇葩。而一张页面往往都有个独立的主题和功能点,比如登录页面,注册页面,管理页面,如是。
在每个页面里面,会放一些链接,已实现功能点的串联,有的链接点了,刷,跑到同一站点的另一个页面去了;有的链接点了,啾,可能跳到其他网站的页面去;还有的链接点了,恩...,这次没跑,但当前页面的样子可能有所变化了。这些模式,和Activity给人的感觉很像,只不过实现策略不同罢了,毕竟Android这套架构的核心思想,本身就来自源于Web的Mash-Up概念,视为页面的客户端化,也未尝不可。
Activity,在四大组件中,无疑是最复杂的,这年头,一样东西和界面挂上了勾,都简化不了,想一想,独立做一个应用有多少时间沦落在了界面上,就能琢磨清楚了。从视觉效果来看,一个Activity占据当前的窗口,响应所有窗口事件,具备有控件,菜单等界面元素。从内部逻辑来看,Activity需要为了保持各个界面状态,需要做很多持久化的事情,还需要妥善管理生命周期,和一些转跳逻辑。对于开发者而言,就需要派生一个Activity的子类,然后埋头苦干上述事情。对于Activity的更多细节,先可以参见:reference/android/app/Activity.html。后续,会献上更为详尽的剖析。

Service
服务,从最直白的视角来看,就是剥离了界面的Activity,它们在很多Android的概念方面比较接近,都是封装有一个完整的功能逻辑实现,只不过Service不抛头露脸,只是默默无声的做坚实的后盾。
但其实,换个角度来看,Android中的服务,和我们通常说的Windows服务,Web的后台服务又有一些相近,它们通常都是后台长时间运行,接受上层指令,完成相关事务的模块。用运行模式来看,Activity是跳,从一个跳到一个,呃...,这有点像模态对话框(或者还像web页面好了...),给一个输入(抑或没有...),然后不管不顾的让它运行,离开时返回输出(同抑或没有...)。
而Service不是,它是等,等着上层连接上它,然后产生一段持久而缠绵的通信,这就像一个用了Ajax页面,看着没啥变化,偷偷摸摸的和Service不知眉来眼去多少回了。
但和一般的Service还是有所不同,Android的Service和所有四大组件一样,其进程模型都是可以配置的,调用方和发布方都可以有权利来选择是把这个组件运行在同一个进程下,还是不同的进程下。这句话,可以拿把指甲刀刻进脑海中去,它凸显了Android的运行特征。如果一个Service,是有期望运行在于调用方不同进程的时候,就需要利用Android提供的RPC机制,为其部署一套进程间通信的策略。


Android的RPC实现,如上图所示(好吧,也是从SDK中拿来主义的...),无甚稀奇,基于代理模式的一个实现,在调用端和服务端都去生成一个代理类,做一些序列化和反序列化的事情,使得调用端和服务器端都可以像调用一个本地接口一样使用RPC接口。
Android中用来做数据序列化的类是Parcel,参见:/reference/android/os/Parcel.html,封装了序列化的细节,向外提供了足够对象化的访问接口,Android号称实现非常高效。
还有就是AIDL (Android Interface Definition Language) ,一种接口定义的语言,服务的RPC接口,可以用AIDL来描述,这样,ADT就可以帮助你自动生成一整套的代理模式需要用到的类,都是想起来很乏力写起来很苦力的那种。更多内容,可以再看看:guide/developing/tools/aidl.html,如果有兴致,可以找些其他PRC实现的资料lou几眼。
关于Service的实现,还强推参看API Demos这个Sample里面的RemoteService实现。它完整的展示了实现一个Service需要做的事情:那就是定义好需要接受的Intent,提供同步或异步的接口,在上层绑定了它后,通过这些接口(很多时候都是RPC的...)进行通信。在RPC接口中使用的数据、回调接口对象,如果不是标准的系统实现(系统可序列化的),则需要自定义aidl,所有一切,在这个Sample里都有表达,强荐。
Service从实现角度看,最特别的就是这些RPC的实现了,其他内容,都会接近于Activity的一些实现,也许不再会详述了。

Broadcast Receiver
在实际应用中,我们常需要等,等待系统抑或其他应用发出一道指令,为自己的应用擦亮明灯指明方向。而这种等待,在很多的平台上,都会需要付出不小的代价。
比如,在Symbian中,你要等待一个来电消息,显示归属地之类的,必须让自己的应用忍辱负重偷偷摸摸的开机启动,消隐图标隐藏任务项,潜伏在后台,监控着相关事件,等待转瞬即逝的出手机会。这是一件很发指的事情,不但白白耗费了系统资源,还留了个流氓软件的骂名,这真是卖力不讨好的正面典型。
在Android中,充分考虑了广泛的这类需求,于是就有了Broadcast Receiver这样的一个组件。每个Broadcast Receiver都可以接收一种或若干种Intent作为触发事件(有不知道Intent的么,后面会知道了...),当发生这样事件的时候,系统会负责唤醒或传递消息到该Broadcast Receiver,任其处置。在此之前和这以后,Broadcast Receiver是否在运行都变得不重要了,及其绿色环保。
这个实现机制,显然是基于一种注册方式的,Broadcast Receiver将其特征描述并注册在系统中,根据注册时机,可以分为两类,被我冠名为冷热插拔。所谓冷插拔,就是Broadcast Receiver的相关信息写在配置文件中(求配置文件详情?稍安,后续奉上...),系统会负责在相关事件发生的时候及时通知到该Broadcast Receiver,这种模式适合于这样的场景。某事件方式 -> 通知Broadcast -> 启动相关处理应用。比如,监听来电、邮件、短信之类的,都隶属于这种模式。而热插拔,顾名思义,插拔这样的事情,都是由应用自己来处理的,通常是在OnResume事件中通过registerReceiver进行注册,在OnPause等事件中反注册,通过这种方式使其能够在运行期间保持对相关事件的关注。比如,一款优秀的词典软件(比如,有道词典...),可能会有在运行期间关注网络状况变化的需求,使其可以在有廉价网络的时候优先使用网络查询词汇,在其他情况下,首先通过本地词库来查词,从而兼顾腰包和体验,一举两得一石二鸟一箭双雕(注,真实在有道词典中有这样的能力,但不是通过Broadcast Receiver实现的,仅以为例...)。而这样的监听,只需要在其工作状态下保持就好,不运行的时候,管你是天大的网路变化,与我何干。其模式可以归结为:启动应用 -> 监听事件 -> 发生时进行处理。
除了接受消息的一方有多种模式,发送者也有很重要的选择权。通常,发送这有两类,一个就是系统本身,我们称之为系统Broadcast消息,在reference/android/content/Intent.html的Standard Broadcast Actions,可以求到相关消息的详情。除了系统,自定义的应用可以放出Broadcast消息,通过的接口可以是Context.sendBroadcast,抑或是Context.sendOrderedBroadcast。前者发出的称为Normal broadcast,所有关注该消息的Receiver,都有机会获得并进行处理;后者放出的称作Ordered broadcasts,顾名思义,接受者需要按资排辈,排在后面的只能吃前面吃剩下的,前面的心情不好私吞了,后面的只能喝西北风了。
当Broadcast Receiver接收到相关的消息,它们通常做一些简单的处理,然后转化称为一条Notification,一次振铃,一次震动,抑或是启动一个Activity进行进一步的交互和处理。所以,虽然Broadcast整个逻辑不复杂,却是足够有用和好用,它统一了Android的事件广播模型,让很多平台都相形见绌了。更多Broadcast Receiver相关内容,参见:/reference/android/content/BroadcastReceiver.html。

Content Provider
Content Provider,听着就和数据相关,没错,这就是Android提供的第三方应用数据的访问方案。在Android中,对数据的保护是很严密的,除了放在SD卡中的数据,一个应用所持有的数据库、文件、等等内容,都是不允许其他直接访问的,但有时候,沟通是必要的,不仅对第三方很重要,对应用自己也很重要。
比如,一个联系人管理的应用。如果不允许第三方的应用对其联系人数据库进行增删该查,整个应用就失去了可扩展力,必将被其他应用抛弃,然后另立门户,自个玩自个的去了。
Andorid当然不会真的把每个应用都做成一座孤岛,它为所有应用都准备了一扇窗,这就是Content Provider。应用想对外提供的数据,可以通过派生ContentProvider类,封装成一枚Content Provider,每个Content Provider都用一个uri作为独立的标识,形如:content://com.xxxxx。所有东西看着像REST的样子,但实际上,它比REST更为灵活。和REST类似,uri也可以有两种类型,一种是带id的,另一种是列表的,但实现者不需要按照这个模式来做,给你id的uri你也可以返回列表类型的数据,只要调用者明白,就无妨,不用苛求所谓的REST。
另外,Content Provider不和REST一样只有uri可用,还可以接受Projection,Selection,OrderBy等参数,这样,就可以像数据库那样进行投影,选择和排序。查询到的结果,以Cursor(参见:reference/android/database/Cursor.html )的形式进行返回,调用者可以移动Cursor来访问各列的数据。
Content Provider屏蔽了内部数据的存储细节,向外提供了上述统一的接口模型,这样的抽象层次,大大简化了上层应用的书写,也对数据的整合提供了更方便的途径。Content Provider内部,常用数据库来实现,Android提供了强大的Sqlite支持,但很多时候,你也可以封装文件或其他混合的数据。
在Android中,ContentResolver是用来发起Content Provider的定位和访问的。不过它仅提供了同步访问的Content Provider的接口。但通常,Content Provider需要访问的可能是数据库等大数据源,效率上不足够快,会导致调用线程的拥塞。因此Android提供了一个AsyncQueryHandler(参见:reference/android/content/AsyncQueryHandler.html),帮助进行异步访问Content Provider。
在各大组件中,Service和Content Provider都是那种需要持续访问的。Service如果是一个耗时的场景,往往会提供异步访问的接口,而Content Provider不论效率如何,都提供的是约定的同步访问接口。我想这遵循的就是场景导向设计的原则,因为Content Provider仅是提供数据访问的,它不能确信具体的使用场景如何,会怎样使用它的数据;而相比之下,Service包含的逻辑更复杂更完整,可以抉择大部分时候使用某接口的场景,从而确定最贴切的接口是同步还是异步,简化了上层调用的逻辑。

配置
四大组件说完了,四大组件幕后的英雄也该出场了,那就是每个应用都会有一份的配置文件,名称是AndroidManifest.xml,在工程的根目录下。在这个配置文件中,不仅会描述一些应用相关的信息,很重要的,会包含一个应用中所有组件的信息。如果你派生Activity或者Service实现了一个相关的类,这只是把它组件化的第一步,你需要把这个类的相关信息写到配置文件中,它才会作为一个组件被应用到,否则只能默默无闻的黯淡度过余生。



摆了一幅图出来,这次不是偷来的,是敝帚自珍原创,所以没有意外的画的很丑,但基本还是可以体现出一些意思。在In Others的部分,这里是一般平台应用之间通信和交互的模型,每个应用都有很强烈的应用边界(往往表现为进程边界...),App 1的还是App 2的,分得很是清楚。每个应用内部,都有自己的逻辑去切分功能组件,这样的切分通常没有什么标准,率性而为。应用间的交互逻辑也比较零散,App 1与App 2交互,往往需要明确知道对方应用的具体信息,比如进程ID,进程名称之类的,这样使得应用和应用之间的联系,变得很生硬。而上层应用和系统应用的通信,往往有很多特定的模式,这种模式,很可能是无法直接应用在普通应用之间的,换而言之,系统应用是有一定特殊性的。
重点,在图的下半部,描述的是Android的应用情形。在Android中,应用的边界,在组件这个层面,是极度模糊,什么进程、什么应用,都可以不必感知到。举个例子,App 1,实现了A和B两个组件,App 2,实现了C这个组件。A和C,都想使用B这个组件,那么它们的使用方式是完全一致的,都需要通过系统核心的组件识别和通信机制,找到和使用组件B。A,虽说和B是一个娘胎里蹦出来的,很不好意思,没有任何特殊的后面和捷径,还是要跑规矩的途径才能用到,一片和谐社会的景象油然而生。
在Android中,所有组件的识别和消息传递逻辑都必须依赖底层核心来进行(通信可以没有底层核心的参与,比如一旦Service找到了,就可以和它产生持久的通信...),没有底层核心的牵线搭桥,任何两个组件都无法产生联系。比如一个Activity,跳到另一个Activity,必须要向底层核心发起一个Intent,有底层解析并认可后,会找到另一个Activity,把相关消息和数据传给它。一个Activity想使用Content Provider中的数据,必须通过底层核心解析相关的uri,定位到这个Content Provider,把参数传递给它,然后返回Activity需要的Cursor。这样的设计,保证了底层核心对所有组件的绝对掌控权和认知权,使得搭积木似的开发变成可能。
为了,使得核心系统能够完整的掌握每个组件的信息,这就需要配置文件了。配置文件,就是将组件插到底层核心上的这个插头。只有通过这个插头插在底层核心的插座上(不要乱想,非十八禁...),组件才能够发光发热,闪耀光芒。
组件的配置信息在我看来主要包含两个方面,一部分是描述如何认知。比如,Activity、Service、Broadcast Receiver都会有名字信息,和希望能够把握的Intent信息(姑且看成消息好了...),Content Provider会有一个描述其身份的uri。当其他组件通过这样的名字或者Intent,就可以找到它。
另一部分是运行相关的信息。这个组件,期望怎么来运行,放在单独的进程,还是和调用者一个进程,还是找相关的其他组件挤在同一个进程里面,这些内容,都可以在配置的时候来决定(调用者在这个约束范围内,有进一步的选择权...)。更多配置项,请参见:guide/topics/manifest/manifest-intro.html。

通过前续内容,也许可以帮助大家对Android组件有个初略的了解。但这些了解都还停留在静态层面,程序是个动态的概念,关于各个组件具体是怎么联系在一起的,如何手拉手运行起来完成一项功能的,这便是后话了。

论坛徽章:
0
43 [报告]
发表于 2011-05-18 23:25 |只看该作者
组件生命周期
任何架构上的变化,都会引起上层开发模式的变化,Android的进程模型,虽然使开发者不再需要密切关注进程的创建和销毁的时机,但仍然需要关注这些时间点对组件的影响。比如,你可能需要在进程销毁之前,将写到内存上的内容,持久化到硬盘上,这就需要关注进程退出前发生的一些事件。
在Android中,把握这些时间点,就必须了解组件生命周期(Components Lifecycles)。所谓组件的生命在周期,就是在组件在前后台切换、被用户创建退出、被系统回收等等事件发生的时候,会有一些事件通知到对应组件上,开发人员可以选择性的处理这些事件在对应的时间点上来完成一些附加工作。
除Content Provider,其他组件都会有生命周期的概念,都需要依照这个模型定时定点处理一些状况,全部内容参见:guide/topics/fundamentals.html#lcycles。在这里,擒贼先擒王,还是拿Activity出来作楷模。



继续偷图,来自SDK。一个自然的Activity生命旅途,从onCreate开始,到onDestroy消亡。但月有阴晴圆缺组件有祸福旦夕,在系统需要的时候且组件位于后台时,所在的进程随时可能为国捐躯被回收,这就使得知道切入后台这个事情也变得很重要。
当组件进入栈顶,与用户开始交互,会调用onResume函数,类似,当退出栈顶,会有onPause函数被呼唤。onResume和onPause可以处理很多事情,最常规的,就是做一些文件或设置项的读写工作。因为,在该组件不再前台运行的时候,可能别的组件会需要读写同样一份文件和设置,如果不再onResume做刷新工作,用的可能就是一份脏数据了(当然,具体情况,还需要具体分析,如果文件不会被多头读写,可以放到onCreate里面去做读工作)。
除了前述切入后台会被其他组件骚扰的问题,另外,死无定因也是件很可怕的事情。在Android中,组件都有两种常见的死法,一种是自然消亡,比如,栈元素ABC,变成AB了,C组件就自然消亡了。这种死发轻如鸿毛,不需要额外关心。但另一种情况,就是被系统回收,那是死的重如泰山,为国捐躯嘛。
但这种捐躯的死法,对用户来说,比较费解。想象一下,一款游戏,不能存盘,你一直玩啊玩,三天三夜没合眼,这时候你mm打来电话鼓励一下,你精神抖擞的准备再接再厉,却发现你的游戏进程,在切入后台之后,被系统回收了,一夜回到解放前三天努力成为一场泡影,你会不会想杀做游戏的人,会不会会不会会不会,一定会嘛。这时候,如果没有Activity生命周期这码事,游戏程序员一定是被冤死的,成了Android的替罪羊。但是,Android的组件是有生命周期的,如果真的发生这样情况,不要犹豫,去杀开发的程序员吧。
为了逃生,程序员们有一块免死金牌,那就是Android的state机制。所谓state,就是开发人员将一些当前运行的状态信息存放在一个Bundle对象里面,这是一个可序列化键值对集合。如果该Activity组件所处的进程需要回收,Android核心会将其上Activity组件的Bundle对象持久化到磁盘上,当用户回到该Activity时候,系统会重新构造该组件,并将持久化到磁盘上的Bundle对象恢复。有了这样的持久化的状态信息,开发人员可以很好的区分具体死法,并有机会的使得死而复生的Activity恢复到死前状态。开发者应该做的,是通过onSaveInstanceState函数把需要维系的状态信息(在默认的状态下,系统控件都会自己保存相关的状态信息,比如TextView,会保存当前的Text信息,这都不需要开发人员担心...),写入到Bundle对象,然后在onRestoreInstanceState函数中读取并恢复相关信息(onCreate,onStart,也都可以处理...)。


线程
读取数据,后台处理,这些猥琐的伙计,自然少不了线程的参与。在Android核心的调度层面,是不屑于考量线程的,它关注的只有进程,每一个组件的构造和处理,都是在进程的主线程上做的,这样可以保证逻辑的足够简单。多线程,往往都是开发人员需要做的。
Android的线程,也是通过派生Java的Thread对象,实现Run方法来实现的。但当用户需要跑一个具有消息循环的线程的时候,Android有更好的支持,来自于Handler和Looper。Handler做的是消息的传送和分发,派生其handleMessage函数,可以处理各种收到的消息,和win开发无异。Looper的任务,则是构造循环,等候退出或其他消息的来临。在Looper的SDK页面,有一个消息循环线程实现的标准范例,当然,更为标准的方式也许是构造一个HandlerThread线程,将它的Looper传递给Handler。
在Android中,Content Provider的使用,往往和线程挂钩,谁让它和数据相关呢。在前面提到过,Content Provider为了保持更多的灵活性,本身只提供了同步调用的接口,而由于异步对Content Provider进行增删改查是一个常做操作,Android通过AsyncQueryHandler对象,提供了异步接口。这是一个Handler的子类,开发人员可以调用startXXX方法发起操作,通过派生onXXXComplete方法,等待执行完毕后的回调,从而完成整个异步调用的流程,十分的简约明了。
实现
整个任务、进程管理的核心实现,尽在ActivityManagerService中。上一篇说到,Intent解析,就是这个ActivityManagerService来负责的,其实,它是一个很名不副实的类,因为虽然名为Activity的Manager Service,但它管辖的范围,不只是Activity,还有其他三类组件,和它们所在的进程。
在ActivityManagerService中,有两类数据结构最为醒目,一个是ArrayList,另一个是HashMap。ActivityManagerService有大量的ArrayList,每一个组件,会有多个ArrayList来分状态存放。调度工作,往往就是从一个ArrayList里面拿出来,找个方法调一调,然后扔到另一个ArrayList里面去,当这个组件没对应的ArrayList放着的时候,说明它离死不远了。HashMap,是因为有组件是需要用名字或Intent信息做定位的,比如Content Provider,它的查找,都是依据Uri,有了HashMap,一切都顺理成章了。
ActivityManagerService用一些名曰xxxRecord的数据结构,来表达各个存活的组件。于是就有了,HistoryRecord(保存Activity信息的,之所以叫History,是相对Task栈而言的...),ServiceRecord,BroadcastRecord,ContentProviderRecord,TaskRecord,ProcessRecord,等等。
值得注意的,是TaskRecord,我们一直再说,Task栈这样的概念,其实,真实的底层,并不会在TaskRecord中,维系一个Activity的栈。在ActivityManagerService中,各个任务的Activity,都以HistoryRecord的形式,集中存放在一个ArrayList中,每个HistoryRecord,会存放它所在TaskRecord的引用。当有一个Activity,执行完成,从概念上的Task栈中退出,Android是通过从当前HistoryRecord位置往前扫描同一个TaskRecord的HistoryRecord来完成的。这个设计,使得上层很多看上去逻辑很复杂的Task体系,在实现变得很统一而简明,值得称道。
ProcessRecord,是整个进程托管实现的核心,它存放有运行在这个进程上,所有组件的信息,根据这些信息,系统有一整套的算法来决议如何处置这个进程,如果对回收算法感兴趣,可以从ActivityManagerService的trimApplications函数入手来看。
对于开发者来说,去了解这部分实现,主要是可以帮助理解整个进程和任务的概念,如果觉得这块理解的清晰了,就不用去碰ActivityManagerService这个庞然大物了。

界面构造
UI界面,对于每个应用而言,是它与用户进行交互的门脸。好的门脸,不只是是要亮丽可人,最好还能秀色可餐过目不忘,甚至还应该有涵养有气质,彬彬有理温柔耐心。
对于开发者来说,锻造这样的面容,不但需要高超的技艺,也需要有称手的工具和对得起党的料子。俗话说,朽木不可雕也,芙蓉不是一日炼成的,不是什么平台都能叫特能书。有套好用的UI框架,对于开发者而言,真有如沙漠中的甘露,而要是撞见了杯具的UI套件,整个界面开发就有如梦魇了。
Android的UI框架,最核心的,是资源和Layout体系,然后,通过完善的控件库,简明的接口设计,进一步帮助开发者,能够最快的搭建自己需要界面(听到这里,Symbian同学开始钻土...)。
UI控件
做UI,有时候就像搭积木,在Android中,这个最原子的积木块,就是View。所有其他的UI元素,都是派生于此类的子孙类们。

又从SDK中偷来张图,用来描述Android的UI控件结构,在每一个window下,这都是一个标准而完整的树结构。View有一个子类ViewGroup,它相当于一个容器类或者是复合控件,所有派生与ViewGroup的子类在这颗UI树中都可以承担着父节点的职责,而另一些绕过ViewGroup从View直通下来的,就只能蜷局在叶节点的范畴内了。
之所有说这是一个很标准的控件树,是因为父控件对子控件有绝对的掌控权,每个子控件的占地面积和位置,都是基于父控件来分配的,它能够接受和处理的事件,也是父控件派发下去的。这样的结构,被很多平台和框架广泛的认可,和传统的win开发和杯具的Symbian相比,虽然因为事件传播途径变长了,很多操作的效率变低了,但整个结构更有层次性,每个控件只需要多其父控件负责指挥子控件就好,职责明确,逻辑简单,利于开发和设计。
谈及任何平台的控件,都有一些不可避免的主题,比如,每个控件如何标识,如何设定大小和位置,如何接受和处理事件,如何绘制,诸如此类。
标识
在Android中,你可以为每个控件选择设定一个id,这个id的全局的唯一性不需要保证,但在某个局部的范围内具有可识别性,这样就可以通过这个id找到这个控件(如果不需要查找,就别设置了...)。
但是,在父控件中逐级的find比较,找到id匹配的控件,然后再做转型,是一个比较重量的操作,于是Android又为控件憋出另一个属性,tag。它接受任意object类型的数据,你可以把和这个控件对象相关的内容堆在里面。比如,在list中,我们常常将和每个list item相关的所有控件元素封装成一个object,扔到tag中,就不需要每次都去比较id进行寻找,更加高效快捷。
尺寸
在Android中,控件最重要的大小属性,就是width/height,开发者可以明确的指明控件的大小,可以设定成为fill_parent和wrap_content,这样的概念性的大小。丈量并设定控件的位置,是通过两步来进行的。
第一步是measure。它传入此控件的width/height信息,控件会根据自己的参数,计算出真实需要的width/height,然后调用setMeasuredDimension方法,缓存成成员变量,留作后用。
在计算出大小之后,会进行另一个步骤,layout。在这个过程中,父控件会计算其上各个子控件的位置,从而完成整个大小和位置的确定流程。整个measure和layout的流程,都是自上到下,从树顶往叶子来推进的。
当开发人员需要自定义控件的时候,可能需要关注这些内容,通过重载onMeasure和onLayout方法,可以定义自己控件的丈量方式。
事件
在Android中,所有的按键,触屏等事件,都是从顶至下进行分发的。每个ViewGroup的对象,会维系一个focused变量,它表示在这个父控件中具备focus的控件,当有按键时间发生的时候,会找到这个focused子控件,并传递给它。同理,触屏事件的分发也是类似,只不过和focus无关,父控件会遍历所有子控件,看看谁处于触碰位置,从而传递给谁。
另外还有一些事件,逻辑上并不是从顶至下发起的。比如,当你修改某个子控件的内容,使得该子控件的大小和内容都发生了变化,就需要进行控件的重排和重绘,这些操作不仅是子控件自己的事情,需要整个控件树上的所有控件都需要配合。在Android中,处理这类事情的实现策略是子控件维系一个ViewParent对象,该对象象征着整个控件树的管理者,子控件产生影响整个控件树的事件时,会通知到ViewParent,ViewParent会将其转换成一个自顶向下的事件,分发下去。
Android的事件处理逻辑,采用的是观察者模式。Android的控件提供了一些列的add/set Listener的接口,使得外部观察者,有机会处理控件事件。比如,你需要在某个button被点击时做一些事情,你就需要派生一个View.OnClickListener对象作为观察者,调用该控件的setOnClickListener接口注册进去,当button被点击,就可以获得处理点击事件的机会了。当然,有的时候,你需要处理的逻辑更为复杂,光是站在外面围观叫好不能解决问题,可能就需要派生某个控件,去重载onXXXX之类的事件处理函数,进行更完整的控制。
焦点
对于一个非触屏的机器,焦点的维系是一个极其重要的事情,而在有触屏的年代,焦点的地位虽有所下降,但依然还是需要妥善保护的。
Android中,是以控件树为单位,来管理焦点的。每个控件,可以设置上下左右四向的focus转移对象。当在一个控件上发生焦点转移事件,Android会如前述,自顶向下根据设定好的焦点转移逻辑,跳转到正确的控件上。和Symbian相比,真是,真是。。。
Layout
Layout是一类特殊的ViewGroup控件,它们本身没有任何可显示内容,形如透明的玻璃盒子,存活的唯一理由,就是其中的内部结构,能够更好的摆放它的子控件们。
比如线性的Layout,LinearLayout。放入这个Layout的子控件,会按水平或垂直方向,排排坐,一个挨着一个按顺序排列下去。TableLayout,可以将子控件按照表格的形式,一枚枚放置好。而RelativeLayout则更灵活,可以设定各个控件之间的对齐和排列关系,适合定制复杂的界面。
有了Layout的存在,控件和控件之间不再割裂的存在,而是更有机的结合在了一起,设定起来也更为方便。比Symbian那样人肉维系各个控件的关系,轻松自在多了。
更多
这些问题的完整答案,参见SDK中View的页面:/reference/android/view/View.html。

论坛徽章:
0
44 [报告]
发表于 2011-05-19 10:32 |只看该作者
回复 3# clzhana


    加注释的艺术在于对存在歧义或者晦涩难懂的代码进行适当的说明,以澄清代码读者的迷惑。当然,如果注释写的诙谐幽默,那就更艺术了。

论坛徽章:
0
45 [报告]
发表于 2011-05-20 00:14 |只看该作者
人机界面的发展
人机交互界面研究经历了两个界限分明的时代,第一代是以文本为基础的交互, 如菜单、命令、对话等,难用且不灵活。第二代则是直接操作界面,它引出更自然的视觉通信交互。而下一代则是交互多媒体集成方法,需要大量使用语言、自然语言和高级图形,也可使用其它交互媒体,如人的动作、手势和三维图像等。而人机交互界面的研究已超越心理学,并进入到社会学的研究,界面技术与多媒体技术,通信技术,特别是与人工智能技术愈来愈密不可分。下面就单个方面进行一下介绍:
人机界面发展概述
1.人机界面领域的形成
  从计算机问世以来,早期用户是以计算机专业人员为主,但随着计算机广泛进入人们的工作生活领域,计算机用户发生了改变,非计算机专业的普通用户成了用户的主体。这一重大转变使计算机的可用性问题变得日益突出起来。人机界面应当是什么样的?如何去建造这样的界面?人们开始关注和研究这些问题。这些问题既涉及人也涉及计算机及一些相关的学科,如:心理学、人的因素学(Human Factors)、社会学、语言学等。随着计算机技术的发展,应用领域的拓宽,从而带来了不同的理论方法。八十年代以来,人机界面的研究有了前所未有的发展,微型计算机的迅速普及对此起了重要的推动作用。
2.研究人机界面的各种理论和方法
(1)分析与评价技术:
  用于分析、评价用户界面有效性的理论和经验方法,如任务分析、话语分析、内容分析及可用性评价等。
(2)设计方法论:
  用来产生好的用户界面设计的方法与技术,如:软件心理学、环境因素设计法、多方参与设计法以及支持设计过程的工具和标记法。
(3)开发工具和方法:
  支持用户界面开发的工具箱、用户界面管理系统(UIMS)、快速原型法和程序设计辅助工具等。
(4)交互方式与设备:
  新的输入/输出设备和设备运用策略,包括视觉、声音、触觉、姿态等通信模态及多种模态的集成。
(5)关键用户界面成分:
  如用户界面隐喻(metaphor)、用户界面风格、智能界面技术、取消、超文本/超媒体以及联机帮助。
(6)用户模型:
  包括用户行为模型、关于系统的用户内心模型、用户个体差异等。
(7)特定应用的用户界面设计:  
  满足某类应用问题对人机交互作用的特定限制条件和要求的用户界面设计。如:虚拟现实、智能辅导系统、信息检索、Internet/WWW、CAD/CAM、专家系统过程控制、决策支持等。
(计算机辅助协同工作(CSCW):
  关于如何使用计算机系统帮助人的群体有效协同工作的研究,包括现场观察研究、理论模型、群体用户界面开发设计等。
(9)法律与标准:
  关于用户界面的专利和版权问题、用户界面的标准化。
  这些研究方向目前大多处于十分活跃的发展阶段,并且有着较强的分化和相互渗透倾向,有些方向甚至有可能发展为具有相当规模的相对对立的研究领域。
用户界面管理程序(User Interface Manager)
1.UIM的引出与形成
  用户界面管理程序是一种功能完备的软件构件,它可取代管理用户界面的一切功能。由于大多数应用软件用于交互系统,而且许多软件的工作量是为实现用户界面,所以重复编程浪费了时间和人力,由此引出了UIM。
2.UIM的特点
  可作为一种多方面适用的、可再用的界面模块负责所有界面呈现和对话管理。
3.UIM的功能
  是负责所有界面呈现和用户管理,下图描述了其模式:

  一个完整的UIM的方案是提供界面构件和管理工具,它们能帮助应
用程序员快速构造界面,并在原型开发周期中评估设计。
4.UIM的简单开发工具
  屏幕描绘仪,屏幕设计工具。
自适应人机界面
自适应人机界面是用户管理程序[UIM]的一个典型,是当今一个相当活跃的研究课题。自适应界面的提出是初学者与专家型用户相互冲突的要求,并且随着实践的加强,大多数初学者会逐渐变成专家。
(1)什么才是自适应界面?
  自适应界面的要求:
  I.能检测用户类型,并及时转换界面。
  II.能对用户需求变化作出响应。
  III.保证改变的数量和类型不会引起界面设计太不一致,即不违背界面设计一致性原则。
(2)自适应界面的难点:
  1.自适应到什么程度为好?
  2.怎样的自适应才能使不同用户能力都相适应?
智能界面管理系统
简介:
  智能界面是用户界面管理程序[UIM]的一个典型。它是一种功能完备的软件构件,可以取代管理用户界面的一切功能。只是不包括与作业处理相应的应用软件。
智能界面管理系统的设计目标:
  多方面适用且可重用的界面模块,既与用户联系,又与应用软件联系。
智能界面管理系统要求:
  要能适应于不同类型的应用软件,也要能无须做实质性的修改即可在不同系统环境间移植。
智能管理系统的研究:
  集中于如何从界面中分类除应用软件以及提供界面构件和管理工具,从而能帮助应用程序人员快速构造界面,并在原型开发中就能评估界面设计。
对话形式规范化说明
对话的形式规范说明的必要性:
  I.用形式化体系设计的界面,可增强可靠性。
  II.可用软件模块构造界面,实现可重用性。
已存在的几种形式规范描述:
  1.用Z语言对界面行为进行行为描述(Sufran,1986)。
  2.用路径代数(Alty,1984)描述对话序列。
  3.代数形式化体系描述系统的交互集(Dix和Runciman,1985)。
  形式化技术的不同点在于它们的表达能力和灵活性,但总的目的是向人们演示,对话行为可作有限的描述。例如:“所见即所得”(What You See Is What You Get)原则,按生成型工程原理GUEPS可细化为因果语句和状态约束集描述界面怎样正常工作,怎样不能正常工作。形式规范化面临着许多问题,关键在于对什么内容形式,但其仍在发展。
形式规范的发展方向:
  是把软件工程原则和认知问题用于交互,并推导出一系列用于设计的上下文无关的原则,实现界面工程化设计。
人机界面设计方法的改进
  人机界面设计是系统设计过程的一部分,所以必须结合到现代系统开发方法中去。目前的系统开发方法对界面设计问题和用户关注太少或更本没加注意,以致用户批评仍持续不断。界面设计的共同课题是让用户关心和介入。其目的在于促进人在系统开发中的参与与作用。
以用户为中心的设计方法:
  1.用户参与设计——用户应当积极主动加入设计过程,并进入设计组共同进行决策。
  2.以用户为中心的设计——系统设计必须根据用户的需要来确定,而不能由功能过程需求或硬件限制等来推动。
  3.迭代设计(原型设计)——人机交互文献特别强调,在设计期间必须注意原型及其细化周期的概念。
人机界面的评估也是重要的研究课题方法:
(1)诊断分析——确定界面设计的不良特性;
(2)监视——误差率、命令使用频率和使用持续时间;
(3)实验分析——收集评估数据。
人机界面的总结
  随着人机界面在计算机容量、网络技术、图形技术、多媒体技术以及新型输入输出设备方面的迅速发展,今后的人机界面将具有一些新的特点:
  (1)人与计算机的交互操作变得更接近于同现实世界的交互操作,为减轻人在交互作用上的认识负担和更多地利用人从演化和经历中获得的自然技能提供了新的可能性。
  (2)人机界面将越来越多地具有多模态高宽带的特点。更多的人的感受表达模态将被用于和计算机的信息交流,甚至可以设想最重要把人的神经细胞的直接触发和测取作为交互的通道,为此同时输入输出设备之间的区别将变得越来越不明显。  
??(3)网络技术尤其是Internet的普及使今后的计算机用户具有更多的群体特点,从而对适应人的群体社会化组织和行为规律的群体用户界面将会有大的需求。
  人机界面设计下一代方法是交互的集成方法。它将大量地使用语音、自然语言和高级图形,也可用其它交互媒体,如眼的动作和手势、姿态等,还可用三维图像生动地引导解释交互和任务。
  总之,未来的人机界面设计对研究和设计者提出了多方面的挑战。许多信息技术领域的公司企业,这些年纷纷对与用户界面开发有关的专业人员提出要求,很多大学适应这种要求,设立了相关的课题甚至专业。

论坛徽章:
0
46 [报告]
发表于 2011-05-20 00:14 |只看该作者
浅谈人机界面设计
摘要:本文介绍了人机界面设计的一般过程,人机界面实现的原则,人机界面的风格和对人机界面设计的评价。
关键词:人机界面 菜单 风格 软件模型
  由于受传统观念的影响,很长一段时间里,人机界面一直不为软件开发人员所重视,认为这纯粹是为了取悦用户而进行的低级活动,没有任何实用价值。评价一个应用软件质量高低的唯一标准,就是看它是否具有强大的功能,能否顺利帮助用户完成他们的任务。近年来,随着计算机硬件技术的迅猛发展,计算机的存储容量、运行速度和可靠性等技术性能指标有了显著的提高,计算机硬件的生产成本却大幅度下跌,个人计算机日益普及。新一代的计算机用户,在应用软件的可操作性以及软件操作的舒适性等方面对应用软件提出了更高的要求除期望所用的软件拥有强大的功能外,更期望应用软件能尽可能的为他们提供一个轻松、愉快、感觉良好的操作环境。这表明,人机界面的质量已成为一个大问题,友好的人机界面设计已经成为应用软件开发的一个重要组成部分。
  1人机界面的风格分析
   这里所指的人机界面的风格,是指计算机系统的用户界面上控制输入的方法,大致经过了四代的演变:
   1.1命令语言:在图形显示、鼠标、高速工作站等技术出现之前,现实可行的界面方式只能是命令和询问方式,通信完全以正文形式并通过用户命令和用户对系统询问的响应来完成。这种方式使用灵活,便于用户发挥其创造性,对熟练的用户有很高的工作效率,但对一般用户来说要求高,易出错,不友善并难于学习,它的错误处理能力也较弱。
   1.2菜单选项:这种方式与命令行方式相比不易出错,可以大大缩短用户的培训时间,减少用户的击键次数,可以使用对话管理工具,错误处理能力有了显著提高。但使用起来仍然乏味,可能出现菜单层次过多及菜单选项复杂的情形,必须逐级进行选择,不能一步到位,导致交互速度显得太慢。
   1.3面向窗口的点选界面此类界面亦称WIMP界面,即窗口(Windows)、图标(Icons)、菜单(Menus)、指示器(PointingDevice)四位一体,形成桌面(Desktop)。这种方式能同时显示不同种类的信息,使用户可在几个工作环境中切换而不丢失几个工作之间的联系,用户可通过下拉式菜单方便执行控制型和对话型任务,引入图标、按钮和滚动杆技术,大大减少键盘输入,对不精于打字的用户无疑提高了交互效率。
   1.4自然语言使用自然语言与应用软件进行通信,把第三代界面技术与超文本、多任务概念结合起来,使用户可同时执行多个任务(以用户的观点)。
   随着文字、图形、语音的识别与输术技术的进一步发展,多媒体技术在人机界面开发领域内的进一步发展,自然语言风格的人机界面将得以迅速的发展,最终走向实用化。
  2人机界面的设计原则
   人机界面设计的好坏与设计者的经验有直接有关系,有些原则对几乎所有良好的人机界面的设计都是适用的,一般地可从可交互性、信息、显示、数据输入等方面考虑:
   原则1:在同一用户界面中,所有的菜单选择、命令输入、数据显示和其他功能应保持风格的一致性。风格一致的人机界面会给人一种简洁、和谐的美感。原则2:对所有可能造成损害的动作,坚持要求用户确认,例如提问“你肯定……?”等,对大多数动作应允许恢复(UNDO),对用户出错采取宽容的态度。原则3:用户界面应能对用户的决定做出及时的响应,提高对话、移动和思考的效率,最大可能的减少击键次数,缩短鼠标移动距离,避免使用户产生无所适从的感觉。原则4:人机界面应该提供上下文敏感的求助系统,让用户及时获得帮助,尽量用简短的动词和动词短语提示命令。原则5:合理划分并高效使用显示屏。仅显示与上下文有关的信息,允许用户对可视环境进行维护:如放大、缩小图像;用窗口分隔不同种类的信息,只显示有意义的出错信息,避免因数据过于费解造成用户烦恼。原则6:保证信息显示方式与数据输入方式的协调一致,尽量减少用户输入的动作,隐藏当前状态下不可选用的命令,允许用户自选输入方式,能够删除无现实意义的输入,允许用户控制交互过程。
   上述原则都是进行人机界面设计应遵循的最基本的原则,除此之外还有许多设计原则应当考虑,比如如何正确的使用颜色等。
  3人机界面设计的过程
   人机界面的设计过程可分为以下几个步骤:
   3.1创建系统功能的外部模型设计模型主要是考虑软件的数据结构、总体结构和过程性描述,界面设计一般只作为附属品,只有对用户的情况(包括年龄、性别、心理情况、文化程度、个性、种族背景等)有所了解,才能设计出有效的用户界面;根据终端用户对未来系统的假想(简称系统假想)设计用户模型,最终使之与系统实现后得到的系统映象(系统的外部特征)相吻合,用户才能对系统感到满意并能有效的使用它;建立用户模型时要充分考虑系统假想给出的信息,系统映象必须准确地反映系统的语法和语义信息。总之,只有了解用户、了解任务才能设计出好的人机界面。
   3.2确定为完成此系统功能人和计算机应分别完成的任务
   任务分析有两种途径。一种是从实际出发,通过对原有处于手工或半手工状态下的应用系统的剖析,将其映射为在人机界面上执行的一组类似的任务;另一种是通过研究系统的需求规格说明,导出一组与用户模型和系统假想相协调的用户任务。
   逐步求精和面向对象分析等技术同样适用于任务分析。逐步求精技术可把任务不断划分为子任务,直至对每个任务的要求都十分清楚;而采用面向对象分析技术可识别出与应用有关的所有客观的对象以及与对象关联的动作。
   3.3考虑界面设计中的典型问题
   设计任何一个机界面,一般必须考虑系统响应时间、用户求助机制、错误信息处理和命令方式四个方面。系统响应时间过长是交互式系统中用户抱怨最多的问题,除了响应时间的绝对长短外,用户对不同命令在响应时间上的差别亦很在意,若过于悬殊用户将难以接受;用户求助机制宜采用集成式,避免叠加式系统导致用户求助某项指南而不得不浏览大量无关信息;错误和警告信息必须选用用户明了、含义准确的术语描述,同时还应尽可能提供一些有关错误恢复的建议。此外,显示出错信息时,若再辅以听觉(铃声)、视觉(专用颜色)刺激,则效果更佳;命令方式最好是菜单与键盘命令并存,供用户选用。
   3.4借助CASE工具构造界面原型,并真正实现设计模型软件模型一旦确定,即可构造一个软件原形,此时仅有用户界面部分,此原形交用户评审,根据反馈意见修改后再交给用户评审,直至与用户模型和系统假想一致为止。一般可借助于用户界面工具箱(Userinterfacetoolkits)或用户界面开发系统(Userinterfacedevelopmentsystems)提供的现成的模块或对象创建各种界面基本成分的工作。
  4人机界面设计的评价
   怎样评价一个人机界面设计质量的优劣,目前还没有一个统一的标准。一般地,评价可以从以下几个主要方面进行考虑:(1)用户对人机界面的满意程度;(2)人机界面的标准化程度;(3)人机界面的适应性和协调性;(4)人机界面的应用条件;(5)人机界面的性能价格比。
   目前人们习惯于用“界面友好性”这一抽象概念来评价一个人机界面的好坏,但“但面友好”与“界面不友好”恐怕无人能定一个确切的界线,一般认为一个友好的人机界应该至少具备以下特征:(1)操作简单,易学,易掌握;(2)界面美观,操作舒适;(3)快速反应,响应合理;(4)用语通俗,语义一致。
   需指出,一个用户界面设计质量的优劣,最终还得由用户来判定,因为软件是供用户使用的,软件的使用者才是最有发言权的人。
  参考文献:
  (1)齐治昌,等.软件工程(M).北京:高等教育出版社,1998.
  (2)王旭,等.C语言实用界面技术(M).兰州:西北工业大学出版社,1997.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP