溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

如何使用Swing動(dòng)態(tài)界面設(shè)計(jì)技術(shù)

發(fā)布時(shí)間:2021-11-03 13:53:03 來(lái)源:億速云 閱讀:187 作者:小新 欄目:編程語(yǔ)言

小編給大家分享一下如何使用Swing動(dòng)態(tài)界面設(shè)計(jì)技術(shù),相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

Swing 工具包提供各種用于創(chuàng)建用戶(hù)界面的工具和幾乎令人眼花繚亂的選項(xiàng),這些選項(xiàng)用于在程序生存期間修改界面。小心地使用這些功能可以導(dǎo)致界面能夠適應(yīng)用戶(hù)的需要并簡(jiǎn)化交互過(guò)程。粗心地使用同樣的功能可以導(dǎo)致非?;靵y或徹底不可用的程序。本文介紹Swng動(dòng)態(tài)界面設(shè)計(jì)的技術(shù)和體系,并提供有關(guān)構(gòu)建有效的界面的幫助。您將修改隨 Sun JDK 一起提供的基于 SwingSet2 示例應(yīng)用程序的源代碼;此應(yīng)用程序的 UI 使用許多動(dòng)態(tài)的特性并且可以作為理解它們的極好的起點(diǎn)。

禁用小部件

Swing動(dòng)態(tài)界面設(shè)計(jì)的最簡(jiǎn)單形式是使不可用的菜單項(xiàng)或按鈕變灰的 UI。禁用 UI 小部件與禁用所有小部件的方法都是相同的。setEnabled() 函數(shù)是 Component 類(lèi)的一個(gè)功能。清單 1 顯示了禁用按鈕的代碼:

清單 1. 禁用按鈕

1 button.setEnabled(false);

正如您看到的,十分簡(jiǎn)單。關(guān)鍵問(wèn)題是何時(shí)應(yīng)該 啟用或禁用一個(gè)按鈕。通常的設(shè)計(jì)決策是當(dāng)按鈕不可用時(shí)禁用它。例如,當(dāng)一個(gè)文件從上一次保存以來(lái)還沒(méi)有被修改時(shí),很多程序禁用 Save 按鈕(以及任何相應(yīng)的菜單項(xiàng))。

關(guān)于禁用按鈕的重要警告是要記住在適當(dāng)?shù)臅r(shí)候重新啟用它們。例如,如果在單擊按鈕和按鈕的動(dòng)作完成之間有一個(gè)確認(rèn)步驟,即使確認(rèn)失敗也應(yīng)該重新啟用按鈕。

調(diào)整范圍

有時(shí),應(yīng)用程序需要?jiǎng)討B(tài)地調(diào)整數(shù)值小部件的范圍,例如 Spinner 或者 Slider。這可能比它看起來(lái)要復(fù)雜許多。特別是 Slider 有二級(jí)功能 —— 刻度、刻度間隔和標(biāo)簽 —— 這些可能需要隨著范圍的調(diào)整而加以調(diào)整以避免災(zāi)難發(fā)生。

SwingSet2 示例沒(méi)有進(jìn)行任何一項(xiàng)調(diào)整,所以您需要通過(guò)把 ChangeListener 連接到一個(gè)可以修改其他滑塊的滑塊來(lái)修改它。輸入新的 SliderChangeListener 類(lèi), 如清單 2 所示:

清單 2. 更改滑塊的范圍

1 class SliderChangeListener implements ChangeListener {  2        JSlider h;  3   4        SliderChangeListener(JSlider h) {  5               this.h = h;  6        }  7   8        public void stateChanged(ChangeEvent e) {  9            JSlider js = (JSlider) e.getSource();  10            int i = js.getValue();  11            h.setMaximum(i);  12            h.repaint();  13        }  14 }

當(dāng)創(chuàng)建第三個(gè)水平滑塊時(shí)(最初示例中的滑塊在每個(gè)單位處帶有標(biāo)記,在 5、10 和 11 等處帶有標(biāo)簽), 另外還創(chuàng)建了一個(gè)新的 SliderChangeListener,它把滑塊作為構(gòu)造器參數(shù)傳遞。當(dāng)創(chuàng)建第三個(gè)垂直的滑塊(范圍從 0 到 100)時(shí),新的 SliderChangeListener 作為變更監(jiān)聽(tīng)器添加到它。這大致按預(yù)期的那樣工作:調(diào)整垂直滑塊改變了水平滑塊的范圍。

不幸的是,刻度和標(biāo)簽根本不能很好地工作。當(dāng)范圍變得不是太大時(shí),每五個(gè)刻度處的標(biāo)簽?zāi)苷_地工作,但是刻度 11 處的額外標(biāo)簽很快成為一個(gè)可用性問(wèn)題,如圖 1 所示:

圖 1. 一起運(yùn)行的標(biāo)簽

如何使用Swing動(dòng)態(tài)界面設(shè)計(jì)技術(shù)

更新刻度和標(biāo)簽

在Swing動(dòng)態(tài)界面設(shè)計(jì)時(shí),明顯的解決方案是,只要滑塊的***值被更新,就在水平滑塊上簡(jiǎn)單地設(shè)置刻度間隔,如清單 3 所示:

清單 3. 設(shè)置刻度間隔

1 // DOES NOT WORK 2 int tickMajor, tickMinor;  3 tickMajor = (i > 5) ? (i / 5) : 1;  4 tickMinor = (tickMajor > 2) ?  (tickMajor / 2) : tickMajor;  5 h.setMajorTickSpacing(tickMajor);  6 h.setMinorTickSpacing(tickMinor);  7 h.repaint();

目前清單 3 是正確的,但是它沒(méi)有引起畫(huà)在屏幕上的標(biāo)簽的任何變化。必須使用 setLabelTable() 分別設(shè)置標(biāo)簽。 添加額外一行修復(fù)它:

1 h.setLabelTable(h.createStandardLabels(tickMajor));

這仍然出現(xiàn)在刻度 11 處存在最初設(shè)置的標(biāo)簽這樣的錯(cuò)誤。當(dāng)然本來(lái)的意圖是想在滑塊的最右端始終有一個(gè)標(biāo)簽??梢酝ㄟ^(guò)刪除舊的標(biāo)簽(在設(shè)置新的***值之前)然后添加一個(gè)新的標(biāo)簽達(dá)到這一目的。此代碼 “幾乎” 可以工作:

清單 4. 替換標(biāo)簽

