现在的位置: 首页 > 综合 > 正文

Java中scroll pane的使用(二)

2013年02月22日 ⁄ 综合 ⁄ 共 13630字 ⁄ 字号 评论关闭

package com.han;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.font.LineMetrics;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.ScrollPaneConstants;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

/**
 * @author HAN
 * 
 */
@SuppressWarnings("serial")
public class ScrollDemo extends JPanel implements ItemListener {
	private JScrollPane scrollPane;
	private Rule hRule;
	private Rule vRule;

	/**
	 * Create a placeholder icon, which consists in a white box with a black
	 * border and a red x inside. It's used to display something when there are
	 * issues loading an icon from an external location.
	 * 
	 * @author HAN
	 * 
	 */
	class MissingIcon implements Icon {
		private int width = 32;
		private int height = 32;

		@Override
		public void paintIcon(Component c, Graphics g, int x, int y) {
			// TODO Auto-generated method stub
			Graphics2D g2 = (Graphics2D) g;
			Shape rect = new Rectangle2D.Double(x + 1, y + 1, width - 2,
					height - 2);
			g2.setColor(Color.WHITE);
			g2.fill(rect);
			g2.setColor(Color.BLACK);
			g2.draw(rect);// By default, the stroke is 1.0f solid line.

			g2.setColor(Color.RED);
			BasicStroke stroke = new BasicStroke(4.0f);
			g2.setStroke(stroke);
			g2.draw(new Line2D.Double(x + 10, y + 10, x + width - 10, y
					+ height - 10));
			g2.draw(new Line2D.Double(x + 10, y + height - 10, x + width - 10,
					y + 10));
		}

		@Override
		public int getIconWidth() {
			// TODO Auto-generated method stub
			return width;
		}

		@Override
		public int getIconHeight() {
			// TODO Auto-generated method stub
			return height;
		}
	}

	public class Corner extends JComponent {
		@Override
		protected void paintComponent(Graphics g) {
			g.setColor(new Color(230, 163, 4));
			g.fillRect(0, 0, getWidth(), getHeight());
		}
	}

	/**
	 * The enum type is more powerful than the traditional constants defining by
	 * encapsulate them in an interface.
	 * 
	 * @author HAN
	 * 
	 */
	enum RuleConstants {
		HORIZONTAL, VERTICAL
	}

	public class Rule extends JComponent {
		private RuleConstants direction;
		private String mode;

		// Define the distance on screen for an inch and a centimeter.
		final int INCH = Toolkit.getDefaultToolkit().getScreenResolution();
		final int CM = (int) (INCH / 2.54);

		private int preferredWidth;
		private int preferredHeight;

		private static final int SIZE = 35;

		Rule(RuleConstants direction, String mode) {
			this.direction = direction;
			this.mode = mode;
		}

		private void setUnitMode(String mode) {
			this.mode = mode;
		}

		private int getIncrement() {
			if (mode.equals("cm")) {
				return CM;
			} else if (mode.equals("inch")) {
				return INCH / 2;
			} else {
				return 0;
			}
		}

