【问题标题】:ListView with Spinner Inside: 1st Item of the listview affects its last item带有 Spinner 的 ListView:listview 的第一项影响其最后一项
【发布时间】:2014-04-02 11:09:35
【问题描述】:

我遇到过这种奇怪的情况,每次我ListView 的第一个项目中的微调器中选择一个值最后一个ListView'sitem 的微调器值都是相同的作为第一项。这只有在 ListView 项目的总数为 5 及以上 时才会发生。我注释掉了代码并只保留了声明,它仍在发生。这是 Android 中的错误吗?

说明:

  • 我的 ListView 的 Scroll Listener 是空的

  • 我的微调器的 setOnItemSelectedListener 已被注释掉。

  • Android SDK 工具版本为 22.6.2

  • Android SDK Platform-Tools 是 19.0.1

这是适配器代码:

@Override
public View getView(final int position, View convertView, final ViewGroup parent) {

    Viewholder v = new Viewholder();
    v.rowView = convertView;
    LayoutInflater inflater = (LayoutInflater) context
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    if (v.rowView == null) {
        v.rowView = inflater.inflate(R.layout.inner_base_header_cutom, parent, false);
        v.spinner = (Spinner) v.rowView.findViewById(R.id.spinner1);
        v.rowView.setTag(v);
    } else {
        v = (Viewholder) v.rowView.getTag();
    }

    return v.rowView;
}

ViewHolder:

class Viewholder{
    View rowView;
    TextView itemPID;
    TextView itemPrice;
    TextView itemItemId;
    Spinner spinner;
    TextView subtotal;
}

XML:

<Spinner
    android:id="@+id/spinner1"
    android:layout_width="100dip"
    android:layout_height="wrap_content"
    android:layout_margin="20dip"
    android:entries="@array/quanitiy"
    android:layout_alignTop="@+id/itemPrice"
    android:layout_toRightOf="@+id/imageDisplay" />

数组:

   <string-array name="quanitiy">
        <item>Quantity</item>
        <item>1</item>
        <item>2</item>
        <item>3</item>
        <item>4</item>
        <item>5</item>
        <item>6</item>
        <item>7</item>
        <item>8</item>
        <item>9</item>
        <item>10</item>
    </string-array>

更新 我注释掉了OnItemClickListner 的代码。我更新了上面的代码,问题仍然存在。唯一剩下的就是声明。

场景: 如果我在ListView 的微调器[index 0] 的第一项选择1,则ListView 的微调器的最后一项也会在没有交互的情况下获得1。当向下列表视图的项目直到最后一部分时,我发现它们都是相同的。我注释掉了代码,只保留了声明。

【问题讨论】:

  • 请缩进您的代码。那我看看 ;) 如果你使用的是 Eclipse,Ctrl+Maj+F 是你的朋友!
  • 谢谢@SilentKiller。
  • 你的getView() 中没有return 语句,它根本不应该编译。
  • 我添加了退货声明。
  • 你能发布R.layout.inner_base_header_cutom的XML

标签: java android android-arrayadapter android-spinner


【解决方案1】:

好的,现在我明白了。感谢您的澄清。您的问题来自被回收的视图。你应该看看this post 以更好地了解回收。

简短的回答

基本上,当您在getView() 中使用convertView 时,您正在重用不再可见的项目的视图。这就是为什么它的字段(包括微调器、价格的文本视图......)已经设置了一些东西,你应该在getView() 中自己设置它们。赋予这些文本字段和微调器的值应取决于指定 position 处的项目。

详细解答

以下是回收的作用(图片取自前面提到的帖子):

你的问题

当您滚动显示一个新项目(比如项目 8,如图所示)时,用于显示它的视图与第一个项目(项目 1)的视图相同。因此,当您在第一项的微调器上选择某些内容,然后向下滚动时,您也会看到最后一项的更改,因为您的 getView 不会更改用于第 1 项的视图的值(但它应该更新它们!)。

