draw rectangle canvas android - android

I am new to android programming and have run into a small problem. If you could help, that would be greatly appreciated.
I am making rectangles for a live wallpaper I am making using:
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
Paint paint = new Paint();
paint.setColor(Color.CYAN);
Paint ypaint = new Paint();
ypaint.setColor(Color.RED);
Paint ppaint = new Paint();
ppaint.setColor(Color.GREEN);
drawVerStripes(c, ppaint, 0,20);
drawVerStripes(c, paint, 50,20);
drawVerStripes(c, ypaint,0,10);
}
} finally {
if (c != null)
holder.unlockCanvasAndPost(c);
}
The function:
Params:
width: width of the rectangle
space: the difference between last rect and this rect
int y=0;
int oldY=0;
private void drawVerStripes(Canvas c, Paint paint, int space, int width) {
y=oldY+space;
c.drawRect(y, 0, y+width,c.getHeight(), paint);
oldY=y;
}
The result is the rectangles moving to one side of screen really fast. I want them to stay on screen and not move.
In other words, is there a way that drawVerStripes is executed only once and not every frame.

Your drawFrame method is called with each frame. If your devices refresh rate is 70, then your method is called 70 times a second. If you increase x or y coordinate by 1, then you move 70 pixels in either x or y coordinates. You should do refresh rate independent movement.
1) Calculate delta time which is the time that passed since the last screen update.
2) Determine how many pixels you want your rectangle to move every second.
Let's say you want you rectangle to move 50 pixels in x.
Then do the following. x += deltaTime * 50.
long startTime = System.nanoTime(); // Execute before the first time your drawFrame executes
float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; // In drawFrame method calculate
long startTime = System.nanoTime(); // Execute after calculating delta time.

I believe that you need to set y and oldY to 0 again at the beginning of drawFrame.

Related

AlertDialog with TextView - can't use textView.setText method

I'm trying to draw a parabola with delay, using custom view. So far I've learned that I need to use #Override onDraw method, but 1. I can't make my parabola discrete and 2. I don't know how to program it so the shape is created step-by-step (with delay).
I also need to draw it after click of a button, so that is another complication for me. Right now I'm trying to draw a simple line step-by-step but this snippet don't work:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path, paint);
int x1 = 10;
int x2 = 100;
int y1 = 10;
int y2 = 100;
int diff = x2-x1;
for (int i = 0; i<diff; i++){
canvas.drawLine(x1, y1, x1+1, y1+1, paint);
x1++;
y1++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
I can give you some tricks but please finish it your self.
You have to use a timer to refresh your component, if use this, it will refresh "onDraw" each 100ms.
private Handler handler = new Handler();
private Runnable AlarmRunnable = new Runnable() {
#Override
public void run() {
handler.postDelayed(this, 100);
invalidate();
}
};
define global variables instead of local.
int cc = 0;
int x1 = 10;
int x2 = 100;
int y1 = 10;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (cc++ > 100) // STOP HANDLER AFTER COUNTER GETS DONE
handler.removeCallbacks(AlarmRunnable);
System.out.println("CC:" + cc);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(lineWidth);
paint.setColor(Color.WHITE);
for (int i = 0; i<cc; i++){
canvas.drawLine(x1, y1, x1 + 1, y1 + 1, paint);
x1++;
y1++;
}
}
start handeler in Constructor
public YOURCOMPONENT(Context context, AttributeSet attrs) {
super(context, attrs);
handler.post(AlarmRunnable);
....
}
Rendering a Discrete Parabola
As for drawing a discrete parabola, you should draw points (or circles with a radius size of your choice, but centered at the points) along the different x and y coordinates with a larger step size.
For example, you can draw a parabola from x=-1 to x=1 with a step size of 1 by drawing at the following (x, y) points: (-1, 0), (0, 4), (1, 0).
You should make sure that the way you scale your x-axis on your graph, is in a way that there is greater than 1 pixel distance between the points to make it look discrete.
Animated onDraw
Regardless of whether your drawing logic within onDraw is correct or not, you are running a long operation with Thread.sleep() on a UI callback, which is bad practice.
Since you are drawing the whole parabola within one call of onDraw, I would assume the whole image is rendered at once rather than animated.
Looking at a similar question, you should create another thread that is in charge of a rendering loop for your custom view, to create an animation where you draw each frame.

