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();
Related
I am trying to wait after each for loop using Threads in my application but I have a problem. This for loop has to be executed when the film from URL is playing but...
Unfortunately the loop is executed with pauses that I put into the code and later the film starts with text updated. This should start simultaneously. The for loop and the film. During the film the texts should be updated one after another.
NOTE: I shorted the ArrayList dict to make code easier to understand.
NOTE2: The app tries to open video file from URL but it gives me a message:
W/MediaPlayer: Couldn't open (Video URL) : java.io.FileNotFoundException: No content provider: (Video URL).
EDIT: I am putting an entire class code for you.
public class Video extends Activity {
private VideoView videoView;
private TextView englishTrans1;
private TextView polishTrans1;
private TextView englishTrans2;
private TextView polishTrans2;
private TextView englishTrans3;
private TextView polishTrans3;
int j = 0;
int i =0;
public static final String TAG = "My tag";
ArrayList<Translations> dict = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video);
videoView = findViewById(R.id.video_view1);
MediaController mMedia = new MediaController(this);
mMedia.setMediaPlayer(videoView);
mMedia.setAnchorView(videoView);
videoView.setMediaController(mMedia);
String path1 = (HERE IS VIDEO URL);
Uri uri = Uri.parse(path1);
videoView.setVideoURI(uri);
videoView.start();
englishTrans1 = findViewById(R.id.english_trans1);
polishTrans1 = findViewById(R.id.polish_trans1);
englishTrans2 = findViewById(R.id.english_trans2);
polishTrans2 = findViewById(R.id.polish_trans2);
englishTrans3 = findViewById(R.id.english_trans3);
polishTrans3 = findViewById(R.id.polish_trans3);
dict.add(new Translations("kot","cat"));
dict.add(new Translations("pies","dog"));
dict.add(new Translations("kawa","coffee"));
dict.add(new Translations("herbata","tea"));
dict.add(new Translations("kościół","church"));
dict.add(new Translations("ślub","wedding"));
final Handler h = new Handler() {
#Override
public void handleMessage(#NonNull Message msg) {
for(Translations x : dict){
try {
synchronized (this) {
Thread.sleep(2000);
}
}catch (InterruptedException e){
}
switch (j) {
case 1: {
Log.d(TAG, "First word translated");
englishTrans1.setText(x.getEnglishWord());
polishTrans1.setText(x.getPolishWord());
break;
}
case 2: {
Log.d(TAG, "Second word translated");
englishTrans2.setText(x.getEnglishWord());
polishTrans2.setText(x.getPolishWord());
break;
}
case 3: {
Log.d(TAG, "Third word translated");
englishTrans3.setText(x.getEnglishWord());
polishTrans3.setText(x.getPolishWord());
break;
}
}
if (j < 3) {
j++;
} else {
j = 1;
}
}
}
};
Runnable r = new Runnable() {
#Override
public void run() {
h.sendEmptyMessage(0);
}
};
Thread t = new Thread(r);
t.start();
}
}
Translations.java class with constructor.
public class Translations {
private String polishWord;
private String englishWord;
public Translations(){
}
public Translations(String mPolishWord,String mEnglishWord){
polishWord = mPolishWord;
englishWord = mEnglishWord;
}
public String getPolishWord() {
return polishWord;
}
public void setPolishWord(String polishWord) {
this.polishWord = polishWord;
}
public String getEnglishWord() {
return englishWord;
}
public void setEnglishWord(String englishWord) {
this.englishWord = englishWord;
}
}
Why loop at all, The "subtitles" as that is what the code snippet is doing, are linked to the video.
What if the person scrubs the film and fast forwards or rewinds, Most subtitle setups have the sentence on screen linked with a timecode for the film, so you then have the subtitle triggered on a change of timecode on the playing video, and pass in the timecode so it will get the sentence for that part of the video and display it on screen.
I have found a solution. The issue was that I was trying to sleep the Thread in Handler. The JVM thought that the main thread should be paused, not the "t" Thread. I moved the Thread.sleep() method to run() and the for loop too. I left only switch() in the Handler to change the UI. It works right now.
final Handler h = new Handler() {
#Override
public void handleMessage(#NonNull Message msg) {
Translations x = dict.get(i-1);
switch (j) {
case 1: {
Log.d(TAG, "First word translated");
englishTrans1.setText(x.getEnglishWord());
polishTrans1.setText(x.getPolishWord());
break;
}
case 2: {
Log.d(TAG, "Second word translated");
englishTrans2.setText(x.getEnglishWord());
polishTrans2.setText(x.getPolishWord());
break;
}
case 3: {
Log.d(TAG, "Third word translated");
englishTrans3.setText(x.getEnglishWord());
polishTrans3.setText(x.getPolishWord());
break;
}
}
}
};
Runnable r = new Runnable() {
#Override
public void run() {
for(i = 0;i<dict.size();i++) {
try {
Thread.sleep(2000);
}catch (InterruptedException e){
}
if (j < 3) {
j++;
} else {
j = 1;
}
h.sendEmptyMessage(0);
}
}
};
Thread t = new Thread(r);
t.start();
}
}
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.
I need to change the color of a blank ImageView using the hex color code values stored in the String array transmitArray, with the delay specified in TransmitFreq. However when I run the code, only the first color (corresponding to the first array value) is shown.
I tried three methods, namely (thread.sleep), countdown timer and post.delayed but with no success. I would appreciate if someone could point out what I'm doing wrong.
public class Main2Activity extends AppCompatActivity {
String [] transmitArray;
long transmitFreq;
public static int i;
public static View colourView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_main2);
final String transmitArray [] = getIntent().getStringArrayExtra("COLOUR_DATA");
transmitFreq = getIntent().getLongExtra("FREQ_VALUE", 0);
int arrayLength = transmitArray.length;
colourView = findViewById(R.id.colourBox);
/*
//Method 1: Using Countdown timer
new CountDownTimer(transmitFreq*(transmitArray.length), transmitFreq) {
public void onTick(long millisUntilFinished) {
colourView.setBackgroundColor(Color.parseColor(transmitArray[i]));
i++;
}
public void onFinish() {
i=0;
}
}.start();
//Method 2: Using post.delayed
Handler handler = new Handler();
for (i = 0; i < arrayLength ; i++) {
handler.postDelayed(new Runnable() {
#Override
public void run() {
String transmitColour = transmitArray[i];
colourView.setBackgroundColor(Color.parseColor(transmitColour));
}
}, transmitFreq);
}*/
//Method 3: Using thread.sleep
for (i = 0; i < arrayLength ; i++) {
String transmitColour = transmitArray[i];
colourView.setBackgroundColor(Color.parseColor(transmitColour));
try {
Thread.sleep(transmitFreq);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
You can initially use a default color inside the onCreate Method and then try the 3 methods outside the OnCreate Method. Try this code
public class Main2Activity extends AppCompatActivity {
String [] transmitArray;
long transmitFreq;
public static int i;
public static View colourView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_main2);
colourView = findViewById(R.id.colourBox);
}
final String transmitArray [] = getIntent().getStringArrayExtra("COLOUR_DATA");
transmitFreq = getIntent().getLongExtra("FREQ_VALUE", 0);
int arrayLength = transmitArray.length;
/*
//Method 1: Using Countdown timer
new CountDownTimer(transmitFreq*(transmitArray.length), transmitFreq) {
public void onTick(long millisUntilFinished) {
colourView.setBackgroundColor(Color.parseColor(transmitArray[i]));
i++;
}
public void onFinish() {
i=0;
}
}.start();
//Method 2: Using post.delayed
Handler handler = new Handler();
for (i = 0; i < arrayLength ; i++) {
handler.postDelayed(new Runnable() {
#Override
public void run() {
String transmitColour = transmitArray[i];
colourView.setBackgroundColor(Color.parseColor(transmitColour));
}
}, transmitFreq);
}*/
//Method 3: Using thread.sleep
for (i = 0; i < arrayLength ; i++) {
String transmitColour = transmitArray[i];
colourView.setBackgroundColor(Color.parseColor(transmitColour));
try {
Thread.sleep(transmitFreq);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
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
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.