Android ViewHolder в CursorAdapter, приводящий к повреждению listView - PullRequest
0 голосов
/ 26 сентября 2011

В последние несколько дней я боролся с этим, пытаясь понять это, надеюсь, вы мне поможете ...

У меня есть активность, в которой отображается список игроков, для которой настроен листадаптер, например:

PlayerCursorAdapter playerAdapter = new PlayerCursorAdapter(this,
                                         R.layout.players_row, c, columns, to);
setListAdapter(playerAdapter);

При нажатии на элемент в списке этот код будет выполнен, показывая диалог с опциями «Редактировать» и «Удалить» для редактирования и удаления игроков:

private class OnPlayerItemClickListener implements OnItemClickListener {
        public void onItemClick(AdapterView<?> parent, View view, int position,
                long rowId) {
            Toast.makeText(view.getContext(),
                    "Clicked Item [" + position + "], rowId [" + rowId + "]",
                    Toast.LENGTH_SHORT).show();

            // Prepare Dialog with "Edit" and "Delete" option
            final CharSequence[] choices = {
                    view.getContext().getString(R.string.buttonEdit),
                    view.getContext().getString(R.string.buttonDelete) };

            AlertDialog.Builder builder = new AlertDialog.Builder(
                    view.getContext());
            builder.setTitle(R.string.title_edit_delete_player);
            builder.setItems(choices, new EditOrDeleteDialogOnClickListener(
                    view, rowId));

            AlertDialog alert = builder.create();

            // Show Dialog
            alert.show();
        }

В зависимости от вашего выбора (Редактировать или удалить игрока) будет исполнен следующий слушатель:

private class EditOrDeleteDialogOnClickListener implements
        DialogInterface.OnClickListener {

    private View view;
    private long rowId;

    public EditOrDeleteDialogOnClickListener(View view, long rowId) {
        this.view = view;
        this.rowId = rowId;
    }

    public void onClick(DialogInterface dialog, int item) {
        if (item == 0) {
            // Edit
            showDialog(PlayGameActivity.DIALOG_EDIT_PLAYER_ID);
        } else if (item == 1) {
            // Delete from database
            DatabaseHelper databaseHelper = new DatabaseHelper(
                    view.getContext());
            databaseHelper.deletePlayer(rowId);

            // Requery to update view.
            ((PlayerCursorAdapter) getListAdapter()).getCursor().requery();

            Toast.makeText(
                    view.getContext(),
                    view.getContext().getString(
                            R.string.message_player_removed)
                            + " " + rowId, Toast.LENGTH_SHORT).show();

        }
    }
}

Код для адаптера здесь:

public class PlayerCursorAdapter extends SimpleCursorAdapter {

    private LayoutInflater layoutInflater;
    private int layout;

    public PlayerCursorAdapter(Context context,
            int layout, Cursor c, String[] from, int[] to) {
        super(context, layout, c, from, to);
        this.layout = layout;
        layoutInflater = LayoutInflater.from(context);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        Cursor c = getCursor();
        View view = layoutInflater.inflate(layout, parent, false);

        // Get Data
        int nameCol = c.getColumnIndex(Player.COLUMN_PLAYER_NAME);
        String name = c.getString(nameCol);
        int gamesPlayedCol = c.getColumnIndex(Player.COLUMN_GAMES_PLAYED);
        String gamesPlayed = c.getString(gamesPlayedCol);
        int gamesWonCol = c.getColumnIndex(Player.COLUMN_GAMES_WON);
        String gamesWon = c.getString(gamesWonCol);

        // Set data on fields
        TextView topText = (TextView) view.findViewById(R.id.topText);
        if (name != null)
            topText.setText(name);

        TextView bottomText = (TextView) view.findViewById(R.id.bottomText);
        if (gamesPlayed != null && gamesWon != null)
            bottomText.setText(view.getContext().getString(
                    R.string.info_played_won)
                    + gamesPlayed + "/" + gamesWon);

        CheckBox checkBox = (CheckBox) view.findViewById(R.id.checkBox);

        // Set up PlayerViewHolder
        PlayerViewHolder playerViewHolder = new PlayerViewHolder();
        playerViewHolder.playerName = name;
        playerViewHolder.gamesPlayed = gamesPlayed;
        playerViewHolder.gamesWon = gamesWon;
        playerViewHolder.isChecked = checkBox.isChecked();
        view.setTag(playerViewHolder);
        return view;
    }

    private class PlayerViewHolder {
        String playerName;
        String gamesPlayed;
        String gamesWon;
        boolean isChecked;
    }

    @Override
    public void bindView(View view, Context context, Cursor c) {
        PlayerViewHolder playerViewHolder = (PlayerViewHolder) view.getTag();

        TextView topText = (TextView) view.findViewById(R.id.topText);
        topText.setText(playerViewHolder.playerName);

        TextView bottomText = (TextView) view.findViewById(R.id.bottomText);
        bottomText.setText(view.getContext()
                .getString(R.string.info_played_won)
                + playerViewHolder.gamesPlayed
                + "/"
                + playerViewHolder.gamesWon);

        CheckBox checkBox = (CheckBox) view.findViewById(R.id.checkBox);
        checkBox.setChecked(playerViewHolder.isChecked);
    }
}

Теперь проблема в том, что после удаления нескольких игроков из списка, список облажался, например. это показывает что-то отличное от того, что действительно доступно.

Я немного поэкспериментировал, и если я перестану использовать PlayerViewHolder в bindView и вместо этого прочитаю текст из курсора и назначу его непосредственно текстовым полям, тогда он будет работать ... Так что вопрос в том, почему мой ViewHolder испортить вещи ???

Любая помощь будет принята с благодарностью!

Спасибо!

  • Zyb3r

1 Ответ

0 голосов
/ 28 сентября 2011

Нашли решение ...

По сути, я повторно инициализирую Cursor, и ListAdapter plus снова назначает ListAdapter для ListView, когда я изменяю данные в базе данных.

Я не совсем уверен, почему это nessasary, но notifyDataSetChanged (), notifyDataSetInvalidated () и все другие вещи, которые я пробовал, не работали, поэтому сейчас я использую этот подход. : О)

  • Zyb3r
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...