1.简介:
模态对话框(Modal Dialogue Box),是指在用户想要对对话框以外的应用程序进行操作时,必须首先对该对话框进行响应。
模态对话框垄断了用户的输入。当一个模态对话框打开时,用户只能与该对话框进行交互,而其他用户界面对象收不到输入信息。模态对话框下,用户需要操作目标对话框就必须先操作模态对话框。
(上述简介摘自https://baike.baidu.com/item/模态对话框/6449704?fr=aladdin)
模态对话框的应用场景非常广泛,例如:在RMI中,客户端向服务器发送某次请求时,客户应该等待服务器处理完相关操作后才可继续进行第二次相同请求,而不是对同一请求疯狂的向服务器发送多次。为了保证在服务器处理相关请求时客户端可以进行等待并无法进行无意义的操作,这时就可以使用模态对话框。
2.具体实现:
(这里以RMI为例,如果对RMI还有疑惑的话可以看看RMI详解)
Ⅰ.首先要区分哪些方法需要模态框,哪些方法不需要模态框。我们的做法是新建一个注解,该注解用于标记执行需要模态框的方法,注解的内容是模态框需要显示的内容;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target(METHOD)
public @interface ModelDialog {
String caption();//模态框具体显示的内容
}
用注解标记区分方法:
public interface IStudentAction {
@ModelDialog(caption="正在登陆中……")
StudentVideoModel getStudent(String id, String password);
StudentVideoModel getCurriculum(String name,String number);
}
Ⅱ.在代理类中通过注解区分方法并解析注解得到模态框的内容,并进行初始化模态框并显示。
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<?> interfaces,JFrame parent) {
ClassLoader classLoader = interfaces.getClassLoader();
return (T) Proxy.newProxyInstance(classLoader, new Class<?>[] {interfaces}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
client.setMethod(method);
client.setArgs(args);
Object result = null;
//判断用户调用的方法上有没有ModelDialog注解,有的话调用显示模态框,没有就直接执行
if(method.isAnnotationPresent(ModelDialog.class)) {
ModelDialog modeldialog = method.getAnnotation(ModelDialog.class);
String modelcaption = modeldialog.caption();
MecDialog dialog = new MecDialog(parent,true);
dialog.setCaption(modelcaption);
dialog.setRmiClient(client);
dialog.showDialog();
//特别注意:在执行showdialog方法的时候,模态框显示出来,只要模态框不被关闭,则showdialog就一直执行中,程序卡死到这一行不往下执行
result = dialog.getResult();
}else {
result = client.RMIsend();
}
return result;
}
});
}
Ⅲ,最难的问题浮现出来了,正如代码中所说的那样:showdialog会让程序卡死在这一行,只要不关闭模态框,程序就不会继续运行下去。可是关闭模态框的方法得在show之后才执行(关闭模态框肯定是等服务器处理完相关方法并返回结果后,被客户端接收到才关闭)这下子就陷入了死路。
我们的解决思路是:巧用dialog的侦听事件,当侦听到模态框显示之后,才向服务器发送待处理的方法,等得到返回结果后在关闭模态框,并把结果传递到代理类中。
以下是具体处理:
private void dealAction() {
addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
//等侦听到dialog显示后便执行该方法
result = rmiClient.RMIsend();
//得到结果后关闭掉模态框
dispose();
}
});
}
public Object getResult() {
return result;
}
public void showDialog() {
this.setVisible(true);
}
修改之前的RMIsend()方法:
Object RMIsend() {//将之前的连接服务器方法和得到返回值方法结合
try {
//建立通信信道
socket = new Socket(IP,port);
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
//将参数转化成gson字符串
ArgumentMaker arg = new ArgumentMaker();
for(int i = 0; i<args.length;i++) {
arg.addArg("arg"+ i, args[i]);
}
String argsString = arg.toString();
Object obj = null;
try {
//发送给服务器
dos.writeUTF(String.valueOf(method.toString().hashCode()));
dos.writeUTF(argsString);
//侦听服务器返回处理结果
String result = dis.readUTF();
close();
obj = ArgumentMaker.gson.fromJson(result, method.getReturnType());
}
return obj;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}catch (UnknownHostException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return null;
}
这样子便会巧妙的避开这个showdialog的执行问题,将模态框的显示和服务器的处理巧妙切合起来。
测试结果如下:
点击登陆前:
点击登陆后:
等服务器处理完相关内容并且返回结果后这个模态框就会自动消失,可见我们的解决思路没有问题