Java 内置微调器的ListView:ListView的第一项影响其最后一项

Java 内置微调器的ListView:ListView的第一项影响其最后一项,java,android,android-arrayadapter,android-spinner,Java,Android,Android Arrayadapter,Android Spinner,我有一个奇怪的场景,每次我从列表视图的第一个项目中的微调器中选择一个值,最后一个列表视图的项目的微调器值与第一个项目相同。只有当ListView项目总数小于等于或大于5时,才会发生这种情况。我注释掉了代码,只保留了声明,但它仍然在发生。这是安卓系统中的一个bug吗 澄清: 我的ListView的滚动侦听器为空 我的微调器的setOnItemSelectedListener被注释掉 Android SDK工具版本为22.6.2 Android SDK平台工具是19.0.1 以下是适配

我有一个奇怪的场景,每次我
列表视图的第一个项目中的微调器中选择一个值,最后一个
列表视图的项目的微调器值与第一个项目相同。只有当ListView项目总数小于等于或大于5时,才会发生这种情况。我注释掉了代码,只保留了声明,但它仍然在发生。这是安卓系统中的一个bug吗

澄清:

  • 我的ListView的
    滚动侦听器
    为空

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

  • Android SDK工具版本为22.6.2

  • Android SDK平台工具是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;
}
持证人:

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


数组:

   <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>

量
1.
2.
3.
4.
5.
6.
7.
8.
9
10
更新 我注释掉了
OnItemClickListner
的代码。我更新了上面的代码,问题仍然存在。只剩下声明了

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

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

简短的回答 基本上,当您在
getView()
中使用
convertView
时,您是在重用不再可见的项的视图。这就是为什么它的字段(包括微调器、价格的文本视图…)已经设置为某个值,您应该在
getView()
中自己设置它们。提供这些文本字段和微调器的值应取决于位于指定
位置的项目

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

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

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

getView
解决方案 您不应该让项目随心所欲地运行,但应该告诉他们在
getView()
中初始化项目视图的内容时要显示什么。对于上面的图片,这意味着您需要将以前的项目1更改为项目8

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

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

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

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

简短的回答 基本上,当您在
getView()
中使用
convertView
时,您是在重用不再可见的项的视图。这就是为什么它的字段(包括微调器、价格的文本视图…)已经设置为某个值,您应该在
getView()
中自己设置它们。提供这些文本字段和微调器的值应取决于位于指定
位置的项目

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

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

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

getView
解决方案 您不应该让项目随心所欲地运行,但应该告诉他们在
getView()
中初始化项目视图的内容时要显示什么。对于上面的图片,这意味着您需要更改wha
   <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>
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
}
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 ;)