I'm working on a countdown that shows seconds remaining to start of the game. So I put TextView into middle of layout and in for loop I am changing the text of TextView, playing the sound using MediaPlayer, then waits 1 second and repeat the proccesss.
The problem is that my text view is not updating - after finishing whole for loop it just change TextView to last text that should be displayed. But since all sounds was played, I can say that Activity is running normaly. So I'm looking for some easy way how to update my UI. I saw some examples of AsynTask, but I think that's too difficult solution for this simple problem. Also, I've tried using handle to change text, but it has got no effect.
My code:
This is in onResume() method:
//Using handler to delay countdown,
//so Activity have enough time to display view
hand.postDelayed((new Runnable(){
#Override
public void run() {
countdownToStart();
game.start();
}
}), 1000);
countdownToStart() method:
protected void countdownToStart(){
int soundSource[] = {R.raw.cntdwn_three, R.raw.cntdwn_two, R.raw.cntdwn_one, R.raw.cntdwn_go};
final String nums[] = {"3", "2", "1", "GO"};
final TextView countdownText = (TextView) findViewById(R.id.challenge_countdown_text);
Handler hand2 = new Handler();
//4 different texts = 4 cycles of for loop
for(int i = 0; i < 4; i++){
//Load sound
for(int x = 0; x < 5; x++){
countdownSound = MediaPlayer.create(getApplicationContext(), soundSource[i]);
if(countdownSound != null)
break;
}
if(countdownSound == null){
Toast.makeText(getApplicationContext(), R.string.sound_load_error, Toast.LENGTH_SHORT).show();
onBackPressed();
}
countdownSound.start();
//Set textview
countdownText.setText(nums[i]);
if(i == 3)
countdownText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 200);
//Delay between countdowns
SystemClock.sleep(1000);
//Release MediaPlayer
countdownSound.release();
}
}
}
Try RunOnUIThread by creating a new Thread
Updated
final TextView countdownText = (TextView) findViewById(R.id.challenge_countdown_text);
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
try{
for(int i = 0; i < 4; i++){
//Load sound
for(int x = 0; x < 5; x++){
countdownSound = MediaPlayer.create(getApplicationContext(), soundSource[i]);
if(countdownSound != null)
break;
}
if(countdownSound == null){
Toast.makeText(getApplicationContext(), R.string.sound_load_error, Toast.LENGTH_SHORT).show();
onBackPressed();
}
countdownSound.start();
//Set textview
runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
countdownText.setText(nums[i]);
if(i == 3)
countdownText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 200);
}
});
Thread.sleep(1000);
}
}
catch(Exception e){
// handle exception
}
}
}).start();
//Delay between countdowns
SystemClock.sleep(1000);
//Release MediaPlayer
countdownSound.release();
}
I'm 7 years behind schedule, but..
If you're using view binding,
And already setup binding, check if in setContentView it's R.layout.whateverlayout or binding.getRoot()
Related
My problem is when I click a toggle button, mp3player is playing and when I click off it is stopping. So when I try to run first time it works. But when I click second time it gives IllegalStateException and "E/MediaPlayer(1009): attachNewPlayer called in state 32" error. How can I fix this problem? Thanks .
My code is here:
public class MyButtons extends Activity {
private static final int[] idBtns = { R.id.btn1, R.id.btn2, R.id.btn3,
R.id.btn4, R.id.btn5, R.id.btn6, R.id.btn7, R.id.btn8, R.id.btn9 };
String[] mpUrls = new String[idBtns.length];
ToggleButton[] mbuttons = new ToggleButton[idBtns.length];
MediaPlayer mp3player = new MediaPlayer();
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.mybuttons);
mp3player.setAudioStreamType(AudioManager.STREAM_MUSIC);
for (int i = 0; i < idBtns.length; i++) {
final int k = i;
mbuttons[k] = (ToggleButton) findViewById(idBtns[k]);
mpUrls[k] = "http://www.testsite.com/def-music-"+ (k + 1) + ".mp3";
}
for (int i = 0; i < idBtns.length; i++) {
final int k = i;
mbuttons[k].setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
boolean on = ((ToggleButton) view).isChecked();
if (on) {
try {
mp3player.setDataSource(mpUrls[k]);
mp3player.prepare();
mp3player.start();
mp3player.setLooping(true);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
Toast.makeText(getApplicationContext(), e.toString(),Toast.LENGTH_LONG).show();
}
} else {
if (mp3player.isPlaying()) {
mp3player.pause();
mp3player.seekTo(0);
}
}// if(on)-else statement's end
}// onClick's end
});
}// for loop's end
}
}
enter image description here
setDataSource have to be called in Idle state. If you click toggle button second time, it's state must not be idle. So you should manage the state in you app using media player listenr and you should make it work accordingly. Anternatively, you can reset mediaplyer every time you start to play. But it propably provides bad user environment.
You can refer document about Valid and invalid states.
https://developer.android.com/reference/android/media/MediaPlayer.html
Android question:
When I set text to a view, when the view will update the UI?
Here is my case 1:
for(int i=0;i< 2000; i++){
aTextView.setText("a"+i);
}
aTextView is a MyTextView, that extends from TextView. I overwirte the onDraw as:
public static int counterP = 0;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
counterP++;
Log.d("MYButton", "onDraw: " + counterP);
}
From the log, I can see, the printed counterP is not consequent. In the loop, the onDraw method is called only 2-4 times.
I did another test, case 2:
boolean notInit = true;
List<String> cmdList = null;
long stSelfRefresh = 0;
String contentStr;
TextView selfTv;
public void onSelfRefreshTv(View v) {
if (cmdList == null || cmdList.isEmpty()) {
Log.e(TAG, "empty now, reset");
notInit = true;
}
if (notInit) {
cmdList = new ArrayList<String>();
for (int i = 0; i < 2000; i++) {
cmdList.add("a" + i);
}
stSelfRefresh = SystemClock.uptimeMillis();
notInit = false;
selfTv = (MyTextView) findViewById(R.id.mytv);
}
contentStr = cmdList.remove(0);
Log.d(TAG, "contentStr = " + contentStr);
selfTv.setText(contentStr);
if (!cmdList.isEmpty()) {
if (!mHandler.sendEmptyMessage(99)) {
Log.e(TAG, "SKIP my self");
}
} else {
Log.d(TAG, "Cost time: " + (SystemClock.uptimeMillis() - stSelfRefresh));
}
}
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 99:
onSelfRefreshTv(null);
break;
default:
break;
}
}
};
The result of case 2 is the counterP is printed out consequent. 1-2000.
I don't know why the textView is updated for each time?
Do you have any idea for this?
Thanks in advance.
******add case 3***********
for(int i=0;i< 2000;i++){
contentStr = cmdList.remove(0);
mHandler.sendEmptyMessage(100);
}
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 100:
selfTv.setText(contentStr);
break;
default:
break;
}
}
};
******end of case 3**********
The test result for case 3 is similar with case 1.
It looks like you are running your code on the UI Thread.
This means: as long as your code is running, the drawing code can not run. The onDraw() is called after your loop finished counting and only the last value is actually drawn on the display.
It's as simple as that.
If you want something count up on your display, you can use a AsyncTask to move the counting part into a non-UI Thread:
new AsyncTask<Void,Integer, Void>() {
#Override
protected Void doInBackground(Void... voids) {
for (int i =0; i<2000; i++) {
publishProgress(i);
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
aTextView.setText("a"+values[0]);
}
};
Your second case is like this:
You prepare the list with all 2000 elements.
Set the first element of the list to the TextView and remove the item from the list.
Send yourself a message to run on the UI Thread as soon as the system thinks it is appropriate.
Let go the control of the UI Thread.
Your code is actually finished and the system calls onDraw().
The message from 4 gets processed and you start again with 2.
You see that the system gets control over the UI Thread in the middle of your loop. That's why it's counting visibly.
Case 3 is different from 2:
You are sending 2000 messages in on loop without letting the system handle it.
Can I use a thread for increment a counter and shows it in a frame of Android activity.
Public class MainActivity extendsActivity {
TextView counter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
counter = (TextView) findViewById(R.id.TV_counter);
Thread t = new Thread() {
public void run() {
runOnUiThread(new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
try {
counter.setText("" + i);
System.out.println("Value of i= " + i);
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
};
t.start();
}
}
I wrote this code, but it run properly in console, but the text view displays i=4 in the terminal, I modified the time to sleep(3000) and the problem persists.
First you don't ever want to put sleep in UI Thread that can lead to unresponsive user interface and that is never good. You should use it just to update your graphics. Try replacing your code with this
Thread t = new Thread() {
public void run() {
for (int i = 0; i < 5; i++) {
try {
final int a = i;
runOnUiThread(new Runnable() {
public void run() {
counter.setText("" + a);
}
});
System.out.println("Value of i= " + i);
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
You are going to notice that sleep and for loop is outside UIThread and in your first thread, so basically all of your math is done outside and you just display the results.
This is just a correction of your code and suggestion for further thinking
EDIT: And for you to better understand why your code is not working, you set some value on your TextView, and immediately after you set UIThread to sleep, UIThread blocks instead of giving it time to finish updating graphics, after he finish sleep you set new value, and he never got to update previous one so in the end you see just the last one.
Hope this helps and enjoy your work.
you can use a CountDownTimer, and update your UI in the onTick() method ( this method is executed on the UI Thread):
int i=0;
CountDownTimer timer = new CountDownTimer(5000,1000) {
#Override
public void onTick(long millisUntilFinished) {
// this method will be executed every second ( 1000 ms : the second parameter in the CountDownTimer constructor)
i++;
txt.setText(i);
}
#Override
public void onFinish() {
// TODO Auto-generated method stub
}
};
timer.start();
I am trying to make view of random text 3 times with time pausing between.
I just can't! It all run all together. I admit I don't know thread subject at all, I just need simple solution.
public void loading3() {
Random randomDouble = new Random();
temp = (double) randomDouble.nextInt(100);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
result.setText(temp + "%");
}
This is the code. I want to use it let's say 3-4 times in a row. How can i do it? I try inside loop or writing same methods in a row but it won't work because it doesn't wait for the first method to end before start the new one.
You should not sleep on the main thread. This can easily be done with a Handler or Timer. Here is an example with a Handler:
private int mCount = 0;
private Handler mHandler = new Handler();
private Runnable mUpdater = new Runnable() {
public void run() {
Random randomDouble = new Random();
temp = (double) randomDouble.nextInt(100);
result.setText(temp + "%");
mCount++;
if (mCount < 3)
mHandler.postDelayed(mUpdater, 1000);
}
}
public void loading3() {
mHandler.postDelayed(mUpdater, 1000);
}
What I'm trying to create is a simple progress bar, that would load for ~10 sec.
So what I want is a for loop like this:
for(int i = 1; i <= 100; i++) {
progressDialog.setProgress(i);
//100ms delay
}
Thanks
You can use Async Task for the purpose
in preExecute() method initialize loop index to 0;
in background process sleep thread for 10 seconds, and then call sendUpdate method to send progress
in postExecute update progress bar to progress get in parameter.
The following code may be helpful for you.
public void startProgress(View view) {
// Do something long
Runnable runnable = new Runnable() {
#Override
public void run() {
for (int i = 0; i <= 10; i++) {
final int value = i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
#Override
public void run() {
progressDialog.setProgress(value);
}
});
}
}
};
new Thread(runnable).start();
}
A shorter solution without explicitly creating a new Thread would look like this:
final Handler handler = new Handler(Looper.getMainLooper());
final int sleepMs = 100;
for (int i = 1; i <= 100; ++i) {
handler.postDelayed(() -> progressDialog.setProgress(value), i * sleepMs);
}