Android Add Delay in onDraw method - android

I am new to android and building an app which involves displaying a view for 2 seconds and then change. Here's my onDraw method:
#Override
public void onDraw(Canvas canvas)
{
float level = game.level;
width = getWidth();
tile_length = width/level;
Paint rect = new Paint();
rect.setColor(getResources().getColor(R.color.dark));
canvas.drawRect(0, 0, width, width, rect);
game.numbers.setTextSize( (0.70f * tile_length));
game.numbers.setTextAlign(Paint.Align.CENTER);
grid.setColor(getResources().getColor(R.color.lines));
rect.setColor(getResources().getColor(R.color.tile_on));
int ind = 1;
int tile_num = 1;
FontMetrics fm = game.numbers.getFontMetrics();
float x = tile_length/2;
float y = tile_length/2 - (fm.ascent + fm.descent) / 2;
Log.v(LOG_TAG, "changed = " + game.changed);
for (int i=0; i<width; i+=tile_length)
{
for(int j=0; j<width; j+=tile_length)
{
for(int k = 0; k<level; k++ )
if(tile_num == game.random[k])
{
// Log.v(LOG_TAG, "i = " + i + "j = " + j);
game.set_Coordinates(ind-1, i, j);
String tile = Integer.toString(ind++);
canvas.drawRect(i, j, i+tile_length, j+tile_length, rect);
canvas.drawText(tile, i+x, j+y, game.numbers); //needs to be updated after 2 seconds
break;
}
tile_num++;
}
}
}
I understand i have to use postdelayed method somewhere, but don't know how...Now i just want to ommit the canvas.drawText line after the delay.

do you mean something like this
new Handler().postDelayed(new Runnable(){
public void run(){
// do something here like draw text;
}
}, 2000);

A timer is needed, indeed. What I do, which is very simple, is first to create records of coordinates (and any other data needed) for every point of the drawing -- instead of drawing the points on the spot -- and then reproduce them using a timer (Android handler, preferably, like the one suggested above). This also offers you a lot of possibilities while actual drawing: pause, go faster/slower, go backwards, ...
I don't know if this method can be used for complicated drawings, but it is fine for drawing shapes, curves, surfaces, etc.

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.

When I try to create animated objects from sprite on Android, they start to be slow

