I'm trying to move a ball on canvas. a and b are similar to x,y coordinate positions. Any way from my code im trying to get different values dynamically. The a,b are global variables. But it seems that "invalidate()" or the refreshing of screen only happens afer end of the whole loop. Do you know why?. And if i have to build this on another thread please suggest me with some simple codes.
private void shootBall(){
while (a>b){
a = getPositionX();
b = getPositionY();
invalidate();
}
}
}
I think it's more correct to say that you can call invalidate() from within a loop, but that that invalidation will not be handled (the canvas won't be redrawn) until after your loop is complete. The problem is that you are calling invalidate on the same thread (the UI toolkit thread) as the one that would call your onDraw() method. So unless/until you hand control back to the toolkit, it cannot possibly do the rendering. So your invalidate() call does actually invalidate the view ... but the view won't be redrawn until after your loop completes and your function returns.
It is more correct to change the position in some function that is called via some timer (which is essentially what the animation classes do). In that function, you would change the values, invalidate() appropriately, and return. Then the toolkit re-renders the scene and your function will get future callbacks and update the position accordingly.
do it like this, and use postInvalidate() instead:
private void shootBall(){
new Thread(new Runnable() {
public void run() {
while (a>b){
a = getPositionX();
b = getPositionY();
postInvalidate();
}
}
}).start();
}
edit: but as mentioned before, don't assume that the invalidate redraws the screen, it marks it as to-be-redrawn and the UI thread will get around to it.
You can put invalidate() at the end of onDraw() like in this example: How can I use the animation framework inside the canvas?
However this works well on some devices while bottlenecks and slows down on other.
To use a thread and SurfaceView go through all of these tutorials: http://www.droidnova.com/playing-with-graphics-in-android-part-i,147.html
UI cant be modified form any new thread..you should use invalidate() in the same thread where your view
Related
I'm having an issue with an android application.
I have a thread that constantly iterators over a list or shapes, updates their positions, and sometimes removes an item from the list. At the end of the thread's while loop it calls postInvalidate() to prompt a repaint.
Here is the code where modification to the ArrayList happens.
Iterator<Shape> iterator = this.myList.iterator();
while (iterator.hasNext()) {
Shape nextShape = iterator.next();
nextShape.move();
if(condition) {
iterator.remove()
}
}
The onDraw method uses a for each loop to draw each of those items. I am getting concurrent modification in the onDraw method despite only modifying the list through the iterator. I've tried CopyOnWriteArrayList and Collections.synchronized with the same results.
Any help would be appreciated.
There's a couple of ways to solve this. One would be to use a synchronized block in the thread and in onDraw, but that would prevent the 2nd thread from running at the same time as the UI thread.
I think the best way is for you to use two collections. Your code should look like this
onDraw(){
synchronized(myList){
//Do your draw code. Get the iterator inside this block
}
}
threadFunction(){
List threadList;
while(1){
//Update threadList, which holds all the objects
synchronized(myList){
myList = threadList.copy(); //Do a deep copy, don't just copy over the reference
}
postInvalidate();
}
}
This will make your draw function operate on a copy of the list made at the end of an iteration. If you're working on a deep copy, you won't get any synchronization problems as the thread isn't altering the same list. The synchronized blocks will stop the thread from overwriting the draw function's copy during a draw. The only remaining issue is that the overwrite of the list in the thread will hang until a draw is done, but since updating it more frequently than that won't show on screen anyway, my guess is that's acceptable.
I have some methods in my View that modify some of the shapes that are drawn when called. In Java in order to make sure the component is updated I would call repaint(). Is there something that will make sure my view is updated correctly?
I had read somewhere that calling invalidate() in the onDraw() method would keep things up to date and therefore I wouldn't need to have something like repaint() in my methods that modify that shapes that are drawn.
Is this correct, or is there something else I have to do?
EDIT
To add in an example, a method I call in my view is:
public void setLineThickness(int thickness) {
aLineThickness = thickness;
if(aLineThicness > 1)
//repaint(); - Okay in Java but not in Android
}
Calling invalidate() will tell the view it needs to redraw itself (call onDraw) sometime in the future. So if you change something in your view, like the line thickness, call invalidate() after it. That way you know your view will eventually be updated.
All your drawing code should be implemented in onDraw() and your other methods should just change the state of your view, which will then be used to draw it, after you call invalidate().
I have a view (custom drawn) added with getWindowManager().addView() and later I'm modifiying the LayoutParameters of it (changing x & width) and call getWindowManager().updateViewLayout(). This works but I am getting two screen refreshes, first one only moves the whole thing according to the new x and later one scales it according to the new width. Any ideas about why is this happening even though I only call updateViewLayout just one time with the new layout parameters?
FYI: onDraw method of the custom drawn view mentioned here is also called only one time by the system during this process.
Try:
runOnUiThread(new Runnable() {
public void run() {
view.updateViewLayout();
}
});
http://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable)
If it doesn't work, check this:
How to move a view in Android?
Are you doing this?
try to do :
view.post(new Runnable() {
public void run() {
view.updateViewLayout();
}
});
updateViewLayout is an a method that can be overriden by your custom ViewGroup and in this overrided method you can implement all what your want to change.
Maybe you do something wrong in it?
Or also maybe you have to implement this code in UiThread like in other questions. - In this case when you change your parameters asynchronously with first call of drawing function by system you method maybe can change only one parameter and on second call the second parameter will be also changed.
Just want to start out by saying this seems like a great site, hope you guys can help!
I'm trying to use the structure laid out in LunarLander to create a simple game in which the user can drag some bitmaps around on the screen (the actual game is more complex, but that's not important). I ripped out the irrelevant parts of LanderLander, and set up my own bitmap drawing, something like
BoardThread (an inner class of BoardView):
run()
{
while(mRun)
{
canvas = lockSurfaceHolder...
syncronized(mSurfaceHolder)
{
/* drawStuff using member position fields
in BoardView */
}
unlockSurfaceHolder
}
}
My drawStuff simply walks through some arrays and throws bitmaps onto the canvas. All that works fine. Then I wanted to start handling touch events so that when the user presses a bitmap, it is selected, when the user unpresses a bitmap, it is deselected, and if a bitmap is selected during a touch move event, the bitmap is dragged. I did this stuff by listening for touch events in the BoardView's parent, BoardActivity, and passing them down into the BoardView. Something like
In BoardView
handleTouchEvent(MotionEvent e)
{
synchronized(mSurfaceHolder)
{
/* Modify shared member fields in BoardView
so BoardThread can render the bitmaps */
}
}
This ALSO works fine. I can drag my tiles around the screen no problem.
However, every once in a while, when the app first starts up and I trigger my first touch event, the handleTouchEvent stops executing at the synchronized line (as viewed in DDMS). The drawing loop is active during this time (I can tell because a timer changes onscreen), and it usually takes several seconds or more before a bunch of touch events come through the pipeline and everything is fine again.
This doesn't seem like deadlock to me, since the draw loop is constantly going in and out of its syncronized block. Shouldn't this allow the event handling thread to grab a lock on mSurfaceHolder? What's going on here? Anyone have suggestions for improving how I've structured this?
Some other info. This "hang" only ever occurs on first touch event after activity start. This includes on orientation change after restoreState has been called. Also, I can remove EVERYTHING within the syncronized block in the event handler, and it will still get hung up at the syncronized call.
Thanks!
So Rob Green from rbgrn helped me get a little more info on this. I ended up using a concurrent queue to deliver touch events to my game thread, but I still had hanging issues when saveInstanceState got called, so I'm now calling Thread.sleep(16) at the bottom of every iteration of my game thread's while loop. Full discussion here.
I have a complex custom view - photo collage.
What is observed is whenever any UI interaction happens, the view is redrawn.
How can I avoid complete redrawing (for example, use a cached UI) of the view specially when I click the "back" button to go back to previous activity because that also causes redrawing of the view.
While exploring the API and web, I found a method - getDrawingCache() - but don't know how to use it effectively.
How do I use it effectively?
I've had other issues with Custom Views that I outline here.
I found a better way than using getDrawingCache.
In the method onDraw, apart from drawing in the natural canvas, I also draw on an memory-only canvas.
Bitmap cacheBmp = Bitmap.Create(....);
Canvas cacheCanvas = new Canvas(cacheBmp);
void onDraw(Canvas c)
{
if(updateDueToInteraction)
{
c.drawXXX(...);
cacheCanvas.drawXXX(...);
} else
{
c.drawBitmap(cacheBmp, 0, 0);
}
}
First of all you will have to use the setDrawingCacheEnabled(true) method, so that you're View is cache-enabled. Then, you can use the getDrawingCache(boolean) method which returns a Bitmap representing the View. Then, you can draw that bitmap manually.
If you don't enable caching by calling the setDrawingCacheEnabled(true) method, you will have to call buildDrawingCache() before (and call destroyDrawingCache() when you're done).
Bye!