Создание многопоточного приложения с элементами вывода графической информации - Java
Формулировка задачи:
В общем кодил тут програмку
Суть заключается в том, что по экрану летают ромбы
В меню управления есть три пункта
1. Приостановка выборочных фигур
2. Приостановка всех фигур
3. И возобновление движения
суть вопроса заключается в том
никак не могу понять как прописать что при нажатии кнопки приостановка выборочных фигур остонавливаются только те фигуры которые находят в диапазоне (угол скорости фигуры находится между 180 и 270 градусов (3-я четверть))
Прикрепил архив с кодом
программирую в Eclipse
Помогите разобраться)
Решение задачи: «Создание многопоточного приложения с элементами вывода графической информации»
textual
Листинг программы
MainFrame
package mainPackage;
import java.awt.BorderLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
@SuppressWarnings("serial")
public class MainFrame extends JFrame {
// Константы, задающие размер окна приложения, если оно
// не распахнуто на весь экран
private static final int WIDTH = 700;
private static final int HEIGHT = 500;
private JMenuItem pauseMenuItem;
private JMenuItem resumeMenuItem;
// Поле, по которому движутся фигуры
private Field field = new Field();
// Конструктор главного окна приложения
public MainFrame() {
super("Программирование и синхронизация потоков");
setSize(WIDTH, HEIGHT);
Toolkit kit = Toolkit.getDefaultToolkit();
// Отцентрировать окно приложения на экране
setLocation((kit.getScreenSize().width - WIDTH)/2,
(kit.getScreenSize().height - HEIGHT)/2);
// Установить начальное состояние окна развернутым на весь экран
setExtendedState(MAXIMIZED_BOTH);
// Создать меню
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu figuresMenu = new JMenu("Фигуры");
Action addFigureAction = new AbstractAction("Добавить фигуру") {
public void actionPerformed(ActionEvent event) {
field.addFigure();
if (!pauseMenuItem.isEnabled() &&
!resumeMenuItem.isEnabled()) {
// Ни один из пунктов меню не является
// доступным - сделать доступным "Паузу"
pauseMenuItem.setEnabled(true);
}
}
};
menuBar.add(figuresMenu);
figuresMenu.add(addFigureAction);
JMenu controlMenu = new JMenu("Управление");
menuBar.add(controlMenu);
pauseMenuItem = controlMenu.add(pauseAction);
pauseMenuItem.setEnabled(false);
Action resumeAction2 = new AbstractAction("Остановка рандомных фигурок"){
public void actionPerformed(ActionEvent event) {
field.paused2();
pauseMenuItem.setEnabled(true);
resumeMenuItem.setEnabled(true);
pauseSelect.setEnabled(false);
}
};
Action pauseAction = new AbstractAction("Приостановить движение"){
public void actionPerformed(ActionEvent event) {
field.pause();
pauseMenuItem.setEnabled(false);
resumeMenuItem.setEnabled(true);
}
};
pauseMenuItem = controlMenu.add(pauseAction);
pauseMenuItem.setEnabled(false);
Action resumeAction = new AbstractAction("Возобновить движение") {
public void actionPerformed(ActionEvent event) {
field.resume();
pauseMenuItem.setEnabled(true);
resumeMenuItem.setEnabled(false);
}
};
resumeMenuItem = controlMenu.add(resumeAction);
resumeMenuItem.setEnabled(false);
// Добавить в центр граничной компоновки поле Field
getContentPane().add(field, BorderLayout.CENTER);
}
// Главный метод приложения
public static void main(String[] args) {
// Создать и сделать видимым главное окно приложения
MainFrame frame = new MainFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
MovingFigure
package mainPackage;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
public class MovingFigure implements Runnable {
// Максимальный размер половины стороны квадрата, описывающего фигуру
private static final int maxFramingSquareHalfSize = 60;
// Минимальный размер половины стороны квадрата, описывающего фигуру
private static final int minFramingSquareHalfSize = 10;
// Минимальное время, на которое может засыпать поток после каждого смещения
private static final int minSleepTime = 1;
private Field field;
// Характеристики фигуры
// Половина стороны квадрата, описывающего фигуру
private int framingSquareHalfSize;
// Цвет для заливки внутреннего простанства фигуры
private Color color;
// Штриховка для рисования границы фигуры
float [] dash = {8,2,8,2,2,2,2,2,4,2,4,2};
private Stroke stroke = new BasicStroke(2.0f, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_ROUND, 10.0f, dash, 0.0f);
// Текущие координаты центра фигуры
private double x;
private double y;
// Время сна потока между двумя пересчетами координат
private int sleepTime;
// Вертикальная и горизонтальная компоненты смещения
private double shiftX;
private double shiftY;
//Конструктор класса MovingFigure
public MovingFigure(Field field) {
//Необходимо иметь ссылку на поле, по которому передвигается фигура,
//чтобы отслеживать выход за его пределы через getWidth(), getHeight()
this.field = field;
//Случайно выбираем размер половины стороны квадрата, описывающего фигуру
framingSquareHalfSize = minFramingSquareHalfSize + new Double(
Math.random()*(maxFramingSquareHalfSize - minFramingSquareHalfSize)).intValue();
//Абсолютное значение времени сна потока зависит от размера фигуры,
//чем он больше, тем больше
sleepTime = 16 -
new Double(Math.round(210 / framingSquareHalfSize)).intValue();
if (sleepTime < minSleepTime)
sleepTime = minSleepTime;
//Начальное направление движения случайно, угол в пределах от 0 до 2PI
double angle = Math.random()*2*Math.PI;
//Вычисляются горизонтальная и вертикальная компоненты смещения
shiftX = 3*Math.cos(angle);
shiftY = 3*Math.sin(angle);
//Цвет фигуры выбирается случайно
color = new Color((float)Math.random(), (float)Math.random(),
(float)Math.random());
//Начальное положение фигуры случайно
x = framingSquareHalfSize + Math.random()*
(field.getSize().getWidth()-2*framingSquareHalfSize);
y = framingSquareHalfSize + Math.random()*
(field.getSize().getHeight()-2*framingSquareHalfSize);
//Создаем новый экземпляр потока, передавая аргументом
//ссылку на объект класса, реализующего Runnable (т.е. на себя)
Thread thisThread = new Thread(this);
//Запускаем поток
thisThread.start();
}
//Метод run() исполняется внутри потока. Когда он завершает работу,
//то завершается и поток
public void run() {
try {
//Крутим бесконечный цикл, т.е. пока нас не прервут,
//мы не намерены завершаться
while(true) {
//Синхронизация потоков выполняется на объекте field.
//Если движение разрешено - управление будет возвращено в метод.
//В противном случае - активный поток заснет
field.canMove(this);
if (x + shiftX <= framingSquareHalfSize) {
//Достигли левой стенки, отскакиваем право
shiftX = -shiftX;
x = framingSquareHalfSize;
} else if (x + shiftX >= field.getWidth()-
framingSquareHalfSize) {
// Достигли правой стенки, отскок влево
shiftX = -shiftX;
x=new Double(field.getWidth()-
framingSquareHalfSize).intValue();
} else if (y + shiftY <= framingSquareHalfSize) {
// Достигли верхней стенки
shiftY = -shiftY;
y = framingSquareHalfSize;
} else if (y + shiftY >= field.getHeight()-
framingSquareHalfSize) {
// Достигли нижней стенки
shiftY = -shiftY;
y=new Double(field.getHeight()-
framingSquareHalfSize).intValue();
} else {
// Просто смещаемся
x += shiftX;
y += shiftY;
}
// Засыпаем на sleepTime миллисекунд
Thread.sleep(sleepTime);
}
} catch (InterruptedException ex) {
// Если нас прервали, то ничего не делаем
// и просто выходим (завершаемся)
}
}
// Метод прорисовки самой себя
public void paint (Graphics2D canvas) {
Rectangle2D.Double rect1 = new Rectangle2D.Double(x-framingSquareHalfSize, y-framingSquareHalfSize,
2*framingSquareHalfSize, 2*framingSquareHalfSize);
Rectangle2D.Double rect2 = new Rectangle2D.Double(x-framingSquareHalfSize, y-framingSquareHalfSize,
2*framingSquareHalfSize, 2*framingSquareHalfSize);
Area figure = new Area(rect1);
AffineTransform at = AffineTransform.getRotateInstance(
Math.PI/3, rect1.getCenterX(), rect2.getCenterY());
figure.transform(at);
//figure.add(new Area(rect2));
canvas.setPaint(color);
canvas.fill(figure);
canvas.setStroke(stroke);
canvas.setPaint(Color.black);
canvas.draw(figure);
//canvas.draw(line);
}
}
Field
package mainPackage;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JPanel;
import javax.swing.Timer;
@SuppressWarnings("serial")
public class Field extends JPanel {
// Флаг приостановки движения
private boolean paused;
// Динамический список движущихся фигур
private ArrayList<MovingFigure> figures = new ArrayList<MovingFigure>(10);
// Класс таймер отвечает за регулярную генерацию событий ActionEvent
// При создании его экземпляра используется анонимный класс,
// реализующий интерфейс ActionListener
private Timer repaintTimer = new Timer(10, new ActionListener() {
public void actionPerformed(ActionEvent ev) {
// Задача обработчика события ActionEvent - перерисовка окна
repaint();
}
});
// Конструктор класса Field
public Field() {
// Установить цвет заднего фона белым
setBackground(Color.WHITE);
// Запустить таймер
repaintTimer.start();
}
// Унаследованный от JPanel метод перерисовки компонента
public void paintComponent(Graphics g) {
// Вызвать версию метода, унаследованную от предка
super.paintComponent(g);
Graphics2D canvas = (Graphics2D) g;
// Последовательно запросить прорисовку от всех фигур из списка
for (MovingFigure figure: figures)
figure.paint(canvas);
}
// Метод добавления новой фигуры в список
public void addFigure() {
// Заключается в добавлении в список нового экземпляра MovingFigure
// Всю инициализацию положения, скорости, размера, цвета
// MovingFigure выполняет в своем конструкторе
figures.add(new MovingFigure(this));
}
// Cинхронизированный метод приостановки движения фигур
// (только один поток может одновременно быть внутри)
public synchronized void pause() {
// Включить режим паузы
paused = true;
}
// Cинхронизированный метод возобновления движения фигур
// (только один поток может одновременно быть внутри)
public synchronized void resume() {
// Выключить режим паузы
paused = false;
// Будим все ожидающие продолжения потоки
notifyAll();
}
// Синхронизированный метод проверки, может ли фигура двигаться
// (не включен ли режим паузы?)
public synchronized void canMove(MovingFigure figure) throws InterruptedException {
// Если режим паузы включен, то поток, зашедший
// внутрь данного метода, засыпает
if (paused)
wait();
}
}