понедельник, 28 мая 2012 г.

Моя первая библиотека, или Сложные Диалоги в Java (SWING)

Добрый вечер. Сегодня я хочу предоставить на ваш суд свою первую библиотеку. Прошу не судить строго, но буду рад конструктивной критике и предоложениям по развитию. Если же кто-то захочет помочь в модификации, буду только счастлив. Теперь про библиотеку. Она позволяет вам создавать сложные диалоговые окна. Что это? Зачем? Стандартная библиотека позволяет создавать простые диалоговые окна(выбор Да\Нет, ввод строки, выдор варианта из списка). А что, если вам требуется диалоговое окно, результатом котого будет целый объект. Например, Диалоговое окно выбора места на карте, а результатом будет объект адреса, на котоый кликнул пользователь. Вот, с помошью моей библиотеки, вы сможете это сделать быстро и просто.
Скачать сие творение можно от сюда ComplexDialog . Там же можно присоедениться к разработке.

Теперь про то, как ей пользоваться. Подключив библиотеку(Есть много способов это сделать. Зависит от среды разработки и ваших вкусовых предпочтений) ,вы получаете 2 класса и 1 интерфейс.

Интрфейс ObjectFactory имеет один единственный метод, который возврашает значение указаного вами типа.

Первый класс - ComplexDialog. Это, непосредственно ,класс диалога. Он насделуется от JDialog .Для работы с ним, вы должны указать тип который диалог обязан вернуть. После вызова его конструктора и вызова метода show\setVisible(true) ,можно смело вызывать метод getReturnedStatus(), который вернёт сформированный пользователем объект. Программа приостановится, пока пользователь не нажмёт на OK или Canel. Если Пользователь жмёт Canel, getReturnedStatus возвращает нулевой указатель.

Для того, чтоб детальнее понять ComplexDialog, стоит изучить FactoryPanel. Это абстрактный класс. Он абсолютно пустой. Наследуется от JPanel и реализует интерфейс ObjectFactory. От него вам стоит наслелдовать панель, в которой будет вестись настройка вашего объекта. Обратите внимание, класс нетипизирован и дать ему тип вы должны такой же, как и в ComplexDialog. Потому что ComplexDialog использует метод getResult() для возвращения значения. На самом деле, вы можете создать панельку и в визуальном редакторе, но потом подредактировать код, изменив extends высшей панельки на FactoryPanel, и реализовать метод getResult(),как того требует интерфейс ObjectFactory.

Для создания ComplexDialog нужно:
JFrame parent - окно -родитель(осталось от JDialog);
FactoryPanel mainpanel - панелька, в которой происходит настройка необходимого вам типа; 
String title - надпись на окошке (осталось от JDialog) ; 
boolean modal - модальность окна (осталось от JDialog); 


Для более наглядного объяснения, хочу привести пример практического использования этой библиотеки. Одним из самых простых и достаточно часто необхидимых примеров ,наверное, будет DatePicker, то есть Диалог, в котором пользователь выберет какую- либо дату. 


Наш "ХелоВорлд" будет просить пользователя ввести дату его рождения и подсчитает количество дней прожитых пользователем. Для начала, Создадим формочку, которая наследуется от FactoryPanel. Для подтверждения своих слов, я создал её в визуальном редакторе, позже реализовал внутри необходимый функционал и реазиловал метод getResult() ,который возвращает объект типа java.util.Date, сформированный пользователем. FactoryPanel:
package alexkutsan_blogspot;

import complexdialog.FactoryPanel;
import java.util.Calendar;
import java.util.Date;
import javax.swing.DefaultComboBoxModel;
/**
 *
 * @author alex
 */

public class DatePickerPanel extends FactoryPanel<Date>{

