Android – how to use spinner in recyclerview?

Which is the best practice for handling spinner in the recyclerview Adapter?

This is my 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;
    }
}
}

This is my 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;
}
}

Thus, when I scroll recyclerview, I encounter a lag

If I delete these lines, everything is OK:

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

So the question is how I handle the spinner adapter. How should I handle it?

Thank you in advance

resolvent:

short

To improve performance,

>Remove assignments from onbindviewholder > reuse layoutinflator instead of getting a new one every time. > minimize repetitive work in onbindviewholder implementation > spinner adapter should also recycle views

background

When using adapters for scrolling, the most important thing is to ensure that we do not assign new objects (or minimize them)

The overall purpose of recyclerview with adapter is to ensure that we recycle our objects so that the work required during scrolling is minimized

Since memory allocation is very "expensive", in order to improve scrolling performance, the first consideration is the allocation during onbindviewholder. All allocations (if any) should be made in oncreateviewholder

Once all allocations are cleared, if we still have lag, it's time to make some micro improvements, including improving code quality, reusing logical results, etc

What should I do?

1) Delete assignment from onbindviewholder

In the following 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;
    }
});

You currently have 3 direct assignments (New) and some indirect assignments (inflated). Change this code so that all assignments are in oncreateviewholder. For example:

Perform the following assignment in oncreateviewholder:

// 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();
    }
});

Bind relevant data in onbindviewholder, as shown below:

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

2) Reuse the layoutinflator instead of getting a new one every time

In the following 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);
}

You'll get a new layoutinflator every time. This is a waste. It's best to get one in the adapter constructor and save it as a member

3) Minimize repetitive work in the onbindviewholder implementation

For example, in the following 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

You are calculating the same logic twice. It is best to calculate and reuse the result once:

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) The spinner adapter should also recycle views

In cartspinneradapter. Getview(), you also allocate memory. It occurs (every time * list items * count) – this is a lot of allocation. Please use convertview instead. Take a look at this tutorial dzone.com/articles/android-listview-optimizations

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>