I have a Java programming which displays the grid of 10x10 cells. In each cell I would like to draw a single character and have it take up the whole cell.
I am currently using the following code, but it isn't quite the right size.
graphics.setFont(new Font("monospaced", Font.PLAIN, 12));
for(int x = 0; x < GRID_WIDTH; x++) {
for(int y = 0; y < GRID_HEIGHT; y++) {
graphics.drawString(Character.toString(grid[x][y]), x * CELL_WIDTH, (y + 1) * CELL_HEIGHT);
}
}
Is there any way in Java to draw a 10x10 (or CELL_WIDTHxCELL_HEIGHT) character?
I build these methods in a project I happened to have open when reading this question =D. Do note that the method pickOptimalFontSize should be adapted for your specfic case. The default size is 130 which would likely be far to high for your case. You can tweak it as you need but this demonstrates the basics. In your case use them like this:
Font baseFont = new Font("monospaced", Font.PLAIN, 12);
for(int x = 0; x < GRID_WIDTH; x++) {
for(int y = 0; y < GRID_HEIGHT; y++) {
graphics.setFont(pickOptimalFontSize(graphics, Character.toString(grid[x][y]), CELL_WIDTH, CELL_HEIGHT, baseFont));
drawString(graphics, Character.toString(grid[x][y]), x * CELL_WIDTH, (y + 1) * CELL_HEIGHT, "left", "center");
}
}
public static void drawString(Graphics g, String str, double x, double y, String hAlign, String vAlign) {
FontMetrics metrics = g.getFontMetrics();
double dX = x;
double dY = y;
if(hAlign == null || "left".equals(hAlign.toLowerCase())) {
} else if("center".equals(hAlign.toLowerCase())) {
dX -= metrics.getStringBounds(str, g).getWidth()/2;
} else if("right".equals(hAlign.toLowerCase())) {
dX -= metrics.getStringBounds(str, g).getWidth();
}
if(vAlign == null || "bottom".equals(vAlign.toLowerCase())) {
} else if("center".equals(vAlign.toLowerCase())) {
dY += metrics.getAscent()/2;
} else if("top".equals(vAlign.toLowerCase())) {
dY += metrics.getAscent();
}
g.drawString(str, (int)dX, (int)dY);
}
private static Font pickOptimalFontSize (Graphics2D g, String title, int width, int height, Font baseFont) {
Rectangle2D rect = null;
float fontSize = 130; //initial value
Font font;
do {
fontSize-=1;
font = baseFont.deriveFont(fontSize);
rect = getStringBoundsRectangle2D(g, title, font);
} while (rect.getWidth() >= width || rect.getHeight() >= height);
return font;
}
public static Rectangle2D getStringBoundsRectangle2D (Graphics g, String title, Font font) {
g.setFont(font);
FontMetrics fm = g.getFontMetrics();
Rectangle2D rect = fm.getStringBounds(title, g);
return rect;
}
I found a solution that works as I wanted: I created a class called CharacterImageGenerator that generates (and caches) Images of characters. I then draw and scale these images whenever I want to draw a character.
public class CharacterImageGenerator {
private FontMetrics metrics;
private Color color;
private Map<Character, Image> images;
public CharacterImageGenerator(FontMetrics metrics, Color color) {
this.metrics = metrics;
this.color = color;
images = new HashMap<Character, Image>();
}
public Image getImage(char c) {
if(images.containsKey(c))
return images.get(c);
Rectangle2D bounds = new TextLayout(Character.toString(c), metrics.getFont(), metrics.getFontRenderContext()).getOutline(null).getBounds();
if(bounds.getWidth() == 0 || bounds.getHeight() == 0) {
images.put(c, null);
return null;
}
Image image = new BufferedImage((int)bounds.getWidth(), (int)bounds.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
Graphics g = image.getGraphics();
g.setColor(color);
g.setFont(metrics.getFont());
g.drawString(Character.toString(c), 0, (int)(bounds.getHeight() - bounds.getMaxY()));
images.put(c, image);
return image;
}
}
Which I then initialize with a large font to get decent looking characters.
// During initialization
graphics.setFont(new Font("monospaced", Font.PLAIN, 24));
characterGenerator = new CharacterImageGenerator(graphics.getFontMetrics(), Color.WHITE);
And then scale and draw to the size I want.
private void drawCharacter(int x, int y, char c) {
graphics.drawImage(characterGenerator.getImage(c), PADDING + (x * TILE_WIDTH), PADDING + (y * TILE_HEIGHT), TILE_WIDTH, TILE_HEIGHT, null);
}
Related
I was working on a brick breaker game in proccessing, and was trying to create the collision system for the game. I was able to identify when a collision happens between the ball and the brick, but I was not able to to make the brick dissapear when it gets hit by the ball. How do I do this? I would also appreciate a explanation, as I am a beginner.
Thanks.
color black = color(0,0,0);
color red = color(255,0,0);
color white = color(255,255,255);
float ballx = 412.5, bally = 600, balld = 20, ballr = balld/2, paddleX = 362.5, paddleY = 650;
float paddleW = 100, paddleH = 15;
float ballspdX, ballspdY;
boolean ball_drop = true;
float direction_choice;
float[] brickX = new float[10];
float[] brickY = new float[5];
float brickW = 50, brickH = 25;
void setup(){
size(825,800);
surface.setTitle("Brick breaker");
noCursor();
smooth();
brickX[0] = 50;
brickX[1] = 125;
brickX[2] = 200;
brickX[3] = 275;
brickX[4] = 350;
brickX[5] = 425;
brickX[6] = 500;
brickX[7] = 575;
brickX[8] = 650;
brickX[9] = 725;
brickY[0] = 50;
brickY[1] = 125;
brickY[2] = 200;
brickY[3] = 275;
brickY[4] = 350;
}
void paddle(){
noStroke();
fill(white);
rect(paddleX, paddleY, paddleW, paddleH);
}
void ball(){
noStroke();
fill(red);
circle(ballx, bally, balld);
}
void draw(){
background(black);
paddle();
ball();
if (bally + ballr == paddleY && ballx > paddleX && ballx < paddleX + (paddleW / 2)){
ball_drop = false;
ballspdY = -ballspdY;
ballspdX = -4;
}
/*
if (bally + ballr == paddleY && ballx > paddleX && ballx < paddleX + paddleW){
ball_drop = false;
ballspdY = -ballspdY;
direction_choice = int(random(1,3));
if (direction_choice == 1){
ballspdX = 4;
}
if (direction_choice == 2){
ballspdX = -4;
}
}
println(direction_choice);
*/
if (bally + ballr == paddleY && ballx > paddleX + (paddleW /2) && ballx < paddleX + paddleW){
ball_drop = false;
ballspdY = -ballspdY;
ballspdX = 4;
}
if (ballx + ballr > width || ballx - ballr < 0){
ballspdX = -ballspdX;
}
if (bally - ballr < 0){
ballspdY = -ballspdY;
}
if (ball_drop){
ballspdX = 0;
ballspdY = 1;
}
for (int i = 0; i < brickX.length; i ++){
for(int j = 0; j < brickY.length; j ++){
fill(red);
rect(brickX[i], brickY[j], brickW, brickH);
if (collideLineCircle(brickX[i], brickY[j] + brickH, brickX[i] + brickW, brickY[j] + brickH, ballx, bally, ballr)){
ballspdY = -ballspdY;
}
}
}
if (bally >= 800){
bally = 600;
ballx = 412.5;
ball_drop = true;
}
bally += ballspdY;
ballx += ballspdX;
}
boolean collideLineCircle(float x1, float y1, float x2, float y2, float cx, float cy, float cr){
float A = y2 - y1, B = x1 - x2, C = x2*y1 - x1+y2;
float denom = A+A + B*B;
if (denom == 0){
return dist(x1,y1, cx, cy) < cr;
}
float Ix = (B*B*cx-A*B*cy - A*C)/denom, Iy = (A*A*cy-A*B*cx - B*C)/denom;
if (Ix >= min(x1,x2) && Ix <= max(x1,x2) & Iy >= min(y1,y2) && Iy <= max(y1,y2)) {
return abs (A*cx + B*cy + C)/sqrt(denom) < cr;
}
float d1 = dist(x1,y1,cx,cy), d2 = dist(x2,y2,cx,cy);
return min(d1,d2) < cr;
}
void mouseMoved(MouseEvent evt){
paddleX = evt.getX();
}
There are a number of ways this can be done. Perhaps the simplest change, given the data structures you already have is to create another array, this time a boolean array, and call it something like brickIsVisible.
Then you can use a simple if statement, e.g., if (brickIsVisible[i]) in your loops to determine how to color the brick. The same variable can be used to tell whether or not to do a collision test between the ball and the brick.
As for the coloring itself, there are a couple options.
I'm not sure how you are drawing your board. If you redraw the entire board with each gameloop frame, then presumably you first draw the background (which covers over the bricks) and then you draw the bricks. If this what is happening, then if brickIsVisible[i] is false you can simply omit drawing that brick on top of the background.
Another technique that is sometimes used is to make use of a transparent color. This is done by adding an alpha channel value to the color definition. Thus there are four variables instead of three. They are referred to as RGBA instead of RGB. If the fourth variable is 0, the color will be transparent. If it is 255 it will be fully visible. Intermediate values in sequence can be used if you want to create a fade in or fade out.
I am guessing you are using java.awt.Color. Following is an example that uses the four argument constructor, with the fourth being the alpha channel.
Color invisiblePurple = new Color(255, 0, 255, 0);
If the color is invisible, the RGB values don't really matter.
These days I mostly use JavaFX for graphics, so I might not be up on all the tricks of the trade with AWT/Swing. With JavaFX, one can directly set an opacity property for a graphic being displayed, which is pretty convenient. I think part of the reason that this is available in JavaFX is that screen graphics are structured more like a DOM tree (like an HTML domain object model), and the redrawing is handled behind the scenes on the current state of the tree, rather than explicitly for each object, as it is with AWT/Swing.
Another possible technique shown below uses a brick class array. The class has a 'show' boolean associated with it and the bricks are only displayed when brick[id].show is true. In this demo when the brick is selected with a mouse brick[id].show is set to false and that brick is not displayed. For your project you would have to do this when a brick is hit by the ball.
/*
Creates a grid of rectangles from a Brick class array.
Syntax: brick[id] = new Brick( x, y, w, h, "title", bkgrndColor, txtColor);
ID is taken from position in array.
*/
final int _brickGridX = 40;
final int _brickGridY = 60;
final int _brickW = 120;
final int _brickH = 60;
color BLUE = color(64, 124, 188);
color LTGRAY = color(185, 180, 180);
color YELLOW = color(245, 250, 13);
color RED = color(255, 0, 0);
color BLACK = color(0, 0, 0);
color WHITE = color(255, 255, 255);
Brick[] brick;
class Brick {
float x, y, w, h;
String title;
color bkgrnd;
color txtColor;
boolean show;
// Constructor
Brick(int xpos, int ypos, float wt, float ht, String titleStr, color background, color foreground) {
x = xpos;
y = ypos;
w = wt;
h = ht;
title = titleStr;
bkgrnd = background;
txtColor = foreground;
}
void display(int id) {
if (brick[id].show) {
fill(bkgrnd); // background color
stroke(0);
rect(x, y, w, h);
fill(txtColor); // text color
textSize(42);
textAlign(CENTER, CENTER);
text(title, x, y, w, h);
}
}
void press(int id) {
println("brick id = ", id + ": show =", brick[id].show);
}
}
void brickGrid() {
int left = 0;
int top = 0;
int vg = 0; // Space between cols (vert.gutter)
int hg = 0; // Space between rows (horz.gutter)
int rows = 5;
int cols = 5;
int id = 0;
brick = new Brick[rows*cols]; // creates brick array
for (int k = 0; k < cols; k++) {
for (int j = 0; j < rows; j++) {
left = _brickGridX + j * (_brickW + vg);
top = _brickGridY + k * (_brickH + hg);
brick[id] = new Brick(left, top, _brickW, _brickH, str(id), LTGRAY, BLACK);
brick[id].show = true;
id++;
}
}
}
void setup() {
size(800, 600);
background(BLUE);
brickGrid();
}
void draw() {
background(BLUE);
for (int i = 0; i < brick.length; i++) {
brick[i].display(i);
}
}
void mousePressed() {
for (int i = 0; i < brick.length; i++) {
if ((mouseX >= brick[i].x) && (mouseX <= brick[i].x + _brickW) && (mouseY >= brick[i].y) && (mouseY <= brick[i].y + _brickH)) {
brick[i].show = false;
// brick[i].press(i);
}
}
}
i have been trying to make a custom BarChart in Java for a school project but for some reason it has some weird Scaling issues. Here is the Code.
static class BarChart extends JPanel
{
private int[] chartValues;
private String[] chartLabels;
private String chartTitle;
public BarChart(String title,int[] values,String[]labels)
{
this.chartTitle = title;
this.chartValues = values;
this.chartLabels = labels;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Random r = new Random();
if(this.chartValues == null || this.chartValues.length==0)
{
return;
}
Dimension chartDimension = this.getSize();
int panelWidth = chartDimension.width;
int panelHeight = chartDimension.height;
int barWidth = panelWidth / this.chartValues.length;
int maxValue = this.chartValues[0];
int minValue = this.chartValues[0];
for(int tempValue:this.chartValues)
{
maxValue = Math.max(maxValue, tempValue);
minValue = Math.min(minValue, tempValue);
}
Font titleFont = new Font("Book Antiqua", Font.BOLD, 15);
FontMetrics titleFontMetrics = g.getFontMetrics(titleFont);
Font labelFont = new Font("Book Antiqua", Font.PLAIN, 14);
FontMetrics labelFontMetrics = g.getFontMetrics(labelFont);
int titleWidth = titleFontMetrics.stringWidth(this.chartTitle);
int stringHeight = titleFontMetrics.getAscent();
int stringWidth = (panelWidth - titleWidth) / 2;
g.setFont(titleFont);
g.drawString(this.chartTitle, stringWidth, stringHeight);
int top = titleFontMetrics.getHeight();
int bottom = labelFontMetrics.getHeight();
if(maxValue==minValue)
{
return;
}
double barScale = (panelHeight - top - bottom)/(maxValue - minValue);
stringHeight = panelHeight - labelFontMetrics.getDescent();
int xPos = 5;
g.setFont(labelFont);
for(int i=0; i<this.chartValues.length;i++)
{
int tempValue = this.chartValues[i];
int barHeight = (int) ( (double)tempValue * barScale);
int yPos = top;
if(tempValue>=0)
{
yPos += (int) ((maxValue - tempValue)* barScale);
}
else
{
yPos += (int) (maxValue * barScale);
barHeight = - barHeight;
}
g.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
g.fillRect(xPos, yPos, barWidth, barHeight);
g.setColor(Color.BLUE);
g.drawRect(xPos, yPos, barWidth, barHeight);
xPos += barWidth;
}
g.setColor(Color.BLACK);
g.drawString("Xronos", stringWidth, stringHeight);
}
}
But when i run this with my main with values {1,5,4,7,120} i get this depending on the screen resolution.
Wrong image (the height between bars and label is too much).
Correct height between bars and label. I really apreciate any help. And sorry if this stupid is a question.
you are doing integer division here:
double barScale = (panelHeight - top - bottom)/(maxValue - minValue);
try
double barScale = (panelHeight - top - bottom)/(double)(maxValue - minValue);
instead
I am trying to write a function that overlays an image at a rectangle with transparency over top of another image, However it doesn't layer the images it just erases the section that I overlay and the transparency cuts through the entire image. Here is my code.
public static void overlayImage(String imagePath, String overlayPath, int x, int y, int width, int height) {
Mat overlay = Imgcodecs.imread(overlayPath, Imgcodecs.IMREAD_UNCHANGED);
Mat image = Imgcodecs.imread(imagePath, Imgcodecs.IMREAD_UNCHANGED);
Rectangle rect = new Rectangle(x, y, width, height);
Imgproc.resize(overlay, overlay, rect.size());
Mat submat = image.submat(new Rect(rect.x, rect.y, overlay.cols(), overlay.rows()));
overlay.copyTo(submat);
Imgcodecs.imwrite(imagePath, image);
}
EDIT: Here are some example pictures:
Before:
After:
Found this function that does exactly what I needed.
public static void overlayImage(Mat background,Mat foreground,Mat output, Point location){
background.copyTo(output);
for(int y = (int) Math.max(location.y , 0); y < background.rows(); ++y){
int fY = (int) (y - location.y);
if(fY >= foreground.rows())
break;
for(int x = (int) Math.max(location.x, 0); x < background.cols(); ++x){
int fX = (int) (x - location.x);
if(fX >= foreground.cols()){
break;
}
double opacity;
double[] finalPixelValue = new double[4];
opacity = foreground.get(fY , fX)[3];
finalPixelValue[0] = background.get(y, x)[0];
finalPixelValue[1] = background.get(y, x)[1];
finalPixelValue[2] = background.get(y, x)[2];
finalPixelValue[3] = background.get(y, x)[3];
for(int c = 0; c < output.channels(); ++c){
if(opacity > 0){
double foregroundPx = foreground.get(fY, fX)[c];
double backgroundPx = background.get(y, x)[c];
float fOpacity = (float) (opacity / 255);
finalPixelValue[c] = ((backgroundPx * ( 1.0 - fOpacity)) + (foregroundPx * fOpacity));
if(c==3){
finalPixelValue[c] = foreground.get(fY,fX)[3];
}
}
}
output.put(y, x,finalPixelValue);
}
}
}
I have a strange problem, I can figure it out.
here is my program running:
the clock on the right shouldn't even be there, it should just be string that displays the time. I create two different objects and add them to a content panel, then add the content panel to the JFrame. I also have a menu bar that I add to the JFrame and it shows up on the right side of the content pane. Here is my main class:
class DigitalClock3DialsView extends DigitalClockView {
private static int caps[] = { BasicStroke.CAP_BUTT,
BasicStroke.CAP_SQUARE, BasicStroke.CAP_ROUND};
private static int joins[] = { BasicStroke.JOIN_MITER,
BasicStroke.JOIN_BEVEL, BasicStroke.JOIN_ROUND};
private static Color colors[] = {Color.gray, Color.pink, Color.lightGray};
private static BasicStroke bs1 = new BasicStroke(1.0f);
// three arms of clock
private Line2D lines[] = new Line2D[3];
private int rAmt[] = new int[lines.length];
private int speed[] = new int[lines.length];
private BasicStroke strokes[] = new BasicStroke[lines.length];
private GeneralPath path;
private Point2D[] pts;
private float size;
private Ellipse2D ellipse = new Ellipse2D.Double();
private BufferedImage bimg;
//variables to keep track if minutes or hours increased
private int oldMinute;
private int oldHour;
/**
* init background
*/
public void init() {
setBackground(Color.white);
}
/**
* reset view: the shape of arms, etc
*/
public void reset(int w, int h)
{
oldMinute = 0;
oldHour = 0;
size = (w > h) ? h/6f : w/6f;
for (int i = 0; i < lines.length; i++) {
lines[i] = new Line2D.Float(0,0,size,0);
strokes[i] = new BasicStroke(size/3, caps[i], joins[i]);
rAmt[i] = 270; // vertical
}
//speed of the 3 arms
speed[0] = 6;
speed[1] = 6;
speed[2] = 6;
path = new GeneralPath();
path.moveTo(size, -size/2);
path.lineTo(size+size/2, 0);
path.lineTo(size, +size/2);
// YW: do not know how to show the regular clock view
// with inc of 5 mins on the contour
// can you help to fix this?
ellipse.setFrame(w/2-size*2-4.5f,h/2-size*2-4.5f,size*4,size*4);
double linApproxLen = 0.75 * size * 0.258819; // sin(15 degree)
PathIterator pi = ellipse.getPathIterator(null, linApproxLen);
Point2D[] points = new Point2D[100];
int num_pts = 0;
while ( !pi.isDone() )
{
float[] pt = new float[6];
switch ( pi.currentSegment(pt) ) {
case FlatteningPathIterator.SEG_MOVETO:
case FlatteningPathIterator.SEG_LINETO:
points[num_pts] = new Point2D.Float(pt[0], pt[1]);
num_pts++;
}
pi.next();
}
pts = new Point2D[num_pts];
System.arraycopy(points, 0, pts, 0, num_pts);
}
private Point2D[] computePoints(double w, double h, int n)
{
Point2D points[] = new Point2D[n];
double angleDeltaRad = Math.PI * 2 / n;
for (int i=0; i<n; i++)
{
double angleRad = i * angleDeltaRad;
double ca = Math.cos(angleRad);
double sa = Math.sin(angleRad);
double x = sa * w/2;
double y = ca * h/2;
points[i] = new Point2D.Double(x,y);
}
return points;
}
public void paint(Graphics g)
{
System.out.printf("seconds: %s, minutes: %d \n", second, minute);
Dimension d = getSize();
updateSecond(d.width, d.height);
if(oldMinute < minute || (oldMinute==59 && minute==0))
{
oldMinute = minute;
updateMinute(d.width, d.height);
}
if(oldHour < hour || (oldHour==12 && hour == 1) )
{
oldHour = hour;
updateHour(d.width, d.height);
}
Graphics2D g2 = createGraphics2D(d.width, d.height);
drawClockArms(d.width, d.height, g2);
g2.dispose();
g.drawImage(bimg, 0, 0, this);
}
public Graphics2D createGraphics2D(int w, int h) {
// standard Java 2D code
Graphics2D g2 = null;
if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
bimg = (BufferedImage) createImage(w, h);
reset(w, h);
}
g2 = bimg.createGraphics();
g2.setBackground(getBackground());
g2.clearRect(0, 0, w, h);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
return g2;
}
private void drawClockArms(int w, int h, Graphics2D g2) {
ellipse.setFrame(w/2-size,h/2-size,size*2,size*2);
g2.setColor(Color.black);
g2.draw(ellipse);
for (int i = 0; i < lines.length; i++) {
AffineTransform at = AffineTransform.getTranslateInstance(w/2,h/2);
at.rotate(Math.toRadians(rAmt[i]));
g2.setStroke(strokes[i]);
g2.setColor(colors[i]);
g2.draw(at.createTransformedShape(lines[i]));
g2.draw(at.createTransformedShape(path));
}
g2.setStroke(bs1);
g2.setColor(Color.black);
for (int i = 0; i < pts.length; i++) {
ellipse.setFrame(pts[i].getX(), pts[i].getY(), 9, 9);
g2.draw(ellipse);
}
}
/**
* step forward on the display: move arm forward
*/
public void step(int w, int h) {
for (int i = 0; i < lines.length; i++)
{
rAmt[i] += speed[i];
System.out.println(rAmt[i]);
if (rAmt[i] == 360) {
rAmt[i] = 0;
}
}
}
public void updateSecond(int w, int h) {
rAmt[0] += speed[0];
if (rAmt[0] == 360) {
rAmt[0] = 0;
}
}
public void updateMinute(int w, int h) {
rAmt[1] += speed[1];
if (rAmt[1] == 360) {
rAmt[1] = 0;
}
}
public void updateHour(int w, int h) {
rAmt[2] += speed[2];
if (rAmt[2] == 360) {
rAmt[2] = 0;
}
}
}
and here is the parent class that extends JPanel:
/**
* Digital Clock view base classes
*/
abstract class DigitalClockView extends JPanel
{
protected int second = 0;
protected int minute = 0;
protected int hour = 0;
public void draw()
{
this.repaint();
}
public void updateTime(int second, int minute, int hour)
{
this.second = second;
this.minute = minute;
this.hour = hour;
}
public abstract void paint(Graphics g);
}
my menu buttons are showing on the right side when I use the menu bar. It's almost as if it is just cloning the view on the left side and showing it on the right side again. Why is this?
super.repaint()
Don't do that, as calling repaint from within a paint method has potential for danger. Call the super.paint(g) method inside of a paint(Graphics g) override.
Or even better yet, don't override paint but instead override your JPanel's paintComponent(Graphics g) method and call super.paintComponent(g). For graphics you must call the same super method as the overridden method, and you're not doing that.
Also and again, you should avoid use of null layout as this makes for very inflexible GUI's that while they might look good on one platform look terrible on most other platforms or screen resolutions and that are very difficult to update and maintain.
Here's my original question on SO kindly answered. The height is now set at what I think is the correct size. But I can't see the bottom 2/3s of the panel.
I have read, and asked, and mused, and experimented, but I still cannot find an answer. I don't need code, just a little help.
My JFrame class;
public Frame(String title) throws FileNotFoundException {
super(String.format("Title", title));
this.panel = new Panel();
this.panel.drawLinesAndTab();
this.panel.setSize(this.panel.getPreferredSize());
this.panel.validate();
this.scroller = new JScrollPane(this.panel);
//this.scroller.setPreferredSize(new Dimension(this.panel.getPreferredSize()));
this.scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
this.scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
//this.scroller.setSize(new Dimension(this.panel.getPreferredSize()));
this.scroller.getVerticalScrollBar().setUnitIncrement(20);
this.getContentPane().add(this.scroller);
//this.pack();
}
and this is the JPanel class. I know it's huge, and I do have plans to re-write this code, but I'm under time limitations and have to try and get it at least seeing all of the output.
public Panel() throws FileNotFoundException {
this.tab = new ReadTabFile("tabSource.txt");
this.image = new BufferedImage(this.width, this.height, BufferedImage.TYPE_INT_RGB);
Graphics g = this.image.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, this.image.getWidth(), this.image.getHeight());
setBorder(BorderFactory.createLineBorder(Color.black));
this.setFocusable(true);
}
public void drawLinesAndTab() {
Graphics g = this.image.getGraphics();
g.setColor(Color.black);
this.list = tab.readTabCode();
this.a = 20;
this.b = 100;
this.c = 60;
this.x = 40;
this.y = 100;
this.beginBarlineX = 20;
this.beginBarlineY = 100;
this.endBarY = 980;
this.endBarY = 100;
this.title = tab.getTitle();
g.drawString(this.title, 40, 20);
for (int i = 0; i < this.list.size(); i++) {
Bar theBar = (Bar) this.list.get(i);
drawBarline(a, b, a, b + 125);
ArrayList<String> stuff = theBar.getLinesInBar();
for (int j = 0; j < stuff.size(); j++) {
String line = stuff.get(j);
theFlag = line.substring(0, 1);
theNotes = line.substring(1, line.length());
if (newLine = true) {
}
try {
System.out.println(theNotes);
if (c <= (width - 40)) {
newLine = false;
String zero = theFlag;
drawFlag(zero, x + 5, y - 20);
String one = theNotes.substring(0, 1);
g.drawLine(a, b, c, b);
drawLetter(one, x, y);
String two = theNotes.substring(1, 2);
drawLetter(two, x, y += 25);
g.drawLine(a, b += 25, c, b);
String three = theNotes.substring(2, 3);
drawLetter(three, x, y += 25);
g.drawLine(a, b += 25, c, b);
String four = theNotes.substring(3, 4);
drawLetter(four, x, y += 25);
g.drawLine(a, b += 25, c, b);
String five = theNotes.substring(4, 5);
drawLetter(five, x, y += 25);
g.drawLine(a, b += 25, c, b);
String six = theNotes.substring(5, 6);
drawLetter(six, x, y += 25);
g.drawLine(a, b += 25, c, b);
this.repaint();
b -= 125;
y -= 125;
x += 40;
a += 40;
c += 40;
} else {
if (height < (b - 100)) {
height += 205;
}
newLine = true;
a = 20;
x = 20;
b += 225;
c = 60;
y += 225;
beginBarlineX = 20;
beginBarlineY += 100;
endBarX += 100;
endBarY = 100;
this.repaint();
}
} catch (Exception ex) {
System.err.println(ex + " within if drawtab/line for loop");
}
}
}
}
public void drawBarline(int xTop, int yTop, int xBot, int yBot) {
Graphics g = this.image.getGraphics();
g.setColor(Color.black);
g.drawLine(xTop, yTop, xBot, yBot);
}
public Point makeBarline(int xTop, int yTop, int xBot, int yBot) {
Graphics g = this.image.getGraphics();
g.setColor(Color.black);
g.drawLine(xTop, yTop, xBot, yBot);
return (new Point());
}
public Point drawLetter(String letter, int x, int y) throws FontFormatException, IOException {
Graphics g = this.image.getGraphics();
g.setColor(Color.black);
g.setFont(letterFont(letter).deriveFont(20.0f));
g.drawString(letter, x, y);
return (new Point());
}
public Point drawFlag(String letter, int x, int y) throws FontFormatException, IOException {
Graphics g = this.image.getGraphics();
g.setColor(Color.black);
g.setFont(flagFont(letter).deriveFont(30.0f));
g.drawString(letter, x, y);
return (new Point());
}
public Font letterFont(String fontString) throws FontFormatException, IOException {
Graphics g = this.image.getGraphics();
g.setColor(Color.black);
if (!Character.isDigit(fontString.charAt(0))) {
this.letterFont = Font.createFont(Font.TRUETYPE_FONT, new File("LeRoy.ttf"));
g.getFontMetrics(this.letterFont);
g.setFont(this.letterFont);
return this.letterFont;
} else {
return null;
}
}
public Font flagFont(String fontString) throws FontFormatException, IOException {
Graphics g = this.image.getGraphics();
g.setColor(Color.black);
if (!Character.isDigit(fontString.charAt(0))) {
this.flagFont = Font.createFont(Font.TRUETYPE_FONT, new File("LeroyLuteNotes1.ttf"));
g.getFontMetrics(this.flagFont);
g.setFont(this.flagFont);
return this.flagFont;
} else {
return null;
}
}
#Override
public Dimension getPreferredSize() {
return (new Dimension(this.width, this.height));
}
public BufferedImage getImage() {
return this.image;
}
#Override
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
Graphics g = graphics.create();
g.drawImage(this.image, 0, 0, null);
}
}
I'm not sure what you are attempting to do with that code. It looks like you might be trying to do some custom painting on top of an image. If so then I have the following suggestions:
public Dimension getPreferredSize() {
return (new Dimension(this.width, this.height));
}
This makes no sense, you are saying that the preferred size is equal to the actual size of the component. The preferred size should be the size of the image.
Your custom painting code is completely wrong. All custom painting code should be done from the paintComponent() method. So first you would paint the image as the background of your component. Then you would invoke the other painting methods to paint stuff on top of the image. You would pass the Graphics object from the paintComponent() method to all of these other painting methods, instead of using image.getGraphics().
Start with a simple custom painting example from the Swing tutorial on Custom Painting. Once you learn the basics you customize your code one step at a time. That is first paint the background. Make sure the size is correct and scrolling works. Then move to the next step and add another method to paint something else on top of the image.
I never understand why people write hundreds of lines of code without doing basic testing along the way to make sure the code is working.