How to use "runOnUiThread(runnable)" inside static method? - java

I'm writing code in two different classes. The first one runs IOIO Thread which reads pins status of an IOIO board; when this thread is running, it will update the several TextViews which are on the other class (Tab3Activity.java).
I called the method to update the UI just like the code below.
Tab3Activity.setText(index,"string here");
the setText() above need to be static otherwise it gives an err
Cannot make a static reference to the non-static method setText(int, String) from the type Tab3Activity
The problem is on the Tab3Activity.java.
public static void setText(final int idx,final String str) {
runOnUiThread(new Runnable() {
#Override
public void run() {
_textview[idx].setText(str);
}
});
}
the runOnUiThread above gives an err.
Cannot make a static reference to the non-static method runOnUiThread(Runnable) from the type Activity
This is the IOIO Thread code written in Globalioio.java, i'm trying to update the UI on the Tab3Activity.java. Look at the Loop() method.
class Looper extends BaseIOIOLooper {
#Override
public void setup() throws ConnectionLostException {
//setup DigitalOutputs, AnalogInputs etc here.
if(Tab2Activity.isOpened==true){
led_ = ioio_.openDigitalOutput(0, true);
pwm1S = ioio_.openPwmOutput(10, 100);
pwm1S.setDutyCycle((float)Tab2Activity.pwm1Speed.getProgress()/100);
pwm1Move = ioio_.openDigitalOutput(11, false);
pwm2S = ioio_.openPwmOutput(12, 100);
pwm2S.setDutyCycle((float)Tab2Activity.pwm2Speed.getProgress()/100);
pwm2Move = ioio_.openDigitalOutput(13, false);
pwmSrvo1 = ioio_.openPwmOutput(26, 100);
pwmSrvo1.setDutyCycle((float)Tab2Activity.servo1.getProgress()/100);
pwmSrvo2 = ioio_.openPwmOutput(27, 100);
pwmSrvo2.setDutyCycle((float)Tab2Activity.servo2.getProgress()/100);
}
if(Tab3Activity.isOpened==true){
sensor1 = ioio_.openAnalogInput(41);
sensor2 = ioio_.openAnalogInput(42);
for(int i = 0;i<30;i++){
dInput[i] = ioio_.openDigitalInput(DIGITAL_SENSOR_PIN[i]);
}
for(int i = 0; i<10;i++){
aInput[i] = ioio_.openAnalogInput(ANALOG_SENSOR_PIN[i]);
}
}
connStatus=true;
}
#Override
public void loop() throws ConnectionLostException {
try {
if(Tab3Activity.slideDrawer2.isOpened()==true){
final float range1 = (float)(2914/(sensor1.read() * 675.18+5))-1;
Tab3Activity.setSeekBarSensor(0,(int) (range1));
Tab3Activity.setTextSensor(0,Float.toString((range1)));
final float range2 = (float)(2914/(sensor2.read() * 675.18+5))-1;
Tab3Activity.setSeekBarSensor(1,(int) (range2));
Tab3Activity.setTextSensor(1,Float.toString(range2));
}
if(Tab3Activity.slideDrawer1.isOpened()==true){
if(Tab3Activity.pinsGroup==0){
int idx =0;
for(int i = 0;i<10;i++){
final boolean readingD = dInput[i].read();
if(readingD==true){
Tab3Activity.setSeekBar(idx,(int) (100));
}else{
Tab3Activity.setSeekBar(idx,(int) (0));
}
Tab3Activity.setText(idx,Boolean.toString(readingD));
idx++;
}
}else if(Tab3Activity.pinsGroup==1){
int idx =0;
for(int i = 10;i<20;i++){
final boolean readingD = dInput[i].read();
if(readingD==true){
Tab3Activity.setSeekBar(idx,(int) (100));
}else{
Tab3Activity.setSeekBar(idx,(int) (0));
}
Tab3Activity.setText(idx,Boolean.toString(readingD));
idx++;
}
}else if(Tab3Activity.pinsGroup==2){
int idx=0;
for(int i = 20;i<30;i++){
final boolean readingD = dInput[i].read();
if(readingD==true){
Tab3Activity.setSeekBar(idx,(int) (100));
}else{
Tab3Activity.setSeekBar(idx,(int) (0));
}
Tab3Activity.setText(idx,Boolean.toString(readingD));
idx++;
}
}else if(Tab3Activity.pinsGroup==3){
int idx=0;
for(int i = 0;i<10;i++){
final float readingA = aInput[i].read();
Tab3Activity.setSeekBar(idx,(int) (readingA * 100));
Tab3Activity.setText(idx,Float.toString((readingA * 100)));
idx++;
}
}
}
Thread.sleep(10);
} catch (InterruptedException e) {
ioio_.disconnect();
} catch (ConnectionLostException e) {
throw e;
}
}
}
#Override
public IOIOLooper createIOIOLooper(String arg0, Object arg1) {
// TODO Auto-generated method stub
return new Looper();
}
Is there any alternative to do this?
please give the simple one, i'm quite new to android. Thanks in advance

