1. 程式人生 > >52 java程式設計思想——建立視窗和程式片 程式片限制

52 java程式設計思想——建立視窗和程式片 程式片限制

52.java程式設計思想——建立視窗和程式片 程式片限制

出於安全緣故,程式片十分受到限制,並且有很多的事我們都不能做。您一般會問:程式片看起來能做什麼,傳聞它又能做什麼:擴充套件瀏覽器中WEB 頁的功能。自從作為一個網上衝浪者,我們從未真正想了解是否一個WEB 頁來自友好的或者不友好的站點,我們想要一些可以安全地行動的程式碼。所以我們可能會注意到大量的限制:

(1) 一個程式片不能接觸到本地的磁碟。這意味著不能在本地磁碟上寫和讀,我們不想一個程式片通過WEB頁面閱讀和傳送重要的資訊。寫是被禁止的,當然,因為那將會引起病毒的侵入。當數字簽名生效時,這些限制會被解除。

(2) 程式片不能擁有選單。(注意:這是規定在Swing 中的)這可能會減少關於安全和關於程式簡化的麻煩。我們可能會接到有關程式片協調利益以作為WEB 頁面的一部分的通知;而我們通常不去注意程式片的範圍。這兒沒有幀和標題條從選單處彈出,出現的幀和標題條是屬於WEB 瀏覽器的。也許將來設計能被改變成允許我們將瀏覽器選單和程式片選單相結合起來——程式片可以影響它的環境將導致太危及整個系統的安全並使程式片過於的複雜。

(3) 對話方塊是不被信任的。在Java中,對話方塊存在一些令人難解的地方。首先,它們不能正確地拒絕程式片,這實在是令人沮喪。如果我們從程式片彈出一個對話方塊,我們會在對話方塊上看到一個附上的訊息框“不被信任的程式片”。這是因為在理論上,它有可能欺騙使用者去考慮他們在通過WEB 同一個老顧客的本地應用程式交易並且讓他們輸入他們的信用卡號。在看到AWT 開發的那種GUI 後,我們可能會難過地相信任何人都會被那種方法所愚弄。但程式片是一直附著在一個Web頁面上的,並可以在瀏覽器中看到,而對話方塊沒有這種依附關係,所以理論上是可能的。因此,我們很少會見到一個使用對話方塊的程式片。

在較新的瀏覽器中,對受到信任的程式片來說,許多限制都被放寬了(受信任程式片由一個信任源認證)。

涉及程式片的開發時,還有另一些問題需要考慮:

■程式片不停地從一個適合不同類的單獨的伺服器上下載。我們的瀏覽器能夠快取程式片,但這沒有保證。在Java 1.1 版中的一個改進是JAR(Java ARchive)檔案,它允許將所有的程式片元件(包括其它的類檔案、影象、聲音)一起打包到一個的能被單個伺服器處理下載的壓縮檔案。“數字簽字”(能校驗類建立器)可有效地加入每個單獨的JAR 檔案。

■因為安全方面的緣故,我們做某些工作更加困難,例如訪問資料庫和傳送電子郵件。另外,安全限制規則使訪問多個主機變得非常的困難,因為每一件事都必須通過WEB 伺服器路由,形成一個性能瓶頸,並且單一環節的出錯都會導致整個處理的停止。

■瀏覽器裡的程式片不會擁有同樣的本地應用程式執行的控制元件型別。例如,自從使用者可以開關頁面以來,在程式片中不會擁有一個形式上的對話方塊。當用戶對一個WEB 頁面進行改變或退出瀏覽器時,對我們的程式片而言簡直是一場災難——這時沒有辦法儲存狀態,所以如果我們在處理和操作中時,資訊會被丟失。另外,當我們離開一個WEB 頁面時,不同的瀏覽器會對我們的程式片做不同的操作,因此結果本來就是不確定的。

1     程式片的優點

如果能容忍那些限制,那麼程式片的一些優點也是非常突出的,尤其是在我們構建客戶/伺服器應用或者其它網路應用時:

■沒有安裝方面的爭議。程式片擁有真正的平臺獨立性(包括容易地播放聲音檔案等能力)所以我們不需要針對不同的平臺修改程式碼也不需要任何人根據安裝執行任何的“tweaking ”。事實上,安裝每次自動地將WEB 頁連同程式片一起,因此安靜、自動地更新。在傳統的客戶機/伺服器系統中,建立和安裝一個新版本的客戶端軟體簡直就是一場惡夢。

■因為安全的原因建立在核心Java 語言和程式片結構中,我們不必擔心壞的程式碼而導致毀壞某人的系統。這樣,連同前面的優點,可使用Java (可從JavaScript 和VBScript中選擇客戶端的WEB 程式設計工具)為所謂的Intrant(在公司內部使用而不向Internet 轉移的企業內部網路)客戶機/伺服器開發應用程式。

■由於程式片是自動同HTML 整合的,所以我們有一個內建的獨立平臺檔案系統去支援程式片。這是一個很有趣的方法,因為我們慣於擁有程式檔案的一部分而不是相反的擁有檔案系統。

2     視窗化應用

出於安全的緣故,我們會看到在程式片我們的行為非常的受到限制。我們真實地感到,程式片是被臨時地加入在WEB 瀏覽器中的,因此,它的功能連同它的相關知識,控制元件都必須加以限制。但是,我們希望Java能製造一個開視窗的程式去執行一些事物,否則寧願安放在一個WEB 頁面上,並且也許我們希望它可以執行一些可靠的應用程式,以及誇張的實時便攜性。在這本書前面的章節中我們製造了一些命令列應用程式,但在一些操作環境中(例如:Macintosh)沒有命令列。所以我們有很多的理由去利用Java 建立一個設定視窗,非程式片的程式。這當然是一個十分合理的要求。

一個Java 設定視窗應用程式可以擁有選單和對話方塊(這對一個程式片來說是不可能的和很困難的),可是如果我們使用一個老版本的Java,我們將會犧牲本地作業系統環境的外觀和感受。JFC/Swing 庫允許我們製造一個保持原來作業系統環境的外觀和感受的應用程式。如果我們想建立一個設定視窗應用程式,它會合理地運作,同樣,如果我們可以使用最新版本的Java 並且集合所有的工具,我們就可以釋出不會使使用者困惑的應用程式。如果因為一些原因,我們被迫使用老版本的Java,請在毀壞以建立重要的設定視窗的應用程式前仔細地考慮。

3     選單

直接在程式片中安放一個選單是不可能的(Java 1.0,Java1.1 和Swing 庫不允許),因為它們是針對應用程式的。繼續,如果您不相信我並且確定在程式片中可以合理地擁有選單,那麼您可以去試驗一下。程式片中沒有setMenuBar()方法,而這種方法是附在選單中的(我們會看到它可以合理地在程式片產生一個幀,並且幀包含選單)。

有四種不同型別的MenuComponent (選單元件),所有的選單元件起源於抽象類:選單條(我們可以在一個事件幀裡擁有一個選單條),選單去支配一個單獨的下拉選單或者子選單、選單項來說明選單裡一個單個的元素,以及起源於MenuItem,產生檢查標誌(checkmark)去顯示選單項是否被選擇的CheckBoxMenuItem。不同的系統使用不同的資源,對Java 和AWT 而言,我們必須在原始碼中手工彙編所有的選單。

4     程式碼1

import java.awt.*;

public class Menu1 extends Frame {

    String[] flavors = { "Chocolate", "Strawberry","Vanilla Fudge Swirl", "Mint Chip", "Mocha Almond Fudge",

            "Rum Raisin", "Praline Cream","Mud Pie" };

    TextField t = new TextField("No flavor", 30);

    MenuBar mb1 = new MenuBar();

    Menu f = new Menu("File");

    Menu m = new Menu("Flavors");

    Menu s = new Menu("Safety");

    // Alternativeapproach:

    CheckboxMenuItem[] safety = { new CheckboxMenuItem("Guard"), new CheckboxMenuItem("Hide")};