    /**
     * Creates new form DatePickerPanel
     */
    public DatePickerPanel() {
        super();
        initComponents();
        myinitUI();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        YearBox = new javax.swing.JComboBox();
        MonthBox = new javax.swing.JComboBox();
        DayBox = new javax.swing.JComboBox();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();

        YearBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
        YearBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                YearBoxActionPerformed(evt);
            }
        });

        MonthBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
        MonthBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                MonthBoxActionPerformed(evt);
            }
        });

        DayBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));

        jLabel1.setText("%u0413%u043E%u0434");

        jLabel2.setText("%u041C%u0435%u0441%u044F%u0446");

        jLabel3.setText("%u0414%u0435%u043D%u044C");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(YearBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(6, 6, 6)
                .addComponent(jLabel2)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(MonthBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel3)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(DayBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(27, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(25, 25, 25)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(YearBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(MonthBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(DayBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel1)
                    .addComponent(jLabel2)
                    .addComponent(jLabel3))
                .addContainerGap(35, Short.MAX_VALUE))
        );
    }// </editor-fold>

    private void YearBoxActionPerformed(java.awt.event.ActionEvent evt) {                                        
       int m = MonthBox.getSelectedIndex()   1;
                    int y = YearBox.getSelectedIndex()   (Calendar.getInstance().get(Calendar.YEAR) - CountYears);
                    int n = m != 2 ? m > 7 ? 30   m % 2 == 1 ? 0 : 1 : 30   m % 2 : y % 4 == 0 && y % 100 != 0 || y % 400 == 0 ? 29 : 28;
                    Integer[] Days = new Integer[n];
                    for (int i = 1; i < n   1; i  ) {
                        Days[i - 1] = i;
                    }
                    DayBox.setModel(new DefaultComboBoxModel(Days));
    }                                       

    private void MonthBoxActionPerformed(java.awt.event.ActionEvent evt) {                                         
                    int m = MonthBox.getSelectedIndex()   1;
                    int y = YearBox.getSelectedIndex()   (Calendar.getInstance().get(Calendar.YEAR) - CountYears);
                    int n = m != 2 ? m > 7 ? 30   m % 2 == 1 ? 0 : 1 : 30   m % 2 : y % 4 == 0 && y % 100 != 0 || y % 400 == 0 ? 29 : 28;
                    Integer[] Days = new Integer[n];
                    for (int i = 1; i < n   1; i  ) {
                        Days[i - 1] = i;
                    }
                    DayBox.setModel(new DefaultComboBoxModel(Days));
    }                                        

    // Variables declaration - do not modify
    private javax.swing.JComboBox DayBox;
    private javax.swing.JComboBox MonthBox;
    private javax.swing.JComboBox YearBox;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    // End of variables declaration

    private void myinitUI() {
     Date cur = new Date(System.currentTimeMillis());
            int curentYear = Calendar.getInstance().get(Calendar.YEAR);
            Integer[] Years = new Integer[CountYears];
            for (int i = curentYear - CountYears; i < curentYear; i  ) {
                Years[i - (curentYear - CountYears)] = i;
            }
            Integer[] Month = new Integer[12];
            for (int i = 1; i <= 12; i  ) {
                Month[i - 1] = i;
            }
            Integer[] Data31 = new Integer[31];
            Integer[] Data30 = new Integer[30];
            Integer[] Data29 = new Integer[29];
            Integer[] Data28 = new Integer[28];
            for (int i = 1; i < 32; i  ) {
                if (i < 31) {
                    Data30[i - 1] = i;
                }
                if (i < 30) {
                    Data29[i - 1] = i;
                }
                if (i < 29) {
                    Data28[i - 1] = i;
                }
                Data31[i - 1] = i;
            }
            int m = Month[0];
            int y =Years[0];
            int n = m != 2 ? m > 7 ? 30   m % 2 == 1 ? 0 : 1 : 30   m % 2 : y % 4 == 0 && y % 100 != 0 || y % 400 == 0 ? 29 : 28;
            Integer[] Days = new Integer[n];
            for (int i = 1; i < n   1; i  ) {
                Days[i - 1] = i;
            }
            YearBox.setModel(new DefaultComboBoxModel(Years));
            MonthBox.setModel(new DefaultComboBoxModel(Month));
            DayBox.setModel(new DefaultComboBoxModel(Days));
    }
    int CountYears = 100;

    @Override
    public Date getResult() {
            Integer y =(Integer) YearBox.getSelectedItem();
            Integer m =(Integer) MonthBox.getSelectedItem();
            Integer d =(Integer) DayBox.getSelectedItem();
            Date date = new Date(y-1900, m-1, d);
            return date;
    }
}
Вот такая панелька
У неё незамысловатый функционал .Внимания заслуживает лишь моя функция рассчёта количества дней в месяце.
 int n = m != 2 ? m > 7 ? 30 m % 2 == 1 ? 0 : 1 : 30 m % 2 : y % 4 == 0 && y % 100 != 0 || y % 400 == 0 ? 29 : 28; где 