If this thread is started from the same activity
then you can pass the reference of the activity to the thread, and remove static from that method.
YourThread thread = new YourThread(yourActivity);
thread.start();
//YourThread
public class YourThread extends Thread
{
Tab3Activity activity;
public YourThread(Tab3Activity activity)
{
Tab3Activity.activity = activity;
}
...
activity.setText(index,"string here");
...
}
Note: Make sure your activity has android:configChanges="orientation|keyboardHidden|screenSize". Otherwise as you rotate your devices there will be a new instance of acitivity started.
And if your activity is not starting that thread
then you should not try to access the activity directly through a static method.
If you are sure about your implementation and if it does not lead to a memory leak or crash then try this
Create a static MainLooper Handler in your activity or anywhere.
public static Handler UIHandler = new Handler(Looper.getMainLooper());
now you can use this handler to run on ui thread.
public static void setText(final int idx,final String str) {
UIHandler.post(new Runnable() {
#Override
public void run() {
_textview[idx].setText(str);
}
});
}

Yes there is,
you are coupling between your Thread and the Activity which is not a good design ,
instead use Intent when the Thread finish the I/O fire Intent and catch this inside the activity

Related

Android How Do I Call RecyclerVIew notifyDataSetChanged() after ThreadPoolExecutor finishes?

