3

Welches sind die besten Methoden, um einen Spinner in einem RecyclerView Adapter zu handhaben?Wie man Spinner in Recyclerview verwendet?

Das ist mein RecyclerView Adapter:

public class CartAdapter extends BaseAdapter<Object> { 

public CartAdapter(AbstractBaseActivity activity) { 
    super(activity); 
} 

public static final int TYPE_PRODOTTO = 1; 
public static final int TYPE_SCONTO = 2; 

@Override 
public int getItemViewType(int position) { 

    if (items.get(position) instanceof Article) 
     return TYPE_PRODOTTO; 
    else 
     return TYPE_SCONTO; 
} 

@Override 
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
    View rowView = LayoutInflater.from(parent.getContext()).inflate(viewType == TYPE_PRODOTTO ? R.layout.item_cart : R.layout.item_cart_sconto, parent, false); 
    return new ViewHolder(rowView); 
} 

@Override 
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { 
    final ViewHolder viewHolder = (ViewHolder) holder; 

    final Object object = items.get(position); 

    if (object instanceof Article) { 

     viewHolder.getBinding().setVariable(BR.article, object); 
     viewHolder.getBinding().executePendingBindings(); 

     assert viewHolder.quantitySpinner != null; 
     assert viewHolder.cartoneQuantity != null; 
     assert viewHolder.cartoneValue != null; 

     CartSpinnerAdapter adapter = (CartSpinnerAdapter) viewHolder.quantitySpinner.getAdapter(); 
     adapter.clear(); 
     adapter.setCount(((Article) object).getQuantityAvailable()); 
     adapter.notifyDataSetChanged(); 

     viewHolder.quantitySpinner.setSelection(((Article) object).getQuantity() - 1); //In teoria qui la quantità non deve mai essere zero 

     viewHolder.cartoneQuantity.setVisibility(position % 2 == 1 ? View.GONE : View.VISIBLE); //Controllo da togliere in futuro 
     viewHolder.cartoneValue.setVisibility(position % 2 == 1 ? View.GONE : View.VISIBLE); //Controllo da togliere in futuro 
    } 

    final PopupMenu popup = new PopupMenu(getContext(), viewHolder.deleteMenu); 
    MenuInflater inflater = popup.getMenuInflater(); 
    inflater.inflate(R.menu.delete_menu, popup.getMenu()); 

    viewHolder.deleteMenu.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View view) { 
      popup.show(); 
     } 
    }); 

    popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { 
     @Override 
     public boolean onMenuItemClick(MenuItem item) { 
      if (item.getItemId() == R.id.action_delete) { 
       removeData(holder.getAdapterPosition()); 
       ((CartActivity) activity).checkIfEmpty(); 
      } 

      return true; 
     } 
    }); 
} 

public class ViewHolder extends RecyclerView.ViewHolder { 

    @BindView(R.id.item) 
    View item; 
    @Nullable 
    @BindView(R.id.cart_image) 
    ImageView cartImage; 
    @BindView(R.id.delete_menu) 
    ImageView deleteMenu; 
    @Nullable 
    @BindView(R.id.product_cartone_quantity) 
    TextView cartoneQuantity; 
    @Nullable 
    @BindView(R.id.product_cartone_value) 
    TextView cartoneValue; 
    @Nullable 
    @BindView(R.id.quantity_spinner) 
    AppCompatSpinner quantitySpinner; 

    private ViewDataBinding binding; 

    public ViewHolder(View itemView) { 
     super(itemView); 
     ButterKnife.bind(this, itemView); 
     binding = DataBindingUtil.bind(itemView); 
     if (quantitySpinner != null) 
      quantitySpinner.setAdapter(new CartSpinnerAdapter(itemView.getContext(), R.layout.support_simple_spinner_dropdown_item)); 
    } 

    public ViewDataBinding getBinding() { 
     return binding; 
    } 
} 
} 

und das ist mein Spinner Adapter:

public class CartSpinnerAdapter extends ArrayAdapter<String> { 

LayoutInflater inflater; 

int count; 

public CartSpinnerAdapter(Context context, int resource) { 
    super(context, resource); 

    inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
} 

public CartSpinnerAdapter(Context context, int resource, int count) { 
    super(context, resource); 

    this.count = count; 
    inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
} 

public void setCount(int count) { 
    this.count = count; 
} 

@Override 
public View getDropDownView(int position, View convertView, ViewGroup parent) { 
    return getStandardView(position, parent, true); 
} 

@Override 
public View getView(int position, View convertView, ViewGroup parent) { 
    return getStandardView(position, parent, false); 
} 

@Override 
public int getCount() { 
    return count; 
} 

private View getStandardView(int position, ViewGroup parent, boolean dropdown) { 
    View row = inflater.inflate(R.layout.support_simple_spinner_dropdown_item, parent, false); 

    TextView title = (TextView) row.findViewById(android.R.id.text1); 

    title.setText(String.valueOf(position + 1)); 

    if (dropdown) 
     title.setMinWidth(Utils.dpToPx(getContext(), 64)); 
    else 
     title.setAlpha(0.5f); 

    return row; 
} 
} 

Auf diese Weise, wenn ich die RecyclerView scrollen i Verzögerung erfahren bin.

Wenn ich entfernen Sie diese Zeilen funktioniert alles einwandfrei:

CartSpinnerAdapter adapter = (CartSpinnerAdapter) viewHolder.quantitySpinner.getAdapter(); 
adapter.clear(); 
adapter.setCount(((Article) object).getQuantityAvailable()); 
adapter.notifyDataSetChanged(); 

Das Problem ist also die Art, wie ich den Adapter des Spinner behandeln, wie kann ich damit umgehen?

Vielen Dank im Voraus.

Antwort

12

Short

Um die Leistung zu verbessern,

  1. entfernen Zuweisungen aus onBindViewHolder
  2. Reuse LayoutInflater, statt jedes Mal einen neuen zu bekommen.
  3. minimieren sich wiederholende Arbeit in onBindViewHolder Implementierung
  4. Spinner Adapter auch die Ansichten, die die wichtigste Sache zu machen für das Scrollen Verwendung eines Adapters sicher

Hintergrund

Wenn, bereiten sollte ist, dass wir NICHT neue Objekte zuweisen (oder minimieren Sie es als möglich).

Der ganze Zweck eines RecyclerView mit einem Adapter ist es, sicherzustellen, dass wir unsere Objekte recyceln, so dass die Arbeit beim Scrollen minimal ist.

Da das Zuweisen von Speicher sehr "teuer" ist, sollten Sie zur Verbesserung der Scroll-Leistung zuerst nach Zuweisungen während des onBindViewHolder suchen. Alle Zuordnungen, falls vorhanden, sollten im onCreateViewHolder vorgenommen werden.

Sobald alle Zuordnungen gelöscht sind, ist es Zeit für einige Mikroverbesserungen. Dazu gehören die Verbesserung der Codequalität, die Ergebnisse der Wiederverwendung der Logik usw.

Was ist zu tun?Entfernen

1) Zuweisungen aus onBindViewHolder

Im folgenden Code:

final PopupMenu popup = new PopupMenu(getContext(), viewHolder.deleteMenu); 
MenuInflater inflater = popup.getMenuInflater(); 
inflater.inflate(R.menu.delete_menu, popup.getMenu()); 

viewHolder.deleteMenu.setOnClickListener(new View.OnClickListener() { 
    @Override 
    public void onClick(View view) { 
     popup.show(); 
    } 
}); 

popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { 
    @Override 
    public boolean onMenuItemClick(MenuItem item) { 
     if (item.getItemId() == R.id.action_delete) { 
      removeData(holder.getAdapterPosition()); 
      ((CartActivity) activity).checkIfEmpty(); 
     } 

     return true; 
    } 
}); 

