ListView的回收机制和ViewHolder冲突

时间:2016-04-25 21:05:38

标签: android listview android-recyclerview android-viewholder

我有一个包含5种以上类型视图的ListView,问题是: 我有18行10在屏幕上可见,当我向下滚动它的负载: 1,2,3,4,5,6,7,8,9,10, 1 2 3 下,如图11所示, 4 下,的 5 下,的 6 下,的 7 如果我在没有ViewHolder的情况下这样做,一切正常,但我的所有EditText都消失了,因为它每次创建新视图,我需要在将来创建40行的视图。 代码:

    @Override
public View getView(int position, View convertView, ViewGroup parent) {
    Row row = arrayRows.get(position);

    if (convertView==null) {
        Log.i("LOG", "convertView == null");
        LayoutInflater inflater = (LayoutInflater) this.context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //setting view holder
        ViewHolder viewHolder = new ViewHolder();
        //build rows
        if (row.getClass() == RowTextViewEditText.class) {
            convertView = inflater.inflate(R.layout.row_textview_edittext, null);
            viewHolder.textView = (TextView) convertView.findViewById(R.id.textViewRowTextViewEditText);
            viewHolder.editText = (EditText) convertView.findViewById(R.id.editTextRowTextViewEditText);

            RowTextViewEditText rowTextViewEditText = (RowTextViewEditText) row;
            viewHolder.textView.setText(rowTextViewEditText.textViewText);
            viewHolder.editText.setHint(rowTextViewEditText.hint);
            viewHolder.editText.setText(rowTextViewEditText.editTextText);
            convertView.setTag(viewHolder);
        }
        if (row.getClass() == RowTextViewEditTextTextViewEditText.class) {
            convertView = inflater.inflate(R.layout.row_textview_edittext_textview_edittext, null);
            viewHolder.editText = (EditText) convertView.findViewById(R.id.editTextRowTextViewEditTextTextViewEditText);
            viewHolder.editText2 = (EditText) convertView.findViewById(R.id.editText2RowTextViewEditTextTextViewEditText);
            viewHolder.textView = (TextView) convertView.findViewById(R.id.textViewRowTextViewEditTextTextViewEditText);
            viewHolder.textView2 = (TextView) convertView.findViewById(R.id.textView2RowTextViewEditTextTextViewEditText);

            RowTextViewEditTextTextViewEditText rowEditTextEditText = (RowTextViewEditTextTextViewEditText) row;
            viewHolder.textView.setText(rowEditTextEditText.textViewText);
            viewHolder.textView2.setText(rowEditTextEditText.textViewText2);
            viewHolder.editText.setText(rowEditTextEditText.editTextText);
            viewHolder.editText2.setText(rowEditTextEditText.editTextText2);
            convertView.setTag(viewHolder);
        }
    }
    //I have at least 5 types of rows
    else{
        viewHolder = (ViewHolder)convertView.getTag();
        Log.i("LOG", "convertView=" + convertView.getTag());
    }
    return convertView;
}

这是Logcat:

    convertView == null
    convertView == null
    convertView == null
    convertView == null
    convertView == null
    convertView == null
    convertView == null
    convertView == null
    convertView == null
    convertView == null
    convertView == null
    convertView == null
    convertView=com.adamofsky.survey.OrderAdapter$ViewHolder@413b1a30
    convertView=com.adamofsky.survey.OrderAdapter$ViewHolder@413ada10
    convertView=com.adamofsky.survey.OrderAdapter$ViewHolder@4147edf0
    convertView=com.adamofsky.survey.OrderAdapter$ViewHolder@41415f80
    convertView=com.adamofsky.survey.OrderAdapter$ViewHolder@415ad6d0
    convertView=com.adamofsky.survey.OrderAdapter$ViewHolder@41441e18
    convertView=com.adamofsky.survey.OrderAdapter$ViewHolder@4141afb0

据我所知,第10行convertView由于某种原因而不是null,它会返回1,2,3,4的对象..为什么?

这是ViewHolder类:

    public static class ViewHolder {
    TextView textView;
    TextView textView2;
    EditText editText;
    EditText editText2;
    Spinner spinner;
    CheckBox checkBox;
}

