I can not understand what is the reason the brakes when you scroll through my RecyclerView. Scrolling is called by mRecyclerView.smoothScrollToPosition(position);
When you scroll through the list of twitches (rendering time frame> 16ms)
AlphaAdapter.java
public class AlphaAdapter extends RecyclerView.Adapter<AlphaAdapter.ViewHolder> {
private static final String TAG = "AlphaAdapter";
private Context mContext;
private List<PrimaryWeapon> mGunList;
private boolean isAnimate;
private final int VIEW_TYPE_NULL = 0;
private final int VIEW_TYPE_ITEM = 1;
public AlphaAdapter(Context mContext, List<PrimaryWeapon> mGunList) {
this.mContext = mContext;
this.mGunList = mGunList;
}
public AlphaAdapter(Context mContext, List<PrimaryWeapon> mGunList, boolean a) {
this.mContext = mContext;
this.mGunList = mGunList;
this.isAnimate = a;
}
@Override
public int getItemViewType(int position) {
try {
if (mGunList.get(position).getNameWeapon().equals(""))
return VIEW_TYPE_NULL;
else return VIEW_TYPE_ITEM;
} catch (Exception e) {
e.printStackTrace();
Log.e("AlphaAdapter", String.valueOf(position));
return VIEW_TYPE_NULL;
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
if (viewType == VIEW_TYPE_ITEM)
view = LayoutInflater.from(mContext).inflate(R.layout.item_game, parent, false);
else
view = LayoutInflater.from(mContext).inflate(R.layout.item_null, parent, false);
return new ViewHolder(view);
}
@Override
public void onViewDetachedFromWindow(ViewHolder holder) {
super.onViewDetachedFromWindow(holder);
holder.itemView.clearAnimation();
}
@Override
public boolean onFailedToRecycleView(ViewHolder holder) {
return true;
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
try {
long startTime = System.currentTimeMillis();
if (!getWeapon(position).getNameWeapon().equals("")) {
if (isAnimate) {
if (mLastAnimPosition < position) {
mLastAnimPosition = position;
Animation a = AnimationUtils.loadAnimation(mContext, R.anim.item_game_anim);
holder.itemView.startAnimation(a);
a.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
}
Picasso.with(mContext)
.load("file:///android_asset/" + getWeapon(position).getImagePath())
.placeholder(R.drawable.loading)
.error(R.drawable.load_fail)
.into(holder.image);
holder.main.setText(getWeapon(position).getNameWeapon());
holder.desc.setText(getWeapon(position).getNameSkin());
}
Log.i(TAG, String.valueOf(System.currentTimeMillis() - startTime) + "ms onBind (" + String.valueOf(position) + ");");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public long getItemId(int position) {
return position;
}
private PrimaryWeapon getWeapon(int position) {
return mGunList.get(position);
}
@Override
public int getItemCount() {
return mGunList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView main, desc;
ImageView image;
ViewHolder(View itemView) {
super(itemView);
main = (TextView) itemView.findViewById(R.id.item_textMain);
desc = (TextView) itemView.findViewById(R.id.item_textDescription);
image = (ImageView) itemView.findViewById(R.id.item_image);
}
}
}
Layout element that participates in the adapter consists of LinearLayout, ImageView, 2 TextView.
At first I thought that the problem in downloading images, try to load them into AsyncTask, upload bitmap through Picasso and without, but after all removed from their mark, and the list will still lag.
To dump the RAM after the scroll is, it shows that there are 71 memory instances originally ViewHolder, after adding
@Override
public boolean onFailedToRecycleView(ViewHolder holder) {
return true;
}
this value is reduced to 15-16, although the rate for RecyclerView - 5 copies, and it should override them, but does not create new ones when you scroll.
item_null.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="74dp"
android:layout_height="80dp">
</LinearLayout>
item_game.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_gravity="center"
android:padding="2dp"
android:id="@+id/layout"
android:layout_height="80dp"
android:fitsSystemWindows="true"
android:layout_width="74dp">
<ImageView
android:id="@+id/item_image"
android:layout_width="match_parent"
android:layout_height="56dp"
/>
<TextView
android:layout_gravity="top"
android:layout_width="match_parent"
android:singleLine="true"
android:id="@+id/item_textMain"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:paddingTop="1dp"
android:gravity="left"
android:textColor="#FFF"
android:maxLines="1"
android:textSize="8sp"
android:layout_height="10dp" />
<TextView
android:layout_gravity="bottom"
android:layout_width="match_parent"
android:id="@+id/item_textDescription"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:gravity="left"
android:textColor="#FFF"
android:singleLine="true"
android:maxLines="1"
android:maxLength="40"
android:textSize="6sp"
android:layout_height="12dp" />
</LinearLayout>
You are passing a context to adapter. First of all this could lead to memory leaks and also could be affecting your performance. Instead of passing the context into adapter, just simply get it from ViewHolder. You can always get a context reference inside RecyclerView.Adapter without a need to pass it around.
To dump the RAM after the scroll is, it shows that there are 71 memory instances originally ViewHolder.
Judging from the dump this is most probably the case.
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
...
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
Context context = holder.itemView.getContext();
...
}
In response to the answer that was accepted here:
How could a Context be a memory leak on an adapter? Or even make it slower? It's attached only to the Activity's RecyclerView anyway... Once the Activity is gone, the context is GC-ed together with the adapter. Not only that, but getting the Context from the view will always return you the same one, so it's useless and calls to functions without any need. You can even make the adapter a static inner class, so that you could access it directly, instead of having a declared field. In short, the context isn't the reason you have a memory leak.
What could be the issue, then? I think you need to cancel the previous image request on Picasso, each time you bind to a ViewHolder, like this:
Picasso.with(context).cancelRequest(holder.image);
You might also want to do it for all, in case the Activity was destroyed (yet not because of orientation change), but this depends on your needs.
I suggest to also try Glide library instead. It seems more modern and still being maintained.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With