简体   繁体   中英

What is causing my poor scrolling performance in RecyclerView?

I am having trouble optimizing my RecyclerView. When I cold start the app, the first scroll is always janky.

I followed this , but it does not help as I am not sure what causes the lag.

Here is my RecyclerView's content (list_item.xml):

<android.support.v7.widget.CardView 
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="86dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp">

<android.support.constraint.ConstraintLayout
    android:id="@+id/list_item_constraint_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?android:attr/selectableItemBackground">

    <TextView
        android:id="@+id/list_item_akcija_ime"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:ellipsize="marquee"
        android:fontFamily="sans-serif"
        android:singleLine="true"
        android:text="@string/ime_akcije"
        android:textColor="@color/common_google_signin_btn_text_dark_focused"
        android:textSize="18sp"
        app:layout_constraintEnd_toStartOf="@+id/list_item_eye"
        app:layout_constraintStart_toEndOf="@+id/list_item_slika"
        app:layout_constraintTop_toTopOf="@+id/list_item_slika" />

    <TextView
        android:id="@+id/list_item_akcija_datum"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:fontFamily="sans-serif"
        android:text="@string/_1_7_2019"
        android:textSize="12sp"
        app:layout_constraintStart_toStartOf="@+id/list_item_akcija_ime"
        app:layout_constraintTop_toBottomOf="@+id/list_item_akcija_ime" />

    <ImageView
        android:id="@+id/list_item_slika"
        android:layout_width="110dp"
        android:layout_height="70dp"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:adjustViewBounds="true"
        android:contentDescription="@string/slika_akcije"
        android:scaleType="centerCrop"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <LinearLayout
        android:id="@+id/list_item_starosti_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintBottom_toBottomOf="@+id/list_item_slika"
        app:layout_constraintStart_toStartOf="@+id/list_item_akcija_ime" />

    <ImageView
        android:id="@+id/list_item_fire"
        android:layout_width="21dp"
        android:layout_height="22dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:contentDescription="@string/fire"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/list_item_fires"
        app:layout_constraintTop_toBottomOf="@+id/list_item_eye"
        app:srcCompat="@drawable/campfire" />

    <ImageView
        android:id="@+id/list_item_eye"
        android:layout_width="21dp"
        android:layout_height="22dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:contentDescription="@string/eye"
        app:layout_constraintBottom_toTopOf="@+id/list_item_fire"
        app:layout_constraintEnd_toStartOf="@+id/list_item_clicks"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_chainStyle="packed"
        app:srcCompat="@drawable/eye" />

    <TextView
        android:id="@+id/list_item_clicks"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:textSize="12sp"
        app:layout_constraintBottom_toBottomOf="@+id/list_item_eye"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="@+id/list_item_eye"
        tools:text="159" />

    <TextView
        android:id="@+id/list_item_fires"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="60"
        android:textColor="@color/colorAccent"
        android:textSize="12sp"
        app:layout_constraintBottom_toBottomOf="@+id/list_item_fire"
        app:layout_constraintStart_toStartOf="@+id/list_item_clicks"
        app:layout_constraintTop_toBottomOf="@+id/list_item_eye" />

</android.support.constraint.ConstraintLayout>

This is what I do in onBindViewHolder

@Override
public void onBindViewHolder(@NonNull AkcijaViewHolder akcija, int i) {
    akcija.bindData(akcije.get(i), mOnItemClickListener, context); 
}

My bindData method is pretty expensive, but if I am right this shouldn't affect the performance?