我可以创建一个视图数组,然后从那里获取位置,但它是错误的解决方案,我想理解为什么来自行不可见行的convertView为null 谢谢你的建议。

2 个答案:

答案 0 :(得分:1)

<强>首先

ListView(以及所有其他AdapterView s)都有“视图类型”的概念,更好地将其视为“行类型”。您应该使用getItemViewType()getViewTypeCount()来区分行。这允许ListView在调用getView()时回收正确的行布局。

通常的做法是在getItemViewType()内使用getView()来确定您正在膨胀(或回收)哪种行类型,而不是检查类类型的这种奇怪的业务。如果正确使用视图类型,甚至根本不需要创建这些类。

通常,每种视图类型都有自己的ViewHolder。对于各种不同的视图类型使用相同的ViewHolder类会让人感到困惑。

<强>第二

永远不要对getView()次来电的顺序做出假设。 ListView可以出于各种原因在不同时间调用此方法;它可以用增加或减少顺序的位置来称呼它;它甚至可能没有计划显示视图,只能用于测量。

<强>第三

正如其他人的建议,您应该考虑使用RecyclerView代替ListView。它还支持不同的视图类型,ViewHolder模式由设计强制执行。

为了更好地理解ListView,我建议您使用名为The World of ListView的视频。即使您打算使用RecyclerView,此视频中的原则仍然适用。

答案 1 :(得分:0)

好吧,看起来很多人不理解我的代码中的逻辑部分所以我会解释它而不是回答我自己的问题。 使用类不是用于枚举或检测行类型,而是用于传输和获取适配器中行的数据。每一行都有不同的变量,我必须在行中设置它们,而不是从行中获取它们,例如:

class RowTextViewEditTextCheckBox extends Row{
String textViewText; //for set text:name
String hint; //for set text: please type name here
String checkBoxTitle; //for set checkBoxTitle
String editTextText; // for get text after user will fill the field
boolean checkBoxChecked; //for get result of checkbox when user will check it}

所以我在数组中发送这个对象

arrayRows.add(new RowTextViewEditTextCheckBox(new String[]{"amounts"},this.getString(R.string.burglary),this.getString(R.string.content),this.getString(R.string.amount),"",false));

现在回到问题: 这link帮助我理解我的错误 感谢Karakuri让我指出&#34;查看类型&#34;在ListView中

为什么所有未看到的内容视图都从ViewHolder中取错,而不是创建所需类型的新contentView?

因为ListView检测到行的类型,并且如果此类型之前已经创建并且他在回收器中,他没有创建新的contentView,他从具有相同类型的回收器返回contentView。

那他为什么会回错类型?

因为他不知道你有多少种类型的行,所以默认情况下它是1种类型。在getViewTypeCount()中,您必须设置视图类型计数。

如果我理解某事或所有错误,请告诉我。 这是一个像魅力一样的新代码:

