Java:如何使单独的类与线程上的MainController进行通信 [英] Java: How to make seperate class communicate with MainController on thread

查看:315
本文介绍了Java:如何使单独的类与线程上的MainController进行通信的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用JavaFX的简单合成器程序。我正在尝试创建一个名为Metronome的新类来与MainController类进行交互。我需要节拍器在它自己的线程上运行,但仍然能够从MainController运行方法,特别是在每个节拍上。例如,当打开节拍器时,它需要(在它自己的线程上)设置形状的填充颜色并通过MainController中的方法发出声音。它会不断改变颜色(关闭和打开)并使声音处于延迟循环,直到MainController的方法停止循环。我如何让Metronome类与我的MainController进行通信,然后让它在自己的线程上运行?



编辑:所以我基本上需要我的run()方法节拍器类能够在MainController类中运行方法。

 公共类MainController实现Initializable {

public AudioMain audio = new AudioMain();
@ FXML public AnchorPane mainPane;

public boolean debugMessages = true;
public boolean debugMessages2 = false;
public boolean debugMessages3 = false;

// public final int numKeys = 13;
public int C4 = 60; // C4
的midi音高public int octave = 4; //要分配的默认八度音阶

public String synthType =Saw;
public SynthSet synth = new SynthSet(111,synthType); //创建一个13个SineWaves的新SynthSet

public double bpm = 120;
公共节拍器节拍器=新节拍器(bpm);

....稍后的一些代码....

public void toggleMetronome(){
metronome.toggleMet();
}

public void lightOn(){
metronomeLight.setFill(lightOnColor);
}

public void lightOff(){
metronomeLight.setFill(lightOffColor);
}

然后..

 公共类Metronome实现了Runnable {

public boolean metronomeOn = false;
public boolean metronomeSound = true;
public boolean metOutputMessages = true;
public boolean tick8th = false;
public double bpm = 20;
公共长msPerBeat =(长)(60000 / bpm); //每节拍的毫秒数
public int tickCount = 0;
公共长纳米;

公共节拍器(){

}

公共节拍器(双拍){
setTempo(beat);
}

public static void main(String args []){
Metronome met = new Metronome();
met.metOn();
}

@Override
public void run(){
System.out.println(msPerBeat);
while(metronomeOn){
beat();
延迟(msPerBeat / 2);
if(tick8th)beat8th();
延迟(msPerBeat / 2);
}
}

public void metOn(){
if(!metronomeOn){
outMessage(起始节拍器在+ bpm +bpm );
metronomeOn = true;
new Thread(this).start();
}
}

public void metOff(){
if(metronomeOn){
outMessage(Stoping metronome);
metronomeOn = false;
}
}
public void toggleMet(){
if(metronomeOn){
metOff();
}否则if(!metronomeOn)
metOn();
}

public void beat(){
tickCount ++;
outMessage(Beep+ tickCount);
}
}


解决方案

示例

  import javafx 。动画。*; 
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property。*;
import javafx.beans.value.ChangeListener;
import javafx.geometry。*;
import javafx.scene.Scene;
import javafx.scene.control。*;
import javafx.scene.effect。*;
import javafx.scene.layout。*;
import javafx.scene.media.AudioClip;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;

公共类MetroGnome扩展应用程序{
public void start(舞台阶段){
Metronome metronome = new Metronome();
TempoControl tempoControl =新的TempoControl(节拍器);
BeatIndicator beatIndicator =新的BeatIndicator(节拍器);
PlayControl playControl =新的PlayControl(节拍器);

HBox layout = new HBox(10,playControl,tempoControl,beatIndicator);
layout.setAlignment(Pos.CENTER);
layout.setPadding(new Insets(10));

stage.setScene(new Scene(layout));
stage.show();
}

public static void main(String [] args){
launch(args);
}
}

class PlayControl扩展ToggleButton {
公共PlayControl(Metronome节拍器){
super(Start);

setOnAction(event - > {
if(isSelected()){
metronome.start();
setText(Stop);
} else {
metronome.stop();
setText(Start);
}
});
}
}

类TempoControl扩展VBox {
private static final int MIN_TEMPO = 20;
private static final int MAX_TEMPO = 240;
private static final int DEFAULT_TEMPO = 120;

私人Slider tempoSlider =新滑块(MIN_TEMPO,MAX_TEMPO,DEFAULT_TEMPO);
private Label tempoLabel = new Label(tempoSlider.getValue()+);

公共TempoControl(节拍器节拍器){
super(5);

tempoLabel.textProperty()。bind(Bindings.format(%。0f,tempoSlider.valueProperty()));
setAlignment(Pos.CENTER);
getChildren()。setAll(tempoLabel,tempoSlider);

metronome.setTempo(tempoSlider.getValue());
metronome.tempoProperty()。bind(tempoSlider.valueProperty());
}

public DoubleProperty valueProperty(){
return tempoSlider.valueProperty();
}
}

类BeatIndicator扩展Circle {
//来自:http://soundbible.com/1628-Ting.html
private static final String TING_SOUND =Ting-Popup_Pixels-349896185.wav;

private static AudioClip ting = new AudioClip(
BeatIndicator.class.getResource(TING_SOUND).toExternalForm()
);

公共BeatIndicator(节拍器节拍器){
super(10,Color.RED);
ChangeListener< Beat> beatChangeListener =(observable,oldValue,newValue) - > {
ting.play();
setFill(newValue.getTickTock()== 0?Color.GREEN:Color.ORANGE);
};

DropShadow dropShadow = new DropShadow(5,(Color)getFill());
fillProperty()。addListener((observable,oldValue,newValue) - >
dropShadow.setColor((Color)newValue)
);

Glow beatEffect = new Glow();
beatEffect.setInput(dropShadow);

metronome.isRunningProperty()。addListener((observable,oldValue,newValue) - > {
if(newValue){
setFill(Color.GREEN);
setEffect(beatEffect);
metronome.beatProperty()。addListener(beatChangeListener);
} else {
metronome.beatProperty()。removeListener(beatChangeListener);
setFill(Color .RED);
setEffect(null);
}
});
}
}

类节拍器{
私人最终双倍DEFAULT_TEMPO = 60;
private ReadOnlyObjectWrapper< Beat> beat = new ReadOnlyObjectWrapper<>(null);

私人时间线时间轴=新时间轴();

//速度以每分钟节拍数衡量。
private DoubleProperty tempo = new SimpleDoubleProperty(DEFAULT_TEMPO);
private ReadOnlyBooleanWrapper isRunning = new ReadOnlyBooleanWrapper(false);

private int tickTock = 0;

public Metronome(){
timeline.getKeyFrames()。addAll(
new KeyFrame(Duration.seconds(0),event - > {
beat。 set(new Beat(tickTock,timeline.getCurrentTime()));
tickTock =(tickTock + 1)%2;
}),
new KeyFrame(
Duration.seconds (1)

);

tempo.addListener((observable,oldValue,newValue) - >
timeline.setRate(newValue.doubleValue()/ 60.0)
);
timeline.setRate(tempo.getValue()/ 60.0);
timeline.setCycleCount(Timeline.INDEFINITE);
}

public void start(){
tickTock = 0;
isRunning.set(true);
timeline.playFromStart();
}

public void stop(){
timeline.stop();
isRunning.set(false);
}

public double getTempo(){
return tempo.get();
}

public DoubleProperty tempoProperty(){
return tempo;
}

public void setTempo(double tempo){
this.tempo.set(tempo);
}

public ReadOnlyObjectProperty< Beat> beatProperty(){
return beat.getReadOnlyProperty();
}

public ReadOnlyBooleanProperty isRunningProperty(){
return isRunning.getReadOnlyProperty();
}
}

类Beat {
private final持续时间currentTime;
// tickTock在交替生成的节拍上将切换从1变为零。
private final int tickTock;

public Beat(int tickTock,Duration currentTime){
this.currentTime = currentTime;
this.tickTock = tickTock;
}

public int getTickTock(){
return tickTock;
}

public Duration getCurrentTime(){
return currentTime;
}
}


I have a simple synthesizer program using JavaFX. I am trying to create a new class called Metronome to interact with the MainController class. I need the Metronome to run on it's own thread but still be able to run methods from the MainController, specifically on each beat. For example, when the metronome is turned on, it needs to (on it's own thread) set the fill color of a shape and make a sound through a method in the MainController. It will continuously change the color(off and on) and make the sound on a delayed loop until a method from the MainController stops the loop. How can I have the Metronome class communicate with my MainController and back while having it run on it's own thread?

EDIT: So I basically need my run() method in the metronome class to be able to run methods in the MainController class.

public class MainController implements Initializable {

    public AudioMain audio = new AudioMain();
    @ FXML public AnchorPane mainPane;

    public boolean debugMessages = true;
    public boolean debugMessages2 = false;
    public boolean debugMessages3 = false;

    //public final int numKeys = 13;
    public int C4 = 60; //The midi pitch of C4
    public int octave = 4; //The default octave to be assigned

    public String synthType = "Saw";
    public SynthSet synth = new SynthSet(111, synthType); //Creates a new SynthSet of 13 SineWaves

    public double bpm = 120;
    public Metronome metronome = new Metronome(bpm); 

    ....some code later....

    public void toggleMetronome() {
        metronome.toggleMet();
    }

    public void lightOn() {
        metronomeLight.setFill(lightOnColor);
    }

    public void lightOff() {
        metronomeLight.setFill(lightOffColor);
    }

And then..

public class Metronome implements Runnable{

    public boolean metronomeOn = false;
    public boolean metronomeSound = true;
    public boolean metOutputMessages = true;
    public boolean tick8th = false;
    public double bpm = 20;
    public long msPerBeat = (long) (60000 / bpm); // Miliseconds per beat
    public int tickCount = 0;
    public long nano;

    public Metronome() {

    }

    public Metronome(double beat) {
        setTempo(beat);
    }

    public static void main(String args[]) {
        Metronome met = new Metronome();
        met.metOn();
    }

    @Override
    public void run() {
        System.out.println(msPerBeat);
        while (metronomeOn) {
            beat();
            delay(msPerBeat/2);
            if (tick8th) beat8th();
            delay(msPerBeat/2);
        }
    }

    public void metOn() {
        if (!metronomeOn) {
            outMessage("Starting metronome at " + bpm + " bpm");
            metronomeOn = true;
            new Thread(this).start();
        }
    }

    public void metOff() {
        if (metronomeOn) {
            outMessage("Stopping metronome");
            metronomeOn = false;
        }
    }
    public void toggleMet() {
        if (metronomeOn) {
            metOff();
        }else if (!metronomeOn)
            metOn();
    }

    public void beat() {
        tickCount++;
        outMessage("Beep " + tickCount);
    }
}

解决方案

Sample of a Timeline based Metronome with some visual controls and Metronome beat timing indicator.

Sorry its a bunch of code. You can make it more concise by not having separate classes for each concept and just inlining everything, but I find defining separate objects better once things start to get a bit non-trivial (as in this case).

The Metronome class generates beats via an observable property and the other classes use listeners to react to the beats.

import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.beans.value.ChangeListener;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.effect.*;
import javafx.scene.layout.*;
import javafx.scene.media.AudioClip;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class MetroGnome extends Application {
    public void start(Stage stage) {
        Metronome metronome = new Metronome();
        TempoControl tempoControl = new TempoControl(metronome);
        BeatIndicator beatIndicator = new BeatIndicator(metronome);
        PlayControl playControl = new PlayControl(metronome);

        HBox layout = new HBox(10, playControl, tempoControl, beatIndicator);
        layout.setAlignment(Pos.CENTER);
        layout.setPadding(new Insets(10));

        stage.setScene(new Scene(layout));
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

class PlayControl extends ToggleButton {
    public PlayControl(Metronome metronome) {
        super("Start");

        setOnAction(event -> {
            if (isSelected()) {
                metronome.start();
                setText("Stop");
            } else {
                metronome.stop();
                setText("Start");
            }
        });
    }
}

class TempoControl extends VBox {
    private static final int MIN_TEMPO = 20;
    private static final int MAX_TEMPO = 240;
    private static final int DEFAULT_TEMPO = 120;

    private Slider tempoSlider = new Slider(MIN_TEMPO, MAX_TEMPO, DEFAULT_TEMPO);
    private Label tempoLabel = new Label(tempoSlider.getValue() + "");

    public TempoControl(Metronome metronome) {
        super(5);

        tempoLabel.textProperty().bind(Bindings.format("%.0f", tempoSlider.valueProperty()));
        setAlignment(Pos.CENTER);
        getChildren().setAll(tempoLabel, tempoSlider);

        metronome.setTempo(tempoSlider.getValue());
        metronome.tempoProperty().bind(tempoSlider.valueProperty());
    }

    public DoubleProperty valueProperty() {
        return tempoSlider.valueProperty();
    }
}

class BeatIndicator extends Circle {
    // Ting sound from: http://soundbible.com/1628-Ting.html
    private static final String TING_SOUND = "Ting-Popup_Pixels-349896185.wav";

    private static AudioClip ting = new AudioClip(
            BeatIndicator.class.getResource(TING_SOUND).toExternalForm()
    );

    public BeatIndicator(Metronome metronome) {
        super(10, Color.RED);
        ChangeListener<Beat> beatChangeListener = (observable, oldValue, newValue) -> {
            ting.play();
            setFill(newValue.getTickTock() == 0 ? Color.GREEN : Color.ORANGE);
        };

        DropShadow dropShadow = new DropShadow(5, (Color) getFill());
        fillProperty().addListener((observable, oldValue, newValue) ->
                dropShadow.setColor((Color) newValue)
        );

        Glow beatEffect = new Glow();
        beatEffect.setInput(dropShadow);

        metronome.isRunningProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue) {
                setFill(Color.GREEN);
                setEffect(beatEffect);
                metronome.beatProperty().addListener(beatChangeListener);
            } else {
                metronome.beatProperty().removeListener(beatChangeListener);
                setFill(Color.RED);
                setEffect(null);
            }
        });
    }
}

class Metronome {
    private final double DEFAULT_TEMPO = 60;
    private ReadOnlyObjectWrapper<Beat> beat = new ReadOnlyObjectWrapper<>(null);

    private Timeline timeline = new Timeline();

    // tempo is measured in beats per minute.
    private DoubleProperty tempo = new SimpleDoubleProperty(DEFAULT_TEMPO);
    private ReadOnlyBooleanWrapper isRunning = new ReadOnlyBooleanWrapper(false);

    private int tickTock = 0;

    public Metronome() {
        timeline.getKeyFrames().addAll(
                new KeyFrame(Duration.seconds(0), event -> {
                    beat.set(new Beat(tickTock, timeline.getCurrentTime()));
                    tickTock = (tickTock + 1) % 2;
                }),
                new KeyFrame(
                        Duration.seconds(1)
                )
        );

        tempo.addListener((observable, oldValue, newValue) ->
                timeline.setRate(newValue.doubleValue() / 60.0)
        );
        timeline.setRate(tempo.getValue() / 60.0);
        timeline.setCycleCount(Timeline.INDEFINITE);
    }

    public void start() {
        tickTock = 0;
        isRunning.set(true);
        timeline.playFromStart();
    }

    public void stop() {
        timeline.stop();
        isRunning.set(false);
    }

    public double getTempo() {
        return tempo.get();
    }

    public DoubleProperty tempoProperty() {
        return tempo;
    }

    public void setTempo(double tempo) {
        this.tempo.set(tempo);
    }

    public ReadOnlyObjectProperty<Beat> beatProperty() {
        return beat.getReadOnlyProperty();
    }

    public ReadOnlyBooleanProperty isRunningProperty() {
        return isRunning.getReadOnlyProperty();
    }
}

class Beat {
    private final Duration currentTime;
    // tickTock varies switches from one to zero on alternate generated beats.
    private final int tickTock;

    public Beat(int tickTock, Duration currentTime) {
        this.currentTime = currentTime;
        this.tickTock = tickTock;
    }

    public int getTickTock() {
        return tickTock;
    }

    public Duration getCurrentTime() {
        return currentTime;
    }
}

这篇关于Java:如何使单独的类与线程上的MainController进行通信的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