    MenuItem[] file = { new MenuItem("Open"), new MenuItem("Exit")};

    // A secondmenu bar to swap to:

    MenuBar mb2 = new MenuBar();

    Menu fooBar = new Menu("fooBar");

    MenuItem[] other = { new MenuItem("Foo"), new MenuItem("Bar"), new MenuItem("Baz"), };

    Button b = new Button("Swap Menus");

    public Menu1() {

        for (int i = 0; i < flavors.length; i++) {

            m.add(new MenuItem(flavors[i]));

            // Add separators at intervals:

            if ((i + 1) % 3 == 0)

                m.addSeparator();

        }

        for (int i = 0; i < safety.length; i++)

            s.add(safety[i]);

        f.add(s);

        for (int i = 0; i < file.length; i++)

            f.add(file[i]);

        mb1.add(f);

        mb1.add(m);

        setMenuBar(mb1);

        t.setEditable(false);

        add("Center", t);

        // Set up the system for swapping menus:

        add("North", b);

        for (int i = 0; i < other.length; i++)

            fooBar.add(other[i]);

        mb2.add(fooBar);

    }

    public booleanhandleEvent(Event evt){

        if (evt.id== Event.WINDOW_DESTROY)

            System.exit(0);

        else

            return super.handleEvent(evt);

        return true;

    }

    public booleanaction(Event evt,Object arg){

        if (evt.target.equals(b)) {

            MenuBar m = getMenuBar();

            if (m == mb1)

                setMenuBar(mb2);

            else if (m == mb2)

                setMenuBar(mb1);

        } else if (evt.targetinstanceofMenuItem) {

            if (arg.equals("Open")){

                String s = t.getText();

                boolean chosen = false;

                for (int i = 0; i < flavors.length; i++)

                    if (s.equals(flavors[i]))

                        chosen = true;

                if (!chosen)

                    t.setText("Choose a flavor first!");

                else

                    t.setText("Opening " + s + ". Mmm, mm!");

            } else if (evt.target.equals(file[1]))

                System.exit(0);

            // CheckboxMenuItems cannot use String

            // matching; you must match the target:

            else if (evt.target.equals(safety[0]))

                t.setText("Guard the Ice Cream! "+ "Guarding is " + safety[0].getState());

            else if (evt.target.equals(safety[1]))

                t.setText("Hide the Ice Cream! " + "Is it cold? "+ safety[1].getState());

            else

                t.setText(arg.toString());

        } else

            return super.action(evt, arg);

        return true;

    }

    public staticvoidmain(String[] args){

        Menu1 f = new Menu1();

        f.resize(300, 200);

        f.show();

    }

}/// :~

5     執行

避免了為每個選單編寫典型的冗長的add()列表呼叫,因為那看起來像許多的無用的標誌。取而代之的是,我安放選單項到陣列中,然後在一個for 的迴圈中通過每個陣列呼叫add()簡單地跳過。這樣的話,增加和減少選單項變得沒那麼討厭了。

作為一個可選擇的方法(我發現這很難令我滿意,因為它需要更多的分配)CheckboxMenuItems 在陣列的控制代碼中被建立是被稱為安全建立;這對陣列檔案和其它的檔案而言是真正的安全。程式中建立了不是一個而是二個的選單條來證明選單條在程式執行時能被交換啟用。我們可以看到選單條怎樣組成選單,每個選單怎樣組成選單項(MenuItems),chenkboxMenuItems 或者其它的選單(產生子選單)。當選單組合後,可以用setMenuBar()方法安裝到現在的程式中。值得注意的是當按鈕被壓下時,它將檢查當前的選單安裝使用getMenuBar(),然後安放其它的選單條在它的位置上。

當測試是“open”(即開始)時,注意拼寫和大寫,如果開始時沒有物件,Java 發出no error(沒有錯誤)的訊號。這種字串比較是一個明顯的程式設計錯誤源。校驗和非校驗的選單項自動地執行,與之相關的CheckBoxMenuItems 著實令人吃驚,這是因為一些原因它們不允許字串匹配。(這似乎是自相矛盾的,儘管字串匹配並不是一種很好的辦法。)因此,我們可以匹配一個目標物件而不是它們的標籤。當演示時,getState()方法用來顯示狀態。我們同樣可以用setState()改變CheckboxMenuItem 的狀態。

