RecyclerView onCreateViewHolder called excessively when scrolling fast with DPAD

Dreamingwhale picture Dreamingwhale · Apr 21, 2015 · Viewed 8.7k times · Source

I'm developing on Amazon Fire TV.

Because it's a TV app(No touch), I need focusables inside row's layout to be able to navigate around.

I have a really simple Recyclerview with image, text, and a focusable. When I press up or down, it all scrolls and stuff correctly, but I noticed that when I navigate faster than scroll can keep up, it creates new viewholders (Off screen) and lags up the UI.

I have created an activity with Creation numbers on it. When I scroll slowly, the highest creation # is 10. But when I scroll fast, I get cards with creation number 60 in a second. This causes an enormous lag and the application drops a lot of frames. Is my approach totally wrong?

Use the code below to test this out.

/**
 * Created by sylversphere on 15-04-15.
 */
public class LandfillActivity extends Activity{

private Context context;

private static int ticketNumber;
private static int getTicket(){
    ticketNumber ++;
    return ticketNumber;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    context = this;
    setContentView(R.layout.landfill_activity);
    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
    GridLayoutManager glm = new GridLayoutManager(context, 2);
    recyclerView.setLayoutManager(glm);
    SickAdapter sickAdapter = new SickAdapter();
    recyclerView.setAdapter(sickAdapter);
}

public class SickViewHolder extends RecyclerView.ViewHolder{
    TextView ticketDisplayer;
    public ImageView imageView;
    public SickViewHolder(View itemView) {
        super(itemView);
        ticketDisplayer = (TextView) itemView.findViewById(R.id.ticketDisplayer);
        imageView = (ImageView) itemView.findViewById(R.id.imageView);

        itemView.findViewById(R.id.focus_glass).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                context.startActivity(new Intent(context, LouisVuittonActivity.class));
            }
        });
    }
    public void setTicket(int value){
        ticketDisplayer.setText(""+value);
    }
}

public class SickAdapter extends RecyclerView.Adapter<SickViewHolder>{

    @Override
    public SickViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        SickViewHolder svh = new SickViewHolder(getLayoutInflater().inflate(R.layout.one_row_element, null));
        svh.setTicket(getTicket());
        return svh;
    }

    @Override
    public void onBindViewHolder(SickViewHolder holder, int position) {
        String[] image_url_array = getResources().getStringArray(R.array.test_image_urls);
        Picasso.with(context).load(image_url_array[position % image_url_array.length] ).fit().centerCrop().into(holder.imageView);
    }

    @Override
    public int getItemCount() {
        return 100000;
    }
}
}

one_row_element.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:orientation="horizontal">
        <ImageView
            android:id="@+id/imageView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:adjustViewBounds="true"
            android:scaleType="centerCrop"
            android:src="@mipmap/sick_view_row_bg" />
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left|center_vertical"
            android:layout_marginLeft="15dp"
            android:orientation="horizontal">
            <TextView
                android:id="@+id/virusTextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Creation #"
                android:textColor="#fff"
                android:textSize="40sp" />
            <TextView
                android:id="@+id/ticketDisplayer"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="1"
                android:textColor="#fff"
                android:textSize="40sp" />
        </LinearLayout>
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/focus_glass"
            android:background="@drawable/subtle_focus_glass"
            android:focusable="true"
            android:focusableInTouchMode="true"/>
    </FrameLayout>
</FrameLayout>

test_image_urls.xml (Urls not owned by me)

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="test_image_urls"
    formatted="false">
    <item>http://farm4.static.flickr.com/3175/2737866473_7958dc8760.jpg</item>
    <item>http://farm4.static.flickr.com/3276/2875184020_9944005d0d.jpg</item>
    <item>http://farm3.static.flickr.com/2531/4094333885_e8462a8338.jpg</item>
    <item>http://farm4.static.flickr.com/3289/2809605169_8efe2b8f27.jpg</item>
    <item>http://2.bp.blogspot.com/_SrRTF97Kbfo/SUqT9y-qTVI/AAAAAAAABmg/saRXhruwS6M/s400/bARADEI.jpg</item>
    <item>http://fortunaweb.com.ar/wp-content/uploads/2009/10/Caroline-Atkinson-FMI.jpg</item>
    <item>http://farm4.static.flickr.com/3488/4051378654_238ca94313.jpg</item>
    <item>http://farm4.static.flickr.com/3368/3198142470_6eb0be5f32.jpg</item>
    <item>http://www.powercai.net/Photo/UploadPhotos/200503/20050307172201492.jpg</item>
    <item>http://www.web07.cn/uploads/Photo/c101122/12Z3Y54RZ-22027.jpg</item>
    <item>http://www.mitravel.com.tw/html/asia/2011/Palau-4/index_clip_image002_0000.jpg</item>
    <item>http://news.xinhuanet.com/mil/2007-05/19/xinsrc_36205041914150623191153.jpg</item>
    <item>http://ib.berkeley.edu/labs/koehl/images/hannah.jpg</item>
    <item>http://down.tutu001.com/d/file/20110307/ef7937c2b70bfc2da539eea9df_560.jpg</item>
    <item>http://farm3.static.flickr.com/2278/2300491905_5272f77e56.jpg</item>
    <item>http://www.pic35.com/uploads/allimg/100526/1-100526224U1.jpg</item>
    <item>http://img.99118.com/Big2/1024768/20101211/1700013.jpg</item>
    <item>http://farm1.static.flickr.com/45/139488995_bd06578562.jpg</item>
</string-array>
</resources>

subtle_focus

    <?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="true" android:drawable="@color/glass_focus"/>
    <item android:drawable="@color/glass_normal"/>
</selector>

glass_normal is #9000

glass_focus is #0000

Answer

Sam Judd picture Sam Judd · Apr 28, 2015

Try increasing the maximum number of recycled views in the pool:

recyclerView.getRecycledViewPool().setMaxRecycledViews(50);

50 is an arbitrary number, you can try higher or lower and see what happens.

RecyclerView tries to avoid re-using views that have transient state, so if views are invalidated quickly or are animating, they may not be re-used right away.

Similarly if you have many smaller views, you may end up with more on screen than the default pool size can handle (more common with grid like layouts).