		@Override
		protected void paintComponent(Graphics g) {
			Rectangle currentClip;
			String text;
			Font defaultFont = getFont();

			switch (direction) {
			case HORIZONTAL:
				// Set clip to draw only the visible part of the rule, to ensure
				// speedy scrolling.
				currentClip = new Rectangle(scrollPane.getViewport()
						.getViewPosition().x, 0, scrollPane.getViewport()
						.getExtentSize().width, SIZE);
				g.setClip(currentClip);
				// System.out.println("HORIZONTAL: " + currentClip);

				// Fill this component with a background color.
				g.setColor(new Color(230, 163, 4));
				g.fillRect(currentClip.x, currentClip.y, currentClip.width,
						currentClip.height);

				// Draw ticks and labels.
				g.setColor(Color.BLACK);
				if (mode.equals("cm")) {
					int start;
					if (currentClip.x % CM == 0)
						start = currentClip.x;
					else
						start = (currentClip.x / CM + 1) * CM;
					for (int i = start; i < currentClip.x + currentClip.width; i += CM) {
						// Draw ticks.
						g.drawLine(i, currentClip.height, i,
								currentClip.height - 10);

						// Draw the label.
						Graphics2D g2 = (Graphics2D) g;
						if (i == 0) {
							// Draw particularly the first "0" label.
							text = "0 cm";
							LineMetrics lineMetrics = defaultFont
									.getLineMetrics(text,
											g2.getFontRenderContext());
							g2.drawString(
									text,
									0,
									(float) (currentClip.height - 10 - 2 - lineMetrics
											.getDescent()));
						} else {
							// Draw the other labels.
							text = Integer.toString(i / CM);
							Rectangle2D rect = defaultFont.getStringBounds(
									text, g2.getFontRenderContext());
							LineMetrics lineMetrics = defaultFont
									.getLineMetrics(text,
											g2.getFontRenderContext());
							g2.drawString(
									text,
									(float) (i - rect.getWidth() / 2),
									(float) (currentClip.height - 10 - 2 - lineMetrics
											.getDescent()));
						}
					}

				} else if (mode.equals("inch")) {
					int start;
					if (currentClip.x % (INCH / 2) == 0)
						start = currentClip.x;
					else
						start = (currentClip.x / (INCH / 2) + 1) * (INCH / 2);
					for (int i = start; i < currentClip.x + currentClip.width; i += INCH / 2) {
						if ((i / (INCH / 2)) % 2 == 0) {
							g.drawLine(i, currentClip.height, i,
									currentClip.height - 10);

							// Draw the label.
							Graphics2D g2 = (Graphics2D) g;
							if (i == 0) {
								// Draw particularly the first "0" label.
								text = "0 in";
								LineMetrics lineMetrics = defaultFont
										.getLineMetrics(text,
												g2.getFontRenderContext());
								g2.drawString(
										text,
										0,
										(float) (currentClip.height - 10 - 2 - lineMetrics
												.getDescent()));
							} else {
								// Draw the other labels.
								text = Integer.toString(i / (INCH / 2) / 2);
								Rectangle2D rect = defaultFont.getStringBounds(
										text, g2.getFontRenderContext());
								LineMetrics lineMetrics = defaultFont
										.getLineMetrics(text,
												g2.getFontRenderContext());
								g2.drawString(
										text,
										(float) (i - rect.getWidth() / 2),
										(float) (currentClip.height - 10 - 2 - lineMetrics
												.getDescent()));
							}
						} else {
							g.drawLine(i, currentClip.height, i,
									currentClip.height - 7);
						}
					}
				}
				break;
			case VERTICAL:
				// Set clip to draw only the visible part of the rule, to ensure
				// speedy scrolling.
				currentClip = new Rectangle(0, scrollPane.getViewport()
						.getViewPosition().y, SIZE, scrollPane.getViewport()
						.getExtentSize().height);
				g.setClip(currentClip);
				// System.out.println("VERTICAL: " + currentClip);

				// Fill this component with a background color.
				g.setColor(new Color(230, 163, 4));
				g.fillRect(currentClip.x, currentClip.y, currentClip.width,
						currentClip.height);

				// Draw ticks and labels.
				g.setColor(Color.BLACK);
				if (mode.equals("cm")) {
					int start;
					if (currentClip.y % CM == 0)
						start = currentClip.y;
					else
						start = (currentClip.y / (CM) + 1) * (CM);
					for (int i = start; i < currentClip.y + currentClip.height; i += CM) {
						g.drawLine(currentClip.width, i,
								currentClip.width - 10, i);

						// Draw the label.
						Graphics2D g2 = (Graphics2D) g;
						if (i == 0) {
							// Draw particularly the first "0" label.
							text = "0 cm";
							Rectangle2D rect = defaultFont.getStringBounds(
									text, g2.getFontRenderContext());
							LineMetrics lineMetrics = defaultFont
									.getLineMetrics(text,
											g2.getFontRenderContext());
							g2.drawString(text,
									(float) (currentClip.width - 2 - rect
											.getWidth()), (float) (lineMetrics
											.getAscent()));
						} else {
							// Draw the other labels.
							text = Integer.toString(i / CM);
							Rectangle2D rect = defaultFont.getStringBounds(
									text, g2.getFontRenderContext());
							LineMetrics lineMetrics = defaultFont
									.getLineMetrics(text,
											g2.getFontRenderContext());
							g2.drawString(
									text,
									(float) (currentClip.width - 10 - 2 - rect
											.getWidth()),
									(float) (i + ((lineMetrics.getAscent() + lineMetrics
											.getDescent()) / 2 - lineMetrics
											.getDescent())));
						}
					}

				} else if (mode.equals("inch")) {
					int start;
					if (currentClip.y % (INCH / 2) == 0)
						start = currentClip.y;
					else
						start = (currentClip.y / (INCH / 2) + 1) * (INCH / 2);
					for (int i = start; i < currentClip.y + currentClip.height; i += INCH / 2) {
						if ((i / (INCH / 2)) % 2 == 0) {
							g.drawLine(currentClip.width, i,
									currentClip.width - 10, i);

							// Draw the label.
							Graphics2D g2 = (Graphics2D) g;
							if (i == 0) {
								// Draw particularly the first "0" label.
								text = "0 in";
								Rectangle2D rect = defaultFont.getStringBounds(
										text, g2.getFontRenderContext());
								LineMetrics lineMetrics = defaultFont
										.getLineMetrics(text,
												g2.getFontRenderContext());
								g2.drawString(text,
										(float) (currentClip.width - 2 - rect
												.getWidth()),
										(float) (lineMetrics.getAscent()));
							} else {
								// Draw the other labels.
								text = Integer.toString(i / (INCH / 2) / 2);
								Rectangle2D rect = defaultFont.getStringBounds(
										text, g2.getFontRenderContext());
								LineMetrics lineMetrics = defaultFont
										.getLineMetrics(text,
												g2.getFontRenderContext());
								g2.drawString(
										text,
										(float) (currentClip.width - 10 - 2 - rect
												.getWidth()),
										(float) (i + ((lineMetrics.getAscent() + lineMetrics
												.getDescent()) / 2 - lineMetrics
												.getDescent())));
							}
						} else {
							g.drawLine(currentClip.width, i,
									currentClip.width - 7, i);
						}
					}
				}
				break;
			}
		}

		@Override
		public boolean isOpaque() {
			return true;
		}

		@Override
		public Dimension getPreferredSize() {
			if (direction == RuleConstants.HORIZONTAL) {
				return new Dimension(preferredWidth, SIZE);
			} else if (direction == RuleConstants.VERTICAL) {
				return new Dimension(SIZE, preferredHeight);
			} else {
				return null;
			}
		}

		private void setPreferredWidth(int preferredWidth) {
			this.preferredWidth = preferredWidth;
		}

		private void setPreferredHeight(int preferredHeight) {
			this.preferredHeight = preferredHeight;
		}
	}

	public class PictureView extends JLabel implements Scrollable,
			MouseMotionListener {
		private int increment;

		PictureView(Icon picture, int increment) {
			super(picture);
			this.increment = increment;

			// Sets the autoscrolls property. If true mouse dragged events will
			// be synthetically generated when the mouse is dragged outside of
			// the component's bounds and mouse motion has paused (while the
			// button continues to be held down). The synthetic events make it
			// appear that the drag gesture has resumed in the direction
			// established when the component's boundary was crossed.
			setAutoscrolls(true);

			addMouseMotionListener(this);
		}

		@Override
		public void mouseDragged(MouseEvent e) {
			// Let picture view scroll automatically when mouse drags out of the
			// bounds of viewport and stops.
			Rectangle rect = new Rectangle(e.getX(), e.getY(), 1, 1);
			// Forwards the scrollRectToVisible() message to the JComponent's
			// parent. Components that can service the request, such as
			// JViewport, override this method and perform the scrolling.
			scrollRectToVisible(rect);
		}

		@Override
		public void mouseMoved(MouseEvent e) {
		}

		@Override
		public Dimension getPreferredScrollableViewportSize() {
			return new Dimension(220, 200);
		}

		@Override
		public int getScrollableUnitIncrement(Rectangle visibleRect,
				int orientation, int direction) {
			if (orientation == SwingConstants.HORIZONTAL) {
				int x = visibleRect.x;
				if (x % increment != 0) {
					if (direction < 0) {
						return x - (x / increment) * increment;
					} else {
						return (x / increment + 1) * increment - x;
					}
				} else {
					return increment;
				}
			} else if (orientation == SwingConstants.VERTICAL) {
				int y = visibleRect.y;
				if (y % increment != 0) {
					if (direction < 0) {
						return y - (y / increment) * increment;
					} else {
						return (y / increment + 1) * increment - y;
					}
				} else {
					return increment;
				}
			}
			return 0;
		}

		@Override
		public int getScrollableBlockIncrement(Rectangle visibleRect,
				int orientation, int direction) {
			if (orientation == SwingConstants.HORIZONTAL) {
				return visibleRect.width - increment;
			} else if (orientation == SwingConstants.VERTICAL) {
				return visibleRect.height - increment;
			}
			return 0;
		}

		@Override
		public boolean getScrollableTracksViewportWidth() {
			return false;
		}

		@Override
		public boolean getScrollableTracksViewportHeight() {
			return false;
		}

		private void setIncrement(int increment) {
			this.increment = increment;
		}
	}