Sie haben derzeit drei direkte Zuweisungen (neue) und einige indirekte Zuweisungen (aufblasen). Ändern Sie diesen Code so, dass alle Zuordnungen im onCreateViewHolder sind. Zum Beispiel:

In onCreateViewHolder die Zuweisungen tun wie folgt:

// Allocate Listener only ONCE per recycled view 
viewHolder.deleteMenu.setOnClickListener(new View.OnClickListener() { 
    @Override 
    public void onClick(View view) { 
     // Get needed data from the view TAG, we will set it later 
     final int itemPosition = (Integer)view.getTag(); 

     // Do work only when needed - when user clicked the button 
     final PopupMenu popup = new PopupMenu(getContext(), viewHolder.deleteMenu); 
     MenuInflater inflater = popup.getMenuInflater(); 
     inflater.inflate(R.menu.delete_menu, popup.getMenu()); 

     popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { 
      @Override 
      public boolean onMenuItemClick(MenuItem item) { 
       // Do logic using itemPosition etc 
       return true; 
      } 
     }); 

     popup.show(); 
    } 
}); 

In onBindViewHolder bind relevanten Daten wie folgt:

viewHolder.deleteMenu.setTag(holder.getAdapterPosition()); 

2) Reuse LayoutInflater anstelle immer ein neues bekommen.

Im folgenden Code:

public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
    View rowView = LayoutInflater.from(parent.getContext()).inflate(viewType == TYPE_PRODOTTO ? R.layout.item_cart : R.layout.item_cart_sconto, parent, false); 
    return new ViewHolder(rowView); 
} 

Sie sind jedes Mal einen neuen LayoutInflater bekommen. Es ist eine Verschwendung. Es ist besser, einen im Adapterkonstruktor zu bekommen und ihn als Mitglied zu speichern.

3) Minimieren Sie sich wiederholende Arbeit in onBindViewHolder Implementierung

Zum Beispiel in dem folgenden Code:

viewHolder.cartoneQuantity.setVisibility(position % 2 == 1 ? View.GONE : View.VISIBLE); //Controllo da togliere in futuro 
viewHolder.cartoneValue.setVisibility(position % 2 == 1 ? View.GONE : View.VISIBLE); //Controllo da togliere in futuro 

Sie zweimal die gleiche Logik zu berechnen. Besser einmal berechnen und das Ergebnis wieder verwenden:

int cartoneVisibility = position % 2 == 1 ? View.GONE : View.VISIBLE; 
viewHolder.cartoneQuantity.setVisibility(cartoneVisibility); //Controllo da togliere in futuro 
viewHolder.cartoneValue.setVisibility(cartoneVisibility); //Controllo da togliere in futuro 

4) Spinner Adapter sollte recyceln auch die Ansichten

In CartSpinnerAdapter.getView() Sie auch das Zuweisen von Speicher sind. Es passiert (jedes Mal * Listenelement * zählen) - Das ist eine Menge Zuweisungen. Bitte verwenden Sie stattdessen die convertView. Werfen Sie einen Blick auf dieses Tutorial dzone.com/articles/android-listview-optimizations

+0

Hallo, vielen Dank für die Antwort. Ich habe das Verzögerungsproblem, seit ich die Adapterlogik dem Spinner hinzugefügt habe, und selbst mit diesen Verbesserungen wird das Verzögerungsproblem nicht gelöst. Meine eigentliche Frage ist, wie kann ich einen Spinner Adapter in einem Recyclerview Adapter verwenden? – Bronx

+0

Bitte lesen Sie meine Bearbeitung zu Abschnitt (1) Lösung. –

+1

Wenden Sie außerdem Vorschläge für den CartSpinnerAdapter an. Dort verwenden Sie den Adapter nicht korrekt, da Sie die Ansichten nicht recyceln. Verwenden Sie die ConvertView. –