Win a copy of Event Streams in Action this week in the Java in General forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Devaka Cooray
  • Liutauras Vilda
  • Jeanne Boyarsky
  • Bear Bibeault
Sheriffs:
  • Paul Clapham
  • Knute Snortum
  • Rob Spoor
Saloon Keepers:
  • Tim Moores
  • Ron McLeod
  • Piet Souris
  • Stephan van Hulst
  • Carey Brown
Bartenders:
  • Tim Holloway
  • Frits Walraven
  • Ganesh Patekar

my brand spanking new layout manager

 
Ranch Hand
Posts: 68
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I've been working on a custom layout manager for a long time now, and I'm almost done. I call it OrbitLayout and it displays components in concentric circles. I'm itching to show it off, so if you'd like to give it a try, here it is:
To add components, use the following method:
Container con;
Componenent com;
// initialize them, etc
con.setLayout(new OrbitLayout(0,25); // for some spacing
int orbit = (choose an orbit here from 0 to n, where 0 is the center)
con.add(com, new Integer(orbit));
Try adding a few components (buttons work best) to a few orbits (although the center orbit 0 can only hold 1 component).
Please tell me what you think!!!
Micah

<code>
import java.util.*;
import java.awt.*;
/**
* This layout manager is used to display components in concentric circles surrounding
* a central component (or no central component). For the purposes of this
* source file, each circle is an orbit and the components are satellites.
*
* @author Micah Wedemeyer
* @version 1.0
*/
public class OrbitLayout implements LayoutManager2 {
/**
* A constant used to deliniate the central component that all other components orbit.
*/
final public static int CENTER = 0;
/**
* The minimum spacing between components in the same orbit.
*/
private int satGap;
/**
* The minimum spacing between each orbit.
*/
private int orbitGap;
/**
* The current outer-most orbit.
*/
private int outerOrbit;
/**
* A list of orbits. Each element in the list is a list itself of satellites
* in that orbit. The center object is at orbits[0],
* the first orbit at orbits[1], and the nth at orbits[n].
*/
private ArrayList orbits;
/**
* A central component that all the satellites orbit
*/
private Component center;
/**
* Creates an OrbitLayout with the specified spacing.
* @param satGap The minimum distance between satellites in the same orbit.
* @param orbitGap The minimum distance between concentric orbits.
*/
public OrbitLayout(int satGap, int orbitGap) {
this.satGap = satGap;
this.orbitGap = orbitGap;
outerOrbit = 0;
center = null;
orbits = new ArrayList();
orbits.add(new OrbitList(0));
}
/**
* Does not use, must be overridden for interface.
*/
public void addLayoutComponent(String s, Component c) {
}
/**
* Adds a component to the layout.
* @param c The component to be added.
* @param constraints (an Integer object) The orbit to add the component to.
*/
public void addLayoutComponent(Component c, Object constraints) {
if (constraints instanceof Integer) {
Integer orbit = (Integer)constraints;
addLayoutComponent(c, orbit.intValue());
}
}
/**
* Adds a component to the specified orbit.
* @param comp The component to be added.
* @param orbit The orbit to add the component to.
* @see OrbitLayout#CENTER
*/
public void addLayoutComponent (Component comp, int orbit) {
if (orbit < 0)<br /> throw new IllegalArgumentException("Orbit must be non-negative.");<br /> /* Special Case: The orbit is actually the center. */<br /> if (orbit == CENTER) {<br /> center = comp;<br /> // sets orbits[0] to an OrbitList containing only the center<br /> OrbitList o = new OrbitList(0);<br /> o.add(center);<br /> orbits.set(0, o);<br /> invalidateAllOrbits();<br /> return;<br /> }<br /> /**<br /> * If the new orbit is higher than the outerOrbit, allocate a new ArrayList for<br /> * the new orbit and all intervening orbits. This keeps the orbits List from<br /> * having null slots for empty orbits. This will make iteration easier.<br /> */<br /> if (orbit > outerOrbit) {
for (int i = outerOrbit + 1; i <= orbit; i++)<br /> orbits.add(new OrbitList(i));<br /> outerOrbit = orbit;<br /> }<br /> // Adds the component to its correct orbit.<br /> ((OrbitList)orbits.get(orbit)).add(comp);<br /> // Forces resizing of all external orbits.<br /> invalidateOrbits(orbit);<br /> }<br /> /**<br /> * Removes a component from the layout.<br /> * @param comp The component to be removed from this layout.<br /> */<br /> public void removeLayoutComponent (Component comp) {<br /> if (comp == center) {<br /> center = null;<br /> ((OrbitList)orbits.get(0)).clear();<br /> invalidateAllOrbits();<br /> return;<br /> }<br /> for (Iterator i = orbits.iterator(); i.hasNext(); ) {<br /> OrbitList orb = (OrbitList)i.next();<br /> for (Iterator j = orb.iterator(); j.hasNext(); ) {<br /> Component c = (Component)j.next();<br /> if (c == comp) {<br /> int currentOrbit = orbits.indexOf(orb);<br /> invalidateOrbits(currentOrbit);<br /> j.remove();<br /> return;<br /> }<br /> }<br /> }<br /> }<br /> /**<br /> * =================================<br /> * NEED TO FIX<br /> * ================================<br /> */<br /> public Dimension preferredLayoutSize(Container target) {<br /> // Not sure what to do here, yet.<br /> return target.getPreferredSize();<br /> }<br /> /**<br /> * ===============================<br /> * NEED TO FIX<br /> * ===========================<br /> */<br /> public Dimension minimumLayoutSize(Container target) {<br /> // Not sure what to do here, yet<br /> return target.getMinimumSize();<br /> }<br /> /**<br /> * The maximum size for this layout manager.<br /> * @return the max size for this layout manager.<br /> */<br /> public Dimension maximumLayoutSize(Container target) {<br /> return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);<br /> }<br /> /**<br /> * Returns the alignment along the x axis. This specifies how<br /> * the component would like to be aligned relative to other<br /> * components. The value should be a number between 0 and 1<br /> * where 0 represents alignment along the origin, 1 is aligned<br /> * the furthest away from the origin, 0.5 is centered, etc.<br /> */<br /> public float getLayoutAlignmentX(Container parent) {<br /> return 0.5f;<br /> }<br /> /**<br /> * Returns the alignment along the y axis. This specifies how<br /> * the component would like to be aligned relative to other<br /> * components. The value should be a number between 0 and 1<br /> * where 0 represents alignment along the origin, 1 is aligned<br /> * the furthest away from the origin, 0.5 is centered, etc.<br /> */<br /> public float getLayoutAlignmentY(Container parent) {<br /> return 0.5f;<br /> }<br /> /**<br /> * Invalidates the layout, flushing cached data.<br /> */<br /> public void invalidateLayout(Container parent) {<br /> /* Nothing to do here...I think. */<br /> }<br /> /**<br /> * Lays out the components in the specified container.<br /> * @param parent the container which needs to be laid out.<br /> */<br /> public void layoutContainer (Container parent) {<br /> Insets insets = parent.getInsets();<br /> // In order to make calculations easy, we will assume that (0,0)<br /> // is at the center of the container. We can do conversions at the end.<br /> Point origin = new Point();<br /> origin.x = (parent.getWidth() - insets.left - insets.right) / 2;<br /> origin.y = (parent.getHeight() - insets.top - insets.bottom) / 2;<br /> for (Iterator i = orbits.iterator(); i.hasNext(); ) {<br /> OrbitList currentOrb = (OrbitList)i.next();<br /> if (currentOrb.size() != 0) {<br /> // Calculate the angle in radians between the centerpoints of each<br /> // component. Since there are 2*pi radians in a circle, to evenly space<br /> // the components, we just space them by 2*pi/# of components.<br /> // Additional spacing (satGap) must be added later<br /> double spaceAngle = (Math.PI * 2) / currentOrb.size();<br /> // A multiplier for the spaceAngle. The first component will be placed<br /> // at Theta=0, the second at Theta=(spaceAngle), the third at<br /> // Theta=(spaceAngle * 2), etc.<br /> int angleMult = 0;<br /> for (Iterator j = currentOrb.iterator(); j.hasNext(); ) {<br /> Component c = (Component)j.next();<br /> polarLayout(c, currentOrb.getRadius(), spaceAngle * angleMult, origin);<br /> angleMult++;<br /> }<br /> }<br /> }<br /> }<br /> /**<br /> * Lays out a single component in the container using polar to cartesian<br /> * coordinate conversion.<br /> * @param c The component to be laid out.<br /> * @param radius The radius of the centerpoint of the component.<br /> * @param theta The angle from the origin (center of container) of the centerpoint<br /> * of the component.<br /> * @param origin The centerpoint of the container.<br /> */<br /> private void polarLayout(Component c, int radius, double theta, Point origin) {<br /> Dimension d = c.getPreferredSize();<br /> // Determine the centerpoint for the component in Cartesian coordinates<br /> // using the standard formula for polar -> Cartesian conversion.
Point centerpoint = new Point();
centerpoint.x = (int)(radius * Math.cos(theta)) + origin.x;
centerpoint.y = (int)(radius * Math.sin(theta)) + origin.y;
// Use the preferred size and the centerpoint to calculate the top left
// corner of where the component should be laid out.
Rectangle bounds = new Rectangle();
bounds.x = centerpoint.x - (d.width / 2);
bounds.y = centerpoint.y - (d.height / 2);
bounds.width = d.width;
bounds.height = d.height;
c.setBounds(bounds);
}
/**
* Invalidates all orbits at and above a specified orbit.
* This will force a recalculation at the next sizing or layout.
* This method is called when an orbit is changed (satellite added or removed).
* Each orbit above a changed orbit must also be recalculated, so this invalidates
* all orbits above.
*/
private void invalidateOrbits(int orb) {
for (ListIterator i = orbits.listIterator(orb); i.hasNext(); ) {
OrbitList o = (OrbitList)i.next();
o.invalidate();
}
}
/**
* Invalidates the radius of every orbit.
* @see invalidateRadii(int)
*/
private void invalidateAllOrbits() {
invalidateOrbits(0);
}
/**
* Draws circles to represent the orbits.
* @param parent The container that is being laid out.
*/
public void drawOrbits(Container parent) {
Insets insets = parent.getInsets();
Graphics g = parent.getGraphics();
// In order to make calculations easy, we will assume that (0,0)
// is at the center of the container. We can do conversions at the end.
Point origin = new Point();
origin.x = (parent.getWidth() - insets.left - insets.right) / 2;
origin.y = (parent.getHeight() - insets.top - insets.bottom) / 2;
for (Iterator i = orbits.iterator(); i.hasNext(); ) {
OrbitList currentOrb = (OrbitList)i.next();
int r = currentOrb.getRadius();
Point topLeft = new Point();
topLeft.x = origin.x - r;
topLeft.y = origin.y - r;
g.drawOval(topLeft.x, topLeft.y, r * 2, r * 2);
}
// Draw a small orbit for the centerpoint
g.fillOval(origin.x - 2, origin.y - 2, 4, 4);
}
/**
*
A class to represent each orbit. Each OrbitList is a list of satellites,
* plus the radius at which these satellites should be placed. In order to cut
* down execution time, it will not recalculate radius and maxOverlap each time an element is added.
*
*
Since radius is a function of the radii of closer
* orbits, this sets up a recursive relation. In order to escape the nasty time requirements
* of recursive relations, I made it possible to only recalculate the radii of those orbits
* which are invalid.
*
*
It is important to remember that when adding or removing satellites from an
* orbit, the orbit MUST BE invalidated. This will prevent resizing problems.
*
*
NOTE: This is not for a list of orbits, it is to represent a single orbit.
*/
class OrbitList extends ArrayList {
/**
* The orbit that this list represents.
*/
private int orbit;
/**
* The radius of current orbit in pixels.
*/
private int radius;
/**
* This is 1/2 the length of the main diagonal of the largest component in this orbit.
* This is important because this is the maximum amount that extends past an
* orbit. Therefore, if orbits are spaced at least that much, there will be
* no overlap.
*/
private int maxOverlap;
/**
* Tells whether the current radius and maxOverlap is correct.
*/
private boolean valid;
/**
* Creates an empty OrbitList without setting a correct radius. This will
* be done later, during calls to <code>getRadius()</code>.
* @param orbit The orbit to be represented.
*/
public OrbitList(int orbit) {
super();
this.orbit = orbit;
maxOverlap = 0;
radius = 0;
valid = false;
}
/**
* Gets the current valid radius. If the current radius is invalid, it computes
* and returns the valid one.

* NOTE: This is not a simple O(1) get method. There may be time consuming
* calculations involved. I tried to make it as efficient as possible.
* @return The current valid radius.
*/
public int getRadius() {
calculateRadius();
return radius;
}
/**
* Returns the maximum overlap.
* @return The maximum overlap of this orbit.
*/
public int getMaxOverlap() {
if (!isValid())
calculateOverlap();
return maxOverlap;
}
/**
* Calculates and sets the correct radius.
*/
private void calculateRadius() {
// If valid, don't do anything.
if (isValid())
return;
else if (orbit == 0) {
radius = 0;
}
else {
OrbitList previous = (OrbitList)orbits.get(orbit - 1);
radius = previous.getRadius() + previous.getMaxOverlap() + this.getMaxOverlap() + orbitGap;
}
valid = true;
}
/**
* Calculates and sets the maximum overlap of this orbit. This is 1/2 the length
* of the main diagonal of the largest component. This is important because
* this is the maximum amount that extends past an orbit. Therefore, if orbits
* are spaced at least that much, there will be no overlap.
*/
private void calculateOverlap () {
maxOverlap = 0;
for (Iterator i = this.iterator(); i.hasNext(); ) {
Component c = (Component)i.next();
Dimension d = c.getPreferredSize();
int w = d.width;
int h = d.height;
int over = (int)(Math.sqrt((w * w) + (h * h)) / 2);
maxOverlap = Math.max(maxOverlap, over);
}
}
/**
* Invalidates the current orbit, forcing radius and maxOverlap to be recalculated
* during the next call to <code>getRadius()</code> or <code>getMaxOverlap()</code>.
*/
public void invalidate() {
valid = false;
}
/**
* Determines if the current radius and maxOverlap are valid.
* @return true if the radius and maxOverlap are valid, false otherwise.
*/
public boolean isValid() {
return valid;
}
}
}
</code>
 
It is sorta covered in the JavaRanch Style Guide.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!