我們可能會認為一個選單可以合理地置入超過一個的選單條中。這看似合理,因為所有我們忽略的選單條的add()方法都是一個控制代碼。然而,如果我們試圖這樣做,這個結果將會變得非常的彆扭,而遠非我們所希望得到的結果。(很難知道這是一個程式設計中的錯誤或者說是他們試圖使它以這種方法去執行所產生的。)這個例子同樣向我們展示了為什麼我們需要建立一個應用程式以替代程式片。(這是因為應用程式能支援選單,而程式片是不能直接使用選單的。)我們從幀處繼承代替從程式片處繼承。另外,我們為類建一個構建器以取代init()安裝事件。最後,我們建立一個main()方法並且在我們建的新型物件裡,調整它的大小,然後呼叫show()。它與程式片只在很小的地方有不同之處,然而這時它已經是一個獨立的設定視窗應用程式並且我們可以使用選單。

6     對話方塊

對話方塊是一個從其它視窗彈出的視窗。它的目的是處理一些特殊的爭議和它們的細節而不使原來的視窗陷入混亂之中。對話方塊大量在設定視窗的程式設計環境中使用,但就像前面提到的一樣,鮮于在程式片中使用。我們需要從對話類處繼承以建立其它型別的視窗、像幀一樣的對話方塊。和窗框不同,對話方塊不能擁有選單條也不能改變游標,但除此之外它們十分的相似。一個對話方塊擁有佈局管理器(預設的是BorderLayout 佈局管理器)和過載action()等等,或用handleEvent() 去處理事件。我們會注意到handleEvent()的一個重要差異:當WINDOW_DESTORY 事件發生時,我們並不希望關閉正在執行的應用程式!

相反,我們可以使用對話視窗通過呼叫dispace()釋放資源。在下面的例子中,對話方塊是由定義在那兒作為類的ToeButton的特殊按鈕組成的網格構成的(利用GridLayout 佈局管理器)。ToeButton 按鈕圍繞它自已畫了一個幀,並且依賴它的狀態:在空的中的“X”或者“O”。它從空白開始,然後依靠使用者的選擇,轉換成“X”或“O”。但是,當我們單擊在按鈕上時,它會在“X”和“O”之間來回交換。(這產生了

一種類似填字遊戲的感覺,當然比它更令人討厭。)另外,這個對話方塊可以被設定為在主應用程式視窗中為很多的行和列變更號碼。

6.1     程式碼

import java.awt.*;

class ToeButton extends Canvas {

    int state= ToeDialog.BLANK;

    ToeDialog parent;

    ToeButton(ToeDialog parent) {

        this.parent = parent;

    }

    public voidpaint(Graphics g){

        int x1 = 0;

        int y1 = 0;

        int x2 = size().width - 1;

        int y2 = size().height - 1;

        g.drawRect(x1, y1, x2,y2);

        x1 = x2 / 4;

        y1 = y2 / 4;

        int wide = x2/ 2;

        int high = y2/ 2;

        if (state == ToeDialog.XX) {

            g.drawLine(x1, y1, x1+ wide,y1+ high);

            g.drawLine(x1, y1 + high,x1+ wide,y1);

        }

        if (state == ToeDialog.OO) {

            g.drawOval(x1, y1, x1+ wide/ 2, y1+ high/ 2);

        }

    }

    public booleanmouseDown(Event evt,intx, int y) {

        if (state == ToeDialog.BLANK) {

            state = parent.turn;

            parent.turn = (parent.turn == ToeDialog.XX ? ToeDialog.OO : ToeDialog.XX);

        } else

            state = (state == ToeDialog.XX ? ToeDialog.OO : ToeDialog.XX);

        repaint();

        return true;

    }

}

class ToeDialog extends Dialog {

    // w = numberof cells wide

    // h = numberof cells high

    static finalintBLANK= 0;

    static finalintXX= 1;

    static finalintOO= 2;

    int turn= XX;// Start with x's turn

    public ToeDialog(Frame parent, intw, int h) {

        super(parent, "The gameitself", false);

        setLayout(new GridLayout(w, h));

        for (int i = 0; i < w * h; i++)

            add(new ToeButton(this));

        resize(w * 50, h * 50);

    }

    public booleanhandleEvent(Event evt){

        if (evt.id== Event.WINDOW_DESTROY)

            dispose();

        else

            return super.handleEvent(evt);

        return true;

    }

}

public class ToeTest extends Frame {

    TextField rows = new TextField("3");

    TextField cols = new TextField("3");

    public ToeTest() {

        setTitle("Toe Test");

        Panel p = new Panel();

        p.setLayout(new GridLayout(2, 2));

        p.add(new Label("Rows", Label.CENTER));

        p.add(rows);

        p.add(new Label("Columns", Label.CENTER));

        p.add(cols);

        add("North", p);

        add("South", new Button("go"));

    }

    public booleanhandleEvent(Event evt){

        if (evt.id== Event.WINDOW_DESTROY)

            System.exit(0);

        else

            return super.handleEvent(evt);

        return true;

    }

    public booleanaction(Event evt,Object arg){

        if (arg.equals("go")){

            Dialog d = new ToeDialog(this, Integer.parseInt(rows.getText()), Integer.parseInt(cols.getText()));

            d.show();

        } else

            return super.action(evt, arg);

        return true;

    }

    public staticvoidmain(String[] args){

        Frame f = new ToeTest();

        f.resize(200, 100);

        f.show();

    }

} /// :~

6.2     執行

ToeButton 類保留了一個控制代碼到它ToeDialog 型的父類中。正如前面所述,ToeButton 和ToeDialog 高度的結合因為一個ToeButton 只能被一個ToeDialog 所使用,但它卻解決了一系列的問題,事實上這實在不是一個糟糕的解決方案因為沒有另外的可以記錄使用者選擇的對話類。當然我們可以使用其它的製造ToeDialog.turn(ToeButton 的靜態的一部分)方法。這種方法消除了它們的緊密聯絡,但卻阻止了我們一次擁有多個ToeDialog(無論如何,至少有一個正常地執行)。

paint()是一種與圖形有關的方法:它圍繞按鈕畫出矩形並畫出“X”或“O”。這完全是冗長的計算,但卻十分的直觀。

一個滑鼠單擊被過載的mouseDown()方法所俘獲,最要緊的是檢查是否有事件寫在按鈕上。如果沒有,父視窗會被詢問以找出誰選擇了它並用來確定按鈕的狀態。值得注意的是按鈕隨後交回到父類中並且改變它的選擇。如果按鈕已經顯示這為“X”和“O”,那麼它們會被改變狀態。我們能注意到本書第三章中描述的在這些計算中方便的使用的三個一組的If-else。當一個按鈕的狀態改變後,按鈕會被重畫。

ToeDialog 的構建器十分的簡單:它像我們所需要的一樣增加一些按鈕到GridLayout 佈局管理器中,然後調整每個按鈕每邊大小為50 個畫素(如果我們不調整視窗,那麼它就不會顯示出來)。注意handleEvent()正好為WINDOW_DESTROY 呼叫dispose(),因此整個應用程式不會被關閉。

ToeTest 設定整個應用程式以建立TextField(為輸入按鈕網格的行和列)和“go”按鈕。我們會領會action()在這個程式中使用不太令人滿意的“字串匹配”技術來測試按鈕的按下(請確定我們拼寫和大寫都是正確的!)。當按鈕按下時,TextField 中的資料將被取出,並且,因為它們在字串結構中,所以需要利用靜態的Integer.paresInt()方法來轉變成中斷。一旦對話類被建立,我們就必須呼叫show()方法來顯示和啟用它。

