RecyclerView 的行正在洗牌和改变图片:android

Posted

技术标签:

【中文标题】RecyclerView 的行正在洗牌和改变图片:android【英文标题】:RecyclerView's row is shuffling and change Images : android 【发布时间】:2016-02-08 08:27:52 【问题描述】: 已修复其他问题的问题变化。

我的问题是当 Recycler View 上下滚动时

快速滚动时行项目会改变它的位置并返回到它的原始位置。 行项目使其部分固定为背景

这是我如何在 Fragment 中设置 My Recycler 视图。

private void setRecyclerView() 
    recyclerView.setHasFixedSize(true);
    StaggeredGridLayoutManager layoutManager =
            new StaggeredGridLayoutManager(2, 1);
    recyclerView.setLayoutManager(layoutManager);
    adapter = new TwitterTweetAdapter(getActivity());

    // setting every thing false in item animator 
    recyclerView.setItemAnimator(new RecyclerView.ItemAnimator() 

        @Override
        public void runPendingAnimations() 

        

        @Override
        public boolean animateRemove(RecyclerView.ViewHolder viewHolder) 
            return false;
        

        @Override
        public boolean animateAdd(RecyclerView.ViewHolder viewHolder) 
            return false;
        

        @Override
        public boolean animateMove(RecyclerView.ViewHolder viewHolder, int i, int i2, int i3, int i4) 
            return false;
        

        @Override
        public boolean animateChange(RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder viewHolder2, int i, int i2, int i3, int i4) 
            return false;
        

        @Override
        public void endAnimation(RecyclerView.ViewHolder viewHolder) 

        

        @Override
        public void endAnimations() 

        

        @Override
        public boolean isRunning() 
            return false;
        
    );
    recyclerView.setAdapter(adapter);

这是我的适配器类

在我的适配器中,我设置了一个标题

//我的视图持有者

class Header extends RecyclerView.ViewHolder 

    @Bind(R.id.feed_head)
    CardView feedHeader;
    @Bind(R.id.feed_header_count)
    TextView userCount;
    @Bind(R.id.feed_header_text)
    TextView userText;

    public TwitterHeader(View itemView) 
        super(itemView);
        ButterKnife.bind(this, itemView);
    


public static class RowViewHolder extends RecyclerView.ViewHolder 

    @Bind(R.id.feed_row_view)
    CardView feedRow;
    @Bind(R.id.feed_image)
    ImageView feedImage;
    @Bind(R.id.video_play_icon)
    ImageButton playButton;
    @Bind(R.id.feed_description)
    TextView feedDescription;
    @Bind(R.id.feed_user_image)
    ImageView userImage;
    @Bind(R.id.feed_user_name)
    TextView userName;
    @Bind(R.id.feed_time)
    TextView feedDate;
    @Bind(R.id.feed_progress_bar)
    ProgressBar progressBar;

    public RowViewHolder(View itemView) 
        super(itemView);
        ButterKnife.bind(this, itemView);
    

// 变量声明

private static final int TYPE_HEADER = 0;
private static final int TYPE_FEED = 1;

Context context;
private Activity activity;
private List<MockData> dataLists;
public static List<MockData> dataListsupdated;

int userSearchCount;

// 构造函数

public FeedAdapter(Activity activity) 
    this.activity = activity;

//设置数据列表

public void setDataList(List<MockData> dataLists, int userSearchCount, float density) 
    this.dataLists = dataLists;
    this.density = density;
    this.userSearchCount = userSearchCount;

// onCreateViewHolder

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) 
    if (viewType == TYPE_HEADER) 
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.feed_header, viewGroup, false);
        return new Header(view);
     else 
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.feed_row, viewGroup, false);
        return new RowViewHolder(v);
    

// onBindViewHolder 我正在用数据绑定我的视图。

