Following situation:
I code a Domino game for Android. At the beginning of the game, if not a bot is the starting player, a user should pick two Dominos that get onto the board.
Overview of my non-working approach:
Definition of an empty ArrayList, call of a function that attaches Click Listeners on every Domino belong to the user followed by a while loop that does nothing, should just be the mechanism to wait until the user has picked two dominos. These dominos should be stored in the ArrayList(adding of the dominos to the ArrayList in FirstDominosPickerListener)
The code
In the activity:
ArrayList<Domino> starterDominos = new ArrayList<Domino>();
startingPlayer.chooseStartDominos(starterDominos);
the function:
public void chooseStartDominos(ArrayList<Domino> starterDominos){
///Every Domino gets a ClickListener
for (Domino domino : playerSet){
domino.setOnClickListener(new FirstDominosPickerListener( starterDominos));
}
//The idea is to wait until the user has picked two Dominos. With that loop, no UI at all shows up
while (starterDominos.size()<2){
Log.v(LOG_TAG," WAIT!!!!");
}
}
The problem is the while Loop. With the loop, no UI shows up, i get an empty white screen, altough the code runs. In logcat, i get infinite "Wait" messages. No idea why.
A second approach I've tried was to call a Timer task in the activity after that checks if two dominos were picked (through the size of the list) starterDominos = startingPlayer.chooseStartDominos(starterDominos);
I realized that couldn't work because that runs in another thread and because of that, it wasn't possible to access any part of the UI. But the mechanism to pick the dominos worked. UI showed up and run() ended after two dominos were picked through cancel().
So why does the while loop leads to that behavior? Is the whole approach wrong and if so what can I do that the app waits until the dominos were picked and then proceed
Your while loop is blocking the main UI thread so nothing can be drawn or updated on the screen. You need to set up a listener for when a user selects a domino, then proceed after a user has selected two dominos.
You can use a class variable in the activity class 'userDominoSelectCount' which can be a short integer field initialized to 0.
You can add the onClickListener for domino in the activity onCreate method itself:
domino.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
++userDominoSelectCount;
if(userDominoSelectCount == 2){
//initiate AI logic
userDominoSelectCount = 0;
}
}
});
The way you are doing will block the main thread as mentioned in other answer to the question. OnClicklisteners are meant to be run in event loop and ideally should not block UI.
Related
We're using the Wicket Wizard component. On the last step of the wizard, we find that if the user clicks the finish button rapidly, then Wicket calls our onFinish() method multiple times. This causes problems for us.
Interestingly, the problem occurs in all browsers we've tried (IE, Chrome), but not in Firefox. This browser appears to detect the multiple clicks, and only sends one click event to the server.
Question is, what can we do to prevent this?
From looking at the Wicket framework code, I can see that the nav buttons are contained in a WizardButtonBar, and the FinishButton is handling the onClick event, but I'm unsure of the correct way to override or control this behaviour, and somehow detect the multiple onClick events
You don't seem to use Ajax, do you?
If you did, you could just add a veil - e.g. by overriding #getAjaxIndicatorMarkupId() - and any double submit would be prevented. Additional benefit: If the user pressed the back button after the wizard has finished, the browser will leave the wizard and the user cannot finish it a second time.
For non-Ajax request, you'll have to add your own veil JavaScript to your button.
To prevent the user from going back and finishing the wizard a second time, you can use a token-base solution:
On start of the wizard aquire a token (could just be a counter). Once the wizard has finished, mark the token as expended (e.g. store it along your domain objects).
On each request to the wizard, you can check whether the token is still valid - if not, redirect the user to another page explaning that the wizard has already finished.
Andrew very kindly submitted the original question for me. I did try doing this (pretty much as you suggested):
class MyWizard extends Wizard {
private Integer finishPressed = -1; // deliberately set an invalid value to start with
public MyWizard() {
finishPressed = 0; // only place where finishedPressed is set to 0
log.info( "init: finishPressed={}", finishPressed );
}
#Override
public void onFinish() {
log.info( "begin: finishPressed={}", finishPressed );
if ( ! finishPressed ) {
finishPressed += 1;
// do onFinish actions ...
}
log.info( "end: finishPressed={}", finishPressed );
}
}
However, what I find is that onFinish() is called as many times as I pressed the Finish button, but each time finishPressed is 0 when it enters the method, instead of the count incrementing.
Note: I only ever see "init: finishPressed=0" once in the log. I don't know how it keeps getting reset to 0. However, I did notice that the onFinish() calls were handled by a different thread each time. But as said, the constructor (the only place finishPressed is set to 0) was only called once.
Only by declaring finishPressed as static (which obviously will cause problems in real live operation) do I see the finishPressed incrementing as expected.
In my application, i have a radio button field selected. When i am opening the page, the selected field has some delay. Example: I am opening the page, that radio button is in unselected state, after some time (say like hovering on the screen/scrolling) the radio button turns to selected state. Is there is any other way to solve this delay?
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
#Override
public void execute() {
statementGroupSelectionModel.setSelected(item, profile.getSelected());
}
});
Here in the above code, "item" is widget field name and depending upon my "getselected" value, the radio icon will get selected. Here my "getSelected" value is true.
Any code inside scheduleDeferred will have its execution postponed until the browser finishes rendering the page and the flow control returns to the Javascript event loop, as explained in GWT's documentation:
Deferring some logic into the immediate future: the Scheduler class
Sometimes you want to break up your logic loop so that the JavaScript event loop gets a chance to run between two pieces of code. The Scheduler class will allow you to do that. The logic that you pass to Scheduler will run at some point in the future, after control has been returned to the JavaScript event loop. This little delay may give the interface a chance to process some user events or initialize other code. To use the Scheduler class in its simplest form, you create a subclass of the Command class, overriding the execute() method and pass it to Scheduler.scheduleDeferred
If what you want is for the radio button to be selected immediately, just remove the Scheduler wrapping, i.e. move statementGroupSelectionModel.setSelected(item, profile.getSelected()); out of scheduleDeferred.
// Put this here
statementGroupSelectionModel.setSelected(item, profile.getSelected());
// Remove the following code
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
...
});
If what you want is not that (sorry, your explanation is rather unclear), but just to be able to set the delay time yourself instead of having to wait for the browser, you can use the Timer class which is explained in the documentation linked above.
I am writing a trivia/quiz program in which the user answers questions and his/her answers are judged as correct or incorrect based on the screen coordinates they have clicked. For a clean design, I wish to load the questions using an external function, then attach a MouseListener to the screen object afterward.
My code currently resembles that given below.
void main() {
screen = new QuizScreen();
//load data for question
screen.awaitAnswer();
}
public class QuizScreen implements MouseListener{
//...variables...
//...blank methods for MousePressed, MouseReleased, etc...
public void mouseClicked(MouseEvent me){
//check if answer is right
}
public void awaitAnswer(){
QuizScreen.addMouseListener(this);
}
}
This code works fine for loading, allowing play of, and checking the answer to a single question. However, after the first question has been loaded, I want to be able to repeat the process -- to load ANOTHER (and possibly many more) questions -- by adding a loop to the "main" function. This is currently not possible, since I don't know how to get the MouseListener to stop listening to the user's clicks and return from awaitAnswer() to main().
How do I stop getting the MouseListener to listen to the user? How would I get out of an event-driven section of my code and back to an automatically executing section?
Remove the awaitAnswer method.
Create a (default) constructor for QuizScreen with statement addMouseListener(this);.
From your mouseClicked function, just execute a callback to your main program to create and destroy the QuizScreen
I have a function that creates a socket connection and then waits for a response. During this time I do not want the user to be able to click any buttons, else they can crash the application.
as such at the start of the function I do the following:
public void Checkout()
{
AlertDialog.Builder builder2 = new AlertDialog.Builder(NuPos_testActivity.this);
builder2.setMessage("Processing");
AlertDialog alert2 = builder2.create();
alert2.show();
This greys out the screen and displays processing till the response comes back from the server. My problem is while this is the first thing at the top of the function it only runs after everything else. I don't understand why?
To be more clear about my problem: I run an alert box before code, but it only shows after the code has finished running. how do I fix this?
Showing an AlertDialog doesn't halt the code. It just prevents user interaction. If you want to delay further code execution until you are ready, look at something like ASyncTask which has an onPostExecute() method to notify you when the task is complete.
It sounds like your code sample above, and the code for your Socket, are running in the same Thread (which is the UIThread). If this is the case, Android will usually perform the 'processing' code first, and delay the painting until afterwards (as the painting has a lower priority).
You need to move the Socket code into a separate Thread, so that it can run independently from the UIThread, which should allow Android the time to display the AlertDialog earlier.
Basically I have created a Blackjack app that uses several methods that get called and pass a long the information that is needed (about cards and actions). I have 2 buttons displayed on screen that are click able (hit and stand). Now I have a method called player turn... and this is when I (through static ints) have if a button is selected, things will happen. However I did this with an infinite while loop thinking it will just keep checking to see if a button is pressed and then only do action when a button is pressed. This isn't working as my screen is not refreshing after each textview or imageview change thus as soon as I hit start game the game appears to "freeze" (being due to the method never ending) and I am therefore unable to click said buttons. Is there a way to call something similar to keyboard listener in java (Which pauses activity and waits for user input), in android? If not, what would you suggest the workaround be? Lastly (though not as currently important) how would I properly refresh everything after each change (I believe I need to use invalidate.. though I'm not sure what to have before invalidate so it refreshes the whole thing)? Thanks a bunch in advance.
Looks like a new Activity (invoked by startActivityForResult), and a Handler (called from the onActivityResult) could solve your problem.
Take a look at the design-snippet bellow:
Let's say the method you are willing to 'sleep' is the doSomething from the MainActivity class. I assume this is invoked from some event handler implementation. You should move all the calls from that method into the BackgroundActivity activity,
- which should show a ProgressDialog and/or nothing else -.
In your doSomething method you should start an activity by an intent pointing to the BackgroundActivity with
this.startActivityForResult(new Intent(this, BackgroundActivity.class),
BG_ACTIVITY_ID);
where BG_ACTIVITY_ID is your unique identifier for the started activity, so when it finishes, you can handle it.
Inside the processBackgroundData (or when the background activity is finished processing), you should set the result before calling finish():
final Intent intent = new Intent();
intent.putExtra(KEY_FOR_DATA_YOU_NEED, dataYouNeed);
setResult(Activity.RESULT_OK, intent);
finish();
In your MainActivity you should also override the onActivityResult method, to retrieve the necessary information from the finished BackgroundActivity task:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
//you might also want to check if the resultCode is `Activity.RESULT_OK`...
switch (requestCode)
{
case BG_ACTIVITY_ID:
//get the data you need
final String dataYouNeed = data.getStringExtra(KEY_FOR_DATA_YOU_NEED);
//process the data...
//if necessary (threads!), call your backgroundHandler's
//sendMessage with the retrieved informations as obj.parameters
[...]
break;
default:
[...]
break;
}
}
And inside your BackgroundHandler's handleMessage method (since you are back to the ui thread: the thread in which your main application is running) you can do all the necessary ui modifications, and also listen forward for the user events.
This way you are getting rid of the infinite loop, and can always be aware of user interactions.
The Activity Callback Routines WAIT for the last Event Listener...
Could this be an Easy Solution??
This "wait for user input" question is a question that comes up regularly, has puzzled me as well, and I haven't as yet seen what looks to me like a real clean solution.
I came up with an idea, recently, but I'm pretty green at the Android game and I want to see how it holds up to scrutiny.
The technique is based on the idea that the activity callback routines do not complete as long as there is an event listener that continues to listen. As a result, the callback routine executes the last statement, and then does nothing but does not end. (IOW, "you can check out any time you want, but you can never leave....until the event listeners let you." Yuk.)
This seems to fit the requirements. The UI is not frozen and there is no infinite loop. The only demand on the UI is that it sustain the event listner. When the user supplies the input, the event callback routine, i.e. onKey, handles the input, and then does ALL THE STEPS REQUIRED BEFORE THE NEXT user input is needed. Then the event callback routine sets a new listener and returns, releasing the old listener, and leaving the UI with one task -- sustain the new listener. This continues iteratively until the activity callback routine is completed.
Here is some code I tested:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void onStart() {
super.onStart();
final EditText et = (EditText) findViewById(R.id.edittext);
et.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
boolean tf = checKey(keyCode, event);
if(tf==false)return false;
gotPW();
return true;
}
});
}
gotPW(){
...do ALL the stuff you want to do before you need more user input
...set ANOTHER event listener that is connected to another
event callback method
...return, releasing the first event listener, and putting the onStart
routine back in its same state -- nothing to do except to attend to
an event listener.
}
//this checks to see if the enter key has been hit
public boolean checKey(int keyCode, KeyEvent event){
if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
(keyCode == KeyEvent.KEYCODE_ENTER))
{
return true;
}
return false;
}
The foregoing code seems to do what is required...onStart waited for my input.
Is this a good technique?
Are there problems I haven't identified?
Has everybody been doing this all along,
and only newbies like myself think they are on to something?
Android will run an event loop for you - so you don't have to loop around waiting for input yourself.
You can hook into the loop in a number of ways. From 1.6 onwards the easiest way to do this is to specify the hook in your XML, e.g:
<Button android:onClick="myClickHandler" />
Alternatively you can do this in code by calling setOnClickListener.
Take a look at http://android-developers.blogspot.com/2009/10/ui-framework-changes-in-android-16.html for some examples (read the section headed "Easier click listeners")
If you can invalidate on your view then the Android event loop will take care of re-drawing the screen, including any layout changes. If you have custom View's their onDraw() will be called as appropriate.
Hope this helps.
Hm I might have the wrong interpretation but it seems that doing setOnClickListener has it so that when that certain part of the code is reached, the button becomes clickable and actions are taken when the button is clicked? I already have it so that when a button is clicked and it's Player's turn, actions will occur. However what I need to know is how to have the program literally wait or pause until one of the buttons are pressed.
Similar to when you incite a keyboard and java waits for keyboard input to be made.
Do Not Poll to see if your button has been pressed. Just do whatever it is you want done in the OnClickHandler.
Here's an example of what your code might look like (The Wrong Way):
while(1) {
if (buttonHitClicked()) {
doHit();
} else if (buttonStandClicked()) {
doStand();
}
//sleep(); // won't help
}
And here's the right way:
OnClickListener hitListener = new OnClickListener() {
public void onClick(View v) { doHit(); }
};
OnClickListener standListener = new OnClickListener() {
public void onClick(View v) { doStand(); }
};
protected void OnCreate(Bundle savedValues) {
...
Button hitButton = (Button)findViewById(R.id.hitButton);
button.setOnClickListener(hitListener);
Button standButton = (Button)findViewById(R.id.standButton);
button.setOnClickListener(standListener);
...
}
Yes, the Right Way involves a bit more typing... but it also works. And if you look at the link Collin posted, there's a less "type-y" way to do it that involves adding stuff to your AndroidManifest file. You just point it at a functions in your class with the right signature (public void funcName(View v) as I recall), and Android handles all the listener stuff for you.
As far as "making your program wait", it already is. Your program code isn't running until some call is made to one of your classes' entry points. Until then, execution is floating around in Android's UI handler code in your process, passing messages to and fro. As soon as one of those messages ends up in your app, (onCreate, a click listener, etc) your code does it's thing and returns, allowing that UI thread to go back to handling all the input for your app's process.
And if your app takes longer than 5 seconds or so to respond to a message, it'll be killed by the system, with an "ANR"... App Not Responding.
Another handy Google IO video entitled "Writing Zippy Android Apps".