How to properly sidescroll a screen in android, using canvas?

Ok, I am developing a sidescrolling game and my problem is on how to properly draw and update the screen. I am drawing on a SurfaceView and I use Path to make the contourns, currently the algorithm only draws this:
And I am sidescrolling by using Path.offSet() and then canvas.drawPath(), later on I update the last X position on the path by using Path.addRect() (and thats basically how I am drawing everything: using Path.addRect())
So here is the thread that updates the screen:
#Override
public void run() {
int x = LibraryLoader.getTerrainSizeX();
int y = LibraryLoader.getTerrainSizeY();
int count = 0;
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
Path path = new Path();
makePath(path, x, y, 0, LibraryLoader.getTerrainThickness());
Path path2 = new Path();
makePath(path2, x, y, LibraryLoader.getTerrainThickness(), y);
while (run) {
Canvas c = null;
try {
c = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
fps = fps();
drawMyData(c, path, path2, paint, fps);
LibraryLoader.updateOffSet();
updatePaths(path, path2, x, y);
if ((count++) == (x / 2) - 1) {
LibraryLoader.updateOffSetArray();
count = 0;
}
}
} finally {
if (c != null) {surfaceHolder.unlockCanvasAndPost(c);}
}
}
and its respective methods:
public void updatePaths(Path path, Path path2, int x, int y) {
path.offset(-1f, 0);
path.addRect(x-3, topValue, x-2, bottomValue, Path.Direction.CW);
path2.offset(-1f, 0);
path2.addRect(x-3, topValue, x-2, y, Path.Direction.CW);
}
So, in my phone it works perfectly at 60fps, the problem is I tested in a lower end device and it begins at 40fps then drops every update until it gets below 10fps...(and keeps dropping). I guess I need to clean the state of the path, or I shouldn't even be using the Path class to begin with. So my question is how should I update the screen with the best performance? Obs: The canvas is not hardware accelerated.
Well folks I figured out that I was wrong about everything I did. The answer is simple: If your android application updates the whole screen every frame, use Opengl. Canvas is for app design for what I've seen, hope I am not mistaken. For example, if you want to make a custom animation for a LOGO or a button, so you use canvas, I guess. If anyone stumbles in this post do watch the videos Morrison Chang mentioned, they are very helpful to put you on the right track. Cheers.

How to avoid ghost drawing after clearing SurfaceView canvas