I am learning ThreadPoolExecutor by following this tutorial. To demonstrate its usage, I made a simple android project, it has a recyclerview that will show some Strings. Initially, the array of Strings(String[] myDataset = new String[10]) has 10 nulls. My threadPoolExecutor generates some random strings and fills up the array. So whenever a new String is generated and placed inside the array, I should call notifyDataSetChanged() so that the recyclerView will update and show those random Strings.
the problem
I don't understand how to call notifyDataSetChanged() and so I am pinned down. I got this exception:
Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Since I know AsyncTask, I understand that this error means I cannot call this method in background thread but I have to call it in main thread/ui thread ( so in AsyncTask, it would look like this:
#Override
protected void onPostExecute(String result) {
weakReference.get().notifyDataSetChanged(); // something like that
}
). I need it's ThreadPoolExecutor counterpart. I did google and found this but I am not sure how to do this.
The necessary code segment is given below:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private String[] myDataset;
private final ThreadPoolExecutor threadPoolExecutor;
private Future future;
private Runnable getRunnable(final int i) {
Runnable runnable = new Runnable() {
#Override
public void run() {
String randomString = MyAdapter.getRandomString(i)+" "+i; // <--create random string
Log.e(TAG, randomString);
myDataset[i] = randomString;
try { Thread.sleep(3000); }
catch (InterruptedException e) { e.printStackTrace(); }
}
};
return runnable;
}
public void doSomeBackgroundWork(){
Runnable[] commands = new Runnable[myDataset.length];
for(int i1=0; i1<commands.length; i1++) {
final int j1 = i1;
commands[j1] = () -> {
String randomString = MyAdapter.getRandomString(j1)+" "+j1;
Log.e(TAG, randomString);
myDataset[j1] = randomString;
try { Thread.sleep(3000); }
catch (InterruptedException e) { e.printStackTrace(); }
// notifyDataSetChanged(); // <-------- Error. Where/How should I call it?
};
threadPoolExecutor.execute(commands[j1]);
}
}
public MyAdapter(String[] myDataset) {
this.myDataset = myDataset; // {null, null, ... ...}
this.threadPoolExecutor = DefaultExecutorSupplier.getInstance().forBackgroundTasks(); // returns new ThreadPoolExecutor( ... parameters... );
// future[i] = threadPoolExecutor.submit(command); future[i].cancel(true); use it like this
doSomeBackgroundWork();
}
// ... the rest of the recyclerview related code
}
Could anyone help me? Thank you for reading.
There is a Handler class under the hood in all cases where you need to communicate to UIThread from the another Thread (AsyncTask use it as well).
Some of possible choices:
Use Handler, connected to main looper:
Handler handler = new Handler(getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
notifyDataSetChanged();
}
});
Use "runOnUiThread" that you've mentioned:
runOnUiThread(new Runnable() {
#Override
public void run() {
notifyDataSetChanged();
}
});
Use the "post" method of your UI-View (RecyclerView, for example):
yourRecyclerView.post(new Runnable() {
#Override
public void run() {
notifyDataSetChanged();
}
});
In case anyone needs, here is what I did based on sergiy-tikhonov's answer.
public void doSomeBackgroundWork(){
Runnable[] commands = new Runnable[myDataset.length];
for(int i1=0; i1<commands.length; i1++) {
final int j1 = i1;
commands[j1] = () -> {
String randomString = MyAdapter.getRandomString(j1)+" "+j1;
Log.e(TAG, randomString);
myDataset[j1] = randomString;
try { Thread.sleep(3000); }
catch (InterruptedException e) { e.printStackTrace(); }
// notifyDataSetChanged(); // <-------- Error
recyclerViewWeakReference.get().post(new Runnable() { // <---- this is the change
#Override
public void run() {
notifyDataSetChanged();
}
});
};
threadPoolExecutor.execute(commands[j1]);
}
}
So as you can see, I tried the third option. First I created a WeakReference<RecyclerView> recyclerViewWeakReference = new WeakReference<RecyclerView>(myRecyclerView) in the parent fragment (or activity if you are using that). Then I passed the weak reference into MyAdapter. I used weakReference because that is what you do with AsyncTask,so my instincts alerted me to do so. I hope this is helpful.

Handlers don't end after calling removeCallbacksAndMessages(null)

