I use functions for canvas like drawCircle and drawPoint in android.
This works fine.
But the problem now is to draw these different items with a delay, so it looks like an animation.
What kind of mechanism should I use? Have tried with async but I dont like that way of doing it.
Should I use some kind of timer that just draw with an interval or is there other clever ways to do this?
I use this strategy, first I declare a Handler and a Runnable that way:
private final Observable mObservable = new Observable();
private final static int TIME_STEP_MS = 5;
private final Handler mHandler = new Handler();
private final Runnable mTimeManager = new Runnable()
{
public void run()
{
mObservable.notifyObservers(TIME_STEP_MS);
mHandler.postDelayed(mTimeManager, TIME_STEP_MS);
}
};
Then when I want to start my time manager I just call the mTimeManager.run() and it will start to notify my Observer s (previously added) periodically.
If you need for some reason stop the timer or something you just do that:
mHandler.removeCallbacks(mTimeManager);
[ EDIT - More complete code ]
Ok than let's make it clearer, first I made a custom Observable object like that [that's optional]:
private final Observable mObservable = new Observable()
{
public void notifyObservers()
{
setChanged();
super.notifyObservers();
};
#Override
public void notifyObservers(Object data)
{
setChanged();
super.notifyObservers(data);
};
};
the reason for that is just because I can't call setChanged() outside Observable class - it's protected, if it's not changed it doesn't notify any observer.
The other declarations keep the same as shown before, now I need to start this TimeManager somewhere, my app is a LiveWallpaper and I make all rendering stuff into a class that extends a Thread but you don't need that necessarily, I made a method called resumeDrawing(), this one is called right after super.start(); at my #Override of public synchronized void start() from Thread class, the method looks like that:
public void resumeDrawing()
{
if (!mTimeManagerRunning) // just a boolean field in my class
{
System.err.println("Resuming renderer."); // just for debug
mTimeManager.run();
mTimeManagerRunning = true;
}
else
{
System.err.println("Renderer already running."); // just for debug
}
}
and it's dual:
public void pauseDrawing()
{
if (mTimeManagerRunning)
{
System.err.println("Pausing renderer.");
mHandler.removeCallbacks(mTimeManager);
mTimeManagerRunning = false;
}
else
{
System.err.println("Renderer already paused.");
}
}
Ok, now we can start and stop the time manager, but who's listening? Nobody! so let's add'em: On the constructor of my Renderer I add some Observer s to my mObservable object, one of those is the Renderer itself, so my renderer extends Thread and implements Observer:
#Override // from Observer interface
public void update(Observable arg0, Object arg1)
{
mElapsedMsRedraw += (Integer) arg1;
if (mElapsedMsRedraw >= mDrawingMsPerFrame)
{
mElapsedMsRedraw = 0;
drawEm(); // refresh the canvas and stuff
}
}
to add observers you simply do mObservable.addObserver(THE_OBJECT - Implements Observer)
you can see that I don't re-render my stuff each time I'm notified, that's because I use this TimeManager for other thinks than just refresh the Canvas like updating the position of the objects I want to draw just internally.
So, what you need to slow down the drawing is to change the way your objects change internally while the time passes, I mean your circles and points etc, or you can chance your time step, I recommend the first one.
Was it clearer? I hope it helps.
I would use a timer, or create Animations. You can create Animations that will do all sorts of things including changing transparency over time.
Here's the Android Documentation for Animation Resources
I believe there may be sophisticated ways of doing this, but for my needs I used a simple method that has a lot of advantages:
I first 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). This also offers 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
RecyclerView calls onCreateViewHolder a bunch of times and then just keeps binding the data to these views. My view creation is slightly expensive and hence I need to defer rest of the UI tasks until my RecyclerView is done creating all the views.
I tried adding a ViewTreeObserver.OnGlobalLayoutListener but this callback gets called before even the first onCreateViewHolder() call.
Any idea how do I go about it?
After some research I've found out a solution with Handler. As you I'm looking for a beautiful code and this is a bit messy for me. But works perfectly anyway.
Handler is a class that you can use in a way to post message and/or Runnable, which will be added in a queue, then executed when that queue is finished.
My plan is, given that the adapter works on the UI, (inflate ect...) the creation and initialization (all onCreateViewHolder and onBindViewHolder) are added at a moment in the handler of the main thread.
That means that if you post a message in the main thread queue (the same obligatory used by your adapter), then the message will be executed after any previous request (after your adapted has finished to initialize everything).
Exemple :
Main activity
Initialization of the handler :
private Handler mHandler;
#Override
protected void onCreate(Bundle iSavedInstanceState) {
...
mHandler = new Handler(Looper.getMainLooper());
}
Initialization of your CustomAdapter :
private void initializeAdapter(...) {
MyCustomAdapter lMyNewAdapter = new MyCustomAdapter(...)
...
lNewAdapter.SetOnFirstViewHolderCreation(new
MyCustomAdapter.OnFirstViewHolderCreation {
#Override
public void onCreation() {
mHandler.post(new Runnable() {
#Override
public void run() {
// Finally here, the code you want to execute
// At the end of any Create and Bind VH of your
// Adapter
}
});
}
});
}
MyCustomAdapter
private boolean mIsViewHolderCreationStarted;
private OnFirstViewHolderCreation mOnFirstViewHolderCreation;
public CustomItemViewAdapter onCreateViewHolder(
#NonNull ViewGroup iViewGroup, int iI) {
...
if (!mIsViewHolderCreationStarted) {
mIsViewHolderCreationStarted = true;
if (mOnFirstViewHolderCreation != null) {
// It's at this point that we want to add a new request
// in the handler. When we're sure the request of the
// adapter has begun.
mOnFirstViewHolderCreation.onCreation();
}
}
}
public void setOnFirstViewHolderCreation(OnFirstViewHolderCreation iAction) {
mOnFirstViewHolderCreation = iAction;
}
public interface OnFirstViewHolderCreation {
void onCreation();
}
Note
Be aware that this solution will execute a code at the end of the first initialization of the enteer page that it is possible to show in a case of a RecyclerView.
A onCreateViewHolder might be called in case the screen is scrolled.
Which means that this solution does not guarantee you this handler message is executed after all possible onCreateViewHolder.
It only helps you to avoid an overload on the MainThread, during the greedy work of the adapter init.
Something else, in case you're using animations with your adapter to make it appears smoothly or something else (one of the good reasons to use this way to do), don't forget to put your RecyclerView in VISIBLE and not GONE, otherwise, the initialization of the adapter never happens.
I'm trying to update the canvas every 100ms using postDelayed() and then invalidate() (which calls onDraw again), but for some reason it will update by 30-50ms. I have debug messages printing out every time the onUpdate() function is called and the timestamps are spaced apart by 30-50ms. Why is it going so much faster than I want it to go? Usually, you'd expect functions run using a delay to be slower than what you specified.
private int FRAME_RATE = 100;
protected void onDraw(Canvas c) {
onDrawAnimation(c);
// Delay the next update by FRAME_RATE milliseconds
_h.postDelayed(_game_loop, FRAME_RATE);
}
private Runnable _game_loop = new Runnable() {
#Override
public void run() {
if (!_paused) {
onUpdate(); // Update locations/speed
invalidate(); // Update visuals (ends up calling onDraw)
}
}
};
Do it like this.
private Runnable _game_loop = new Runnable() {
#Override
public void run() {
_h.postDelayed(_game_loop, FRAME_RATE);
if (!_paused) {
onUpdate(); // Update locations/speed
invalidate(); // Update visuals (ends up calling onDraw)
}
}
};
Also, don't forget to remove all delayed callbacks:
public void onPause(){
_h.removeCallbacks(_game_loop);
// or _h.removeCallbacksAndMessages(null);
// to remove everything
}
You will probably want to separate onUpdate() and invalidate() in different methods and call them at different intervals.
Usually, you need to update the visuals much more often than the game logic, and you want to keep the visual refresh as lightweight as possible. In you code, if you are doing something that takes longer than 100 milliseconds, you will get visible stuttering.
I am trying to draw points to android canvas slowly. I want to use canvas.drawline function however drawing one point to another i want to have small delay. Can you help me please?
Here's the general pattern:
// PSEUDOCODE
// in a class created in a Looper thread, e.g., the main thread
private final Handler handler = new Handler();
static class DrawTask implements Runnable {
// final fields for start point, end point, number of segments, interval, etc.
// mutable field for progress
// constructor with appropriate params
#Override
public void run() {
// draw the current line segment
if(!finished) {
handler.postDelayed(this, interval);
}
}
}
// in some draw method
handler.post(new DrawTask(...));
You might want to hang onto a reference to the DrawTask so you can cancel it with Handler#removeCallbacks(...) in case it's still running when your activity is paused.
I'm developing a relatively small 2D game for Android right now. To process the collision detections as efficient as possible, I've created multiple threads working on the calculations:
Thread #1: Main handling of the frames, limiting them to X per second, handling the Bitmaps (rotate, draw...)
Thread #2: Calculate some collisions
Thread #3: Calculate other collisions
What I need is some sort of synchronization, but I am unsure of what's the best way to achieve this. I thought of something like this:
Thread #1:
public class Thread1 imlements Runnable {
public static ArrayList<Boolean> ResponseList = new ArrayList<Boolean>();
static {
ResponseList.add(0, false); // index 0 -> thread 1
ResponseList.add(1, false); // index 1 -> thread 2
}
public void run() {
boolean notFinished;
while(!isInterrupted() && isRunning) {
notFinished = true;
// do thread-business, canvas stuff, etc, draw
while(notFinished) {
notFinished = false;
for(boolean cur: ResponseList) {
if(!cur) notFinished = true;
}
// maybe sleep 10ms or something
}
}
}
}
And in the other calculation threads something like:
public class CalcThread implements Runnable {
private static final INDEX = 0;
public void run() {
while(isRunning) {
ResponseList.set(INDEX, false);
executeCalculations();
ResponseList.set(INDEX, true);
}
}
}
Or would it be faster (as this is what I'm concerned about) to use a Looper/Handler combination? Just read about this, but I'm not sure yet how to implement this. Would look deeper into this is this would be the more efficient method.
I don't know if it is going to be faster, but it will be more reliable for sure. For example, you are using ArrayList from multiple threads without serialization and ArrayList is not thread-safe
Handler is just one of the available mechanisms, I would recommend you to study java.util.concurrent - there is no point in reinventing the wheel, many synchronization primitives are already available. Perhaps Future would work for you
A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation.
Im doing a little app, its a memory game, you choose one card, it turns up, you choose the second card, it turns up, if they are the same they are out of the game, if they dont match, they are turned down again.
I have
public class PlayActivity extends Activity implements OnClickListener.
The flip events are trigged by click handlers, declared at public void onCreate(Bundle savedInstanceState) they work fine.
When the first card is selected, it calls my method Action, this sets the image from default (the card back) to the 'real' image (the card front). Fine so far.
My problem is the second card: when its selected, it calls method Action, where it should set the front image (lets call it 'middle action'), then a litle pause (a while loop doing nothing until x milliseconds), and then it checks what to do (if they match or not) and turn them down or take the out of the game. You can see where is the problem: the screen only displays the result after x milliseconds (the 'middle action' is not being draw).
Since I have done some little games with XNA, I know the loop Update-Draw, so I know here im updating the same thing twice so always the last one is drawn. But here, the only updating I can have is when click events are trigged, I need a periodic, constant update.
Help?
You can probably use a TimerTask in order to handle that. You can implement it like the following.
This probably isn't the most robust way to do it, but it is an idea. If I figure out a better way to do it in a short time I'll edit my post. :)
Also I would like to add that if you want to make a game that uses an update / draw loop you may need to use a SurfaceView to draw your game. Look at the example here http://developer.android.com/resources/samples/JetBoy/index.html
public class TestGameActivity extends Activity {
/* UIHandler prevents exceptions from
performing UI logic on a non-UI thread */
private static final int MESSAGE_HIDE_CARD = 0;
private class UIHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_HIDE_CARD:
ImageView cardView = (ImageView) msg.obj;
cardView.setImageResource(R.drawable.faceDownCard);
break;
}
}
}
private UIHandler handler = new UIHandler();
// Handle my click. V is the card view
public void onClick(View v) {
final int viewID = v.getId();
// Create a hide task
TimerTask hideTask = new TimerTask() {
#Override
public void run() {
// Construct a message so you won't get an exception
Message msg = new Message();
msg.what = MESSAGE_HIDE_CARD;
msg.obj = findViewById(viewID);
handler.sendMessage(msg);
}
};
// Schedule the task for 2 seconds
new Timer().schedule(hideTask, 2000);
}
}