m - месяц(1..12)
y - год.

 Как видите, в функции getResult() формируется и возвращается Дата, введённая пользователем. Теперь главный класс:
 


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package alexkutsan_blogspot;
import complexdialog.ComplexDialog;
import java.io.IOException;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import javax.swing.JOptionPane;
/**
 *
 * @author alex
 */
public class Alexkutsan_blogspot {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws ParseException, IOException, URISyntaxException {


        // Date today = new Date(System.currentTimeMillis());
        Date today = new Date(System.currentTimeMillis());
        DatePickerPanel p = new DatePickerPanel();
        ComplexDialog<Date> dp = new ComplexDialog<Date>(null,p,"%u0412%u0432%u0435%u0434%u0438%u0442%u0435 %u0434%u0430%u0442%u0443 %u0432%u0430%u0448%u0435%u0433%u043E %u0440%u043E%u0436%u0434%u0435%u043D%u0438%u044F", true);
        dp.setVisible(true);
        Date born = dp.getReturnedStatus();
        if (born != null) {
            long difference = today.getTime() - born.getTime();
            long daycount = TimeUnit.MILLISECONDS.toDays(difference);
            JOptionPane.showMessageDialog(dp, String.format("%u0417%u0435%u043C%u043B%u044F %u0432%u0430%u0441 %u043D%u043E%u0441%u0438%u0442 %u0432%u0441%u0435%u0433%u043E-%u0442%u043E %s %u0434%u043D%u0435%u0439 ", daycount), "%u0412%u044B %u0442%u0430%u043A %u043C%u043E%u043B%u043E%u0434%u044B", JOptionPane.INFORMATION_MESSAGE);
        }
        //URLLabel myEgo = new URLLabel(new URI("http://alexkutsan.blogspot.com/"),"%u041F%u0440%u043E%u0433%u0440%u0430%u043C%u043C%u0443 %u0440%u0430%u0437%u0440%u0430%u0431%u043E%u0442%u0430%u043B %u0410%u043B%u0435%u043A%u0441%u0430%u043D%u0434%u0440 %u041A%u0443%u0446%u0430%u043D");
        //JLabel mersi = new JLabel("%u0411%u043B%u0430%u0433%u043E%u0434%u0430%u0440%u043D%u043E%u0441%u0442%u0438:\n \t %u0410%u043B%u0435%u043A%u0441%u0430%u043D%u0434%u0440 %u041A%u0438%u043B%u0438%u043C%u043D%u0438%u043A \t \n %u0410%u043B%u0438%u0441%u0430 %u0411%u0440%u044B%u043B%u044C");
        JOptionPane.showMessageDialog(dp,new About(), "%u0410%u0432%u0442%u043E%u0440: ", JOptionPane.INFORMATION_MESSAGE);
        System.exit(0);
    }
}

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

        ComplexDialog<Date> dp = new ComplexDialog<Date>(null,p,"%u0412%u0432%u0435%u0434%u0438%u0442%u0435 %u0434%u0430%u0442%u0443 %u0432%u0430%u0448%u0435%u0433%u043E %u0440%u043E%u0436%u0434%u0435%u043D%u0438%u044F", true);
        dp.setVisible(true);
        Date born = dp.getReturnedStatus();
Выглядит диалог Так:
По нажатию, у меня в born лежит либо дата, которую выбрал пользователь, либо null. Если Дата != null, то выскочит сообщение c подсчитаными днями. Работать это будет только ,если вы ещё создадите эти классы. Они не нуждаются в дополнительном описании, разве что URLLable. Но о ней я расскажу позже. Скачать пример можно по этой ссылке ComplexDialog , в каталоге Examples.
 

