I have a music player that has a ProgressBar that has a max value of mediaPlayer.getTotalDuration().toSeconds()
Recently I have been trying to make a MouseListener to seek the mediaPlayer to the returned X value when the ProgressBar is clicked on a certain position.
The problem: I click on the ProgressBar and it appears to be receiving milliseconds so I multiply it by 1000 so it seeks to the corresponding second-count.
This works accurately for some music/mp3s but for shorter ones, or some longer ones, the ProgressBar only jumps to the nearest possible position, or jumps to some other position, completely inaccurate due to the * 1000 calculation of the X value. (Below I've tried someone's suggestion for another answer to calculate the X value to seconds and I have also tried setting the ProgressBar value and setting that value to where the mediaPlayer is.)
"int point" is where I receive the X value.
Code:
progressBar.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent mouseClick) {
try{
progressBar.setMaximum((int)mediaPlayer.getTotalDuration().toSeconds());
int point = (int)Math.round(((double)mouseClick.getX() / (double)progressBar.getWidth()) * progressBar.getMaximum()); // previously tried "* 1000".
Duration pointDuration = new Duration(point);
mediaPlayer.seek(pointDuration);
} catch (Exception e7){
//
}
}
});
(Unfortunately that calculation is even worse.)
What sort of calculation should I use to correctly seek to the clicked position?
You have to choose your units wisely, and to make them appear in your code. What I usually do is to limit the progressbar to a [0.0,1.0] range, i.e. a percentage with double arithmetic.
When you receive a mouse click event you convert the event position in that [0,1] range.
Two things are needed:
the width (in pixels) of the progressbar
the x-coordinate of the mouseclick relative to progressbar component
And you have an accurate point (note toMilliseconds):
double dx = evt.getX();
double dwidth = progressBar.getWidth();
double progression = (dx / dwidth);
int milliseconds = (progression * mediaPlayer.getTotalDuration().toMilliseconds())
Duration duration = new Duration(milliseconds);
mediaPlayer.seek(duration);
Well 1st mistake you make is setting progressBar maximum value when you click the progress bar. While media is playing and you did not pressed progressBar yet the max value of progres bar is wrong. This value should be set when the mediaPlayer duration is changed. You should add listener to totalDurationProperty change in same method that mediaPlayer got media assigned.
Media media = new Media(filePath);
mediaPlayer = new MediaPlayer(media);
mainMediaView.setMediaPlayer(mediaPlayer);
mediaPlayer.totalDurationProperty().addListener(new ChangeListener<Duration>() {
#Override
public void changed(ObservableValue<? extends Duration> observable, Duration oldValue,
Duration newValue) {
progressBar.setMax(newValue.toSeconds());
}
});
2nd problem is that Slider component and its progress bar have different sizes.
progressBar mouse listener:
progressBar.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent mouseClick) {
try{
double value;
value = (mouseClick.getX()-9) * (progressBar.getMax() / (progressBar.getWidth()-19));
progressBar.setValue(value);
} catch (Exception e7){
}
}
});
You might ask how did -9 and -19 showed up in there. Got those values by trial and error. As Slider component is larger than its progress bar getWidth() returns larger value. That includes gap on both sides of progress bar. -9 is for adjusting click position to gap between slider component and beginning of progress bar. -19 thats slider component width smaller by gaps on both sides of progress bar.
Related
I am working on a "Simon Says" pattern game. The player must repeat an increasingly long pattern till they mess up.
The game consists of a 2x2 grid of colors, each with a darker one behind it. By changing the alpha from 1--->0---->1. I give the effect of clicking.
Below is the code I am using to display the pattern as it increases, it's called whenever the pattern resets or increases. But currently it always skips the last value in the pattern, and if the same color is repeated it does not flash again, it simply remains dark, what am I doing wrong?
Helpful Information:
Pattern is the arraylist where I am storing the pattern the user must repeat.
getColorById returns the corresponding ImageView for each color [0-3]
TLDR:
I need to flash every single item in an arrayList called pattern, so the player can see it and repeat it, yet currently it skips the last item and if the same item is repeated it simply stays flashed versus flashing twice.
EDIT: Sometimes, the last item in the sequence does show, but it is very random on whether it shows or not.
public void flashPattern(View view){
final Queue<Integer> queue = new LinkedList<>(pattern);
final int timeBetweenSteps = 500;
new CountDownTimer(pattern.size() * timeBetweenSteps, timeBetweenSteps) {
private ImageView lastLight = null;
#Override
public void onTick(long millisUntilFinished) {
if (lastLight != null) {
lastLight.animate().alpha(1f).setDuration(250);
}
lastLight = getColorById(queue.remove());
lastLight.animate().alpha(0f).setDuration(250);
}
#Override
public void onFinish() {
if (lastLight != null) {
lastLight.animate().alpha(1f).setDuration(250);
}
}
}.start();
}
I am trying to code my way into displaying in a label(like a tooltip) the current value of a position in a slider when I drag the slider thumb. For example: if a slider has Integer values from 0 to 100, and I drag the slider thumb to the middle of the slider, a small popup will show me that I am currently in position 50.
So far, that's what I managed to do:
final Label textLabel = new Label("Volume");
final Slider volumeSlider = new Slider();
volumeSlider.valueProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue arg0, Object arg1, Object arg2) {
textLabel.textProperty().setValue(
String.valueOf((int) volumeSlider.getValue()));
}
});
root.getChildren().addAll(volumeSlider, textLabel);
textLabel.textProperty().setValue("abc");
It doesn't display on screen the values.
Also, if any of you happen to know, how do I set a starting point in a slider track? Like, if I wanted the program to start and the slider thumb to start in the middle of the slider(value 50) instead of starting on the lowest part.
I'd like to ask is it possible to stop knob moving further upon hit certain value in JScrollBar ?
For example, set the minimum and maximum value of a vertical scroll bar to 0 and 100. If the user scroll the knob till 60, he or she should not be able to move knob up further but able to move knob down.
Hence, I have simply wrote following adjust listener:
class MyAdjustmentListener2 implements AdjustmentListener
{
public void adjustmentValueChanged(AdjustmentEvent e)
{
label.setText("New Value is " + e.getValue() + " ");
if (e.getValue() < 60 )
{
vbar.setValue(60);
}
}
}
The label is a JLabel I used to monitor the adjusted value, the vbar is a vertical JScrollBar instance. Above code will force the knob jump back to value 60 if user try to move it above the value 60. This slightly different from what I want to achieve.
I have looked into the API of JScrollBar, I didn't found any straight method provided to use. Please share some hints to me here. Thank you for your time and suggestion.
I am trying to make a player in java.
Have made a seekbar using jprogressbar as shown in this link in Andrew Thompson's answer,
I have been able to add a mouselistener and detect click on jprogressbar, but how do I get the selected value of jprogressbar to which I will seek my bar to?
I tried,
progressBar.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
int v = progressBar.getSelectedValue();
jlabel.setText("----"+v);
}
});
But didn't work as I expected, could not even find anything on internet.
Please help me. Thanks for your time and effort, really appreciated.
You would probably have to calculate the location on the JProgressBar based solely on the mouse click co-ordinates. You could essential do this:
progressBar.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
int v = progressBar.getValue();
jlabel.setText("----"+v);
//Retrieves the mouse position relative to the component origin.
int mouseX = e.getX();
//Computes how far along the mouse is relative to the component width then multiply it by the progress bar's maximum value.
int progressBarVal = (int)Math.round(((double)mouseX / (double)progressBar.getWidth()) * progressBar.getMaximum());
progressBar.setValue(progressBarVal);
}
});
Whenever I click a JSlider it gets positioned one majorTick in the direction of the click instead of jumping to the spot I actually click. (If slider is at point 47 and I click 5 it'll jump to 37 instead of 5). Is there any way to change this while using JSliders, or do I have to use another datastructure?
As bizarre as this might seem, it's actually the Look and Feel which controls this behaviour. Take a look at BasicSliderUI, the method that you need to override is scrollDueToClickInTrack(int).
In order to set the value of the JSlider to the nearest value to where the user clicked on the track, you'd need to do some fancy pants translation between the mouse coordinates from getMousePosition() to a valid track value, taking into account the position of the Component, it's orientation, size and distance between ticks etc. Luckily, BasicSliderUI gives us two handy functions to do this: valueForXPosition(int xPos) and valueForYPosition(int yPos):
JSlider slider = new JSlider(JSlider.HORIZONTAL);
slider.setUI(new MetalSliderUI() {
protected void scrollDueToClickInTrack(int direction) {
// this is the default behaviour, let's comment that out
//scrollByBlock(direction);
int value = slider.getValue();
if (slider.getOrientation() == JSlider.HORIZONTAL) {
value = this.valueForXPosition(slider.getMousePosition().x);
} else if (slider.getOrientation() == JSlider.VERTICAL) {
value = this.valueForYPosition(slider.getMousePosition().y);
}
slider.setValue(value);
}
});
This question is kind of old, but I just ran across this problem myself. This is my solution:
JSlider slider = new JSlider(/* your options here if desired */) {
{
MouseListener[] listeners = getMouseListeners();
for (MouseListener l : listeners)
removeMouseListener(l); // remove UI-installed TrackListener
final BasicSliderUI ui = (BasicSliderUI) getUI();
BasicSliderUI.TrackListener tl = ui.new TrackListener() {
// this is where we jump to absolute value of click
#Override public void mouseClicked(MouseEvent e) {
Point p = e.getPoint();
int value = ui.valueForXPosition(p.x);
setValue(value);
}
// disable check that will invoke scrollDueToClickInTrack
#Override public boolean shouldScroll(int dir) {
return false;
}
};
addMouseListener(tl);
}
};
This behavior is derived from OS. Are you sure you want to redefine it and confuse users? I don't think so. ;)