1

I'm a newbie, I tried to inflate the item row but it slows the application upon scrolling. In the code below, it says I need to use View Holder.

        row = inflater.inflate(mLayoutResourceId, parent, false);

How can I fix this?

package com.example.android.ontrack.adapters;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import com.example.android.ontrack.R;
import com.example.android.ontrack.models.Order;

public class OrdersList extends ArrayAdapter<Order> {

    Context mContext;
    int mLayoutResourceId;
    Order mData[] = null;

    public OrdersList(Context context, int resource, Order[] data) {
        super(context, resource, data);
        this.mContext = context;
        this.mLayoutResourceId = resource;
        this.mData = data;
    }


    @Override
    public Order getItem(int position) {
        return super.getItem(position);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View row;

        LayoutInflater inflater = LayoutInflater.from(mContext);
        row = inflater.inflate(mLayoutResourceId, parent, false);

            TextView nameOfSchool = row.findViewById(R.id.school_name_orders);
        TextView nameOfAgent = row.findViewById(R.id.agent_name);
        TextView orderId = row.findViewById(R.id.order_id);
        TextView netRevenue = row.findViewById(R.id.net_revenue);
        TextView orderQuantity = row.findViewById(R.id.total_quantity);
        TextView date = row.findViewById(R.id.date);

        Order orders = mData[position];

        nameOfSchool.setText(orders.nameOfSchool);
        nameOfAgent.setText(String.valueOf(orders.nameOfAgent));
        orderId.setText(String.valueOf(orders.orderId));
        netRevenue.setText(String.valueOf(orders.netRevenue));
        orderQuantity.setText(String.valueOf(orders.totalQuantity));
        date.setText(String.valueOf(orders.date));

        return row;
    }
}
halfer
  • 19,824
  • 17
  • 99
  • 186
niel
  • 125
  • 9

2 Answers2

6

Your problem is in this bit of code:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View row;

    LayoutInflater inflater = LayoutInflater.from(mContext);
    row = inflater.inflate(mLayoutResourceId, parent, false);

    ...
    return row;
}

The warning message is saying that, no matter what gets passed by the system to getView(), you're always calling inflater.inflate() to generate your row.

The first thing to understand is that the convertView param passed to getView() is sometimes null, and sometimes not. When it is not null, it is an "old" ("recycled") view that was previously returned from getView(). In other words, it's the exact same type of view that you'd inflate yourself, it just has old data in it.

So, you can fix that problem by changing the above code to this instead:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View row;

    if (convertView == null) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        row = inflater.inflate(mLayoutResourceId, parent, false);
    } else {
        row = convertView;
    }

    ...
    return row;
}

The second thing to understand is that findViewById() is slow. Not like multiple seconds slow, but slow enough that if you have to run it hundreds of times it will cause your UI to lag. So the "view holder pattern" is a way for you to avoid having to look up these views every time.

The first thing you do is create a class that has fields for each view in your row. Given what you posted, that would look something like this:

private static class ViewHolder {

    private final TextView nameOfSchool;
    private final TextView nameOfAgent;
    private final TextView orderId;
    private final TextView netRevenue;
    private final TextView orderQuantity;
    private final TextView date;

    private ViewHolder(View row) {
        nameOfSchool = row.findViewById(R.id.school_name_orders);
        nameOfAgent = row.findViewById(R.id.agent_name);
        orderId = row.findViewById(R.id.order_id);
        netRevenue = row.findViewById(R.id.net_revenue);
        orderQuantity = row.findViewById(R.id.total_quantity);
        date = row.findViewById(R.id.date);
    }
}

Now you have to plug it in to your getView() code. You will want to create a new ViewHolder every time you inflate a view (i.e. when convertView is null), and you'll want to re-use an existing ViewHolder otherwise. We use the view's tag for this.

All together, it looks like this:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View row;
    ViewHolder holder;

    if (convertView == null) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        row = inflater.inflate(mLayoutResourceId, parent, false);
        holder = new ViewHolder(row);
        row.setTag(holder);       
    } else {
        row = convertView;
        holder = (ViewHolder) row.getTag();
    }

    Order orders = mData[position];

    holder.nameOfSchool.setText(orders.nameOfSchool);
    holder.nameOfAgent.setText(String.valueOf(orders.nameOfAgent));
    holder.orderId.setText(String.valueOf(orders.orderId));
    holder.netRevenue.setText(String.valueOf(orders.netRevenue));
    holder.orderQuantity.setText(String.valueOf(orders.totalQuantity));
    holder.date.setText(String.valueOf(orders.date));

    return row;
}
Ben P.
  • 52,661
  • 6
  • 95
  • 123
1

Layout inflation can be very time consuming due to hierarchy computation and a few other things, which is why android introduced a platform for RECYCLING views.

You create a view once and when it is out of use it can be recycled. So it just has to bind the data to the view.

A view holder is a wrapper for the view and it is the one that holds ref. to the view to be re-used. You can check out these single page tutorial on RecyclerView which is a more flexible and efficient version of ListView

https://developer.android.com/training/material/lists-cards.html

halfer
  • 19,824
  • 17
  • 99
  • 186
kooldeji
  • 59
  • 7