I am building an Othello game( it similar to Go game) on android device by using Android Studio.
I used the minimax algorithm to build a smart bot to defeat the player. However, there are many recursive call in the minimax algorithm, so the calculation of the bot is very slow( I have tried to play and saw that it took about 30 seconds for calculation). So, I want to show a progress bar to signal the player that the bot is calculating. I have tried as follows but the progress bar did not show on my activity:
//Each time the player goes one step, I show progress bar on activity
// to signal to the player that the bot is calculating
progress_bar.setVisible(View.VISIBLE);
//Then, wait for bot's calculation. I have tried to play many time
//and I saw that it takes about 30 seconds
minimax = new Minimax(chessColorMaxtrix);
best_position=minimax.findBestMove();
put(best_position); //complete bot's calculation
//Then, I set the progress bar invisible
progress_bar.setVisible(View.INVISIBLE);
//And wait for the next move of the player
Further, if I do not do progress_bar.setVisible(View.INVISIBLE); then the progress_bar shows on activity normally. But it is not what I want.
I want to ask, Is my progress_bar usage right or wrong? If right, why did not the progress_bar show on activity. If wrong, How can I solve may problem?
Couple of things:
use Tools-> LayoutInspector to see if your progress bar is in view hierarchy.
Also see z-order may be its under another view.
I generally add a full screen relative layout scrim and progress bar inside it, so that taps are not allowed at time when progress bar is visible from layout. That way i am sure of z-order of my progress bar. As last item of my layout:
<RelativeLayout
android:id="#+id/progress_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<ProgressBar style="#style/yourStyle"/></RelativeLayout>
Toggle visibility of container
Try use AsyncTask for your purpose, e.g.:
YourClass {
...
yourMethod () {
new DisplayProgressBarDuringYourOperation().execute();
}
private class DisplayProgressBarDuringCalculation extends AsyncTask<Void, Void, Void> {
/**
* This method displays progress bar in UI thread.
*/
#Override
protected void onPreExecute() {
progressBar.bringToFront();
progressBar.setVisibility(View.VISIBLE);
}
/**
* This method executes your bot calculation in background thread.
*/
#Override
protected Void doInBackground(Void... params) {
// put your bot calculation code here
}
/**
* This method removes progress bar from UI thread when calculation is over.
*/
#Override
protected void onPostExecute(Void response) {
progressBar.setVisibility(View.INVISIBLE);
}
}
}
see about AsyncTask here: https://stackoverflow.com/a/9671602/9626373
Related
I am learning Android's new SplashScreen API introduced with Android 12. I have so far gotten it to work on my Emulator and Google Pixel 4A, but I want to increase its duration. In my Splash Screen I do not want a fancy animation, I just want a static drawable.
I know, I know (sigh) some of you might be thinking, that I should not increase the duration and I know there are several good arguments in favor of not doing so. However, for me the duration of a splash screen with a non animated drawable is so brief (less than a second), I think it raises an accessibility concern, especially so since it cannot be disabled (ironically). Simply, the organization behind the product or its brand/product identity cannot be properly absorbed or recognized by a new user at that size and in that time, rendering the new splash screen redundant.
I see the property windowSplashScreenAnimationDuration in the theme for the splash screen (shown below), but this has no effect on the duration presumably because I am not animating.
<style name="Theme.App.starting" parent="Theme.SplashScreen">
<!--Set the splash screen background, animated icon, and animation duration.-->
<item name="windowSplashScreenBackground">#color/gold</item>
<!-- Use windowSplashScreenAnimatedIcon to add either a drawable or an
animated drawable. One of these is required-->
<item name="windowSplashScreenAnimatedIcon">#drawable/accessibility_today</item>
<item name="windowSplashScreenAnimationDuration">300</item> <!--# Required for-->
<!--# animated icons-->
<!--Set the theme of the activity that directly follows your splash screen-->
<item name="postSplashScreenTheme">#style/Theme.MyActivity</item>
<item name="android:windowSplashScreenBrandingImage">#drawable/wculogo</item>
</style>
Is there a straightforward way to extend the duration of a non animated splash screen?
As I was writing this question and almost ready to post it, I stumbled on the method setKeepOnScreenCondition (below) that belongs to the splashScreen that we must install on the onCreate of our main activity. I thought it seemed wasteful not to post this, given there are no other posts on this topic and no such similar answers to other related questions (as of Jan 2022).
SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
plashScreen.setKeepOnScreenCondition(....);
Upon inspecting it I found this method receives an instance of the splashScreen.KeepOnScreenCondition() interface for which the implementation must supply the following method signature implementation:
public boolean shouldKeepOnScreen()
It seems this method will be called by the splash screen and retain the splash screen visibly until it returns false. This is where the light bulb moment I so love about programming occurred.
What if I use a boolean initialized as true, and set it to false after a delay? That hunch turned out to work. Here is my solution. It seems to work and I thought it would be useful to others. Presumably instead of using a Handler for a delay, one could also use this to set the boolean after some process had completed.
package com.example.mystuff.myactivity;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.splashscreen.SplashScreen;
import android.os.Bundle;
import android.os.Handler;
public class MainActivity extends AppCompatActivity {
private boolean keep = true;
private final int DELAY = 1250;
#Override
protected void onCreate(Bundle savedInstanceState) {
// Handle the splash screen transition.
SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
super.onCreate(savedInstanceState);
//Keep returning false to Should Keep On Screen until ready to begin.
splashScreen.setKeepOnScreenCondition(new SplashScreen.KeepOnScreenCondition() {
#Override
public boolean shouldKeepOnScreen() {
return keep;
}
});
Handler handler = new Handler();
handler.postDelayed(runner, DELAY);
}
/**Will cause a second process to run on the main thread**/
private final Runnable runner = new Runnable() {
#Override
public void run() {
keep = false;
}
};
}
If you are into Java Lambdas an even nicer and more compact solution is as follows:
package com.example.mystuff.myactivity;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.splashscreen.SplashScreen;
import android.os.Bundle;
import android.os.Handler;
public class MainActivity extends AppCompatActivity {
private boolean keep = true;
private final int DELAY = 1250;
#Override
protected void onCreate(Bundle savedInstanceState) {
// Handle the splash screen transition.
SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Keep returning false to Should Keep On Screen until ready to begin.
splashScreen.setKeepOnScreenCondition(() -> keep);
Handler handler = new Handler();
handler.postDelayed(() -> keep = false, DELAY);;
}
}
If you have comments or feedback (besides telling me I should not increase the duration of the splash screen), or a better way please do comment or respond with additional answers.
in Kotlin:
var keepSplashOnScreen = true
val delay = 2000L
installSplashScreen().setKeepOnScreenCondition { keepSplashOnScreen }
Handler(Looper.getMainLooper()).postDelayed({ keepSplashOnScreen = false }, delay)
you can put this into onCreate fun before super.onCreate calling (in activity with LAUNCHER intent filter in Manifest)
One proxy way could be to use
runBlocking { delay(1200) } in onCreate method, to keep on main thread for some specific time.
Im trying to call a function from within a doInBackground of an AsyncTask that fades in a progressBar. Everytime i call this function the progressbar fades in as required then disappears. I added a check at the end which tells me that the visibility of the progressbar has returned back to GONE as soon as the runOnUIThread method completes:
Reset Prog To Gone
Why is this and how can I make it so the progressbar remains visible?
I have been using the following:
#TargetApi(12)
private void fadeInProgressBar(final int time, ProgressBar prog){
System.out.println("MainActivity: Fading In ProgressBar");
runOnUiThread(new Runnable(){
#Override
public void run() {
if(usingHigherApiThan(12)&&prog.getVisibility()==View.GONE){
prog.setVisibility(View.VISIBLE);
if(prog.getAlpha()<1){
prog.animate().setDuration(time).alpha(1);
}
}else if(prog.getVisibility()==View.GONE){
prog.setVisibility(View.VISIBLE);
final AlphaAnimation animation = new AlphaAnimation(0F, 1F);
animation.setDuration(time);
animation.setFillAfter(true);
prog.startAnimation(animation);
}
}
});
if(prog.getVisibility()==View.VISIBLE){
System.out.println("Left Prog Visible");
}else{
System.out.println("Reset Prog To Gone");
}
}
note usingHigherApiThan(12) just checks the build version is 12 or greater and this problem occurs for both above 12 and below 11.
You don't need to, and probably shouldn't, do it this way. AsyncTask has methods that run on the UI thread already (all but doInBackground())
Put your code that you want to run on the UI into onPostExecute() to run when doInBackground() has finished.
Or if it suits your needs better you can also put the code in onProgressUpdate() and call this function when needed with publishProgress().
Alternatively, if you only need it to run when the task starts then you can just put it in onPreExecute(). All of these run on the UI Thread. There's no need to add extra steps with runOnUiThread().
AsyncTask Docs
In my app, I am showing a dialog during a long-running background process which is modal.
This dialog is dismissed when android returns from the background task.
final ProgressDialog progressDialog = ProgressDialog.show(activity, "", "Doing something long running", true, true);
new Thread(new Runnable() {
public void run() {
someLongRunningCode(); // (not using AsyncTask!)
activity.runOnUiThread(new Runnable() {
public void run() {
progressDialog.dismiss();
}
});
}
}).start();
Now, however, when the user rotates his device while the background process is still running the activity is recreated and thus, the progressdialog gets detached from the activity.
When the long running process is done, android obviously tries to hide the (now detached) progress dialog which then results in an exception: java.lang.IllegalArgumentException: View not attached to window manager.
Is there any way to check if it is safe to dismiss the dialog?
Edit: Or better still, is there any way to attach the existing dialog to the newly created activity?
A new, cloned dialog would be fine aswell.
Just catch the exception and ignore it. Android's decision to restart the activity on rotation makes threads have a few odd exceptions like that. But the end result you want is no dialog box displayed. If you just catch the exception, no dialog box is displayed. So you're good.
If you aren't using a separate layout/drawables for landscape and portrait, you can just override configChange in your manifest for the activity so it doesn't destroy the activity (it will still correctly rotate and resize everything for you). That way the same dialog will be up and you shouldn't get the exception. The other option would require a lot of work around onSaveInstanceState and onRestoreInstanceState, and you'd need to be very careful of timing issues if the thread actually finishes during that time. The whole recreate activity on rotation idea doesn't work well with multithreading.
if (progressDialog != null) {
progressDialog.dismiss();
}
I've faced this issue before and solved using the below code.
private boolean isDialogViewAttachedToWindowManager() {
if (dialog.getWindow() == null) return false;
View decorView = progressDialog.getWindow().getDecorView();
return decorView != null && decorView.getParent() != null;
}
i have an activity or screen which contains mainly 3 views (ex.textview1,textview2,textview3) and a progress bar.
While progress bar running or loading if i click on either of textview1,textview2,textview3,
corresponding next activity or screen is opening.
But i want like while progress bar running relevant screen should disable(not tappable) or should not move to next screen if i click on any of these views.
please reply with an example
Just create a boolean for when the you have something in progress and then check for it in you onClickListener.
boolean isRunning;
#Override
public void onClick(View v) {
if(isRunning)
return;
//handle code normally
}
You can use AsyncTask class to do this
FYI, AsyncTask is known as Painless Threading in Android.
onCreate() {
...
MyTask task = new MyTask();
task.execute();
}
class MyTask extends AsyncTask
{
onPreExecute() {
// disable your views here
}
doInBackground() {
// do your loading here in background
}
onPostExecute() {
// enable your views to start activities
}
}
The example is pretty straightforward: i want to let the user know about what the app is doing by just showing a text (canvas.drawText()). Then, my first message appears, but not the other ones. I mean, i have a "setText" method but it doesn't updates.
onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(splash); // splash is the view class
loadResources();
splash.setText("this");
boundWebService();
splash.setText("that"):
etc();
splash.setText("so on");
}
The view's text drawing works by doing just a drawText in onDraw();, so setText changes the text but doesn't show it.
Someone recommended me replacing the view with a SurfaceView, but it would be alot of trouble for just a couple of updates, SO... how the heck can i update the view dinamically at runtime?
It should be quite simple, just showing a text for say 2 seconds and then the main thread doing his stuff and then updating the text...
Thanks!
Update:
I tried implementing handler.onPost(), but is the same story all over again. Let me put you the code:
public class ThreadViewTestActivity extends Activity {
Thread t;
Splash splash;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
splash = new Splash(this);
t = new Thread(splash);
t.start();
splash.setTextow("OA");
try { Thread.sleep(4000); } catch (InterruptedException e) { }
splash.setTextow("LALA");
}
}
And:
public class Splash implements Runnable {
Activity activity;
final Handler myHandler = new Handler();
public Splash(Activity activity) {
this.activity=activity;
}
#Override
public void run() {
// TODO Auto-generated method stub
}
public synchronized void setTextow(final String textow) {
// Wrap DownloadTask into another Runnable to track the statistics
myHandler.post(new Runnable() {
#Override
public void run() {
TextView t = (TextView)activity.findViewById(R.id.testo);
t.setText(textow);
t.invalidate();
}
});
}
}
Although splash is in other thread, i put a sleep on the main thread, i use the handler to manage UI and everything, it doesn't changes a thing, it only shows the last update.
I haven't hit this yet, but I think the usual pattern is to do lengthy initialization in a background thread, and use Handler.post() to update the UI. See http://developer.android.com/reference/android/widget/ProgressBar.html for a different, but possibly related, example.
Also see this answer, especially the first paragraph:
The problem is most likely that you
are running the splash screen (some
sort of Dialog such as ProgressDialog
I assume) in the same thread as all
the work being done. This will keep
the view of the splash screen from
being updated, which can keep it from
even getting displayed to the screen.
You need to display the splash screen,
kick off an instance of AsyncTask to
go download all your data, then hide
the splash screen once the task is
complete.
Update (based on your update and your comment): You are not supposed to update the UI in any thread except the one where your Activity is created. Why is it impossible for you to load your resources in a background thread?
First: onCreate is executed on main UI thread of application so no UI updates until you leave it. Basically you need one thread to execute long running tasks and some mechanism to push updates into the UI.
Most usual approach is to extend AsyncTask see this link for further info
i suppose that your view is an extended view and you call onDraw for drawing the view, so, maybe the view isnĀ“t 'refresh' their state, so try this
onCreate(Bundle bundle) {
setContentView(splash); // splash is the view class
loadResources();
splash.setText("this");
splash.invalidate();
boundWebService();
splash.setText("that"):
splash.invalidate();
etc();
splash.setText("so on");
splash.invalidate();
}