public void bindData(final Akcija akcija, final AkcijaAdapter.OnItemClickListener onItemClickListener, Context context) {

    /* Set Akcija's ime to the TextView */
    imeTextView.setText(akcija.getIme());
    imeTextView.setSelected(true);

    /* Set date and show it */
    Date date = new Date(akcija.getDatum() * 1000);
    DateFormat dateFormat = new SimpleDateFormat("d. M. yyyy", Locale.getDefault()); // Format for 13. 4. 2019
    String strDate = dateFormat.format(date);
    datumTextView.setText(strDate);

    /* Set clicks */
    klikTextView.setText(String.valueOf(akcija.getClicks()));

    /* Draw starosti circle_mc images */
    String starosti = akcija.getStarost(); // get starosti...
    String[] starostiArr = starosti.split(" "); // and split them into strings

    linearLayout.removeAllViews();
    for (String starost : starostiArr) { // loop through all starosti. For each starost draw it by calling drawStarostSlika and pass in the drawable of circle
        switch (starost) {
            case "M":
                drawStarostSlika(R.drawable.circle_mu);
                break;
            case "MČ":
                drawStarostSlika(R.drawable.circle_mc);
                break;
            case "GG":
                drawStarostSlika(R.drawable.circle_gg);
                break;
            case "PP":
                drawStarostSlika(R.drawable.circle_pp);
                break;
            case "RR":
                drawStarostSlika(R.drawable.circle_rr);
                break;
            case "G":
                drawStarostSlika(R.drawable.circle_gr);
                break;
            default:
                break;
        }
    }

    /* Load image into slikaImageView from Firebase Storage */
    GlideApp.with(context)
            .load(storageReference.child(akcija.getIme() + ".jpg"))
            .thumbnail(0.5f)
            .transition(DrawableTransitionOptions.withCrossFade())
            .diskCacheStrategy(DiskCacheStrategy.DATA)
            .into(slikaImageView);


    /* Make each item clickable. On click, run the method passed in to adapter from anywhere. */
    listItemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            onItemClickListener.onItemClick(akcija, v);
        }
    });
}

This is how I populate the RecyclerView

private DatabaseReference mDatabaseReference = FirebaseDatabase.getInstance().getReference().child("akcije"); 
private StorageReference storageReference = FirebaseStorage.getInstance().getReference();

List<Akcija> akcije = new ArrayList<>();
AkcijaAdapter adapter;
RecyclerView recyclerView;

Context context; 

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    context = MainActivity.this;



    mDatabaseReference.addValueEventListener(valueEventListener); // set listener for the method defined below

    adapter = new AkcijaAdapter(context, akcije, onItemClickListener); // for now, add empty akcija's so the list is not skipped
    recyclerView = findViewById(R.id.list);
    recyclerView.setHasFixedSize(true); 
    recyclerView.setAdapter(adapter);
    recyclerView.setLayoutManager(new LinearLayoutManager(context)); 
}

/* When Firebase's databases changes update the recycler view */
ValueEventListener valueEventListener = new ValueEventListener() {

    @Override
    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
        for (DataSnapshot ds : dataSnapshot.getChildren()) {
            akcije.add(new Akcija(
                    ds.getKey(), // the key is the same as Akcija's name
                    ds.child("datum").getValue(Long.class), // milliseconds since 1970
                    ds.child("starost").getValue().toString(),
                    ds.child("link").getValue().toString(),
                    ds.child("klik").getValue(Integer.class)
            ));
        }

        /* Sort akcija's by date */
        Collections.sort(akcije, new Comparator<Akcija>() {
            @Override
            public int compare(Akcija o1, Akcija o2) {
                return o2.getDatum().compareTo(o1.getDatum());
            }
        });

        adapter = new AkcijaAdapter(context, akcije, onItemClickListener); // update the list and
        recyclerView.setAdapter(adapter); // set the adapter
        mDatabaseReference.removeEventListener(this); // only update the app when it's ran
    }

    @Override
    public void onCancelled(@NonNull DatabaseError databaseError) {

    }
};


AkcijaAdapter.OnItemClickListener onItemClickListener = new AkcijaAdapter.OnItemClickListener() {
    @Override
    public void onItemClick(Akcija akcija, View listItemView) {
    // do stuff here
    }
};

}

I also thought using ConstraintLayout was optimal, but I don't know if my list_item is still too expensive?

The most probable reason why you're getting those lags is that every time you're receiving new data from your database you're setting a new adapter to your RecyclerView.

Try using a ListAdapter instead of a RecyclerView.Adapter as it's also more functional with reacting to changes. With that adapter you'll set it to your RecyclerView only once, then every time you receive new data you should call something like adapter.submitList(myNewData);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM