在RecyclerView中的图像奇怪的行为

我正在使用android.support.v7.widget.RecyclerView来显示包含文本和在某些情况下也包含图像的简单项目。 基本上我从这里https://developer.android.com/training/material/lists-cards.html跟随教程,只是从String []更改为JSONArray和ViewHolder的XML的mDataset的types。 我的RecyclerView位于一个简单的片段:

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> <!-- A RecyclerView with some commonly used attributes --> <android.support.v7.widget.RecyclerView android:id="@+id/my_hopps_recycler_view" android:scrollbars="vertical" android:layout_centerInParent="true" android:layout_width="200dp" android:layout_height="wrap_content"/> </RelativeLayout> 

在这个片段中,我加载了来自web服务器的所有信息。 收到信息后,我在RecyclerView中初始化这些项目。 我使用在我的Fragment的onCreate方法中设置的LinearLayoutManager。 添加适配器后,我在我的RecyclerView上调用setHasFixedSize(true)方法。 我的适配器类看起来像这样:

 import android.graphics.BitmapFactory; import android.support.v7.widget.RecyclerView; import android.util.Base64; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import org.apache.commons.lang3.StringEscapeUtils; import org.json.JSONArray; import org.json.JSONObject; import saruul.julian.MyFragment; import saruul.julian.R; public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { /** * Fragment holding the RecyclerView. */ private MyFragment myFragment; /** * The data to display. */ private JSONArray mDataset; public static class ViewHolder extends RecyclerView.ViewHolder { public TextView text; ImageView image; public ViewHolder(View v) { super(v); text = (TextView) v.findViewById(R.id.my_view_text); image = (ImageView) v.findViewById(R.id.my_view_image); } } public MyAdapter(MyFragment callingFragment, JSONArray myDataset) { myFragment = callingFragment; mDataset = myDataset; } @Override public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.my_view, parent, false); ViewHolder vh = new ViewHolder(v); return vh; } // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(ViewHolder holder, int position) { try { JSONObject object = mDataset.getJSONObject(position); if ( object.has("image") ){ if ( !hopp.getString("image").equals("") ){ try { byte[] decodedString = Base64.decode(StringEscapeUtils.unescapeJson(object.getString("image")), Base64.DEFAULT); holder.image.setImageBitmap(BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length)); holder.image.setVisibility(View.VISIBLE); } catch ( Exception e ){ e.printStackTrace(); } } } if ( object.has("text") ){ holder.text.setText(object.getString("text")); } } catch ( Exception e ){ e.printStackTrace(); } } // Return the size of your dataset (invoked by the layout manager) @Override public int getItemCount() { return mDataset.length(); } } 

和我的RecyclerView中的视图我的XML:

 <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" android:id="@+id/card_view" android:layout_gravity="center" android:layout_width="match_parent" android:layout_height="wrap_content" card_view:cardCornerRadius="4dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" android:padding="@dimen/activity_horizontal_margin"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:id="@+id/my_hopp_people_reached"/> <ImageView android:id="@+id/my_hopp_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" android:adjustViewBounds="true"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:id="@+id/my_hopp_text"/> <ImageButton android:id="@+id/my_hopp_delete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@null" android:src="@drawable/ic_action_discard" /> </LinearLayout> </android.support.v7.widget.CardView> 

所以现在这是我的问题:一切工作正常。 文本和图片在我的RecyclerView中正确显示。 但是,如果我通过向下滚动并再次向上滚动到达列表的末尾,图片将显示在不应包含图片的项目中。 目前我有九个项目。 最后两个包含一张照片。 所以我向下滚动,只看到前七项中的文字。 到达列表的末尾显示了两张照片。 但是,如果我再次向上滚动这些图片出现在其他项目。

向上和向下滚动总是以相同的顺序改变图片:

  • 第一个向上滚动(在下降一次后)在第二个项目中创build一个图片。
  • 向下滚动在第七项创build一个图片。
  • 向上滚动在第一个项目中创build一个图片。
  • 向下滚动不会产生任何结果。
  • 向上滚动在第三个项目中创build一个图片,第二个项目中的图片被另一个图片覆盖。
  • 向下滚动会在第6个项目中创build一个图片,并让第7个项目中的图片消失。
  • 等等 …

我已经发现,每次在屏幕上再次出现一个项目时,都会调用onBindViewHolder(ViewHolder holder,int position)方法。 我检查了我的mDataset中的位置和给定的信息。 在这里一切正常:位置是正确的,也是给定的信息。 所有项目中的文本不会更改,并始终显示正确。 有没有人有这样的问题,并知道一些解决scheme? 任何forms的帮助,高度赞赏。 🙂

Solutions Collecting From Web of "在RecyclerView中的图像奇怪的行为"

我想你需要在onBindViewHolder(ViewHolder holder, int position)方法中的if ( object.has("image") )语句中添加一个else子句,将图像设置为null:

 holder.image.setImageDrawable(null); 

我认为onBindViewHolder的文档描述了为什么这是必要的:

由RecyclerView调用以在指定的位置显示数据。 此方法应更新itemView的内容以反映给定位置处的项目。

您应该始终假定传入的ViewHolder需要完全更新:)

不知道人们是否仍然有这个问题,但如果声明不适合我。 什么为我工作是这样的:

在适配器中使用此方法的覆盖

 @Override public int getItemViewType(int position) { return position; } 

然后在onBindViewHolder中调用基于这个图像本身的if语句,如下所示:

 int pos = getItemViewType(position); if(theList.get(pos).getPicture() == null) { holder.picture.setVisibility(View.GONE); } else { Picasso.with(context).load(R.drawable.robot).into(holder.picture); } 

我正在使用毕加索,但它应该与任何其他情况。 希望这可以帮助别人!

基于@ Lion789的回答。 我正在使用Google的Volley进行图像和json加载。

这使我们能够在稍后将被回收的视图上检索位置。

 @Override public int getItemViewType(int position) { return position; } 

提出请求时,请将该位置设置为TAG

 @Override public void onBindViewHolder(final ItemHolder holder, int position) { ImageRequest thumbnailRequest = new ImageRequest(url, new Response.Listener<Bitmap>() { @Override public void onResponse(Bitmap response) { holder.itemThumbnail.setImageBitmap(response); } },[...]); thumbnailRequest.setTag(String.valueOf(position)); //setting the TAG mQueue.addToRequestQueue(thumbnailRequest); } 

当视图被回收时,我们将取消请求,并删除当前的图像

 @Override public void onViewRecycled(ItemHolder holder) { super.onViewRecycled(holder); mQueue.cancelAll(String.valueOf(holder.getItemViewType())); holder.itemThumbnail.setImageDrawable(null); } 

排除取消请求的排球不会被交付。