注意:您的案例中第一项和最后一项的对应完全是偶然的。通常,视图会被重用 2 个项目,这些项目大约被一个屏幕高度上的项目数分开,如图所示。

getView 代码中的内联解释

Viewholder v = new Viewholder(); // why create a new one, while you might reuse the former one?

// here rowView becomes the same as the former item's view, with all the former values
v.rowView = convertView; 

// you don't need this here but only in the case rowView is null, should be in your 'if'
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

if (v.rowView == null) {
    v.rowView = inflater.inflate(R.layout.inner_base_header_cutom, parent,false);
    v.spinner = (Spinner) v.rowView.findViewById(R.id.spinner1);
    v.rowView.setTag(v);
    // here you inflated a new view (nothing reused) 
    // but you didn't initialize anything
} else {
    v = (Viewholder) v.rowView.getTag();
    // here you're reusing the former View and ViewHolder 
    // and you don't change the values
}

解决方案

你不应该让项目随心所欲,但你应该告诉他们在 getView() 中初始化项目视图的内容时要显示什么。对于上图,这意味着您需要将原来的第 1 项更改为第 8 项

您应该在适配器中保留每个项目的状态。根据position,您可能有一个支持列表或其他东西来显示您的ListView 中的不同元素,对吧? 然后,您应该使用类似的列表(或相同的列表)来存储微调器中选择的值、价格文本字段显示的值等。无论如何,这些可能与当前列表中的项目字段相对应,因此您可能已经有地方存放它们了。

另一方面,当您在 getView() 中重复使用 convertView 时,请确保使用 position 中的项目的正确值初始化微调器(和文本字段,以及此项目视图中的所有内容)。

getView 的解决方案代码(模板)

Viewholder v;

if (convertView == null) {
    // create a new holder for the new item view
    v = new Viewholder();
    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    v.rowView = inflater.inflate(R.layout.inner_base_header_cutom, parent,false);
    v.rowView.setTag(v);

    // populate the new holder's fields
    v.spinner = (Spinner) v.rowView.findViewById(R.id.spinner1);
    v.itemPID = (TextView) v.rowView.findViewById(...); // put the right ID here
    v.itemPrice = (TextView) v.rowView.findViewById(...); // put the right ID here
    v.itemItemId = (TextView) v.rowView.findViewById(...); // put the right ID here
    v.subtotal =  (TextView) v.rowView.findViewById(...); // put the right ID here
} else {
    v = (Viewholder) convertView.getTag();
    // the views of v are already populated here (reused)     
}

/* 
 * Reused or not, the item view needs to be initialized here.
 * Initialize all views contained in v with values that are 
 * meaningful for the item at 'position'.
 */

// for instance:
MyItemClass theItemAtPosition = myBackingList.get(position);
v.subtotal.setText(String.valueOf(theItemAtPosition.getSubtotal()));
v.spinner.setSelection(theItemAtPosition.getQuantity());

// to be continued, you get the idea ;)

【讨论】:

  • 感谢您的解释,我需要时间来研究您的答案。你能建议一个更好的代码吗?再次感谢。
  • 我编辑了我的答案,向您展示了一些解决方案的代码。但我不能做更多不知道你的列表是什么样子(我的意思是列表内容,ListView 的后备列表)
  • 现在看起来好多了,你应该可以彻底理解你的问题了;)
  • 我不确定如何获得微调器位置,因为它不包含在项目中。请看这个:pastebin.com/a7Xd4UHT 一些 cmets 是运行代码的一部分。
  • 我还没有阅读完整的代码,但您可能应该在地图中添加一个描述项目的键,例如 KEY_QUANTITY 并使用微调器中选择的任何内容进行设置(默认情况下,也许放 0).
猜你喜欢
  • 2015-12-11
  • 1970-01-01
  • 2017-02-17
  • 2023-03-16
  • 1970-01-01
  • 2012-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多