	public ScrollDemo() {
		// The constructor serves also as a content pane.
		super(new BorderLayout());

		// Load picture.
		Icon picture;
		BufferedImage image = createImage("/images/flyingBee.jpg");
		if (image == null) {
			picture = new MissingIcon();
		} else {
			picture = new ImageIcon(image, "flyingBee");
		}

		// Create and set up the horizontal and vertical rules.
		hRule = new Rule(RuleConstants.HORIZONTAL, "cm");
		vRule = new Rule(RuleConstants.VERTICAL, "cm");
		// The scroll pane puts the row and column headers in JViewPorts of
		// their own. Thus, when scrolling horizontally, the column header
		// follows along, and when scrolling vertically, the row header follows
		// along. Make sure the row and column have the same width and height as
		// the view (if the scroll pane has set the viewport border, the
		// border's size should be taken into account), because JScrollPane does
		// not enforce these values to have the same size. If one differs from
		// the other, you are likely to not get the desired behavior.
		hRule.setPreferredWidth(picture.getIconWidth());
		vRule.setPreferredHeight(picture.getIconHeight());

		// Create the upper-left corner.
		JToggleButton toggleButton = new JToggleButton("cm", true);
		toggleButton.addItemListener(this);
		toggleButton.setFont(new Font("SansSerif", Font.PLAIN, 11));
		toggleButton.setMargin(new Insets(2, 2, 2, 2));
		JPanel upperLeftCorner = new JPanel();
		upperLeftCorner.add(toggleButton);

		// Create the upper-right corner.
		Corner upperRightCorner = new Corner();

		// Create the lower-right corner.
		Corner lowerLeftCorner = new Corner();

		// Create the picture view as the client of the scroll pane.
		PictureView pictureView = new PictureView(picture, hRule.getIncrement());

		// Create and set up the scroll pane.
		scrollPane = new JScrollPane();
		scrollPane.setViewportView(pictureView);
		scrollPane.setRowHeaderView(vRule);
		scrollPane.setColumnHeaderView(hRule);
		scrollPane.setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER,
				upperLeftCorner);
		scrollPane.setCorner(ScrollPaneConstants.UPPER_RIGHT_CORNER,
				upperRightCorner);
		scrollPane.setCorner(ScrollPaneConstants.LOWER_LEFT_CORNER,
				lowerLeftCorner);

		// Layout the content pane.
		add(scrollPane, BorderLayout.CENTER);
	}

	/**
	 * @param path
	 *            - the path used to create the buffered image.
	 * @return an BufferedImage object, or <code>null</code> if the given path
	 *         is not valid or an error occurs during reading.
	 */
	private BufferedImage createImage(String path) {
		URL imageURL = getClass().getResource(path);
		if (imageURL != null) {
			try {
				return ImageIO.read(imageURL);
			} catch (IOException e) {
				System.err.println("an error occurs during reading.");
				e.printStackTrace();
				return null;
			}
		} else {
			System.err.println("Couldn't find file: " + path);
			return null;
		}
	}

	private static void createAndShowGUI() {
		// Create and set up the window.
		JFrame frame = new JFrame("Scroll demo");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// Set up the content pane.
		JPanel contentPane = new ScrollDemo();
		contentPane.setOpaque(true);// Content pane must be opaque.
		contentPane.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
		frame.setContentPane(contentPane);

		// Display the window.
		frame.pack();
		frame.setVisible(true);
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// Schedule a job for the EDT:
		// Creating and showing this application's GUI.
		SwingUtilities.invokeLater(new Runnable() {

			@Override
			public void run() {
				createAndShowGUI();
			}

		});
	}

	@Override
	public void itemStateChanged(ItemEvent e) {
		// Set the unit mode: cm or inch.
		if (e.getStateChange() == ItemEvent.SELECTED) {
			hRule.setUnitMode("cm");
			vRule.setUnitMode("cm");
		} else {
			hRule.setUnitMode("inch");
			vRule.setUnitMode("inch");
		}
		hRule.repaint();
		vRule.repaint();

		// Notify the scrollable the new increment.
		((PictureView) scrollPane.getViewport().getView()).setIncrement(hRule
				.getIncrement());
	}

}

抱歉!评论已关闭.