mid project here and looking for a quick and dirty way out of a sticky situation.
The game looks like this. To play, click a card, and click another, and if they match they both stay shown. Match all and game is over.
In my case, when I have one card shown, when the user selects the second card, the processing logic, should set the card to be visible AND ONLY THEN, check if there is a match. And if not, return the card back to the old image.
This code, I hope, helps you understand what I'm trying to do.
gridLayout.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//HOW DO I FORCE THE INTERFACE TO REDRAW BEFORE CONTINUING THE THREAD?
engine.setCardStateToShow(position);
gridLayout.invalidate();
gridLayout.invalidateViews();
try{
Thread.sleep(1000);
}catch (Exception x){
}
//DETERMINE IF THE CARDS MATCH AND IF NOT -- SET CARD INVIS
String message = engine.itemClicked(position, gridLayout);
Toast.makeText(getApplication(), message, Toast.LENGTH_SHORT).show();
//If engine returns a value of WASWINNER, we can end the activity and end the game.
if (message.equals(GameEngine.WASWINNER)) {
startActivity(endActivity);
}
}
});
}
The final result as it stands, is the application changes the image to show, checks for match, if no match changes to not show. And only afterwards, re-draws the interface again with the new values, so to the end user, if there was no match, they never see the "wrong" tile, because it is reverted back to "not show."
Thanks in advance and please correct me if the question could have been asked better.
Okay, I managed to resolve this issue making use of the AsyncTask abstract class. By using AsyncTask, I effectively could "multi thread", separating the comparison logic from the UI logic. As a result, I could manage the UI directly and seperately as shown in the basic explanation below.
By using the onPreExecute() I showed the cards.
BY using doInBackground() I could check and set card show state processing.
By using onPostExecute() I could return the cards to their final view state, directly in the UI thread, based on the results from the DoInBackground() method of the AsyncTask implementation.
Related
I am trying to build Tic-Tac-Toe game( just for human play with android device, I have implemented a smart algorithm called minimax for my bot) by using android GridView. I have built everything successfully( UI, minimax algorithm for my bot) but there is still a hard to understand problem, that is when I click on Gridview item, I want GridView must update immediately, then my bot start it calculating.So, I have implement my code to set on GridView item click as followings:
gridviewBoard.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l){
//if it is a legal moves of player
if (isLegal(false, i)){
//put X into board
put(false, i);
//I want to update gridview immediately by using notifyDataSetChanged()
chessAdapter.notifyDataSetChanged();
//then start my bot calculating
// findBestMove is a method to find the best move for my bot
// by using minimax algorithm
int nextMove = findBestMove();
}
}
}
Howerver, the gridviewBoard did not update immediately, the gridviewBoard only updates when the findBestMove function is computed while it takes a long time to calculate. Summary, I want to update gridviewBoard immediately after item click and before my bot calculating. Can someone help me, and sorry for my bad English grammar.
I been fighting an odd issue these last few days. I have a custom ExpandableListAdapter where each row contains an ImageView, among other things. I have a class that handles the asynchronous loading of images from the multitude of places they may reside (disk cache, app data, remote server, etc). In my adapter's getView method I delegate the responsibility of returning a View to the list Item itself (I have multiple row types for my group list). I request the image load as follows:
final ImageView thumb = holder.thumb;
holder.token = mFetcher.fetchThumb(mImage.id, new BitmapFetcher.Callback() {
#Override
public void onBitmap(final Bitmap b) {
thumb.post(new Runnable() {
#Override
public void run() {
thumb.setImageBitmap(b);
}
});
}
#Override
public void onFailure() {
}
});
Yeah, it's ugly, but I decided against some contract where you have the BitmapFetcher.Callback execute its methods on the UI thread by default.
Anyway, when I load the Activity that contains the ExpandableListView there will often be thumb images missing from different rows in the list. Reloading the Activity may cause some of the missing thumbs to show but others that were previously showing may not be anymore. The behavior is pretty random as far as I can tell. Scrolling the ListView such that the rows with missing images get recycled causes the new thumb images (when the recycled row gets displayed again) to load fine. Scrolling back to rows that previously contained missing images causes the missing images to appear. I can confirm that all the images are loading correctly from my BitmapFetcher (mFetcher) class. I should also mention that I load other images in other places. Every once in awhile they don't appear either.
After pulling most of my hair out, I discovered that changing:
thumb.post(new Runnable() {
to:
mExpListView.post(new Runnable() {
fixes the issue. I originally thought that the issue might be happening because I was using a final reference to a View, but the other locations in the app use non-final references to a view to post messages, and, as I mentioned, sometimes those did not work. I eventually changed everything to use an Activity's runOnUiThread() method (and my own getUiThreadRunner().execute method when inside Fragments) and that seems to fix the issue all around.
So my question remains, in what cases can View.post() to fail to deliver the runnable to the associated ViewRoot's message queue in the proper order? Or, perhaps the invalidate() is happening before the View is returned from getView and thus before it's placed in a ViewGroup that can be reached from the root View. Those are really the only cases I can think of that would prevent the image from showing up. I can guarantee that none of these calls are happening until at least onStart has finished executing. Further, it looks like it's fine to post to a View even if it hasn't been attached to a Window yet:
// Execute enqueued actions on every traversal in case a detached view enqueued an action
getRunQueue().executeActions(attachInfo.mHandler);
(in performTraversal). The only difference between the runOnUiThread and post seems to be that an Activity has a different Handler than the ViewRootImpl.
Activity:
final Handler mHandler = new Handler();
whereas in ViewRootImpl:
final ViewRootHandler handler = new ViewRootHandler();
But, this should not be a problem provided both Handlers were constructed in the same Thread (or using the same Looper). That leaves me wondering if it is, indeed, a problem to invalidate() a View that has not yet been added to the hierarchy. For this to be the case invalidate should either 1. not do anything if it's not visible, or 2. only be valid for the next performTraversal() that happens.
View.invalidate() checks a nice private method that's not documented called skipInvalidate():
/**
* Do not invalidate views which are not visible and which are not running an animation. They
* will not get drawn and they should not set dirty flags as if they will be drawn
*/
private boolean skipInvalidate() {
return (mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null &&
(!(mParent instanceof ViewGroup) ||
!((ViewGroup) mParent).isViewTransitioning(this));
}
It looks like number 1 is more accurate! However, I would think this only pertains to a View's VISIBILITY property. So, is it accurate to assume that a View is considered not VISIBLE if it cannot be reached from the ViewRoot? Or is the VISIBILITY property unaffected by the View's container? If the former is the case (which I suspect it is) it raises a concern. My use of Activity.runOnUiThread is not a solution to the problem. It only happens to work because the invalidate() calls are being sent to a different Handler and being executed later (after getView returns and after the row has been added and made visible on the screen). Has anybody else run into this issue? Is there a good solution?
Hey David I ran into a similar issue long time back. The basic requirement for view.post(Runnable r) is that the view should be attached to the window for Runnable to be executed. However, since you are loading images asynchronously in your first case, therefore there is a probability that imageView aren't attached to window when post request is made and hence, some images fail to load.
Quoting earlier version of docs on the same:
View.post() : Causes the Runnable to be added to the message queue. The runnable will be run on the user interface thread. This method can
be invoked from outside of the UI thread only when this View is
attached to a window.
Switching to you next question, what is the best solution to handle this situation ?
Can't comment on the best solution. However, I think both handler.post() and activity.runOnUIThread() are good to go. Since, they basically post runnable in main thread queue irrespective of anything and in general, the request to display list rows would be enqueued prior to our thumb.post(). So, they might work flawlessly for most cases. (Atleast I've never faced a problem with them !). However. if you find a better solution, do share it with me.
Try this : setBitmap() like this :
runOnUiThread(new Runnable() {
#Override
public void run() {
thumb.setImageBitmap(b);
}
});
I need to write an android application like a book. I have approximately 100 images and i need to show them with back, forward and another buttons.
I tried to create an xml-layout for each image and made images to layout's background.
While running application, if i press buttons fast, program crashes during switching xml-layout.. If i decrease image sizes my problem decreases also. Unfortunately, i need another solution in order to solve it because i cannot use smaller image-size but i have crash problem still.
Have one layout, with an ImageView in it. Then keep changing the source image for the image view whenever you need to cycle to the next or previous image.
Part of the problem is that clicking a UI button returns immediately / queues clicks, even though the action associated with that click has not yet completed. For reasons beyond the scope of this response, its worth noting that simply deactivating the button while "doing work" is ineffective. There are a couple solutions to this kind of problem: One is to use a boolean flag that gets set only after the underlying "work" has completed. Then within the button action handler, you ignore button clicks that occur before the flag is reset:
/**
* Button presses are ignored unless idle.
*/
private void onMyButtonClicked() {
if(idle) {
doWork();
}
}
/**
* Does some work and then restores idle state when finished.
*/
private void doWork() {
idle = false;
// maybe you spin off a worker thread or something else.
// the important thing is that either in that thread's run() or maybe just in the body of
// this doWork() method, you:
idle = true;
}
Another generic option is to filter using time; ie. you set a limit where the max frequency of button presses is 1hz:
/**
* Determines whether or not a button press should be acted upon. Note that this method
* can be used within any interactive widget's onAction method, not just buttons. This kind of
* filtering is necessary due to the way that Android caches button clicks before processing them.
* See http://code.google.com/p/android/issues/detail?id=20073
* #param timestamp timestamp of the button press in question
* #return True if the timing of this button press falls within the specified threshold
*/
public static synchronized boolean validateButtonPress(long timestamp) {
long delta = timestamp - lastButtonPress;
lastButtonPress = timestamp;
return delta > BUTTON_PRESS_THRESHOLD_MS;
}
Then you'd do something like this:
private void onMyButtonClicked() {
if(validateButtonPress(System.currentTimeMillis())) {
doWork();
}
}
This last solution is admittedly non deterministic, but if you consider that users almost never intentionally click button more than 1-2 times a second on a mobile device, its not so bad.
I have a bit of a problem I cannot solve, since it might a bug or something like that. I would like to make a chart with androidplot, and it works really good, but of course it needs some time to draw it (using hundreds of data), so I use a progress dialog to indicate the loading. But what happens is really weird.
I can define the appearance of the activity when it's loading and when it's loaded. When its loading I define a textview in the background setting its text to "loading" and if it is loaded, that textview contains lots of datas, text etc.
onCreate
{
Thread thread = new Thread(new Runnable() {
public void run() {
-------what needs to be appeared after its loaded ----
Textview -> 12,3245,456,78,789
}
----what is on the screen while the progressbar is on---
TextView -> loading..
}
But most of the time after the progress dialog disappears, nothing happens, the textview still says "loading" and rarely it loads the datas and makes the data appear and changes the textview. What I noticed that the more data I would like to appear the rarelier it stays at the loading phase. Of course everytime the loading progessbar appeers then disappears.
Any Suggestion? It is really weird because I use it in a tablayout and other tabs never do this, always make the data appear.
Thanks in advance!
UP: Okay, its always the first tab, whatever it contains, so the first tab is somehow wrong...
The Andoid UI toolkit is not thread-safe. So, you must not manipulate your UI
from a worker thread—you must do all manipulation to your user interface from
the UI thread. Thus, there are simply two rules to Android's single thread model:
1. Do not block the UI thread
2. Do not access the Android UI toolkit from outside the UI thread
read this for more information on how to access UI elements from outside.
edit::
use AsyncTask ::
onCreate
{
new myLoading().execute();
}
class myLoading extends AsyncTask<Void, Void, Void>
{
protected Void doInBackground(Void ... ) {
.......... do all the loading here .........
}
protected void onPostExecute(Void ) {
Textview -> 12,3245,456,78,789
}
}
I figured out some workaround. I dont have any clue about the solution, my only guess is the loading screen somehow overtake the arrival of all the data and so the chart cannot be drawn. or whatever...
I put a thread.sleep(1000) and now it works.
I have a ListActivity that launches a task to hit a web service and display the results in a ListView. Each one of the results has an image ID attached to it.
I wrote a method that will get the image IDs of the rows displayed on screen (firstVisiblePosition() to lastVisiblePosition()) and launch a task to query another web service to get the images to display for those items. I call this method when the list's scroll state becomes SCROLL_STATE_IDLE. This makes it so the user can scroll and the task to get the images for the visible rows does not execute until the scrolling stops, preventing it from looking up images for off-screen rows.
My issue is that when the results initially show in the ListView, I can't find a good way to call my method to look up which image IDs to query for. Apparently, calling this method right after calling setAdapter does not work (I'm guessing because some of the ListView's work happens asynchronously). I am using multiple Adapter's (for reasons not pertinent to this post), so I need a good way of waiting for the list items to show before I call my method to get the IDs.
Any ideas?
After you've set the adapter or called notifyDatasetChanged() on the adapter, add your "load images" code to the list's post queue as a Runnable:
list.post( new Runnable() {
#Override
public void run() {
//do stuff
}
});
If I'm understanding your question right, you're having trouble loading images over the net and performance issues; if so,
I would create a simple image cache in my Adapter such as a local but global HashMap:
private HashMap<String, Drawable> imgCache = new HashMap<String, Drawable>();
then in my getView() method, I would asynchronously (using a Thread and a Handler) load the images and save loaded images in my imgCache by assigning position as the key and loaded images as Drawables.
final Handler h = new Handler() {
public void handleMessage(Message msg) {
if(msg.obj != null) {
Drawable drawable = (Drawable)msg.obj;
image.setImageDrawable(drawable);
image.postInvalidate();
image.requestLayout();
imgCache.put(cacheKey, drawable);
}
}
};
loadImage(myImageView, imageURL, h); // threaded method which loads the images from net
also, in my getView() method I would first ask imgCache to see if the image already exist before loadImage is called.
This should optimize your list and rescue you from using multiple Adapter etc.
Hope this helps,
-serkan