问题
I'm trying to do this: In my GUI, I have one JTable with a custom model, and in other part of the GUI, I have one panel where i draw some arrows. I want to "synchronize this two view. So let's say if we have 5 rows in my JTable, I will draw 5 arrows in the panel of arrows. If I make a modification in the number of row, I must have the same number of row. So I'm trying to use Design Pattern Observer.
To make simple, I try to provide an example computable in one file: I create n buttons in my first Panel (ButtonPanel) and I create n label in the second panel (LabelButton).
The problem is : How to synchronize the number of label withthe number of label with DP Observer
import java.awt.Color;
import java.awt.GridLayout;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
interface Observer {
public void update(Subject subject, int number);
}
interface Subject {
public void addObserver(Observer observer);
public void delObserver(Observer observer);
public void notifyObservers();
}
class SynchronizeNumber implements Subject {
private ArrayList<Observer> observers;
private int numberSync;
public SynchronizeNumber() {
super();
observers = new ArrayList<Observer>();
}
public SynchronizeNumber(int numberSync) {
super();
this.numberSync = numberSync;
observers = new ArrayList<Observer>();
}
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void delObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer o : observers) {
o.update(this, this.numberSync);
}
}
/**
* @return the number
*/
public int getNumberSync() {
return numberSync;
}
/**
* @param number
* the number to set
*/
public void setNumberSync(int numberSync) {
this.numberSync = numberSync;
notifyObservers();
}
}
class ButtonPanel extends JPanel implements Observer {
private int numberButton;
public ButtonPanel() {
super();
}
public ButtonPanel(int numberButton) {
this.numberButton = numberButton;
for (int i = 1; i <= numberButton; i++)
this.add(new JButton("" + i));
}
/**
* @return the numberLabel
*/
public int getNumberButton() {
return numberButton;
}
/**
* @param numberLabel
* the numberLabel to set
*/
public void setNumberButton(int numberButton) {
this.numberButton = numberButton;
}
@Override
public void update(Subject subject, int number) {
if (subject instanceof SynchronizeNumber) {
SynchronizeNumber synchronizeNumber = (SynchronizeNumber) subject;
numberButton = synchronizeNumber.getNumberSync();
System.out.println("ButtonPanel, numberButton: " + numberButton);
}
}
}
class LabelPanel extends JPanel implements Observer {
private int numberLabel;
public LabelPanel() {
super();
}
public LabelPanel(int numberLabel) {
super();
this.numberLabel = numberLabel;
for (int i = 1; i <= numberLabel; i++)
this.add(new JLabel("label numbe: " + i));
this.setBorder(new LineBorder(Color.blue));
}
@Override
public void update(Subject subject, int number) {
if (subject instanceof SynchronizeNumber) {
SynchronizeNumber synchronizeNumber = (SynchronizeNumber) subject;
numberLabel = synchronizeNumber.getNumberSync();
System.out.println("LabelPanel, numberLabel: " + numberLabel);
}
}
/**
* @return the numberLabel
*/
public int getNumberLabel() {
return numberLabel;
}
/**
* @param numberLabel
* the numberLabel to set
*/
public void setNumberLabel(int numberLabel) {
this.numberLabel = numberLabel;
}
}
public class Test {
public static void main(String[] args) {
JFrame f = new JFrame();
f.setLayout(new GridLayout(2, 1));
// create an subject synchronizeNumber
SynchronizeNumber synchronizeNumber = new SynchronizeNumber();
// set number to 1
synchronizeNumber.setNumberSync(1);
//create observers buttonPanel and labelPanel
ButtonPanel buttonPanel = new ButtonPanel(synchronizeNumber.getNumberSync());
LabelPanel labelPanel = new LabelPanel(synchronizeNumber.getNumberSync());
// add buttonPanel and labelPanel as observers
synchronizeNumber.addObserver(buttonPanel);
synchronizeNumber.addObserver(labelPanel);
// make a change manually
synchronizeNumber.setNumberSync(4);
f.add(buttonPanel);
f.add(labelPanel);
f.setSize(400, 400);
f.setLocationRelativeTo(null);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
}
edit: I use my own DP Observer and not from the API because multiple extending is not possible
回答1:
I use Observer pattern because I think it is the best solution.
Swing is an Event Driven API and acts pretty much like an Observer Pattern in the sense that there are events and listeners subscribed to listen those events. For instance JButton component dispatches an ActionEvent every time it is pressed and notifies to subscribed ActionListeners that something happened.
With the same principle, is responsibility of TableModel to dispatch a TableModelEvent every time data has been changed (rows added/deleted, cells updated, etc) and notify subscribed TableModelListeners in such events.
You can use this fact to accomplish your main goal:
So let's say if we have 5 rows in my JTable, I will draw 5 arrows in the panel of arrows.
- Create a
JPanel
with the ability to draw many arrows as you wish. See Performing Custom Painting lesson. - Attach a
TableModelListener
to yourTableModel
in order to repaint yourJPanel
when rows are inserted/deleted.
I have two classes and I want a minimun dependency between the two classes.
If you follow my suggestions, you should be able to create an independent and reusable JPanel
like this:
class MyCustomPanel extends JPanel {
private int numberOfArrows = 0;
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g); // never forget to call super.paintComponent(g)
Graphics graphics = g.create();
int y = 10;
for(int i = 0; i < numberOfArrows; i++) {
graphics.drawLine(10, y, getWidth() - 10, y); // instead of lines just draw your arrows
y += 10;
}
graphics.dispose();
}
/**
* Public method to set the number of arrows that has to be drawn.
* @param arrows
*/
public void setNumberOfArrows(int arrows) {
numberOfArrows = arrows;
repaint();
}
@Override
public Dimension getPreferredSize() {
return isPreferredSizeSet()
? super.getPreferredSize() : new Dimension(200,200);
}
}
This way your panel can take the number of arrows from the outside independently on how it is determined (it could be number of rows in a table model, number of elements in a list, a fixed value, whatever you want).
回答2:
I have implemented it exactly as you wanted:
import java.awt.Color;
import java.awt.GridLayout;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
interface Observer {
public void update(Subject subject, int number);
}
interface Subject {
public void addObserver(Observer observer);
public void delObserver(Observer observer);
public void notifyObservers();
}
class SynchronizeNumber implements Subject {
private ArrayList<Observer> observers;
private int numberSync;
public SynchronizeNumber() {
super();
observers = new ArrayList<Observer>();
}
public SynchronizeNumber(int numberSync) {
super();
this.numberSync = numberSync;
observers = new ArrayList<Observer>();
}
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void delObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer o : observers) {
o.update(this, this.numberSync);
}
}
/**
* @return the number
*/
public int getNumberSync() {
return numberSync;
}
/**
* @param number
* the number to set
*/
public void setNumberSync(int numberSync) {
this.numberSync = numberSync;
notifyObservers();
}
}
class ButtonPanel extends JPanel implements Observer {
private int numberButton;
public ButtonPanel() {
super();
}
public ButtonPanel(int numberButton) {
this.numberButton = numberButton;
for (int i = 0; i < numberButton; i++)
this.add(createButton(i));
}
/**
* @return the numberLabel
*/
public int getNumberButton() {
return numberButton;
}
/**
* @param numberLabel
* the numberLabel to set
*/
public void setNumberButton(int numberButton) {
this.numberButton = numberButton;
}
@Override
public void update(Subject subject, int number) {
if (subject instanceof SynchronizeNumber) {
SynchronizeNumber synchronizeNumber = (SynchronizeNumber) subject;
int diff = numberButton + (number - numberButton);
// if diff and numberButton are equal do nothing
if (diff < numberButton) {
for (int i = numberButton - 1; i >= diff; i--) {
this.remove(this.getComponent(i));
}
} else if (diff > numberButton) {
for (int i = numberButton; i < diff; i++) {
this.add(createButton(i));
}
}
numberButton = number;
System.out.println("ButtonPanel, numberButton: " + numberButton);
}
}
private JButton createButton(int i) {
return new JButton("" + ++i);
}
}
class LabelPanel extends JPanel implements Observer {
private int numberLabel;
public LabelPanel() {
super();
}
public LabelPanel(int numberLabel) {
super();
this.numberLabel = numberLabel;
for (int i = 0; i < numberLabel; i++)
this.add(createLabel(i));
this.setBorder(new LineBorder(Color.blue));
}
@Override
public void update(Subject subject, int number) {
if (subject instanceof SynchronizeNumber) {
SynchronizeNumber synchronizeNumber = (SynchronizeNumber) subject;
int diff = numberLabel + (number - numberLabel);
// if diff and numberButton are equal do nothing
if (diff < numberLabel) {
for (int i = numberLabel - 1; i >= diff; i--) {
this.remove(this.getComponent(i));
}
} else if (diff > numberLabel) {
for (int i = numberLabel; i < diff; i++) {
this.add(createLabel(i));
}
}
numberLabel = number;
System.out.println("LabelPanel, numberLabel: " + numberLabel);
}
}
/**
* @return the numberLabel
*/
public int getNumberLabel() {
return numberLabel;
}
/**
* @param numberLabel
* the numberLabel to set
*/
public void setNumberLabel(int numberLabel) {
this.numberLabel = numberLabel;
}
private JLabel createLabel(int i) {
return new JLabel("label name:" + ++i);
}
}
public class Test {
public static void main(String[] args) {
JFrame f = new JFrame();
f.setLayout(new GridLayout(2, 1));
// create an subject synchronizeNumber
SynchronizeNumber synchronizeNumber = new SynchronizeNumber();
// set number to 1
synchronizeNumber.setNumberSync(1);
// create observers buttonPanel and labelPanel
ButtonPanel buttonPanel = new ButtonPanel(synchronizeNumber.getNumberSync());
LabelPanel labelPanel = new LabelPanel(synchronizeNumber.getNumberSync());
// add buttonPanel and labelPanel as observers
synchronizeNumber.addObserver(buttonPanel);
synchronizeNumber.addObserver(labelPanel);
// make a change manually
synchronizeNumber.setNumberSync(4);
synchronizeNumber.setNumberSync(3);
synchronizeNumber.setNumberSync(11);
synchronizeNumber.setNumberSync(5);
synchronizeNumber.setNumberSync(7);
f.add(buttonPanel);
f.add(labelPanel);
f.setSize(400, 400);
f.setLocationRelativeTo(null);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
}
来源:https://stackoverflow.com/questions/24886172/how-to-synchronize-two-view-in-java-swing