2

I have a custom adapter for displaying and getting user input from a ListView. Rows in the list contain a TextView, EditText and a Spinner. It's the spinner that is causing me trouble. The application concerns sailing race scoring where a competitor's result can be either a finish position (entered in the EditText) or a result code (selected using the spinner from a predetermined set of values). There seem to be a lot of people with similar problems to mine but nothing quite like what I have here.

The problem is that the OnItemSelectedListener is only triggered in certain situations and not every time I need it to trigger and the situations depend on whether or not the corresponding EditText has focus or not or if the EditText in another row has focus.

  1. When the list is first displayed, nothing shows focus and I can select codes using the spinner in any row of the list and the listener is triggered every time.

  2. If an EditText has focus (whether the popup keyboard is shown or not) the spinner in the same row works just fine and the listener triggers. However in the case of the last row of the list I can't get it to trigger at all.

  3. In the case of the last row, if I click in the EditText of a different row and then try selecting using the spinner of the last row, the listener triggers. This is the weirdest part of all.

I have tried out lots of the solutions to other related problems but no luck so far. This includes messing with the focusability of the spinner and the EditText; adding an OnTouchListener to the spinner to forcibly move the focus around between the items or deliberately clearing focus. Any help or suggestions of what to try next greatly appreciated as this is driving me nuts and for now I need to move on to another unrelated bit of functionality in the hope that this is fixable somehow in the future.

Here is the xml for the row

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:orientation="vertical">
 <TextView
     android:id="@+id/race_line"
     android:textSize="16sp"
     android:textStyle="bold"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:paddingRight="10dip"
     android:text="@string/race_line"
 />
 <TextView
     android:id="@+id/race_number"
     android:textSize="16sp"
     android:textStyle="bold"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:paddingRight="10dip"
     android:layout_toRightOf="@id/race_line"
 />
 <EditText
    android:id="@+id/result"
    android:layout_width="100dip"
    android:layout_height="wrap_content"
    android:layout_toRightOf="@id/race_number"
    android:inputType="number"
    android:imeOptions="actionDone"
    android:imeActionLabel="Done"
    android:focusableInTouchMode="true"
    android:focusable="true"
 />
 <Spinner 
    android:id="@+id/result_code_spinner"
    android:layout_width="100dip"
    android:layout_height="wrap_content"
    android:prompt="@string/result_prompt"
    android:layout_toRightOf="@id/result"
    android:focusableInTouchMode="false"
    android:focusable="true"
 />
 </RelativeLayout>

And here is the code

public class ResultsEntryListAdapter extends BaseAdapter {
protected static ArrayList<resultObj> combinedList;
private Context mContext;

private LayoutInflater mInflater;
public ResultsEntryListAdapter(Context context, ArrayList<resultObj> listToDisplay) {
    combinedList = listToDisplay;
    mInflater = LayoutInflater.from(context);
    mContext = context;
}

public int getCount() {
    return combinedList.size();
}

public Object getItem(int position) {
    return combinedList.get(position);
}

public long getItemId(int position) {
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final int pos = position;
    final ViewHolder holder;
    // Add an on click listener to this method that will update the source data in the calling activity when items change
    if (convertView == null) {
        convertView = mInflater.inflate(R.layout.result_row, null);
        holder = new ViewHolder();
        holder.race_line = (TextView) convertView.findViewById(R.id.race_line);
        holder.race_number = (TextView) convertView.findViewById(R.id.race_number);
        holder.result = (EditText) convertView.findViewById(R.id.result);
        holder.result_code_spinner = (Spinner) convertView.findViewById(R.id.result_code_spinner);
        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
                mContext, R.array.result_codes, android.R.layout.simple_spinner_item);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        holder.result_code_spinner.setAdapter(adapter);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }

