Reference many buttons in a short method - android

I'm designing a keyboard for Android.
I just learned this neat way of defining my buttons in the onCreate method by implementing the View.OnTouchListener in my MainActivity:
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
defineButtons();
keyC.setOnTouchListener(this);
//etc..
}
and then:
private void defineButtons()
{
keyC = (Button) findViewById(R.id.c);
//etc..
}
Then i would define what happens when the buttons are touched by this method:
public boolean onTouch(View view, MotionEvent motion)
{
int note = 0;
switch(view.getId())
{
case R.id.c: /** Note C*/
{
note = 60 + transposeOctave;
motionTracker(view, motion, note);
}
break;
//etc...
}
This last method has to be repeated for every button separately, which seems kind of ugly.
Can I use the Id to return a numerical value to modify my note value maybe?
Cheers

Are your keys defined in XML? If so, I would simply add a tag to each one with the proper numeric value, like so:
android:tag="1"
Then, in your code, you can simply do this:
int note = 0;
int tagValue = 0;
//Tags in XML are always strings
String tag = (String)view.getTag();
//Parse it to an integer
tagValue = Integer.parseInt(tag);
note = tagValue + transposeOctave;
motionTracker(view, motion, note);

You can't force an ID to a specific value. There is no way to do it in Android, the Android Asset Packaging Tool (AAPT) will always automatically generate them for you.
But you can still hide the "ugly" conversion from constant to integer in a function. example: keyboardIdToFrequency()

you can add the specific value as Tag by using
btn.setTag("something");
and use in onTouch
Button btn = (Button)view;
String val = btn.Gettag();
note = Integer.Valueof(val)+ transposeOctave;

Related

Score not incrementing when it's supposed to

What i'm trying to do is quite simple. I want to generate random numbers(rand1 & rand2) and have the user give the correct answer of the sum. There are 2 buttons and the correct answer could be on either one. I am using the randDecider variable to determine whether or not the correct answer should show up on the first or second button. The randDecider is either a 1 or 2.
The issue I am having is sometimes when I click on the correct answer, The score doesn't increment. And it turns out that sometimes when I press the wrong answer, the score increments. So i'm assuming it's a 50/50 chance the score will increment regardless if the answer is correct or not.
protected void setRandom(View v) {
//Assigning random values to ints
rand1 = r.nextInt(5) + 1;
rand2 = r.nextInt(5) + 1;
randDecider = r.nextInt(2)+1 ;
//The sum of the randoms
sum = rand1 + rand2;
//Setting the texts of the random values
randTV1.setText(rand1 + "");
randTV2.setText(rand2 + "");
/*If the random deciding number is 1, set the correct answer
on the choice1 button*/
if (randDecider == 1){
choice1.setText(sum+"");
choice2.setText(sum+1+"");
//If the correct answer was chosen, increment the score and set the text
if(v.getId()==R.id.choice1){
score++;
scoreTV.setText(score+"");
}
}
/*If the random deciding number is 2, set the correct answer
to the choice2 button*/
if (randDecider == 2){
choice1.setText(sum+1+"");
choice2.setText(sum+"");
//If the correct answer was chosen, increment the score and set the text
if(v.getId()==R.id.choice2){
score++;
scoreTV.setText(score+"");
}
}
I'd suggest splitting the two distinct functions a) creating and presenting the problem to be solved and b) checking the response and then c) introducing a class variable to store the answer until another problem is presented (which would be after responding b) ).
So you could
a) add a line in the class
int correctanswer;
b) Add a method for setting the problem e.g. setProblem (called initially and then within c))
c) Add a method for checking the response e.g. setResponse which calls the setProblem method when the score has been adjusted. The setResponse method being called when either button is clicked.
d) add a call to initially invoke the setProblem.
e) set the 2 onclick listeners to call the setResponse method.
The following could be a resolution (based upon your question) :-
public class MainActivity extends AppCompatActivity {
int score, correctanswer;
TextView scoreTV, randTV1, randTV2;
Button choice1, choice2;
Random r = new Random(System.currentTimeMillis());
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// get UI components
scoreTV = (TextView) this.findViewById(R.id.score);
randTV1 = (TextView) this.findViewById(R.id.rand1);
randTV2 = (TextView) this.findViewById(R.id.rand2);
choice1 = (Button) this.findViewById(R.id.choice1);
choice2 = (Button) this.findViewById(R.id.choice2);
// Initialise
score = 0;
scoreTV.setText(Integer.toString(score));
setProblem();
// Button Listeners
choice1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
checkResponse((Button)v);
}
});
choice2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
checkResponse((Button) v);
}
});
}
// Set the problem
public void setProblem() {
//Assigning random values to ints
int rand1 = r.nextInt(5) + 1;
int rand2 = r.nextInt(5) + 1;
int randDecider = r.nextInt(2)+1 ;
//The sum of the randoms
int sum = rand1 + rand2;
correctanswer = sum;
//Setting the texts of the random values
randTV1.setText(rand1 + "");
randTV2.setText(rand2 + "");
/*If the random deciding number is 1, set the correct answer
on the choice1 button*/
if (randDecider == 1){
choice1.setText(sum+"");
choice2.setText(sum+1+"");
} else {
choice2.setText(sum+"");
choice1.setText(sum+1+"");
}
}
// Check the user's response (called by onClick listeners)
public void checkResponse(Button v) {
if ((new Integer(v.getText().toString()) == correctanswer)) {
score++;
scoreTV.setText(score+"");
}
setProblem();
}
}
At the top of your method, you have several lines which generate the random numbers and the decider. These are correct.
Then, you show the random numbers and place the answers on the correct buttons, which is presumably also correct.
However, at the same time, you check whether the correct button is selected. This means you're checking against the last button the user pressed, not their answer.
One way to fix this is to save the sum and correct answer positions for at least one rotation. Change your setRandom method to generate the numbers and set them to the screen as they are now, but also to save the correct answer to an outside variable.
Then, in the button's onPressed method, check whether the pressed button is correct, increment the score, and call setRandom to put a new question on the screen.
The problem in your code stems from the fact that you check the answer right as you put the question on screen. Happy programming!