    @Override
public int getItemViewType(int position) {
    Row row = arrayRows.get(position);
    if (row.getClass()==RowTextViewEditTextSpinner.class){
        return 0;
    }
    if (row.getClass()==RowTextViewEditTextTextViewEditText.class){
        return 1;
    }
    if (row.getClass()==RowTextViewEditTextCheckBox.class){
        return 2;
    }
    if (row.getClass()==RowTextViewEditText.class){
        return 3;
    }
    if (row.getClass()==RowEditText.class){
        return 4;
    }
    if (row.getClass()==RowTextView.class){
        return 5;
    }
    else {
        return 5;
    }
}
@Override
public int getViewTypeCount() {
    return 6;//if types creates by code may be here some logic of counting
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    Row row = arrayRows.get(position);
    int type = getItemViewType(position);

    //setting view holders
    ViewHolderTextViewEditTextSpinner viewHolderTextViewEditTextSpinner;
    ViewHolderTextViewEditTextTextViewEditText viewHolderTextViewEditTextTextViewEditText;

    //build rows
    switch (type){
        case TEXTVIEW_EDITTEXT_SPINNER:
            if (convertView==null){
                convertView = inflater.inflate(R.layout.row_textview_edittext_spinner, parent, false);
                viewHolderTextViewEditTextSpinner = new ViewHolderTextViewEditTextSpinner();
                viewHolderTextViewEditTextSpinner.textView = (TextView) convertView.findViewById(R.id.textViewRowTextViewEditTextSpinner);
                viewHolderTextViewEditTextSpinner.editText = (EditText) convertView.findViewById(R.id.editTextRowTextViewEditTextSpinner);
                viewHolderTextViewEditTextSpinner.spinner = (Spinner) convertView.findViewById(R.id.spinnerRowTextViewEditTextSpinner);
                convertView.setTag(viewHolderTextViewEditTextSpinner);
            }
            else {
                viewHolderTextViewEditTextSpinner = (ViewHolderTextViewEditTextSpinner)convertView.getTag();
            }
            final RowTextViewEditTextSpinner rowEditTextSpinner = (RowTextViewEditTextSpinner)row;
            ArrayAdapter<String> dataAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, rowEditTextSpinner.spinnerStrings);
            viewHolderTextViewEditTextSpinner.textView.setText(rowEditTextSpinner.textViewText);
            viewHolderTextViewEditTextSpinner.editText.setHint(rowEditTextSpinner.hint);
            viewHolderTextViewEditTextSpinner.editText.setText(rowEditTextSpinner.editTextText);
            viewHolderTextViewEditTextSpinner.spinner.setAdapter(dataAdapter);
            viewHolderTextViewEditTextSpinner.spinner.setSelection(rowEditTextSpinner.spinnerSelectedPosition);
            break;
        case TEXTVIEW_EDITTEXT_TEXTVIEW_EDITTEXT:
            if (convertView==null){
                convertView = inflater.inflate(R.layout.row_textview_edittext_textview_edittext, parent, false);
                viewHolderTextViewEditTextTextViewEditText = new ViewHolderTextViewEditTextTextViewEditText();
                viewHolderTextViewEditTextTextViewEditText.editText = (EditText) convertView.findViewById(R.id.editTextRowTextViewEditTextTextViewEditText);
                viewHolderTextViewEditTextTextViewEditText.editText2 = (EditText) convertView.findViewById(R.id.editText2RowTextViewEditTextTextViewEditText);
                viewHolderTextViewEditTextTextViewEditText.textView = (TextView) convertView.findViewById(R.id.textViewRowTextViewEditTextTextViewEditText);
                viewHolderTextViewEditTextTextViewEditText.textView2 = (TextView) convertView.findViewById(R.id.textView2RowTextViewEditTextTextViewEditText);
                convertView.setTag(viewHolderTextViewEditTextTextViewEditText);
            }
            else {
                viewHolderTextViewEditTextTextViewEditText = (ViewHolderTextViewEditTextTextViewEditText)convertView.getTag();
            }
            RowTextViewEditTextTextViewEditText rowEditTextEditText = (RowTextViewEditTextTextViewEditText)row;
            viewHolderTextViewEditTextTextViewEditText.textView.setText(rowEditTextEditText.textViewText);
            viewHolderTextViewEditTextTextViewEditText.textView2.setText(rowEditTextEditText.textViewText2);
            viewHolderTextViewEditTextTextViewEditText.editText.setText(rowEditTextEditText.editTextText);
            viewHolderTextViewEditTextTextViewEditText.editText2.setText(rowEditTextEditText.editTextText2);
            break;

    }
    return convertView;
}

    static public class ViewHolderTextViewEditTextSpinner{
    TextView textView;
    EditText editText;
    Spinner spinner;
}
static public class ViewHolderTextViewEditTextTextViewEditText{
    TextView textView;
    TextView textView2;
    EditText editText;
    EditText editText2;
}
static public class ViewHolderTextViewEditTextCheckBox{
    TextView textView;
    EditText editText;
    CheckBox checkBox;
}
static public class ViewHolderTextViewEditText{
    TextView textView;
    EditText editText;
}
static public class ViewHolderEditText{
    EditText editText;
}
static public class ViewHolderTextView{
    TextView textView;
}

如果可能是更好的解决方案或者这是错误的解决方案,请告诉我