リストとカードの作成ガイドに基づいて、RecyclerViewの例を作成しました。私のアダプターには、レイアウトを膨らませるためだけのパターン実装があります。
問題は、スクロールパフォーマンスが低いことです。これは、8つのアイテムしかないRecycleViewにあります。
一部のテストでは、AndroidLではこの問題が発生しないことを確認しました。しかし、KitKatバージョンでは、パフォーマンスの低下が明らかです。
リストとカードの作成ガイドに基づいて、RecyclerViewの例を作成しました。私のアダプターには、レイアウトを膨らませるためだけのパターン実装があります。
問題は、スクロールパフォーマンスが低いことです。これは、8つのアイテムしかないRecycleViewにあります。
一部のテストでは、AndroidLではこの問題が発生しないことを確認しました。しかし、KitKatバージョンでは、パフォーマンスの低下が明らかです。
回答:
最近同じ問題に直面したので、これが最新のRecyclerViewサポートライブラリで行ったことです。
複雑なレイアウト(ネストされたビュー、RelativeLayout)を新しい最適化されたConstraintLayoutに置き換えます。AndroidStudioでアクティブ化します。[SDKマネージャー]-> [SDKツール]タブ-> [サポートリポジトリ]-> [AndroidのConstraintLayout]と[Solver]の[ConstraintLayout]を確認します。依存関係に追加します。
compile 'com.android.support.constraint:constraint-layout:1.0.2'
可能であれば、RecyclerViewのすべての要素を同じ高さにします。そして追加:
recyclerView.setHasFixedSize(true);
デフォルトのRecyclerView描画キャッシュメソッドを使用し、ケースに応じてそれらを微調整します。そのためにサードパーティのライブラリは必要ありません。
recyclerView.setItemViewCacheSize(20);
recyclerView.setDrawingCacheEnabled(true);
recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
多くの画像を使用する場合は、それらのサイズと圧縮が最適であることを確認してください。画像のスケーリングもパフォーマンスに影響を与える可能性があります。問題には、使用されるソースイメージとデコードされたビットマップの2つの側面があります。次の例は、Webからダウンロードした画像をデコードする方法のヒントを示しています。
InputStream is = (InputStream) url.getContent();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap image = BitmapFactory.decodeStream(is, null, options);
最も重要な部分は指定ですinPreferredConfig
-それは画像の各ピクセルに使用されるバイト数を定義します。これは推奨されるオプションであることに注意してください。ソース画像の色が多い場合でも、別の構成でデコードされます。
onBindViewHolder()が可能な限り安価であることを確認してください。OnClickListenerを一度設定onCreateViewHolder()
し、インターフェイスを介してアダプタの外部のリスナーを呼び出し、クリックされたアイテムを渡すことができます。このようにして、常に余分なオブジェクトを作成する必要はありません。ここでビューに変更を加える前に、フラグと状態も確認してください。
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Item item = getItem(getAdapterPosition());
outsideClickListener.onItemClicked(item);
}
});
データが変更された場合は、影響を受けるアイテムのみを更新してみてください。たとえば、データセット全体をで無効にする代わりに、notifyDataSetChanged()
アイテムを追加/ロードするときは、次を使用します。
adapter.notifyItemRangeInserted(rangeStart, rangeEnd);
adapter.notifyItemRemoved(position);
adapter.notifyItemChanged(position);
adapter.notifyItemInserted(position);
最後の手段としてnotifyDataSetChanged()に依存します。
ただし、使用する必要がある場合は、アイテムを一意のIDで維持してください。
adapter.setHasStableIds(true);
RecyclerViewは、このメソッドが使用されると、安定したIDを持っていることを報告するアダプターの目に見える構造変更イベントを合成しようとします。これは、アニメーションとビジュアルオブジェクトの永続性の目的に役立ちますが、個々のアイテムビューは、リバウンドして再レイアウトする必要があります。
すべてを正しく行ったとしても、RecyclerViewが期待どおりにスムーズに実行されていない可能性があります。
recyclerView.setItemViewCacheSize(20);
がパフォーマンスを損なう理由がわかります。しかしrecyclerView.setDrawingCacheEnabled(true);
、recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
しかし!これらが何かを変えるかどうかはわかりません。これらはView
、図面キャッシュをビットマップとしてプログラムで取得し、後でそれを利用できるようにする特定の呼び出しです。RecyclerView
それについては何もしていないようです。
私はあなたのパフォーマンスを殺すことができる少なくとも1つのパターンを発見しました。それonBindViewHolder()
は頻繁に呼ばれることを忘れないでください。したがって、そのコードで行うことはすべて、パフォーマンスを停止させる可能性があります。RecyclerViewがカスタマイズを行う場合、このメソッドに誤ってスローコードを挿入するのは非常に簡単です。
位置に応じて、各RecyclerViewの背景画像を変更していました。ただし、画像の読み込みには少し手間がかかるため、RecyclerViewの動作が遅くなります。
画像のキャッシュを作成すると、驚異的に機能しました。onBindViewHolder()
キャッシュされた画像への参照を最初からロードするのではなく、変更するだけです。これで、RecyclerViewが圧縮されます。
誰もがこの正確な問題を抱えているわけではないことを私は知っているので、私はコードをロードすることを気にしません。ただしonBindViewHolder()
、RecyclerViewのパフォーマンスが低下する可能性のあるボトルネックとして、自分で行われる作業を考慮してください。
@Galyaの詳細な回答に加えて、最適化の問題である可能性がありますが、デバッガーを有効にすると処理速度が大幅に低下する可能性があることも事実です。
を最適化するためにすべてを行っRecyclerView
てもスムーズに機能しない場合は、ビルドバリアントをに切り替えてrelease
、非開発環境(デバッガーを無効にした状態)でどのように機能するかを確認してください。
debug
ビルドバリアントでアプリのパフォーマンスが遅いことがありましたが、release
バリアントに切り替えるとすぐにスムーズに動作しました。これは、release
ビルドバリアントを使用して開発する必要があるという意味ではありませんが、アプリを出荷する準備ができているときはいつでも問題なく動作することを知っておくとよいでしょう。
RecyclerView
のパフォーマンスについてお話を伺いました。こちらが英語のスライドとロシア語の録画ビデオです。
一連のテクニックが含まれています(それらのいくつかはすでに@Daryaの回答でカバーされています)。
簡単な要約は次のとおりです。
Adapter
アイテムのサイズが固定されている場合は、次のように設定します。
recyclerView.setHasFixedSize(true);
データエンティティをlongで表すことができる場合(hashCode()
たとえば)、次のように設定します。
adapter.hasStableIds(true);
実装:
// YourAdapter.java
@Override
public long getItemId(int position) {
return items.get(position).hashcode(); //id()
}
この場合Item.id()
、Item
コンテンツが変更されても同じままであるため、機能しません。
PS DiffUtilを使用している場合、これは必要ありません。
正しくスケーリングされたビットマップを使用します。車輪の再発明をしてライブラリを使用しないでください。
選択方法の詳細はこちら。
常に最新バージョンのを使用してくださいRecyclerView
。たとえば、25.1.0
-プリフェッチでパフォーマンスが大幅に向上しました。
詳細はこちら。
DiffUtillを使用します。
DiffUtilは必須です。
公式ドキュメント。
アイテムのレイアウトを簡素化してください!
TextViewsを充実させるための小さなライブラリ-TextViewRichDrawable
詳細な説明については、スライドを参照してください。
setHasStableId
フラグを使用することで問題が解決するかどうかはよくわかりません。提供する情報に基づいて、パフォーマンスの問題はメモリの問題に関連している可能性があります。ユーザーインターフェイスとメモリに関するアプリケーションのパフォーマンスは、かなり関連しています。
先週、アプリがメモリリークしていることを発見しました。これを発見したのは、アプリを20分使用した後、UIのパフォーマンスが非常に遅いことに気付いたためです。アクティビティを閉じたり開いたり、要素の束を含むRecyclerViewをスクロールしたりするのは本当に遅かった。http://flowup.io/を使用して本番環境で一部のユーザーを監視した後、次のことがわかりました。
フレーム時間は本当に長く、1秒あたりのフレーム数は本当に低かった。:Sをレンダリングするのに約2秒かかるフレームがあることがわかります。
この悪いフレーム時間/ fpsの原因を突き止めようとすると、次のようにメモリの問題が発生したことがわかりました。
アプリがフレームをドロップしているのと同時に、平均メモリ消費量が15MBに近い場合でも。
それが私がUIの問題を発見した方法です。アプリでメモリリークが発生し、ガベージコレクターイベントが多数発生しました。これは、Android VMがアプリを停止してフレームごとにメモリを収集する必要があるため、UIのパフォーマンスが低下する原因でした。
コードを見ると、Android Choreographerインスタンスからリスナーの登録を解除していなかったため、カスタムビュー内にリークがありました。修正をリリースした後、すべてが正常になりました:)
メモリの問題が原因でアプリがフレームをドロップしている場合は、次の2つの一般的なエラーを確認する必要があります。
アプリが1秒間に複数回呼び出されるメソッド内にオブジェクトを割り当てているかどうかを確認します。この割り当ては、アプリケーションが遅くなっている別の場所で実行できる場合でも。例として、リサイクラービュービューホルダーのonBindViewHolderのonDrawカスタムビューメソッド内にオブジェクトの新しいインスタンスを作成する場合があります。アプリがインスタンスをAndroidSDKに登録しているが、リリースしていないかどうかを確認します。リスナーをバスイベントに登録すると、リークが発生する可能性もあります。
免責事項:アプリの監視に使用しているツールは開発中です。私は開発者の一人なので、このツールにアクセスできます:)このツールにアクセスしたい場合は、すぐにベータ版をリリースします!あなたは私たちのウェブサイトに参加することができます:http://flowup.io/。
さまざまなツールを使用する場合は、traveview、dmtracedump、systrace、またはAndroidStudioに統合されたAndoridパフォーマンスモニターを使用できます。ただし、このツールは接続されたデバイスを監視し、残りのユーザーデバイスやAndroidOSのインストールは監視しないことに注意してください。
RecyclerViewでは、item_layoutの背景にビットマップ画像を使用しています。
@Galyaが言ったことはすべて真実です(そして私は彼の素晴らしい答えに感謝します)。しかし、彼らは私のために働きませんでした。
これが私の問題を解決したものです:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
詳細については、この回答をお読みください 。
私の場合、複雑なrecyclerviewの子がいます。そのため、アクティビティの読み込み時間に影響しました(アクティビティのレンダリングでは約5秒)
postDelayed()でアダプタをロードします->これにより、アクティビティのレンダリングに良い結果が得られます。アクティビティの後、recyclerviewのロードをスムーズにレンダリングします。
この答えを試してください、
recyclerView.postDelayed(new Runnable() {
@Override
public void run() {
recyclerView.setAdapter(mAdapter);
}
},100);
コメントですでにViewHolder
パターンを実装していることがわかりますが、パターンを使用するアダプターの例をここに投稿RecyclerView.ViewHolder
して、同様の方法で統合していることを確認できるようにします。ここでも、コンストラクターはニーズに応じて異なります。例です:
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {
Context mContext;
List<String> mNames;
public RecyclerAdapter(Context context, List<String> names) {
mContext = context;
mNames = names;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(android.R.layout.simple_list_item_1, viewGroup, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
//Populate.
if (mNames != null) {
String name = mNames.get(position);
viewHolder.name.setText(name);
}
}
@Override
public int getItemCount() {
if (mNames != null)
return mNames.size();
else
return 0;
}
/**
* Static Class that holds the RecyclerView views.
*/
static class ViewHolder extends RecyclerView.ViewHolder {
TextView name;
public ViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(android.R.id.text1);
}
}
}
作業に問題がRecyclerView.ViewHolder
ある場合は、Gradleで常に確認できる適切な依存関係があることを確認してください
それがあなたの問題を解決することを願っています。
これにより、よりスムーズなスクロールが可能になりました。
アダプターのonFailedToRecycleView(ViewHolderホルダー)をオーバーライドします
進行中のアニメーション(ある場合)ホルダーを停止します。 "animateview" .clearAnimation();
trueを返すことを忘れないでください。
ピカソライブラリで1行だけを使用してこの問題を解決します
。フィット()
Picasso.get().load(currentItem.getArtist_image())
.fit()//this wil auto get the size of image and reduce it
.placeholder(R.drawable.ic_doctor)
.into(holder.img_uploaderProfile, new Callback() {
@Override
public void onSuccess() {
}
@Override
public void onError(Exception e) {
Toast.makeText(context, "Something Happend Wrong Uploader Image", Toast.LENGTH_LONG).show();
}
});