I have a calendar screen I have designed for class using JavaFX and Scene Builder.
The part where I've placed the number is a label and the part that, currently, says "None", is a button. I want to reference the value in the Label when I select the Button so that I can display the Appointments for the user for that day.
Is there a way to reference a control by by the FX:ID string name so that I can do this? The Label is called lblDayOfTheWeekxx and the button is called btnAppointmentxx, where xx is the value from 01 to 42.
This is what I have tried for changing the value. It's just a test where I'm trying to turn the value of the first button to "DONE".
#FXML
void handleDayChosen(ActionEvent event) {
try {
// FXMLLoader loader = new FXMLLoader();
// loader.setLocation(getClass().getResource("/Views/FormMain.fxml"));
// Parent myView = loader.load();
// Scene myScene = new Scene(myView);
// Label lbl = (Label) myScene.lookup("#lblDateOfMonth01");
// Label myLblField = (Label)myView.FindControl("txtField" + 1);
// lbl.setText("DONE");
FXMLLoader loader = new FXMLLoader(getClass().getResource("/Views/FormMain.fxml"));
Parent root = loader.load();
Button foo = (Button)loader.getNamespace().get("lblAppointments01");
foo.setText("DONE");
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
I'm new to Java so I don't know what I'm doing wrong.
Thanks in advance,
Bill
This is what I finally came up with. I know it isn't pretty but it works.
GridPane gpCal = this.gpCalendar;
for (int i = 1 ; i <= 7; i++) {
int row = i * 2;
for (int col = 1; col <= 7; col++) {
int pos = ((i-1)*7)+col;
lblDay[pos] = new Label();
lblDay[pos].setText("");
lblDay[pos].setPrefSize(100, 20);
lblDay[pos].setText(String.valueOf(col) + ", " + String.valueOf(row));
gpCal.add(lblDay[pos], col, row);
}
row++;
for (int col = 0; col <= 6; col++) {
int pos = ((i-1)*7)+col;
btnAppts[pos] = new Button();
btnAppts[pos].setText("");
btnAppts[pos].setPrefSize(100, 100);
btnAppts[pos].setText(String.valueOf(col) + ", " + String.valueOf(row));
gpCal.add(btnAppts[pos], col, row);
}
}
Now for the easy part of formatting the buttons and labels to kind of match below.
Thanks for the help,
Bill
UIs like this do not lend themselves at all well to FXML. It is usually much easier, and way less code, to create a UI like this using Java. That way you can create the button and label in a loop, and add a different event handler to each:
int numDays = 30 ; // in real life this comes from the month and year
GridPane calendarPane = ...; // can be a #FXML-injected variable if needed
for (int i = 1 ; i <= numDays ; i++) {
Label dayLabel = new Label(Integer.toString(i));
Button button = new Button("None");
// set styles, etc
int day = i ;
button.setOnAction(e -> processButtonPress(button, dayLabel, day));
VBox vbox = new VBox(dayLabel, button);
int row = getRowForDay(i);
int col = getColumnForDay(i);
calendarPane.add(vbox, col, row);
}
// ...
private void handleDayChosen(Button button, Label label, int dayOfMonth) {
// whatever you need here...
label.setTextFill(Color.GREEN);
button.setText("Done");
}
Obviously you can still use FXML for the surrounding UI if you want, and just put the loop above in the controller's initialize() method. But this is clearly better than the 100+ lines of FXML plus 60 different variables in the controller to achieve the same thing.
Related
So i have a barchart in MPandroid, and i need to put the names on the chart, but there is no space where i want them, the solution is to put them on top of the chart. Like this:
can someone help me achieve this ?
There isn't a built-in way of putting the text in a non-standard location, but you can do it by writing a custom renderer for your chart. If you extend the HorizontalBarChartRenderer class and override the drawValues method (making just a few modifications to the original version in the code linked) you can change where the label gets drawn.
Custom Renderer
Code copied from drawValues in HorizontalBarChartRenderer and simplified to only include required parts for this use-case (removed logic for stacked charts, icons, toggling values on and off, etc). The only change is to the x,y values passed to drawValue()
private static class MyRenderer extends HorizontalBarChartRenderer {
public MyRenderer(BarDataProvider chart, ChartAnimator animator,
ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
}
#Override
public void drawValues(Canvas c) {
List<IBarDataSet> dataSets = mChart.getBarData().getDataSets();
final float valueOffsetPlus = Utils.convertDpToPixel(5f);
for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) {
IBarDataSet dataSet = dataSets.get(i);
// apply the text-styling defined by the DataSet
applyValueTextStyle(dataSet);
ValueFormatter formatter = dataSet.getValueFormatter();
// get the buffer
BarBuffer buffer = mBarBuffers[i];
for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) {
if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 1]))
break;
if (!mViewPortHandler.isInBoundsX(buffer.buffer[j]))
continue;
if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1]))
continue;
BarEntry entry = dataSet.getEntryForIndex(j / 4);
String formattedValue = formatter.getBarLabel(entry);
// Modify the x, y position here to control where the
// text is. The "buffer" array gives the positions of
// the current bar (in pixels)
drawValue(c,
formattedValue,
buffer.buffer[j] + valueOffsetPlus,
buffer.buffer[j+1] - valueOffsetPlus,
dataSet.getValueTextColor(j / 2));
}
}
}
}
Example Use
To use the custom renderer, set it on the chart and modify the bar widths to leave room for the text.
HorizontalBarChart chart = findViewById(R.id.chart);
List<Float> values = Arrays.asList(3f, 2f, 2.2f);
List<Integer> colors = Arrays.asList(Color.CYAN, Color.RED, Color.GREEN);
List<String> labels = Arrays.asList("Pickled Horses", "Horses", "Pickles");
List<BarEntry> entries = new ArrayList<>();
for(int i = 0; i < values.size(); ++i) {
entries.add(new BarEntry(i+1, values.get(i), labels.get(i)));
}
BarDataSet ds = new BarDataSet(entries, "Values");
ds.setColors(colors);
ds.setValueTextSize(14f);
ds.setValueFormatter(new ValueFormatter() {
#Override
public String getBarLabel(BarEntry barEntry) {
return (String)barEntry.getData();
}
});
ds.setAxisDependency(YAxis.AxisDependency.RIGHT);
BarData data = new BarData(ds);
// Reduce bar width to leave room for text
data.setBarWidth(0.75f);
// Use the custom renderer
chart.setRenderer(new MyRenderer(chart, chart.getAnimator(), chart.getViewPortHandler()));
// Misc formatting & setup
chart.setData(data);
chart.getAxisLeft().setEnabled(false);
chart.getAxisRight().setAxisMinimum(0f);
chart.getAxisRight().setAxisMaximum(4f);
chart.getAxisRight().setGranularity(1f);
chart.getAxisRight().setTextSize(14f);
chart.getAxisRight().setDrawGridLines(false);
chart.getXAxis().setDrawLabels(false);
chart.getXAxis().setDrawGridLines(false);
chart.getXAxis().setAxisMaximum(3.6f);
chart.getDescription().setEnabled(false);
chart.getLegend().setEnabled(false);
chart.setBorderColor(Color.BLACK);
chart.setBorderWidth(1f);
chart.setDrawBorders(true);
I wrote this java -fx program and it works perfectly without the recursive implementations. I wrote the button onAction lambda to ensure the program works correctly before converting to recursion. I've spent the last few hours trying to figure out the two required recursives and how to call them with the button.onAction lambda expression, but need a push in the right direction. Here's what I have.
static TextField textField = new TextField ();
static String text = textField.getText();
static TextArea textArea = new TextArea();
static Button btSubmit = new Button ("Submit");
#Override
public void start(Stage primaryStage){
Label label1 = new Label("Enter letters and I will "
+ "count the capitals.\t"); //Create textfield label.
textArea.setEditable(false);
textArea.setMaxWidth(450);
textArea.setMaxHeight(100);
HBox hbox = new HBox(); //Create hbox.
hbox.setAlignment(Pos.BASELINE_CENTER); //Set hbox to center.
hbox.getChildren().addAll(label1, textArea, textField,
btSubmit); //Add children to hbox.
BorderPane pane = new BorderPane(); //Create pane.
pane.setTop(hbox); //Set hbox to top of pane.
pane.setCenter(textArea); //Set text area to center.
Scene scene = new Scene (pane, 450, 200); //Create scene.
primaryStage.setTitle("Count Capital Letters");
primaryStage.setScene(scene);
primaryStage.show();
btSubmit.setOnAction(e -> {
String text = textField.getText();
int upperCase = 0;
for (int i = 0; i < text.length(); i++){
if (Character.isUpperCase(text.charAt(i))) upperCase++;
}
String numCaps = String.valueOf(upperCase);
textArea.appendText("Number of capitals: " + numCaps);
});
}
public static int count(char[] chars) {
String text = textField.getText();
chars = text.toCharArray();
if (chars.length == 0);
return 0;
}
public static int count(char[] chars, int high) {
high = 0;
String text = textField.getText();
chars = text.toCharArray();
for (int i = 0; i < chars.length; i++){
if (Character.isUpperCase(chars[i])) {
high++;
}
}
return high;
}
public static void main(String[] args){
launch(args);
}}
What I'm trying to do is have the button action call the recursive methods, but I'm confused on how to replace my current action with a call to the recursives.
After sleeping on it, here's what I've come up with for the two recursive methods. I knew I had two separate methods last night and that a recursive is supposed to call itself. Here's what I've changed so far.
Instead of the two separate methods:
public static int count(char[] chars, int high) {
int count = 0;
if (high < chars.length) {
if (Character.isUpperCase(chars[high])) {
return 1 + count;
}
else {
return count(chars, high+1);
}
}
return 0;
}
Am I at least on the right track? Being required to use the two recursive methods (original and its helper) is throwing me off.
It's not clear from your code what high is supposed to be. I would assume it's supposed to be the index of the last character in the character array you are going to examine (probably exclusive).
So your recursive step would really need to reduce high, not increase it, and you just examine chars[high-1] directly. You also need a termination step, which is when there are no more characters to look at.
So I would implement this as
private int count(char[] chars, int high) {
// if high==0 we are looking at zero characters, so there are no upper-case characters:
if (high == 0) {
return 0 ;
}
if (Character.isUpperCase(chars[high-1])) {
// if the last character we look at is upper-case, then the total
// for this set of characters is one more than the total for the
// set of characters omitting the last one:
return 1 + count(chars, high-1);
} else {
// otherwise (the last character is not upper-case), the total
// for this set of characters is the same as the total for the
// set of characters omitting the last one:
return count(chars, high-1);
}
}
For convenience, you could also implement
public int count(String text) {
return count(text.getChars(), text.length());
}
and then your event handler just needs to call
label1.setText("Number of upper case: "+count(textField.getText()));
(Why on earth you made everything static is a whole other discussion.)
I have 34 labels with images i can't figure how do i make when ill click the label itself to get selected and in the down right corner that "Selected: " to get changed on every label select.
The labels variable names are from n1 to n34 i have this code so far but in the list getSelectedNumbers()
List<JLabel> lotteryBoxes = new ArrayList<>();
List<JLabel> getSelectedNumbers() {
List<JLabel> numbers = new ArrayList<>();
Iterator<JLabel> it = lotteryBoxes.iterator();
while (it.hasNext()) {
JLabel nr = it.next();
if (nr.isCursorSet()) {
numbers.add(nr);
Selected.setText("Selected: " + nr);
}
return numbers;
}
I do not know what to do, please give me some answers.
If you create the labels in a loop, you can add a handler to them. Either the same handler that checks which of the labels was clicked, or a separate handler for each one.
Here there is a separate handler for each and the labels are put into an array so you can use them later (outside the loop).
int numberOfLabels = 34;
JLabel[] labels = new JLabel[numberOfLabels];
for (int index=0; index<numberOfLabels; index++) {
String labelText = "" + (index + 1);
final JLabel label = new JLabel(labelText));
final int labelNumber = index + 1;
label.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
// do something, you can use "label" in here, eg:
selected.setText(label.getText());
// you have access to the number in "labelNumber"
}
});
somePanel.add(label);
labels[index] = label; // save the label if you need to access it later
}
I have a program that takes an input file, pulls a color word + hexadecimal value from it (for exaple Red 0xFF0000). I had my code working perfectly except I tried to replace my 2 arrayLists with a HashMap... That is where things took a wrong turn. I have my code back to what I believe it was before except now it is NOT changing colors when the radio buttons are pushed. Anyone want to take a peek?
public HashMapTests() {
JPanel p1 = new JPanel();
this.getContentPane().setLayout(new GridLayout(5,4));
ButtonGroup group = new ButtonGroup();
for (int i = 0; i < colorCollection.size(); i++) {
jrbColor[i] = new JRadioButton(colorCollection.get(i));
jrbColor[i].setText(colorCollection.get(i));
group.add(jrbColor[i]);
p1.add(jrbColor[i]);
}
for(int i = 0; i < colorCollection.size(); i++){
jrbColor[i].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
for (int j = 0; j < colorCollection.size(); j++){
String hexColor = hexCollection.get(j);
if(hexCollection.get(j).equals(((JRadioButton)e.getSource()).getText())){
getContentPane().setBackground(Color.decode(hexColor));
repaint();
}
}
}
});
}
add(p1);
}
First investigation:
while (colorCollection.size() < 10)
shall be replaced with
if (colorCollection.size() < 10)
Second observation:
jrbColor[i] = new JRadioButton(colorCollection.get(i));
jrbColor[i].setText(colorCollection.get(i));
The second line is useless, see constructor's javadoc.
Third:
The second loop where you attach the listener is useless, you can put this code to the first loop where you create a button.
Finally:
if (hexCollection.get(j).equals(((JRadioButton) e.getSource()).getText())) {
You compare here content of hexCollection with radio button text, but the button has label from colorCollection. I cannot look to your file but I think that this will be the problem.
Map Solution:
Initialization
String name = fileInput.next();
String hexValue = fileInput.next();
colors.put(name, hexValue);
Buttons
int i = 0;
for (String s : colors.keySet()) {
jrbColor[i] = new JRadioButton(s);
group.add(jrbColor[i]);
p1.add(jrbColor[i]);
jrbColor[i].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String hexColor = colors.get(((JRadioButton) e.getSource()).getText());
getContentPane().setBackground(Color.decode(hexColor));
}
});
}
The code below is supposed to create and object instance for a specific type (say color) JButton I want to represent in a grid. When I iterate through the for-loop to add the buttons to the jframe it adds nothing. But if I add a single instance variable it will add that. Anybody have an idea?
public class Grid {
protected JButton [][] board;
private JButton player;
private JButton openCell;
private JButton wall;
private JButton closedCell;
public Grid(String [] args) { // args unused
// Instantiation
board = new JButton [6][6];
layout = new String [6][6];
blueCell = new JButton("BLUE CELL");
redCell = new JButton("RED CELL");
greenCell = new JButton("GREEN CELL");
whiteCell = new JButton("WHITE CELL");
// Configuration (add actions later)
// Decoration
blueCell.setBackground(Color.blue);
redCell.setBackground(Color.red);
greenCell.setBackground(Color.green);
whiteCell.setBackground(Color.white);
for (int rows = 0; rows < 6; rows++) {
for (int cols = 0; cols < 6; cols++) {
if ((layout[rows][cols]).equals('*')) {
board[rows][cols] = blueCell;
}
else if ((layout[rows][cols]).equals('.')) {
board[rows][cols] = redCell;
}
else if ((layout[rows][cols]).equals('x')) {
board[rows][cols] = greenCell;
}
else {
board[rows][cols] = whiteCell;
}
}
}
JFrame game = new JFrame();
game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.setLayout(new GridLayout (6, 6));
game.setSize(500, 500);
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board.length; j++) {
if ((board[i][j]).equals(blueCell)) {
grid.add(blueCell);
}
else if ((board[i][j]).equals(redCell)) {
grid.add(redCell);
}
else if ((board[i][j]).equals(greenCell)) {
grid.add(greenCell);
}
else {
grid.add(whiteCell);
}
}
}
grid.setVisible(true);
} // end of constructor
} // end of Grid class
You can add a component to your GUI only once. If you add it to another component, it will be removed from the previous component. You're trying to add the same JButtons several times, and that won't work. Instead you're going to have to create more JButtons. Consider having your buttons share Actions which is allowed.
If you need more help, consider posting compilable code (your current code is not), a small runnable, compilable program that demonstrates your problem, in other words, an sscce.
Edit
You comment:
But don't these count as instances of a JButton not the same JButton? (I don't understand what your answer meant...)
Think of it mathematically... how many JButtons do you create in your code above? Well, this is easy to figure, exactly 4:
blueCell = new JButton("BLUE CELL");
redCell = new JButton("RED CELL");
greenCell = new JButton("GREEN CELL");
whiteCell = new JButton("WHITE CELL");
So, now ask yourself, how many JButtons are you trying to display in your GUI with these four JButtons? If it's four, then you're possibly OK (as long as you use each button), but if it's more, then you're in trouble. From your 6x6 grid, board = new JButton [6][6];, it looks like you're trying to display 36 JButtons, and if this is true, you've got problems.
But again, if still stuck, please consider creating and posting an sscce.