2013-08-19 10 views
9

Ich benutze einen ArrayAdapter für eine Liste meiner eigenen Art von Objekten (nur ein Typ) und ich gebe dem Benutzer eine Option zum Erstellen weiterer Elemente mehr Ansichten für diese Artikel). An einem bestimmten Punkt hat getView einen neuen "position" Index mit einem "convertView" ungleich null gesendet. Es zeigt dann die erste Ansicht in der letzten Position. Danach werden beim Scrollen alle Ansichten durcheinander gebracht. Ich nehme an, dies bedeutet, dass ich die Ansichten auf eine Weise manipulierte, die ich nicht hätte, aber ich sehe einfach nicht wo. Hier ist ein Code:Getview-Parameter "convertview" nicht Null auf neuen "Position" -Parameter

@Override 
public View getView(int position, View convertView, ViewGroup parent) { 
    View v; 
    PreviewItemHolder holder = null; 

    // Initialize view if convertview is null 
    if (convertView == null) { 
     v = newView(parent, position); 
    } 
    // Populate from previously saved holder 
    else { 
     // Use previous item if not null 
     v = convertView; 
    } 

    // Populate if the holder is null (newly inflated view) OR 
    // if current view's holder's flag is true and requires populating 
    if ((holder == null) || (holder.readPopulateFlag())) { 
     bindView(position, v); 
    } 

    return v; 
} 

    private View newView(ViewGroup parent, int position) { 
    // Getting view somehow... 
    LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    View inflatedView = inflater.inflate(R.layout.preview_element_set, parent, false); 
    PreviewItemHolder holder = new PreviewItemHolder(); 

    holder.set = (Set) mSets.get(position); 
    holder.previewElementHolders = new ArrayList<PreviewElementHolder>(); 
    holder.expandArea = (View) inflatedView.findViewById(R.id.expandArea); 
    holder.repetitionsLabel = (TextView) inflatedView.findViewById(R.id.previewRepetitionsInput); 
    holder.endlessInput = (CheckBox) inflatedView.findViewById(R.id.previewSetEndlessInput); 
    holder.nameLabel = (TextView) inflatedView.findViewById(R.id.previewSetNameLabel); 
    holder.commentInput = (EditText) inflatedView.findViewById(R.id.previewSetCommentInput); 
    holder.soundInput = (EditText) inflatedView.findViewById(R.id.previewSetSoundInput); 
    holder.addElementButton = (Button) inflatedView.findViewById(R.id.previewSetAddElements); 
    holder.expand = (View) inflatedView.findViewById(R.id.infoArea); 
    holder.collapse = (View) inflatedView.findViewById(R.id.collapse); 

    final int setsLength = holder.set.getElements().size(); 

    for (int i = 0; i < setsLength; i++) { 
     AElement currElement = holder.set.getElements().get(i); 

     // Creating new element holder according to the type 
     if (currElement instanceof Rest) { 
      holder.previewElementHolders.add(new PreviewRestHolder()); 
     } 
     else if (currElement instanceof TimeExercise) { 
      holder.previewElementHolders.add(new PreviewTimeExerciseHolder()); 
     } 
     else if (currElement instanceof RepetitionExercise) { 
      holder.previewElementHolders.add(new PreviewRepetitionExerciseHolder()); 
     } 

     View currLayout = inflateElement(currElement, inflater, i, holder.previewElementHolders.get(i)); 

     // Add the child before the hairline, collapse image and the add 
     // button 
     // (3 last children of the expandArea view 
     ((ViewGroup) holder.expandArea).addView(currLayout, ((ViewGroup) holder.expandArea).getChildCount() - CHILDREN_INDEX_AFTER_PHASES_LABEL); 
    } 

    inflatedView.setTag(holder); 

    return inflatedView; 
} 

