web-dev-qa-db-ja.com

RecyclerViewのスクロール時の遅延

RecyclerViewをスクロールしたときにブレーキがかかる理由がわかりません。スクロールはmRecyclerView.smoothScrollToPosition(position);によって呼び出されます。けいれんのリストをスクロールすると(レンダリング時間枠> 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);
        }
    }

}

アダプターに参加するレイアウト要素は、LinearLayout、ImageView、2TextViewで構成されます。

最初は、画像のダウンロードに問題があると思いました。画像をAsyncTaskにロードし、ピカソを介してビットマップをアップロードしますが、結局はマークから削除され、リストはまだ遅れます。

スクロール後にRAMをダンプするには、追加した後、元々ViewHolderが71個のメモリインスタンスがあることを示しています。

@Override
    public boolean onFailedToRecycleView(ViewHolder holder) {
        return true;
    }

この値は15-16に減少します ただし、RecyclerViewのレート-5 コピーであり、それらをオーバーライドする必要がありますが、スクロールしても新しいものは作成されません。 Screenshot 1

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>
6
Nikita G.

ここで受け入れられた答えに応えて:

コンテキストがアダプタのメモリリークになる可能性はありますか?それとも遅くしますか?とにかく、それはアクティビティのRecyclerViewにのみアタッチされます...アクティビティがなくなると、コンテキストはアダプタと一緒にGCされます。それだけでなく、ビューからコンテキストを取得すると常に同じコンテキストが返されるため、それは役に立たず、必要なしに関数を呼び出します。アダプターを静的内部クラスにして、フィールドを宣言する代わりに直接アクセスできるようにすることもできます。要するに、コンテキストはメモリリークがある理由ではありません。

では、何が問題になるのでしょうか。次のように、ViewHolderにバインドするたびに、ピカソでの前の画像リクエストをキャンセルする必要があると思います。

 Picasso.with(context).cancelRequest(holder.image);

アクティビティが破壊された場合(ただし、向きの変更が原因ではありません)に備えて、すべての人にそれを実行することもできますが、これはニーズによって異なります。

代わりにGlideライブラリも試してみることをお勧めします。それはより現代的であり、まだ維持されているようです。

1

コンテキストをアダプターに渡します。まず第一に、これはメモリリークにつながる可能性があり、パフォーマンスに影響を与える可能性もあります。コンテキストをアダプタに渡す代わりに、ViewHolderから取得するだけです。 RecyclerView.Adapter内でコンテキスト参照を取得することができ、それを渡す必要はありません。

スクロール後にRAM)をダンプすると、元々ViewHolderに71個のメモリインスタンスがあることがわかります。

ダンプから判断すると、これはおそらく事実です。

@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();
    ...
}
4
Andrej Jurkin