пятница, 21 мая 2010 г.

Java проблемы новичка. Часть 1

Маленькая предыстория.
Давно хотел написать программу? которая поможет отвлечься от компьютера на пару минут. Основная её задача - сообщить пользователю о том, что необходимо отвлечься от компьютера и отдохнуть.

Требования к проекту:
  • Программу можно закрывать только в рабочий период;
  • Программа не должна сворачиваться в период отдыха.

Далее я опишу проблемы возникшие при работе над проектом. Проект разрабатывается на Java, место его расположения здесь, используемое IDE - Eclipse.

Поехали.

Работа с циклом for, for-each

В процессе работы часто бывает нужно последовательно перебрать все элементы массива.
Существует простая возможность прямого перебора всех элементов массива или коллекции. Это перебор "for-each", конструкция цикла не меняется для коллекций и массивов.
for (type var : arr) {
        body-of-loop
    }

где:
     type - тип возвращаемой переменной var
      arr - массив или коллекция которую перебирают

Рабочий пример:
int[] J = {1,2,3,4};
for (int i : J){
    System.out.println(i);
}



Стандартный перебор массива циклом for  будет такой:
for (int i = 0; i < arr.length; i++) {
        type var = arr[i];
        body-of-loop
    }
А для коллекции это выглядело бы вот так:
for (Iterator iter = coll.iterator(); iter.hasNext(); ) {
        type var = iter.next();
        body-of-loop
    }

Диалоговое окно

Здесь вроде проблем нет, но лично я всё время забываю, как это делать. Думаю, будет полезно записать лишний раз.
Итак простая команда:

JOptionPane.showMessageDialog(frame, "Сообщение");

где:
    frame - это форма от которой построено диалоговое окно
    "Сообщение" - это сообщение окна.

На всякий случай стоит сказать, что для использования JOptionPane необходимо импортировать javax.swing.JOptionPane, а то вдруг IDE не подскажет.

Остановка выполнения потока. Команда "sleep"

