CursorAdapter 中的 Android ViewHolder 导致 listView 被搞砸

发布于 2024-12-06 14:57:36 字数 5493 浏览 0 评论 0原文

在过去的几天里我一直在努力解决这个问题,我希望你能帮助我...

我有一个活动,通过设置这样的列表适配器来显示玩家列表:

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);
    }
}

现在的问题是,删除一些之后列表中的玩家,列表被搞砸了,例如。它显示的内容与实际可用的内容不同。

我已经做了一些实验,如果我停止在bindView中使用PlayerViewHolder,而是从光标读取文本并将其直接分配给文本字段,那么它就可以工作......所以问题是,为什么我的ViewHolder把事情搞砸了???

任何帮助将不胜感激!

谢谢!

  • Zyb3r

I've been struggeling in the past few days trying to figure this out, I hope you can help me...

I have an Activity that shows a list of Players by setting a listadapter like this:

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

When clicking an item in the list, this code will be executed showing a dialog with an "Edit" and "Delete" option for editing and removing players:

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();
        }

Based on your choice (Edit or delete player), the following listener will be executed:

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();

        }
    }
}

The code for the adapter is here:

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);
    }
}

Now, the problem is that after removing a few of the players in the list, the list gets screwed up, eg. it shows something different than what is actually available.

I've experimented a little and if I stop using the PlayerViewHolder in bindView and instead read the text from the cursor and assign it directly to the text fields, then it works.... So question is, why is my ViewHolder screwing up things???

Any help will be greatly appreciated!

Thanks!

  • Zyb3r

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

¢蛋碎的人ぎ生 2024-12-13 14:57:36

找到了解决方案...

基本上,当我更改数据库中的数据时,我会重新初始化 Cursor 和 ListAdapter,并将 ListAdapter 重新分配给 ListView 。

我不完全确定为什么这是必要的,但是 notifyDataSetChanged()、notifyDataSetInvalidated() 和我尝试过的所有其他方法都不起作用,所以现在我正在使用这种方法。 :o)

  • Zyb3r

Found a solution...

Basically I reinitialize the Cursor and ListAdapter plus assigns the ListAdapter to the ListView all over again when I change the data in the database.

I'm not entirely sure why this is nessasary, but notifyDataSetChanged(), notifyDataSetInvalidated() and all the other things I tried didn't work, so now I'm using this approach. :o)

  • Zyb3r
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文