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.
Related
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.
I have a ripple button that once it's clicked, plays a ripple effect animation.
Inside of the button's skin, there's a Circle whose opacity is set to 0 at the beginning of the execution of the program and once the button is clicked, the opacity is set to 1 and the radius of the button gets larger.
There is a DoubleBinding binding, whose definition is the following:
DoubleBinding circleRippleRadius = new DoubleBinding() {
{
bind(heightProperty(), widthProperty()); // Sets the parameters of the bind method.
}
#Override // Overrides the computeValue method, which computes the value of the binding.
protected double computeValue() {
return Math.max(heightProperty().get(), widthProperty().get()); // Return the greatest of both numbers
}
};
Instead of using the button's heightProperty and widthProperty properties, I'd like to use the height and width of the scene to which the button is added, since I want the circle that appears once the button is clicked to fill the entire screen.
How could I achieve this?
UPDATE: Here is the code that defines the animation's component's values and the animation itself:
private void createRippleEffect(Circle circleRipple) {
circleRipple.setOpacity(1.0); // Sets the opacity of the circleRipple to 0, since it must not be showed yet.
/*Fade Transition*/
FadeTransition fadeTransition = new FadeTransition(RIPPLE_DURATION, circleRipple);
fadeTransition.setInterpolator(Interpolator.EASE_OUT);
fadeTransition.setFromValue(1.0); // Sets the opacity to %100
fadeTransition.setToValue(1.0); // The opacity doesn't change.
/*Scale Transition*/
Timeline scaleRippleTimeline = new Timeline();
NumberBinding circleRippleRadius =
Bindings.max(Bindings.selectDouble(sceneProperty(), "width"),
Bindings.selectDouble(sceneProperty(), "height"));
circleRippleRadius.addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> { // Each time it changes
KeyValue scaleValue = new KeyValue(circleRipple.radiusProperty(), newValue, Interpolator.EASE_OUT);
KeyFrame scaleFrame = new KeyFrame(RIPPLE_DURATION, scaleValue);
scaleRippleTimeline.getKeyFrames().add(scaleFrame);
});
private void createRippleEffect(Circle circleRipple) {
circleRipple.setOpacity(1.0); // Sets the opacity of the circleRipple to 0, since it must not be showed yet.
/*Fade Transition*/
FadeTransition fadeTransition = new FadeTransition(RIPPLE_DURATION, circleRipple);
fadeTransition.setInterpolator(Interpolator.EASE_OUT);
fadeTransition.setFromValue(1.0); // Sets the opacity to %100
fadeTransition.setToValue(1.0); // The opacity doesn't change.
/*Scale Transition*/
Timeline scaleRippleTimeline = new Timeline();
NumberBinding circleRippleRadius =
Bindings.max(Bindings.selectDouble(sceneProperty(), "width"),
Bindings.selectDouble(sceneProperty(), "height"));
circleRippleRadius.addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> { // Each time it changes
KeyValue scaleValue = new KeyValue(circleRipple.radiusProperty(), newValue, Interpolator.EASE_OUT);
KeyFrame scaleFrame = new KeyFrame(RIPPLE_DURATION, scaleValue);
scaleRippleTimeline.getKeyFrames().add(scaleFrame);
});
SequentialTransition rippleTransition = new SequentialTransition(); // The circle must change its opacity and scale at the same time
rippleTransition.getChildren().addAll(
scaleRippleTimeline,
fadeTransition
);
ParallelTransition parallelTransition = new ParallelTransition();
getStyleClass().addListener((ListChangeListener.Change<? extends String> c) -> { // Don't pay attention to this. The style changes if
// the CSS file has "toggle" or "flat" inside its list,
// but these are never added so it doesn't matter.
if (c.getList().indexOf("flat") == -1 && c.getList().indexOf("toggle") == -1) {
setMinWidth(88);
setEffect(new DropShadow(BlurType.GAUSSIAN, Color.rgb(0, 0, 0, 0.30), 5, 0.10, 0, 2));
parallelTransition.getChildren().addAll(rippleTransition); // parallelTransition is basically the same as rippleTransition, since
// "toggle" and "flat" are never added to the CSS's list.
} else {
parallelTransition.getChildren().addAll(rippleTransition);
setMinWidth(USE_COMPUTED_SIZE);
setEffect(null);
}
});
this.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
parallelTransition.stop(); // In case that the parallelTransition is already running, stop it.
circleRipple.setOpacity(0.0); // Sets the opacity of the circle to 0, since the animation must play from he beginning.
circleRipple.setRadius(0.1); // Sets the radius to 0.1 for the same reason as the circle's opacity.
circleRipple.setCenterX(event.getX()); // The center of the circle is the location in which the mouse was clicked.
circleRipple.setCenterY(event.getY()); // The center of the circle is the location in which the mouse was clicked.
parallelTransition.playFromStart(); // Plays the animation.
});
}
being this the method that defines the button's skin:
#Override
public Skin<?> createDefaultSkin() {
ButtonSkin buttonSkin = getButtonSkin();
if (buttonSkin == null) {
buttonSkin = new ButtonSkin(this);
Circle circleRipple = new Circle(0.1, RIPPLE_COLOR);
buttonSkin.getChildren().add(0, circleRipple);
setSkin(buttonSkin);
createRippleEffect(circleRipple);
getStyleClass().add("ripple-button"); // What the CSS does is changing the button's color and text size, nothing important.
}
return buttonSkin;
}
Thanks in advance.
SOLUTION:
The problem of the circleRippleRadius's value being shorter than what the stage's one was probably due to that the stage's size was different than the scene's. I didn't know that, now I do.
For having the button's circle filling the entire screen, all I had to do was passing the widthProperty and heightProperty properties of the stage as parameters through the button's constructor.
Inside of the button's class, I create two ReadOnlyDoubleProperty properties for the width and the height that are empty until a button is created; in which case, the values of the defined ReadOnlyDoubleProperty properties are overwritten by the value of the widthProperty and heightProperty properties passed as parameters.
Having done that, all what I have to do is adding a listener to each property and changing the value of the circleRippleRadius to the greater of the properties' values each time that one of them changes.
You should not be extending DoubleBinding. Instead, you should create bindings from the existing factory methods:
NumberBinding circleRippleRadius =
Bindings.max(widthProperty(), heightProperty());
// Optional
DoubleExpression circleRippleRadiusAsDouble =
DoubleExpression.doubleExpression(circleRippleRadius);
To bind to scene properties, you want to use Bindings.selectDouble, which can handle the initially null scene property of your button:
NumberBinding size =
Bindings.max(
Bindings.selectDouble(button.sceneProperty(), "width"),
Bindings.selectDouble(button.sceneProperty(), "height"));
Basically, I'm trying to make a button that has the text aligned to the left (so I'm using setHorizontalAlignment(SwingConstants.LEFT)) and the image on the right border of the button, far from the text.
I already tried setHorizontalTextAlignment(SwingConstants.LEFT), but that just makes the text go relativity to the left of the icon, which is not exactly what I want, since I needed the icon to be secluded from it.
Also, I can't make any fixed spacing because it's a series of buttons with different texts with different sizes.
I can't make any fixed spacing because it's a series of buttons with different texts with different sizes.
You can dynamically change the spacing with code like:
JButton button = new JButton("Text on left:")
{
#Override
public void doLayout()
{
super.doLayout();
int preferredWidth = getPreferredSize().width;
int actualWidth = getSize().width;
if (actualWidth != preferredWidth)
{
int gap = getIconTextGap() + actualWidth - preferredWidth;
gap = Math.max(gap, UIManager.getInt("Button.iconTextGap"));
setIconTextGap(gap);
}
}
};
button.setIcon( new ImageIcon("copy16.gif") );
button.setHorizontalTextPosition(SwingConstants.LEADING);
This is a derivative of camickr's answer to allow editing in a GUI builder as well as placing it in a dynamic layout. I also removed the UIManager.getInt("Button.iconTextGap") so the gap will shrink to 0 if necessary.
I called it a 'Justified' button in analogy with justified text alignment (stretches a paragraph to left & right by growing width of space characters).
public class JustifiedButton extends JButton {
#Override
public void doLayout() {
super.doLayout();
setIconTextGap(0);
if (getHorizontalTextPosition() != CENTER) {
int newGap = getSize().width - getMinimumSize().width;
if (newGap > 0)
setIconTextGap(newGap);
}
}
#Override
public Dimension getMinimumSize() {
Dimension minimumSize = super.getMinimumSize();
if (getHorizontalTextPosition() != CENTER)
minimumSize.width -= getIconTextGap();
return minimumSize;
}
#Override
public Dimension getPreferredSize() {
Dimension preferredSize = super.getPreferredSize();
if (getHorizontalTextPosition() != CENTER)
preferredSize.width -= getIconTextGap();
return preferredSize;
}
}
This is not exactly production-ready and needs some field-testing. If I find anything, I'll edit the code.
[edit] Now works for vertical text alignments. Also simplified a bit.
[edit2] Also manipulate getPreferredSize to play nice with scroll pane (otherwise it keeps growing and never shrinks again)
You can add a layout manager to your button.
JButton btn = new JButton();
btn.add(new JLabel(text));
btn.add(new JLabel(img));
btn.setLayout(/*best layout choice here*/);
btn.setPreferredSize(new Dimension(x,y));
btn.setMaximumSize(new Dimension(maxX, minY));
btn.setMinimumSize(new Dimension(minX, minY)); //this one is most important when it comes to layoutmanagers
Sorry I can't be much help when it comes to picking out a good layout - But this will eventually get you what you want. Maybe someone else can comment on which one to use.
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);
}
});
I am using JProgressBar to display a progress bar on my frame. The progress bar is set to indeterminate mode as i dont know when the task will end. Instead of displaying the normal progress bar a wierd orange wave is displayed.
The wave keeps moving when the task is running. After it has ended the value is set to 100 and it displays it in the form or orange blocks(which are also moving!). I am using the following code to display the progress bar
Container content = this.getContentPane();
content.setLayout(null);
prog = new JProgressBar(0, 100);
prog.setValue(0);
prog.setStringPainted(true);
Dimension preferredSize;
preferredSize=new Dimension();
preferredSize.width=300;
preferredSize.height=15;
prog.setPreferredSize(preferredSize);
content.add(prog);
Insets insets = content.getInsets();
Dimension size;
size = prog.getPreferredSize();
prog.setBounds(30+insets.left, 180+insets.top, size.width, size.height);
How do i change it back to the normal progress bar?
I didn't look deep into it, but it might be a bug of Nimbus LaF.
Anyway, in order for the orange blocks to stop moving (when its value is set to 100), you also seem to need to call:
prog.setIndeterminate(false);
If you want to "automate" this, you could subclass JProgressBar, e.g.:
prog = new JProgressBar(0, 100) {
public void setValue(int newValue) {
super.setValue(newValue);
if (newValue >= this.getMaximum()) {
this.setIndeterminate(false);
}
}
};
prog.setValue(0);
...