明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

Java中簡(jiǎn)單完成界面跳轉(zhuǎn)

[摘要]假設(shè)這樣一種情況,我們的系統(tǒng)的界面使用javax.swing包構(gòu)建,界面的基礎(chǔ)是BaseView,他是一個(gè)容器,當(dāng)然他應(yīng)當(dāng)提供獲取控件元素的功能,比如得到按鈕,下拉框,表格等,當(dāng)然僅僅是一個(gè)容器而已,而我們的界面的元素全部部署在JPanel上。   描述為:   一個(gè)界面就是一個(gè)BaseView,...
假設(shè)這樣一種情況,我們的系統(tǒng)的界面使用javax.swing包構(gòu)建,界面的基礎(chǔ)是BaseView,他是一個(gè)容器,當(dāng)然他應(yīng)當(dāng)提供獲取控件元素的功能,比如得到按鈕,下拉框,表格等,當(dāng)然僅僅是一個(gè)容器而已,而我們的界面的元素全部部署在JPanel上。

  描述為:

  一個(gè)界面就是一個(gè)BaseView,他只包含一個(gè)JPanel,這個(gè)包含JPanel包含所有我們的Swing控件,例如JButton,JLable等等。

  問題出現(xiàn)了:我們通常因?yàn)闃I(yè)務(wù)的需要完成一個(gè)界面的操作要自動(dòng)跳轉(zhuǎn)到下一個(gè)界面,完成下一個(gè)界面又能跳回來(題外話:由于我們的操作是基于GUI的,所以往往能保存Session信息,而Web卻做不到),而這往往成為系統(tǒng)實(shí)現(xiàn)過程中效率低下的一個(gè)因素,我就見到我現(xiàn)在的系統(tǒng)中有人用600行代碼判斷上一個(gè)界面應(yīng)該是哪一個(gè)來跳轉(zhuǎn)過來,因?yàn)楹芏嘟缑娑伎梢蕴疆?dāng)前界面。

  當(dāng)然有一種做法是,在下一個(gè)界面類中包含指向上一個(gè)界面的變量,我們說,這不方便,也增加了依賴性,這對(duì)軟件是不利的。

  接下來,我給出我的解決方法,希望對(duì)采用這種界面結(jié)構(gòu)的朋友有所裨益。

  (以下全部用簡(jiǎn)化模型來講述.)

  1.簡(jiǎn)單點(diǎn),我們假設(shè)BaseView繼承JWindow,當(dāng)然可以是別的容器(依據(jù)你的實(shí)現(xiàn)),大概象這樣:

  public abstract class BaseView extends JWindow{
  ...
  (實(shí)現(xiàn)一些取得界面控件,和界面信息的方法).
  }


  2.每個(gè)界面類都象這樣定義:

  public class MyView extends BaseView{
  JPanel myPanel;
  public void playoutPanel(){
  JButton myButton = new JButton("OK");
  myPanel.add(myButton);
  ......
  (添加你需要的控件和布局到myPanel上)
  }
  }

  3.假設(shè)有其他的界面OneView,TwoView,ThreeView處理完操作后都需要跳轉(zhuǎn)到myView,在myView中的ok按鈕按下的時(shí)候需要回到原始界面。

  原來臃腫的代碼需要在myView中添加一個(gè)變量BaseView anyView;用來存放轉(zhuǎn)來的那個(gè)界面anyView,賦值在三者中的跳轉(zhuǎn)代碼中引用myView來設(shè)定.跳轉(zhuǎn)代碼象這樣:

  public void jump(){
  MyView myView = new MyView();
  myView.anyView = this;
  this.remove(this.xxPanel);
  this.add(myView.getPanel());
  this.repaint();
  }

  看起來還不錯(cuò),雖然需要引用MyView類,并調(diào)用他的變量和方法.但是跳轉(zhuǎn)回來卻不那么容易,否則怎么會(huì)用600行!

  大概象這樣:(這已經(jīng)是被我簡(jiǎn)化的)

  public void goBack(){
  if(anyView instanceof OneView){
  anyView.remove(this.myView);
  OneView ov = (OneView)anyView;
  anyView.add(ov.getPanel());
  anyView.repaint();
  }
  if(anyView instanceof TwoView){
  ....
  }
  ...
  }

  不經(jīng)大量應(yīng)用別的業(yè)務(wù)用例界面,這種編譯依賴性真不是什么好事,更何況用了大量的低效的instanceof判斷和轉(zhuǎn)型操作.

  為了優(yōu)化這種情形,徹底解決這個(gè)問題,我想應(yīng)該設(shè)計(jì)一個(gè)第三方類來消除這種依賴性,并且讓界面跳轉(zhuǎn)不要這么費(fèi)勁。這個(gè)第三方的類是這樣設(shè)計(jì)的:

  在這個(gè)類中,必須有一個(gè)變量來保存某一個(gè)界面跳轉(zhuǎn)的路徑,如A->B->C.路徑一旦被保存,你就擁有了控制顯示任何一個(gè)界面的權(quán)利了。在這個(gè)鏈中,第一個(gè)位置的界面應(yīng)該是這次跳轉(zhuǎn)的第一站,最后一個(gè)位置是當(dāng)前站。這里存在一個(gè)因果關(guān)系:只有跳轉(zhuǎn)了才可以跳回去。這樣使得我們可以用數(shù)組來保存這個(gè)路徑,F(xiàn)實(shí)中,跳轉(zhuǎn)的情形應(yīng)該不會(huì)超過10次,所以我們把路徑長(zhǎng)度設(shè)為10(當(dāng)然你可以根據(jù)需要更改)。這個(gè)類的樣子大概象這樣:

  class ViewPath{
  JPanel[] pnlPath = null; //跳轉(zhuǎn)的界面路徑,界面跳轉(zhuǎn)最大10個(gè)層次吧!!!
  int index = 0; //路徑中的當(dāng)前下標(biāo)
  BaseView bsView = null; //當(dāng)前路徑所在的同一個(gè)View

  //在路徑中尋找目標(biāo)的方法
  public int find(JPanel pnl){ //該路徑下是否有某個(gè)Panel,有的話返回下標(biāo),沒有的話返回-1
  if(bsView==null) return -1; //沒有初始化,該路徑下沒有任何Panel
  for(int i=0;i
  if(pnl==pnlPath[i]){
   index = i;
   return i; //如果找到了則返回位置,并且把當(dāng)前位置設(shè)為目標(biāo)位置
  }
  }
  return -1; //沒有找到,返回-1
  }

  //構(gòu)造函數(shù)
  ViewPath(JPanel myPanel,BaseView myView){
  pnlPath = new JPanel[10]; //設(shè)置路徑最大長(zhǎng)度為10

  bsView = myView; //設(shè)置該路徑所屬的那個(gè)View
  pnlPath[0] = myPanel; //設(shè)立起始站
  index = 0; //設(shè)立起始站索引
  }
  }

  這樣一個(gè)類就完成了保存一次跳轉(zhuǎn)路徑的作用.(當(dāng)然,是否應(yīng)該在find方法中設(shè)立目標(biāo)位置是否合適有待商榷)

  那么我們?nèi)绾问褂眠@樣一個(gè)路徑?

  我們?cè)O(shè)立一個(gè)輔助類來完成這個(gè)工作,我們命名為ViewJump,我們知道作為輔助的類最好是不要有實(shí)例,特別是象這樣的起接口作用的類,只提供靜態(tài)方法.它的框架象這樣:

  public class ViewJump{
  private static ViewPath[] viewPath = null; //路徑池,系統(tǒng)多處使用,靜態(tài)但私有,因?yàn)楣﹥?nèi)部用

  private ViewJump(){} //私有構(gòu)造方法,輔助類只提供靜態(tài)方法
  private static int find(JPanel pnl); //尋找給定的Panel是否在已有路徑中,私有
  private static int newPath(JPanel myPanel,BaseView myView); //建立一個(gè)新路徑,私有
  /**
  * 每個(gè)類需要使用該輔助類時(shí)都需要第一步注冊(cè)自己,然后才能做其他操作
  * 返回一個(gè)注冊(cè)碼id,輔助類需要使用這個(gè)注冊(cè)碼進(jìn)行其他操作
  */
  public static int registerPath(JPanel myPanel,BaseView myView);
  /**
  * 設(shè)立下一個(gè)界面.
  */
  public static void setNext(int id,JPanel aim);
  /**
  * 回到上一個(gè)界面
  */
  public static void back(int id);
  /**
  * 回到第一個(gè)界面
  */
  public static void backHome(int id);
  /**
  * 跳轉(zhuǎn)到下一個(gè)界面
  */
  public static void jump(int id);
  }

  完成這樣一個(gè)類的代碼量并不多,一百多行,但是卻使得用戶完全脫離了處理不同界面的煩惱.稍后會(huì)把該類的源碼附上,值得一提的是,這個(gè)類的實(shí)現(xiàn)固然可以用到類似的實(shí)現(xiàn)當(dāng)中,但是如果用戶的界面結(jié)構(gòu)并不是如此搭建,你就需要更改參數(shù)類型了.如果能把這些抽象出來,得到一個(gè)抽象類或接口,參數(shù)用Object類型.用戶根據(jù)自己的需要去實(shí)現(xiàn)這些方法,豈不妙哉!

  使用這個(gè)類,你可以簡(jiǎn)便的多的完成諸如上面的任務(wù):

  OneView中:

  public void jump(){
  MyView myView = new MyView();
  int id = ViewJump.registerPath(this.xxPanel,this);
  ViewJump.setNext(id,myView.getPanel());
  ViewJump.jump(id);
  }

  MyView中退回的部分:

  protected void goBack(){
  int id = ViewJump.registerPath(this.myPanel,this);
  ViewJump.back(id);
  }

  天哪,這并不神奇,600行代碼僅僅用了兩行就實(shí)現(xiàn)了!

  好了,我就說這么多了,一切都掌握在你手中,用你的智慧來優(yōu)化我們的冗余代碼吧,因?yàn)檫@樣它看起來相當(dāng)不錯(cuò).

   附:完整代碼:(我把ViewPath類放在同一個(gè)文件ViewJump.java里,代碼上面已經(jīng)給出)

  public class ViewJump{
  private static ViewPath[] viewPath = null;

  //私有構(gòu)造函數(shù)
  private ViewJump(){}

  //尋找該P(yáng)anel是不是在路徑中
  /**
  * 找到了返回在實(shí)例數(shù)組中的下標(biāo)
  * 沒有找到返回-1
  * @param pnl
  * @return
  */
  private static int find(JPanel pnl){
  // System.out.println("執(zhí)行find() in ViewJump");
  if(viewPath == null viewPath.length==0) return -1;
  for(int i = 0;i
   ViewPath vp = viewPath[i]; //對(duì)該路徑檢查
   if(vp.find(pnl) != -1){
    return i;
   }
  }
  return -1;
  }

  //建立一個(gè)新的路徑
  /**
  *
  * @param myPanel
  * @param myView
  */
  private static int newPath(JPanel myPanel,BaseView myView){
  System.out.println("執(zhí)行newPath() in ViewJump");
  //檢驗(yàn)一下看有沒有無(wú)效的路徑,有則清除
  if(viewPath == null viewPath.length==0) {
   viewPath = new ViewPath[]{new ViewPath(myPanel,myView)};
   return 0;
  }

  ViewPath[] vjArr = new ViewPath[viewPath.length];
  int count = 0;
  for(int i = 0;i
   if(viewPath[i].bsView!=null){ //把不為空的值取出來
    vjArr[count++] = viewPath[i];
   }
  }
  viewPath = new ViewPath[count+1];
  System.arraycopy(viewPath,0,vjArr,0,count); //復(fù)制到原來的數(shù)組變量中
  //最后一個(gè)位置留給新加入的元素
  viewPath[count] = new ViewPath(myPanel,myView);
  return count;
  }

  //獲得實(shí)例的方法
  /**
  * 必須檢查該P(yáng)anel是不是已經(jīng)在路徑中了,如果在路徑中,
  * 則返回注冊(cè)的編號(hào),用此編號(hào)扁可以訪問到正確的類型了
  * 如果不在路徑中,則以此為開始新建一個(gè)新的路徑
  * 本來檢查路徑的時(shí)候沒有必要檢查路徑的第一個(gè)元素,
  * 因?yàn)橐粋(gè)元素不可能是開端,但是為了防止用戶連續(xù)兩次registerPath的錯(cuò)誤
  * 請(qǐng)把第一個(gè)元素也給檢查一下
  * myView 參數(shù)只有當(dāng)該界面為跳轉(zhuǎn)的起始點(diǎn)時(shí)才需要,否則保持原始的View
  * @param me
  * @param other
  * 返回實(shí)例數(shù)組的下標(biāo),
  */
  public static int registerPath(JPanel myPanel,BaseView myView){
  System.out.println("執(zhí)行registerPath() in ViewJump");
  int idx = find(myPanel);
  System.out.println("idx="+idx);
  if(idx==-1){ //返回-1表示沒有找到,建立一個(gè)新的路徑
   System.out.println("新建一個(gè)路徑");
   idx = newPath(myPanel,myView);
  }
  System.out.println("執(zhí)行完注冊(cè)路徑..");
  return idx; //返回實(shí)例下標(biāo)
  }

  //設(shè)定要跳轉(zhuǎn)的下一個(gè)目標(biāo)
  public static void setNext(int id,JPanel aim){
  if(id<0 id>=viewPath.length){
   return;
  }
  ViewPath vp = viewPath[id];
  //設(shè)定目標(biāo),從這里看,這是存在安全漏洞的,如果使用者亂傳遞id進(jìn)來的話
  JPanel[] path = vp.pnlPath;
  path[vp.index+1] = aim;
  }

  //回到上一個(gè)
  public static void back(int id){
  if(id<0 id>=viewPath.length){
  return;
  }
  ViewPath vp = viewPath[id];
  //回到上一個(gè)界面
  if(vp.index>0){ //只有當(dāng)前面有路徑時(shí)才作
  vp.bsView.remove(vp.pnlPath[vp.index]); //移去當(dāng)前的
  vp.index--; //游標(biāo)往前走一步
  vp.bsView.add(vp.pnlPath[vp.index],BorderLayout.CENTER); //增加當(dāng)前的到界面

  vp.bsView.validate();
  vp.bsView.repaint();
  }
  }

  //回到起源處
  public static void backHome(int id){
  if(id<0 id>=viewPath.length){
  return;
  }
  ViewPath vp = viewPath[id];
  //直接回到第一步,需要清除該路徑嗎?中途斷裂怎么辦?辦法是檢查View是否已為空
  //選擇不清除,每次在建立新的路徑時(shí),檢查路徑是不是已經(jīng)無(wú)效了
  vp.bsView.remove(vp.pnlPath[vp.index]); //移去當(dāng)前的
  vp.index = 0; //游標(biāo)往前走一步
  vp.bsView.add(vp.pnlPath[vp.index],BorderLayout.CENTER); //增加當(dāng)前的到界面

  vp.bsView.validate();
  vp.bsView.repaint();
  }

  //跳轉(zhuǎn)到下一處
  public static void jump(int id){
  if(id<0 id>=viewPath.length){
  return;
  }
  ViewPath vp = viewPath[id];
  if(vp.pnlPath[vp.index+1]==null){
  return; //下一步根本沒有設(shè)置
  }

  vp.bsView.remove(vp.pnlPath[vp.index]); //移去當(dāng)前的
  vp.index++;
  vp.bsView.add(vp.pnlPath[vp.index],BorderLayout.CENTER);
  vp.bsView.validate();
  vp.bsView.repaint();
  }
  }