问题
i'm having an issue working with CardViews . I`ve created a CardView containing 1 TextView and 2 ImageViews , which gets displayed in an RecycleView . When i try to update the contents of one of the CardViews (created 5 cards for testing) the interface starts to lag when scrolling over the updated card, but goes back to normal when i pass the item. It only happens when ImageViews are present , when substituting the ImageViews for say, TextViews with some random text in them, it works normally.
Now, here's my code , after that i'll add some more information that could be causing it.
First , the Adapter class, which also contains the items class and List :
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;
}
}
Now, here is my main class , the 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;
}
And finally, not that i probably matters, my 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>
Now , somebody in another discussion showed me this as a possible cause : Why RecyclerView.notifyItemChanged() will create a new ViewHolder and use both the old ViewHolder and new one?
So i added the Log items inside the onBind and onCreate methods of the Adapter to see if that could be causing it, and indeed, it`s not just creatind one additional ViewHolders, but 3 of them, and each time i scroll over the newly updated card, it seems to be re-binding, the onBind method seems to be called each time.
Setting the animation to 'null' as per the solution in the thread doesn't seem to have any effect in my case, so maybe that`s not what is causing it.
Any ideas ?
回答1:
Well, i solved it. It seems to happen only when i'm loading the images via the XML method, if i load them with a 3rd party library like Picasso, the lag seems to dissapear. Something like :
Picasso.with(context).load(MovieDetails.getPoster())
.error(R.drawable.placeholder)
.placeholder(R.drawable.placeholder)
.into(movieViewHolder.imageViewPoster);
inside the onBindViewHolder and instead of passing around bitmaps, just pass a String for the URL or location of the image. And pass the Context to the Adapter class.
来源:https://stackoverflow.com/questions/32554358/encountering-lag-when-updating-a-cardview-item-in-a-recycleview