4

OK, I've read around and see that Java only passes by value, not by reference so I don't know how to accomplish this.

  • I've 6 Spinners in an Android Activity that are populated with different SQLite queries.
  • The code to populate each Spinner and set the OnItemSelectedListener is very similiar so I was hoping to refactor to one method and call it 6 times with each Spinner ID and Sqlite query.
  • How do I get the Spinner onItemSelectedListener to change the right instance member on each different Spinner?

    public void fillSpinner(String spinner_name, final String field_name) {
    // This finds the Spinner ID passed into the method with spinner_name
    // from the Resources file. e.g. spinner1
    int resID = getResources().getIdentifier(spinner_name, "id",
            getPackageName());
    Spinner s = (Spinner) findViewById(resID);
    final Cursor cMonth;
    // This gets the data to populate the spinner, e.g. if field_name was
    // strength = SELECT _id, strength FROM cigars GROUP BY strength
    cMonth = dbHelper.fetchSpinnerFilters(field_name);
    startManagingCursor(cMonth);
    String[] from = new String[] { field_name };
    int[] to = new int[] { android.R.id.text1 };
    SimpleCursorAdapter months = new SimpleCursorAdapter(this,
            android.R.layout.simple_spinner_item, cMonth, from, to);
    months.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    s.setAdapter(months);
    // This is setting the Spinner Item Selected Listener Callback, where
    // all the problems happen
    s.setOnItemSelectedListener(new OnItemSelectedListener() {
        public void onItemSelected(AdapterView<?> parent, View view,
                int position, long id) {
            Cursor theCursor = (Cursor) parent.getSelectedItem();
            // This is the problem area.
            object_reference_to_clas_member_of_field_name = theCursor
                    .getString(theCursor.getColumnIndex(field_name));
        }
    
        public void onNothingSelected(AdapterView<?> parent) {
            // showToast("Spinner1: unselected");
        }
    });
    

    }

You call this method like this fillSpinner("spinner1","strength");.

It finds the spinner with id spinner1 and queries the database for the strength field. field_name, which is strength in this example had to be declared a final variable to be used in the onItemSelectedListener or I'd get the error Cannot refer to a non-final variable field_name inside an inner class defined in a different method.

But how do I get the onItemSelectedListener to change the value of a different instance member when each different Spinner is used? This is the all important line of code: object_reference_to_clas_member_of_field_name = theCursor .getString(theCursor.getColumnIndex(field_name));

I can't use a final String as the variable will obviously change when the user selects a different value. I've read around a good bit and am stumped to a solution. I can just copy and paste this code 6 times and forget about refactoring but I'd really like to know the elegant solution. Post a comment if you don't understand my question, I'm not sure if I explaned myself well.

Community
  • 1
  • 1
georgiecasey
  • 21,793
  • 11
  • 65
  • 74
  • actually, it is not clear what do you want and what problem you are facing in onItemSelected. Please elaborate more. – jeet Jan 18 '12 at 04:59
  • `Cannot refer to a non-final variable field_name inside an inner class defined in a different method.`, why don't you declare `field_name` globally in the main class. – Lalit Poptani Jan 21 '12 at 04:35

3 Answers3

1

Refactor your listener to a new "class". Initialize with the right arguments/instances as required so that the repeated "code" is reusuable.

panzerschreck
  • 3,582
  • 2
  • 20
  • 29
1

You can do it, by passing additional class as parameter of fillSpinner method:

A. Create interface

public interface OnSpinnerValueSelected {
    void onValueSelected(String selectedValue);
}

B. Change your method a bit:

public void fillSpinner(String spinner_name, final String field_name,
                    final OnSpinnerValueSelected valueChangeListener) {

    // Prepare spinner

    s.setOnItemSelectedListener(new OnItemSelectedListener() {

        public void onItemSelected(AdapterView<?> parent, View view,
                     int position, long id) {
            Cursor theCursor = (Cursor) parent.getSelectedItem();

            valueChangeListener.onValueSelected(theCursor
                           .getString(theCursor.getColumnIndex(field_name)));
        }

        public void onNothingSelected(AdapterView<?> parent) {

        }
    });
}

C. provide listener:

fillSpinner("spinner1","strength", new OnSpinnerValueSelected() {
    public void onValueSelected(String selectedValue) {
        yourObject.setField(selectedValue);
    }
});
Jin35
  • 8,602
  • 3
  • 32
  • 52
  • Great, this worked and just seems better than having hardcoded variable names in the refactored function itself. So in Java you can't pass a String object by reference, but you can pass a final object that has a method to set the value of the String object. – georgiecasey Jan 21 '12 at 14:38
0

Right, this is how I managed it but I'm still open to new suggestions for an accepted answer and I also created a bounty.

I didn't create a new class like panzerschreck suggested so I'm posting this as a new answer to my own question. Bit of a hack but I just created an if..then..else statement in the listener to check what spinner was selected and then set a different instance member.

s.setOnItemSelectedListener(new OnItemSelectedListener() {
        public void onItemSelected(AdapterView<?> parent, View view,
                int position, long id) {
            Cursor theCursor = (Cursor) parent.getSelectedItem();
            if (field_name.equalsIgnoreCase("strength")) {
                strength=theCursor.getString(theCursor.getColumnIndex(field_name));
            } else if (field_name.equalsIgnoreCase("ring")) {
                ring_gauge=theCursor.getString(theCursor.getColumnIndex(field_name));
            } else if (field_name.equalsIgnoreCase("country")) {
                country=theCursor.getString(theCursor.getColumnIndex(field_name));
            } else if (field_name.equalsIgnoreCase("wrapper")) {
                wrapper=theCursor.getString(theCursor.getColumnIndex(field_name));
            } else if (field_name.equalsIgnoreCase("length")) {
                length=theCursor.getString(theCursor.getColumnIndex(field_name));
            } else if (field_name.equalsIgnoreCase("price")) {
                price=theCursor.getString(theCursor.getColumnIndex(field_name));
            } 
            // showToast(category);
        }

        public void onNothingSelected(AdapterView<?> parent) {
            // showToast("Spinner2: unselected");
        }
    });

Here are the class members

private String strength,ring_gauge,country,wrapper,length,price;

Bit of hack but without Java allowing objects to be really passed by reference, it's all I could do.

Community
  • 1
  • 1
georgiecasey
  • 21,793
  • 11
  • 65
  • 74
  • 1
    I must say its not a good practice to put 'if' statements. Avoid hard coded values too where ever possible, it's a maintenance nightmare ! – panzerschreck Jan 19 '12 at 03:30
  • I couldn't use a switch statement on Strings if that's what you meant. I know, I didn't want to use hard coded values either but what other choice is there. I'd love to know the elegant solution to this. I tried creating a new class as a listener like you suggested but again the problem is how I pass instance members by reference and not by value to the constructor of this new listener class. – georgiecasey Jan 19 '12 at 03:50