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.