Иногда бывает необходимо остановить выполнение программы на определённый период. Делается это очень просто, главное запомнить в каком классе описан метод "sleep"
try {
        Thread.sleep(10000); // ждать 10 секунд
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
где:
     10000 - количество миллисекунд.
     e.printStackTrace(); - выведет в консоль приложения стек ошибки.

Внешний вид JFrame, развёртывание окна на весь экран, отключение пиктограмм работы с окном.

Уж не знаю насколько корректно назван данный раздел, задача заключается в том, что нужно отключить кнопки сворачивания и разворачивания окна, а так же возможность его закрыть нажав "х".
Над решением данной проблемы пришлось повозиться.
Для того, что бы построить форму максимального размера (раскрыть на весь экран) обычно советуют следующее:
JFrame fr = new JFrame(); // создание окна
    fr.setVisible(true); // показать окно
    fr.setExtendedState(fr.getExtendedState() | fr.MAXIMIZED_BOTH); // сделать окно максимальных размеров
    fr.setAlwaysOnTop(true); // оставить окно поверх всех окон
Всё вроде просто и понятно. Но у меня не работало. Возможно были проблемы с операционкой, но окно не разворачивалось на весь экран.

Так что пришлось пойти другим путём:
JFrame fr = new JFrame(); // создание окна
    fr.setAlwaysOnTop(true); // оставляем всегда на верху
    fr.setSize(Toolkit.getDefaultToolkit().getScreenSize()); // получаем разрешение экрана и устанавливаем максимальный размеры для окна
    fr.setVisible(true); // показываем окно  
Результат выполнения этого кода меня устраивает гораздо больше.

Для отключения кнопок окна и запрета его закрытия можно дописать следующие строчки:
fr.setDefaultCloseOperation(fr.DO_NOTHING_ON_CLOSE); // отключает возможность закрыть окно при нажатии "х"
    fr.setUndecorated(true); // убираем заголовок окна и 3 кнопки работы с окном

Воспроизведение звукового .wav файла.

Механизм воспроизведения звукового файла оказался проще, чем показалось на первый взгляд.
Обычно, в случаях когда нужно подать звуковой сигнал, я использовал BEEP, но с удивлением узнал, что домашний ноутбук, такой радостью не обладает. Так-что пришлось осваивать:
private void PlaySound() throws UnsupportedAudioFileException, IOException, LineUnavailableException {
        // процедура проигрывающая один заранее заданный файл
        AudioInputStream stream = AudioSystem.getAudioInputStream(new File("womp.wav")); // создаём аудио поток из файла
        DataLine.Info info = new DataLine.Info(Clip.class, stream.getFormat()); // получаем информацию о звуке из потока
        Clip clip = (Clip) AudioSystem.getLine(info); // инициализируем проигрыватель
        clip.open(stream); // воспроизводим файл
        clip.start(); // закрываем проигрыватель
    }
где:
      "womp.wav" - звуковой файл находящийся в одном каталоге с исполняемым файлом
      для работы со звуком необходим пакет javax.sound.sampled.*

Работа с XML.

Работа с XML документами является очень большой и сложной темой. Но я не могу пройти мимо и хочу оставить маленький пример, так сказать на будущее.

Чтение XML файла простого формата

Пример обрабатывает простой файл следующего формата:
<Head>
   <tag1>value</tag1>
   ...
   <tagN>value</tagN>
</Head>

В примере ниже идёт обращение по именам тегов:
try {
        Document sets = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(path); // загружаем заданный файл
        Element EyeSets = sets.getDocumentElement(); // получаем элементы документа

        // получаем значение по именам
        String sWaitPeriod = EyeSets.getElementsByTagName("tag1").item(0).getFirstChild().getNodeValue();
        String sPracticePeriod = EyeSets.getElementsByTagName("tag2").item(0).getFirstChild().getNodeValue();
        String sPracticeCount = EyeSets.getElementsByTagName("tag3").item(0).getFirstChild().getNodeValue();
       
        // преобразование полученных данных в требуемый формат   
        try {
            waitPeriod = Integer.parseInt(sWaitPeriod);
            practicePeriod = Integer.parseInt(sPracticePeriod);
            practiceCount = Integer.parseInt(sPracticeCount);
        } catch (NumberFormatException nfe) {
            JOptionPane.showMessageDialog(null, nfe.getMessage());
            System.err.println(nfe.getMessage());
        }    
    } catch (Exception e){
        System.err.println(e.getMessage());
    }
где:

      path - входная переменная задающая место расположение обрабатываемого файла 
      Integer.parseInt - преобразует строку в число

Загрузка XML файла простого формата

Сохранённый выше файл можно загрузить с помощью примера ниже:

// задаём экземпляр потока выходного файла
    FileOutputStream fo = null;
    try {
        // создаём экземпляр с заданным именем файла
        fo = new FileOutputStream(path);
    } catch (FileNotFoundException e1) {
        e1.printStackTrace();
    }

    // создание экземпляра класса? генерирующего xml файл в указанный выходной поток
    XMLStreamWriter writer;
        try {
            writer = XMLOutputFactory.newInstance().createXMLStreamWriter(fo);
            try {
                 // начало создания файла  
                 writer.writeStartDocument();
                 // основной тег
                 writer.writeStartElement("setings");
                     // открытие узла  
                     writer.writeStartElement("tag1");
                        //запись значения узла
                         writer.writeCharacters(Integer.toString(waitPeriod));
                     // закрытие узла
                     writer.writeEndElement();
                     writer.writeStartElement("tag2");
                         writer.writeCharacters(Integer.toString(practicePeriod));
                     writer.writeEndElement();
                     writer.writeStartElement("tag3");
                         writer.writeCharacters(Integer.toString(practiceCount));
                     writer.writeEndElement();
                 // закрываем все открытые узлы
                 writer.writeEndDocument();
                }
            finally {
                // закрываем класс
                writer.close();
                }
        } catch (XMLStreamException e) {
            e.printStackTrace();
        }
где:
      path - путь где будет сохранён файл

Стоит отметить что существует 2 технологии работы с XML:

  •  SAX (Simple API for XML); 
  •  DOM (Document Object Model).

В примерах выше вся работа велась через DOM объекты

Сериализация объектов в бинарный файл

Выше был приведён пример сохранения требуемых данных в XML файл. Но это не единственный способ сохранения состояний объектов. Существует возможность сериализации объекта в файл - для последующей его передачи куда либо или сохранения его текущих состояний.

Необходимо помнить? что сериализуемые объекты должны наследовать интерфейс java.io.Serializable.

Функция для сохранения объекта очень проста:

public void writeSets(Vector<eyesset>  ser) throws Exception{
        System.out.println("write objects:");
        FileOutputStream fos=new FileOutputStream("output.bin");
        ObjectOutputStream oos=new ObjectOutputStream(fos);
        oos.writeObject(ser);
        oos.close();
    }
где:
     Vector<eyesset> - коллекция объектов для сериализации.(может быть любым другим объектом реализующим интерфейс Serializable)

Для десериализации объекта можно использовать следующий пример:

/**
     * Функция загружает настройки из сериализованного файла
     * Изменяет список всех упражнений в текущем классе
     *
     * @return     список всех упражнений
     * @throws Exception    FileNotFoundException - ошибка отсутствия загружаемого файла
     *                         EOFException    - ошибка обработки потока (поток достиг своего конца)
     *     
     */
    public Vector<eyesset> readSets() throws Exception {
        // загружаем все упражнения из сохранённого файла
        
        File f = new File("output.bin");
        if (f.exists()) {
        
        System.out.println("Read objects:");
        FileInputStream fis=new FileInputStream("output.bin");
        ObjectInputStream ois=new ObjectInputStream(fis);

        Vector<eyesset> readObject = (Vector<eyesset>)ois.readObject();
        ois.close();
        return readObject;
        } else {return new Vector();} 

Выше, перед функцией, можно увидеть пример документирования кода ( JavaDoc). О том как писать JavaDoc можно почитать вот здес здесь

Переопределение отображения JList.

Довольно часто в списках JList требуется отображать только определённые данные из объекта. У программиста есть 2 варианта:
  • или создавать массив строк с готовыми данными для отображения, что на мой взгляд является не корректно; 
  • или переопределить отрисовку данных.
Если придерживаться 2ого варианта, то можно посмотреть пример ниже:
jlSet.setCellRenderer(new javax.swing.DefaultListCellRenderer(){
        public Component getListCellRendererComponent(JList list,
            Object value,
            int index, boolean isSelected,
            boolean cellHasFocus) {
                    
                Property prop = (Property)value;
                setText(prop.getDisplayCoordinat());
                return this;
                }
            });
Почитать первоисточник можно вот здесь


Ну вот кажется и всё.
Ждём продолжения, поскольку проект ещё не закончен и впереди показались проблемы, которые требуют своего решения.
Надеюсь на комментарии.

Продолжение часть 2

1 комментарий: