RecyclerView的行正在改组并更改图片:android

时间:2015-11-07 05:39:43

标签: android fragment adapter android-recyclerview android-viewholder

  • 其他问题已得到解决,问题发生了变化。

我的问题是当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);
}

这是我的适配器类

在我的适配器中,我正在设置One Header

//我的ViewHolders

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();
}

}

4 个答案:

答案 0 :(得分:3)

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

public void setProfilePicture(String uri) {

        //As per the solution discussed here http://stackoverflow.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://stackoverflow.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调用这些方法来设置Image。 Post是我的对象,其中包含单个帖子的详细信息,例如姓名,用户ID,人物图片的图片网址和可选的帖子图片的图片网址。如果您再遇到任何问题,请告诉我。

@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

    }

答案 1 :(得分:2)

如果您看到RecyclerView的源代码超过HERE,请导航到包含这些行的第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值。

这个独特的价值是什么?

  1. 如果你有Twitter帖子的唯一时间戳?
  2. 计算出的唯一长值,例如对数据集中的一个或多个项进行哈希处理,以产生唯一值。
  3. 这对于动画和在正确位置显示正确的行都很重要。希望有助于解决您的问题

答案 2 :(得分:1)

您遇到的问题是您没有实施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 :(得分:0)

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

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

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

我也有同样的问题。做这个。这肯定会解决你的问题。