适配器 onClickListener 没有在应该触发的时候触发,或者正确更新视图

Adapter onClickListener not firing when it should, or updating View correctly

求助!这是有效的,但没有版本控制。

评分按钮正在更新一个 TextView 从被点击的按钮向下几行,并且 Android 我为解决问题而输入的 Studio 日志甚至没有记录按钮已被点击。

整个事情都在横向发展,问题是'为什么点击 plusButton 和 minusButton 会在我想要更新内容的 'active' 行下方的几行中更新一个 TextView。

这是一张图片,我点了4次小熊,它更新了Player 4的得分TextView,wth?

package com.basketball.dating;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.ArrayList;

/**
 * {@link ScoreBoardAdapter} is an {@link ArrayAdapter} that can provide the layout for each list item
 * based on a data source, which is a list of {@link ScoreBoard} objects.
 */
public class ScoreBoardAdapter extends ArrayAdapter<ScoreBoard>  {

    /** Resource ID for the background color for this list of words */
    private int mColorResourceId;

    final String TAG = "ScoreBoardAdapter";
    public ScoreHolder holder = new ScoreHolder();

    /**
     * Create a new {@link ScoreBoardAdapter} object.
     *
     * @param context is the current context (i.e. Activity) that the adapter is being created in.
     * @param scoreBoards is the list of {@link ScoreBoard}s to be displayed.
     */
    public ScoreBoardAdapter(Context context, ArrayList<ScoreBoard> scoreBoards) {
        super(context, 0, scoreBoards);
        //mColorResourceId = new ContextCompat().getColor(getContext(), R.color.colorAccent);
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        // Check if an existing view is being reused, otherwise inflate the view
        View listItemView = convertView;
        //ScoreHolder holder = new ScoreHolder();

        if (listItemView == null) {
            listItemView = LayoutInflater.from(getContext()).inflate(
                    R.layout.list_item, parent, false);
            // now stuff all the row's views into a ScoreHolder object
            holder.plusButton = (TextView) listItemView.findViewById(R.id.plusButton);
            holder.minusButton = (TextView) listItemView.findViewById(R.id.minusButton);
            holder.scoreTextView = (TextView) listItemView.findViewById(R.id.score_text_view);

            // now attach these details to the row, so it 'remembers' they're there
            listItemView.setTag(holder);
        }
        else {
            Log.wtf("ScoreBoardADAPTER", "NOT_NULL ROW");
            holder = (ScoreHolder) listItemView.getTag();
        }
        holder.plusButton.setOnClickListener(new View.OnClickListener() {
            //private int pos = position;
            public void onClick(View v) {
                //TextView mPlusButton = (TextView) v;
                Log.wtf(TAG, "hit increment " + position);
                int score = Integer.parseInt((String) holder.scoreTextView.getText());
                if (score < 99) {
                    holder.scoreTextView.setText(String.valueOf(score + 1));
                }
            }
        });
        holder.minusButton.setOnClickListener(new View.OnClickListener() {
            //private int pos = position;
            public void onClick(View v) {
                //TextView mMinusButton = (TextView) v;
                int score = Integer.parseInt((String) holder.scoreTextView.getText());
                holder.scoreTextView.setText(String.valueOf(score - 1));
            }
        });

        // Get the {@link Word} object located at this position in the list
        ScoreBoard currentScoreBoard = getItem(position);

        // Find the TextView in the list_item.xml layout with the ID miwok_text_view.
        TextView playerNameTextView = (TextView) listItemView.findViewById(R.id.playerName);

        playerNameTextView.setText("Player " + String.valueOf(currentScoreBoard.getPlayerName()) );

        // Find the ImageView in the list_item.xml layout with the Avatar.
        ImageView imageView = (ImageView) listItemView.findViewById(R.id.playerAvatar);
        // display the provided image based on the resource ID
        imageView.setImageResource(currentScoreBoard.getAvatar());
        // Make sure the view is visible
        imageView.setVisibility(View.VISIBLE);

        // TODO adapt to colour the leaders gold, silver, bronze
//        // Set the theme color for the list item
//        View textContainer = listItemView.findViewById(R.id.text_container);
//        // Find the color that the resource ID maps to
//        int color = ContextCompat.getColor(getContext(), R.color.colorAccent);
//        // Set the background color of the text container View
//        textContainer.setBackgroundColor(color);

        // Return the whole list item layout so that it can be shown in the ListView.
        return listItemView;
    }

    /* need to be able to save an object of key textViews for each list item , or 'row' */
    static class ScoreHolder {
        TextView plusButton;
        TextView minusButton;
        TextView scoreTextView;
    }
}

您的直接问题是整个适配器只有一个 ScoreHolder 对象。创建了多个视图,但由于它们都使用相同的支架,所有 OnClickListeners 将更改最后创建的列表行的 TextView

大多数 AdapterViews,包括 ListView,都使用 Views 的池来存储他们的项目。 convertView 参数最有可能是 null 前几次 getView 被调用,当池已满时,您将开始接收非空对象作为 converViews .

TL;DR:您有多个列表项 Views 共享一个 ScoreHolder 对象。

尽量不要将其作为适配器的字段

public ScoreHolder holder = new ScoreHolder();

而且我认为你这样做的原因是因为你无法从 onClick 方法中获得对 holder 的引用,并且 IDE 试图使 holder 有效地最终化,但那不是这是可能的,因为您需要将 holder 分配给两个不同的事物,因此您只需将 holder 移动到一个字段即可。

TL;DR 尝试将其放回 getView 方法中

View listItemView = convertView;
final ScoreHolder holder = listItemView == null ? new ScoreHolder() : listItemView.getTag();

    if (listItemView == null) {
        listItemView = LayoutInflater.from(getContext()).inflate(
                R.layout.list_item, parent, false);

如果这不起作用(不记得三元是否对最终变量起作用),那么你可以将两个按钮的标签也设置为持有者,然后在两个点击监听器中获取持有者v

中的 onClick 方法