В пакете swing содержится ProgressMonitorStream, который автоматически отображает диалоговое окно с информацией о том, какая часть данных была уже прочитана из потока.
Использовать данный фильтр чрезвычайно просто. Следует лишь поместить ProgressMonitorInputStream в состав последовательности используемых потоков.
Предположим, например, что вам требуется читать текст из файла. Формирование последовательности начинается с потока FileInputStream:
1 |
FileInputStream in = new FileInputStream(f); |
Обычно этот поток преобразуют в InputStreamReader:
1 |
InputStreamReader reader = new InputStreamReader(in); |
Если же вы хотите отслеживать чтение данных из потока, нужно сначала преобразовать FileInputStream в ProgressMonitorInputStream:
1 |
ProgressMonitorInputStream = progressIn = new ProgressMonitorInputStream(parent, caption, in); |
Конструктору ProgressMonitorInputStream передается родительский компонент, заголовок и, конечно же, поток, предназначенный для отслеживания. Метод read() данного класса анализирует количество прочитанных байтов и обновляет содержимое диалогового окна.
Создав объект ProgressMonitorInputStream, можно продолжить формирование последовательности потоков:
1 |
InputStreamReader reader = new InputStreamReader(profressIn); |
На этом действия по организации мониторинга чтения данных заканчивается. При чтении из файла автоматически отобразится окно, показанное на рис.2.
Рис.2. Окно, позволяющее отслеживать процесс чтения данных из потока ввода.
Для определения общего количества байтов в потоке объект ProgressMonitorInputStream использует метод available() класса InputStream. Однако этот метод возвращает лишь число байтов, которые могут быть прочитаны без блокировки ввода. Таким образом, рассматриваемый здесь фильтр хорошо работает с файлами и данными, получаемыми посредством протокола HTTP, поскольку их размер известен заранее, однако следует помнить, что результаты его применения к другим потокам могут быть гораздо хуже.
Программа, код который увидите в конце урока, подсчитывает строки в файле. Если размеры файла велики, отображается диалоговое окно, отображающее ход выполнения задачи.
Если пользователь щелкает на кнопке Cancel, поток ввода закрывается. Поскольку код, обрабатывающий ввод, уже знает о том, что нужно делать при окончании ввода, для обработки отмены не нужна дополнительная логика.
Обратите внимание, что в данном примере использован не самый эффективный способ заполнения текстовой области. Программа работала бы гораздо быстрее, если бы мы сначала прочитали файл в StringBuilder, а затем связали текстовую область с контекстом.
Однако в данном случае медленное выполнение программы является преимуществом, так как главная задача примера — продемонстрировать индикацию хода выполнения задачи.
Чтобы избежать мерцания, мы не отображаем текстовую область до ее заполнения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import javax.swing.*; /** * Программа, демонстрирующая мониторинг процесса * чтения данных из файла. * @author pro-java.ru */ public class ProgressMonitorInputStreamTest { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { TextFrame frame = new TextFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }); } } /** * Фрейм, содержащий меню, которое позволяет задавать * команду загрузки текстового файла, и текстовую область, * предназначенную для отображения содержимого файла. * Текстовая область создается после загрузки файла и * по окончании загрузки файла устанавливается в качестве * области содержимого фрейма. Это позволяет избержать * мерцания в процессе загрузки файла. */ class TextFrame extends JFrame { public TextFrame() { setTitle("ProgressMonitorInputStreamTest"); setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); textArea = new JTextArea(); add(new JScrollPane(textArea)); chooser = new JFileChooser(); chooser.setCurrentDirectory(new File(".")); JMenuBar menuBar = new JMenuBar(); setJMenuBar(menuBar); JMenu fileMenu = new JMenu("File"); menuBar.add(fileMenu); openItem = new JMenuItem("Open"); openItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { try { openFile(); } catch(IOException exception) { exception.printStackTrace(); } } }); fileMenu.add(openItem); exitItem = new JMenuItem("Exit"); exitItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); } }); fileMenu.add(exitItem); } /** * Выбор файла для ввода, загрузка файла в текстовую * область и установка текстовой области в качестве * области содержимого фрейма. */ public void openFile() throws IOException { int r = chooser.showOpenDialog(this); if(r != JFileChooser.APPROVE_OPTION) return; final File f = chooser.getSelectedFile(); // Создание последовательности потоков // и включение в нее фильтра. FileInputStream fileIn = new FileInputStream(f); ProgressMonitorInputStream progressIn = new ProgressMonitorInputStream(this, "Reading " + f.getName(), fileIn); final Scanner in = new Scanner(progressIn); textArea.setText(""); SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() { protected Void doInBackground() throws Exception { while(in.hasNextLine()) { String line = in.nextLine(); textArea.append(line); textArea.append("\n"); } in.close(); return null; } }; worker.execute(); } private JMenuItem openItem; private JMenuItem exitItem; private JTextArea textArea; private JFileChooser chooser; public static final int DEFAULT_WIDTH = 300; public static final int DEFAULT_HEIGHT = 200; } |