private void bindView(int position, View inflatedView) { 
    final PreviewItemHolder holder = (PreviewItemHolder) inflatedView.getTag(); 
    holder.set.setId(position); 
    holder.endlessInput.setChecked(holder.set.getEndless()); 
    holder.soundInput.setText(holder.set.getSound()); 
    holder.nameLabel.setText(holder.set.getName()); 
    holder.commentInput.setText(holder.set.getComment()); 

    // Make sure there is a name. If none, put default 
    if (holder.nameLabel.getText().equals("")) { 
     holder.nameLabel.setText(R.string.default_set_name); 
    } 

    // Set repetitions value according to the endless flag 
    if (holder.set.getEndless()) { 
     holder.repetitionsLabel.setText(R.string.infinity); 
    } 
    else { 
     holder.repetitionsLabel.setText(String.valueOf(holder.set.getRepetitions())); 
    } 

    // Set click listeners 
    holder.endlessInput.setOnCheckedChangeListener(new OnCheckedChangeListener() { 

     @Override 
     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 

      // Save endless flag 
      holder.set.setEndless(isChecked); 

      // If an endless set - Dropset 
      if (isChecked) { 
       holder.repetitionsLabel.setText(R.string.infinity); 
      } 
      else { 
       // Regular set 
       holder.repetitionsLabel.setText(String.valueOf(holder.set.getRepetitions())); 
      } 

      hideShowRepsWeights(holder); 
     } 

    }); 

    holder.repetitionsLabel.setOnClickListener(new OnClickListener() { 
     @Override 
     public void onClick(View v) { 

      NumericDialog instance = NumericDialog.newInstance(holder, holder.set, NumericDialog.INTEGER_MODE, Consts.SET_REPETITIONS_METHOD_NAME); 
      instance.show(((Activity) getContext()).getFragmentManager(), null); 
     } 
    }); 

    holder.nameLabel.setOnClickListener(new OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      // Setting flag to true to allow populating this view 
      holder.rePopulateFlag = true; 
      SetNameDialog instance = SetNameDialog.newInstance(holder.set); 
      instance.show(((Activity) getContext()).getFragmentManager(), null); 
     } 
    }); 

    holder.commentInput.setOnFocusChangeListener(new OnFocusChangeListener() { 
     @Override 
     public void onFocusChange(View v, boolean hasFocus) { 
      if (!hasFocus) { 
       // After focus is lost, save the text into the set 
       holder.set.setComment(holder.commentInput.getText().toString()); 
      } 
     } 
    }); 

    // TODO Change that into a dialog that allows selection of sounds 
    holder.soundInput.setOnFocusChangeListener(new OnFocusChangeListener() { 
     @Override 
     public void onFocusChange(View v, boolean hasFocus) { 
      if (!hasFocus) { 
       // After focus is lost, save the text into the set 
       holder.set.setSound(holder.soundInput.getText().toString()); 
      } 
     } 
    }); 

    holder.expand.setOnClickListener(new OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      // Change visibility - Show expandArea and its data 
      holder.expandArea.setVisibility(View.VISIBLE); 
      holder.expand.setVisibility(View.GONE); 
      holder.collapse.setVisibility(View.VISIBLE); 
     } 
    }); 

    holder.collapse.setOnClickListener(new OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      // Change visibility - Hide expandArea and its data 
      holder.expandArea.setVisibility(View.GONE); 
      holder.collapse.setVisibility(View.GONE); 
      holder.expand.setVisibility(View.VISIBLE); 
     } 
    }); 

    holder.addElementButton.setOnClickListener(new OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      AddElementDialog instance = AddElementDialog.newInstance(holder); 
      instance.show(((Activity) getContext()).getFragmentManager(), null); 
     } 
    }); 

    // Populate elements 
    for (PreviewElementHolder elementHolder : holder.previewElementHolders) { 
     populateElement(elementHolder, holder); 
    } 

    // Finally hide/show if needed - Should this be put somewere else? 
    hideShowRepsWeights(holder); 
} 

Bitte sagen Sie mir, wenn Sie denken, ich sollte mehr Methoden hochladen, um die Dinge klarer zu machen.

+0

Was ist holder.readPopulateFlag() ?? –

Antwort

15

Ein Freund erklärte mir das Problem und jetzt scheint es zu funktionieren. Grundsätzlich hält ListView nur eine kleine Anzahl von Ansichten und recycelt sie ständig. In meinem Fall habe ich ein Nexus 4 und so scheint es insgesamt 7 Aufrufe zu haben, da der 8te immer derjenige war, der Ärger zu machen begann. Was ich in meinem getView() vermisste, war eine Bedingung, die nach der Korrelation zwischen der Position und der ID des aktuellen Elements innerhalb des ArrayAdapters sucht. Hier ist, wie es jetzt aussieht, dass es funktioniert:

@Override 
public View getView(int position, View convertView, ViewGroup parent) { 
    View v; 
    PreviewItemHolder holder = null; 

    // Initialize view if convertview is null 
    if (convertView == null) { 
     v = newView(parent, position); 
    } 
    // Populate from previously saved holder 
    else { 
     // If position and id of set do not match, this view needs to be re-created, not recycled 
     if (((PreviewItemHolder) convertView.getTag()).set.getId() != position) { 
      v = newView(parent, position); 
     } 
     else { 
      // Use previous item if not null 
      v = convertView; 

      // Get holder 
      holder = (PreviewItemHolder) v.getTag(); 
     } 
    } 

    // Populate if the holder is null (newly inflated view) OR 
    // if current view's holder's flag is true and requires populating 
    if ((holder == null) || (holder.readPopulateFlag())) { 
    bindView(position, v); 
    } 

    return v; 
} 
+0

Ich hatte das gleiche Problem mit einem BaseAdapter, aber mein Gehirn kann das nicht ... Ich weiß, es war lange her, aber haben Sie mehr Erklärung, warum das half? Es hat mir auch geholfen, ich verstehe es einfach nicht ... – keybee

+0

Das ist ausgezeichnet. Das gleiche Problem ist bei mir aufgetreten, als ich 3 verschiedene Arten von Layouts in meinem 'ListView' verwendet habe. Sobald die oberste Ansicht vom Typ 1 ist und die nächste auf dem Bildschirm einzugebende Ansicht von einem anderen Typ ist, war "convertView" nicht null und die falsche Ansicht wurde mit "getTag" wiederverwendet. –

1

Sie müssen bindView()immer anrufen. Die Idee oder Wiederverwendung ist wie folgt. Wenn convertView Null ist, erstellen und initialisieren Sie eine neue Ansicht. Wenn convertView nicht null ist, nehmen Sie diese Ansicht und konvertieren es neue Ansicht sein, was bedeutet, dass Sie bindView() mit convertView Instanz aufrufen.

Checkout this Javadoc für weitere Details.

+0

Ich habe versucht, aber es hat das Problem nicht gelöst. Auch wenn ich jedesmal bindView() aufruft, passiert das Problem immer noch genauso wie zuvor. Ich glaube nicht, dass Sie die Daten in converView jedes Mal neu setzen müssen, um das zu beheben. –

0

Wenn Sie verschiedene Arten von Ansichten haben, die recycelt werden, sollten Sie die Listenansicht sagen, wie viele Typen, die Sie haben, indem die Methode implementieren

@Override 
    public int getViewTypeCount() { 
     return 7; 
    } 
Verwandte Themen