I have two text fields that I want to show counting up and stopping at a certain number. Typically it works fine, but sometimes one or both stop short by one number.
final TextView t1 = (TextView) findViewById(R.id.text1);
final TextView t2 = (TextView) findViewById(R.id.text2);
winthread = new Thread(new Runnable() {
float store_count = oldstore;
int counter = 0;
public void run() {
while (counter < final_value) {
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
t1.post(new Runnable() {
public void run() {
t1.setText(String.format("T1 $%.2f", store_count));
}
});
t2.post(new Runnable() {
public void run() {
t2.setText(String.format("T2 %d", counter));
}
});
counter++;
store_count = oldstore + counter * 5;
}
}
});
winthread.start();
From the code, T1 should stop at oldstore+final_valuex5 and T2 should stop at final_value but sometimes T1 stops at oldstore+final_valuex5-1 or T2 stops at final_value-1
Related
I made two timers. At some point one of them is increasing and other one is decreasing. I made two integers to increase/decrease every second and use setText for TextView. But for some reason it's not updating. I printed out integers and code was working but text isn't changing for TextView. Here's my code:
TextView timerone, timertwo;
int turn = 1;
int timerOne = 20;
int timerTwo = 20;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_begin_after);
timerone = findViewById(R.id.timerone);
timertwo = findViewById(R.id.timertwo);
timerone.setText(String.valueOf(timerOne));
timertwo.setText(String.valueOf(timerTwo));
Thread counterThread=new Thread(()->{
try{
while(true){
if(turn % 2 == 0) {
timerOne++;
timerTwo--;
}else{
timerOne--;
timerTwo++;
}
Thread.sleep(1000);
}
}catch (Exception e){
}
});
counterThread.start();
}
If you want to modify views from off the UI thread, you need to use runOnUiThread or you will get an error. The following works to update the text views inside the loop.
TextView timerone = findViewById(R.id.timerone);
TextView timertwo = findViewById(R.id.timertwo);
timerone.setText(String.valueOf(timerOne));
timertwo.setText(String.valueOf(timerTwo));
Thread counterThread=new Thread(()->{
try{
while(true){
if(turn % 2 == 0) {
timerOne++;
timerTwo--;
}else{
timerOne--;
timerTwo++;
}
runOnUiThread(() -> {
timerone.setText(String.valueOf(timerOne));
timertwo.setText(String.valueOf(timerTwo));
});
Thread.sleep(1000);
}
}catch (Exception e){
}
});
counterThread.start();
Note, if your version of Java doesn't support lambdas, you can use this instead
runOnUiThread(new Runnable() {
#Override
public void run() {
timerone.setText(String.valueOf(timerOne));
timertwo.setText(String.valueOf(timerTwo));
}
});
Try this solution
Thread counterThread=new Thread(()->{
try{
while(true){
if(turn % 2 == 0) {
timerOne++;
timerTwo--;
}else{
timerOne--;
timerTwo++;
}
// Here you will be updating textview's
runOnUiThread(new Runnable() {
#Override
public void run() {
timerone.setText(String.valueOf(timerOne));
timertwo.setText(String.valueOf(timerTwo));
}
});
Thread.sleep(1000);
}
}catch (Exception e){
}
});
counterThread.start();
I have two threads in my main activity from Runner and start them with clicking a button. The only thing thy do is count up. I want to update two TextViews wit the current count from ech thread. When i start my app and click my button, the app crashed.
The code run perfektly in the console.
The class Runner is only used for the counting. I want to Update the two TextViews after each passage in the methode running().
public class Runner extends Activity implements Runnable {
int count = 0;
String name;
public Runner(String name) {
super();
this.name = name;
}
public void warten() throws InterruptedException {
int zahl = (int) (Math.random() * 500) + 500;
Thread.sleep(zahl);
}
public void running() throws InterruptedException {
for(int i = 0; i < 10; i++) {
warten();
count++;
System.out.println(name + " ist bei: " + count);
if(count == 10) {
System.out.println(name + " IST FERTIG");
}
runOnUiThread(new UiThread(name, count));
}
}
public void run() {
try {
running();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
The class UiThread should be the main thread for updating the UI.
public class UiThread extends Activity implements Runnable {
String name;
int count;
TextView textView1;
TextView textView2;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView1 = findViewById(R.id.textView2);
textView2 = findViewById(R.id.textView3);
}
public UiThread(String name, int count){
this.name = name;
this.count = count;
}
#Override
public void run() {
if(name == "thread1"){
textView1.setText("thread1 ist bei: " + count);
}else{
textView2.setText("thread2 ist bei: " + count);
}
}
}
i would recommend you checkout this guide on android threading docs.
You can't initialize an activity class, you only add it to the manifest, doing so will lead to a null context. So remove the constructor in your UiThread class.
Also you do not need for your runner class to extend Activity. Pass an instance of Activity inorder to use the runOnUiThread() method.
Here is an example of how the UiTHread class should be.
public class UiThread extends Activity {
public TextView textView1;
public TextView textView2;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView1 = findViewById(R.id.textView2);
textView2 = findViewById(R.id.textView3);
startThreads();
}
private void startThreads(){
// Initialize the runnable class passing this activity context.
Runner runner = new Runner(this);
Thread thread1 = new Thread(runner, "thread1");
Thread thread2 = new Thread(runner, "thread2");
thread1.start();
thread2.start();
}
}
the runner class
public class Runner implements Runnable {
private int count = 0;
private UiThread context;
public Runner(UiThread activityContext) {
this.context = activityContext;
}
public void warten() throws InterruptedException {
int zahl = (int) (Math.random() * 500) + 500;
Thread.sleep(zahl);
}
public void running() throws InterruptedException {
for(int i = 0; i < 10; i++) {
warten();
count++;
System.out.println(Thread.currentThread().getName() + " ist bei: " + count);
if(count == 10) {
System.out.println(Thread.currentThread().getName() + " IST FERTIG");
}
// update text views from the main thread.
context.runOnUiThread(new Runnable() {
#Override
public void run() {
if(Thread.currentThread().getName().equals("thread1")){
context.textView1.setText("thread1 ist bei: " + count);
}else{
context.textView2.setText("thread2 ist bei: " + count);
}
}
});
}
}
public void run() {
try {
running();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Hope this helps.
I have this code:
public void setText(String s){
TextView tv= (TextView)HomeActivity.tf.getView().findViewById(R.id.textViewFragment);
char c;
for(int i=0; i< s.length(); i++){
c= s.charAt(i);
tv.append(String.valueOf(c));
try{
Thread.sleep(100);
}catch(Exception e){}
}
}
The problem is that i cant get the TextView to display the letters one-by-one. After the loop is completed and everything is executed, thats when the text shows up.
I want to have it show up letter by letter, to give an animation effect to the TextView.
This code works,
public void setText(final String s)
{
TextView tv= (TextView)HomeActivity.tf.getView().findViewById(R.id.textViewFragment);
final int[] i = new int[1];
i[0] = 0;
final int length = s.length();
final Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
char c= s.charAt(i[0]);
Log.d("Strange",""+c);
tv.append(String.valueOf(c));
i[0]++;
}
};
final Timer timer = new Timer();
TimerTask taskEverySplitSecond = new TimerTask() {
#Override
public void run() {
handler.sendEmptyMessage(0);
if (i[0] == length - 1) {
timer.cancel();
}
}
};
timer.schedule(taskEverySplitSecond, 1, 500);
}
Just in case someone's still looking for a better solution (with animating letters), try out Fade-In TextView.
This TextView library inherits its properties directly from the native TextView class, which means that all the native TextView methods are supported. There are practically no limitations including multiline support. It also has some of its own methods and attributes which offer full control over the View.
More simple
Thread thread = new Thread() {
int i;
#Override
public void run() {
try {
for (i = 0; i < text.length(); i++) { // use your variable text.leght()
Thread.sleep(1000);
runOnUiThread(new Runnable() {
#Override
public void run() {
textView.setText(text.substring(0, i));
}
});
}
} catch (InterruptedException e) {
}
}
};
thread.start();
Im a beginner android developer, so bear with me:
Im getting this error: "CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views"
Anyways, i have a media player with two threads, the first one updates a circular progress bar and the second one updates a text view that i want to use to show the time in the mp3 file. The first thread gives me no errors and runs perfectly fine. (I implemented this before the textview update)
The second thread however gives me the error in the title. I've looked into handlers asynctasks and runonuithread but I can't figure out how to utilize any of them since im using a while loop that's constantly updating it.
Also, why is only the second one giving me an error?
new Thread(new Runnable() {
#Override
public void run() {
ProgressBar myProgress = (ProgressBar) findViewById(R.id.circle_progress_bar);
int currentPosition = 0;
int total = mediaPlayer.getDuration();
myProgress.setMax(total);
while (mediaPlayer != null && currentPosition < total) {
try {
Thread.sleep(1000);
currentPosition = mediaPlayer.getCurrentPosition();
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
myProgress.setProgress(currentPosition);
}
}
}).start();
new Thread(new Runnable() {
#Override
public void run() {
TextView currentTime = (TextView) findViewById(R.id.textView9);
int currentPosition = 0;
int total = mediaPlayer.getDuration();
while (mediaPlayer != null && currentPosition < total) {
try {
Thread.sleep(1000);
currentPosition = mediaPlayer.getCurrentPosition();
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
currentTime.setText(getTimeString(currentPosition));
}
}
}).start();
And here's the code for getTimeString:
private String getTimeString(long millis) {
StringBuffer buf = new StringBuffer();
int hours = (int) (millis / (1000*60*60));
int minutes = (int) (( millis % (1000*60*60) ) / (1000*60));
int seconds = (int) (( ( millis % (1000*60*60) ) % (1000*60) ) / 1000);
buf
.append(String.format("%02d", hours))
.append(":")
.append(String.format("%02d", minutes))
.append(":")
.append(String.format("%02d", seconds));
return buf.toString();
}
do
myProgress.setMax(total);
in
runOnUIThread(new Runnable () {
#Override
public void run () {
myProgress.setMax(total);
}
});
Explanation
Views in android are only work/change/created on UI threads only. Other worker thread donot modify UI elements in Android, because Android is single threaded application.
You can also use AsyncTask which have onPreExecute() and onPostExecute() methods which run on UI thread to post updates to UI
As ρяσѕρєя K say, you can't update view in non-ui thread(Main Thread), so you can use a handler to finish this work
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
// TODO : change currentTime and myProgress as a class number //
TextView currentTime = (TextView) findViewById(R.id.textView9);
ProgressBar myProgress = (ProgressBar) findViewById(R.id.circle_progress_bar);
if (msg.what == 0) {
currentTime.setText(getTimeString(msg.arg1));
} else if (msg.what == 1) {
myProgress.setProgress(msg.arg1);
}
}
};
Then in you two thread, replace
// myProgress.setProgress(currentPosition);
handler.obtainMessage(1, currentPosition, 0).sendToTarget();
// currentTime.setText(getTimeString(currentPosition));
// param1 -> msg.what, param2 -> msg.arg1, parm3 -> msg.arg2
handler.obtainMessage(0, currentPosition, 0).sendToTarget();
Do you Ui work in run On Ui thread :
example:
new Thread(new Runnable() {
#Override
public void run() {
int currentPosition = 0;
int total = mediaPlayer.getDuration();
while (mediaPlayer != null && currentPosition < total) {
try {
Thread.sleep(1000);
currentPosition = mediaPlayer.getCurrentPosition();
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
runOnUIThread(new Runnable () {
#Override
public void run () {
ProgressBar myProgress = (ProgressBar) findViewById(R.id.circle_progress_bar);
myProgress.setMax(total);
myProgress.setProgress(currentPosition);
}
});
}
}
}).start();
Eclipse is offering final but I can't increase the i variable.
#Override
public void onClick(View v) {
final TextView tv = (TextView) findViewById(R.id.tvSayac);
int i = 1;
do {
try {
new Thread(new Runnable() {
public void run() {
tv.post(new Runnable() {
public void run() {
tv.setText(Integer.toString(i));
}
});
}
});
i++;
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (i < 16);
}
A final is an entity that can not be changed after it is initialized.
Final (Java)
What you could do is create a variable within the scope of the do/while loop that is final with the value of i and send that into the function.
The easiest solution here is to create a class:
public class FinalCounter {
private int val;
public FinalCounter(int intialVal) {
val=intialVal;
}
public void increment(){
val++;
}
public void decrement(){
val--;
}
public int getVal(){
return val;
}
public static void main(String[] arg){
final FinalCounter test = new FinalCounter(0);
test.increment(); // 1
test.increment(); // 2
test.increment(); // 3
test.increment(); // 4
test.increment(); // 5
test.decrement(); // 4
System.out.println(test.getVal()); // prints 4
}
}
I think it is possible to create a local copy of the variable i. Try this:
#Override
public void onClick(View v) {
final TextView tv = (TextView) findViewById(R.id.tvSayac);
int i = 1;
do {
final int localCopy = i; // Create here a final copy of i
try {
new Thread(new Runnable() {
public void run() {
tv.post(new Runnable() {
public void run() {
// use here the copy
tv.setText(Integer.toString(localCopy));
}
});
}
}).start(); // Don't forget to start the Thread!
i++;
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (i < 16);
}
By creating a final local copy:
the compiler won't complain anymore
because of Java copies by value, you will only increase i and not localCopy.
I suppose you want to start the Thread as well...
EDIT: Indeed, you were right. You have to create the local final copy inside the loop. Check the new code.
A final variable can only be initialized once not necessarily when you are defining it. It can be set any time within the constructor , but only once. In your case when you are incrementing i using i++, you are trying to assign the incremented value to i again which is not allowed.
You could create a counter class like that and increment it. This way, the reference of the Counter object could be final but you could still set its value ?
What I did was add a:
private int i;
Before this:
#Override
public void onClick(View v) {
final TextView tv = (TextView) findViewById(R.id.tvSayac);
i = 1;
do {
try {
new Thread(new Runnable() {
public void run() {
tv.post(new Runnable() {
public void run() {
tv.setText(Integer.toString(i));
}
});
}
});
i++;
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (i < 16);
}
And you'll be able to use your variable as usual after that, without having to mark it as final.