if (holder instanceof Header) 
        Header header = (Header) holder;
        if (userSearchCount == 20) 
            header.userCount.setText(R.string.twitter_default_count);
         else 
            header.userCount.setText(userSearchCount);
        
        header.userText.setText(R.string.user);
        header.feedHeader.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                Intent intent = new Intent(activity, TwitterSearchActivity.class);
                activity.startActivity(intent);
            
        );

     else 
        if (holder instanceof RowViewHolder) 
            final RowViewHolder rowViewHolder = (RowViewHolder) holder;
            final MockData responseList = dataLists.get(position);

            rowWidth = rowViewHolder.feedImage.getWidth();
            rowViewHolder.playButton.setVisibility(View.GONE);

            if (responseList.text.startsWith("RT")) 
                rowViewHolder.feedRow.setVisibility(View.GONE);
                FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
                params.setMargins(0, 0, 0, 0);
                rowViewHolder.feed.setLayoutParams(params);
             else 
                setRowImage(rowViewHolder, responseList);
                rowViewHolder.feedDescription.setText(responseList.text);

                rowViewHolder.userImage.setVisibility(View.VISIBLE);
                Glide.with(activity)
                        .load(responseList.user.profileImageUrlHttps)
                        .into(rowViewHolder.userImage);

                rowViewHolder.userName.setText(responseList.user.screenName);

                Date date = new Date(responseList.createdAt);
                DateFormat dateFormat = android.text.format.DateFormat.getDateFormat(activity.getApplicationContext());
                rowViewHolder.feedDate.setText(dateFormat.format(date));

                rowViewHolder.feedRow.setOnClickListener(new View.OnClickListener() 
                    @Override
                    public void onClick(View v) 
                        startActivity(position);
                    
                );
            
        
    


// 设置行图像。

private void setRowImage(RowViewHolder rowViewHolder, Tweet responseList) 
    if (responseList.entities.media != null) 
        MediaEntity mediaEntity = responseList.entities.media.get(0);
        if (mediaEntity.mediaUrlHttps != null) 
            int height = mediaEntity.sizes.medium.h;
            int width = mediaEntity.sizes.medium.w;

            int ratio = width / 143;
            int newHeight = (int) ((height / ratio) * density);

            Glide.with(activity)
                    .load(mediaEntity.mediaUrlHttps)
                    .override(width, newHeight)
                    .placeholder(R.color.colorAccent)
                    .into(rowViewHolder.feedImage);

         else 
            rowViewHolder.feedImage.setImageDrawable(null);
        
     else 
        rowViewHolder.feedImage.setImageDrawable(null);
    

//查看条件设置标题和行。

@Override
public int getItemViewType(int position) 
    if (isPositionHeader(position))
        return TYPE_HEADER;
    return TYPE_FEED;


private boolean isPositionHeader(int position) 
    return position == 0;


@Override
public int getItemCount() 
    return dataLists == null ? 0 : dataLists.size();

【问题讨论】:

而不是让行的可见性消失为什么不删除构造函数中包含“RT”的dataLists条目然后设置适配器 是的,我尝试在片段中检查该条件,然后将其传递给构造函数 UnsupportedOperationException 你能告诉我情况吗? 尝试recentData.addAll(recentData.data.tweets) inplaceof List recentData = recentData.data.tweets ***.com/questions/2965747/… 【参考方案1】:

我的应用程序中有一个非常相似的适配器,我在其中显示来自 Facebook 而不是 Twitter 的提要,首先,切换到 Glide 而不是 Picasso,因为它使用 RecyclerView 提供了更好的缓存和图像加载,请查看 THIS。因此,现在解决您的问题,在第一步中在您的适配器上调用 setHasStableIds(true),覆盖 getItemId 方法以从您的适配器返回一个合适的 long,因为您使用的是提要,您可以计算 postId 的哈希值或一些唯一标识提要中每个帖子的字段。我的提要也有一个可选的图像,这就是我有这两种方法的原因

public void setProfilePicture(String uri) 

        //As per the solution discussed here http://***.com/questions/32706246/recyclerview-adapter-and-glide-same-image-every-4-5-rows
        if (uri != null) 
            Glide.with(mContext)
                    .load(uri)
                    .asBitmap()
                    .transform(new CropCircleTransform(mContext))
                    .into(mProfilePicture);
         else 
            Glide.clear(mProfilePicture);
            mProfilePicture.setImageResource(R.drawable.com_facebook_profile_picture_blank_square);
        
    

    public void setPostPicture(String uri) 

        //As per the solution discussed here http://***.com/questions/32706246/recyclerview-adapter-and-glide-same-image-every-4-5-rows
        if (uri != null) 
            Glide.with(mContext)
                    .load(uri)
                    .asBitmap()
                    .transform(new CropTransformation(mContext, mPostImageWidth, mPostImageHeight))
                    .into(mPostPicture);
         else 
            Glide.clear(mPostPicture);
            mPostPicture.setImageDrawable(null);
        
    

