I want to create some stuff in Java, but have no idea where to start. The first task I'm attacking is to create some dials like these: (The white bar will be animated and reflect the value of the numbers) Any help would be appreciated, I'm still relatively new to Java and my GUI programming abilities are pretty minimal. I tried Googling, but couldn't find anything useful. Cheers
Java is a little awkward for this kind of stuff. I'll try and knock a few things together to get you started tomorrow...maybe.
This program reads values in from the console and then draws the images according to the input. See how you get on with it. Post any questions that you have and I'll try to answer. Code: import java.awt.Color; import java.awt.Graphics; import java.awt.event.WindowAdapter; import javax.swing.JFrame; import javax.swing.JPanel; import java.util.Scanner; public class Dial { public static void main(String[] args) { //A JFrame is the main window final JFrame frame = new JFrame(); //prevent automatic close operation as it needs to be done progamatically frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); frame.setVisible(true); //we need to see the window frame.setSize(270, 200); frame.setBackground(Color.BLACK); //A JPanel is what we draw on (see class below) DrawPanel panel = new DrawPanel(); //add the panel to the frame frame.setContentPane(panel); //input scanner reads the input value from the console. //the scanner is final to allow access in the window listener final Scanner scanner = new Scanner(System.in); //variables to handle user input String angleInput; int angle = 0; //pick up the window close event to close the input scanner and dispose of //the window frame.addWindowListener(new WindowAdapter(){ @Override public void windowClosing(java.awt.event.WindowEvent windowEvent){ frame.dispose(); scanner.close(); System.exit(0); } }); //loop reads input from console and sets the arc while(true){ //read console input as string angleInput = scanner.nextLine(); try{ //parse string to int angle = Integer.parseInt(angleInput); }catch (Exception e){ //catches errors and restarts the loop if non-int is entered System.out.println("Gotta catch'em all"); continue; } //set the angle of the dangle and call the repaint method //to redraw the panel with the new arc values panel.setAngle(angle); panel.repaint(); } } } //Need to override the paint method of JPanel to draw things //so a class that inherits from a JPanel is required class DrawPanel extends JPanel{ //something to get rid of a warning. Not required private static final long serialVersionUID = 1L; private int angle = 0; private int displayValue = 0; public void setAngle(int angle){ if (angle < 0) angle = 0; if (angle > 180) angle = 180; this.angle = 180 - angle; this.displayValue = angle; } public int getAngle(){ return this.angle; } //Override indicator, paint method must be overridden in order //to draw shapes @Override public void paintComponent(Graphics g){ //three fill arcs are used to create the dial //change colours to see what's going on g.setColor(Color.WHITE); g.fillArc(50, 50, 150, 150, 0, 180); g.setColor(Color.BLACK); g.fillArc(60, 60, 130, 130, 0, 180); g.setColor(Color.BLACK); g.fillArc(40, 40, 170, 170, 0, angle); g.setColor(Color.WHITE); //Text Strings g.drawString("Min 0",50, 140); g.drawString("Max 180",150, 140); g.drawString(String.valueOf(displayValue), 120, 100); g.drawString("Speed MPH", 90, 115); } }
Had a tinker and it's working nicely. The comments were really helpful. Now, since I want multiple dials on screen at once, what's the best way to go about this? I could just stick the other dials in that class, drawing the other arcs (dials) in different places on the frame using different names, but I assume I will be severely reprimanded for even thinking about doing it that way. So instead, I guess I need a class that sets up the frame and then calls that dial class (with a bit of modification) passing values to define the variables (arc locations, text names, input variable)?
Good stuff. I would create a configurable dial class. Then an array list or similar data structure to hold each class instance. Then pass that data structure into the draw panel class. The procedure would be instanciate a dial class setting up the text and max and min in the constructor. Add the instance to a collection. Set the value for each dial. Pass the collection to the draw panel class then draw each dial. I would also add a layout method to the draw panel class which automatically lays out the dials depending on the size of the window and how many dials you have. That might warrant it's own layout manager class depending on how complex it is.
Is there any way to easily align the drawStrings? Currently when another digit is added it shifts the whole thing to the right e.g. Code: 99 100 999 1000 Instead, I want it to just add that digit onto the left, with the units staying stationary e.g. Code: 99 100 999 1000
There doesn't appear to be a way because the drawString uses the XY co-ordinate of the left side to determine its position. If you are using multiple strings you could probably align by estimating the pixel length of the string based on the number of characters and then calculate the necessary X co-ordinate offset to do right side alignment. The above method might not work too well if the font isn't very monospacey so you could use the drawChars method or 1 character strings and position each character individually which is probably more work, but you might get a better looking result. You could also change the font to something that is monospaced if that helps. But I don't know how to do it off hand. Left padding with spaces would be another option.
It's for when the speed for example goes from 99 to 100, the units and tens digits stay where they are and the hundreds will just be appended onto the left instead of pushing everything right. Your suggestions are the same conclusions I came to, sounded too messy to be the only way to do it though.
Just padded everything with extra zeros based on the value. Not sure how I feel about having them visible, quite like it, but might hide them. Also got AA on the dials now
Yes it is messy, but if you encapsulate the code in a class or a method, you can hide the mess away somewhere . Setting a max character limit and left padding with spaces would be a small enough piece of work. You could start looking at using JLabels or similar plus a layout manager, but the complexity would go up a fair bit. Layout managers are a pain in the arse to use. But it would give you better control over how text is arranged. Layout mangers are used to create more standard, form-style desktop GUI applications. If you go down that route you would probably be looking at setting up your text with a layout manager and then trying to layer the JPanel on top of it all (or vice versa more likely). Edit: I think it looks alright with the zeros. Spaces would be fine too. Yes it looks a lot better with anti-aliasing.
Yeah, most of my Java experience was using layout mangers (GridBag mainly), but for the application it should be at a fixed resolution and window size. It may be bad practice to use definite values for locations and sizes (for obvious reasons), but it gives me more control and stops everything moving around whenever i make changes.
Nah, if you can guarantee resolution and size for your application, there's no point in adding the flexibility a layout manager would provide as it would just be more work for redundant functionality.
I can guarantee it being fixed for it's intended application, but the extra flexibility is always nice if I every want to reuse the code for a different purpose or develop it further. Not gonna go making extra work for myself though, at least for now.
Any way to make a key listener that doesn't require focus? Eventually I may need to have some level of control with an external switch / button, but for now a key press will do. Everything seems to require a text box with focus though - I just want it to respond if it's the active window (e.g. press 2 to display panel 2 etc). Been busy working on other functionality too (suggestions / criticism appreciated):
No I'm not sure how to do that. You might want to check out keybindings though as it sounds like what you are after http://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html