I have two handlers. Handler in a handler. Both of them are in a for-loop.
The overview is something like this,
for{
handler.postDelayed(runnableA{
for{
handler2.postDelayed(runnableB{
function();
}, 3000);
}
}, 1000);
}
I wanted to end handlers' work at any time when the user clicks back button. So, I created two Runnable Classes so that I can use something like runnableA.removellbacksAndMessages(null).
Handler messageHandler;
Handler countDownHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(this, "Start Play in 5 seconds", Toast.LENGTH_SHORT).show();
countDownHandler = new Handler();
for (int i = 7; i >= 0; --i) {
final int idx = i;
Runnable countRunnable = new CountRunnable(idx, countDownView);
countDownHandler.postDelayed(countRunnable, 1000 * (7 - i));
}
}
And this is Runnable Classes.
public class CountRunnable implements Runnable {
int idx;
TextView countDownView;
public CountRunnable(int idx, TextView countDownView) {
this.idx = idx;
this.countDownView = countDownView;
}
#Override
public void run() {
int messageSize = messageItems.size();
for (int j = 0; j < messageSize; j++) {
final int jdx = j;
messageHandler = new Handler();
Runnable messageRunnable = new MessageRunnable(jdx);
messageHandler.postDelayed(messageRunnable, 3000 * jdx);
}
}
}
class MessageRunnable implements Runnable {
int jdx;
public MessageRunnable(int jdx) {
this.jdx = jdx;
}
#Override
public void run() {
addMessageView(messageItems.get(jdx));
}
}
This is onBackPressed():
#Override
public void onBackPressed() {
super.onBackPressed();
Toast.makeText(getApplicationContext(), "All Work Ended.", Toast.LENGTH_SHORT).show();
scrollFlag = true;
try {
messageHandler.removeCallbacksAndMessages(null);
} catch (Exception e) {
Log.d(TAG, "messageHandler never used");
e.printStackTrace();
}
try {
countDownHandler.removeCallbacksAndMessages(null);
} catch (Exception e) {
e.printStackTrace();
}
}
public void addMessageView(String message){
try{
mTextView.setText(message);
}catch(Exception e){
Toast.makeText(getApplicationContext(), "Abnormal End", Toast.LENGTH_SHORT).show();
}
}
But, I keep getting errors because the activity already ended but the handlers can't find the activity. So, Abnormal End Toast message shows as many as the size of inner for loop.
I can ignore this if I don't use the Toast message, but I am afraid of Memory leak or Bad formed Program or something like that.
How can I fix this problem?
The main problem is that you are creating n numbers of CountRunnables and m number MessageRunnables. Despite creating more than one numbers of handlers you are removing callbacks only for the latest-created Hanlder.
Here's what you should do:
Keep a reference of all the Handlers and Runnables and call messageHandler.removeCallbacksAndMessages(null); and countDownHandler.removeCallbacksAndMessages(null); on all of them.

How do I make a CountDown show/start at the same moment my Button gets enabled?

