How to get Spinner inside ListView work in Android

2019-09-19 03:33发布

I am developing an app in which I need a ListView whose rows have a TextView, 2 CheckBox and a Spinner.

However, I am experiencing issues with onItemSelected() of the Spinner, as it gets called each time it is displayed for each row. In this method I am updating database records with the selected option, but as Android calls it automatically, every time the items get reset because Android calls it with position 0 and this is the value updated in the database.

I have read a lot of links about the issue with onItemSelected() and some hacks, but all of them are to use without a ListView. Any points here?

I have tried to track in a List which positions are actually displayed to make it work but it does not. I think it is because of the recycling in Android that causes the troubleshooting method get called for Spinners already shown!

So the point is: How can I differenciate a real call to onItemSelected() because of a user selection from the Android call when displaying the Spinner?

Here is the code of my adapter that extends SimpleCursorAdapter.

Thank you so much in advance.

public ParticipationAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
    super(context, layout, c, from, to);
    mActivity = (Activity)context;
    ParticipationComment.ParticipationCommentManager commentManager = new ParticipationComment.ParticipationCommentManager(mActivity);
    mParticipationCommentsCursor = commentManager.get();
    mActivity.startManagingCursor(mParticipationCommentsCursor);
    commentManager.detach();
    mPositionsOfCursorIds = getPositionsOfCursorIds(mParticipationCommentsCursor);
    mSpinnerPositionsDisplayed = new ArrayList<Integer>();
}

@Override
public View getView(final int participationPosition, View convertView, ViewGroup parent) {
    final Cursor participationsCursor = getCursor();
    mActivity.startManagingCursor(participationsCursor);
    participationsCursor.moveToPosition(participationPosition);
    View participationRow;
    if (convertView == null) {
        participationRow = LayoutInflater.from(mActivity).inflate(R.layout.participation_row_student, null);
    } else {
        mSpinnerPositionsDisplayed.remove((Integer)convertView.getTag());
        participationRow = convertView;
    }
    participationRow.setTag(participationPosition);
    Spinner commentSpinner = (Spinner)participationRow.findViewById(R.id.participation_comment_id_spinner);
    SimpleCursorAdapter commentSpinnerAdapter = new SimpleCursorAdapter(
            mActivity,
            android.R.layout.simple_spinner_item,
            mParticipationCommentsCursor,
            new String[] {DatabaseManager.NAME},
            new int[] {android.R.id.text1}
    );
    commentSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    commentSpinner.setAdapter(commentSpinnerAdapter);
    long participationCommentId = participationsCursor.getLong(participationsCursor.getColumnIndex(DatabaseManager.PARTICIPATION_COMMENT_ID));
    if (participationCommentId != 0) {
        commentSpinner.setSelection(mPositionsOfCursorIds.get(participationCommentId));
    }
    commentSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            participationsCursor.moveToPosition(participationPosition);
            if (!mSpinnerPositionsDisplayed.contains(participationPosition)) {
                // Android calls this method the first time a Spinner is displayed,
                // to differentiate from a real user click we check if the current Spinner's position
                // in the ListView is being shown
                mSpinnerPositionsDisplayed.add(participationPosition);
            } else {
                ParticipationComment participationComment = new ParticipationComment((Cursor)parent.getItemAtPosition(position));
                Participation.ParticipationManager participationManager = new Participation.ParticipationManager(mActivity);
                Participation participation = new Participation(participationsCursor);
                participation.setConnectionProfileParticipationCommentId(participationComment.getConnectionProfileId());
                participation.setParticipationCommentId(participationComment.getIdOpenErp());
                participation.setChanged(true);
                participationManager.update(participation);
                participationManager.detach();
            }
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
            // Not used
        }
    });
    TextView studentName = (TextView)participationRow.findViewById(R.id.participation_student_name);
    studentName.setText(participationsCursor.getString(participationsCursor.getColumnIndex(DatabaseManager.NAME)));
    CheckBox expectedPresent = (CheckBox)participationRow.findViewById(R.id.participation_expected_present_value);
    expectedPresent.setChecked(participationsCursor.getInt(participationsCursor.getColumnIndex(DatabaseManager.EXPECTED_PRESENT)) == 1);
    CheckBox present = (CheckBox)participationRow.findViewById(R.id.participation_present_value);
    present.setChecked(participationsCursor.getInt(participationsCursor.getColumnIndex(DatabaseManager.PRESENT)) == 1);
    return participationRow;
}

2条回答
再贱就再见
2楼-- · 2019-09-19 03:41

What about using a small flag to discard first call of ItemSelected ?

查看更多
Ridiculous、
3楼-- · 2019-09-19 03:59

A better way is to use a AlertDialog Variant.. like this.. and create a button which initially has the first selection as its Text and its changed based on the AlertDialog choice..

查看更多
登录 后发表回答