I use followed example (described here) to animate my sprite sheet.
from example I wrote my Object:
public class Sprite {
private int x, y;
private int width, height;
private Bitmap b;
MainGamePanel ov;
int currentFrame = 0;
public Sprite(MainGamePanel mainGamePanel, Bitmap blob) {
this.ov = mainGamePanel;
this.b = blob;
// 1x12
height = b.getHeight();
width = b.getWidth()/12;
x = y = 50;
}
private void update(int dist) {
currentFrame = ++currentFrame % 12;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//return _b;
}
public void draw(int shift, Canvas canvas, int dist) {
update(dist);
int srcX = currentFrame * width;
Rect src = new Rect(srcX, 0, srcX+width, height);
Rect dst = new Rect(x, y+shift, x+width, y+height+shift);
canvas.drawBitmap(b, src, dst, null);
}
Here every 100 msec I take different part from image (Bitmap) and show it.
1 -> 2 -> 3 -> 4 -> ... -> 12
So my creature flies and moves with wings.
If I show only 1 object, it seems good but when I try to run 20 creatures in loop:
Bitmap blob = BitmapFactory.decodeResource(getResources(), R.drawable.sprite3);
for(int i=0; i<20; i++){
spriteList.add(new Sprite(this, blob));
}
....
for(int i=0; i<spriteList.size(); i++){
sprite = spriteList.get(0);
sprite.draw(canvas, dist);
}
My objects start to be slow according to drawn object count.
It happens I think because of Thread.sleep(100);.
I don't see any performance problem.
Sounds like each object sleep pauses all objects.
For 20 objects this sleep grows to 2 sec.
For sure I use workaround like:
int sleep = 100/spriteList.size();
Thread.sleep(sleep);
But code looks messy.
Can anyone help me how to fix it?
Here is sprite3 image:
[EDIT]
Maybe I need create each object in separate Thread?
You should definitely not sleep while rendering; it greatly reduces the performance, especially, as you add new Animation Clips, etc. Also, don't create objects within your onDraw method and do try to reuse the Rect objects. Creating objects during rendering is very expensive.

Drawing to the canvas on Android - screen flickering/tearing

I have an isometric map which I draw to the canvas. When I try and move the map around, these black lines flicker in between some of the tiles making the whole thing look rather shoddy.
Here is the relevant code from my updating thread
public void run() {
Canvas c;
while (isRunning){
c = null;
try {
c = cellMap.getHolder().lockCanvas(null);
synchronized (cellMap.getHolder()) {
cellMap.onDraw(c);
}
} finally {
if( c != null){
cellMap.getHolder().unlockCanvasAndPost(c);
}
}
}
}
and from my CellMap class (which extends SurfaceView):
public void onDraw(Canvas canvas){
canvas.drawColor(Color.BLACK);
int x = 0;
int y = 0;
for(int i = 0; i < mapSize; i++){
for(int j = 0; j < mapSize; j++){
x = (i-j) * 30 + xOffset;
y = (i+j) * 15 + yOffset;
mapCells[i][j].draw(canvas, paint, x, y);
}
}
mapCells[][] is an array of objects which contain the Bitmap image that needs to be drawn. The draw() function is only 1 line canvas.drawBitmap(bitmapImage, x, y, null)
I have discovered that removing the line Canvas.draw(Color.black) gets rid of the flicker. However, when I move the map the canvas is not cleared so I still see the image from the previous frames. I'd imagine it is because it is taking too long to draw the bitmaps? but the map is only very small at the moment.
I found the problem. When moving the map, the offset values were being changed. This lead to sections of the map being drawn temporarily with different offset values - creating the tearing. duh!
I solved this by copying the xOffset and yOffset values at the start of the onDraw() method, and using those values for the update.

Animation at a specified rate using canvas / Ondraw

In my Android app, I am trying to show letters one by one with a short delay between each, while also playing a sound for each letter. I have everything working, and the sounds play with the correct delay, but the text always prints to the screen far too fast. The canvas seems to be updated even when i am not specifically invalidating the view.
Here is what I have so far - I also tried a variant of this based on the "snake" example and had the same results... any help would be appreciated!
public class SpellingView extends View {
private static final String WORD = "TRUCK";
int width;
int height;
String textToPrint;
float textspace;
int j=0;
private final Path arc;
private final Paint tPaint;
//constructor for SpellingView
public SpellingView(Context context) {
super(context);
arc = new Path();
tPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
displayLetterLoop();
}
public void displayLetterLoop(){
for (int i = 0; i < WORD.length(); i++){
final Runnable mUpdateUITimerTask = new Runnable() {
public void run() {
Spelling.mp.start();
}
};
final Handler mHandler = new Handler();
mHandler.postDelayed(mUpdateUITimerTask, i*1500);
}
}
#Override
protected void onDraw(Canvas canvas) {
int k;
// Drawing commands go here
width = canvas.getWidth();
height = canvas.getHeight();
arc.addArc(new RectF((width*.15f), (height*.15f), (width*.85f), (height*.4f)), 180,180);
tPaint.setStyle(Paint.Style.FILL_AND_STROKE);
tPaint.setColor(Color.RED);
tPaint.setTextSize(height * 0.1f);
tPaint.setTextAlign(Paint.Align.LEFT);
setBackgroundColor(Color.BLACK);
for (k = 0; k < j; k++){
char c = WORD.charAt(k);
String cs = Character.toString(c);
textToPrint+= cs;
textspace =(float) (k*(width/WORD.length())*.9);
canvas.drawTextOnPath(cs, arc, textspace , 0, tPaint);
}
if(j<WORD.length()){
j++;
}
}
}
Custom view will invalidate itself when is a part of a layout which for some reason redraw itself. Therefore you could envelop your code in onDraw() with a condition and a flag so that it draws your stuff only when the timer sets the flag and calls invalidate. After one letter is drawn then the flag shoud be set on false like:
if (drawLetter){
drawLetter = false;
/code...
}
However this also may need to be a sychronized block.
OnDraw should happen 60 times a second and not only when you are invalidating.
So maybe you need to update some class variables (when you are invalidating) and use those for your draw logic # OnDraw.

animate number change

i want to build a component that will be able to show a integer number max 5 digits in the style of the old analog car counters and animate the digit change.
that looks something like this maybe...
i have tried searching for this kinda of examples but i couldn't find anything so far.
in your opinion what is the best approach to achieve this?
i looked at the iphone alarm time picker and as far as i can tell there is only a fixed background and they push the numbers in or out the view. but how do i place the digits in this case and reference them to a particular value?
tnx.
You can try to create your own view, extending view and overriding onDraw().
Here you can use rows of numbers in bitmaps and editing their position based on the number you wish to show.
Dont forget to call invalidate() after setting new numbers to redraw the view.
I will paste an example containing a start for your project.
The bitmap number is a vertical image with numbers from 1-9 (and 0&.)
Ex.
class TickerView extends View { ..
public void setDouble(double d) {
value = d;
invalidate();
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int startx = 0;
int starty = 0;
DecimalFormat df = new DecimalFormat("#0.00");
String str = df.format(value);
String original = Double.toString(value);
Bitmap nums = BitmapFactory.decodeResource(context.getResources(),
R.drawable.numbers);
for (int i = 0; i < str.length(); i++) {
int num = 0;
try {
num = Integer.parseInt(str.charAt(i) + "");
} catch (Exception e) {
num = 10;
}
int numbefore = 0;
try {
numbefore = Integer.parseInt(original.charAt(i -1) + "");
} catch (Exception e) {
numbefore = 0;
}
canvas.drawBitmap(nums, startx + (i * 40), (starty + 40)
- (num * 50) + (numbefore), paintY);
}
paintY.setStrokeWidth(10);
canvas.drawLine(startx, starty+36, startx + (str.length() * 40), starty+36,
paintY);
canvas.drawLine(startx, starty + 90, startx + (str.length() * 40),
starty + 90, paintY);
invalidate();
}
}

Categories

Resources