Hallo zusammen!
Immer wieder kommt es einmal vor, dass es sich lohnt für sein Programm einen Installationassistenten zu schreiben. Bisher habe ich mir schon 2 mal einen "Installer" für jeweils ein Programm geschreiben. Dabei ist mir aufgefallen, dass das "Grundgerüst" der GUI des Installers in beiden Fällen ziemlich ähnlich ist. Somit ist die GUI des 2. Programmes auch des Öfteren durch nerviges COPY & PAST entstanden.
Zudem bin ich hier im Forum auf diese Seite gestoßen. Allerdings gefällt mir persönlich dieser Assistent dort optisch nicht sehr. Also habe ich mir selbst einen Installationsassistenten, bzw. eine Art Vorlage für einen solchen Assistenten geschrieben.
Gleich mal vorweg:
Hier der Installationsassistent, welcher nur aus einer Klasse besteht:
Hier noch eine Klasse zum Testen:
Noch ein paar Fragen:
Hier noch das dazugehörige Testbild:
Immer wieder kommt es einmal vor, dass es sich lohnt für sein Programm einen Installationassistenten zu schreiben. Bisher habe ich mir schon 2 mal einen "Installer" für jeweils ein Programm geschreiben. Dabei ist mir aufgefallen, dass das "Grundgerüst" der GUI des Installers in beiden Fällen ziemlich ähnlich ist. Somit ist die GUI des 2. Programmes auch des Öfteren durch nerviges COPY & PAST entstanden.
Zudem bin ich hier im Forum auf diese Seite gestoßen. Allerdings gefällt mir persönlich dieser Assistent dort optisch nicht sehr. Also habe ich mir selbst einen Installationsassistenten, bzw. eine Art Vorlage für einen solchen Assistenten geschrieben.
Gleich mal vorweg:
- Kritik und Verbesserungsvorschläge sind sehr erwünscht autsch: & :toll, denn schließlich soll das Programm ja hier als Vorlage (=Entwurf) dienen
- Das die Klasse nicht direkt von JFrame erbt ist Absicht, damit es bei der Verwendung übersichtlich bleibt und nicht jede Menge deprectated - Methoden entstehen
- Das Programm wird selbstverständlich irgendwann später vollständig mit Kommentaren versehen, bzw. übersichtlicher gemacht, sowie eine Jar erzeugt und hier angehängt, welche dann in ein Programm einfach eingebunden werden kann
Hier der Installationsassistent, welcher nur aus einer Klasse besteht:
Java:
package com;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import com.jgoodies.forms.layout.ColumnSpec;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.RowSpec;
/**
* Klasse InstallWizard
*
* Diese Klasse ermöglicht es einen einfachen Installationsassistenten zu
* erstellen.
*
* @author GUI-Programmer
* @version 27.01.2012
*/
public class InstallWizard {
public static final int DEUTSCH = 0;
public static final int ENGLISH = 1;
private Controller controller;
private String textNext, textBtnBack, textBtnNext, textBtnCancel, textBtnFinish, textFinishTitle;
private Font fontButtons, fontHeadlines;
private JFrame frame;
private JPanel pnlWelcome;
private WelcomeImage imgWelcome;
private JLabel lblWelcomeTitle;
private JEditorPane edtrpnWelcomeText;
private JButton btnNextWelcome;
private JButton btnCancelWelcome;
private JPanel pnlFinish;
private JButton btnFinish;
private JPanel pnlInnerFinish;
private List<JPanel> listPages;
private List<JButton> listBackButtons, listNextButtons, listCancelButtons;
public InstallWizard() {
this(InstallWizard.DEUTSCH);
}
public InstallWizard(int language) {
this("", language);
}
public InstallWizard(String title, int language) {
switch(language) {
case 0: {
textNext = "Drücken Sie 'Weiter' um fortzufahren...";
textBtnBack = "Zurück";
textBtnNext = "Weiter";
textBtnCancel = "Abbrechen";
textBtnFinish = "Fertigstellen";
textFinishTitle = "Installation abgeschlossen!";
break;
}
case 1: {
textNext = "Press 'Next' to continue...";
textBtnBack = "Back";
textBtnNext = "Next";
textBtnCancel = "Cancel";
textBtnFinish = "Finish";
textFinishTitle = "Installation finished!";
break;
}
}
fontButtons = new Font("Tahoma", Font.BOLD, 12);
fontHeadlines = new Font("Tahoma", Font.BOLD, 16);
listPages = new ArrayList<JPanel>();
listBackButtons = new ArrayList<JButton>();
listNextButtons = new ArrayList<JButton>();
listCancelButtons = new ArrayList<JButton>();
controller = new Controller();
frame = new JFrame(title);
frame.getContentPane().setLayout(new CardLayout());
}
// Private methods
private JPanel createSouthPanel() {
JPanel pnlSouth = new JPanel();
pnlSouth.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.BLUE));
pnlSouth.setOpaque(false);
pnlSouth.setPreferredSize(new Dimension(600, 45));
pnlSouth.setLayout(new FormLayout(
new ColumnSpec[] {
ColumnSpec.decode("12px"),
ColumnSpec.decode("1px:grow"),
ColumnSpec.decode("75px"),
ColumnSpec.decode("12px"),
ColumnSpec.decode("75px"),
ColumnSpec.decode("50px"),
ColumnSpec.decode("110px"),
ColumnSpec.decode("12px")
},
new RowSpec[] {
RowSpec.decode("45px")
}
));
return pnlSouth;
}
public void createWelcomePanel() {
pnlWelcome = new JPanel();
pnlWelcome.setOpaque(false);
pnlWelcome.setLayout(new BorderLayout(0, 0));
JPanel pnlCenter = new JPanel();
pnlCenter.setOpaque(false);
pnlCenter.setLayout(new FormLayout(
new ColumnSpec[] {
ColumnSpec.decode("5px"),
ColumnSpec.decode("1px:grow"),
ColumnSpec.decode("20px"),
ColumnSpec.decode("1px:grow"),
ColumnSpec.decode("1px:grow"),
ColumnSpec.decode("1px:grow"),
ColumnSpec.decode("1px:grow"),
ColumnSpec.decode("20px")
},
new RowSpec[] {
RowSpec.decode("5px"),
RowSpec.decode("12px"),
RowSpec.decode("20px"),
RowSpec.decode("12px"),
RowSpec.decode("1px:grow"),
RowSpec.decode("12px"),
RowSpec.decode("5px")
}
));
imgWelcome = new WelcomeImage();
pnlCenter.add(imgWelcome, "2, 2, 1, 5, fill, fill");
lblWelcomeTitle = new JLabel();
lblWelcomeTitle.setFont(fontHeadlines);
pnlCenter.add(lblWelcomeTitle, "4, 3, 4, 1, fill, fill");
edtrpnWelcomeText = new JEditorPane();
edtrpnWelcomeText.setFont(new Font("Tahoma", Font.PLAIN, 14));
edtrpnWelcomeText.setBorder(null);
edtrpnWelcomeText.setOpaque(false);
edtrpnWelcomeText.setForeground(Color.BLACK);
edtrpnWelcomeText.setDisabledTextColor(Color.BLACK);
edtrpnWelcomeText.setEnabled(false);
edtrpnWelcomeText.setText("\n\n"+textNext);
pnlCenter.add(edtrpnWelcomeText, "4, 5, 4, 1, fill, fill");
pnlWelcome.add(pnlCenter, BorderLayout.CENTER);
JPanel pnlSouth = createSouthPanel();
btnCancelWelcome = new JButton(textBtnCancel);
btnCancelWelcome.setPreferredSize(new Dimension(110, 28));
btnCancelWelcome.setFont(fontButtons);
btnCancelWelcome.addActionListener(controller);
pnlSouth.add(btnCancelWelcome, "7, 1, fill, center");
btnNextWelcome = new JButton(textBtnNext);
btnNextWelcome.setPreferredSize(new Dimension(75, 28));
btnNextWelcome.setFont(fontButtons);
btnNextWelcome.addActionListener(controller);
pnlSouth.add(btnNextWelcome, "5, 1, fill, center");
pnlWelcome.add(pnlSouth, BorderLayout.SOUTH);
frame.getContentPane().add(pnlWelcome, "Welcome");
}
public void createFinishPanel() {
pnlFinish = new JPanel();
pnlFinish.setOpaque(false);
pnlFinish.setLayout(new BorderLayout(0, 0));
JPanel pnlCenter = new JPanel();
pnlCenter.setOpaque(false);
pnlCenter.setLayout(new FormLayout(
new ColumnSpec[] {
ColumnSpec.decode("20px"),
ColumnSpec.decode("1px:grow"),
ColumnSpec.decode("20px")
},
new RowSpec[] {
RowSpec.decode("17px"),
RowSpec.decode("20px"),
RowSpec.decode("12px"),
RowSpec.decode("1px:grow"),
RowSpec.decode("30px"),
}
));
JLabel lblFinishTitle = new JLabel(textFinishTitle);
lblFinishTitle.setFont(fontHeadlines);
pnlCenter.add(lblFinishTitle, "2, 2, fill, fill");
pnlInnerFinish = new JPanel();
pnlInnerFinish.setOpaque(false);
pnlCenter.add(pnlInnerFinish, "2, 4, fill, fill");
pnlFinish.add(pnlCenter, BorderLayout.CENTER);
JPanel pnlSouth = createSouthPanel();
btnFinish = new JButton(textBtnFinish);
btnFinish.setPreferredSize(new Dimension(110, 28));
btnFinish.setFont(fontButtons);
btnFinish.addActionListener(controller);
pnlSouth.add(btnFinish, "7, 1, fill, center");
pnlFinish.add(pnlSouth, BorderLayout.SOUTH);
frame.getContentPane().add(pnlFinish, "Finish");
}
// Private Klassen
@SuppressWarnings("serial")
private class WelcomeImage extends JPanel {
private Image image;
public Image getImage() {
return image;
}
public void setImage(Image image) {
this.image = image;
repaint();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(image != null) {
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
}
}
private class Controller implements ActionListener {
@Override
public void actionPerformed(ActionEvent evt) {
Object src = evt.getSource();
if(src.equals(btnNextWelcome)) {
welcomeNextButtonActionPerformed(evt);
} else if(src.equals(btnCancelWelcome)) {
welcomeCancelButtonActionPerformed(evt);
} else if(src.equals(btnFinish)) {
finishButtonActionPerformed(evt);
} else {
for(int i=0; i<listBackButtons.size(); i++) {
if(src.equals(listBackButtons.get(i))) {
backButtonActionPerformed((i+1), evt);
}
}
for(int i=0; i<listNextButtons.size(); i++) {
if(src.equals(listNextButtons.get(i))) {
nextButtonActionPerformed((i+1), evt);
}
}
for(int i=0; i<listCancelButtons.size(); i++) {
if(src.equals(listCancelButtons.get(i))) {
cancelButtonActionPerformed((i+1), evt);
}
}
}
}
}
// Protected methods
protected void welcomeCancelButtonActionPerformed(ActionEvent evt) {
System.exit(0);
}
protected void welcomeNextButtonActionPerformed(ActionEvent evt) {
try {
showPage(1);
} catch(Exception ex) {
showFinishPage();
}
}
protected void finishButtonActionPerformed(ActionEvent evt) {
System.exit(0);
}
protected void backButtonActionPerformed(int page, ActionEvent evt) {
try {
showPage((page-1));
} catch(Exception ex) {
showWelcomePage();
}
}
protected void nextButtonActionPerformed(int page, ActionEvent evt) {
try {
showPage((page+1));
} catch(Exception ex) {
showFinishPage();
}
}
protected void cancelButtonActionPerformed(int page, ActionEvent evt) {
System.exit(0);
}
// Public methods
public void addPage(JPanel page) {
JPanel pnlContent = new JPanel(new BorderLayout(0, 0));
pnlContent.setOpaque(false);
pnlContent.add(page, BorderLayout.CENTER);
listPages.add(page);
JPanel pnlSouth = createSouthPanel();
JButton btnBack = new JButton(textBtnBack);
btnBack.setPreferredSize(new Dimension(75, 28));
btnBack.setFont(fontButtons);
btnBack.addActionListener(controller);
pnlSouth.add(btnBack, "3, 1, fill, center");
listBackButtons.add(btnBack);
JButton btnNext = new JButton(textBtnNext);
btnNext.setPreferredSize(new Dimension(75, 28));
btnNext.setFont(fontButtons);
btnNext.addActionListener(controller);
pnlSouth.add(btnNext, "5, 1, fill, center");
listNextButtons.add(btnNext);
JButton btnCancel = new JButton(textBtnCancel);
btnCancel.setPreferredSize(new Dimension(110, 28));
btnCancel.setFont(fontButtons);
btnCancel.addActionListener(controller);
pnlSouth.add(btnCancel, "7, 1, fill, center");
listCancelButtons.add(btnCancel);
pnlContent.add(pnlSouth, BorderLayout.SOUTH);
frame.getContentPane().add(pnlContent, ("Page "+listPages.size()));
}
public void setBackButtonEnabled(int page, boolean enabled) {
int index = page - 1;
listBackButtons.get(index).setEnabled(enabled);
}
public boolean isBackButtonEnabled(int page, boolean enabled) {
int index = page - 1;
return listBackButtons.get(index).isEnabled();
}
public void setNextButtonEnabled(int page, boolean enabled) {
int index = page - 1;
listNextButtons.get(index).setEnabled(enabled);
}
public boolean isNextButtonEnabled(int page, boolean enabled) {
int index = page - 1;
return listNextButtons.get(index).isEnabled();
}
public void setCancelButtonEnabled(int page, boolean enabled) {
int index = page - 1;
listCancelButtons.get(index).setEnabled(enabled);
}
public Color getBackground() {
return frame.getContentPane().getBackground();
}
public boolean isCancelButtonEnabled(int page, boolean enabled) {
int index = page - 1;
return listCancelButtons.get(index).isEnabled();
}
public int getDefaultCloseOperation() {
return frame.getDefaultCloseOperation();
}
public void setDefaultCloseOperation(int operation) {
frame.setDefaultCloseOperation(operation);
}
public void setSize(int width, int height) {
frame.setSize(width, height);
}
public int getWidth() {
return frame.getWidth();
}
public int getHeight() {
return frame.getHeight();
}
public void setContentSize(int width, int height) {
frame.getContentPane().setPreferredSize(new Dimension(width, height));
}
public int getContentWidth() {
return frame.getContentPane().getWidth();
}
public int getContentHeight() {
return frame.getContentPane().getHeight();
}
public void pack() {
frame.pack();
}
public String getTitle() {
return frame.getTitle();
}
public void setTitle(String title) {
frame.setTitle(title);
}
public Image getWelcomeImage() {
return imgWelcome.getImage();
}
public void setWelcomeImage(Image image) {
imgWelcome.setImage(image);
}
public String getWelcomeTitle() {
return lblWelcomeTitle.getText();
}
public void setWelcomeTitle(String title) {
lblWelcomeTitle.setText(title);
}
public String getWelcomeText() {
String text = edtrpnWelcomeText.getText();
int length = ("\n\n"+textNext).length();
int endIndex = text.length()-length-1;
text = text.substring(0, endIndex);
return text;
}
public void setWelcomeText(String text) {
edtrpnWelcomeText.setText(text+"\n\n"+textNext);
}
public JPanel getInnerFinishPanel() {
return pnlInnerFinish;
}
public void setInnerFinishPanel(JPanel pnlInnerFinish) {
Container parent = this.pnlInnerFinish.getParent();
parent.remove(this.pnlInnerFinish);
parent.add(pnlInnerFinish, "2, 4, fill, fill");
parent.repaint();
parent.validate();
}
public Image getIconImage() {
return frame.getIconImage();
}
public void setIconImage(Image image) {
frame.setIconImage(image);
}
public void setVisible(boolean visible) {
frame.setVisible(visible);
}
public boolean isVisible() {
return frame.isVisible();
}
public Dimension getMaximumSize() {
return frame.getMaximumSize();
}
public void setMaximumSize(Dimension maximumSize) {
frame.setMaximumSize(maximumSize);
}
public Dimension getMinimumSize() {
return frame.getMinimumSize();
}
public void setMinimumSize(Dimension minimumSize) {
frame.setMinimumSize(minimumSize);
}
public void setLocationRelativTo(Component c) {
frame.setLocationRelativeTo(c);
}
public void setBackground(Color c) {
frame.getContentPane().setBackground(c);
}
public void showWelcomePage() {
CardLayout cardLayout = (CardLayout) frame.getContentPane().getLayout();
cardLayout.show(frame.getContentPane(), "Welcome");
}
public void showFinishPage() {
CardLayout cardLayout = (CardLayout) frame.getContentPane().getLayout();
cardLayout.show(frame.getContentPane(), "Finish");
}
public void showPage(int page) {
if(page<=0) {
throw new ArrayIndexOutOfBoundsException("page must be more than 0");
} else if(page>listPages.size()) {
throw new ArrayIndexOutOfBoundsException("page "+page+" does not exist");
}
CardLayout cardLayout = (CardLayout) frame.getContentPane().getLayout();
cardLayout.show(frame.getContentPane(), ("Page "+page));
}
}
Hier noch eine Klasse zum Testen:
Java:
package com;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Image;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
/**
* Klasse TestWizard
*
* Diese Klasse dient dazu die Funktionen von InstallWizard zu testen
* und mögliche Fehler zu finden.
*
* @author GUI-Programmer
* @version 27.01.2012
*/
public class TestWizard extends InstallWizard {
public TestWizard() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(Exception ex) {
ex.printStackTrace();
}
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setContentSize(600, 400);
createWelcomePanel();
createFinishPanel();
Image image = null;
try {
image = ImageIO.read(TestWizard.class.getResource("Schlamm.jpg"));
} catch(IOException ex) {
ex.printStackTrace();
}
setWelcomeImage(image);
setWelcomeTitle("Herzlich Wilkommen im Installation Assistent!");
String text = "";
for(int i=0; i<8; i++) {
text = text+"Some stupid text stays here. Don't read it!!! ";
}
setWelcomeText(text);
JPanel panel1 = new JPanel();
panel1.setBackground(Color.RED);
addPage(panel1);
JPanel panel2 = new JPanel();
panel2.setBackground(Color.YELLOW);
addPage(panel2);
JPanel panel3 = new JPanel();
panel3.setBackground(Color.GREEN);
addPage(panel3);
JPanel panel4 = new JPanel();
panel4.setBackground(Color.BLUE);
addPage(panel4);
JPanel panelFinish = new JPanel();
panelFinish.setBackground(Color.WHITE);
setInnerFinishPanel(panelFinish);
pack();
setLocationRelativTo(null);
setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new TestWizard();
}
});
}
}
Noch ein paar Fragen:
- Kann man bei einer Methode irgendwie im Kommtar speziell darauf aufmerksam machen, dass hier eventuell eine Überschreibung sinnvol wäre, oder kann man das, so wie ich das hier beschreibe, einfach selbst kommentieren? Beispielmethoden:
Code:
welcomeCancelButtonActionPerformed(ActionEvent evt)
Code:backButtonActionPerformed(int page, ActionEvent evt)
- Gibt es eine Möglichkeit eine LookAndFeel - Änderung zu regestrieren? Denn wenn Ja, dann wäre es sinnvoller die Methoden
Code:
createWelcomePanel()
Code:createFinishPanel()
Hier noch das dazugehörige Testbild: