I have some code that starts text to speech and begins playing a sound when a button is pressed. Everything is working fine except for one detail. When two buttons are pressed consecutively, the sounds begin to overlap. Is there a way to cut off the mediaplayer every time a button is pressed? I have used noise1.pause(), noise1.stop(), and noise1.reset() but nothing works. I was thinking about using mediaplayer complete listener but I'm not sure how I could implement this. Any help would be appreciated. Thanks a lot!!!
public void onClick(View view) {
Resources res = getResources();
final TextView tv = (TextView) findViewById(R.id.color_text);
final MediaPlayer noise1 = MediaPlayer.create(this, R.raw.one);
final MediaPlayer noise2 = MediaPlayer.create(this, R.raw.two);
final MediaPlayer noise3 = MediaPlayer.create(this, R.raw.three);
switch (view.getId()) {
case R.id.one_button:
String oneString = res.getString(R.string.One);
tv.setText(oneString);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
tts.speak(oneString, TextToSpeech.QUEUE_FLUSH, null);
} else {
tts.speak(oneString, TextToSpeech.QUEUE_FLUSH, null, null);
}
noise1.start();
break;
case R.id.two_button:
String twoString = res.getString(R.string.Two);
tv.setText(twoString);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
tts.speak(twoString, TextToSpeech.QUEUE_FLUSH, null);
} else {
tts.speak(twoString, TextToSpeech.QUEUE_FLUSH, null, null);
}
noise2.start();
break;
case R.id.three_button:
String threeString = res.getString(R.string.Three);
tv.setText(threeString);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
tts.speak(threeString, TextToSpeech.QUEUE_FLUSH, null);
} else {
tts.speak(threeString, TextToSpeech.QUEUE_FLUSH, null, null);
}
three.start();
break;
}
Define and initialize the MediaPlayer instances outside the onClick(...) method and the stop() method should work.
Here is what I mean:
MediaPlayer noise1, noise2, noise3;
TextView tv;
public void onCreate(....){
.....
tv = (TextView) findViewById(R.id.color_text);
noise1 = MediaPlayer.create(this, R.raw.one);
noise2 = MediaPlayer.create(this, R.raw.two);
noise3 = MediaPlayer.create(this, R.raw.three);
....
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.one_button:
...
noise1.start();
noise2.stop();
noise3.stop();
break;
case R.id.two_button:
...
noise1.stop();
noise2.start();
noise3.stop();
break;
case R.id.three_button:
...
noise1.stop();
noise2.stop();
noise3.start();
break;
}
It doesn't work now because you're trying to stop a MediaPlayer instance that you've just created not the one that was created and started on the previous button press.
I don't have reputation for commenting, so have this instead.
The created MediaPlayer objects are within the scope of the method, which means as soon as onClick returns, the objects have no reference, and hence, cannot be stopped.
The solution is to move the objects higher in the scope, either having references in the encompassing object or storing the MediaPlayers in a collection. Then, at the start of the onClick method, stop the old ones before creating new ones and overriding the reference.
Unless there is some caveat with Android, this should work.
Related
I know setText just changes the text only once but I can't seem to find the reason why the text in it changes before I move on to the next question in the quizActivity
What I have made is an app with one activity in it which has a quiz in it, a question is displayed along with 4 options. When the user selects an option, if that option is correct then it becomes green and red otherwise and additionally, I then open a dialog box showing whether the answer was right or wrong and then the question is changed when the user clicks Next on the dialog box.
But what is happening that when the user selects an option, in between the process of clicking the option and then clicking next on the dialog box, the text in the questions and the options changes and I can't seem to figure out why is that happening. In total, the question and options change two times when they should change only once, the unexpected change is when the user clicks on an option and the dialog box opens.
Here's the code:
#Override
public void onClick(View view) {
int selectedOption = 0;
switch (view.getId()) {
case R.id.option_1_tile:
selectedOption = 1;
break;
case R.id.option_2_tile:
selectedOption = 2;
break;
case R.id.option_3_tile:
selectedOption = 3;
break;
case R.id.option_4_tile:
selectedOption = 4;
break;
default:
}
checkAnswer(selectedOption, view);
}
Here's the function which checks the answer:
private void checkAnswer(int selectedOption, View view) {
if (selectedOption == selected_questions.get(quesNum).getAnswer()) {
//Right Answer
(view).setBackgroundTintList(ColorStateList.valueOf(Color.GREEN));
quizReference.child(selected_questions.get(quesNum).getId()).child("correct_attempts").setValue(String.valueOf(Integer.valueOf(selected_questions.get(quesNum).getCorrect_attempts()) + 1));
quizReference.child(selected_questions.get(quesNum).getId()).child("total_attempts").setValue(String.valueOf(Integer.valueOf(selected_questions.get(quesNum).getTotal_attempts()) + 1));
score++;
correctDialog();
} else {
//Wrong Answer
(view).setBackgroundTintList(ColorStateList.valueOf(Color.RED));
quizReference.child(selected_questions.get(quesNum).getId()).child("total_attempts").setValue(String.valueOf(Integer.valueOf(selected_questions.get(quesNum).getTotal_attempts()) + 1));
switch (selected_questions.get(quesNum).getAnswer()) {
case 1:
options[0].setBackgroundTintList(ColorStateList.valueOf(Color.GREEN));
break;
case 2:
options[1].setBackgroundTintList(ColorStateList.valueOf(Color.GREEN));
break;
case 3:
options[2].setBackgroundTintList(ColorStateList.valueOf(Color.GREEN));
break;
case 4:
options[3].setBackgroundTintList(ColorStateList.valueOf(Color.GREEN));
break;
}
wrongDialog ();
}
}
Here's the function which changes the question:
private void changeQuestion() {
resetColor ();
if (quesNum < selected_questions.size() - 1) {
quesNum++;
playAnim(question, 0, 0);
playAnim(option1_text, 0, 1);
playAnim(option2_text, 0, 2);
playAnim(option3_text, 0, 3);
playAnim(option4_text, 0, 4);
qCount.setText(String.valueOf(quesNum + 1) + "/" + String.valueOf(selected_questions.size()));
} else {
// Go to Score Activity
Intent intent = new Intent(quizActivity.this, scoreActivity.class);
intent.putExtra("SCORE", String.valueOf(score) + "/" + String.valueOf(selected_questions.size()));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
}
Here's the function which sets the text and animation:
private void playAnim(final View view, final int value, final int viewNum) {
view.animate().alpha(value).scaleX(value).scaleY(value).setDuration(500)
.setStartDelay(100).setInterpolator(new DecelerateInterpolator())
.setListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
if (value == 0) {
switch (viewNum) {
case 0:
((TextView) view).setText(selected_questions.get(quesNum).getQuestion());
break;
case 1:
((TextView) view).setText(selected_questions.get(quesNum).getOption1());
break;
case 2:
((TextView) view).setText(selected_questions.get(quesNum).getOption2());
break;
case 3:
((TextView) view).setText(selected_questions.get(quesNum).getOption3());
break;
case 4:
((TextView) view).setText(selected_questions.get(quesNum).getOption4());
break;
}
if (viewNum != 0)
(view).setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#E99C03")));
playAnim(view, 1, viewNum);
}
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
}
Here's the code for the dialog boxes:
public void wrongDialog() {
final Dialog dialogWrong = new Dialog(quizActivity.this);
dialogWrong.requestWindowFeature(Window.FEATURE_NO_TITLE);
if (dialogWrong.getWindow() != null) {
ColorDrawable colorDrawable = new ColorDrawable(Color.TRANSPARENT);
dialogWrong.getWindow().setBackgroundDrawable(colorDrawable);
}
dialogWrong.setContentView(R.layout.dialog_wrong);
dialogWrong.setCancelable(false);
dialogWrong.show();
TextView wrongText = (TextView) dialogWrong.findViewById(R.id.wrongText);
Button buttonNext = (Button) dialogWrong.findViewById(R.id.dialogNext);
//OnCLick listener to go next que
buttonNext.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//This will dismiss the dialog
dialogWrong.dismiss();
//reset the color of buttons back to white
resetColor();
//Change question
changeQuestion();
}
});
}
public void correctDialog() {
final Dialog dialogCorrect = new Dialog(quizActivity.this);
dialogCorrect.requestWindowFeature(Window.FEATURE_NO_TITLE);
if (dialogCorrect.getWindow() != null) {
ColorDrawable colorDrawable = new ColorDrawable(Color.TRANSPARENT);
dialogCorrect.getWindow().setBackgroundDrawable(colorDrawable);
}
dialogCorrect.setContentView(R.layout.dialog_correct);
dialogCorrect.setCancelable(false);
dialogCorrect.show();
TextView correctText = (TextView) dialogCorrect.findViewById(R.id.correctText);
Button buttonNext = (Button) dialogCorrect.findViewById(R.id.dialogNext);
//OnCLick listener to go next que
buttonNext.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//This will dismiss the dialog
dialogCorrect.dismiss();
//reset the color of buttons back to white
resetColor();
//it will increment the question number
changeQuestion();
}
});
}
I have tried to explain it to my best ability though I would be glad to answer any additional information/code you may want. Also, this is the link for the project if you have the time to run it and understand the problem better.
I have checked your code. you have placed an addValueEventListener in setUpdates method. When you select an option, you update the firestore database by setting fields like total attempts. As a result, eventListener gets triggered and "selectQuestionSet" function is called.
Hence, every time you select an option, selectQuestionSet function is called. You should make sure that its called only once at the start.
The app has a MainActivity with 6 editText fields, and a button. There are 5 more activities, named Activity2, Activity3, etc. Now, When a user enters names in editText fields, and press a button, the app should find out how many editText fields are filled, and open the activity with a corresponding number in it's name.
Example:
If only one field is filled, a toast should appear, saying More players.
If two fields are filled, app opens Activity2.
If three fields are filled, app opens Activity3, etc.
Now, to the problem. I am missing something out, and can't find out what. Here is MainActivity.java
public class MainActivity extends AppCompatActivity {
private EditText editText1,editText2,editText3,editText4,editText5,editText6;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = findViewById(R.id.btn);
editText1 = findViewById(R.id.editText1);
editText2 = findViewById(R.id.editText2);
editText3 = findViewById(R.id.editText3);
editText4 = findViewById(R.id.editText4);
editText5 = findViewById(R.id.editText5);
editText6 = findViewById(R.id.editText6);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int filledFileds = countFilledFields();
Log.d("filled", String.valueOf(filledFileds));
Class newClass = MainActivity.class;
switch (filledFileds){
case 1:
Context context = getApplicationContext();
CharSequence text = "You need more players!";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
break;
case 2:
newClass = Activity2.class;
System.out.println("Activity2");
break;
case 3:
newClass = Activity3.class;
System.out.println("Activity3");
break;
case 4:
newClass = Activity4.class;
System.out.println("Activity4");
break;
case 5:
newClass = Activity5.class;
System.out.println("Activity5");
break;
case 6:
newClass = Activity6.class;
System.out.println("Activity6");
break;
default:
}
Intent intent = new Intent(MainActivity.this, newClass);
}
});
}
private int countFilledFields() {
ArrayList<EditText> editTexts = new ArrayList<>();
editTexts.add(editText1);
editTexts.add(editText2);
editTexts.add(editText3);
editTexts.add(editText4);
editTexts.add(editText5);
editTexts.add(editText6);
int filledNumber = 0;
for(int i = 0;i < editTexts.size() ;i++){
if(editTexts.get(i).getText()!=null && !editTexts.get(i).getText().toString().matches("")){
filledNumber += 1;
}
}
return filledNumber;
}
}
The log shows the exact number, something is not working...
Here is your click listener, with the switch omitted for brevity:
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int filledFileds = countFilledFields();
Log.d("filled", String.valueOf(filledFileds));
Class newClass = MainActivity.class;
switch (filledFileds){
...
}
Intent intent = new Intent(MainActivity.this, newClass);
}
The problem is at the very end: you've created an Intent object ... but you're not doing anything with it. Probably you have just forgotten a startActivity() call:
Intent intent = new Intent(MainActivity.this, newClass);
startActivity(intent);
Also, looking this over, you have a problem with the case where the user only enters one EditText. As written, you'll still try to start a new activity (you'll just start a new copy of the same MainActivity, which is probably a bad idea). A better idea would be to only start the new activity if the user fills out enough EditTexts:
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int filledFileds = countFilledFields();
Log.d("filled", String.valueOf(filledFileds));
Class newClass = null;
switch (filledFileds){
...
}
if (newClass != null) {
Intent intent = new Intent(MainActivity.this, newClass);
startActivity(intent);
}
}
You're missing one thing:
startActivity(intent);
I'm new to android development and I am creating an android application that works like "4 Pics 1 Word" for my project. I'm having difficulties in storing ArrayList in SharedPreferences or in the internal storage of the android phone. The reason why is because I am randomizing the next activity using random generator and ArrayList. Any suggestions or ideas that my help my case? Thank you in advance! I've been stuck here for hours now.
This is my MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
Button btnStart;
Context context;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnStart = (Button) findViewById(R.id.btnStart);
btnStart.setOnClickListener(this);
}
#Override
public void onClick(View v) {
// We are creating a list, which will store the activities that haven't been opened yet
ArrayList<Class> activityList = new ArrayList<>();
activityList.add(first.class);
activityList.add(second.class);
activityList.add(third.class);
activityList.add(fourth.class);
activityList.add(fifth.class);
Random generator = new Random();
int number = generator.nextInt(5) + 1;
Class activity = null;
// Here, we are checking to see what the output of the random was
switch(number) {
case 1:
activity = first.class;
// We are adding the number of the activity to the list
activityList.remove(first.class);
break;
case 2:
activity = second.class;
activityList.remove(second.class);
break;
case 3:
activity = third.class;
activityList.remove(third.class);
break;
case 4:
activity = fourth.class;
activityList.remove(fourth.class);
break;
default:
activity = fifth.class;
activityList.remove(fifth.class);
break;
}
// We use intents to start activities
Intent intent = new Intent(getBaseContext(), activity);
// `intent.putExtra(...)` is used to pass on extra information to the next activity
intent.putExtra("ACTIVITY_LIST", activityList);
startActivity(intent);
}
}
And here's my first activity:
public class first extends AppCompatActivity implements View.OnClickListener{
EditText etAnswer;
Button btnGo;
Context context;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
etAnswer = (EditText) findViewById(R.id.etAnswer);
btnGo = (Button) findViewById(R.id.btnGo);
btnGo.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.btnGo:
String answer = etAnswer.getText().toString();
if(answer.equals("Jose Rizal") || answer.equals("jose rizal") || answer.equals("Rizal") || answer.equals("rizal") ){
AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
dlgAlert.setMessage("The famous Rizal monument in Luneta was not the work of a Filipino but a Swiss sculptor named Richard Kissling?" +
"Source: http://www.joserizal.ph/ta01.html");
dlgAlert.setTitle("Did you know that ...");
dlgAlert.setPositiveButton("Next",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
ArrayList<Class> activityList = new ArrayList<>();
Bundle extras = getIntent().getExtras();
activityList = (ArrayList<Class>) extras.get("ACTIVITY_LIST");
if(activityList.size() == 0) {
Context context = getApplicationContext();
CharSequence last = "Congratulations! You just finished the game! Please wait for the next update!";
int durationFinal = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context, last, durationFinal);
toast.show();
} else {
// Now, the random number is generated between 1 and however many
// activities we have remaining
Random generator = new Random();
int number = generator.nextInt(activityList.size()) + 1;
Class activity = null;
// Here, we are checking to see what the output of the random was
switch(number) {
case 1:
// We will open the first remaining activity of the list
activity = activityList.get(0);
// We will now remove that activity from the list
activityList.remove(0);
break;
case 2:
// We will open the second remaining activity of the list
activity = activityList.get(1);
activityList.remove(1);
break;
case 3:
// We will open the third remaining activity of the list
activity = activityList.get(2);
activityList.remove(2);
break;
case 4:
// We will open the fourth remaining activity of the list
activity = activityList.get(3);
activityList.remove(3);
break;
default:
// We will open the fifth remaining activity of the list
activity = activityList.get(4);
activityList.remove(4);
break;
}
// Note: in the above, we might not have 3 remaining activities, for example,
// but it doesn't matter because that case wouldn't be called anyway,
// as we have already decided that the number would be between 1 and the number of
// activities left.
// Starting the activity, and passing on the remaining number of activities
// to the next one that is opened
Intent intent = new Intent(getBaseContext(), activity);
intent.putExtra("ACTIVITY_LIST", activityList);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
}
});
dlgAlert.setCancelable(true);
dlgAlert.create().show();
}else{
Context context = getApplicationContext();
CharSequence text = "Wrong! Try Again.";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
break;
}
}
}
Okay, this is a horrible hack and I don't endorse it in any way, but since you are so close to finishing your app, I propose a workaround:
Instead of storing an ArrayList<Class> in your SharedPreferences (which is impossible), store a HashSet<String> containing the fully qualified names of your classes via putStringSet().
In order to get the String representations of the fully qualified names of your classes you need to call getName(), e.g. first.class.getName().
Then, you can get your Set<String> from SharedPreferences using getStringSet() and create a Class instance for each String in that set via Class.forName().
what I am trying to do is to call on one of three sounds on this button click. The sounds are in a preference screen. On the button click it is also supposed to display an animation. What is currently happening is I will click on the button and everything will work perfectly, but then I stop the animation and sound the second time I click the button. When I click the button again to start back up the animation and sound I cant hear the sound, but the animation still works. I honestly have no idea whats wrong. Here's my code...
public void buttonClick() {
imgView = (ImageView) findViewById(R.id.imageView);
button = (Button) findViewById(R.id.button);
blade = (ImageView)findViewById(R.id.imageView4);
final Animation animRotate = AnimationUtils.loadAnimation(this, R.anim.rotate);
1stSound = MediaPlayer.create(this, R.raw.file.mp3);
2ndSound = MediaPlayer.create(this, R.raw.file.mp3);
3rdSOund = MediaPlayer.create(this, R.raw.file.mp3);
button.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
SharedPreferences getPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
boolean option1 = getPrefs.getBoolean("alternate", false);
boolean option2 = getPrefs.getBoolean("white", false);
boolean option3 = getPrefs.getBoolean("standard",false);
if (blade.getAnimation() == null) {
// no animation, start it
if (option1 == true){
1stSound.start();
blade.startAnimation(animRotate);
} else if (option2 == true){
3rdSound.start();
blade.startAnimation(animRotate);
} else if (option3 == true) {
2ndFan.start();
blade.startAnimation(animRotate);
}
} else {
//animation is showing, stop it
blade.clearAnimation();
3rdSound.stop();
2ndSound.stop();
1stSound.stop();
}
current_image_index++;
current_image_index = current_image_index % images.length;
imgView.setImageResource(images[current_image_index]);
imgView.invalidate();
}
}
);
}
Take a look at the MediaPlayer state diagram. After calling stop() you'll need to also call prepare() (or prepareAsync()) to be able to play it again.
In short, do this:
3rdSound.stop();
3rdSound.prepareAsync();
2ndSound.stop();
2ndSound.prepareAsync();
1stSound.stop();
1stSound.prepareAsync();
Can anyone help me work out where I'm going wrong here. On the button click the media player plays one of the mfiles at random and I'm trying to set a textview depending on which file was played. Currently the setText if statements only match the audio playing half the time. Really not sure where I'm going wrong here.
private final int SOUND_CLIPS = 3;
private int mfile[] = new int[SOUND_CLIPS];
private Random rnd = new Random();
MediaPlayer mpButtonOne;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mfile[0] = R.raw.one;
mfile[1] = R.raw.two;
mfile[2] = R.raw.three;
//Button setup
Button bOne = (Button) findViewById(R.id.button1);
bOne.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final TextView textOne = (TextView)findViewById(R.id.textView1);
mpButtonOne = MediaPlayer.create(MainActivity.this, mfile[rnd.nextInt(SOUND_CLIPS)]);
if (mpButtonOne==null){
//display a Toast message here
return;
}
mpButtonOne.start();
if (mfile[rnd.nextInt(SOUND_CLIPS)] == mfile[0]){
textOne.setText("one");
}
if (mfile[rnd.nextInt(SOUND_CLIPS)] == mfile[1]){
textOne.setText("two");
}
if (mfile[rnd.nextInt(SOUND_CLIPS)] == mfile[2]){
textOne.setText("three");
}
mpButtonOne.setOnCompletionListener(new soundListener1());
{
}
So just to clarify the problem I am having is that the setText only matches the audio occasionally, not on every click. The rest of the time it displays the wrong text for the wrong audio.
You are choosing another random file
mfile[rnd.nextInt(SOUND_CLIPS)]
set that to a variable in onClick() then check against that variable in your if statement
public void onClick(View v) {
int song = mfile[rnd.nextInt(SOUND_CLIPS)];
final TextView textOne = (TextView)findViewById(R.id.textView1);
mpButtonOne = MediaPlayer.create(MainActivity.this, song);
if (song == mfile[0]){
textOne.setText("one");
}
Edit
To make it a member variable so you can use it anywhere in the class, just declare it outside of a method. Usually do this before onCreate() just so all member variables are in the same place and it makes your code more readable/manageable.
public class SomeClass extends Activity
{
int song;
public void onCreate()
{
// your code
}
then you can just initialize it in your onClick()
public void onClick(View v) {
song = mfile[rnd.nextInt(SOUND_CLIPS)];
final TextView textOne = (TextView)findViewById(R.id.textView1);
mpButtonOne = MediaPlayer.create(MainActivity.this, song);