然后你要做的就是从你的 onBindViewHolder 调用这些方法来设置图像。 Post 是我的对象,其中包含单个帖子的详细信息,例如姓名、用户 ID、人员图片的图像 url 和帖子图片的图像 url,这是可选的。如果您进一步遇到任何问题,请告诉我。

@Override
    public void onBindViewHolder(ItemHolder holder, int position) 
        Post post = mResults.get(position);
        holder.setUserName(post.getUserName());
        holder.setUpdatedTime(post.getUpdatedTime());
        holder.setMessage(post.getMessage(), mState, position);
        holder.setPostPicture(post.getPicture());
        holder.setProfilePicture(post.getUserPicture());
        // Check for an expanded view, collapse if you find one

    

【讨论】:

setHasStableIds(true) 已经完成。好吧,我尝试过使用 Glide 库,行仍在洗牌。 @abhishek 你是如何实现 getItemID 的? 我喜欢你的代码,我有一个回收器适配器,我想实现可扩展视图选项,在可扩展选项中我想放置一个水平滚动视图,看来你已经实现了某种形式的可扩展适配器,能否请您分享您的整个代码或至少告诉我如何实现? @lifeevader 我已经回答了一个问题,即 cardview 在回收站视图中扩展。 ***.com/questions/31911684/… .. 您可以在此处设置水平 recyclerView 的可见性。如果没有成功,请告诉我。 @Tunji_D,我没有实现 getItemID 因为我不需要它。【参考方案2】:

您遇到的问题是您没有实现getItemID,或者您没有正确实现它。

如果您将setHasStableIds 设置为true,并且您没有实现getItemId 来为列表中的每个项目返回一个唯一的long,则RecyclerView 重复项目。这是因为 getItemID 的默认实现为列表中的所有项目返回 -1,因此,当在视图回收后调用 onBindViewHolder 时,它将返回 ViewHolder 显示的最后一个内容的缓存视图。

实现这一点的一种简单方法是返回绑定到该 ViewHolder 的列表中对象的哈希码。

@Override
public long getItemId(int position)      
    Object listItem = listItems.get(position);
    return listItem.hashCode();  

显然,这意味着对于该特定对象,您必须重写它的 equals()hashCode() 方法,使其对每个对象都是唯一的。一个好主意是 String 显示它是从与对象名称连接的数据库中创建的时间。

【讨论】:

【参考方案3】:

如果您在HERE 上看到 RecyclerView 的源代码,请导航到包含这些行的第 3028 行

/**
 * Returns a unique key to be used while handling change animations.
 * It might be child's position or stable id depending on the adapter type.
 */
long getChangedHolderKey(ViewHolder holder) 
    return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;

并且在包含此的源文件的第 5279 行

/**
     * Return the stable ID for the item at <code>position</code>. If @link #hasStableIds()
     * would return false this method should return @link #NO_ID. The default implementation
     * of this method returns @link #NO_ID.
     *
     * @param position Adapter position to query
     * @return the stable ID of the item at position
     */
    public long getItemId(int position) 
        return NO_ID;
    

它清楚地表明重写 setHasStableIds 后需要重写 getItemId 方法。要让 RecyclerView 中的每个项目在您上下滚动时显示正确的项目,getItemId 无法返回位置,或者更糟的是被忽略。确保返回一个唯一的 long 值,用于标识数据集中的每一行。

这个独特的价值是什么?

    您的 Twitter 帖子发布时间的唯一时间戳(如果有的话)? 计算出的唯一长值,例如对您的数据集中的一个或多个项目进行散列,这将产生一个唯一值。

这对于动画和在正确位置显示正确的行都很重要。希望这有助于解决您的问题

【讨论】:

【参考方案4】:

在您的 recyclerview 适配器中覆盖这 2 个方法并更改 getItemViewType 的代码,如下所示。

 @Override
        public long getItemId(int position) 
            return position;
        

    @Override
    public int getItemViewType(int position) 
             if (isPositionHeader(position)) 
        return TYPE_HEADER;
            
             else 
               return position;
             
           

我也遇到了同样的问题。做这个。这肯定会解决您的问题。

【讨论】:

以上是关于RecyclerView 的行正在洗牌和改变图片:android的主要内容,如果未能解决你的问题,请参考以下文章

Recyclerview 和处理不同类型的行膨胀

Recyclerview 和处理不同类型的行膨胀

解决RecyclerView瀑布流效果结合Glide使用时图片变形的问题

Android 解决RecyclerView瀑布流效果结合Glide使用时图片变形的问题

ListView 中的图像不断洗牌

RecyclerView 中的多个视图