I have the following simple countdown script component attached the a Canvas object containing a single Text component. 'instruction' SHOULD reference the TextField, no? When I run it by calling startCountdown, the script hangs in the while loop at the line
Debug.Log ("TIMER countdown time = "+time+ " and instruction = "+instruction);
The instruction variable doesn't trace out at all. The public var 'time' is set to some value greater than 0 in the inspector.
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class Timer : MonoBehaviour {
public int time;
Text instruction;
public void Start () {
instruction = GetComponent<Text>();
}
public void startCountdown() {
StartCoroutine (countdown ());
Debug.Log ("YES TIMER START!");
}
IEnumerator countdown() {
while (time > 0) {
Debug.Log ("TIMER countdown time = " + time + " and instruction = " + instruction);
yield return new WaitForSeconds(1);
instruction.text = time.ToString();
time -= 1;
}
instruction.text = "Blast Off!";
}
}
I think your GetComponent might be returning nothing. Hard to tell since I don't know what gameObject you've attached the script to. I use a public to reference the Text UI element.
I got this to work in Unity 4.6. You must drag a reference to the Text component into the script. I attach my script to an empty gameObject called SceneController. The following works for me (countdown, then blastoff!).
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class Timer : MonoBehaviour {
public int time;
public Text instruction;
public void Start () {
startCountdown();
}
public void startCountdown() {
StartCoroutine (countdown ());
Debug.Log ("YES TIMER START!");
}
IEnumerator countdown() {
while (time > 0) {
Debug.Log ("TIMER countdown time = " + time + " and instruction = " + instruction.text);
yield return new WaitForSeconds(1);
instruction.text = time.ToString();
time -= 1;
}
instruction.text = "Blast Off!";
}
}
Related
I am using CursorLoader to load data from database inside my Fragment, which has a RecyclerView. This fragment is being used inside a ViewPager. The ViewPager is contained inside a ContainerFragment, which in turn is inside an Activity.
The ContainerFragment initializes first 4 loaders out of the 10 required in onActivityCreated(). From the log I can see that the loader(s) are in fact getting created. However, onLoadFinished is not getting called for any of the loaders.
Now the twist comes when I swipe through the ViewPager to the third fragment. Now I see the third loader's onLoadFinished getting called. Now this is probably not called from the fragment but from the PagerAdapter's getItem() method, which in addition to instantiating the fragments, also initializes loaders with their IDs in case they haven't been already.
From all other questions on StackOverflow about onLoadFinished not getting called, I have tried the following solutions which are not working for me:
Using forceLoad()
this.getListView().refreshDrawableState(); -> not tried, and don't understand why this should work. Moreover, I am using RecyclerView.
"Importing the correct class" -> I am using AndroidX, and moreover, the loader does load sometimes. If it was the wrong class, it wouldn't have worked any time right?
"Fragment should use SupportLoaderManager" -> tried replacing in the fragment with getActivity().getSupportLoaderManager. For fragment I think it's just getLoaderManager. It was already working sometimes with just getLoaderManager. (No difference observed)
#onActivityCreated
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
startLoaders();
}
startLoaders()
private void startLoaders() {
while(loaderSeqIndex < CATEGORIES.size() && loaderSeqIndex < 4) {
Bundle bundle = new Bundle();
bundle.putString("category", CATEGORIES.get(loaderSeqIndex));
getLoaderManager().initLoader(loaderSeqIndex++, bundle, this);
}
}
LoaderCallbacks
#NonNull
#Override
public Loader<Cursor> onCreateLoader(int id, #Nullable Bundle args) {
Timber.i("Loader created onCreateLoader called");
CursorLoader loader = ArticleLoader.newCategorizedArticlesInstance(getContext(), args.getString("category"));
loader.registerListener(id, new Loader.OnLoadCompleteListener<Cursor>() {
#Override
public void onLoadComplete(Loader<Cursor> loader, Cursor data) {
int position = loader.getId();
cursorHashMap.put(CATEGORIES.get(position), data);
Timber.i("mPager.getCurrentItem(): " + mPager.getCurrentItem() + " position: " + position);
if (position == mPager.getCurrentItem()) {
ArticleListViewModel model = ViewModelProviders.of(ArticleListContainerFragment.this).get(ArticleListViewModel.class);
model.setCursor(data);
}
}
});
return loader;
}
#Override
public void onLoadFinished(#NonNull Loader<Cursor> loader, Cursor data) {
int position = loader.getId();
cursorHashMap.put(CATEGORIES.get(position), data);
Timber.i("mPager.getCurrentItem(): " + mPager.getCurrentItem() + " position: " + position);
if(position == mPager.getCurrentItem()) {
ArticleListViewModel model = ViewModelProviders.of(this).get(ArticleListViewModel.class);
model.setCursor(data);
// mPagerAdapter.notifyDataSetChanged();
}
}
PagerAdapter's #getView
private class MyPagerAdapter extends FragmentStatePagerAdapter {
...
#Override
public Fragment getItem(int position) {
ArticleListFragment fragment = ArticleListFragment.newInstance();
Bundle bundle = new Bundle();
bundle.putString("category", CATEGORIES.get(position));
bundle.putInt("id",position);
fragment.setArguments(bundle);
// fragment.setCursor(cursorHashMap.get(CATEGORIES.get(position)));
return fragment;
}
...
}
I was expecting timber to print from the #onLoadFinished method just to make sure that it's getting called, which isn't happening.
Something weird that is happening is that:
the cursorHashMap that I am using, get's properly populated I open the app a second time (when refresh doesn't happen). And the cursor get's populated without #onLoadFinished being called.
I'm working on this game breakout-game
And i'm trying to make the ball fire and collide with the wall bouncing, to do that I did what he said, I added a script and put this code:
public class BallMove : MonoBehaviour {
private Rigidbody rb;
public float ballVelocity = 800f;
private bool isMove;
// Use this for initialization
void awake() {
rb = GetComponent<Rigidbody> ();
}
// Update is called once per frame
void Update () {
if (Input.GetButtonDown ("Fire1") && isMove == false) {
transform.parent = null;
isMove = true;
rb.isKinematic = false;
rb.AddForce (new Vector3(ballVelocity,ballVelocity,0));
}
}
}
I understand every line of this code, but when I try to play this, I got a nullReferenceException ,I can run the game but when I press the key I'm getting a error,someone know why? and what happens?
You should use Awake(), not awake(). In your case, you are using a "customized" function, and not the "official" one used by the Unity engine.
So, the engine cannot start that function by itself, and rb stills null when used in the Update().
Example:
void Awake() {
rb = GetComponent<Rigidbody> ();
}
So I followed this tutorial: https://www.youtube.com/watch?v=gyyj57O0FVI
and I made exactly the same code in javafx8.
public class CountdownController implements Initializable{
#FXML
private Label labTime;
#Override
public void initialize(URL location, ResourceBundle resources) {
new Thread(){
public void run(){
while(true){
Calendar calendar = new GregorianCalendar();
int hour = calendar.get(Calendar.HOUR);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
String time = hour + ":" + minute + ":" + second;
labTime.setText(time);
}
}
}.start();
}
After I close the Window, application/thread is still running in the system. My guess its because the infinite loop, but shouldnt the thread be terminated with application closing?
Second thing is that when I try to set the text for Label I get the error:
Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:204)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:364)
at javafx.scene.Parent$2.onProposedChange(Parent.java:364)
at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:575)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(LabeledSkinBase.java:204)
at com.sun.javafx.scene.control.skin.LabelSkin.handleControlPropertyChanged(LabelSkin.java:49)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$60(BehaviorSkinBase.java:197)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$$Lambda$144/1099655841.call(Unknown Source)
at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(MultiplePropertyChangeListenerHandler.java:55)
at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:103)
at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:110)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:143)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:49)
at javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
at javafx.scene.control.Labeled.setText(Labeled.java:146)
at application.CountdownController$1.run(CountdownController.java:29)
...yes, I am going to read more about threads, but I would like to know the answer to these questions.
Part I
A thread, when created, runs independent of other threads. You have a new thread which has an infinite loop, which implies, it will keep running forever, even after the stage has been closed.
Normally, using a infinite loop is not advised, because breaking out of it is very difficult.
You are advised to use :
TimerTask
ScheduledExecutorService
You can then call either one of them (based on whatever you are using)
TimerTask.cancel()
ScheduledExecutorService.shutdownNow()
when your stage is closed. You can use something like :
stage.setOnCloseRequest(closeEvent -> {
timertask.cancel();
});
JavaFX API's (thanks to James_D comment's)
These do not need to be explicitly canceled as ScheduledService uses daemon threads and AnimationTimer runs on the JavaFX thread.
ScheduledService
AnimationTimer
Part II
Your second part of the question has been answered time and again in the forum.
You need to be on the JavaFX Application thread to use scene graph elements.
Since you have created a new thread and trying to update label, which is a JavaFX node, it throws the exception. For more information, please visit:
JavaFX error when trying to remove shape
Why am I getting java.lang.IllegalStateException "Not on FX application thread" on JavaFX?
Javafx Not on fx application thread when using timer
With ScheduledExecutorService as far as I am concerned You cant easly set it as deamon and I don't want to play with stage.setOnCloseRequest(closeEvent -> {});
With AnimationTimer I cant do something like Thread.sleep(100) beetween iteration like you suggested because "AnimationTimer runs on the JavaFX thread."
ScheduledService is just quite difficult for me to understand right now...
so, as I was reading and reading about it I came to conclusion that maybe this simple option will be the best:
public class CountdownController implements Initializable{
#FXML
private Label labTime;
#FXML
private Button buttSTOP;
#Override
public void initialize(URL location, ResourceBundle resources) {
Timer timer = new Timer(true); //set it as a deamon
timer.schedule(new MyTimer(), 0, 1000);
}
public class MyTimer extends TimerTask{
#Override
public void run() {
Calendar calendar = new GregorianCalendar();
int hour = calendar.get(Calendar.HOUR);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
String time = hour + ":" + minute + ":" + second;
Platform.runLater(() -> {
labTime.setText(time);
});
}
}
Thanks James_D and ItachiUchiha. It works, let me know if I'am something missing!
EDIT:
I also include code for Counting down the time, as it was my initial aim, maybe someone will find it usefull as well:
public class CountdownController implements Initializable{
#FXML
private Label labTime;
#FXML
private Button buttSTOP;
private Timer timer = new Timer(true); //set it as a deamon
private int iHours = 0,
iMinutes = 1,
iSeconds = 10;
public void initCountdownController(int iHours, int iMinutes, int iSeconds){
this.iHours = iHours;
this.iMinutes = iMinutes;
this.iSeconds = iSeconds;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
buttSTOP.setOnAction(e -> {
buttSTOPAction(e);
});
timer.schedule(new MyTimer(), 0, 1000);
}
private void buttSTOPAction(ActionEvent e) {
timer.cancel();
}
public class MyTimer extends TimerTask{
#Override
public void run() {
String time = iHours + ":" + iMinutes + ":" + iSeconds;
Platform.runLater(() -> {
labTime.setText(time);
});
if(iSeconds < 1)
if(iMinutes < 1)
if(iHours < 1)
this.cancel();
else{
iHours--;
iMinutes = 59;
iSeconds = 59;
}
else{
iMinutes--;
iSeconds = 59;
}
else
iSeconds--;
}
}
I'm using a GridView to display a list of words in a 4-column table. The getView method of my custom adapter checks the width of the word and shrinks it if it doesn't fit. It does this using a recursive check that keeps scaling the text down until it fits.
private void shrinkText(final TextView wv, final String word) {
wv.setTextSize(defaultTextSize);
new Thread(new Runnable() {
#Override
public void run() {
while (wv.getWidth()>0 && wv.getPaint().measureText(word) > wv.getWidth()) {
Logg.d("word too big. Shrink from " + wv.getTextSize()/density + " to " + (wv.getTextSize()/density-1.0f));
wv.setTextSize(wv.getTextSize() / density - 1.0f);
}
}
}).start();
}
Because I'm using a while loop, I am using a new thread to protect against ANR in the unlikely event of an infinite loop. Here's the weird thing: sometimes it works great. And then sometimes I get the following error:
09-26 14:25:31.389 6427-7765/com.myapp.debug E/AndroidRuntime﹕ FATAL EXCEPTION: Thread-7789
Process: com.myapp.debug, PID: 6427
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
I tried putting the setTextSize inside a runOnUiThread statement, but I can't get it to work inside the adapter. Ultimately I just want this to work. I think my options are:
Keep the while loop in the UI thread and somehow safeguard it (how?)
Move the setTextSize call to the UI thread (how?)
Something else?
Thanks for your help!
UPDATE: based on Rustam's answer, I used wv.post to write to the UI. However the need to use the while loop meant that setTextSize had to be done in the same thread as the while condition itself. I switched from evaluating the TextView.getTextSize to the Paint.getTextSize, since I could set the Paint's text size without impacting the UI, and therefore inside the offshoot thread. Jerry-rigged, but it seems to work.
private void shrinkText(final TextView wv, final String word) {
wv.setTextSize(defaultTextSize);
final Paint mPaint = new Paint(wv.getPaint());
new Thread(new Runnable() {
#Override
public void run() {
while (wv.getWidth()>0 && mPaint.measureText(word) > wv.getWidth()) {
Logg.d("word too big. Shrink from " + mPaint.getTextSize()/density + " to " + (mPaint.getTextSize()/density-1.0f));
mPaint.setTextSize(mPaint.getTextSize() - 1.0f);
}
if (wv.getWidth()>0 && wv.getPaint().measureText(word) > wv.getWidth()) {
wv.post(new Runnable() {
#Override
public void run() {
Logg.d(word + " final size=" + mPaint.getTextSize() / density);
wv.setTextSize(mPaint.getTextSize() / density);
}
});
}
}
}).start();
}
try to update like this:
private void shrinkText(final TextView wv, final String word) {
wv.setTextSize(defaultTextSize);
new Thread(new Runnable() {
#Override
public void run() {
while (wv.getWidth()>0 && wv.getPaint().measureText(word) > wv.getWidth()) {
Logg.d("word too big. Shrink from " + wv.getTextSize()/density + " to " + (wv.getTextSize()/density-1.0f));
wv.post(new Runnable() {
public void run() {
wv.setTextSize(wv.getTextSize() / density - 1.0f);
}
});
}
}).start();
}
I am working on a j2me application which contain a class to find the location of mobile using GPS.I need to include gauge while the location provider API is called and it finds the location.I am new to j2me so still not clear with all the concepts.I am pasting my code below.Please help me through this.Thanks in advance..
package org.ets.utils;
import javax.microedition.lcdui.*;
import javax.microedition.location.*;
import javax.microedition.io.*;
import java.io.*;
import org.ets.midlet.ETS_infozech;
import javax.microedition.midlet.*;
public class Locfinder {
public Locfinder(ETS_infozech midlet)
{
this.midlet = midlet;
}
public static String ex()
{
try {
checkLocation();
} catch (Exception ex)
{
ex.printStackTrace();
}
//System.out.println(string);
return string;
}
public static void checkLocation() throws Exception
{
Location l;
LocationProvider lp;
Coordinates c;
// Set criteria for selecting a location provider:
// accurate to 500 meters horizontally
Criteria cr= new Criteria();
cr.setHorizontalAccuracy(500);
// Get an instance of the provider
lp= LocationProvider.getInstance(cr);
//Request the location, setting a one-minute timeout
l = lp.getLocation(60);
c = l.getQualifiedCoordinates();
if(c != null ) {
// Use coordinate information
double lat = c.getLatitude();
double lon = c.getLongitude();
string = " LAT-" + lat + " LONG-" + lon;
}
}
}
There's no way you can link a Gauge to some task.
You have to set values to the Gauge manually. So you'd create a Gauge and add it to your Form. Then start your code to perform the look-up.
In between your lines of code, you'd add myGauge.setValue(some_value); to increase the indicator.
Of course, this becomes difficult when most of the task is contained in a single line of code, like e.g. lp.getLocation(60);.
I think, in that case, I would create a Thread that automatically increases the value on the Gauge in the 60 seconds, but can be stopped/overridden by a manual setting.
class Autoincrementer implements Runnable {
private boolean running;
private Gauge gauge;
private int seconds;
private int secondsElapsed;
public Autoincrementer(Gauge gauge) {
this.gauge = gauge;
this.seconds = gauge.getMaxValue();
this.running = true;
this.secondsElapsed = 0;
}
public void run() {
if (running) {
secondsElapsed++;
gauge.setValue(secondsElapsed);
if (secondsElapsed>=gauge.getMaxValue()) running = false; // Stop the auto incrementing
try {
Thread.sleep(1000); // Sleep for 1 second
} catch (Exception e) {}
}
}
public void stop() {
running = false;
}
}
You would then create a Gauge and add it to your Form
myGauge = new Gauge("Process", false, 60, 0);
myForm.append(myGauge);
Then start the auto-increment.
myIncrementer = new Autoincrementer(myGauge);
new Thread(myIncrementer).start();
And then call your look-up code.
checkLocation();
Inside your look-up code, add code to stop the auto-incrementing and set the Gauge object to 100%, if the look-up was successful (meaning before the timeout).
myIncrementer.stop();
myGauge.setValue(60);
LWUIT 1.5 can help you in this. Am not sure for Location API which you are using.
But you will get Gauge using LWUIT 1.5. Use Lwuit instead of LCDUI.
http://www.oracle.com/technetwork/java/javame/javamobile/download/lwuit/index.html