android working with Views

In my activity I have the following views
TextView player1;
TextView player2;
TextView player3;
TextView player4;
EditText player1name;
EditText player2name;
EditText player3name;
EditText player4name;
Each of the TextView's has the onclick listener applied to it. and so fires the OnClick function.
When we get to the onClick this is what i am currently doing:
#Override
public void onClick(View v) {
//the v variable is the clicked textview, in this case "player1"
//hide the textview and show the resultant edittext
v.setVisibility(View.GONE);
player1name.setVisibility(View.VISIBLE);
//set focus on edit text and when focus is lost hide it and set the textview text
player1name.requestFocus();
imm.showSoftInput(player1name, InputMethodManager.SHOW_FORCED);
player1name.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View y, boolean x) {
v.setVisibility(View.VISIBLE);
player1name.setVisibility(View.GONE);
imm.hideSoftInputFromWindow(player1name.getWindowToken(), 0);
String name = player1name.getText().toString();
if (name.equals("")) {
v.setText("Player Name1");
} else {
v.setText(name);
}
}
});
}
However with this solution I will need to duplicate this code and change the view names for player2 - player2name, player3 - player3name etc
i can obviously grab the clicked TextView via v, however what i cant seem to do is grab its corresponding EditText.
i had thought of doing this:
View test = v + "name";
//then i replace all references to player1name with the test variable
but it doesnt work it wants me to convert View test; into a string
any suggestions?
EDIT: made it easier to understand my question
View test = v + "name";
will give a compile error. Because "v" is not a string type. and also even if it was String, test is not. This line is pretty wrong.
There a few options to achieve what you want,
You can use hashmap
Declare a global field for hashmap
private final HashMap<Integer,EditText> map = new HashMap<Integer,EditText>();
and in onCreate method put your textview id as key, and put your edittext variables in value.
player1name = (EditText) findViewById(R.id.player1name);
map.put(R.id.textView1, player1name);
// for the rest
in onClick method
EditText e = map.get(v.getId());
Then replace them with "e"
e.requestFocus(); //example
Will you please state your problem clearly? Currently, your language is very ambiguous and I can not figure out, exactly what are you looking for. It will help us to know your problem and in turn solve it.