    /* If the user enters a result code using the spinner then put the result code into the
     * data structure and set the numerical result (string) to a null value.
     */
     final OnItemSelectedListener spinnerListener = new OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> av, View v, int spinPosition, long id) {
            int spin_position = spinPosition;
            // Stick the result of the selection back into the data bound to the view
            // This stops the selection vanishing when the view is scrolled off the screen
            combinedList.get(pos).setSpinPosition(spin_position);
            int result = 0;
            if (spin_position != 0) {                   result = 0;
                if (spin_position == 11) {
                    // Special case of RDG with score:
                    // Do nothing with the result as we have to take it as entered
                    holder.result_code_spinner.setSelection(11);
                } else if (spin_position == 15) {
                    // Another special case ZFP. Take score as entered here as well
                    holder.result_code_spinner.setSelection(15);
                } else {
                    // put the encoded result into the data structure
                    combinedList.get(pos).setResult(Integer.toString(result));
                    holder.result.setText("");
                }
            }
        }

        @Override
        public void onNothingSelected(AdapterView<?> arg0) {
            // Do nothing, change nothing

        }
    };

    /* The result value needs to be saved if one of several things happen:
     * 1. The focus moves away from the cell being edited
     * 2. The done/return key is pressed on the keyboard
     * 3. The back button is pressed (to dismiss the keyboard)
     */
    class DoneOnEditorActionListener implements OnEditorActionListener {
        @Override
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            if (actionId == EditorInfo.IME_ACTION_DONE) {
                InputMethodManager imm = (InputMethodManager)v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
                EditText et = (EditText) v;
                String sResult = et.getText().toString();
                int result = sResult.equals("") ? 0 : Integer.parseInt(sResult);
                if (result > 0 ) {
                    combinedList.get(pos).setResult(sResult);
                } else if (result == 0) {
                    combinedList.get(pos).setResult("");
                }
                // Special case of RDG needs to retain the spin position if a result is then entered
                if (combinedList.get(pos).getSpinPosition() != 11 && combinedList.get(pos).getSpinPosition() != 15) {
                    combinedList.get(pos).setSpinPosition(0);
                }
                holder.result_code_spinner.setSelection(0);
                //holder.result.clearFocus(); //move focus away
                return true;    
            }
            return true;
        }
    }


    final OnFocusChangeListener resultFocusListener = new OnFocusChangeListener() {
        @Override
        // Called when the user touches the editText to enter a score
        public void onFocusChange(View v, boolean hasFocus) {
            EditText et = (EditText) v;
            if (hasFocus == false) {
                String sResult = et.getText().toString();
                int result = sResult.equals("") ? 0 : Integer.parseInt(sResult);
                if (result > 0 ) {
                    combinedList.get(pos).setResult(sResult);
                } else if (result == 0) {
                    combinedList.get(pos).setResult("");
                }
                int spinPosition = combinedList.get(pos).getSpinPosition();
                if (spinPosition != 11 && spinPosition != 15) {
                    combinedList.get(pos).setSpinPosition(spinPosition);
                    holder.result_code_spinner.setSelection(spinPosition);
                    holder.result.clearFocus();
                }
            }
        }

    };


    /* If the user enters a numerical result then check it is positive and put it into the
     * data structure. At the same time set the spin position to 0 to indicate this is a 
     * valid result.
     */

    holder.result_code_spinner.setOnItemSelectedListener(spinnerListener);
    holder.result.setOnEditorActionListener(new DoneOnEditorActionListener());
    holder.result.setOnFocusChangeListener(resultFocusListener);

    // The race_line just says "Race:" for now but in future we may want to allow races to have names and fill in the name here
    holder.race_line.setText(R.string.race_line);
    holder.race_number.setText(combinedList.get(position).getRaceNumber());
    String sResult = combinedList.get(position).getResult();
    int spinPosition = combinedList.get(position).getSpinPosition();
    if (sResult.equals("")) {
        holder.result.setText("");
        holder.result_code_spinner.setSelection(spinPosition); // spin position may be 0 or +ve, display it anyway
    } else { // result is non-zero so need to display either the value or the spin position
        int result = Integer.parseInt(sResult);
        if (spinPosition == 11 || spinPosition == 15) {
            // Display both spinner and result text
            holder.result.setText(Integer.toString(result));
            holder.result_code_spinner.setSelection(spinPosition);
        } else if (result > 0) {
            holder.result.setText(Integer.toString(result));
            holder.result_code_spinner.setSelection(0);
        } else {
            holder.result.setText("");
            holder.result_code_spinner.setSelection(combinedList.get(position).getSpinPosition());
        }
    }
    return convertView;
}

static class ViewHolder {
    TextView race_line;
    TextView race_number;
    EditText result;
    Spinner result_code_spinner;

}
}
4

0 回答 0