在 RecycleView 中更新 CardView 项目时遇到延迟
Encountering lag when updating a CardView item in a RecycleView
我在使用 CardViews 时遇到问题。我创建了一个包含 1 个 TextView 和 2 个 ImageView 的 CardView,它显示在 RecycleView 中。当我尝试更新其中一张 CardView 的内容(创建了 5 张用于测试的卡片)时,在更新的卡片上滚动时界面开始滞后,但当我通过该项目时又恢复正常。只有当 ImageViews 存在时才会发生,当用 ImageViews 代替其中包含一些随机文本的 TextViews 时,它可以正常工作。
现在,这是我的代码,之后我将添加一些可能导致它的更多信息。
首先,适配器 class,它还包含项 class 和列表 :
public class MovieDetailsAdapter extends RecyclerView.Adapter<MovieDetailsAdapter.MovieViewHolder> {
public List<MovieDetails> movieList;
private static final String TAG = "MyAdapter";
public MovieDetailsAdapter() {
movieList = new ArrayList< MovieDetails>();
}
public void setItemCount(int count ,String title, Bitmap poster,Bitmap fanart ) {
movieList.clear();
movieList.addAll(generateDummyData(count, title, poster , fanart));
notifyDataSetChanged();
}
@Override
public int getItemCount() {
return movieList.size();
}
public void addItem(int position,String title, Bitmap poster, Bitmap fanart) {
if (position > movieList.size()) return;
movieList.add(position, generateDummyItem(title, poster, fanart));
// notifyDataSetChanged();
notifyItemInserted(position);
}
public void updateItem(int position, String title, Bitmap poster, Bitmap fanart) {
if (position > movieList.size()) return;
movieList.add(position, generateDummyItem(title, poster, fanart));
notifyItemChanged(position);
// notifyDataSetChanged();
}
@Override
public MovieViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.
from(viewGroup.getContext()).
inflate(R.layout.card_layout_movies, viewGroup, false);
Log.d(TAG, "create header view holder");
return new MovieViewHolder(itemView);
}
@Override
public void onBindViewHolder(MovieViewHolder movieViewHolder, int i) {
Log.d(TAG, "bind header view holder");
MovieDetails mdet = movieList.get(i);
movieViewHolder.vTitle.setText(mdet.Title);
movieViewHolder.vPoster.setImageBitmap(mdet.imageViewPoster);
movieViewHolder.vFanart.setImageBitmap(mdet.imageViewFanart);
Log.d(TAG, "position: " + i + " holder: " + movieViewHolder + " text: " + movieViewHolder.vTitle);
}
public static class MovieViewHolder extends RecyclerView.ViewHolder {
protected TextView vTitle;
protected ImageView vPoster;
protected ImageView vFanart;
public MovieViewHolder(View v)
{
super(v);
vTitle = (TextView) v.findViewById(R.id.title);
vPoster = (ImageView) v.findViewById(R.id.imageViewPoster);
vFanart = (ImageView) v.findViewById(R.id.imageViewFanart);
}
}
public static class MovieDetails {
protected String title;
protected Bitmap imageViewPoster;
protected Bitmap imageViewFanart;
public MovieDetails(String title, Bitmap imageViewPoster,Bitmap imageViewFanart )
{
this.title = title;
this.imageViewPoster = imageViewPoster;
this.imageViewFanart = imageViewFanart;
}
}
public static MovieDetails generateDummyItem(String title, Bitmap poster, Bitmap fanart) {
MovieDetails mov = new MovieDetails(title, poster, fanart);
return mov;
}
public static List< MovieDetailsAdapter.MovieDetails> generateDummyData(int count, String title , Bitmap imageViewPoster, Bitmap imageviewFanart) {
ArrayList<MovieDetailsAdapter.MovieDetails> items = new ArrayList<MovieDetailsAdapter.MovieDetails>();
for (int i=0; i < count; i++) {
items.add(new MovieDetailsAdapter.MovieDetails(title, imageViewPoster, imageviewFanart));
}
return items;
}
}
现在,这是我的主要 class,activity class
public class MoviesListActivity extends AppCompatActivity {
public MovieDetailsAdapter ca = new MovieDetailsAdapter();
public RecyclerView recList;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_movies_list);
RecyclerView recList = (RecyclerView) findViewById(R.id.cardList);
LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
recList.setLayoutManager(llm);
button = (Button) findViewById(R.id.button);
MyOnClickListener Listener = new MyOnClickListener();
button.setOnClickListener(Listener);
recList.setItemAnimator(null);
Bitmap bittest1 = decodeSampledBitmapFromResource(getResources(), R.drawable.poster_example, 640, 955);
Bitmap bittest2 = decodeSampledBitmapFromResource(getResources(), R.drawable.fanart_example, 800, 450);
ca.setItemCount(5, "TestingTitle", bittest1, bittest2);
recList.setAdapter(ca);
}
public class MyOnClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
Bitmap bittest12 = decodeSampledBitmapFromResource(getResources(), R.drawable.poster2, 640, 955);
Bitmap bittest22 = decodeSampledBitmapFromResource(getResources(), R.drawable.fanart2, 800, 450);
Toast.makeText(getApplicationContext(), "msg msg", Toast.LENGTH_SHORT).show();
ca.updateItem(1, "test", bittest12, bittest22);
// ca.addItem(1,"test",bittest12,bittest22);
break;
default:
break;
}
}
}
//------methods used for resizing the images down to smaller ones before loading into memory---//
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
最后,我可能并不重要,我的 CardView XML。
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
card_view:cardCornerRadius="4dp"
android:layout_margin="5dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="20dp"
android:background="@color/bkg_card"
android:text="Movie Title"
android:gravity="center_vertical"
android:textColor="@android:color/white"
android:textSize="14dp"/>
<ImageView
android:elevation="5dp"
android:scaleType="fitCenter"
android:layout_width="100dp"
android:layout_height="150dp"
android:id="@+id/imageViewPoster"
android:text="Poster"
android:gravity="center_vertical"
android:layout_below="@id/title"
android:layout_marginTop="10dp"
android:layout_marginLeft="5dp"/>
<ImageView
android:id="@+id/imageViewFanart"
android:scaleType="fitCenter"
android:layout_alignBottom="@+id/imageViewPoster"
android:layout_toEndOf="@+id/imageViewPoster"
android:layout_width= "235dp"
android:layout_height="122dp"
android:layout_marginRight="5dp"/>
</RelativeLayout>
现在,另一个讨论中的某人告诉我这是一个可能的原因:
所以我在 Adapter 的 onBind 和 onCreate 方法中添加了 Log 项,以查看是否可能导致它,实际上,它不仅仅是创建一个额外的 ViewHolders,而是其中的 3 个,并且每次我翻过新更新的卡片,好像是重新绑定,好像每次都调用onBind方法
根据线程中的解决方案将动画设置为 'null' 对我的情况似乎没有任何影响,所以也许这不是导致它的原因。
有什么想法吗?
嗯,我解决了。它似乎只有在我通过 XML 方法加载图像时才会发生,如果我用像 Picasso 这样的第三方库加载它们,延迟似乎就会消失。像 :
Picasso.with(context).load(MovieDetails.getPoster())
.error(R.drawable.placeholder)
.placeholder(R.drawable.placeholder)
.into(movieViewHolder.imageViewPoster);
在 onBindViewHolder 中
而不是传递位图,只需传递 URL 或图像位置的字符串。并将上下文传递给适配器 class.
我在使用 CardViews 时遇到问题。我创建了一个包含 1 个 TextView 和 2 个 ImageView 的 CardView,它显示在 RecycleView 中。当我尝试更新其中一张 CardView 的内容(创建了 5 张用于测试的卡片)时,在更新的卡片上滚动时界面开始滞后,但当我通过该项目时又恢复正常。只有当 ImageViews 存在时才会发生,当用 ImageViews 代替其中包含一些随机文本的 TextViews 时,它可以正常工作。
现在,这是我的代码,之后我将添加一些可能导致它的更多信息。
首先,适配器 class,它还包含项 class 和列表 :
public class MovieDetailsAdapter extends RecyclerView.Adapter<MovieDetailsAdapter.MovieViewHolder> {
public List<MovieDetails> movieList;
private static final String TAG = "MyAdapter";
public MovieDetailsAdapter() {
movieList = new ArrayList< MovieDetails>();
}
public void setItemCount(int count ,String title, Bitmap poster,Bitmap fanart ) {
movieList.clear();
movieList.addAll(generateDummyData(count, title, poster , fanart));
notifyDataSetChanged();
}
@Override
public int getItemCount() {
return movieList.size();
}
public void addItem(int position,String title, Bitmap poster, Bitmap fanart) {
if (position > movieList.size()) return;
movieList.add(position, generateDummyItem(title, poster, fanart));
// notifyDataSetChanged();
notifyItemInserted(position);
}
public void updateItem(int position, String title, Bitmap poster, Bitmap fanart) {
if (position > movieList.size()) return;
movieList.add(position, generateDummyItem(title, poster, fanart));
notifyItemChanged(position);
// notifyDataSetChanged();
}
@Override
public MovieViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.
from(viewGroup.getContext()).
inflate(R.layout.card_layout_movies, viewGroup, false);
Log.d(TAG, "create header view holder");
return new MovieViewHolder(itemView);
}
@Override
public void onBindViewHolder(MovieViewHolder movieViewHolder, int i) {
Log.d(TAG, "bind header view holder");
MovieDetails mdet = movieList.get(i);
movieViewHolder.vTitle.setText(mdet.Title);
movieViewHolder.vPoster.setImageBitmap(mdet.imageViewPoster);
movieViewHolder.vFanart.setImageBitmap(mdet.imageViewFanart);
Log.d(TAG, "position: " + i + " holder: " + movieViewHolder + " text: " + movieViewHolder.vTitle);
}
public static class MovieViewHolder extends RecyclerView.ViewHolder {
protected TextView vTitle;
protected ImageView vPoster;
protected ImageView vFanart;
public MovieViewHolder(View v)
{
super(v);
vTitle = (TextView) v.findViewById(R.id.title);
vPoster = (ImageView) v.findViewById(R.id.imageViewPoster);
vFanart = (ImageView) v.findViewById(R.id.imageViewFanart);
}
}
public static class MovieDetails {
protected String title;
protected Bitmap imageViewPoster;
protected Bitmap imageViewFanart;
public MovieDetails(String title, Bitmap imageViewPoster,Bitmap imageViewFanart )
{
this.title = title;
this.imageViewPoster = imageViewPoster;
this.imageViewFanart = imageViewFanart;
}
}
public static MovieDetails generateDummyItem(String title, Bitmap poster, Bitmap fanart) {
MovieDetails mov = new MovieDetails(title, poster, fanart);
return mov;
}
public static List< MovieDetailsAdapter.MovieDetails> generateDummyData(int count, String title , Bitmap imageViewPoster, Bitmap imageviewFanart) {
ArrayList<MovieDetailsAdapter.MovieDetails> items = new ArrayList<MovieDetailsAdapter.MovieDetails>();
for (int i=0; i < count; i++) {
items.add(new MovieDetailsAdapter.MovieDetails(title, imageViewPoster, imageviewFanart));
}
return items;
}
}
现在,这是我的主要 class,activity class
public class MoviesListActivity extends AppCompatActivity {
public MovieDetailsAdapter ca = new MovieDetailsAdapter();
public RecyclerView recList;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_movies_list);
RecyclerView recList = (RecyclerView) findViewById(R.id.cardList);
LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
recList.setLayoutManager(llm);
button = (Button) findViewById(R.id.button);
MyOnClickListener Listener = new MyOnClickListener();
button.setOnClickListener(Listener);
recList.setItemAnimator(null);
Bitmap bittest1 = decodeSampledBitmapFromResource(getResources(), R.drawable.poster_example, 640, 955);
Bitmap bittest2 = decodeSampledBitmapFromResource(getResources(), R.drawable.fanart_example, 800, 450);
ca.setItemCount(5, "TestingTitle", bittest1, bittest2);
recList.setAdapter(ca);
}
public class MyOnClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
Bitmap bittest12 = decodeSampledBitmapFromResource(getResources(), R.drawable.poster2, 640, 955);
Bitmap bittest22 = decodeSampledBitmapFromResource(getResources(), R.drawable.fanart2, 800, 450);
Toast.makeText(getApplicationContext(), "msg msg", Toast.LENGTH_SHORT).show();
ca.updateItem(1, "test", bittest12, bittest22);
// ca.addItem(1,"test",bittest12,bittest22);
break;
default:
break;
}
}
}
//------methods used for resizing the images down to smaller ones before loading into memory---//
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
最后,我可能并不重要,我的 CardView XML。
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
card_view:cardCornerRadius="4dp"
android:layout_margin="5dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="20dp"
android:background="@color/bkg_card"
android:text="Movie Title"
android:gravity="center_vertical"
android:textColor="@android:color/white"
android:textSize="14dp"/>
<ImageView
android:elevation="5dp"
android:scaleType="fitCenter"
android:layout_width="100dp"
android:layout_height="150dp"
android:id="@+id/imageViewPoster"
android:text="Poster"
android:gravity="center_vertical"
android:layout_below="@id/title"
android:layout_marginTop="10dp"
android:layout_marginLeft="5dp"/>
<ImageView
android:id="@+id/imageViewFanart"
android:scaleType="fitCenter"
android:layout_alignBottom="@+id/imageViewPoster"
android:layout_toEndOf="@+id/imageViewPoster"
android:layout_width= "235dp"
android:layout_height="122dp"
android:layout_marginRight="5dp"/>
</RelativeLayout>
现在,另一个讨论中的某人告诉我这是一个可能的原因:
所以我在 Adapter 的 onBind 和 onCreate 方法中添加了 Log 项,以查看是否可能导致它,实际上,它不仅仅是创建一个额外的 ViewHolders,而是其中的 3 个,并且每次我翻过新更新的卡片,好像是重新绑定,好像每次都调用onBind方法
根据线程中的解决方案将动画设置为 'null' 对我的情况似乎没有任何影响,所以也许这不是导致它的原因。
有什么想法吗?
嗯,我解决了。它似乎只有在我通过 XML 方法加载图像时才会发生,如果我用像 Picasso 这样的第三方库加载它们,延迟似乎就会消失。像 :
Picasso.with(context).load(MovieDetails.getPoster())
.error(R.drawable.placeholder)
.placeholder(R.drawable.placeholder)
.into(movieViewHolder.imageViewPoster);
在 onBindViewHolder 中 而不是传递位图,只需传递 URL 或图像位置的字符串。并将上下文传递给适配器 class.