Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 51 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
51
Dung lượng
451,13 KB
Nội dung
• Its abstract javax.swing.DefaultRowSorter<M, I> subclass, which supports sorting and filtering around a grid-based data model • The DefaultRowSorter<M, I>’s javax.swing.table.TableRowSorter<M extends TableModel> subclass, which provides table component sorting and filtering via a javax.swing.table.TableModel It is easy to introduce sorting to a table component. After creating the table’s model and initializing the table component with this model, pass the model to TableRowSorter<M extends TableModel>’s constructor. Then pass the resulting RowSorter<M> to JTable’s public void setRowSorter(RowSorter<? extends TableModel> sorter) method: TableModel model = JTable table = new JTable (model); RowSorter<TableModel> sorter = new TableRowSorter<TableModel> (model); table.setRowSorter (sorter); To demonstrate how easy it is to add sorting to your tables, I have designed a simple application that itemizes some grocery items and their prices in a two-column table. Listing 4-3 presents the source code. Listing 4-3. PriceList1.java // PriceList1.java import javax.swing.*; import javax.swing.table.*; public class PriceList1 extends JFrame { public PriceList1 (String title) { super (title); setDefaultCloseOperation (EXIT_ON_CLOSE); String [] columns = { "Item", "Price" }; Object [][] rows = { { "Bag of potatoes", 10.98 }, { "Magazine", 7.99 }, CHAPTER 4 ■ GUI TOOLKITS: SWING130 830-X CH04.qxd 8/30/07 6:52 PM Page 130 { "Can of soup", 0.89 }, { "DVD movie", 39.99 } }; TableModel model = new DefaultTableModel (rows, columns); JTable table = new JTable (model); RowSorter<TableModel> sorter = new TableRowSorter<TableModel> (model); table.setRowSorter (sorter); getContentPane ().add (new JScrollPane (table)); setSize (200, 150); setVisible (true); } public static void main (String [] args) { Runnable r = new Runnable () { public void run () { new PriceList1 ("Price List #1"); } }; java.awt.EventQueue.invokeLater (r); } } Run this application, and you will see output similar to the table shown in Figure 4-3. Figure 4-3. Unsorted table Click the Item column’s header, and the rows will sort in ascending order of this column’s values. A small up triangle will appear beside the column name to identify the current sort direction, as shown in Figure 4-4. CHAPTER 4 ■ GUI TOOLKITS: SWING 131 830-X CH04.qxd 8/30/07 6:52 PM Page 131 Figure 4-4. Sorted table based on the Item column Suppose that you decide to sort the items in ascending order of price (smallest price item first). After clicking the Price column’s header, a small up triangle appears beside the column name. However, as Figure 4-5 indicates, the sort result is unexpected. Figure 4-5. Prices are sorted based on string comparisons. The prices are not sorted based on their numerical values. If they were, the row con- taining DVD movie and 39.99 would be the last row in the table. Instead, rows have been sorted based on the string representations of the price values. According to the JDK documentation for the TableRowSorter<M extends TableModel> class, java.util.Comparator<T> comparators are used to compare objects during a sort. The Comparator<T> comparator that is chosen is based on five rules, which I have excerpted (with minor additions) from the documentation (in the top-down order in which the decision is made): • If a Comparator<T> has been specified for the column by the setComparator method, use it. • If the column class as returned by getColumnClass is String, use the Comparator<T> returned by Collator.getInstance(). • If the column class implements Comparable<T>, use a Comparator<T> that invokes the compareTo method. CHAPTER 4 ■ GUI TOOLKITS: SWING132 830-X CH04.qxd 8/30/07 6:52 PM Page 132 •If a javax.swing.table.TableStringConverter has been specified, use it to convert the values to Strings and then use the Comparator<T> returned by Collator.getInstance(). • Otherwise, use the Comparator<T> returned by Collator.getInstance() on the results from calling toString on the objects. If you explicitly attach a Comparator<T> to a column via public void setComparator(int column, Comparator<?> comparator), this Comparator<T> will be used during the sort (as indicated by the first rule). In contrast, it is easier to subclass javax.swing.table.DefaultTableModel and override the public Class getColumnClass(int column) method, which is what Listing 4-4 accomplishes. Listing 4-4. PriceList2.java // PriceList2.java import javax.swing.*; import javax.swing.table.*; public class PriceList2 extends JFrame { public PriceList2 (String title) { super (title); setDefaultCloseOperation (EXIT_ON_CLOSE); String [] columns = { "Item", "Price" }; Object [][] rows = { { "Bag of potatoes", 10.98 }, { "Magazine", 7.99 }, { "Can of soup", 0.89 }, { "DVD movie", 39.99 } }; TableModel model = new DefaultTableModel (rows, columns) { public Class getColumnClass (int column) { if (column >= 0 && CHAPTER 4 ■ GUI TOOLKITS: SWING 133 830-X CH04.qxd 8/30/07 6:52 PM Page 133 column <= getColumnCount ()) return getValueAt (0, column).getClass (); else return Object.class; } }; JTable table = new JTable (model); RowSorter<TableModel> sorter = new TableRowSorter<TableModel> (model); table.setRowSorter (sorter); getContentPane ().add (new JScrollPane (table)); setSize (200, 150); setVisible (true); } public static void main (String [] args) { Runnable r = new Runnable () { public void run () { new PriceList2 ("Price List #2"); } }; java.awt.EventQueue.invokeLater (r); } } DefaultTableModel always returns Object.class from its getColumnClass() method. According to the fifth rule, this results in the toString() method being called during sorting (and the result shown previously in Figure 4-5). By overriding getColumnClass() and having the overridden method return the appropriate type, sorting takes advantage of the returned Class object’s Comparable<T> (if there is one), according to the third rule. Figure 4-6 shows the properly sorted price list. CHAPTER 4 ■ GUI TOOLKITS: SWING134 830-X CH04.qxd 8/30/07 6:52 PM Page 134 Figure 4-6. The DVD movie item is now displayed in the last row, where it should be based on its price. ■Tip JTable’s public void setAutoCreateRowSorter(boolean autoCreateRowSorter) method offers the simplest way to attach a row sorter to a table component. For more information about this method, check out “Mustang (Java SE 6) Gallops into Town” ( http://www.informit.com/articles/ article.asp?p=661371&rl=1). Filtering the Table’s Rows The DefaultRowSorter<M, I> class provides a public void setRowFilter(RowFilter<? super M,? super I> filter) method for installing a filter that lets you determine which rows are displayed and which rows are hidden. You can pass to filter an instance of one of the filters returned from the static methods in the abstract javax.swing.RowFilter<M, I> class, or pass null to remove any installed filter and allow all rows to be displayed. Table 4-2 shows RowFilter’s filter factory methods. Table 4-2. RowFilter Filter Factory Methods Method Description public static <M,I> RowFilter<M,I> Returns a row filter that includes a row if all andFilter(Iterable<? extends RowFilter<? of the specified row filters include the row. super M,? super I>> filters) public static <M,I> RowFilter<M,I> Returns a row filter that includes only those rows dateFilter(RowFilter.ComparisonType that have at least one java.util.Date value in type, Date date, int indices) the indices columns that meets the criteria specified by type. If the indices argument is not specified, all columns are checked. public static <M,I> RowFilter<M,I> Returns a row filter that includes a row if the notFilter(RowFilter<M,I> filter) specified row filter does not include the row. public static <M,I> RowFilter<M,I> Returns a row filter that includes only those rows numberFilter(RowFilter.ComparisonType type, that have at least one Number value in the Number number, int indices) indices columns that meets the criteria specified by type. If the indices argument is not specified, all columns are checked. CHAPTER 4 ■ GUI TOOLKITS: SWING 135 Continued 830-X CH04.qxd 8/30/07 6:52 PM Page 135 public static <M,I> RowFilter<M,I> Returns a row filter that includes a row if any of orFilter(Iterable<? extends the specified row filters include the row. RowFilter<? super M,? super I>> filters) public static <M,I> RowFilter<M,I> Returns a row filter that uses a regular expression regexFilter(String regex, int indices) to determine which rows to include. Each column identified by indices is checked. The row is returned if one of the column values matches the regex. If the indices argument is not specified, all columns are checked. Row filtering is useful in the context of a database application. Rather than submit a potentially expensive SQL SELECT statement to the database management system to retrieve a subset of a table’s rows based on some criteria, filtering rows via a table compo- nent and its row filter is bound to be much faster. For example, suppose your table component presents a log of bugs (bug identifier, description, date filed, and dated fixed) found while testing software. Furthermore, suppose that you want to present only those rows whose description matches some entered regular expression. Listing 4-5 presents an application that accomplishes this task. Listing 4-5. BugLog.java // BugLog.java import java.awt.*; import java.awt.event.*; import java.text.*; import java.util.*; import javax.swing.*; import javax.swing.table.*; public class BugLog extends JFrame { private static DateFormat df; public BugLog (String title) throws ParseException CHAPTER 4 ■ GUI TOOLKITS: SWING136 Table 4-2. Continued Method Description 830-X CH04.qxd 8/30/07 6:52 PM Page 136 { super (title); setDefaultCloseOperation (EXIT_ON_CLOSE); String [] columns = { "Bug ID", "Description", "Date Filed", "Date Fixed" }; df = DateFormat.getDateTimeInstance (DateFormat.SHORT, DateFormat.SHORT, Locale.US); Object [][] rows = { { 1000, "Crash during file read", df.parse ("2/10/07 10:12 am"), df.parse ("2/11/07 11:15 pm") }, { 1020, "GUI not repainted", df.parse ("3/5/07 6:00 pm"), df.parse ("3/8/07 3:00 am") }, { 1025, "File not found exception", df.parse ("1/18/07 9:30 am"), df.parse ("1/22/07 4:13 pm") } }; TableModel model = new DefaultTableModel (rows, columns); JTable table = new JTable (model); final TableRowSorter<TableModel> sorter; sorter = new TableRowSorter<TableModel> (model); table.setRowSorter (sorter); getContentPane ().add (new JScrollPane (table)); JPanel pnl = new JPanel (); pnl.add (new JLabel ("Filter expression:")); final JTextField txtFE = new JTextField (25); pnl.add (txtFE); JButton btnSetFE = new JButton ("Set Filter Expression"); ActionListener al; al = new ActionListener () { public void actionPerformed (ActionEvent e) { String expr = txtFE.getText (); sorter.setRowFilter (RowFilter.regexFilter (expr)); sorter.setSortKeys (null); } }; CHAPTER 4 ■ GUI TOOLKITS: SWING 137 830-X CH04.qxd 8/30/07 6:52 PM Page 137 btnSetFE.addActionListener (al); pnl.add (btnSetFE); getContentPane ().add (pnl, BorderLayout.SOUTH); setSize (750, 150); setVisible (true); } public static void main (String [] args) { Runnable r = new Runnable () { public void run () { try { new BugLog ("Bug Log"); } catch (ParseException pe) { JOptionPane.showMessageDialog (null, pe.getMessage ()); System.exit (1); } } }; EventQueue.invokeLater (r); } } Run this application, specify [F|f]ile as a regular expression, and click the Set Filter Expression button. In response, only the first and third rows will be displayed. To restore all rows, simply leave the text field blank and click the button. ■Note The sorter.setSortKeys (null); method call unsorts the view of the underlying model after changing the row filter. In other words, if you have performed a sort by clicking some column header, the sorted view will revert to the unsorted view following this method call. CHAPTER 4 ■ GUI TOOLKITS: SWING138 830-X CH04.qxd 8/30/07 6:52 PM Page 138 Look and Feel Enhancements Unlike other Java-based GUI toolkits, Swing decouples its API from the underlying plat- form’s windowing system toolkit. This decoupling has resulted in trade-offs between platform independence and the faithful reproduction of a native windowing system’s look and feel. Because of user demand for the best possible fidelity of system look and feels, Java SE 6 improves the Windows look and feel and the GTK look and feel by allow- ing them to use the native widget rasterizer to render Swing’s widgets (components, if you prefer). Starting with Java SE 6, Sun engineers have reimplemented the Windows look and feel to use UxTheme, a Windows API hammered out between Microsoft and the author of the popular WindowBlinds ( http://www.stardock.com/products/windowblinds/) theming engine. This API exposes the manner in which Windows controls are rendered. As a result, a Swing application running under Windows XP should look like XP; when this application runs under Windows Vista, it should look like Vista. For completeness, the original Windows look and feel is still available as the Windows Classic look and feel ( com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel). Sun engineers have also reimplemented the GTK look and feel to employ native calls to GIMP Toolkit (GTK) engines, which makes it possible to reuse any installed GTK engine to render Swing components. If you are running Linux or Solaris, you can now use all of your favorite GTK themes to render Swing applications and make these applications integrate (visually) nicely with the desktop. New SwingWorker Multithreaded Swing programs can include long-running tasks, such as a task that per- forms an exhaustive database search across a network. If these tasks are run on the event-dispatching thread (the thread that dispatches events to GUI listeners), the application will become unresponsive. For this reason, a task must run on a worker thread, which is also known as a background thread. When the task completes and the GUI needs to be updated, the worker thread must make sure that the GUI is updated on the event-dispatching thread, because most Swing methods are not thread-safe. Although the javax.swing.SwingUtilities public static void invokeLater(Runnable doRun) and public static void invokeAndWait(Runnable doRun) methods (or their java.awt. EventQueue counterparts) could be used for this purpose, it is easier to work with Java SE 6’s new javax.swing.SwingWorker<T, V> class, because this class takes care of interthread communication. CHAPTER 4 ■ GUI TOOLKITS: SWING 139 830-X CH04.qxd 8/30/07 6:52 PM Page 139 [...]... result Listing 4 -6 presents the application’s source code 830-X CH 04. qxd 8/30/07 6: 52 PM Page 141 CHAPTER 4 ■ GUI TOOLKITS: SWING Listing 4 -6 PrimeCheck .java // PrimeCheck .java import java. awt.*; import java. awt.event.*; import java. math.*; import java. util.concurrent.*; import javax.swing.*; public class PrimeCheck extends JFrame { public PrimeCheck () { super ("Prime Check"); setDefaultCloseOperation... null, null, true) I took advantage of public boolean print() to add a printing capability to the web browser application shown earlier (in Listing 4- 1) The revised application’s source code is shown in Listing 4- 7 Listing 4- 7 BrowserWithPrint .java // BrowserWithPrint .java import java. awt.*; import java. awt.event.*; import java. awt.print.*; import java. io.*; import javax.swing.*; import javax.swing.event.*;... Japanese Imperial Era calendar Locale-Sensitive Services Are you tired of waiting for Sun to implement a specific locale that is important to your application? If so, you’ll want to check out the locale-sensitive services This new Java SE 6 feature consists of Service Provider Interface (SPI) classes that let you plug localedependent data and services into Java Service Provider Interface Classes The... ()); } } }; txtURL.addActionListener (al); setSize (300, 300); setVisible (true); } void addTab () { JEditorPane ep = new JEditorPane (); ep.setEditable (false); ep.addHyperlinkListener (this); tp.addTab (null, new JScrollPane (ep)); JButton tabCloseButton = new JButton (ii); tabCloseButton.setActionCommand (""+tabCounter); tabCloseButton.setPreferredSize (iiSize); ActionListener al; al = new ActionListener... possible to lay out a GUI using springs and struts Although this layout manager predates Java SE 6, it has suffered from bugs such as not always correctly resolving its constraints Java SE 6 fixes this bug by basing the algorithm used to calculate springs on the last two specified springs, along each axis Java SE 6 has also greatly improved drag-and-drop for Swing components These improvements have to do... domain names (see Chapter 8), to these i18n-specific features: • Japanese Imperial Era calendar • Locale-sensitive services • New locales • Normalizer API • ResourceBundle enhancements Japanese Imperial Era Calendar Many Japanese commonly use the Gregorian calendar Because Japanese governments also use the Japanese Imperial Era calendar for various government documents, Java SE 6 introduces support for... = tp.getSelectedComponent (); JScrollPane sp = (JScrollPane) c; c = sp.getViewport ().getView (); JEditorPane ep = (JEditorPane) c; ep.setPage (ae.getActionCommand ()); 147 830-X CH 04. qxd 148 8/30/07 6: 52 PM Page 148 CHAPTER 4 ■ GUI TOOLKITS: SWING miPrint.setEnabled (true); } catch (Exception e) { lblStatus.setText ("Browser problem: "+e.getMessage ()); } } }; txtURL.addActionListener (al); setSize... pass null to footerFormat if there is no footer • If you would like to display a print dialog (unless headless mode is in effect) that lets the user change printing attributes or cancel printing, pass true to showPrintDialog 830-X CH 04. qxd 8/30/07 6: 52 PM Page 145 CHAPTER 4 ■ GUI TOOLKITS: SWING • Specify the initial javax.print.PrintService for the print dialog via service Pass null to use the default... Modify BrowseWithPrint .java (Listing 4- 7) to work with PrintRequestAttributeSet 151 830-X CH 04. qxd 8/30/07 6: 52 PM Page 152 830-X CH05.qxd 9/18/07 9:23 PM CHAPTER Page 153 5 Internationalization J ava SE 6 s internationalization (i18n) support ranges from Abstract Windowing Toolkit-oriented non-English locale input bug fixes (see Chapter 3), to network-oriented internationalized domain names (see Chapter... its currencyCode and locale arguments is null 163 830-X CH05.qxd 1 64 9/18/07 9:23 PM Page 1 64 CHAPTER 5 ■ INTERNATIONALIZATION Let’s introduce this currency name provider to Java This task divides into two subtasks: Create the JAR file This JAR file must include CurrencyNameProviderImpl.class and a META-INF directory whose services subdirectory stores java. util.spi CurrencyNameProvider This file must . Listing 4 -6 presents the application’s source code. CHAPTER 4 ■ GUI TOOLKITS: SWING 140 830-X CH 04. qxd 8/30/07 6: 52 PM Page 140 Listing 4 -6. PrimeCheck .java // PrimeCheck .java import java. awt.*; import. true to showPrintDialog. CHAPTER 4 ■ GUI TOOLKITS: SWING 144 830-X CH 04. qxd 8/30/07 6: 52 PM Page 144 • Specify the initial javax.print.PrintService for the print dialog via service. Pass null to. Listing 4- 7. Listing 4- 7. BrowserWithPrint .java // BrowserWithPrint .java import java. awt.*; import java. awt.event.*; import java. awt.print.*; import java. io.*; import javax.swing.*; import javax.swing.event.*; public