I perform some drawing on a canvas every 10 sec. Unfortunately it disappears before it will be redrawn, so we have 10 sec with a blank screen. I tired to save a canvas before drawing and restore it, did not help.
This bug was appeared after I had shifted a line canvas.drawPath(linePath, linePaint); from outside of a loop into the loop.
CODE:
private void drawLine(Canvas canvas) {
yStep = (yHeight) / fullChargeLevel;
xStep = (xWidth) / timeStampBarsQuantity;
boolean check = false;
float time;
float chrg;
while (batteryUsageHistory != null && batteryUsageHistory.moveToNext()) {
int charge = batteryUsageHistory.getInt(1);
int time_stamp = batteryUsageHistory.getInt(2);
if (charge < 1) {
if(check){
canvas.drawPath(linePath, linePaint); //This line I shifted into here
linePath.reset();
}
check = false;
continue;
}
time = xPos + time_stamp * xStep;
chrg = yPos - (charge * yStep);
if (!check) {
linePath.moveTo(time, chrg);
check = true;
continue;
}
linePath.lineTo(time, chrg);
}
//canvas.drawPath(linePath, linePaint); //This line I shifted from here
}
You should implement your drawing logic in onDraw by extending some View class:
protected void onDraw(Canvas canvas) {
// here goes your custom drawing logic
}
as stated in: https://developer.android.com/training/custom-views/custom-drawing.html
it is because android redraws the component when it needs to. It is always like that you need to implement the drawing method and the GUI framework will call this method when neccessary, it is not the opposite, that the GUI framework displays your painting, it is just calling your painting method when needed.
I just moved back that line canvas.drawPath(linePath, linePaint); I was previously moved. And it works!.
Related
When I click screen, the Stop screen flashes on and then it goes back to the Walk screen. How do I get it to stay on the Stop screen until I click my mouse again?
boolean walk;
String textStop;
String textWalk;
float x; //positions
float y; //positions
void setup() {
size (400,500);
stop = false;
textStop = "STOP!";
textWalk = "WALK!";
}
void draw() {
background(0,255,0);
text(textWalk, x , y);
x = 150;
y = 250;
textSize(40);
}
void mousePressed() {
if (stop);{
background(255,0,0);
text(textStop, x , y);
}
} ```
Whenever you call background() you're clearing the screen.
Because this happens in draw() every frame is cleared and redrawn with textWalk (and textStop) only gets displayed when you click (for the short duration of the mousePressed() event).
You could use the walk variable for that. I see you use stop though it's not declared. They seem to be the opposite of each other in context so why not simpify and use just one. Sure you can code this if/else to toggle the value or you can simply use the !(logical not) operator:
void mousePressed() {
walk = !walk;
}
when walk is true render textWalk
otherwise (else) render textStop
something like:
if(walk){
text(textWalk, x , y);
}else{
text(textStop, x , y);
}
Here's a modified version of your code using the notes above:
boolean walk = true;
String textStop = "STOP!";
String textWalk = "WALK!";
float x = 150; //positions
float y = 250; //positions
void setup() {
size (400,500);
textSize(40);
}
void draw() {
if(walk){
background(0,255,0);
text(textWalk, x , y);
}else{
background(255,0,0);
text(textStop, x , y);
}
}
void mousePressed() {
walk = !walk;
}
(Notice I've moved a few lines: in general it's a good idea to initialise values that don't change once at the start of your program rather than continuously. In this basic example it doesn't really affect performance, but for more complex programs it's a good idea to keep track of what changes and what doesn't.
Also, it might help with other programs in the future to have a bit of separation between data that changes on events (e.g. walk on mouse pressed) and how that data gets rendered (e.g. using walk to render in draw())
I am currently working on an animation to compare two stock exchange algorithms. I am running the algorithms within the paint component extending JComponent. (not the best, but I don't care) I need to have the screen refresh half way through the paint component. I do not want it to have to get all the way through before it up dates the screen. The reason being is I have one algorithm with a nested while loop and the other without. How would I go about doing this?
public void paintComponent(Graphics g) {
//calls in the super class and calls the calibrate the graphics method
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g;
calibrateFrame( getHeight(), getWidth() );
//Clears the rectangle to avoid overlaying, makes it black
g2D.clearRect(0, 0, getWidth(), getHeight());
g2D.setColor(Color.BLACK);
g2D.fillRect(0, 0, getWidth(), getHeight());
//Draws the rectangles without the algorithm started
redraw(g2D, -1);
/**
*algorithms
*/
fastSpans[0] = 1;
slowSpans[0] = 1;
//Makes a new stack pushes 0 on the stack
Stack myStack = new Stack();
myStack.push(0);
//If it has not been sorted or paused, continue the algorithm
if (!(pause) && !(sorted)){
//The slower algorithm needs to start out at zero (j)
int j = indexValue-1;
g2D.setColor(Color.BLUE);
//Calculates the values for the X and Y coordinates for the
//new rectangle, along with the height
int slowY = calSlowY(j);
int slowX = calSlowX(j);
int curHeightSlow = (int) ((stocks[j]/maxStockValue)*maxHeight);
//Here is the actual algorithm
int k = 1;
boolean span_end = false;
//Nested While Loop
while (((j-k)>0) && !span_end){
if (stocks[j-k] <= stocks[j]){
k = k + 1;
// Draw the current component
// **********************
// DO REFRESH MID PAINT COMPONENT
}
else{ span_end = true; }
}
slowSpans[j] = k;
g2D.setColor(Color.WHITE);
for(int i = 0; i < numberOfStock ; i++){
}
if (!(indexValue >= numberOfStock)){
while (!( myStack.empty()) && (stocks[(int)myStack.peek()]) <= stocks[indexValue]){
myStack.pop();
}
if (myStack.empty()){
fastSpans[indexValue] = indexValue + 1;
}
else {
fastSpans[indexValue]= indexValue - (int) myStack.peek();
//System.out.println("Im in the else");
}
myStack.push(indexValue);
}
}
drawStrings(g2D);
}
I am running the algorithms within the paint component extending JComponent. (not the best, but I don't care)
But you should care, since this impacts on your problem and possible solution.
I need to have the screen refresh half way through the paint component. I do not want it to have to get all the way through before it up dates the screen. The reason being is I have one algorithm with a nested while loop and the other without. How would I go about doing this?
Then don't run the algorithm through paintComponent. Instead use a SwingWorker<Void, Image>, update a BufferedImage and pass that image to the GUI through the worker's publish/process method pair, calling repaint() at the same time. For more on how to use a SwingWorker, please have a look at: Lesson: Concurrency in Swing
Don't understand what half way means.
If it means half of the component, then the simple way is to using two JComponent.
If u means in same component one line updated but another line not updated.
What I understand the repaint is calling when it packs(), updateUI(), or caused by invalidate().
So in my view, the repaint() should only care about paint these lines/2D, and in another thread to execute these loops/generate these data. Once the data collection is finished just call updateUI()/paintImmediately.
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
repaint();
}
});
I am currently working on a simple loading screen for my libgdx game however, it has a problem. The loading screen works flawlessly on the android project but when it comes to the desktop version it is not working. Currently the loading screen should show "LOADING" -> "LOADING." -> "LOADING.." -> "LOADING..." , with 1 second intervals and then repeat. When I load the game on the desktop application, it shows "LOADING" (and it is flashing like crazy) and then proceeds with the periods like it is supposed to. The whole time everything is flashing like the refresh rate is to high or something, and it shows phantom periods in the background when they are not supposed to be there. Can someone tell me when this is happening on the desktop version and not the Android version? Also here is the code from the two functions of my "LoadingScreen implements Screen"
The render(float delta) function:
float updateTextTime = 0;
#Override
public void render(float delta) {
if (updateTextTime >= 1){ //check to see if it has been a second
if (periods == 0){
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); //clears the buffer bit
}
textBatch.begin();
font.draw(textBatch,loading.substring(0,7+periods),(Gdx.graphics.getWidth()/2)-(textwidth/2),(Gdx.graphics.getHeight()/2)+(textheight/2));
if (periods < 3){
periods += 1;
}else{
periods = 0;
}
textBatch.end();
updateTextTime = 0;
}else{
updateTextTime += delta; //accumlate
}
}
and the show() function:
#Override
public void show() {
textBatch = new SpriteBatch();
font = new BitmapFont(Gdx.files.internal("fonts/ArmyChalk.fnt"), Gdx.files.internal("fonts/ArmyChalk.png"),false);
if (game.AppTypeMine == ApplicationType.Android){
font.scale(2.0f);
}
textwidth = font.getBounds(loading).width;
textheight = font.getBounds(loading).height;
}
FINAL SOLUTION:
Changed render() function:
public void render(float delta) {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
textBatch.begin();
font.draw(textBatch,loading.substring(0,7+periods),(Gdx.graphics.getWidth()/2)-(textwidth/2),(Gdx.graphics.getHeight()/2)+(textheight/2));
textBatch.end();
if (updateTextTime >= 0.5){ //check to see if it has been a second
if (periods < 3){
periods += 1;
}else{
periods = 0;
}
updateTextTime = 0;
}else{
updateTextTime += delta; //accumlate
}
}
You should always render something in the render callback.
You are probably seeing double-buffering problems (OpenGL cannot tell that you didn't draw anything into the current framebuffer, so it ends up rendering whatever was left in there).
Change your code so that you always invoke the font.draw call. I think you can just move the textBatch.begin(), font.draw(), and textBatch.end() calls outside the initial if/else block.
If you don't want render to be invoked quite so often, you can switch Libgdx to a non-continuous rendering mode (but you'll need to find a hook to kick the render calls back to life every second or so). See http://bitiotic.com/blog/2012/10/01/enabling-non-continuous-rendering-in-libgdx/.
I have a Canvas on which I am drawing lines bewteen points in a onTouchEvent(). When the user makes an invalid selection on where to place the line, I draw a red line instead of a black one. However I want the red line to fade away over the next 500-1000ms.
I haven't been able to figure out how to do it, and if it is even possible to animate a single line inside a Canvas.
I have split the dots, the valid black lines and the invalid red line(s) between 3 different bitmaps inside my:
public void onDraw(Canvas canvas) {
// Fade Red Line
canvas.drawBitmap(fBitmap, 0, 0, null);
// Lines
canvas.drawBitmap(mBitmap, 0, 0, null);
// Dots
canvas.drawBitmap(iBitmap, 0, 0, null);
}
and inside my onTouch function are my drawLines().
I was wondering if I can use an AlphaAnimation for this, or if I have to use a timer system to loop over the fBitmap with the red line I want to fade out over time.
Any Ideas?
EDIT: Okay here's how I eventually solved it after coming back to it a year later:
I implemented a SurfaceView and created a thread to continuously update the Canvas.
#Override
public void run() {
while (isRunning) {
if (!surfaceHolder.getSurface().isValid()) {
continue;
}
decreaseLineOpacity();
drawLinesInvalid();
/* Other things to draw*/
Canvas canvas = surfaceHolder.lockCanvas();
canvas.drawBitmap(fBitmap, centerPointWidth, centerPointHeight, null);
// mBitmap is the middleground with the line that fades away.
// and is bound to mCanvas for drawing
canvas.drawBitmap(mBitmap, centerPointWidth, centerPointHeight, null);
canvas.drawBitmap(iBitmap, centerPointWidth, centerPointHeight, null);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
And then the methods that take care of changing the opacity and drawing the line, I store each line I draw in a Queue, until it reaches an opacity of 0, and then I remove it so only the lines that need to be animated are left:
private void drawLinesInvalid() {
Iterator<int[]> iterator = invalidLines.iterator();
// Loop through each element in the Queue, delete it from the bitmap with the porter
// duff mode to keep the transparancy of the layer. Then draw line with the given opacity
while (iterator.hasNext()) {
int[] invalidLine = iterator.next();
float[] line = {invalidLine[1], invalidLine[2], invalidLine[3], invalidLine[4]};
Xfermode originalXfermode = paint.getXfermode();
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mCanvas.drawLines(line, paint);
paint.setXfermode(originalXfermode);
paint.setColor(Color.RED);
paint.setAlpha(invalidLine[0]);
mCanvas.drawLines(line, paint);
}
}
// Call this function in the onTouch method
private void addLineToInvalidList(float[] line) {
// Store each line a queue with its current opacity
int[] lineWithOpacity = new int[5];
lineWithOpacity[0] = 250;
lineWithOpacity[1] = (int) line[0];
lineWithOpacity[2] = (int) line[1];
lineWithOpacity[3] = (int) line[2];
lineWithOpacity[4] = (int) line[3];
invalidLines.add(lineWithOpacity);
}
private void decreaseLineOpacity() {
Iterator<int[]> iterator = invalidLines.iterator();
// Remove all lines that are finished animating (opacity = 0)
while (iterator.hasNext()) {
int[] line = iterator.next();
if (line[0] == 0) {
iterator.remove();
}
}
// Decrease opacity on lines that are left
iterator = invalidLines.iterator();
while (iterator.hasNext()) {
int[] line = iterator.next();
line[0] -= 10;
}
}
Considering you are drawing all three bitmaps on the same view, using an AlphaAnimation may be a bit tricky.
I would suggest spawning a thread that changes the alpha value of the invalid red line(s) bitmap over time (sleeping for set intervals with Thread.sleep). However, for this to perform well you should look into using a SurfaceView as calling postInvalidate multiple times may be quite costly (unless this is running on Android 3.0+ with hardware acceleration).
I have built a custom component that shows only a line. The line is drawn from the top left corner to the bottom right corner as a Line2D at the paint method. The background is transparent. I extended JComponent. These line components are draggable and change their line color when the mouse pointer is located max. 15 pixels away from the drawn line.
But if I have multiple of these components added to another custom component that extends JPanel they sometimes overlap. I want to implement that if the mouse pointer is more than 15 pixels away from the line the mouse events should fall through the component. How to let it fall through is my problem.
Is that even possible?
Thanks in advance!
I want to implement that if the mouse pointer is more than 15 pixels
away from the line the mouse events should fall through the component.
If your child component has a mouse listener, then it will intercept every mouse event occurring over it. If you want to forward the MouseEvent to the parent Component you should manually do it. For example you can implement your custom mouse listener extending MouseAdapter:
public class yourMouseListener extends MouseAdapter{
//this will be called when mouse is pressed on the component
public void mousePressed(MouseEvent me) {
if (/*do your controls to decide if you want to propagate the event*/){
Component child = me.getComponent();
Component parent = child.getParent();
//transform the mouse coordinate to be relative to the parent component:
int deltax = child.getX() + me.getX();
int deltay = child.getY() + me.getY();
//build new mouse event:
MouseEvent parentMouseEvent =new MouseEvent(parent, MouseEvent.MOUSE_PRESSED, me.getWhen(), me.getModifiers(),deltax, deltay, me.getClickCount(), false)
//dispatch it to the parent component
parent.dispatchEvent( parentMouseEvent);
}
}
}
For my final year project at university I did a whiteboard program and had the same problem. For each shape the user drew on the board I created a JComponent, which was fine when they were drawing rectangles, but more difficult with the free form line tool.
The way I fixed it in the end was to do away with JComponents altogether. I had a JPanel which held a Vector (I think) of custom Shape objects. Each object held its own coordinates and line thicknesses and such. When the user clicked on the board, the mouse listener on the JPanel fired and went through each Shape calling a contains(int x, int y) method on each one (x and y being the coordinates of the event). Because the Shapes were added to the Vector as they were drawn I knew that the last one to return true was the topmost Shape.
This is what I used for a straight line contains method. The maths might be a bit iffy but it worked for me.
public boolean contains(int x, int y) {
// Check if line is a point
if(posX == endX && posY == endY){
if(Math.abs(posY - y) <= lineThickness / 2 && Math.abs(posX - x) <= lineThickness / 2)
return true;
else
return false;
}
int x1, x2, y1, y2;
if(posX < endX){
x1 = posX;
y1 = posY;
x2 = endX;
y2 = endY;
}
else{
x1 = endX;
y1 = endY;
x2 = posX;
y2 = posY;
}
/**** USING MATRIX TRANSFORMATIONS ****/
double r_numerator = (x-x1)*(x2-x1) + (y-y1)*(y2-y1);
double r_denomenator = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
double r = r_numerator / r_denomenator;
// s is the position of the perpendicular projection of the point along
// the line: s < 0 = point is left of the line; s > 0 = point is right of
// the line; s = 0 = the point is along the line
double s = ((y1-y)*(x2-x1)-(x1-x)*(y2-y1) ) / r_denomenator;
double distance = Math.abs(s)*Math.sqrt(r_denomenator);
// Point is along the length of the line
if ( (r >= 0) && (r <= 1) )
{
if(Math.abs(distance) <= lineThickness / 2){
return true;
}
else
return false;
}
// else point is at one end of the line
else{
double dist1 = (x-x1)*(x-x1) + (y-y1)*(y-y1); // distance to start of line
double dist2 = (x-x2)*(x-x2) + (y-y2)*(y-y2); // distance to end of line
if (dist1 < dist2){
distance = Math.sqrt(dist1);
}
else{
distance = Math.sqrt(dist2);
}
if(distance <= lineThickness / 2){
return true;
}
else
return false;
}
/**** END USING MATRIX TRANSFORMATIONS****/
}
posX and posY make up the coordinates of the start of the line and endX and endY are, yep, the end of the line. This returned true if the click is within lineThickness/2 of the centre of the line, otherwise you have to click right along the very middle of the line.
Drawing the Shapes was a case of passing in the JPanel's Graphics object to each Shape and doing the drawing with that.
It's been a while since I touched Swing, but I think you will need to handle your mouse events in the parent component and then loop through the child components with lines and determine which one of them should handle the event (well, the logic of deciding should still remain in the line component, but parent will explicitly invoke that logic until one of the components takes the event).
I believe that the easiest way is to catch the event and call parent.processEvent(). So, you component will be transparent for events because it will propagate them to parent.
I was struggling with this sort of question and tried all the stuff with parents and glasspane until i realised that override of contains method does just what you want. Because when parent fires some sort of getcomponent your 'Line' will reply to it: 'no, its not me, i'm not there!' and the loop will check other components.
Also, when you need to setup a complex depth to your draggable object, you can use JLayeredPane descendant.