I've got the following code (it's not the full code, but the rest doesn't matter). I'm trying to set the boolean "ignoreLength" to either true or false based on what the user picks in an Alertdialog. However, when the code is like this, I get this error:
"Cannot refer to a non-final variable ignoreLength inside an inner class defined in a different method"
When I make it final, it changes to this:
"The final local variable ignoreLength cannot be assigned, since it is defined in an enclosing type"
How can I make it so I can change ignoreLength?
package com.grawl.passgen;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class PassGenActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
// Interface -- Default
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Interface -- Custom
final Button button_generate = (Button) findViewById(R.id.button_generate);
final EditText text_pass = (EditText) findViewById(R.id.textPassWord);
final EditText edit_length = (EditText) findViewById(R.id.editLength);
// Set up Arrays
final String[] lowerCase;
final String[] upperCase;
final String[] numbers;
final String[] symbols;
// Fill Arrays
createArray characters = new createArray();
lowerCase = characters.getArrayLower();
upperCase = characters.getArrayUpper();
numbers = characters.getArrayNumbers();
symbols = characters.getArraySymbols();
// Pressing the button WOOOSH!
button_generate.setOnClickListener(new View.OnClickListener() {
**boolean ignoreLength = false;**
public void onClick(View v) {
// Set up parameters
boolean lowerCaseEnabled = true; // needs interface option
boolean upperCaseEnabled = true; // needs interface option
boolean numbersEnabled = true; // needs interface option
boolean symbolsEnabled = true; // needs interface option
// Set up length based on input from EditText
int length = 0;
try {
length = Integer.parseInt(edit_length.getText().toString());
} catch(NumberFormatException nfe) {
Toast.makeText(PassGenActivity.this, "Can't parse " + nfe, Toast.LENGTH_LONG).show();
}
if (length < 1) {
length = 1;
edit_length.setText("1");
Toast.makeText(PassGenActivity.this, "Password length can't be less than 1, set it to 1.", Toast.LENGTH_LONG).show();
};
if (length > 100) {
AlertDialog.Builder alert = new AlertDialog.Builder(PassGenActivity.this);
alert.setTitle("Warning");
alert.setMessage("You are trying to create quite a long password, are you sure?");
alert.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
ignoreLength = true;
}
});
alert.setNegativeButton("No", null);
alert.show();
}
Password password = new Password();
password.fillPassword(lowerCase, upperCase, numbers, symbols);
// Generate password
password.setPassword(lowerCaseEnabled, upperCaseEnabled, numbersEnabled, symbolsEnabled, length);
text_pass.setText(password.getPassword());
}
});
}
}
Firstly as per your question.
"Can't change final variable inside AlertDialog" is wrong.
As making you clear that a final variable can never be changed as it's final and already declared at the time of creation.
Also in you case boolean ignoreLength = false; declare the variable outside and before the click listener and do not make it final. As you need to update ignoreLength value in future.
You can't reassign local variables in java from an inner type. More here.
There are two ways to solve your problem.
1) Make ignore case a field on something--you could actually use the anonymous OnClickListener type, and give it the ignoreLength field, but I think generally you would want it to be a field on the top level class
2)
<superuglyhack>
final boolean[] ignoreLengthHolder= new boolean[]{ false };
...
ignoreLengthHolder[0] = true
</superuglyhack>
You could move the boolean ignoreLength; declaration outside of the onClick to make it a member variable of the anonymous OnClickListener class.
Also, you could put the variable in something that holds it (like an array with one item) and update it that way. ignoreLength[0] = true;
Related
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.
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
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"
I have the following code which I want to use to make sure that my edittext wont be empty. So if the first drawn 0 (zero) is removed it must revert to 0 when the focus changes, Here is the app so far:
package your.test.two;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
public class TesttwoActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
EditText edtxt = (EditText)findViewById(R.id.editText1);
// if I don't add the following the app crashes (obviously):
edtxt.setText("0");
edtxt.setOnFocusChangeListener(new View.OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
// TODO Auto-generated method stub
update();
}
});
}
public void update() {
EditText edittxt = (EditText)findViewById(R.id.editText1);
Integer i = Integer.parseInt(edittxt.getText().toString());
// If i is an empty value, app crashes so if I erase the zero
//on the phone and change focus, the app crashes
}
}
I have tried the following in the update() method:
String str = edittxt.getText().toString();
if (str == "") {
edittxt.setText("0");
}
But it doesn't work. How can I allow the edittext to never be emty, revert to zero when empty but not when a value exists. I have already made sure that the edittext can only allow numerical values.
if(str.equals("")){
edittxt.setText("0");
}
WarrenFaith is right. Refer to this post to learn more about this issue: Java String.equals versus ==
I would recommend surrounding your parseInt call with a try/catch block that catches a NumberFormatException which is probably the error being thrown (since you didn't specify, I can only guess), so it looks like:
public void update() {
EditText edittxt = (EditText)findViewById(R.id.editText1);
Integer i;
try {
i = Integer.parseInt(edittxt.getText().toString());
// do something with i
} catch (NumberFormatException e) {
// log and do something else like notify the user or set i to a default value
}
}
I created this Sign-In page. I start by declaring variables for username/password & buttons. If user enters "test" as username & "test" as password and hits the login button, its supposed to go to the DrinksTwitter.class activity, else throw error message I created. To me the code and login makes perfect sense. I'm not sure why it wont go to the next activity I want it to go to
package com.android.drinksonme;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class Screen2 extends Activity {
// Declare our Views, so we can access them later
private EditText etUsername;
private EditText etPassword;
private Button btnLogin;
private Button btnSignUp;
private TextView lblResult;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Get the EditText and Button References
etUsername = (EditText)findViewById(R.id.username);
etPassword = (EditText)findViewById(R.id.password);
btnLogin = (Button)findViewById(R.id.login_button);
btnSignUp = (Button)findViewById(R.id.signup_button);
lblResult = (TextView)findViewById(R.id.result);
// Check Login
String username = etUsername.getText().toString();
String password = etPassword.getText().toString();
if(username.equals("test") && password.equals("test")){
final Intent i = new Intent(Screen2.this, DrinksTwitter.class);
btnLogin.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
startActivity(i);
}
// lblResult.setText("Login successful.");
else { /* ERROR- Syntax error on token "else", { expected */
lblResult.setText("Invalid username or password.");
}
}
});
final Intent k = new Intent(Screen2.this, SignUp.class);
btnSignUp.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
startActivity(k);
}
}); /* ERROR- Syntax error, insert "}" to complete Statement*/
}
}
I only briefly looked at your code, but I don't understand what you're doing (in that order).
You're checking the password & username on the OnCreate method, which, barring any autofilling, the textboxes will be blank.
Then you declare your onClickListeners.
Try restructuring it to be something like this (pseudo-code):
OnCreate
user = usertextbox
pass = passtextbox
login.setOnClick
if user == validUsername && pass == validPass
Intent i = new Intent(blah.this, blah.class)
startActivity(i)
else
Intent i = new Intent(blah.this, login.class)
startActivity(i)
This is how I have my app's login screen.
In short, you have to make the check occur in the OnClickListener of the action button, not in the OnCreate method of the activity. The way it is now, the form is created, and the textboxes are blank. Then immediately afterwards, they're compared against some values. That call will always fail to execute, so all of the code in your If statement scope will be ignored indefinitely.
Also, the reason Eclipse is giving you that error is because you're trying to place an else statement inside of a class definition.
When you declare a new OnClickListener() { }, you're essentially declaring a class. So you're in the class scope, not the if scope.
That would be like trying to do:
class test {
else {
doStuff();
}
}
I'm almost positive your problem is in setting an onClick() call instead of just starting the activity. It seems a bit complex of a call, and I think you're already checking the conditions in the appropriate place. Why do you need another onClick() call?
You should indent your code correctly. That syntax error is because you don't have a closing brace around the btnLogin.setOnClickListener. You then miss at least a couple more closing brackets.
Also, as others have posted, you check the edittext value before the onCreate() method even gives up control to the UI.
You set the button's click listener only if the strings match.
To me the code and login makes perfect
sense
You should slow down and take a look more carefully. It can't possibly make perfect is sense if there's syntax errors.