Android calculator buttons

I am trying to make a calculator for Android. Here is the code for my buttons:
int[] button_ids = {
R.id.BtnNum0, R.id.BtnNum1, R.id.BtnNum2, R.id.BtnNum3, R.id.BtnNum4, R.id.BtnNum5, R.id.BtnNum6,
R.id.BtnNum7, R.id.BtnNum8, R.id.BtnNum9, R.id.BtnAdd, R.id.BtnSub, R.id.BtnDiv, R.id.BtnMult,
R.id.BtnClear, R.id.equals
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditTextValue = (EditText) findViewById(R.id.editText1);
TVValue = (TextView) findViewById(R.id.textView1);
buttons = new ArrayList<Button>();
for(int id : button_ids) {
Button button = (Button)findViewById(id);
button.setOnClickListener(this);
buttons.add(button);
}
}
How I can change this part to a block of code where I won't have to declare the IDs of the buttons? (e.g. R.id.BtnNum0)
int[] button_ids = {
R.id.BtnNum0, R.id.BtnNum1, R.id.BtnNum2, R.id.BtnNum3, R.id.BtnNum4, R.id.BtnNum5, R.id.BtnNum6,
R.id.BtnNum7, R.id.BtnNum8, R.id.BtnNum9, R.id.BtnAdd, R.id.BtnSub, R.id.BtnDiv, R.id.BtnMult,
R.id.BtnClear, R.id.equals
};
I have been searching for an answer, but I still can't find a solution.
What you can do, since this code seems to only set a single OnClickListener for all Buttons, is to do it in xml
For each Button set
android:onClick="functionName"
then in your code you can do away with all of the id's and your for loop. In Java just create a function like
public void functionName(View v)
{
switch (v.getId())
{
case R.id.buttonId:
// do work for this Button
break;
...
}
The way you are doing it is fine but this is how I prefer to handle this situation. You just have to give all of the Buttons the same function name in xml then use that name as your function name in Java. You also just have to be sure to declare the function with a void return type and make sure it takes a View as its one and only parameter as in my example.
The Button Docs also have an example of this
in your layout file add this to every button
<Button
...
android:onClick="btnClicked"
.../>
then in your code add this method and check for each button in this method
public void btnClicked(View v)
{
switch(v.getId())
{
case R.id.BtnNum0:
// your code
break;
....
}
}
That is likely the best solution unfortunately, unless you use some sort of annotation framework which still doesn't cut down much on the boilerplate.
edit:
You could try to get a pointer to whatever ViewGroup is holding the Button views and then getting all of its children, and then looping through them while casting them to Buttons as you go.
For example: If your Button objects in XML are housed in a LinearLayout, you could get the pointer to that and do something like this:
for(int i=0; i < ((ViewGroup)v).getChildCount(); ++i) {
Button nextChild = (Button) ((ViewGroup)v).getChildAt(i);
}
Of course, I recommend against this, but it is still a possibility.
As trevor-e suggested, you can give an annotation processor a try. Android Annotations can simplify your code to:
#Click
public void BtnNum0() {
// Button 0 clicked
}
#Click
public void BtnNum1() {
// Button 1 clicked
}
// etc.
If you go this route, please do try to use names following the Java convention as the button names correspond with function names.

Android: random and textView

I have just started learning Android and got some misunderstanding. I'm trying to create an application which displays a textView and a button. Every button click generates a new random number which should be displayed in the textView.
But unfortunately my code causes a list of errors. Here it is:
public class FirstAndroidProjectActivity extends Activity {
public OnClickListener listener = new View.OnClickListener() {
#Override
public void onClick(View v) {
TextView tv = (TextView) findViewById(R.id.display);
Random r = new Random();
int i = r.nextInt(101);
tv.setText(i);
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(listener);
}
}
If I just don't use random and use some string except of i
(for example tv.setText("99");) everything is ok, but it doesn't work with a variable as a parameter of setText().
Where is a mistake?
Hope for your help.
You need to convert your random number to a string before setting the text on your TextView
Try
tv.setText(i +"");
Try:
tv.setText(String.valueOf(i));
Java doesn't auto convert types. The + operator is overloaded to transform the parameters passed to it into a String when one or more of those paramaters is a String. So, when you pass i + "" to setText() you are passing a String, however if you just pass i then the compiler sees you passing an int to a method that expects a String and lets you know that that can't be done.
i is an int, try tv.setText("" + i);
Convert your integer to a String before setting it to the textView. You should also move Random r = new Random(); outside of the method, or else your numbers may not really be random :
Random r = new Random();
#Override
public void onClick(View v) {
TextView tv = (TextView) findViewById(R.id.display);
int i = r.nextInt(101);
tv.setText(Integer.toString(i));
}
From the documentation :
If two instances of Random are created with the same seed, and the same sequence of method calls is made for each, they will generate and return identical sequences of numbers
If you create two Random objects too quickly (for example the user clicks two times on the button very quickly), they will share the same seed (the system clock is used to generate it) and as a result you will get the same number two times.
By creating only one Random instance in a global variable, you avoid this issue.
use
tv.setText(new Integer(i).toString()) ;

ParseInt Exception

I am creating a small calc app with EditText views and Im running into an runtime exception when the user leaves an EditText view empty causing the ParseInt to try and Parse nothing. Ive read that I need to 'Try' and 'Catch' this error before it occurs, but Im unsure of where and how to do this!
Any advice is much appreciated!
Here is my code:
public class HandlerExamples extends Activity implements OnClickListener {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button = (Button)findViewById(R.id.testButton);
button.setOnClickListener(this);
public void onClick(View v) {
String a,b,t;
double vis;
EditText txtbox1 = (EditText)findViewById(R.id.A);
EditText txtbox2 = (EditText)findViewById(R.id.B);
EditText txtbox3 = (EditText)findViewById(R.id.t);
TextView tv = (TextView) findViewById(R.id.Answer);
a = txtbox1.getText().toString();
b = txtbox2.getText().toString();
t = txtbox3.getText().toString();
vis = ((Integer.parseInt(a)*1) + (Integer.parseInt(b)*2)) / (Double.parseDouble(t));
tv.setText(double.toString(vis));
}
}
Thanks so much!
public void onClick(View v) {
int id = v.getId();
switch(id){
case R.id.xx:
//do things xx click
break;
case R.id.yy:
//do things yy click
break;
}
}
you can get the view id to know whick widget was clicked.
Changwei Yao defined one way you can do this, but here's the way most Android programmers would do this (programmatically), since it's a little easier to read and figure out what your widgets are doing:
But first, remove the implements OnClickListener from your Activity, as it's not needed.
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// what you want your button to do when clicked
}
}
editText.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// what you want your EditText to do when clicked
// (such as editText.setText(""))
}
}
Another way to do the same thing is to define android:onClick="insert_method_name_here" for the widgets that you want perform an action when clicked. In your case, in your main.xml (since that's what you're using in your Activity), you could write something like...
<Button android:id="#+id/testButton"
(other attributes you wish to apply to the button)
android:onClick="buttonAction" />
<EditText
(other attributes)
android:onClick="textAction" />
And then, in your Activity, you define the methods buttonAction(View v) and textAction(View v). Note that these methods must be public void, and must take the sole argument View v.
(One advantage of the XML method is that you don't necessarily have to define an android:id attribute for these widgets, unless you need to be able to manipulate them or extract information from them in your code (which means you will need to define an android:id for your EditText since you'll likely want the user's input))
If you only need to exclude the empty text field then hotveryspicy's solution is probably the quickest. For a secure solution: catching the NumberFormatException will filter anything that can not be converted to an integer.
int vis;
try {
vis = Integer.parseInt(a);
}
catch(NumberFormatException nfe) {
Log.e(TAG,"trying to convert:"+a+" to integer failed");
vis = 0;
}

Categories

Resources