Hi I am working on plotting a real time graph of incoming signals using SurfaceView.
The sampling rate is 128Hz and the target graph refresh rate is 50Zh.
Things run pretty smoothly, the points are drawn real-time properly.
I plot the data in segments of a few points using Path()
for each segment I call path.computeBounds() to get a rect that I will use to call holder.lockCanvas(rect) and draw the path. Using a rect prevents flickering and reduces cpu usage
when the graph reaches the end I lock the entire canvas and clear the background, draw the graph frame and then continue on plotting.
the problem is that at the beginning of each new "page" I get a ghost image from the last page:
I believe this is caused by double buffering / use of a dirty area when plotting.
I have looked for solutions to this problem but none seem adequate for this type of application. Any help is most welcome.
Thanks
Jean-Pierre
Code follows:
private void draw() {
Point point = null;
Canvas canvas = null;
Path path = new Path();
ArrayList<Point> pointArray;
float oldX = -1;
boolean setToClear = false;
boolean isNewSegment = false;
if (samplesInQueue == 0) {
return;
}
pointArray = new ArrayList<Point>((int) samplesInQueue);
for (int i = 0; i < samplesInQueue; i++) {
// take a peek at the point without retrieving it from the point
// queue
point = Points.peek();
// check if first point of segment is the start of a page
if (i == 0) {
if (lastSegmentEndPoint != null) {
if (point.x < lastSegmentEndPoint.x) {
// yes then we will need to clear the screen now
isNewSegment = true;
}
} else {
// yes then we will need to clear the screen now
isNewSegment = true;
}
}
if (point != null) {
if (point.x > oldX) {
// put consecutive points in the path point array
point = Points.poll();
samplesInQueue--;
pointArray.add(point);
oldX = point.x;
} else {
// we have a wrap around, stop and indicate we need to clear
// the screen on the next pass
if (!isNewSegment) {
setToClear = true;
}
break;
}
}
}
// no points, return
if (pointArray.size() == 0) {
return;
}
// fill the path
for (int i = 0; i < pointArray.size(); i++) {
Point p = pointArray.get(i);
if (i == 0) {
if (lastSegmentEndPoint != null) {
if (p.x >= lastSegmentEndPoint.x) {
// if we have the end of the last segment, move to it
// and line to the new point
path.moveTo(lastSegmentEndPoint.x, lastSegmentEndPoint.y);
path.lineTo(p.x, p.y);
} else {
// otherwise just line to the new point
path.moveTo(p.x, p.y);
}
} else {
path.moveTo(p.x, p.y);
}
} else {
path.lineTo(p.x, p.y);
}
}
if (clear || isNewSegment) {
if (clear) {
clear = false;
}
// we need to clear, lock the whole canvas
canvas = holder.lockCanvas();
// draw the graph frame / scales
drawGraphFrame = true;
drawGraphFrame(canvas);
} else {
// just draw the path
RectF bounds = new RectF();
Rect dirty = new Rect();
// calculate path bounds
path.computeBounds(bounds, true);
int extra = 0;
dirty.left = (int) java.lang.Math.floor(bounds.left - extra);
dirty.top = (int) java.lang.Math.floor(bounds.top - extra);
dirty.right = (int) java.lang.Math.round(bounds.right + 0.5);
dirty.bottom = (int) java.lang.Math.round(bounds.bottom + 0.5);
// just lock what is needed to plot the path
canvas = holder.lockCanvas(dirty);
}
// draw the path
canvas.drawPath(path, linePaint);
// unlock the canvas
holder.unlockCanvasAndPost(canvas);
// remember last segment end point
lastSegmentEndPoint = pointArray.get(pointArray.size() - 1);
// set clear flag for next pass
if (setToClear) {
clear = true;
}
}
Draw frame / clear graph code
private void drawGraphFrame(Canvas canvas) {
if (!drawGraphFrame) {
return;
}
if (canvas == null) {
Log.e(TAG, "trying to draw on a null canvas");
return;
}
drawGraphFrame = false;
// clear the graph
canvas.drawColor(Color.BLACK, Mode.CLEAR);
// draw the graph frame
canvas.drawLine(leftMargin, topMargin, leftMargin, mCanvasHeight - bottomMargin, framePaint);
canvas.drawLine(leftMargin, mCanvasHeight - bottomMargin, mCanvasWidth - rightMargin, mCanvasHeight
- bottomMargin, framePaint);
// more drawing
}
Your problem is quite straight forward.. your only locking the new portion of the canvas that the new path covers. So the best thing to do is to make your path and dirty rect's private members of your class. Then at the start of your draw method get the path's current bounds (the old bounds) in your dirty rect. Now call path.rewind(); and start modifying your path. After do a union on the dirty rect with the new bounds. Now your dirty rect covers the old and new rect's. So your clear will remove the old path. This also reduces overhead because you don't want to be allocating 100+ objects per second for rect's and path's. Now since your drawing an oscilloscope then you probably want to adjust the old bounds to only be a portion of the width of the view. The same amount your new portion covers.
Hope that's cleared things up.
My simple answer is just using this function clear_holder() wherever you want to clear the canvas. I copy and paste 3 line for 3 times because it need 3 times clear to leave holder blank.
After clearing holder, you should draw any new thing you want!
This link give me this source code!
private void clear_holder(SurfaceHolder holder){
Canvas c = holder.lockCanvas();
c.drawColor( 0, PorterDuff.Mode.CLEAR );
holder.unlockCanvasAndPost(c);
c = holder.lockCanvas();
c.drawColor( 0, PorterDuff.Mode.CLEAR );
holder.unlockCanvasAndPost(c);
c = holder.lockCanvas();
c.drawColor( 0, PorterDuff.Mode.CLEAR );
holder.unlockCanvasAndPost(c);
}
It looks like you are clearing the canvas so, it's not double buffering problem. I think it's related to your path been reused.
Try adding adding the next line when starting new page.
path.reset();