我們會注意到ToeDialog 物件賦值給一個對話控制代碼 d。這是一個上溯造型的例子,儘管它沒有真正地產生重要的差異,因為所有的事件都是show()呼叫的。但是,如果我們想呼叫ToeDialog 中已經存在的一些方法,我們需要對ToeDialog 控制代碼賦值,就不會在一個上溯中丟失資訊。

6.3     檔案對話類

在一些作業系統中擁有許多的特殊內建對話方塊去處理選擇的事件,例如:字型檔,顏色,印表機以及類似的事件。幾乎所有的作業系統都支援開啟和儲存檔案,但是,Java 的FileDialog 包更容易使用。當然這會不再檢測所有使用的程式片,因為程式片在本地磁碟上既不能讀也不能寫檔案。(這會在新的瀏覽器中交換程式片的信任關係。)

下面的應用程式運用了兩個檔案對話類的窗體,一個是開啟,一個是儲存。大多數的程式碼到如今已為我們所熟悉,而所有這些有趣的活動發生在兩個不同按鈕單擊事件的action()方法中。

6.3.1       程式碼

import java.awt.*;

public class FileDialogTest extends Frame {

    TextField filename = new TextField();

    TextField directory= newTextField();

    Button open = new Button("Open");

    Button save = new Button("Save");

    public FileDialogTest() {

        setTitle("File Dialog Test");

        Panel p = new Panel();

        p.setLayout(new FlowLayout());

        p.add(open);

        p.add(save);

        add("South", p);

        directory.setEditable(false);

        filename.setEditable(false);

        p = new Panel();

        p.setLayout(new GridLayout(2, 1));

        p.add(filename);

        p.add(directory);

        add("North", p);

    }

    public booleanhandleEvent(Event evt){

        if (evt.id== Event.WINDOW_DESTROY)

            System.exit(0);

        else

            return super.handleEvent(evt);

        return true;

    }

    public booleanaction(Event evt,Object arg){

        if (evt.target.equals(open)) {

            // Two arguments, defaults to open file:

            FileDialog d = new FileDialog(this, "Whatfile do you want to open?");

            d.setFile("*.java"); // Filename filter

            d.setDirectory("."); // Current directory

            d.show();

            String openFile;

            if ((openFile = d.getFile()) != null) {

                filename.setText(openFile);

                directory.setText(d.getDirectory());

            } else {

                filename.setText("You pressed cancel");

                directory.setText("");

            }

        } else if (evt.target.equals(save)) {

            FileDialog d = new FileDialog(this, "Whatfile do you want to save?", FileDialog.SAVE);

            d.setFile("*.java");

            d.setDirectory(".");

            d.show();

            String saveFile;

            if ((saveFile = d.getFile()) != null) {

                filename.setText(saveFile);

                directory.setText(d.getDirectory());

            } else {

                filename.setText("You pressed cancel");

                directory.setText("");

            }

        } else

            return super.action(evt, arg);

        return true;

    }

    public staticvoidmain(String[] args){

        Frame f = new FileDialogTest();

        f.resize(250, 110);

        f.show();

    }

} /// :~

6.3.2       執行

對一個“開啟檔案”對話方塊,我們使用構建器設定兩個自變數;首先是父視窗控制代碼,其次是FileDialog 標題條的標題。setFile()方法提供一個初始檔名--也許本地作業系統支援萬用字元,因此在這個例子中所有的.java 檔案最開頭會被顯示出來。setDirectory()方法選擇檔案決定開始的目錄(一般而言,作業系統允許使用者改變目錄)。

show()命令直到對話類關閉才返回。FileDialog 物件一直存在,因此我們可以從它那裡讀取資料。如果我們呼叫getFile()並且它返回空,這意味著使用者退出了對話類。檔名和呼叫getDirectory()方法的結果都顯示在TextFields 裡。

按鈕的儲存工作使用同樣的方法,除了因為FileDialog 而使用不同的構建器。這個構建器設定了三個自變數並且第三的一個自變數必須為FileDialog.SAVE 或FileDialog.OPEN。

再分享一下我老師大神的人工智慧教程吧。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智慧的隊伍中來!https://www.cnblogs.com/captainbed