I'm just trying to draw a circle on the spot where I touch the screen. When I touch the screen, a circle is drawn there, but then when I touch the screen somewhere else a new circle is drawn (the old one is supposed to appear there, not a new one). Does anyone understand why this is happening? Code:
Thread class:
public class GameThread extends Thread{
private SurfaceHolder sHolder;
private DrawingSurface dSurface;
private boolean okToRun;
Paint redPaint = new Paint();
public int x, y;
boolean myTouchEvent(MotionEvent event){
int touch = event.getAction();
switch(touch){
case MotionEvent.ACTION_DOWN:
x = (int) event.getX();
y = (int) event.getY();
}
return true;
}
public GameThread(SurfaceHolder holder, DrawingSurface surface){
sHolder = holder;
dSurface = surface;
redPaint.setARGB(255, 255, 0, 0);
}//GameThread()
public void setOkToRun(boolean status){
okToRun = status;
}//setOkToRun()
public void run(){
while(okToRun){//gameloop
Canvas canvas = null;
try{
canvas = sHolder.lockCanvas(null);
synchronized(sHolder){
try{
canvas.drawCircle(x, y, 60, redPaint);
} catch (Exception e){
}
}
} finally {
if (canvas != null) {
sHolder.unlockCanvasAndPost(canvas);
}
}
}
}//run()
}
Here are the relevant methods of my surfaceview class:
#Override
public boolean onTouchEvent(MotionEvent event) {
return drawingThread.onTouchEvent(event);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
drawingThread = new GameThread(getHolder(), this);
drawingThread.start();
drawingThread.setOkToRun(true);
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
radius += 10;
drawingThread.setOkToRun(false);
try {
drawingThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
TO BE CLEAR: I want to draw a single circle, and have it appear wherever I tap the screen. It does not do that. Can you tell why that is the case in this code?
I hope I am right that you need to draw a circle at the place where the screen was touched and the problem is that circles from previous touches still appear on the screen.
In this case the problem is, that you are drawing circles on canvas without clearing it. Canvas is represented in a memory as a array. When you call Canvas.drawCircle() part of memory is rewritten by an image of circle. When you do it repeatedly, the canvas contains more circles which are being drawn on the screen. You need to repaint the whole canvas before you draw a new circle. It can be done by calling the method Canvas.drawColor() before calling the method Canvas.drawCircle(). It clears the whole Canvas with selected color.
Related
so I am doing what is suppose to be a simple game but I think I might be complicating things. I have a camera for the GameScreen and a viewPort which follow the position of the player and when it reaches to points on the sides, the camera stops following the player and stays in one point.
This by itself works fine, but then I wanted to add the pause menu and some other features in the game, creating a hud class with its own camera and viewport as well as a Stage and a shapeRenderer.
The problem comes when I create the instance of this hud inside my gameScreen, the camera that I am looking while I am playing looks like is the hudCam, which does not follow the player and basically does not let me see the player when it reaches the edges of the screen.
This is my GameScreen Class:
public class GameScreen implements Screen {
WowInvasion game;
ScrollingBackground background;
private OrthographicCamera gameCam;
private Viewport gameViewPort;
/*
Basically I wanna keep the same sprites running while in the menu, playing and till dead
therefore, I'll have a switch statement with cases on where the games at, inside the functions needed. That way I'll keep
the game has a background for the menu and there's no need for running a second screen.
*/
public static final int MAIN_MENU = 0;
public static final int GAME = 1;
private static int state = 1; //current state. starts with MAIN_MENU //DEBUGGING GAME SCREEN
//STAGES
private GameStage gameStage; //game ui
private menuStage mainMenu; //Main menu of the game
private Hud hud;
//Resources
private TextureAtlas atlas; //for the textures most
private Skin skin; //for the styles and fonts
//Sprites
private Player player;
//Shapes
private float progressPower; //for the power to build up
private final float POWER_CHARGED = 1000; //limit to get power
private final float DECREASING_POWER = 20; //limit to get power
public GameScreen(WowInvasion game){
this.game = game;
gameCam = new OrthographicCamera();
gameCam.setToOrtho(false, WowInvasion.WIDTH, WowInvasion.HEIGHT);
gameViewPort = new StretchViewport(WowInvasion.WIDTH, WowInvasion.HEIGHT, gameCam);
progressPower = 0f;
game.wowAssetManager.loadTexturesGameScreen(); // tells our asset manger that we want to load the images set in loadImages method
game.wowAssetManager.loadSkins(); //load the needed skins
game.wowAssetManager.manager.finishLoading(); // tells the asset manager to load the images and wait until finsihed loading.
skin = game.wowAssetManager.manager.get("ui/menuSkin.json");
}
#Override
public void show() {
game.batch.setProjectionMatrix(gameCam.combined);
background = new ScrollingBackground();
atlas = game.wowAssetManager.manager.get(WowAssetManager.GAME_ATLAS); //declaring atlas
mainMenu = new menuStage(gameViewPort, game.batch, skin, game); //pass it so that we only use one batch and one same viewport
gameStage = new GameStage(gameViewPort, game.batch, skin, game);
hud = new Hud(game.batch, skin);
player = new Player(atlas.findRegion("player"), (int) gameCam.viewportWidth / 2, (int) gameCam.viewportHeight / 2);
switch(state){
case MAIN_MENU:
Gdx.input.setInputProcessor(mainMenu);
break;
case GAME:
background.setFixedSpeed(false); //does not work in here
}
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if(state == GAME) {
background.setFixedSpeed(false);
player.update(delta, gameCam.viewportWidth, gameCam.viewportHeight); //updating player for movement
//really cheap way to charge power with velocity
if(progressPower != POWER_CHARGED) {
progressPower += Math.abs(player.getVelocity().x) + Math.abs(player.getVelocity().y);
progressPower -= DECREASING_POWER;
}
else
progressPower = POWER_CHARGED / 4;
}
mainMenu.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f)); //updating while making sure delta won't be more than 1/30f.
gameStage.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f));
game.batch.begin();
background.updateAndRender(delta, game.batch); //updating scrolling background
player.draw(game.batch);
game.batch.end();
mainMenu.draw(); //draw the menu stage
gameStage.draw(); //draw the ui stage for the game
hud.getStage().draw();
hud.renderRotateMeter();
updateCamera(0, WowInvasion.WIDTH);
System.out.println(player.getPosition().x);
}
public void updateCamera(float startX, float endX){
Vector3 position = gameCam.position;
//linear interpolation : a + (b - a) * lerp
//b = player position
//a = current camera position
//lerp = interpolation factor
position.x = gameCam.position.x + (player.getPosition().x - gameCam.position.x) * .1f;
//making the camera stay when the player gets to close to the sides
if(position.x < startX) {
position.x = startX;
}
if(position.x > endX){
position.x = endX;
}
gameCam.position.set(position);
gameCam.update();
}
#Override
public void resize(int width, int height) {
gameViewPort.update(width, height);
//hud.getViewport().update(width, height);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
dispose();
}
#Override
public void dispose() {
mainMenu.dispose();
gameStage.dispose();
game.dispose();
hud.dispose();
}
public static void setState(int state) {
GameScreen.state = state;
}
}
And this is my HUD:
public class Hud implements Disposable{
private Stage stage;
private Viewport viewport;
Button buttonPause, buttonResume;
private OrthographicCamera hudCam;
private ShapeRenderer sp; //like a batch for shapes
public Hud(SpriteBatch sb, Skin skin){
hudCam = new OrthographicCamera();
hudCam.setToOrtho(false, WowInvasion.WIDTH, WowInvasion.HEIGHT);
viewport = new StretchViewport(WowInvasion.WIDTH, WowInvasion.HEIGHT, hudCam);
stage = new Stage(viewport, sb);
sp = new ShapeRenderer();
Table table = new Table();
table.top();
//this makes the table the size of the stage
table.setFillParent(true);
buttonPause = new Button(skin, "pause");
buttonPause.setTransform(true);
buttonPause.addListener(new ClickListener(){ //listener to handle event
#Override
public void clicked(InputEvent event, float x, float y) {
}
});
buttonResume = new Button(skin, "resume");
buttonResume.setTransform(true);
buttonResume.setScale(0.5f);
buttonResume.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y) {
buttonResume.setVisible(false);
}
});
table.add(buttonPause);
table.row();
table.add(buttonResume);
stage.addActor(table);
}
public void renderRotateMeter(){
sp.setProjectionMatrix(hudCam.combined);
sp.begin(ShapeRenderer.ShapeType.Filled);
sp.setColor(Color.YELLOW);
sp.rect(hudCam.position.x,hudCam.position.y, WowInvasion.WIDTH / 2, 20);
sp.end();
}
public Viewport getViewport() {
return viewport;
}
public Stage getStage() {
return stage;
}
#Override
public void dispose() {
stage.dispose();
sp.dispose();
}
}
thanks in advance!
EDIT
so I tried passing the gameCam has a parameter to the hud and instead of making a new OrthographicCamera I used that one has the hudCamara as well and well, the movement with the player is perfect except now the thins from the Hud do not move at all..
It looks like you only set projectionMatrix to only HUD camera as seen in
sp.setProjectionMatrix(hudCam.combined);
Try to set it the same to other stuff outside of the HUD class prior to draw call too.
Another thing to keep in mind is that, when you involve using multiple Viewport and Camera in the game as most of the time it will be 1 Viewport matching with 1 Camera and work with another set as in your case. In draw call, you need to call apply() or apply(true) of Viewport class too to tell the system that you will draw based on which viewport thus in turn it will adhere to screen coordinate that was set up by viewport's attaching camera.
So assume you have 2 objects that needed to be called in different viewport consecutively, do it like the following code. The methods call is correct according to libgdx API but variable names are fictional.
// draw objA adhering to viewportA (thus cameraA) <-- assume it's player cam
sb.setProjectionMatrix(cameraA.combined);
viewportA.apply();
objA.draw();
// draw objB adhering to viewportB (thus cameraB) <-- assume it's HUD cam
sb.setProjectionMatrix(cameraB.combined);
viewportB.apply(true); // send in true as for HUD, we always want to center the screen
objB.draw();
In summary, 2 things to keep in mind when drawing objects that use multiple of camera and viewport in consecutive draw call.
Set projection matrix to either SpriteBatch or ShapeRenderer.
Call apply() or apply(true) of Viewport class to let it know you work with this viewport.
I am making some kind of a puzzle game, where there are blocks and the user has to click on it to move the blocks, and I need to setOnItemClickListener on each individual block(rect object) as each block will act differently when clicked based on its position. Now I have managed to draw the blocks, but I am not able to add item click listener to each different blocks.
MainActivity.java
public class MainActivity extends Activity {
PuzzleView puzzleView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get a display object to access screen details
Display display = getWindowManager().getDefaultDisplay();
// Load the resolution into a Point object
Point size = new Point();
display.getSize(size);
// Initialize gameView and set it as the view
puzzleView = new PuzzleView(this, size.x, size.y);
setContentView(puzzleView);
}
// This method executes when the player starts the game
#Override
protected void onResume() {
super.onResume();
// Tell the gameView resume method to execute
puzzleView.resume();
}
}
PuzzleView.java
public class PuzzleView extends SurfaceView implements Runnable {
Context context;
// This is our thread
private Thread gameThread = null;
// Our SurfaceHolder to lock the surface before we draw our graphics
private SurfaceHolder ourHolder;
// A boolean which we will set and unset
// When the game is running or not
private volatile boolean playing;
// Game is paused at the start
private boolean paused = true;
// A canvas and a Paint object
private Canvas canvas;
private Paint paint;
// Size of screen in pixels
private int screenX;
private int screenY;
// Blocks of puzzle
private Block[] blocks = new Block[10];
private int numBlocks;
// Position of block
private int block_right;
// When we initialize call on gameView
// This constructor runs
public PuzzleView(Context context, int x, int y) {
// Ask SurfaceView class to set up our object
super(context);
// Make a globally available copy of the context so we can use it in another method
this.context = context;
// Initialize ourHolder and paint objects
ourHolder = getHolder();
paint = new Paint();
screenX = x;
screenY = y;
block_right = 0;
prepareLevel();
}
private void prepareLevel() {
// Here we will intialize all game objects
// Build the blocks
numBlocks = 0;
for(int column = 0; column < 2; column++) {
for(int row = 0; row < 2; row++) {
blocks[numBlocks] = new Block(row, column, screenX, screenY);
numBlocks++;
}
}
}
#Override
public void run() {
while(playing) {
// Update the frame
if(!paused) {
update();
}
// Draw the frame
draw();
}
}
private void update() {
// Move the block
}
private void draw() {
// Make sure our drawing surface is valid or we crash
if(ourHolder.getSurface().isValid()) {
// Lock the canvas ready to draw
canvas = ourHolder.lockCanvas();
// Draw the background color
canvas.drawColor(Color.argb(255, 255, 255, 255));
// Choose the brush color for drawing
paint.setColor(Color.RED);
// Draw the block
for(int i = 0; i < numBlocks; i++) {
canvas.drawRect(blocks[i].getRect(), paint);
}
//Change brush color for text
paint.setColor(Color.BLACK);
// Draw the score
paint.setTextSize(20);
canvas.drawText("Right: " + block_right, 10, screenY - 50, paint);
// Draw everything to the screen
ourHolder.unlockCanvasAndPost(canvas);
}
}
// If MainActivity is started then start our thread
public void resume() {
playing = true;
gameThread = new Thread(this);
gameThread.start();
}
}
Block.java
public class Block {
private RectF rect;
public Block(int row, int column, int screenX, int screenY) {
int padding = 1;
int width = 50;
int height = 50;
int outer_padding = (screenX - 104) / 2;
rect = new RectF(column * width + padding + outer_padding,
row * height + padding + 40,
column * width + width - padding + outer_padding,
row * height + height - padding + 40);
}
public RectF getRect() {
return this.rect;
}
}
Now how do I make a click event listener to each block object ?
All suggestions and improvements are greatly welcomed.
Thanks in advance.
You can't add listeners to drawn objects. The listeners are for separate views.
If you do want to draw everything by yourself, you can still determine if a touch event happened inside of your rectangle:
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// Check if click is within bounds of your rect
if (event.getX() >= mRectStartX && event.getX() <= mRectEndX && event.getY() >= mRectStartY && event.getY() <= mRectEndY) {
// Clicked within your rect, register further clicks by consuming this click
return true;
}
}
return super.onTouchEvent(event);
}
You'll have to re-draw your rectangle once the touch is detected.
This is much more complicated than a simple click listener but that's what you get for wanting to draw on your own :P
P.S. you can use GestureDetector to simplify this process, but essentially it stays the same.
I have a problem I was trying to solve for almost 2 days. (It ended up by switching to OpenGL :D)
Anyway... I have Canvas and I am trying to do a simple snake game. Just for this problem imagine our snake is made up from 5x5 pixels rectangles, he is leaving the trail (no clearing) and moving to the right from 0, 50 position... here is the code:
public class Snake extends AnimationWallpaper {
#Override
public Engine onCreateEngine() {
return new SnakeEngine();
}
class SnakeEngine extends AnimationEngine {
int i = 0;
Paint paint = new Paint();
#Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
// By default we don't get touch events, so enable them.
setTouchEventsEnabled(true);
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
}
#Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xOffsetStep, float yOffsetStep, int xPixelOffset,
int yPixelOffset) {
super.onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep,
xPixelOffset, yPixelOffset);
}
#Override
public Bundle onCommand(String action, int x, int y, int z,
Bundle extras, boolean resultRequested) {
if ("android.wallpaper.tap".equals(action)) {
}
return super.onCommand(action, x, y, z, extras, resultRequested);
}
#Override
protected void drawFrame() {
SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
draw(c);
}
} finally {
if (c != null)
holder.unlockCanvasAndPost(c);
}
}
void draw(Canvas c) {
//c.save();
//c.drawColor(0xff000000);
paint.setAntiAlias(true);
// paint the fill
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
c.drawRect(i*5, 50, (i+1)*5, 55, paint);
//c.restore();
}
#Override
protected void iteration() {
i++;
super.iteration();
}
}
}
And here is output:
And that is not all, when it draws its kinda shifting... meaning what you see is shifted to the right side so it doesnt actualy stand in the same place...
If you have any idea why is it behave like that, please tell me!
In canvas, there's a reusability functionality which practically just add new drawings to whatever is in the canvas, hence if you are drawing 1 square at the time, the next time you will have whatever u had before +1, if you don't want that, and you want to fully manipulate every single draw from scratch, just add this line of code before to start drawing
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
It will clear anything you had in the previous state of the canvas, and you can start again drawing whatever you want...
Hope It Helps!
Regards!
I wanted to use a SurfaceView with a "sticky" canvas. i.e. one that preserves its state and does not get ivalidated. I followed an example that showed how to set up a holder and use a separate thread for the drawing. The idea is that when touching anywhere on the SurfaceView, drawStuff will be called, given some paint, and a random rectangle should be drawn.
Although the code executes perfectly, nothing is drawn at the end.
public class DrawingView extends SurfaceView implements Callback {
Paint currentPaint;
float verts[];
private Boolean drawing = false;
class DrawingThread extends Thread {
private SurfaceHolder surfaceHolder;
private SurfaceView surfaceView;
private Boolean isRunning = false;
public void setIsRunning(Boolean isRunning) {
this.isRunning = isRunning;
}
public DrawingThread(SurfaceHolder surfaceHolder, SurfaceView surfaceView) {
super();
this.surfaceHolder = surfaceHolder;
this.surfaceView = surfaceView;
}
#Override
public void run() {
while(isRunning) {
Canvas c = surfaceHolder.lockCanvas();
surfaceView.draw(c);
surfaceHolder.unlockCanvasAndPost(c);
}
}
}
DrawingThread thread;
public DrawingView(Context context) {
super(context);
this.setWillNotDraw(false);
this.setBackgroundColor(Color.WHITE);
this.getHolder().addCallback(this);
this.thread = new DrawingThread(getHolder(), this);
}
#Override
protected void onDraw(Canvas canvas) {
if (drawing) {
// TEST CODE. WILL NEVER USE THIS IN PRODUCTION
canvas.drawRect(new Rect(new Float(Math.random()*200).intValue(), 20, new Float(Math.random()*200).intValue(), 30), currentPaint);
drawing = false;
}
}
public void drawStuff(Paint paint) {
currentPaint = paint;
drawing = true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread.setIsRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
thread.setIsRunning(false);
}
}
and the paint set up:
view.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.BLUE);
paint.setAlpha(55);
((DrawingView) v).drawStuff(paint);
//v.invalidate();
return false;
}
});
In the end, you're not actually drawing things when you think you are. Calling draw() schedules the draw on your UI thread, but doesn't actually call onDraw from your background thread so your Canvas is no longer valid with respect to the surface. You should render whatever you are doing into a Bitmap object and when you are ready to write to the surface, notify the UI thread. The UI thread can then lock the surface, draw the bitmap into the Canvas, then unlock and post the Canvas to the surface.
I have a Java application which draws a drawing. I want to give the user the possibility to mark an area with the mouse (in order to, for example, zoom into it).
For that I use the MouseMotionListener class, and when the mouse is (clicked and then) moved, I save the location of the currently selected (it isn't final since the user haven't released the mouse) rectangle, and use the repaint() function. I wish to display that rectangle over the original drawing, making it similar to the Selection tool in MSPaint.
The problem is that when I call the repaint() function, the method paintComponent (Graphics page) is invoked, in which I use the method super.paintComponent(page) which erases my drawing. However, if I don't use that method when I know the user is selecting a rectangle, I get that all the selected rectangles are "packed" one above the other, and this is an undesirable result - I wish to display the currently selected rectangle only.
I thought I should be able to save a copy of the Graphics page of the drawing and somehow restore it every time the user moves the mouse, but I could not find any documentation for helpful methods.
Thank you very much,
Ron.
Edit: Here are the relevant pieces of my code:
public class DrawingPanel extends JPanel
{
public FractalPanel()
{
addMouseListener (new MyListener());
addMouseMotionListener (new MyListener());
setBackground (Color.black);
setPreferredSize (new Dimension(200,200));
setFocusable(true);
}
public void paintComponent (Graphics page)
{
super.paintComponent(page);
//that's where the drawing takes place: page.setColor(Color.red), page.drawOval(..) etc
}
private class MyListener implements MouseListener, MouseMotionListener
{
...
public void mouseDragged (MouseEvent event)
{
//saving the location of the rectangle
isHoldingRectangle = true;
repaint();
}
}
}
I'm betting that you are getting your Graphics object via a getGraphics() call on a component, and are disatisfied since this obtains a Graphics object which does not persist. It is for this reason that you shouldn't do this but instead just do your drawing inside of the JPanel's paintComponent. If you do this all will be happy.
As an aside -- we'll be able to help you better if you tell us more of the pertinent details of your problem such as how you're getting your Graphics object and how you're trying to draw with it, key issues here. Otherwise we're limited to taking wild guesses about what you're trying to do.
e.g.,
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class MandelDraw extends JPanel {
private static final String IMAGE_ADDR = "http://upload.wikimedia.org/" +
"wikipedia/commons/thumb/b/b3/Mandel_zoom_07_satellite.jpg/" +
"800px-Mandel_zoom_07_satellite.jpg";
private static final Color DRAWING_RECT_COLOR = new Color(200, 200, 255);
private static final Color DRAWN_RECT_COLOR = Color.blue;
private BufferedImage image;
private Rectangle rect = null;
private boolean drawing = false;
public MandelDraw() {
try {
image = ImageIO.read(new URL(IMAGE_ADDR));
MyMouseAdapter mouseAdapter = new MyMouseAdapter();
addMouseListener(mouseAdapter);
addMouseMotionListener(mouseAdapter);
} catch (MalformedURLException e) {
e.printStackTrace();
System.exit(-1);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
#Override
public Dimension getPreferredSize() {
if (image != null) {
return new Dimension(image.getWidth(), image.getHeight());
}
return super.getPreferredSize();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
if (image != null) {
g.drawImage(image, 0, 0, null);
}
if (rect == null) {
return;
} else if (drawing) {
g2.setColor(DRAWING_RECT_COLOR);
g2.draw(rect);
} else {
g2.setColor(DRAWN_RECT_COLOR);
g2.draw(rect);
}
}
private class MyMouseAdapter extends MouseAdapter {
private Point mousePress = null;
#Override
public void mousePressed(MouseEvent e) {
mousePress = e.getPoint();
}
#Override
public void mouseDragged(MouseEvent e) {
drawing = true;
int x = Math.min(mousePress.x, e.getPoint().x);
int y = Math.min(mousePress.y, e.getPoint().y);
int width = Math.abs(mousePress.x - e.getPoint().x);
int height = Math.abs(mousePress.y - e.getPoint().y);
rect = new Rectangle(x, y, width, height);
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
drawing = false;
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("MandelDraw");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MandelDraw());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
You need to repaint on every mouse movement:
public void mouseDragged(MouseEvent e){
int x = e.getX();
int y = e.getY();
//Update the rectangle holder object with that point coordinates
repaint();
}
You'll probably have a holder rectangle object to hold the initial and final rectangle points. The initials are set on mouse click, the final are modified on mouse dragged and on mouse released.
In paint method, clear the graphics and draw a rectangle with the coordinates in the holder. This is the basic idea.
UPDATE: How to draw a new shape on top of the existing image:
I'm thinking of two options:
If you are only drawing shapes (such as lines, rectangles and other Java2D stuff) you could have a Collection holding these shapes coordinates, and draw all of them on each paint. Pros: good when there are few shapes, allows undoing. Cons: When the number of shapes increase, the paint method will take more and more time in each pass.
Have a "background image". On each paint call, draw first the image and then the currently active shape on top. when an active shape is made persistent (onMouseReleased), it is saved to the background image. Pros: efficient, constant time. Cons: drawing a big background image on every mouse movement could be "expensive".