"How do I make a CountDown show at the same moment my Button gets enabled ?"
Additional info regarding the button: the Buttons Job is it to click 5 times through a stringarray displayed in a Textview to then get disabled for 5 seconds to do the same task again.
so ..I would like a CountDown to visually show those 5 seconds(the time the button is enabled) count down for the User to see.
sadly I dont have an idea how to connect my Button with an CountDown to let it know its supposed to count down at that particular time the Button is enabled.
Also I would like for the CountDown to start everytime the Button gets enabled.
I looked into https://developer.android.com/reference/android/os/CountDownTimer
but it doesnt seem to have a solution for that particular case.
thats my Code for the Button as of now :
next_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (currentnumber == list.length) {
currentnumber = 0;
}
if (Curclicks == mod - 1) {
next_button.setEnabled(false);
display.setText(list[currentnumber]);
currentnumber++;
handler.postDelayed(new Runnable() {
#Override
public void run() {
//the button will unlock after the delay specified
next_button.setEnabled(true);
Curclicks = 0;
}
}, delay);
} else {
display.setText(list[currentnumber]);
currentnumber++;
}
Curclicks++;
}
});
UI Thread code can solve that ? :
private void runThread() {
new Thread() {
public void run() {
while (delay == 5000) { //delay = 5000 ( 5 secs)
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
timer.setText("" + delay);//timer=TxtView
}
});
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
Here is an example of how you can use the postDelayed() method of the Handler to create a count down. I have purposefully left the code a bit verbose so you can go through it step-by-step in order to see what is happening.
Create a few class variables and constants.
private static final long COUNT_DOWN_TICKS = 100l;
private static final long COUNT_DOWN_FINISH = 5000l;
private long countdownElapsed = 0l;
private Handler mCountDownHandler = new Handler();
COUNT_DOWN_FINISH is set to 5000 --> 5 sec. But can be changed to anything you need. Also I use COUNT_DOWN_TICKS set to 100 --> 0.1 sec, just in case you want to display a more precise count down.
From your OnClick() method just call startCountDown() to get the count down started.
private void startCountDown() {
try {
countdownElapsed = 0l;
next_button.setEnabled(false);
displayCountDown();
mCountDownHandler.postDelayed(mCountDownRunnable, COUNT_DOWN_TICKS);
}
catch (Exception ex){
Log.e(TAG, ex.getMessage());
}
}
private Runnable mCountDownRunnable = new Runnable() {
#Override
public void run() {
countdownElapsed = countdownElapsed + COUNT_DOWN_TICKS;
if(countdownElapsed >= COUNT_DOWN_FINISH){
releaseCountDownHandler();
next_button.setEnabled(true);
}
else{
mCountDownHandler.postDelayed(mCountDownRunnable, COUNT_DOWN_TICKS);
}
long secFull = countdownElapsed % 1000;
if(secFull == 0){
runOnUiThread(new Runnable() {
#Override
public void run() {
displayCountDown();
}
});
}
}
};
private void releaseCountDownHandler() {
try {
if(mCountDownRunnable != null) {
mCountDownHandler.removeCallbacks(mCountDownRunnable);
}
}
catch (Exception ex){
Log.e(TAG, ex.getMessage());
}
}
private void displayCountDown(){
long t = (COUNT_DOWN_FINISH - countdownElapsed)/1000;
String myTime = String.valueOf(t);
timer.setText(myTime);
}
In order to dispose of the Runnable properly you will want to call releaseCountDownHandler() from the onPause() method. This is just a short running Thread, but it should still not be ignored.
I prefer the Handler with the postDelay() method to the Thread.sleep() method--something about putting any thread to sleep is disconcerting. Also note that it is a good idea to get accustom to checking the elapsed time condition with ">=" RATHER than "==" depending on the implementation (e.g. you use SystemClock.elapsedRealtime() instead) the condition just might miss the exact value!
EDIT
Somewhere under the definition of your Activity class (for this example I will call it MainActivity) you will need to declare a few variables. Since they are being defined inside the class and NOT inside a method the are referred to as "class variables" and they have a scope of entire class when defined "private".
public class MainActivity extends AppCompatActivity {
//class variables
private static final long COUNT_DOWN_TICKS = 100l;
private static final long COUNT_DOWN_FINISH = 5000l;
private long countdownElapsed = 0l;
private Handler mCountDownHandler = new Handler();
private Button next_button;
private TextView timer;
....
}
You probably have declared the onClick() method inside the onCreate() method of the MainActivity class. So just add the following code:
next_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startCountDown();
}
Everything else I provide are method inside the MainActivity class...NOT inside any other method. So below the onCreate() method add all methods that I previously posted.
It will look something like this:
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
//class variables
private static final long COUNT_DOWN_TICKS = 100l;
private static final long COUNT_DOWN_FINISH = 5000l;
private long countdownElapsed = 0l;
private Handler mCountDownHandler = new Handler();
private Button next_button;
private TextView timer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// you must compensate for the actual layout for your activity
setContentView(R.layout.activity_main);
// you must compensate for the actual id of the TextView
timer = findViewById(R.id.tvTimer);
// you must compensate for the actual id of the Button
next_button = findViewById(R.id.btnNext);
next_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startCountDown();
}
});
}
private void startCountDown() {
try {
countdownElapsed = 0l;
next_button.setEnabled(false);
displayCountDown();
mCountDownHandler.postDelayed(mCountDownRunnable, COUNT_DOWN_TICKS);
}
catch (Exception ex){
Log.e(TAG, ex.getMessage());
}
}
private Runnable mCountDownRunnable = new Runnable() {
#Override
public void run() {
countdownElapsed = countdownElapsed + COUNT_DOWN_TICKS;
if(countdownElapsed >= COUNT_DOWN_FINISH){
releaseCountDownHandler();
next_button.setEnabled(true);
}
else{
mCountDownHandler.postDelayed(mCountDownRunnable, COUNT_DOWN_TICKS);
}
long secFull = countdownElapsed % 1000;
if(secFull == 0){
runOnUiThread(new Runnable() {
#Override
public void run() {
displayCountDown();
}
});
}
}
};
private void releaseCountDownHandler() {
try {
if(mCountDownRunnable != null) {
mCountDownHandler.removeCallbacks(mCountDownRunnable);
}
}
catch (Exception ex){
Log.e(TAG, ex.getMessage());
}
}
private void displayCountDown(){
long t = (COUNT_DOWN_FINISH - countdownElapsed)/1000;
String myTime = String.valueOf(t);
timer.setText(myTime);
}
}
This function should do what you want, just call it into OnClickListener
public void countDown(Button yourBtn) {
new Thread() {
public void run() {
try {
int second = 10;
for (int i = second; i >= 1; i--) {
int finalI = i;
yourBtn.post(new Runnable() {
#Override
public void run() {
yourBtn.setText(String.valueOf(finalI))
}
});
Thread.sleep(1000); // Change text every 1s
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
This code is like my code above except this code introduces a "click counter". I have introduced a new class variable to keep track of how many times the button is clicked. I have also introduced a new constant called "NUMBER_OF_BUTTON_CLICKS" which servers as the upper limit of clicks.
The user can now click on the button (in this case) 5 times. On the fifth click the condition to trigger the startCountDown method is met and the button is disabled for 5 seconds.
public class MainActivity extends AppCompatActivity {
//Constant values
private static final String TAG = MainActivity.class.getSimpleName();
private static final int NUMBER_OF_BUTTON_CLICKS = 5;
private static final long COUNT_DOWN_TICKS = 100l;
private static final long COUNT_DOWN_FINISH = 5000l;
//class variables
private int howManyClicks = 0;
private long countdownElapsed = 0l;
private Handler mCountDownHandler = new Handler();
private Button next_button;
private TextView timer;
private TextView clicks;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// you must compensate for the actual layout for your activity
setContentView(R.layout.activity_main);
// you must compensate for the actual id of the TextView
timer = findViewById(R.id.tvTimer);
// Use this only if you want to display the number of clicks
// you might need to add this TextView if you want to display the number of clicks
clicks = findViewById(R.id.tvClicks);
// you must compensate for the actual id of the Button
next_button = findViewById(R.id.btnNext);
next_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
howManyClicks++;
if(howManyClicks >= NUMBER_OF_BUTTON_CLICKS){
startCountDown();
}
//Add this only if you want to see how many clicks were made
String myClicks = String.valueOf(howManyClicks);
clicks.setText(myClicks)
}
});
}
private void startCountDown() {
try {
countdownElapsed = 0l;
next_button.setEnabled(false);
displayCountDown();
mCountDownHandler.postDelayed(mCountDownRunnable, COUNT_DOWN_TICKS);
}
catch (Exception ex){
Log.e(TAG, ex.getMessage());
}
}
private Runnable mCountDownRunnable = new Runnable() {
#Override
public void run() {
countdownElapsed = countdownElapsed + COUNT_DOWN_TICKS;
if(countdownElapsed >= COUNT_DOWN_FINISH){
releaseCountDownHandler();
next_button.setEnabled(true);
// reset the clicks counter
howManyClicks = 0;
}
else{
mCountDownHandler.postDelayed(mCountDownRunnable, COUNT_DOWN_TICKS);
}
long secFull = countdownElapsed % 1000;
if(secFull == 0){
runOnUiThread(new Runnable() {
#Override
public void run() {
displayCountDown();
}
});
}
}
};
private void releaseCountDownHandler() {
try {
if(mCountDownRunnable != null) {
mCountDownHandler.removeCallbacks(mCountDownRunnable);
}
}
catch (Exception ex){
Log.e(TAG, ex.getMessage());
}
}
private void displayCountDown(){
long t = (COUNT_DOWN_FINISH - countdownElapsed)/1000;
String myTime = String.valueOf(t);
timer.setText(myTime);
//Add this only if you want to see how many clicks were made
String myClicks = String.valueOf(howManyClicks);
clicks.setText(myClicks)
}
}

Display a String letter by letter in a TextView -Android

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();

How can I increase the final integer variable?

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.

Categories

Resources