假设这样一种情况我们的系统的界面使用javaxswing包构建界面的基础是BaseView他是一个容器当然他应当提供获取控件元素的功能比如得到按钮下拉框表格等当然仅仅是一个容器而已而我们的界面的元素全部部署在JPanel上
描述为:
一个界面就是一个BaseView他只包含一个JPanel这个包含JPanel包含所有我们的Swing控件例如JButtonJLable等等
问题出现了:我们通常因为业务的需要完成一个界面的操作要自动跳转到下一个界面完成下一个界面又能跳回来(题外话:由于我们的操作是基于GUI的所以往往能保存Session信息而Web却做不到)而这往往成为系统实现过程中效率低下的一个因素我就见到我现在的系统中有人用行代码判断上一个界面应该是哪一个来跳转过来因为很多界面都可以跳到当前界面
当然有一种做法是在下一个界面类中包含指向上一个界面的变量我们说这不方便也增加了依赖性这对软件是不利的
接下来我给出我的解决方法希望对采用这种界面结构的朋友有所裨益
(以下全部用简化模型来讲述)
简单点我们假设BaseView继承JWindow当然可以是别的容器(依据你的实现)大概象这样:
public abstract class BaseView extends JWindow{
(实现一些取得界面控件和界面信息的方法)
}
每个界面类都象这样定义:
public class MyView extends BaseView{
JPanel myPanel;
public void playoutPanel(){
JButton myButton = new JButton(OK);
myPaneladd(myButton);
(添加你需要的控件和布局到myPanel上)
}
}
假设有其他的界面OneViewTwoViewThreeView处理完操作后都需要跳转到myView在myView中的ok按钮按下的时候需要回到原始界面
原来臃肿的代码需要在myView中添加一个变量BaseView anyView;用来存放转来的那个界面anyView赋值在三者中的跳转代码中引用myView来设定跳转代码象这样:
public void jump(){
MyView myView = new MyView();
myViewanyView = this;
thisremove(thisxxPanel);
thisadd(myViewgetPanel());
thisrepaint();
}
看起来还不错虽然需要引用MyView类并调用他的变量和方法但是跳转回来却不那么容易否则怎么会用行!
大概象这样:(这已经是被我简化的)
public void goBack(){
if(anyView instanceof OneView){
anyViewremove(thismyView);
OneView ov = (OneView)anyView;
anyViewadd(ovgetPanel());
anyViewrepaint();
}
if(anyView instanceof TwoView){
}
}
不经大量应用别的业务用例界面这种编译依赖性真不是什么好事更何况用了大量的低效的instanceof判断和转型操作
为了优化这种情形彻底解决这个问题我想应该设计一个第三方类来消除这种依赖性并且让界面跳转不要这么费劲这个第三方的类是这样设计的:
在这个类中必须有一个变量来保存某一个界面跳转的路径如A>B>C路径一旦被保存你就拥有了控制显示任何一个界面的权利了在这个链中第一个位置的界面应该是这次跳转的第一站最后一个位置是当前站这里存在一个因果关系:只有跳转了才可以跳回去这样使得我们可以用数组来保存这个路径现实中跳转的情形应该不会超过次所以我们把路径长度设为(当然你可以根据需要更改)这个类的样子大概象这样:
class ViewPath{
JPanel[] pnlPath = null; //跳转的界面路径界面跳转最大个层次吧!!!
int index = ; //路径中的当前下标
BaseView bsView = null; //当前路径所在的同一个View
//在路径中寻找目标的方法
public int find(JPanel pnl){ //该路径下是否有某个Panel有的话返回下标没有的话返回
if(bsView==null) return ; //没有初始化该路径下没有任何Panel
for(int i=;iif(pnl==pnlPath[i]){
index = i;
return i; //如果找到了则返回位置并且把当前位置设为目标位置
}
}
return ; //没有找到返回
}
//构造函数
ViewPath(JPanel myPanelBaseView myView){
pnlPath = new JPanel; //设置路径最大长度为
bsView = myView; //设置该路径所属的那个View
pnlPath[] = myPanel; //设立起始站
index = ; //设立起始站索引
}
}
这样一个类就完成了保存一次跳转路径的作用(当然是否应该在find方法中设立目标位置是否合适有待商榷)
那么我们如何使用这样一个路径?
我们设立一个辅助类来完成这个工作我们命名为ViewJump我们知道作为辅助的类最好是不要有实例特别是象这样的起接口作用的类只提供静态方法它的框架象这样:
public class ViewJump{
private static ViewPath[] viewPath = null; //路径池系统多处使用静态但私有因为供内部用
private ViewJump(){} //私有构造方法辅助类只提供静态方法
private static int find(JPanel pnl); //寻找给定的Panel是否在已有路径中私有
private static int newPath(JPanel myPanelBaseView myView); //建立一个新路径私有
/**
* 每个类需要使用该辅助类时都需要第一步注册自己然后才能做其他操作
* 返回一个注册码id辅助类需要使用这个注册码进行其他操作
*/
public static int registerPath(JPanel myPanelBaseView myView);
/**
* 设立下一个界面
*/
public static void setNext(int idJPanel aim);
/**
* 回到上一个界面
*/
public static void back(int id);
/**
* 回到第一个界面
*/
public static void backHome(int id);
/**
* 跳转到下一个界面
*/
public static void jump(int id);
}
完成这样一个类的代码量并不多一百多行但是却使得用户完全脱离了处理不同界面的烦恼稍后会把该类的源码附上值得一提的是这个类的实现固然可以用到类似的实现当中但是如果用户的界面结构并不是如此搭建你就需要更改参数类型了如果能把这些抽象出来得到一个抽象类或接口参数用Object类型用户根据自己的需要去实现这些方法岂不妙哉!
使用这个类你可以简便的多的完成诸如上面的任务:
OneView中:
public void jump(){
MyView myView = new MyView();
int id = ViewJumpregisterPath(thisxxPanelthis);
ViewJumpsetNext(idmyViewgetPanel());
ViewJumpjump(id);
}
MyView中退回的部分:
protected void goBack(){
int id = ViewJumpregisterPath(thismyPanelthis);
ViewJumpback(id);
}
天哪这并不神奇行代码仅仅用了两行就实现了!
好了我就说这么多了一切都掌握在你手中用你的智慧来优化我们的冗余代码吧因为这样它看起来相当不错
附:完整代码:(我把ViewPath类放在同一个文件ViewJumpjava里代码上面已经给出)
public class ViewJump{
private static ViewPath[] viewPath = null;
//私有构造函数
private ViewJump(){}
//寻找该Panel是不是在路径中
/**
* 找到了返回在实例数组中的下标
* 没有找到返回
* @param pnl
* @return
*/
private static int find(JPanel pnl){
// Systemoutprintln(执行find() in ViewJump);
if(viewPath == null || viewPathlength==) return ;
for(int i = ;iViewPath vp = viewPath[i]; //对该路径检查
if(vpfind(pnl) != ){
return i;
}
}
return ;
}
//建立一个新的路径
/**
*
* @param myPanel
* @param myView
*/
private static int newPath(JPanel myPanelBaseView myView){
Systemoutprintln(执行newPath() in ViewJump);
//检验一下看有没有无效的路径有则清除
if(viewPath == null || viewPathlength==) {
viewPath = new ViewPath[]{new ViewPath(myPanelmyView)};
return ;
}
ViewPath[] vjArr = new ViewPath[viewPathlength];
int count = ;
for(int i = ;iif(viewPath[i]bsView!=null){ //把不为空的值取出来
vjArr[count++] = viewPath[i];
}
}
viewPath = new ViewPath[count+];
Systemarraycopy(viewPathvjArrcount); //复制到原来的数组变量中
//最后一个位置留给新加入的元素
viewPath[count] = new ViewPath(myPanelmyView);
return count;
}
//获得实例的方法
/**
* 必须检查该Panel是不是已经在路径中了如果在路径中
* 则返回注册的编号用此编号扁可以访问到正确的类型了
* 如果不在路径中则以此为开始新建一个新的路径
* 本来检查路径的时候没有必要检查路径的第一个元素
* 因为一个元素不可能是开端但是为了防止用户连续两次registerPath的错误
* 请把第一个元素也给检查一下
* myView 参数只有当该界面为跳转的起始点时才需要否则保持原始的View
* @param me
* @param other
* 返回实例数组的下标
*/
public static int registerPath(JPanel myPanelBaseView myView){
Systemoutprintln(执行registerPath() in ViewJump);
int idx = find(myPanel);
Systemoutprintln(idx=+idx);
if(idx==){ //返回表示没有找到建立一个新的路径
Systemoutprintln(新建一个路径);
idx = newPath(myPanelmyView);
}
Systemoutprintln(执行完注册路径);
return idx; //返回实例下标
}
//设定要跳转的下一个目标
public static void setNext(int idJPanel aim){
if(id<||id>=viewPathlength){
return;
}
ViewPath vp = viewPath[id];
//设定目标从这里看这是存在安全漏洞的如果使用者乱传递id进来的话
JPanel[] path = vppnlPath;
path[vpindex+] = aim;
}
//回到上一个
public static void back(int id){
if(id<||id>=viewPathlength){
return;
}
ViewPath vp = viewPath[id];
//回到上一个界面
if(vpindex>){ //只有当前面有路径时才作
vpbsViewremove(vppnlPath[vpindex]); //移去当前的
vpindex; //游标往前走一步
vpbsViewadd(vppnlPath[vpindex]BorderLayoutCENTER); //增加当前的到界面
vpbsViewvalidate();
vpbsViewrepaint();
}
}
//回到起源处
public static void backHome(int id){
if(id<||id>=viewPathlength){
return;
}
ViewPath vp = viewPath[id];
//直接回到第一步需要清除该路径吗?中途断裂怎么办?办法是检查View是否已为空
//选择不清除每次在建立新的路径时检查路径是不是已经无效了
vpbsViewremove(vppnlPath[vpindex]); //移去当前的
vpindex = ; //游标往前走一步
vpbsViewadd(vppnlPath[vpindex]BorderLayoutCENTER); //增加当前的到界面
vpbsViewvalidate();
vpbsViewrepaint();
}
//跳转到下一处
public static void jump(int id){
if(id<||id>=viewPathlength){
return;
}
ViewPath vp = viewPath[id];
if(vppnlPath[vpindex+]==null){
return; //下一步根本没有设置
}
vpbsViewremove(vppnlPath[vpindex]); //移去当前的
vpindex++;
vpbsViewadd(vppnlPath[vpindex]BorderLayoutCENTER);
vpbsViewvalidate();
vpbsViewrepaint();
}
}