概述
模态框的学习
假设存在这样场景:我登录lol页面的时候,一区人很多,我连着点登陆,客户端没给我反应。竟然没反应,然后我就疯狂点登陆,假设lol服务器无法处理这种重复命令,那么lol的服务器反应过来的时候,可能我已经点了n次登陆了,那么服务器就要处理很多没有意义的登陆指令。这是一个假设,是为了说明,用户操作的不当,会造成服务器雪上加霜的情况。
所以我们就要设计一个东西,来阻止用户的无理取闹,我的方式就是用模态框,当你进行点击登陆操作后,弹出一个模态框,对于模态框外面的界面,你不能在点了!模态框显示正在帮你处理,你别着急,这就有效的处理了刚才假设的情况。
下面贴一个模态框的代码:
继承自JDialog 类,当创建这个对象时,当传入参数是true时,表示这是一个模态框。
public class MDialog extends JDialog {
private static final long serialVersionUID = -3666711423943333589L;
public MDialog(Frame owner, boolean modal) {
super(owner, modal);
}
public MDialog(Dialog owner, boolean modal) {
super(owner, modal);
}
public MDialog(Frame owner, String title, boolean modal) {
super(owner, title, modal);
}
public MDialog(Dialog owner, String title, boolean modal) {
super(owner, title, modal);
}
public MDialog setCaption(String context) {
Font font = new Font("宋体", Font.BOLD, 16);
int width = (context.length() + 4) * font.getSize();
int height = 5 * font.getSize();
setSize(width, height);
setLocationRelativeTo(getOwner());
setLayout(null);
setUndecorated(true);
JPanel jpnlMessage = new JPanel();
jpnlMessage.setSize(width, height);
jpnlMessage.setLayout(new BorderLayout());
jpnlMessage.setBackground(Color.lightGray);
jpnlMessage.setBorder(BorderFactory.createLineBorder(Color.gray, 2));
add(jpnlMessage);
JLabel jlblMessage = new JLabel(context, JLabel.CENTER);
jlblMessage.setFont(font);
jlblMessage.setSize(width, height);
jlblMessage.setForeground(Color.blue);
jlblMessage.setHorizontalTextPosition(JLabel.CENTER);
jpnlMessage.add(jlblMessage, BorderLayout.CENTER);
dealAction();
return this;
}
public void dealAction() {
}
public void showDialog(){
setVisible(true);
}
public void closeDialog() {
dispose();
}
}
外面调用的时候就是这样:
MDialog mDialog = new MDialog(mainView, "温馨提示", true);
mDialog.showDialog();
System.out.println("连接成功");
这里有一个坑,这个连接成功不会打印,因为当主线程执行setVisible(true);之后主线程就被阻塞了,jvm会运行模态框中的代码,所以在哪里关闭模态框是一个大问题。这还没完,假如我们要根据主线程中的条件来关闭模态框,而主线程又是被阻塞的,那么就得不到关闭模态框的相关信息,这就矛盾了。有人说用一个新的线程来运行这个模态框,我试过是不行的,但是具体是哪里的问题,我没搞明白。
所以我用了另一种方式:
把主线程要做的事,放到了模态框的事件处理中,当执行完了主线程的代码,他根据事件关闭模态框。
模态框的一个应用
给一个新的应用场景:
现在我需要通过客户端登陆服务器,而这个连接方式采用短连接,短连接的实现方式就是用RMI即客户端调用服务器的远程方法。当客户端输入账号和密码后,点击登陆,服务器反应比较慢。所以我希望用模态框来组织用户继续点击这个登陆按钮,如下图的登陆按钮。
这是登陆界面的代码,当用户点击按钮后,触发事件执行这个方法。
private void dealUserLogin() {
jbtnLogin.setEnabled(false);
//获得框框中账号和密码
String id = jtxtUserName.getText();
String password = new String(jpswPassword.getPassword());
//获得接口的代理对象
IUserAction proxyClass = rmiClient.JDKProxy(IUserAction.class,jfrmLogin,"正在登陆中...请稍后");
//调用代理对象的方法
UserInfo user = proxyClass.userLogin(id, password);
//user是服务器返回的结果,根据这个结果判断这个用户符不符合登陆要求。
if (user.getId().equalsIgnoreCase("ERROR")) {
ViewTool.showError(jfrmLogin, user.getNick());
jpswPassword.setText("");
jtxtUserName.selectAll();
jtxtUserName.requestFocus();
return;
}
jbtnLogin.setEnabled(true);
}
获得接口代理对象的代码,仔细看能发现,这个代理对象中都没有对方法的拦截和对方法参数的改变,和以往我们对代理的认知完全不同,在这个仅仅是把方法的参数传给模态框,并且显示这个模态框的操作。而代理真正对于方法的调用确实在这个模态框的事件中
@SuppressWarnings({ "unchecked", "unused" }) //这是一种特殊情况下的获得代理的操作
public <T> T JDKProxy(Class<?> interfacer,JFrame jFrame,String caption) {
ClassLoader classLoader = interfacer.getClassLoader();
Class<?>[] interfaces = new Class<?>[] { interfacer };
return (T) Proxy.newProxyInstance(classLoader, interfaces,
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
RMIDialog rmiDialog = new RMIDialog(jFrame, caption, true);
rmiDialog.setMethodInvoke(methodInvoke)
.setArgs(args)
.setCaption(caption)
.setMethod(method);
rmiDialog.showDialog();
return rmiDialog.getResult();
}
});
}
看模态框事件中,调用方法的代码,这里是真正执行的rmi操作,即把方法名和方法所需要的参数传递到了服务器,服务器解析然后调用对应方法返回结果。这个result就是服务器返回的结果。
public void dealAction() {
addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
//这里真正执行rmi的操作
result = methodInvoke.methodInvoke(object, method, args);
closeDialog();
}
});
}
可以看一下RMI调用方法的过程,服务器的解析代码就不贴了。
public class DialogMethodInvoke implements IMethodInvoke{
private String rmiIp;
private int rmiPort;
public DialogMethodInvoke() {
}
void setRmiIp(String rmiIp) {
this.rmiIp = rmiIp;
}
void setRmiPort(int rmiPort) {
this.rmiPort = rmiPort;
}
public <T> T methodInvoke(Object object, Method method, Object[] args) {
Socket socket = null;
DataInputStream dis = null;
DataOutputStream dos = null;
try {
socket = new Socket(rmiIp,rmiPort);
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(method.toString());
dos.writeUTF(getArgs(args));
//远程调用服务器端方法的返回值
String str = dis.readUTF();
Type type = method.getGenericReturnType();
@SuppressWarnings("unchecked")
T res = (T)ArgumentMaker.gson.fromJson(str, type);
return res;
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(dis != null) {
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(dos != null) {
try {
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null && !socket.isClosed()) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
private String getArgs(Object[] args) {
if(args == null) {
return "";
}
ArgumentMaker argumentMaker = new ArgumentMaker();
for(int i = 0; i< args.length; i++) {
argumentMaker.add("args"+i, args[i]);
}
return argumentMaker.toString();
}
}
总结一下,模态框的线程问题还是很棘手的。把真正执行代码放到模态框的事件中,有一种取巧的感觉,还是有必要写个博客记录一下。
再一个就是,对于代理模式思路的开辟,代理模式下,不一定就要把对于方法的拦截和参数修改都写在获得代理对象中,我们还可以更加灵活地操作,比如我只是在里面显示了一个模态框。
最后
以上就是凶狠斑马为你收集整理的巧妙处理Swing模态框setVisible(true)线程阻塞的问题模态框的学习模态框的一个应用的全部内容,希望文章能够帮你解决巧妙处理Swing模态框setVisible(true)线程阻塞的问题模态框的学习模态框的一个应用所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复