我一直以为,WPF是把Ui和逻辑分开了,现在GWT的UiBinder几乎也在往这个方向走。xml里面是.ui,.java里面是逻辑。但是其实这所 谓的逻辑也还有层次之分。
很多东西都是如此,就好像原子,之所以取这个名字是以为他不能再分,而后来又发现了质子,中子。细看HiChatUi里 面的代码,基本上有两种,一种是在更新Ui,一种是在调用异步通信。这应该是一个承上启下的类。
WPF为了实现Ui和逻辑的分离必须引入数据绑定 这么个东西。他们想让Ui和逻辑分离,因此就很讨厌看到inputText=“”类似的代码。因为他身在逻辑的地盘,干的却是Ui的活。但是现在Ui不是 一成不变的,不是一个静态的xaml能表示的了的。想想Ui怎么变呢,一般来讲Ui是数据的外衣,因此把Ui绑定到数据,让Ui随着数据变化,也就不需要 在非标记语言的地盘调用代码来修改控件的属性。这一部分被藏起来,被绑定机制做了。那万一这个Ui比较疯癫,他的变化不是那么随着数据的意愿呢?啊,这要 看你怎样定义数据了。在非标记语言的地盘上,数据和Ui数据还是可以划江而治的。有些代码,虽然不是用标记语言写的,但是它完全为Ui而存,其中每一个字 段都可以在Ui上找到相应的位置。由他再和真正的数据,逻辑交互,就显得比较,呃,有弹性。
所以现在看到HiChatUi这么长,我就心 里痒。其实从一开始我就觉得我会需要一个表示客户端的类,一切逻辑在这里面进行,而且里面还会保存有所有聊天记录。只不过本着一切从简的想法先苟且让程序 跑起来再说。不过该做的总归是要做的,看看现在的程序,所有的聊天记录甚至在本地都没有留底的,update得到的新的信息,然后直接加到Ui上面去。看 着没问题,跑着也没问题,多少让人不放心,以后很可能需要在本地维持消息的记录,到时候这份记录放到哪里呢,总不能还放到这个姓Ui的HiChatUi里 吧。再说,我也很不满与现在的状态机,呃,其实现在根本没有状态机,打开页面,就开始了,连没连上人,都可以说话。至少要有三个状态,等人,聊天,散。这 些都要放到HiChatUi里我就要疯了。因此我需要一个client类。
那么client应该是Ui的一部分呢,还是Ui是 client的一部分呢?client这个名字可能让他看起来更像ui的宿主,但是我们往往都说灵魂是装在躯壳里面。因此我还是比较倾向于把client 作为Ui的一个字段。这样Ui处理用户触发的事件就可以调用client的相应方法。但是异步更新自然是client类里面的事,与Ui无关,更新完了如 何修改Ui呢。我脑子里突然冒出了好多种想法。一认为灵魂和肉体是平等的,Ui和client类同在EntryPoint里面被创建,互相包含一个引用。 这样Ui响应到用户事件可以调用到client进行通信,Ui作为一个纯粹的肉体,可以为所有的字段提供get方法,而异步通信的结果也可以调用Ui提供 的get方法得到ui对象进行更新。二呢承认灵魂和肉体的平等,觉得互相包含的结构是可以接受的,但是互相之间完全的透明是不可以的。ui可以提供必要地 接口来更新ui,但是为所有ui元素提供get方法是不可以的。三认为Ui应该完全包含client,然而client里面进行的一切Ui都无从知 道,Ui只能一次一次的update,查看client的现状,而决定要不要修改Ui,要怎样修改。换言之冷漠的Ui丝毫不关心发生过什么,只关心现在怎 么样了。
首先从结构上看Ui和client平等论2:1击败了三,然后我想别人都说我二,我就选二吧。老实说我觉得三也有可取之处,也许更适合那 种变化多是更改而不是增加的一类应用,或者那种每一帧不问历史把屏幕清空重画的那种应用。
更进一步的,二认为提供哪些接口取决于有哪些更新需要修 改Ui,而不取决于Ui有哪些元素。也就是说Ui提供哪些接口不应该由他自己决定。嗯~,如果你想套住一个类,让他实现一个接口。后来发现,一个 EventHandler接口比较好。因为在异步调用的结果里调用一个复杂的同步调用我想着觉得不妥。万一这个调用花了特别长的时间不知道会不会使得两个 Ui的修改同时在两个线程被执行。而event这个东西据我所知一般是添加到一个序列里,然后一个一个拿出来分配。跟进去看了看看到了queue这个词, 应该不会是玩我的吧。
添加HiChangedEventHandler
void onMessageSent();
void onMessageUpdated();
void onStateChanged();
}
添 加HiChangedEvent
public static Type<HiChangedEventHandler> TYPE = new Type<HiChangedEventHandler>();
public enum EChangeType {
MessageSent,
MessageUpdated,
}
private EChangeType changeType;
public HiChangedEvent(EChangeType changeType) {
this.changeType = changeType;
}
@Override
protected void dispatch(HiChangedEventHandler handler) {
switch (changeType) {
case MessageSent:
handler.onMessageSent();
break;
case MessageUpdated:
handler.onMessageUpdated();
break;
}
}
@Override
public com.google.gwt.event.shared.GwtEvent.Type<HiChangedEventHandler> getAssociatedType() {
return TYPE;
}
}
然后修改 HiChatUi,使之实现HiChangedEventHandler接口,再新建HiChatClient,把原来的逻辑事务都转移到里面
private HandlerManager handlerManager;
private int clientId = -1;
public int getClientId() {
return clientId;
}
private HelloServiceAsync service = GWT.create(HelloService.class);
private int lastIndex = 0;
private static final int UPDATE_PERIOD = 500;
private Timer timer = new Timer() {
@Override
public void run() {
update();
}
};
private HiMessage[] newMessages;
public HiMessage[] getNewMessages() {
return newMessages;
}
public HiChatClient() {
timer.scheduleRepeating(UPDATE_PERIOD);//ms
}
public void setEventHandler(HiChangedEventHandler handler) {
handlerManager = new HandlerManager(this);
handlerManager.addHandler(HiChangedEvent.TYPE, handler);
}
private void initClientId() {
service.getClientId(new AsyncCallback<Integer>() {
@Override
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
@Override
public void onSuccess(Integer result) {
clientId = result;
}
});
}
private boolean isUpdating = false;
private void update() {
if (clientId == -1) {
initClientId();
} else {
if (!isUpdating) {
isUpdating = true;
service.updateMessage(clientId, lastIndex, new AsyncCallback<HiMessage[]>() {
@Override
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
@Override
public void onSuccess(HiMessage[] result) {
if (result != null && result.length != 0) {
newMessages = result;
lastIndex += result.length;
handlerManager.fireEvent(new HiChangedEvent(EChangeType.MessageUpdated));
}
isUpdating = false;
}
});
}
}
}
public void sendMessage(String input) {
if (!input.isEmpty()) {
service.sendMessage(clientId, input, new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
@Override
public void onSuccess(Void result) {
handlerManager.fireEvent(new HiChangedEvent(EChangeType.MessageSent));
update();
}
});
}
}
}
再修改原来的HiChatUi
implements HiChangedEventHandler {
private static HelloHiUiUiBinder uiBinder = GWT
.create(HelloHiUiUiBinder.class);
interface HelloHiUiUiBinder extends UiBinder<Widget, HiChatUi> {
}
private HiChatClient client;
@UiField
VerticalPanel contentPanel;
@UiField
TextArea inputText;
@UiField
ScrollPanel scrollPanel;
@UiField
Button startButton;
public HiChatUi() {
initWidget(uiBinder.createAndBindUi(this));
}
public void setClient(HiChatClient client) {
this.client = client;
}
@UiHandler("inputText")
void onKeyPress(KeyPressEvent event) {
if (event.getCharCode() == KeyCodes.KEY_ENTER) {
String input = inputText.getText();
if (!input.isEmpty()) {
client.sendMessage(inputText.getText());
}
}
}
@Override
public void onMessageSent() {
inputText.setText("");
}
@Override
public void onMessageUpdated() {
HiMessage newMessages[] = client.getNewMessages();
if (newMessages != null && newMessages.length != 0) {
for (HiMessage message : newMessages) {
if (message.getClientId() == client.getClientId()) {
contentPanel.add(new HiMessageUi("You:", message.getContent()));
} else {
contentPanel.add(new HiMessageUi("Stanger:", message.getContent()));
}
}
scrollPanel.scrollToBottom();
}
}
@Override
public void onStateChanged() {
// TODO Auto-generated method stub
}
}
暂时还没有实现 StateChanged。
接下来在Ui添加一个按钮,控制连接断开的。呃,日后再说吧。