【问题标题】:Arrayadapter with an onclicklistener troubles带有 onclicklistener 问题的 Arrayadapter
【发布时间】:2014-01-17 20:36:33
【问题描述】:

这是我的自定义 arrayadapter 类,我想在单击它时更改每一行中按钮的图像资源,它可以工作,但另一行中的另一个按钮也会更改。感谢您的帮助!

public class Coursadapter extends ArrayAdapter<String> {

    Context context;
    int layoutResourceId;
    ArrayList<String> data = null;
    WeatherHolder holder;

    public Coursadapter(Context context, int layoutResourceId,
            ArrayList<String> data) {
        // super(context, layoutResourceId, data, coeff);
        super(context, layoutResourceId, data);
        this.layoutResourceId = layoutResourceId;
        this.context = context;
        this.data = data;

    }

    public View getView(int position, View convertView, ViewGroup parent) {
        View row = convertView;

        if (row == null) {
            LayoutInflater inflater = ((Activity) context).getLayoutInflater();
            row = inflater.inflate(layoutResourceId, null);

            holder = new WeatherHolder();
            holder.name = (TextView) row.findViewById(R.id.item_cours_name);
            holder.b = (ImageButton) row.findViewById(R.id.button);
            holder.b.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    WeatherHolder w = (WeatherHolder) v.getTag();
                    w.b.setImageResource(R.drawable.butgreen);
                }
            });
            row.setTag(holder);

        } else {
            holder = (WeatherHolder) row.getTag();
        }

        holder.b.setTag(holder);
        String name1 = data.get(position);
        holder.name.setText(name1);

        return row;
    }

    static class WeatherHolder {

        TextView name;
        ImageButton b;
    }
}

【问题讨论】:

标签: android eclipse view onclick android-arrayadapter


【解决方案1】:

您可以声明变量 int 来保存所选项目的位置和

public void onItemClick(AdapterView<?> adapter, View arg1, int position, long id) {
            mSelectedItem = position;
            mAdapter.notifyDataSetChanged();
    }

并在您的自定义适配器 getView 方法中检查位置并更改按钮的图像

public View getView(int position, View convertView, ViewGroup parent) {
        if (position == mSelectedItem) {
        // set your image
    }

看我和我一起尝试这段代码并工作,在你的活动中实现 OnClickListener

public class MainActivity extends Activity  implements OnClickListener

在你的 getView 方法中

public View getView(int position, View convertView, ViewGroup parent) {
        View row = convertView;

        if (row == null) {
            LayoutInflater inflater = ((Activity) context)
                    .getLayoutInflater();
            row = inflater.inflate(layoutResourceId, null);

            holder = new WeatherHolder();
            holder.name = (TextView) row.findViewById(R.id.item_cours_name);
            holder.b = (ImageButton) row.findViewById(R.id.button);
            holder.b.setOnClickListener(MainActivity.this);

并实现Onclick方法设置资源

@Override
public void onClick(View v) {
    WeatherHolder w = (WeatherHolder) v.getTag();
    w.b.setImageResource(android.R.drawable.star_big_on);
}

【讨论】:

  • 谢谢,但我的 arrayadapter 中没有 OnItemClick 方法,我在哪里可以设置该变量?
  • 在你的类中作为全局变量
  • 您没有onItemClick() 方法,因为它位于OnItemClickListener 中,只有当您希望在您单击Listview 项目中的任意位置时发生操作时才会使用该方法。你只希望它在你点击一个按钮时发生,所以这对你没有帮助。
  • 是的,我无法通过 OnClickListener 获取项目的位置
  • 看我尝试你的代码,当我点击图像按钮时,一个按钮只改变不是所有图像按钮,这是我正确的行为
【解决方案2】:

您的代码的问题是您的 OnClickListener 设置在错误的位置。

考虑getView() 中的 if/else 语句会发生什么。如果传递给该方法的convertView 为空,则可能是第一次创建 View(以及包含它的 ListView) - 例如,您正在启动包含该 ListView 的活动。如果它不为 null,则表示您正在回收一个曾经但现在不在屏幕上的视图。

如果您仔细观察,您会发现您的 OnClickListener 仅在第一种情况下设置 - 一个全新的视图。一旦你稍微滚动一下,你的监听器将永远不会正确地附加到那些来自屏幕外的视图。

更好的getView() 方法是:

public View getView(int position, View convertView, ViewGroup parent) {
    View row = convertView;

    if (row == null) {
        LayoutInflater inflater = ((Activity) context).getLayoutInflater();
        row = inflater.inflate(layoutResourceId, null);

        holder = new WeatherHolder();
        holder.name = (TextView) row.findViewById(R.id.item_cours_name);
        holder.b = (ImageButton) row.findViewById(R.id.button);

        row.setTag(holder);

    } else {
        holder = (WeatherHolder) row.getTag();
    }

    String name1 = data.get(position);
    holder.name.setText(name1);

    holder.b.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {                    
                ((ImageButton)v).setImageResource(R.drawable.butgreen);
            }
        });

    return row;
}

编辑:在我下面的评论的另一个修复中添加。

【讨论】:

  • 谢谢,但我试过了,我也遇到了同样的问题,另一行的另一个按钮也改变了
  • 另一个问题是在你的OnClick() 方法中,你做了很多不必要的工作。传递给它的View 是用户点击的Button。那就是你要改变颜色的那个,对吧?所以你不需要使用getTag(),你也绝对不需要使用setTag()。只需致电v.setImageResource(R.drawable.butgreen);
  • 我已经删除了 setTag() getTag(),我只是在 else 之后使用 OnClickListener with ((ImageView)v).setImageResource(R.drawable.butgreen);和同样的问题发生
【解决方案3】:

发生这种情况的原因是适配器在滚动时回收视图。

例如,假设您总共有 15 行,并且一次有 5 行适合屏幕。 if(row == null) 部分仅对前 5 行执行。当您向下滚动到第 6 行时,它使用第 1 行的视图。这就是为什么你应该在 if-else 子句之后设置 View 的内容。所以现在,当第 6 行进入屏幕时,行不为空。它拥有第一行的视图。当它执行 holder.name.setText(name1); 时,它将第 1 行的名称替换为第 6 行的名称。同样,第 7 行使用第 2 行的视图,依此类推。

现在,假设您单击第一行的按钮。第一行的视图发生变化, w.b.setImageResource(R.drawable.butgreen);设置第一行按钮的图片资源。当您滚动到第 6 行时,它使用第 1 行的视图。但是您仅将 holder.name 设置为第 6 行的名称。第一行按钮的图像资源将保留在那里,从而导致您的问题。

解决方案:创建另一个 ArrayList 并将按钮的状态存储在其中。因此,在您的情况下,我假设按钮的颜色为“红色”或“绿色”。您可以在每个位置存储“红色”或“绿色”,表示每个按钮当前的颜色(您可以将此 ArrayList 初始化为红色)。然后,当每行加载时,您可以设置图像资源以及该行的名称。看看下面的代码。我已将 cmets 添加到我添加的行中。

public class Coursadapter extends ArrayAdapter<String> {

Context context;
int layoutResourceId;
ArrayList<String> data = null;
WeatherHolder holder;
ArrayList<String> colors; // Stores the color of each button

public Coursadapter(Context context, int layoutResourceId, ArrayList<String> data) {
    // super(context, layoutResourceId, data, coeff);
    super(context, layoutResourceId, data);
    this.layoutResourceId = layoutResourceId;
    this.context = context;
    this.data = data;
    this.colors = new ArrayList<>(Collections.nCopies(data.size(), "red")); // initialize all the buttons to red color
}

public View getView(final int position, View convertView, ViewGroup parent) {
    View row = convertView;


    if(row == null)
    {
        LayoutInflater inflater = ((Activity)context).getLayoutInflater();
        row = inflater.inflate(layoutResourceId, parent, false);

        holder = new WeatherHolder();
        holder.name = (TextView)row.findViewById(R.id.item_cours_name);
        holder.b=(ImageButton) row.findViewById(R.id.button);
        holder.b.setTag(holder);
        row.setTag(holder);
    }
    else
    {
        holder = (WeatherHolder)row.getTag();
    }

    if(colors.get(position).equals("red")) {
        holder.b.setImageResource(R.drawable.butred); // set the colors of the button to red or whatever you need
    } else {
        holder.b.setImageResource(R.drawable.butgreen); // set the colors of the button to green
    }

    holder.b.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            WeatherHolder w = (WeatherHolder) v.getTag();
            //w.b.setImageResource(R.drawable.butgreen);
            colors.set(position, "green"); // update the ArrayList colors
        }
    });
    String name1 = data.get(position);
    holder.name.setText(name1);


    return row;
}

static class WeatherHolder
{

    TextView name;
    ImageButton b;
}
}

【讨论】:

  • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
  • 我在这里链接的问题与这个问题完全相同。我找不到将其报告为重复项的选项,因此我将答案链接到此处的另一个问题。我是否也应该在此处包括基本部分?
  • “我找不到将其报告为重复的选项,因此我将答案链接到此处的另一个问题。” => 您可以通过单击问题下方的flag 按钮将问题标记为重复,然后单击Should be closed... --> duplicate of...。 “我也应该把重要的部分也包括在这里吗?” => 是的,这总是一个好主意,因为其他问题可以被删除(与外部链接相同)。
  • 好的,我已将其标记为重复,并将编辑我的答案。谢谢
猜你喜欢
  • 2021-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多