//The contents of this file are subject to the Mozilla Public License Version 1.1
//(the "License"); you may not use this file except in compliance with the 
//License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
//
//Software distributed under the License is distributed on an "AS IS" basis,
//WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
//for the specific language governing rights and
//limitations under the License.
//
//The Original Code is "The Columba Project"
//
//The Initial Developers of the Original Code are Frederik Dietz and Timo Stich.
//Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003. 
//
//All Rights Reserved.
package alexkutsan_blogspot;


import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.URI;
import javax.swing.JLabel;

public class URLLabel extends JLabel {

  boolean entered = false;

  boolean mousehover;
  private  URI site ;
  public URLLabel(URI uri) {
    this(uri, uri.toString());
  }

  public URLLabel(URI uri, String str) {
    super(str);
    this.site = uri;
    setForeground(Color.blue);
    mousehover = false;
    setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    this.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() > 0) {
                    if (Desktop.isDesktopSupported()) {
                        Desktop desktop = Desktop.getDesktop();
                        try {
                          
                            desktop.browse(site);
                            System.exit(0);
                        } catch (IOException ex) {
                            
                        }
                    } 

                }
            }
        });
  }

  public void mouseEntered(MouseEvent e) {
    entered = true;
    if (mousehover) {
      repaint();
    }
  }

  public void mouseExited(MouseEvent e) {
    setCursor(Cursor.getDefaultCursor());
    entered = false;

    if (mousehover) {
      repaint();
    }
  }

  public void mousePressed(MouseEvent e) {
  }

  public void mouseReleased(MouseEvent e) {
  }

    @Override
  public void paint(Graphics g) {
    super.paint(g);

    if (entered || !mousehover) {
      Rectangle r = g.getClipBounds();

      g.drawLine(0, r.height - this.getFontMetrics(this.getFont()).getDescent(), this
          .getFontMetrics(this.getFont()).stringWidth(this.getText()), r.height
          - this.getFontMetrics(this.getFont()).getDescent());
    }
  }
}


}


 

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package alexkutsan_blogspot;

import java.net.URI;
import java.net.URISyntaxException;

/**
 *
 * @author alex
 */
public class About extends javax.swing.JPanel {

    /**
     * Creates new form About
     */
    public About() {
        initComponents();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jLabel1 = new javax.swing.JLabel();
        try{
            jLabel2 = new URLLabel(new URI("http://alexkutsan.blogspot.com/"), "%u0410%u043B%u0435%u043A%u0441%u0430%u043D%u0434%u0440 %u041A%u0443%u0446%u0430%u043D");
            jLabel3 = new javax.swing.JLabel();
            jLabel4 = new javax.swing.JLabel();
            jLabel5 = new javax.swing.JLabel();

            jLabel1.setText("%u041F%u0440%u043E%u0433%u0440%u0430%u043C%u043C%u0443 %u0440%u0430%u0437%u0440%u0430%u0431%u043E%u0442%u0430%u043B:");

        }
        catch(URISyntaxException e){

        }
        jLabel2.setText("%u0410%u043B%u0435%u043A%u0441%u0430%u043D%u0434%u0440 %u041A%u0443%u0446%u0430%u043D");

        jLabel3.setText("%u0411%u043B%u0430%u0433%u043E%u0434%u0430%u0440%u043D%u043E%u0441%u0442%u0438:");

        jLabel4.setText("%u0410%u043B%u0435%u043A%u0441%u0430%u043D%u0434%u0440 %u041A%u0438%u043B%u0438%u043C%u043D%u0438%u043A");

        jLabel5.setText("%u0410%u043B%u0438%u0441%u0430 %u0411%u0440%u044B%u043B%u044C");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel1)
                            .addComponent(jLabel3))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jLabel2))
                    .addGroup(layout.createSequentialGroup()
                        .addGap(41, 41, 41)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel5)
                            .addComponent(jLabel4))))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(jLabel2))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel3)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel4)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel5))
        );
    }// </editor-fold>                        
    // Variables declaration - do not modify                     
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    // End of variables declaration                   
}


В написании статьи хочу выразить благодарность этим людям: 
    Алиса Брыль; 
    Александр Килимник;
Читать дальше......
 
То, что скрыто под катом
Читать дальше......