1 public void stateChanged(ChangeEvent e) {  2        JSlider js = (JSlider) e.getSource();  3        int i = js.getValue();  4   5        // clear old label for top value  6        h.getLabelTable().remove(h.getMaximum());  7   8        h.setMaximum(i);  9   10        int tickMajor, tickMinor;  11        tickMajor = (i > 5) ? (i / 5) : 1;  12        tickMinor = (tickMajor > 2) ? (tickMajor / 2) : tickMajor;  13        h.setMajorTickSpacing(tickMajor);  14        h.setMinorTickSpacing(tickMinor);  15        h.setLabelTable(h.createStandardLabels(tickMajor));  16        h.getLabelTable().put(new Integer(i),  17        new JLabel(new Integer(i).toString(), JLabel.CENTER));  18        h.repaint();  19 }

如果我已經(jīng)告訴過(guò)您一次,那么我就已經(jīng)告訴您兩次了。

我使用幾乎 的意思是,雖然清單 4 中的代碼刪除了刻度 11 處的標(biāo)簽,但是它沒(méi)有在刻度 i 處添加新標(biāo)簽;相反,只能看到間隔為 tickMajor 的標(biāo)簽。首先此解決方法相當(dāng)令人討厭:

清單 5. 強(qiáng)迫顯示更新

1 h.setLabelTable(h.getLabelTable());

這個(gè)看起來(lái)無(wú)意義的操作實(shí)際上有重大的作用。每當(dāng)設(shè)置標(biāo)簽表時(shí)就生成滑塊的標(biāo)簽。沒(méi)有為了修改對(duì)表進(jìn)行特殊回調(diào),所以添加到表中的新值不必產(chǎn)生效果;很顯然,清單 5 中的空操作具有使 Swing 知道它必須更新顯示的副作用。(以免您認(rèn)為這是我自己發(fā)明的,請(qǐng)注意最初的 SwingSet 代碼包括這樣一個(gè)調(diào)用。)

這只留下了一個(gè)問(wèn)題。標(biāo)簽出現(xiàn)在滑塊的末端這個(gè)非常合理的期望有時(shí)使兩個(gè)標(biāo)簽互相直接相鄰,乃至重疊,如圖 2 所示:

圖 2. 滑塊末端的重疊標(biāo)簽

如何使用Swing動(dòng)態(tài)界面設(shè)計(jì)技術(shù)

很多解決此問(wèn)題的方法都是可行的。編寫(xiě)自己的代碼以使用值來(lái)填充標(biāo)簽表并停止以前的序列以便序列中的***標(biāo)簽與滑塊的末端有一些隔離。我將把這個(gè)作為作業(yè)留給您。

在許多情況下,為了啟用和禁用菜單項(xiàng)而限制菜單修改是很實(shí)際的。此方法容易受到用于禁用項(xiàng)的常規(guī)警告的影響:避免由于偶然地禁用重要項(xiàng)而使程序處于不可用狀態(tài)。

添加或刪除菜單項(xiàng)或子菜單也是可能的。修改 JMenuBar 沒(méi)有這么容易;沒(méi)有從工具欄上刪除和替換單個(gè)菜單的接口。如果您想修改工具欄(而不是向最右端添加菜單),需要制作一個(gè)新的工具欄并用它替換舊的工具欄。

修改單個(gè)菜單會(huì)立即生效;您不必在將菜單連接到工具欄或另一個(gè)菜單之前構(gòu)建一個(gè)菜單。當(dāng)需要修改菜單選項(xiàng)的選擇時(shí),最容易的方法是修改選定的菜單。您可能仍然想添加或刪除完整的菜單,并且這么做并不是特別難。清單 6 顯示一個(gè)將菜單插入到菜單條中給定索引前的方法的簡(jiǎn)單示例。此示例假定要替換的 JMenuBar 連接到 JFrame 對(duì)象,但是任何能使您獲得和設(shè)置菜單條的東西的工作方式都是一樣的:

清單 6. 把一個(gè)菜單插入到菜單條中

1 public void insertMenu(JFrame frame, JMenu menu, int index) {  2        JMenuBar newBar = new JMenuBar();  3        JMenuBar oldBar = frame.getJMenuBar();  4        MenuElement[] oldMenus = oldBar.getSubElements();  5        int count = oldBar.getMenuCount();  6        int i;  7   8        for (i = 0; i < count; ++i) {  9               if (i == index)  10                      newBar.add(menu);  11               newBar.add((JMenu) oldMenus[i]);  12        }  13        frame.setJMenuBar(newBar);  14 }

上面的代碼我不是開(kāi)始時(shí)就試圖編成這樣;這是最終的版本,已經(jīng)很好地修復(fù)過(guò)了所以它能夠運(yùn)行并反映一些有趣的怪事。初看起來(lái),實(shí)現(xiàn)此功能的明顯方法似乎應(yīng)該是使用 getComponentAtIndex(),但是這種方法已經(jīng)受到了反對(duì)。幸運(yùn)的是,getSubElements() 已經(jīng)足夠好。為 newBar.add() 而進(jìn)行到 JMenu 的強(qiáng)制類(lèi)型轉(zhuǎn)換可能是安全的,但是我不喜歡這樣。getSubElements() 接口不僅對(duì)菜單條而且對(duì)菜單進(jìn)行操作,菜單可能具有幾種類(lèi)型的子元素,JMenu 是可以添加到 JMenuBar 的惟一元素。所以必須把元素強(qiáng)制轉(zhuǎn)換為 JMenu 以把它傳遞到 JMenuBar.add() 方法。不幸的是,如果將來(lái)的 API 修訂版允許將除 JMenu 類(lèi)型之外的元素添加到 JMenuBar,就不再需要把返回的元素強(qiáng)制轉(zhuǎn)換 JMenu了。

清單 6 中的代碼反映了另外一個(gè)相當(dāng)微妙的界面怪事;菜單數(shù)必須提前緩存起來(lái)。當(dāng)把菜單添加到新的菜單條時(shí),它們從舊的菜單條中被刪除。雖然看起來(lái)相似,但是清單 7 中的代碼不能工作,因?yàn)檠h(huán)提前結(jié)束了:

清單 7. 循環(huán)結(jié)束太早

1 // DOES NOT WORK 2 for (i = 0; i < oldBar.getMenuCount(); ++i) {  3        if (i == index)  4               newBar.add(menu);  5        newBar.add((JMenu) oldMenus[i]);  6 }

清單 7 中的循環(huán)只復(fù)制一半數(shù)量的菜單。例如,如果開(kāi)始菜單條上有 4 個(gè) 菜單,它復(fù)制前面的兩個(gè)菜單。復(fù)制完***個(gè)菜單以后, i 的值為 1 并且 getMenuCount() 返回 3;在復(fù)制完第二個(gè)菜單以后,i 的值為 2 并且 getMenuCount() 返回 2,因此循環(huán)結(jié)束。我沒(méi)有找到任何介紹通過(guò)向菜單條添加菜單從而從另一個(gè)菜單條刪除菜單這樣的特性的文檔,因此可能不是有意這樣。但是,它很容易達(dá)到這個(gè)目的。

從菜單條刪除菜單稍微容易一些;只是把所有其他的菜單從舊的菜單條復(fù)制到新的菜單條,就完成了刪除菜單。很容易!

如果界面使用了很多動(dòng)態(tài)菜單更新,創(chuàng)建一組菜單條并在它們之間切換而不是一直動(dòng)態(tài)地更新它們也許會(huì)更好一些。但是,如果如此快地改變菜單,可能會(huì)使用戶(hù)完全發(fā)瘋。

