I made a keyboard out of buttons. When you press the wrong button, it creates a animation, same for the correct button. All of this works well. The problem is when the user press buttons like a maniac, just like in a fighting videogame. Then the animations will stop before finishing. To prevent this, I tried to set a FLAG_NOT_TOUCHABLE at the beginning of the moethod. But it doesn't really have any effect if you press buttons too fast.
public void onClickKeyboard (View view) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
if (myAnswers[gameState].charAt(s.length())== ' ') {
s = s + " ";
}
s = s + view.getTag().toString();
if (s.equalsIgnoreCase(myAnswers[gameState].substring(0,s.length()))){
ViewCompat.animate(view).alpha(0).setDuration(200).start();
// view.setVisibility(View.INVISIBLE);
preguntaViewInput = myQuestions[gameState] + s.substring(0,1).toUpperCase()+s.substring(1).toLowerCase();
preguntaView.setText(preguntaViewInput, TextView.BufferType.SPANNABLE);
} else {
s = s.substring(0, s.length() - 1); // sense aixo lapp crash quan sapreten 10 lletres incorrectes seguides
b = (Button) view;
view.setBackgroundResource(R.drawable.button_keyboard_wrong);
ViewCompat.animate(view).rotationBy(360).scaleY((float) 0.2).scaleX((float) 0.2).setDuration(200).withEndAction(new Runnable() {
#Override
public void run() {
ViewCompat.animate(b).rotationBy(360).scaleY((float) 1).scaleX((float) 1).setDuration(200).withEndAction(new Runnable() {
#Override
public void run() {
b.setBackgroundResource(R.drawable.buttonpurchase);
}
});
}
});
}
if (s.toString().equalsIgnoreCase(myAnswers[gameState])) {
Spannable ss = (Spannable) preguntaView.getText();
ss.setSpan(new ForegroundColorSpan(Color.GREEN), myQuestions[gameState].length(), s.length() + myQuestions[gameState].length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
buttonNext.setBackgroundColor(Color.argb(64, 0, 250, 0));
String nextString = "correct";
buttonNext.setText(nextString);
buttonNext.setAlpha(0);
buttonNext.setVisibility(View.VISIBLE);
ViewCompat.animate(buttonNext).alpha(1).setDuration(300).start();
}
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
}
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.
I started learning Android Development. I was building a basic addition game, where user has to click on button which shows the addition of two number. There are four Textviews. First one give the time limit for each question to answer. Second gives the question for the user. Third one gives the current score of the user and the last one gives whether the chosen open is correct or incorrect.
Everything is working except the First Button. Every time when the button is pressed the the counting takes very fast.
// When First button is pressed
public void onClickButton1(View view) {
correctIncorrect.setVisibility(View.VISIBLE);
if (Integer.parseInt(button1.getText().toString())==sum) {
correctIncorrect.setText("Correct");
correctUpdater();
}
else {
correctIncorrect.setText("Incorrect");
inCorrectUpdater();
}
}
//Similar to all the buttons
//To Update the score
public void correctUpdater() {
n++;
yourScore++;
score.setText(Integer.toString(yourScore) + "/" + Integer.toString(n));
update();
}
public void inCorrectUpdater() {
n++;
score.setText(Integer.toString(yourScore) + "/" + Integer.toString(n));
update();
}
// To update the timer
//=======================================================================//
public void resetTimer() {
timer.setText(Integer.toString(temp)+"s");
if (temp == 0) {
inCorrectUpdater();
update();
}
else {
timeUpdater();
}
}
public void timeUpdater() {
Handler timeHandler = new Handler();
Runnable timeRunnable = new Runnable() {
#Override
public void run() {
temp--;
resetTimer();
}
};
timeHandler.postDelayed(timeRunnable,1000);
}
//=================================================================//
// Updater function
public void update() {
correctIncorrect.setVisibility(View.INVISIBLE);
Random random = new Random();
int a = random.nextInt(21);
int b = random.nextInt(21);
question.setText(Integer.toString(a) + " + " + Integer.toString(b));
Log.i("info", "onCreate: a = " + a);
Log.i("info", "onCreate: b = " + b);
sum = a+b;
Log.i("info", "onCreate: sum = " + sum);
int whichButton = random.nextInt(4);
Log.i("info", "onCreate: random button is " + whichButton);
values.clear();
for (int i = 0; i< 4; i++) {
if (i == whichButton) {
values.add(sum);
}
else {
values.add(random.nextInt(50));
}
Log.i("info", "onCreate: value[" + i + "] = " + values.get(i));
}
button1.setText(Integer.toString(values.get(0)));
button2.setText(Integer.toString(values.get(1)));
button3.setText(Integer.toString(values.get(2)));
button4.setText(Integer.toString(values.get(3)));
temp = 10;
resetTimer();
}
Am I using the Handler incorrectly? What can I do?
Am I using the Handler incorrectly? What can I do?
A better way to do this is CountDownTimer, by using this you won't have to deal with Handler youself and you also have a provision to cancel a running CountDownTimer.
Everything is working except the First Button
Not really sure what can cause different behaviours on click of buttons with the same code in onClick(). But you can consider using the same onClickListener for all the 4 buttons, in this way :
View.OnClickListener myListener = new View.OnClickListener() {
#Override
public void onClick(View button) {
correctIncorrect.setVisibility(View.VISIBLE);
if (Integer.parseInt(button.getText().toString())==sum) {
correctIncorrect.setText("Correct");
correctUpdater();
}
else {
correctIncorrect.setText("Incorrect");
inCorrectUpdater();
}
}
};
button1.setOnClickListener(myListener);
button2.setOnClickListener(myListener);
button3.setOnClickListener(myListener);
button4.setOnClickListener(myListener);
This will ensure that clicking any of the 4 buttons will have a same behavior (depending on the answer of course)
In case you get confused with the onTick() method on CountDownTimer, you can use modified CountDownTimer which will ensure that the timer doesn't miss any ticks.
I have a problem related click count. The problem is, I can't stop click when a number a click is given.
For example, I allow users to click a button 3 times, if clicks reached 3 times, then stop count, and do what I want.
This is my code I have used.
private int clickcount = 3;
#Override
public void onClick(View v) {
// Do button click handling here
if ( posisi2==getAdapterPosition() ) {
clickcount--;
tombolbaca.setText("Baca " + clickcount + "x");
// try to stop count but it can't, computer still counting
if (clickcount == 3)
{
mTitle.setVisibility(View.GONE);
rl2.setVisibility(View.GONE);
}
} // adapter
} // onClick
I think the trigger to do something might be when the click count is zero, not three:
if (clickcount == 0) {
mTitle.setVisibility(View.GONE);
rl2.setVisibility(View.GONE);
}
It isn't clear whether the above if statement belongs nested inside the outer if, or if it should be at the method level of onClick().
Note: We could have written if (clickCount <= 0), but there may not be a need to do this (nor may it be desirable), since after you have changed the visibility of those elements to GONE once, you don't need to do it again.
Make this Change,
private int clickcount = 3;
#Override
public void onClick(View v) {
// Do button click handling here
if ( posisi2==getAdapterPosition() ) {
clickcount--;
tombolbaca.setText("Baca " + clickcount + "x");
// try to stop count but it can't
if (clickcount <=0) <== make this change
{
mTitle.setVisibility(View.GONE);
rl2.setVisibility(View.GONE);
}
} // adapter
}
try this
private int clickcount = 3;
#Override
public void onClick(View v) {
// Do button click handling here
if ( posisi2==getAdapterPosition() ) {
clickcount--;
tombolbaca.setText("Baca " + clickcount + "x");
// try to stop count but it can't, computer still counting
if (clickcount == 0)
{
mTitle.setVisibility(View.GONE);
rl2.setVisibility(View.GONE);
}
} // adapter
} // onClick
private int clickcount = 0;
#Override
public void onClick(View v) {
// Do button click handling here
if ( clickcount<3 ) {
clickcount++;
tombolbaca.setText("Baca " + clickcount + "x");
}
//Count stops here..
else
{
mTitle.setVisibility(View.GONE);
rl2.setVisibility(View.GONE);
}
}
}
I thought I'd figured out this problem, by making a function showMessage, which can take a Runnable as an argument and run that piece of code if that particular button has been pressed.
Now I find the problem that I need to wait on several inputs before running the code. I essentially go through a bunch of questions which are added dynamically, so I don't know how many there'll be. When the users presses "Submit", it checks if any that are "soft" required and if there's no answer pops up with the message "There isn't an answer, would you like to continue anyway?".
The way I thought I could handle this is by counting the amount of questions which are like that, assigning them an Enumerator with a boolean variable to say if they're INPROGRESS or COMPLETE. I then had the Runnable to set that particular question's Progress state to COMPLETE and if they click "No", then to set the boolean variable to false.
Since the the dialogs are launched asynchronously, I can't just do an if statement, so I did a while any were still in progress. Buuuuuut! When I click submit now, it just freezes. I'm guessing because it's stuck at the while loop, whilst also not launching the dialogs asynchronously. I think it has to "complete" the code before launching them?
My code for reference to the points made above, this is all in the onClick of the submit button:
//Prep work
final ArrayList<Result> results = new ArrayList<Result>();
for (BaseQuestion q : questionViews)
{
if (q.requiredSoft)
{
results.add(Result.INPROGRESS);
}
}
final int[] i = {0};
//Validation checks
Boolean allOk = true;
for (BaseQuestion questionView : questionViews)
{
if (questionView.isRequiredHard())
{
if (questionView.getResponse().isEmpty())
{
Utils.showMessage("You have to fill in '" + questionView.getQuestionText() + "'", v.getContext());
allOk = false;
break;
}
}
else if (questionView.isRequiredSoft())
{
if (questionView.getResponse().isEmpty())
{
Runnable isOk = new Runnable()
{
#Override
public void run()
{
results.get(i[0]).value = 1;
results.get(i[0]).result = true;
i[0]++;
}
};
Runnable isNotOk = new Runnable() {
#Override
public void run() {
results.get(i[0]).value = 1;
i[0]++;
}
};
Utils.showMessage("You've not filled in '" + questionView.getQuestionText() + "'. Do you wish to continue?", v.getContext() , isOk, "Yes", isNotOk, "No");
}
}
else
{
}
}
if (allOk)
{
for (Result result : results)
{
while (result == Result.INPROGRESS)
{
}
if (!result.result)
{
allOk = false;
}
}
if (allOk)
{
submitQuestionnaire();
}
}
you can put all soft check into a runnable linkļ¼ and do the submit action at the last runnable. I not have a IDE now, see following logic
on submit button clicked{
allOk = true;
foreach(question in questions){
if(question require hard && question is empty){
allOk = false;
break;
}
}
if(allOk){
boolean needMoreConfirm = false;
for(int i = 0; i < questions.length; i++){
if(question require soft && question is empty){
Runnable isOk = new MyRunnable(i + 1, questions);/*begin check from next question*/
Utils.showMessage("your question",
new MyRunnable(i + 1)/* check next soft */,
null/*do nothing, wait another submit click*/);
needMoreConfirm = true;
break;
}
}
if(!needMoreConfirm){
do real submit here
}
}
}
class MyRunnable extends Runnable{
protected int mIdx;
Question[] mQuestions;
public MyOkRunnalbe(int idx, Question[] qs){mIdx = idx; mQuestions = qs;}
public void run(){
boolean needMoreConfirm = false;
for(int i = mIdx; i < questions.length; i++){
if(mQuestions[i] is empty && mQuestions[i] is soft){
Utils.showMessage("your question",
new MyRunnable(i + 1)/* begin to check from next soft */,
null/*do nothing, wait another submit click*/);
needMoreCOnfirm = true;
break;
}
}
if(!needMoreConfirm){
do real submit here
}
}
}
SOLVED
If I gave myself another half an hour to tinker, would've been fine! I whacked the checking code in a separate thread, this means it wasn't hanging.
But then I had the problem of the result not changing state. So instead of doing my for loop as a for each item loop, I did it as an index, as it wasn't changing the state when it kept checking.
As shown below:
new Thread(new Runnable() {
#Override
public void run() {
if (allOk[0])
{
for (int i=0; i<results.size(); i++)
{
while (results.get(i) == Result.INPROGRESS)
{
}
if (!results.get(i).result)
{
allOk[0] = false;
}
}
if (allOk[0])
{
submitQuestionnaire();
}
}
}
}).start();
I am new to android and I need help.
I want my image which is falling from the top of screen to get invisible when I click a button on the keyboard which has the same tag as the image. Although I have implemented the above points a problem arises when I want to show the next animation again immediately after the previous image gets invisible. This is my code for animation.
public void startAnimation(final ImageView aniView)
{
animator= ValueAnimator.ofFloat(0 ,.85f);
animator.setDuration(Constants.ANIM_DURATION);
animator.setInterpolator(null);
//generation of random values
Random rand = new Random();
index = rand.nextInt((int) metrics.widthPixels);
//for debugging
i = Integer.toString(index);
Log.e(" index is :", i);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
#Override
public void onAnimationUpdate(ValueAnimator animation)
{
float value = ((Float) (animation.getAnimatedValue())).floatValue();
aniView.setTranslationX(index);
aniView.setTranslationY((height+ (50*mScale))*value);
//aniView.setTranslationY(height);
}
});
animator.start();
}
//keyboard methods goes here
#Override
public void onClick(View v)
{
gettext(v);
}
private void gettext(View v)
{
try
{
String b = "";
b = (String) v.getTag();
String img_val = map.get(b);
int imgid = (Integer) imageView.getTag();
Log.e("img values=:", img_val+" "+imgid);
if(img_val.equals(ALPHABETS[imgid]))
{
imageView.setVisibility(View.INVISIBLE);
animator.start();
//trying to start the animation again
}
I am using the animation.addupdateListener to call the animation again and again but it only calls when the .ofFloat finishes on the value. When I click on the appropriate button the animation gets invisible but the next animation starts again after the same time. I want to start it immediately.
Basically calling start() again won't work. you need to "reset" the animation somehow, did you try calling end() before ?
animator.end();
animator.start();