Android: onDraw method using DrawPoint() causing jitter and distortion on canvas

I am developing an app which draws more or less a two-dimensional matrix of values to a canvas. The values of this matrix are scaled to Alpha levels to illustrate intensity, and the coordinates for the matrix are simply extrapolated from row and column indexes. Below is my onDraw routine.
public void onDraw(Canvas canvas, float [][] spectrum, float nsegs,int seglen) {
//canvas.translate(0,0);
//alpha = 0;
int canHeight = canvas.getHeight();
int canWidth = canvas.getWidth();
//float[] array = generateData(512);
float [] spec = new float[seglen];
final float bw = (float)(canWidth-2)/nsegs;
final float bh = (float)(canHeight-2)/(float) seglen;
for (int i = 0;i<seglen;i++){
spec[i] = spectrum[i][index]; // One column at a time
}
float max = maxVal(spec);
float min = minVal(spec);
xcoor = index;
for (int n = 0; n < seglen; n++){
//Scale value to alpha (0-255)
alpha =(int)Math.round((((spec[n] - min)/max)*255.0));
ycoor = n;
paint.setAlpha(alpha);
canvas.drawPoint(xcoor,ycoor, paint);
}
index = (int) (index +1);
if (index == nsegs-1){
index = 0;
}
}
Here paint configuration is pre-defined as:
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(2);
This program draws one pixel at a time, fills one column of pixels equal to the number of elements in a column of the matrix. Then it starts on the next column, where the previous column is still displayed and so forth. At this stage when all columns are full it stars from the first column again, drawing on top of previous elements.
The Problem: The tailing columns although already drawn appear to flicker and jump around, as does the Alpha. I have attempted to canvas.save() and canvas.restore() to capture the entire canvas and restore it after a column is printed. I have double checked all my row and column indexing and alpha vales to ensure the coordinates increment as per desired (and they do). This is very similar to the sample APIdemo DrawPoints.java, however there are three primary differences.
I am using DrawPoint not DrawPoints, and
I don't use "canvas.setColour" as it removes the tailing columns from the canvas.
This onDraw function is operating in a Thread which extends SurfaceView
Any idea's would be much appreciated, thank you for your time.
In the case above, I was using a SurfaceView instead of a View. Out of the Android dev docs
Note: On each pass you retrieve the Canvas from the SurfaceHolder, the previous state of the Canvas will be retained. In order to properly animate your graphics, you must re-paint the entire surface. For example, you can clear the previous state of the Canvas by filling in a color with drawColor() or setting a background image with drawBitmap(). Otherwise, you will see traces of the drawings you previously performed.
The Solution, re-draw the entire canvas each time to prevent the jitter.

Drawing random circles

I am trying to draw a cupola circles at random positions in an Android application.
I draw them on a bitmap and then draw that bitmap on the canvas. This is the function where a draw the circles:
private void drawRandomCircles(int numOfCircles) {
Canvas c = new Canvas(b);
Paint cPaint = new Paint;
cPaitn.setColor(Color.RED);
for(int i = 0; i < numOfCircles; i++) {
int x = Math.Random % 100;
int y = Math.Random % 100;
c.drawCircle(x, y, 20, cPaint)
}
}
The Bitmap b is global.
And after calling this function I just draw the bitmap in the onDraw method.
Now the problem is that I only get one circle drawn on the screen, no matter the size of numOfCircles.
Any clue what is happening here?
That code doesn't even compile. What is new Paint; for instance?
I suggest you log your arguments to drawCircle to make sure you draw them on different locations. If Math.Random for instance is a field, it would change in between reads, which would put the circles on top of each other.
If you intended to write Math.random() the error is that Math.random() returns a value between 0 and 1. You may want to use
Random r = new Random();
// your loop
int x = r.nextInt(100);
int y = r.nextInt(100);

Categories

Resources