I have put a lot of buttons (16*16) in a button array.
The buttons numbers are directly related to changes they should do in another array (e.g. button[12][7] sets the value of stat[12][7] to 1)
So I thought it's possible to put on single line in the onTouch method that reacts to every button.
Example (of course, not working)
public boolean onTouch(View arg0, MotionEvent arg1) {
if (arg1.getAction() == MotionEvent.ACTION_DOWN) {
if(arg0 == button[int a][int b]){stat[a][b]=1};
In this pseudocode, the button would create 2 ints that describe the 2 dimensions of the array which get passed to the stat array.
If anyone had a solution for this, he would save me a few hours this night.
Thanks for your answers.
I think HasMap is a better solution
private HashMap<Integer,Button> btnMap = new HashMap<Integer, Button>();
private void init(){
Button yourFirstBtn = (Button) findViewById(R.id.yourFirstBtn);
btnMap.put(yourFirstBtn.getId(), yourFirstBtn);
for(Button tempBtn: btnMap.values()){
tempBtn.setOnClickListener(this);
}
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Button clickedBtn = btnMap.get(v.getId());
}
Are you adding the onTouchListener to the buttons' container?
Your best bet is to add an onTouchListener to each button, and then arg0 will correspond to the specific button.
Another option would be to use a GridView, which has an setOnItemClickListener you can use. http://developer.android.com/reference/android/widget/GridView.html
As you add each button to the array, set a tag that indicates its indices. Tags are there for adding properties to a view without having to resort to another data structure.
For example:
button[12][7].setTag("12|7");
If your button were pre-defined in XML, you could do the same with:
android:tag="12|7"
Then in the touch listener (I assume the same one is attached to all the buttons), get the tag from the view that was touched:
String tag = (String) view.getTag();
Then substring out and use the two indexes as required:
String indx1 = tag.substring(0, tag.indexOf("|"));
String indx2 = tag.substring(tag.indexOf("|")+1);
stat[Integer.parseInt(indx1)][Integer.parseInt(indx2)] = 1;
Try something like this:
Button[][] myButtonMatrix = new Button[] {
new Button[] { button11, button12, button13, button 14 },
new Button[] { button21, button22, button23, button24 }
};
public class MatrixButtonListener implements View.OnClickListener {
private int x;
private int y;
public MatrixButtonListener(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y; }
#Override
public void onClick(View v) {
stat[x][y] = x-y; // changes were made only in relation to x and y, nothing else
// for example:
if(x == 0) {
// button in first row
// do something
}
}
};
// to apply to each button in matrix:
for(int i=0; i<myButtonMatrix.length; i++) {
for(int j=0; j<myButtonMatrix[i].length; j++) {
myButtonMatrix[i][j].setOnClickListener(new MatrixButtonListener(i,j));
}
}
What this is supposed to do:
Create a generic OnClickListener class, which takes the x and y position as parameter, so each onClickListener has the same behaviour, but differnet x and y positions, depending on the button itself.
Note: This is not tested.
EDIT:
Another way would be a custom button class, which you use, which contains the X/Y coordinates as well. Simply add onClickListener to each button, cast it back to your custom view, and ask for x/y.
Related
I've inherited an app which displays a range of values as buttons (0 - 1000) - layout ViewA.
The buttons get indexed, and stuff happens. When the user clicks on some subset of those values, they change color, and get associated with some data which gets exported to a csv file eventually.
This works.
Now the user wants to be able to change between the original set, and a new set (0 - 1500) - layout ViewB.
I created these layouts, and put in a checkbox, and when it's selected it sets the visibility of ViewA to GONE and ViewB to Visible.
These buttons should be indexed just like the original ones, but the screen doesn't get updated before it crashes at observerPosBtn[i] = 42. 1 past the max index of the original array.
What (I think) I'm running into is that the layout is not getting updated until I leave the onChecked method, so the buttons which exceed the original range don't exist and can't get indexed. Then when stuff happens to an out-of-range button the app crashes. (Each range worked properly before adding the checkbox switch.)
Is there a way to "force" an update before I leave onChecked, or where does the code jump back to after the onChecked handler that I could call the 'updateObsPosButtons' function?
The code is structured (broadly) like:
Main Activity{
//definitions and stuff like
onCreate(){}
#Override
protected void onResume() {
super.onResume();
if(refreshSelectionEntries) {
startupConfig();
refreshSelectionEntries = false;
}
}
....
public void startupConfig(){
//onClick etcs
show1500mRangeCheckBox = findViewById(R.id.show1500mRangeCheckBox);
boolean[] observerPosChecked = new boolean[observerPosCount];
show1500mRangeCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
currentRange = "1500";
observerPosCount = 61;
} else {
currentRange = "1000";
observerPosCount = 41;
}
updateScreenRange(currentRange);
updateObsPosButtons(currentRange);
}
});
updateObsPosButtons(currentRange); //Called once in initial startup?
....
private void updateScreenRange(String range){
...
final View obs1500Incr50LL = findViewById(R.id.obs1500Incr50LL);
final View obs1500Incr25LL = findViewById(R.id.obs1500Incr25LL);
final View obsIncr50LL = findViewById(R.id.obsIncr50LL);
final View obsIncr25LL = findViewById(R.id.obsIncr25LL);
if (range == "1000") {
obs1500Incr50LL.setVisibility(View.GONE);
obs1500Incr25LL.setVisibility(View.GONE);
obsIncr50LL.setVisibility(View.VISIBLE);
obsIncr25LL.setVisibility(View.VISIBLE);
} else {
obsIncr50LL.setVisibility(View.GONE);
obsIncr25LL.setVisibility(View.GONE);
obs1500Incr50LL.setVisibility(View.VISIBLE);
obs1500Incr25LL.setVisibility(View.VISIBLE);
}
}
private void updateObsPosButtons(String range){
if (range == "1000"){
observerPosChecked = new boolean[observerPosCount];
....
populateObserverPos1000Btn();
//iterate through button range then "do stuff"
for (int observerPosIndex = 0; observerPosIndex < observerPosCount; observerPosIndex++) {
final int i = observerPosIndex;
....
}else{
observerPosChecked = new boolean[observerPosCount];
...
populateObserverPos1500Btn();
//iterate through button range - CRASHES AT i = 42
for (int observerPosIndex = 0; observerPosIndex < observerPosCount; observerPosIndex++) {
final int i = observerPosIndex;
// CHEAT HERE
....
}
} //End StartConfig
private void populateObserverPos1000Btn() {
int oPS_index = 0;
View obsIncr50LL = findViewById(R.id.obsIncr50LL);
View obsIncr25LL = findViewById(R.id.obsIncr25LL);
observerPosBtn[oPS_index++] = obsIncr50LL.findViewById(R.id.observerPosBtn_0);
observerPosBtn[oPS_index++] = obsIncr25LL.findViewById(R.id.observerPosBtn_25);
...
observerPosBtn[oPS_index++] = obsIncr50LL.findViewById(R.id.observerPosBtn_1000);
}
private void populateObserverPos1500Btn() {
int oPS_index = 0;
View obs1500Incr50LL = findViewById(R.id.obsIncr50LL);
View obs1500Incr25LL = findViewById(R.id.obs1500Incr25LL);
observerPosBtn[oPS_index++] = obs1500Incr50LL.findViewById(R.id.observerPosBtn_0);
observerPosBtn[oPS_index++] = obs1500Incr25LL.findViewById(R.id.observerPosBtn_25);
...
observerPosBtn[oPS_index++] = obs1500Incr50LL.findViewById(R.id.observerPosBtn_1500);
}
....
} //End MainActivity
If I "cheat" and add (at CHEAT HERE):
if (observerPosBtn[i] == null){
continue;
}
The updObsPosButtons function does finish and the screen updates, but it seems like if I click on anything above index 41 it crashes.
I think I've included the relevant code.
I've tried a few suggestions I've seen here like notifydatasetchanged, and a run handler... not sure I really can implement them in the current configuration though.
Is there anything I can do without a major refactor?
(I apologize in advance for my poor terminology, code architecture, and general ignorance - my software experience is a little firmware (C) - not Java let alone Android!
I'm the only option to even look it over for this "simple" (haha) update. Either it's something I can take a crack at, or I need to explain the roadblock well enough to outsource it! Thanks so much!)
I want to have a button with a 3D effect, so if you press it, it looks like the button is literally pressed down (inside the 'ground').
Like this:
But i want to save file size and only save the background texture without the text on it. So i am using TextButtons and tried to detect if the button is 'held down' like this:
tbs1 = new TextButton.TextButtonStyle();
tbs1.up = new TextureRegionDrawable(Game.res.getAtlas("buttons_3").findRegion("button3_green_normal"));
tbs1.down = new TextureRegionDrawable(Game.res.getAtlas("buttons_3").findRegion("button3_green_pressed"));
tbs1.font = FontManager.bignoodle_90;
quickMatchButton = new TextButton("Quick Match", tbs1);
quickMatchButton.getLabelCell().padBottom(ASR.h(50));
quickMatchButton.addListener(new ChangeListener() {
#Override
public void changed(ChangeEvent event, Actor actor) {
Game.gsm.setState(Game.gsm.CONFIGUREMATCH);
}
});
quickMatchButton.addListener(new ClickListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button){
//This is not working!
quickMatchButton.getLabelCell().padBottom(ASR.h(100));
return true;
}
});
The problem is that (i think) i can't change the position of the Text Label inside the button after i added the button to my layout table.
So main question: How can i change the position (or the padding) of the TextLabel inside my button, after i created the button?
Thanks for your time!
you can try this:
tbs1.pressedOffsetY = -3;
this works for ImapgeButton, as pressedOffsetY belongs to ButtonStyle, it should work with text buttons too.
I want to know the algorithm to cycle through the sample array below in a order like color1 -> color3 -> color 2 when a button gets clicked each time.
int[] colorList = new int[]{color1, color2, color3};
Currently, Im trying to do it like the sample below but unable to do it.
If possible would you give me a helping hand ? I cannot find a solution to transition in a order I desire. I would love to hear from you!
void updateIndicators(int position) {
for (int i = 0; i < indicators.length; i++) {
indicators[i].setBackgroundResource(
i == position ? R.drawable.indicator_selected : R.drawable.indicator_unselected
);
}
}
Maybe something like this?
public int getNextPosition(int prevPosition){
int maxPosition=2;
return (prevPosition+maxPosition)%(maxPosition+1);
}
This returns 2 when input is 0
1 when input is 2
and 0 when input is 1
So colorList[0]->colorList[2]->colorList[1]
You can also add another function to return the color:
public int nextColor(int prevPosition){
return colorList[getNextPosition(prevPosition)];
}
First declare global variable on your activity class file like below :
int buttonclickcount=0;
In your clicklistener:
yourbutton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
buttonclickcount++;
if(buttonclickcount%3==0)
{
//set color 1 from array
}
else if(buttonclickcount%3==1)
{
//set color 2 from array
}
else if(buttonclickcount%3==2)
{
//set color 3 from array
}
}
});
I have one main layout with one LinearLayout. In this LinearLayout I inflates multiple xml layouts that contain a form with multiple text fields(3 EditText) in it.
On first attempt I show only one form. There is button for adding more forms. Suppose If user clicks on "Add" button two times then user have three total forms. I successfully get all data of these three forms.
For doing this I am targeting the main layout "LinearLayout" and then counting its child. After counting its child I called child views of Main LinearLayout by its position and then get EditText data and save into a list. Then I moved this list to next page. Everything works fine till then. But if user comes back on previous page, all inflated layouts were gone. So, I count the size of list on resume and set the values what users wrote last time.
The problem is when I set the values of EditText according to its view position. Only last object(of list) value is shown in all inflated layouts. Means when for loop ends it sets last object value in all layouts. This is my method for setting values against a view:
private void addFormDataOnResume(LinearLayout viewForm,Traveller otherTraveller)
{
EditText dateOfBirthEt = (EditText)viewForm.findViewById(R.id.dateOfBirth);
dateOfBirthEt.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
showDatePickerDialog(dateOfBirthEt);
}
});
dateOfBirthEt.setText(otherTraveller.getOtherDateOfBirth());
EditText firstNameET = (EditText)viewForm.findViewById(R.id.firstName);
firstNameET.setText(otherTraveller.getOtherFirstName());
EditText lastNameEt = (EditText)viewForm.findViewById(R.id.lastName);
lastNameEt.setText(otherTraveller.getOtherLastName());
}
My loop code:
int otherTraSize = otherTravellersData.size();
//adultsForms is the main linear layout in which I am adding views
for(k=0; k < otherTraSize; k++)
{
addFormOnResume();//Function for adding form layout
int viewPos = adultsForms.getChildCount();
if(viewPos>0)
{
addFormDataOnResume(adultsForms.getChildAt(viewPos-1), otherTravellersData.get(k));
}
}
My FUnction for adding forms:
private void addFormOnResume()
{
LinearLayout viewForm = (LinearLayout)layoutInflater.inflate(R.layout.other_traveller_form, null );
adultsForms.addView(viewForm);
}
I debug my code, all data of list seems fine. Objects are in proper order with proper values.
Please help me why only last object value is set to all of the inflated forms.
Any help would be really appreciated......Thanks in Advance.
Finally I got the answer of my own question. By default when an activity goes in onPause mode. Android saves the EditText value against an id and when we resume it restore the last saved value of EditText. So, in my case it restoring last form values in all layout forms. The solution of my problem is to set android:saveEnabled="false" on the widget entry on the XML. I added this in all EditText. Android no longer saves my data and onResume I can set value of my form fields whatever I want to, by simply getting childView from specified position even if they have same Id.
Replace your loop code with this:
int otherTraSize = otherTravellersData.size();
//adultsForms is the main linear layout in which I am adding views
for(k=0; k < otherTraSize; k++)
{
addFormOnResume();//Function for adding form layout
}
int viewPos = adultsForms.getChildCount();
if(viewPos>0)
{
for(int x=0; x<viewPos; x++){
Log.d("current value",otherTravellersData.get(x).getOtherFirstName());
addFormDataOnResume(adultsForms.getChildAt(viewPos-1), otherTravellersData.get(x));}
}
Its my working code
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnAdd:
view = layoutInflater.inflate(R.layout.fragment_add_materials, null);
parentLinearLayout.addView(view);
arrayList.add(view);
edtMaterialName = (EditText) view.findViewById(R.id.edtinput_material1);
editTextCoast = (EditText) view.findViewById(R.id.editTextCoast1);
edtMaterialName.addTextChangedListener(getMyTextWatcher(edtMaterialName,arrayListMaterials.size()-1 ,"name"));
editTextCoast.addTextChangedListener(getMyTextWatcher(editTextCoast, arrayListMaterials.size()-1, "cost"));
break;
TextWatcher getMyTextWatcher(final EditText edtText, final int listPosition, String nameOrcost){
return new MyTextWatcher(edtText, listPosition, nameOrcost);
}
class MyTextWatcher implements TextWatcher{
int position;
EditText edtText;
String nameOrcost;
MaterialData data;
boolean flag;
int count;
public MyTextWatcher(EditText edtText1, int listPosition, String nameOrcost){
this.position = listPosition;
this.edtText=edtText1;
this.nameOrcost = nameOrcost;
data = arrayListMaterials.get(position);
}
#Override
public void afterTextChanged(Editable sEditable) {
try{
if(this.nameOrcost.equals("name") && edtText.getEditableText().toString().length()>0){
data.setMaterialName(edtText.getEditableText().toString());
}
if(this.nameOrcost.equals("cost") && edtText.getEditableText().toString().trim().length()>0){
String tempCost = edtText.getEditableText().toString().trim();
NumberFormat myFormatter = NumberFormat.getInstance();
Number number = myFormatter.parse(tempCost);
data.setMaterialprice(new BigDecimal(number.toString()).toString());
}
arrayListMaterials.set(position, data);
setTotalPrice(arrayListMaterials);
}catch (Exception e) {
e.printStackTrace();
}
if(this.nameOrcost.equals("cost") && edtText.getEditableText().toString().trim().length()>0){
Utilty.insertCommaIntoNumber(edtText, sEditable.toString());
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
try{
if(this.nameOrcost.equals("cost"))
data.setMaterialprice("0");
if(this.nameOrcost.equals("name"))
data.setMaterialName("");
arrayListMaterials.set(position, data);
}catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
}
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;