I am developing a board game that user plays with android. Since android is quite fast, I want to fake that android is performing some tough calculations and thus needs time for its next move.
What I want to do:-
User turn - he moves.
Android turn - android shows text "I am thinking" for 2 seconds
Android hides that text and and only after that moves his turn.
I tried doing:-
onAndroidTurn(){
textView1.setVisibility(View.VISIBLE);
Thread.sleep(2000);
textView2.setVisibility(View.GONE);
}
But what happens is that thread sleeps but text is not shown (okay I know why).
Then searching on stackoverflow, I learnt a way:-
onAndroidTurn(){
textView1.setVisibility(View.VISIBLE);
new Handler().postDelayed(new Runnable(){
void run() {
textView1.setVisibility(View.GONE);
}
}, 2000);
}
Now what this does is that it runs that text in another thread and android's turn is updated on screen and after moving it's turn android showing "Thinking" is total stupidity.
What can I do for this?
Try the following:
Show "I'm thinking"
Calculate your move but don't actually do the move, just store it for a while
Schedule timer
When timer runs out remove the text and do the move
Something like this:
onAndroidTurn(){
textView1.setVisibility(View.VISIBLE);
saveMove(calculateNextMove());
new Handler().postDelayed(new Runnable(){
void run() {
textView1.setVisibility(View.GONE);
doNextMove(restoreMove());
}
}, 2000);
}
Maybe you could just use an AsyncTask (like this pseudo-code):
private class ShowTextTask extends AsyncTask<Void, Void, Void> {
protected void onPreExecute(Void... result) {
textView.setText("Initital");
}
protected Long doInBackground(Void... urls) {
Thread.sleep(2000);
}
protected void onPostExecute(Void... result) {
textView.setText("After 2 seconds");;
}
}
Okay i did it like this:-
public void androidThinking(){
textView1.setVisibility(View.VISIBLE);
androidThinking = true; //explained below
new Handler().postDelayed(new Runnable(){
#Override
public void run() {
droidThink.setVisibility(View.GONE);
//CODE for android's turn
androidThinking = false; //explained below
}
}, 2000);
androidThinking when set to true prevents the user from moving his turn by forcing the listener for user's button to return prematurely
btn.setOnClickListener(new View.onClickListener(){
#Override
public void onClick(View v) {
if(androidThinking)
return;
Thanx everyone for your reply.
Related
This is my first question so sorry if I don't provide all the needed info. Just comment and I'll add it.
Onto the problem itself. I'm trying to make a splash screeen of sorts which consists of a short video. I want the app to start the following activity after the video ends, but after I start the app it just starts the second activity right away (Menu.class in the code). Once I'm in the Menu activity I can go back but all i see is a black VideoView. Also, the VideoView worked before I added the Handler. Not sure if it even could have been the problem (new to android studio) but doesn't work even after removing #Override (probably a stupidity, right?). Or can it be caused by the Menu.java activity? (It's just the standard pre-generated preset, I didn't make any changes to it)
public class splash_screen extends AppCompatActivity {
static long duration;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_ACTION_BAR);
setContentView(R.layout.activity_splash_screen);
VideoView videoView = this.findViewById(R.id.videoView);
String path = "android.resource://" + getPackageName() + "/" + R.raw.splash;
videoView.setVideoURI(Uri.parse(path));
videoView.start();
duration = videoView.getDuration();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
startActivity(new Intent(splash_screen.this, Menu.class));
}
}, duration);
}
}
Thanks for any and every advice, and like I said let me know if you need more info on the problem.
VideoView need some time to warm up, so here:
videoView.start();
duration = videoView.getDuration();
duration could be 0, that is why handler fires immediately. To switch to the second activity you should remove handler part and use:
videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
startActivity(new Intent(splash_screen.this, Menu.class));
finish();//close activity
}
});
You Can Also Use Thread.sleep(millisecond); If you are adding animation to your splash screen
The Full C O D E:
Thread t=new Thread(){
#Override
public void run() {
try{
sleep(3000);
}
catch (InterruptedException e){
}
finally {
startActivity(i);
}
}
};
t.start();
It Gives a delay of 3 seconds,you can do animation part within it
I am making an android app. Here's what I want: User clicks a button, button text changes to "xyz", and then the program waits for 1 second and then the button text changes to "abc".If I use Thread.sleep(1000) then the program does stop for a second but the button text doesn't change to "xyz" before the program goes to sleep.
You can use Handler to achieve this. Using postdelayed method you can do this. Below is the code to do this
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
button.setText("xyz"); // text changed to xyz
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
button.setText("abc"); //text changed to abc after 1 second.
}
},1000);
}
});
I have an app with a title screen. When the app first starts, I have an onCreate method that contains the following code:
setContentView(R.layout.title_screen);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
setContentView(R.layout.main_screen);
}
}, 2000);
When I run my app and press the back button while on the main_screen layout, it closes the app (as it should). However, when I reopen the app, it displays the title_screen layout for two seconds again even though the app is already running. How can I prevent this?
This will prevent the delay appearing again when resumed:
private static boolean flag = false;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!flag){
setContentView(R.layout.title_screen);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
setContentView(R.layout.main_screen);
}
}, 2000);
flag = true;
} else {
setContentView(R.layout.main_screen);
}
}
Btw, if your app was on background and it is calling onCreate again while being resumed, it means that it is killed by the OS. Therefore it is normal to have the initial delay again.
What I would do is to implement two different activities first one showing title_screen and the second which is started after 2s should show your main screen.
After looking at your code I can see that you ALWAYS start with title_screen then after 2s, you change to main_screen. Therefore, when you press back, that means you finish your activity. When you re-open your app, onCreated is called again, and it run every line of code as the previous opening.Of course, there's no difference in 2 times you open your app. To overcome it, I recommend to use SharedPreference to store the flag to check main_screen or title_screen.
In my project I have a lot of asynctask, which all follow this pattern:
#Override
protected void onPreExecute() {
super.onPreExecute();
crossfade(progressBar, contentView);//hide content, show progress bar
}
#Override
protected Void doInBackground(String... arg0) {
//some work
}
protected void onPostExecute(Void unused) {
crossfade(contentView, progressBar);
}
Code for crossfade:
void crossfade(View contentView, View loadingView){
Runnable r = new Runnable(){
#Override
public void run() {
if(contentView != null){
contentView.setAlpha(0f);
contentView.setVisibility(View.VISIBLE);
contentView.animate()
.alpha(1f)
.setDuration(CROSSFADE_TIME)
.setListener(null);
}
if(loadingView != null){
loadingView.animate()
.alpha(0f)
.setDuration(CROSSFADE_TIME)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
if(loadingView != null){
loadingView.setVisibility(View.GONE);
}
}
});
}
}
};
runOnUiThread(r);
}
The problem happens when asynctask executes faster than animation time, causing second crossfade call before the first one is finished, resulting in both views being invisible.
I tried queueing runnables to execute them sequentially, but the problem is if user clicks a lot of buttons or many fragments are being loaded(they use crossfade method too), UI thread becomes overloaded and it may crash my app. The only solution I see so far is to add extra delay to all my asynctasks, using Thread.sleep(CROSSFADE_TIME), however it looks like a really dirty hack and I'm not sure if it's a good user experience.
In case someone needs it in future, adding loadingView.animate().cancel() and contentView.animate().cancel() before animation cancels previous animations and everything works ok.
Why are you running the crossfade animation in a separate thread? Try to do the animation without
Runnable r = new Runnable(){ and runOnUiThread(r);
I have written a function to create a splash screen with a 5 second timeout for my app.
The code works fine, but when the timeout reaches zero and I want to redirect to my main activity, the app crashes with the following error:
Only the original thread that created a view hierarchy can touch its views.
So I looked around a bit and someone suggested nesting this inside my function. It seems like a good Idea, but now methods like sleep / stop won't work.
My code is below, I can provide more / explain more in details if it isn't clear enough just let me know. Thanks for the help.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showSplashScreen();
}
protected boolean _active = true;
protected int _splashTime = 5000; // Splash screen is 5 seconds
public void showSplashScreen() {
setContentView(R.layout.splash_layout);
// Thread splashThread = new Thread() {
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
int waited = 0;
while (_active && (waited < _splashTime)) {
Thread.sleep(100);
if (_active) {
waited += 100;
}
}
} catch (InterruptedException e) {
// do nothing
} finally {
showApplication();
}
}
});
}
Probably not what you want to hear, but you should never put a splash screen on your mobile app. With the exception of games, when people use a mobile app they want to get in, do what ever it is they need to do, and get out. If you make that process take longer, people are just going to get frustrated with you app. You should probably reconsider just not using a splash screen.
This will perform sleep on the UI thread. That's never a good idea.
Why not something like this?
new Handler().postDelayed(new Runnable() {
public void run() {
// start application ...
}
}, _splashTime);
But this answer has a good point. Displaying a splash screen for 5 seconds can be very annoying.
I believe you want AsyncTask for this. The method called on completion of the task will be called on your UI thread, making modifying UI elements much easier.
Use a Handler to post an event to the UI thread that will remove the splash.
Code should be something like...
splash.show()
new Handler().postDelayed(
new Runnable() {
void run() {
splash.remove();
},
delayTime);
I suggest you to make new activity for your spalsh screen, show it in a regular way (with startActivityForResult) and place in it such code (in it, not in your main activity):
new Handler().postDelayed( new Runnable()
{
public void run()
{ finish(); }
}, 5000 );
Also you can handle in this new activity click events for giving opportunity to user to close it faster, tapping on it.