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.
Related
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);
}
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);
}
}
}
Simple problem to fix (I hope)...can't figure it out myself though.
I need one radio button to be un-checked or un-selected when the other is checked/selected.
Here is my code. Right now the app works fine but when the user goes to change the selection the first button doesn't clear:
Button convert = (Button) findViewById(R.id.btnConvert);
convert.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
weightEntered=Double.parseDouble(weight.getText().toString());
DecimalFormat tenth = new DecimalFormat("#.#");
if (lbToKilo.isChecked()) {
if (weightEntered <= 500) {
convertedWeight = weightEntered / conversionRate;
result.setText(tenth.format(convertedWeight) + " kilograms");
} else {
Toast.makeText(getApplicationContext(), "Pounds must be less than 500.", Toast.LENGTH_LONG).show();
}
}
if (kiloToLb.isChecked()) {
if (weightEntered <= 225) {
convertedWeight = weightEntered * conversionRate;
result.setText(tenth.format(convertedWeight) + " pounds");
} else {
Toast.makeText(getApplicationContext(), "Kilos must be less than 225.", Toast.LENGTH_LONG).show();
}
}
}
});
You simply need to group your RadioButtons in a RadioGroup.
That's all you need to get some mutually exclusive RadioButtons.
For your reference: http://developer.android.com/guide/topics/ui/controls/radiobutton.html
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();
SEE REVISION AT BOTTOM This is a fight card, so it has two people fighting one another, a red vs blue. It has to be a dynamic list that is populated information from parse.com. The first Query is fightOrder. This is a class on Parse.com that has two objectId's on a row. The redCorner and blueCorner find this information in my database (also on parse.com) and display the information accordingly. My problem, is my progressDialog box appears, and it never goes away. My list is never populated. I tried doing it without the dialog box, and populating my list with ever query and had same results.
NOTE: the list is working properly. This is a list I have used successfully before when I would load my information differently. I am just changing the way I load information because I need to have a database of all fighters, and load my fight card from that list.
NOTE: GetCallBack and FindCallBack are asynchronous, that is why this is an odd loop. I have to wait for the done().
Here is the java
public class databaseFightCard extends Activity {
int I;
int size;
private HomeListAdapter HomeListAdapter;
private ArrayList<HomeItem> HomeItemList;
private SeparatedListAdapter adapter;
//this int is to test for main and coMain events. If one is TRUE, It will assign the array position to main or coMain.
int main, coMain;
ParseQuery<ParseObject> blueCorner = ParseQuery.getQuery("FightersDB");
ParseQuery<ParseObject> redCorner = ParseQuery.getQuery("FightersDB");
String name1, name2;
List<String> red = new ArrayList<String>();
List<String> blue = new ArrayList<String>();
private ListView listView;
ProgressDialog progressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_list);
progressDialog = ProgressDialog.show(this, "", "Loading bout...", true);
initialization();
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
HomeItem homeItem = (HomeItem) adapter.getItem(position);
AlertDialog.Builder showFighter = new AlertDialog.Builder(databaseFightCard.this, android.R.style.Theme_DeviceDefault_Dialog);
showFighter.setTitle(homeItem.getHomeItemLeft().toString() + " and " + homeItem.getHomeItemRight().toString());
showFighter.setMessage("166 - 165\nLogan Utah - Richmond Utah");
showFighter.setPositiveButton("DONE", null);
showFighter.setNegativeButton("Cancel", null);
AlertDialog dialog = showFighter.show();
TextView messageView = (TextView) dialog.findViewById(android.R.id.message);
messageView.setGravity(Gravity.CENTER);
Toast.makeText(getBaseContext(), homeItem.getHomeItemLeft().toString() + " " + homeItem.getHomeItemRight().toString(), Toast.LENGTH_LONG).show();
System.out.println("Selected Item : " + homeItem.getHomeItemID());
}
});
HomeListAdapter = new HomeListAdapter(getApplicationContext(), 0, HomeItemList);
//find the fight card, and read the ids
ParseQuery<ParseObject> fightOrder = ParseQuery.getQuery("FightCard");
fightOrder.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> parseObjects, ParseException e) {
if (e == null) {
size = parseObjects.size();
int i = 0;
while (i < size) {
if (parseObjects.get(i).getBoolean("main")) {
main = i;
}
if (parseObjects.get(i).getBoolean("coMain")) {
coMain = i;
}
red.add(i, parseObjects.get(i).getString("redCorner"));
blue.add(i, parseObjects.get(i).getString("blueCorner"));
i++;
}
displayRed();
} else {
e.printStackTrace();
}
}
});
}
private void displayRed() {
adapter = new SeparatedListAdapter(this);
//find one fighter at a time. in the done() method, start the second fighter.
redCorner.getInBackground(red.get(I), new GetCallback<ParseObject>() {
#Override
public void done(ParseObject parseObject, ParseException e) {
if (e == null) {
HomeItemList = new ArrayList<HomeItem>();
HomeItem homeItem = new HomeItem();
homeItem.setHomeItemID(I);
name1 = parseObject.getString("Name");
homeItem.setHomeItemLeft(name1);
HomeItemList.add(homeItem);
if (HomeListAdapter != null) {
if (I == main) {
adapter.addSection(" MAIN EVENT ", HomeListAdapter);
} else if (I == coMain) {
adapter.addSection(" Co-MAIN EVENT ", HomeListAdapter);
} else {
adapter.addSection(" FIGHT CARD ", HomeListAdapter);
}
}
displayBlue();
} else {
e.printStackTrace();
}
I++;
while (I < size){
displayRed();
}
if (size == I) {
listView.setAdapter(adapter);
progressDialog.dismiss();
}
}
});
}
private void displayBlue() {
//find the red fighters then call the dismiss();
blueCorner.getInBackground(blue.get(I), new GetCallback<ParseObject>() {
#Override
public void done(ParseObject parseObject, ParseException e) {
if (e == null) {
HomeItemList = new ArrayList<HomeItem>();
HomeItem homeItem = new HomeItem();
homeItem.setHomeItemID(I);
name2 = parseObject.getString("Name");
homeItem.setHomeItemLeft(name2);
HomeItemList.add(homeItem);
if (HomeListAdapter != null) {
if (I == main) {
adapter.addSection(" MAIN EVENT ", HomeListAdapter);
} else if (I == coMain) {
adapter.addSection(" Co-MAIN EVENT", HomeListAdapter);
} else {
adapter.addSection(" FIGHT CARD ", HomeListAdapter);
}
}
} else {
e.printStackTrace();
}
//if it is done running through all the IDS, set the listView, and dismiss the dialog.
I++;
while (I < size){
displayRed();
}
if (size == I) {
listView.setAdapter(adapter);
progressDialog.dismiss();
}
}
});
}
private void initialization() {
listView = (ListView) findViewById(R.id.Listview);
}
LogCat
java.lang.RuntimeException: This query has an outstanding network
connection. You have to wait until it's done.
That is pointing to this line:
while (I < size){
displayRed();
}
EDIT
I believe that it is the async tasks that are causing this.
On a previous build: I would call for one line item at a time, add it to my list, repeat until finished, then display list.
On the this build: I want to call for redCorner add it to my list, call blueCorner add it to the same line, repeat until finished, then display the list. Here is what it would look like (previous build):
Revised My question is still unanswered. Maybe I need to simplify it. I will have +-20 objectId's from one class. I took out all the code that is irrelevant. Still getting unexpected results with this code.
redCorner.getInBackground(red.get(i), new GetCallback<ParseObject>() {
#Override
public void done(ParseObject parseObject, ParseException e) {
if (e == null) {
Log.d("NAME " + i, name1 + " ");
i++;
while (i < size) {
redCorner.cancel();
displayRed();
}
if (i == size) {
progressDialog.dismiss();
}
} else {
e.printStackTrace();
}
}
});
This is yet another case of not understanding the nature of Async coding (I've seen a lot of questions with the same issue).
In your case you are calling the displayRed() method that fires off some async code, then returns.
Here's how your code might run:
First call to displayRed() (dr1)
(dr1) Async redCorner.getInBackground(..) (async1) started
(dr1) returns
.. some time passes ..
(async1) getInBackground(..) call returns with data, runs code block
calls displayBlue() (db1)
(db1) blueCorner.getInBackground(..) (async2) started
(db1) returns
begins the while loop
calls displayRed() (dr2)
(dr2) Async redCorner.getInBackground(..) (async3) started
(dr2) nothing has touched I yet, tries to start another async redCorner.getInBackgroud(..) (async4)
ERROR
You're writing your code as if the async blocks are running sync instead. Keep in mind that getInBackground means "make a web call to get this data, and when something happens (error or success) run this block of code I'm giving you, possibly on another thread".
Think about the order you want to achieve things, realise that you're asking it to start a process that takes some time, and adjust your code accordingly.