勘誤:在本文的草稿階段,我忽略了 JMenuBar 類(lèi)的繼承方法的列表。其實(shí),它有 remove 和 add 方法可以用來(lái)在指定的索引處進(jìn)行刪除和插入。另外一個(gè)教訓(xùn)是:查看繼承的方法而不僅僅是特定于類(lèi)的方法。

調(diào)整窗口大小

所幸的是對(duì)于大多數(shù)情況,窗口大小調(diào)整是自動(dòng)進(jìn)行的。但是需要考慮調(diào)整大小產(chǎn)生的一些影響。在非常小的窗口中,按鈕條、菜單條和類(lèi)似功能可能變成有問(wèn)題的。管理程序自身的圖形面板需要響應(yīng)調(diào)整大小事件。讓 Swing 處理對(duì) UI 元素的包裝,但是要密切注視組件的大小;不要獲取一次組件的尺寸然后就一直使用這些值。

更微妙的地方是,一些設(shè)計(jì)決策(例如滑塊上刻度的密度)可能被適度地更新以響應(yīng)窗口大小調(diào)整事件。100 像素寬度的滑塊不能具有像 400 像素寬度的滑塊那樣多的可讀標(biāo)簽。您可能想通過(guò)添加全新的有用功能來(lái)讓 UI 更進(jìn)一步用在大型顯示器上。

但是,在多數(shù)情況下,可以忽略窗口大小調(diào)整。您不應(yīng)該做的是不必要地阻止或重寫(xiě)它。布局代碼中的窗口一側(cè)的便捷工具不是必需的。最小的窗口大小可能是無(wú)可厚非的,但是要讓人們能把窗口調(diào)整到他們所需要的大小。

一般原則

Swing 工具包在Swing動(dòng)態(tài)界面設(shè)計(jì)方面提供了很大的靈活性。如果小心地使用,動(dòng)態(tài)更新界面的選項(xiàng)能夠極大地簡(jiǎn)化該界面;例如,只有應(yīng)用菜單的選項(xiàng)時(shí),用戶(hù)才能容易地顯示菜單。不幸的是,一些 API 的特性可能使此方法產(chǎn)生一些離奇的行為,并且副作用和相互影響并不總是像您期望的那樣記錄下來(lái)。如果您有使用動(dòng)態(tài)界面的想法,就要準(zhǔn)備在調(diào)試上花費(fèi)一些額外的時(shí)間。您可能從 Swing 庫(kù)的困境中走出來(lái)并發(fā)現(xiàn)自己需要處理出人意料的行為和/或 bug。

不要讓缺乏明顯的實(shí)現(xiàn)讓您氣餒。像本文的 JMenuBar 示例所顯示的,即使當(dāng) API 不支持某個(gè)任務(wù)時(shí),您也能自己實(shí)現(xiàn)它,雖然有一點(diǎn)間接。盡量不要走極端。當(dāng)動(dòng)態(tài) UI 讓用戶(hù)清楚它們的固有限制時(shí),它們才能***地發(fā)揮作用。理想的情況是,用戶(hù)甚至可能不會(huì)注意到界面變化。如果他們能夠使用程序的 Object 菜單的惟一時(shí)刻是當(dāng)他們使某個(gè)對(duì)象被選擇時(shí),那么其余的時(shí)間他們將不會(huì)介意不存在該菜單。

另一方面,如果存在這種可能性:用戶(hù)不能猜測(cè)出一個(gè)選項(xiàng)不可用的原因,這時(shí)讓用戶(hù)嘗試操作并獲得包含信息的消息也許會(huì)更好。這對(duì)于一些操作尤其重要。如果保存選項(xiàng)被禁用,而我想保存數(shù)據(jù),那么這不會(huì)發(fā)生作用。程序可能認(rèn)為已經(jīng)保存了數(shù)據(jù),但是為什么不讓我無(wú)論如何都保存它呢?如果存在不能保存文件的特殊原因,我可能想知道是什么原因。

盡管研究了很多年,界面設(shè)計(jì)在很多方面仍舊是一個(gè)較新的領(lǐng)域,只進(jìn)行了很少的試驗(yàn)。Swing動(dòng)態(tài)界面設(shè)計(jì)是一個(gè)很好的特性,能夠使 UI 更清晰、更簡(jiǎn)單和反應(yīng)更迅速。添加動(dòng)態(tài)特性需要從幾分鐘的工作到大量時(shí)間的花費(fèi)不等。

以上是“如何使用Swing動(dòng)態(tài)界面設(shè)計(jì)技術(shù)”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI