Android: Dealing with blank/invalid entry? - android

In the Following code I am taking an answer from a user for a mathematical question. When the answer is entered the question then updates to a new one.
How would I add validation so that when a user enters letters or hits submit without entering an answer, the question just stays the same and allows the user to enter again. At the minute, when that happens the app crashes. (note: Main functionality I am refering to occurs in onClick).
public class PracticeTest extends Activity implements View.OnClickListener{
//declare vars
int multiplier;
int[] results=new int[12];
int numberPassed;
TextView question;
EditText answer;
int score;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.practicetest);
// This declares and int variable, assigns it an int value from the
// calling Intent if its not there it is defaulted to 0
numberPassed = getIntent().getIntExtra("convertedNumber2", 0);
//setting up vars(possibly do this in different method?
Button submit = (Button) findViewById(R.id.btnGoPractice2); //declared here as it is only used once
answer = (EditText) findViewById(R.id.etEnterNumberPractice2);
question = (TextView) findViewById(R.id.tvTopPractice2);
//setting listeners
submit.setOnClickListener(this);
updateQuestion();
}
public void onClick(View view) {
// sets text view equal to whats typed in in editText
final String entry = answer.getText().toString();
// convert from string value to int
int a = Integer.parseInt(entry); //note: maybe change name
results[multiplier-1]=a;
score++;//Irrelevant?
if(multiplier<12){
//called after an answer is given
updateQuestion();
} else{
//System.out.println(score);
Intent intent = new Intent(this, Results.class);
intent.putExtra("results", results);
intent.putExtra("numberPassed", numberPassed);
this.startActivity(intent);
}
}
public void updateQuestion(){
multiplier++;
//string to hold quest
String q= numberPassed + "x" + multiplier + "=";
question.setText(q);
answer.setText("");
}
}

So entry is the answer you get? Maybe try regex, you can use this code after submitting the answer or you could check this when a user edits the EditText. The last thing can be done with a TextWatcher, but that would make it a bit more complicated than necessary.
if(entry.matches("[0-9]+") {
// new question
} else {
// warning no valid answer
}
If you want your users only have the option to input numbers. You should set in your EditText:
android:inputType="number"

Related

How do I pass a variable from one Class to another in Android

How do I assign user input (from a TextView) into a variable then call that variable in another class?
From my MainActivity, I have the followingn where user input is taken:
Button confirm;
EditText inputField;
String typedChar;
char[] cars = typedChar.toCharArray();
#SuppressLint("WrongViewCast")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
confirm = (Button)findViewById(R.id.btConfirmInput);
inputField = (EditText) findViewById(R.id.etInputChars);
confirm.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
typedChar = inputField.getText().toString();
}
}
);
I'm trying to store the input and convert it to char
String typedChar;
char[] cars = typedChar.toCharArray();
Now I want to use cars in another class in the following method which print to a custom view:
private void drawText() {
for (int i = 0; i < txtPosByColumn.length; i++) {
canvas.drawText("" + cars[RANDOM.nextInt(cars.length)], i * fontSize, txtPosByColumn[i] * fontSize, paintTxt);
if (txtPosByColumn[i] * fontSize > height && Math.random() > 0.975) {
txtPosByColumn[i] = 0;
}
txtPosByColumn[i]++;
}
I'm however able to assign hardcoded value to cars like bellow:
private char[] chars = "010101".toCharArray();
but I want it come from user input
Anyone please kindly advice, guide. I know I'm doing things wrong but can't figure out...
PS: Noob here
You put your variable in an Intent like this:
confirm.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
typedChar = inputField.getText().toString();
char[] chars = typedChar.toCharArray();
Intent intent = new Intent(MyCurrentActivity.this, MySecondActivity.class);
intent.putExtra("somethingWithARelevantName", chars);
startActivity(intent);
}
}
);
And you get it in your second activity like this:
Intent intent = getIntent();
char[] chars = intent.getExtras().getCharArray("somethingWithARelevantName");
edit: if want your variable in a class that is not an activity, you can pass it in the constructor:
class MyClass{
char[] chars;
MyClass(char[] chars){
this.chars = chars;
}
}
You should specified what is the type of that other class.
If it is a simple Java class you can pass it as a field to your drawText(char[] array);
If however you are dealing with activities the :
In your first activity, before launching the other activity, use Extra intent to send data between activities as the answer show before.

I need an efficient way of duplicating an existing activity in Android Studio

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import org.w3c.dom.Text;
public class MainActivity extends AppCompatActivity {
public int currentQuestion;
public String[] questions;
public String[] answers;
int score = 0;
Button answerButton;
Button questionButton;
Button homeButton;///NEW ****
public TextView questionView;
public TextView answerView;
public EditText answerText;
public TextView scoreText;
public void main() {
questions = new String[]{"Type Yes", "Type No", "Type And", "Type The"}; /*Array of Hard Coded Questions*/
answers = new String[]{"Yes", "No", "And", "The",}; /*Array of Hard Coded Answers to indexed to match the questions*/
currentQuestion = -1; /*This will index the questions to be used*/
answerButton = (Button) findViewById(R.id.AnswerButton);
questionButton = (Button) findViewById(R.id.QuestionButton);
homeButton = (Button) findViewById(R.id.HomeButton);
questionView = (TextView) findViewById(R.id.QuestionTextView);
answerView = (TextView) findViewById(R.id.AnswerTextView);
answerText = (EditText) findViewById(R.id.AnswerText);
scoreText = (TextView) findViewById(R.id.ScoreText);
///Check the user inserted answer string against the correct or incorrect answers.... NEEDS VALIDATION....
answerButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
checkAnswer();
}
});
///Skips to the next questions
questionButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getNextQuestion();
}
});
/// Returns you to the Home screen
homeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, _MainMenu.class));
}
});
}
public void getNextQuestion() {
//1st question so reset everything
if(currentQuestion == -1)
{
setupQuiz();
}
currentQuestion++;
//Check to see if the end of the questions has been reached
if(currentQuestion == questions.length)
{
endQuiz();
}
else
{
questionView.setText(questions[currentQuestion]);
answerText.setText("");
}
}
public void setupQuiz()
{
score = 0;
answerButton.setVisibility(View.VISIBLE);
questionView.setVisibility(View.VISIBLE);
answerText.setVisibility(View.VISIBLE);
answerView.setText("");
questionButton.setText("Skip Question");
}
public void endQuiz()
{
currentQuestion = -1;
answerButton.setVisibility(View.INVISIBLE);
questionView.setVisibility(View.INVISIBLE);
answerText.setVisibility(View.INVISIBLE);
questionButton.setText("Try Again");
scoreText.setText("Final Score: " + score);
}
public void checkAnswer() ///validaion goes here and not in getnextquestion
{
String answer = answerText.getText().toString();
boolean result = answer.equalsIgnoreCase(answers[currentQuestion]);
//answer is correct
if(result == true) {
score += 10; /* ++ will increment the score by 1, +=10 will increment the score by the value added (10)*/
answerView.setText("Correct!");
}/*answerView, text view set to print the string in the event of the correct answer*/
else //answer was wrong
answerView.setText("Incorrect, The answer was " + answers[currentQuestion]); /*answers[currentQuestion] answers reads the answer to the current question in use */
scoreText.setText("Current Score = "+score);
getNextQuestion();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
main();
getNextQuestion();
}
}
I am building a simple Quiz game in Android that currently has a main Menu Activity that links you directly to a quiz activity that will ask you an "x amount of questions". Ultimately I want three difficulty levels for the Quiz. There will be 3 buttons on the home screen that will direct the user to an Easy, Medium or Hard version of the Quiz.
As each activity will be exactly the same other than the actual questions being asked. I was wondering if there is a more efficient way of duplicating the class a couple of times without having to rebuild the user interface and then copy the code into the separate classes.
I attached an example of my code that works perfectly fine so far. The game is very simple and is a learning exercise for me for than anything.
The best way for something like this is to use a single activity.
Every activity makes the application a lot heavier and this is why you usually try to create the smallest number of different activities as possible.
If your pages are pretty much the same you should consider adding a simple value and doing different stuffs based on the value.
On button click, simply add an int value to the intent opening the questions activity (1 = easy, 2 = medium, 3 = hard, or any value you like) with
intent.putExtra("lvl", lvl);
Now, on activity start, call this number retrieving the value from the intent with
int myLvl = getIntent().getIntExtra("lvl", 0);
now simply call a switch:
switch(myLvl){
case 1: doLvl1Stuffs();
break;
case 2: doLvl2Stuffs();
break;
case 3: doLvl3Stuffs();
break;
default: throw new Exception("no lvl found");
break;
Hope this helps, but generally avoid creating more activities than the needed ones
I think the real answer here is the good old "prefer composition over inheritance" mem.
Example: upon "collection" your UI elements; you could create a helper class like
public class QuizButtonManager {
private final Button answerButton;
private final Button questionButton;
private final Button homeButton;
public QuizButtonManager(Button answerButton ... ) {
this.answerButton = answerButton;
...
And then you simply move your setupQuiz() and endQuiz methods into that class.
Then you look into other responsibilities that you have currently forced into your activities. You "cut" them out and put them into distinct classes; thus enabling much simpler "re-use".
The best way is to Create BaseActivity that contains all the Mutual Components and the other activities extend from it and implement their logic
but as you say it's all the same. the difference is the Questions so you don't need to create multiple activities you can use the same activity and set a flag to know what is the current difficulty and set the questions based on it

Passing a counter from one Activity to another

I have an class Voice, which extends Activity, and contains a counter. When the user answers correctly, the counter adds one via counter++;
public class Voice extends Activity implements OnClickListener{
ListView lv;
static final int check = 111;
int counter_score;
TextView txView;
MediaPlayer ourSong;
ImageView display;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.letter_a);
initialize();
}
private void initialize() {
lv = (ListView)findViewById(R.id.lvVoiceReturn);
Button b = (Button)findViewById(R.id.imageButtonSelector);
txView = (TextView)findViewById(R.id.counter);
b.setOnClickListener(this);
counter_score=0;
}
This score, is bundled and passed on to the next activity "What" within a string "your score is 1".
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == check && resultCode == RESULT_OK) {
ArrayList<String> results = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
lv.setAdapter( new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, results));
if(results.contains("hey") || results.contains("a") || results.contains("ay")) {
//toast referenced to xml the after 400ms
counter_score++;
txView.setText("Your Score is" + " " + counter_score);
AlertDialog dialogBuilder = new AlertDialog.Builder(this).create();
dialogBuilder.setTitle("AWSOME");
dialogBuilder.setMessage("¡Your current score is" + counter_score);
dialogBuilder.setIcon(R.drawable.ic_mark);
dialogBuilder.show();
ourSong = MediaPlayer.create(Voice.this, R.raw.rightsound2);
ourSong.start();
Thread timer = new Thread() {
public void run(){
try {
sleep(2500);
}catch (InterruptedException e){
e.printStackTrace();
} finally {
String score = txView.getText().toString();
Bundle keeper = new Bundle();
keeper.putString("key", score);
Intent putScore = new Intent(Voice.this, What.class);
putScore.putExtras(keeper);
startActivity(putScore);
}
}
};
timer.start();
}
}
The next Activity, What, gets this Bundle and displays it fine using setText(gotScore)
public class What extends Activity implements OnClickListener {
ListView lv;
static final int check = 111;
private int counter_score;
TextView txView;
MediaPlayer ourSong;
ImageView display;
String gotScore;
String classes[] = {"What", "Pagina", "What", "example3", "example4", "example5",
"example6"};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.letter_b);
initialize();
Bundle gotKeeper = getIntent().getExtras();
gotScore = gotKeeper.getString("key");
txView.setText(gotScore);
}
private void initialize() {
// TODO Auto-generated method stub
lv = (ListView)findViewById(R.id.lvVoiceReturn);
Button b = (Button)findViewById(R.id.imageButtonSelector);
txView = (TextView)findViewById(R.id.counter);
b.setOnClickListener(this);
..this is when things go bad :(
On What I have another question tied to a counter as well. When the user answers correctly the counter adds one via counter++; and it does. However, it changes the txview string to "your score is 1". I can't get it to add 1 to the counter result passed from the previous activity within the string, so that the counter on What reads "your score is 2". This gets passes to the next activity in Bundle keeper, which holds the aggregate score.
I've read a few tutorials on passing an int verses a string, but some of the code they use like getInt is not recognized. I'm stumped.
What you're bundling and passing to the What activity is not the counter but the string "Your score is 1". If you want to increment that number in the next activity then you should be sending just the integer value and constructing whatever string you need there instead.
I ve read a few tuts on passing an int vs a string..but some of the code they use like getInt is not recognized..anywho Im stumped..
I'm not too sure I know what you mean by getInt() is not recognized. In any case, make things easier for yourself when passing counter from one activity to another. If it is an int and you plan on manipulating like an int in the receiving activity then add it to the bundle as an int. For example:
Bundle keeper = new Bundle();
keeper.putInt("key", counter_score);
And retrieve it from the bundle with:
Bundle gotKeeper = getIntent().getExtras();
int score = gotKeeper.getInt("key");
What if you make a "global" class to be shared across the different activities, and use it to keep the variables used "in sync"?
For example - Globals.java:
public class Globals {
public int counter_score;
}
And then reference that variable using Globals.counter_score
You can of course also use that shared class for other variables and functions as well - for example common operations.
Update
As the commenters pointed out, this method isn't particularily good - I forgot that the code is simply referenced, and doesn't "live" on its own to keep information for the other activities (thanks for correcting me on that one, I'm still learning...)
Something that COULD work better, though, is to pass the current state of the counter_score variable in the intent when you launch your second activity - for example:
IntentToLaunchTheOtherActivity( counter_score );
And then maybe pass the variable back to the previous activity if it's changed afterwards...
I got it work. Essentially I needed to what what TJ Third suggested converting keeper.putString("key", counter_score); to keeper.putInt("key", counter_score);, I also needed to convert the bundle being received to an int within the "What" activity. Within "What" activity I renamed int counter_score; and int gotKeeper;(this was String gotKeeper) then instead of calling counter_score =0; now that the bundle passed is an int,I called counter_score = gotKeeper; under initialize(); so the counter score equals the result generated from the previous activity "Voice".
Now when the user answers correctly, counter++; adds one to the existing counter_score and bundles it and send it to the next activity, and rinse a repeat.
static final int check = 111;
int counter_score;
TextView txView;
MediaPlayer ourSong;
ImageView display;
int gotKeeper;
String classes[] = {"What", "Pagina", "What", "example3", "example4", "example5",
"example6"};
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.letter_b);
initialize();
Bundle gotKeeper = getIntent().getExtras();
gotKeeper = gotScore.getInt("key");
counter_score = gotKeeper;
Again thnx to everyone for your suggestions and insight.Huge help to a newbie.

problem with activity crashing when a function is called

this is my first time on this forum, so forgive me if my question seems odd. I'll try to be as thorough as possible.
I am creating a translation program.
this program has a menu activity, translate activity, addword activity.
The three activities are linked together via intents and they are
added in the manifest file.
In the translate activity I want to create a method for translating.
After I press the translate button, the program crashes.
public class VertaalActivity extends Activity {
private Button vertaal;
private Button terug;
private EditText ET_NL;
private EditText ET_EN;
private ArrayList<String>nlWoorden = new ArrayList<String>();
private ArrayList<String>enWoorden = new ArrayList<String>();
public void Vertaal(){
String woord = ET_NL.getText().toString();
if(nlWoorden.contains(woord)){
int i = nlWoorden.indexOf(woord);
ET_EN.setText(enWoorden.get(i));
}else{
ET_EN.setText("Woord niet gevonden");
}
}
public void ArrayVullen(){
nlWoorden.add("auto");
nlWoorden.add("bord");
nlWoorden.add("trein");
nlWoorden.add("spel");
nlWoorden.add("scherm");
nlWoorden.add("toetsenbord");
nlWoorden.add("foto");
enWoorden.add("car");
enWoorden.add("plate");
enWoorden.add("train");
enWoorden.add("game");
enWoorden.add("screen");
enWoorden.add("keyboard");
enWoorden.add("picture");
}
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.vertalerlayout);
terug = (Button)findViewById(R.id.terug);
vertaal = (Button)findViewById(R.id.vertalen);
ArrayVullen();
vertaal.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Vertaal();
/*
* Tested the toast and the toast shows the text
*
Context context = getApplicationContext();
CharSequence text = "Hello toast!";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
*/
}
});
terug.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent(VertaalActivity.this,MenuActivity.class);
startActivity(intent);
}
});
}
}
I can't see that you get the EditTexts from your XML (like you do with the buttons). Before using ET_NL you need to do something like this:
ET_NL = (EditText)findViewById(R.id.etnl); // Or whatever id you've declared in your layout XML
Same thing goes for the ET_EN variable. Otherwise the will be null in your Vertaal() method, causing the app to crash.
Try this code before using the editText field
ET_NL= (EditText)findViewById(R.id.edittext1);
ET_EN = (EditText)findViewById(R.id.edittext2);

Hide/remove random string?

I have used this question here on stackoverflow to create a random string without problem :D
( Show random string)
Some times the same string shows up and that's annoying.
So I want the string to only show one time per session, I have already a Quit Session button that kills the class. So lets say I have numbers from 1-3. Number 2 shows up first, then 1, becuase there's only one number left only 3 can be shown.
My button code for the "next button". Currently it kills the class and starts it again! How can I change it so it just displays a new string?
private void onButtonClick(Button clickedButton) {
Intent startIntent = null;
if (clickedButton.getId() == R.id.quit) {
startIntent = new Intent(this, mainmenu.class);
finish();
}
else if (clickedButton.getId() == R.id.next) {
startIntent = new Intent(this, play.class);
startActivity(startIntent);
finish();
}
if (startIntent != null) {
startIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivityIfNeeded(startIntent, 0);
}
}
private void setupbutton() {
View.OnClickListener onClickHandler = new View.OnClickListener() {
public void onClick(View v) {
onButtonClick((Button)v);
}
};
Button button = (Button)findViewById(R.id.quit);
button.setOnClickListener(onClickHandler);
button = (Button)findViewById(R.id.next);
button.setOnClickListener(onClickHandler);
The same string is appearing because the random number generator generates random numbers! It could generate the same number multiple times in a row.
There are many possibilities, to name two:
Put all strings in an array and shuffle it. Then take the elements out one by one starting with index one:
List strings = new ArrayList<String>(getResources().getStringArray(R.array.myArray));
Collections.shuffle(list);
Put all strings in an array, select a string with a random index and remove the string:
Random rgenerator = new Random();
ArrayList<String> strings = new ArrayList<String>(getResources().getStringArray(R.array.myArray));
int index = rgenerator.nextInt(strings.size());
String randomString = strings.get(index);
strings.remove(index);
EDIT:
Ok, I see...
You have to remember which strings have not been used already. Store the strings in a List and make the list static, so you can save state between the creation of different instances of the Activityplay:
private static ArrayList<String> myString;
Then change your constructor a bit:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.play);
setupbutton();
if (myString == null || myString.size() == 0) {
Resources res = getResources();
myString = new ArrayList(Arrays.asList(res.getStringArray(R.array.myArray)));
}
// get a random string
int rand = rgenerator.nextInt(myString.size());
String q = myString.get(rand);
// remove the last used string from the list
myString.remove(rand);
TextView tv = (TextView) findViewById(R.id.text1);
tv.setText(q);
}
Make a random permutation of the indexes and then use it to pick the next string in the sequence.

Categories

Resources