Android RecyclerView适配器使用错误的值

Android RecyclerView适配器使用错误的值,android,android-recyclerview,Android,Android Recyclerview,我有一个RecyclerView,它显示了两种视图,一种表示用户发布,另一种表示事件发布。两者都有共同的元素,例如显示时间戳的TextView。所以我创建了一个PublicationViewHolder,它将这个TextView时间戳放入一个变量并加载它。我的问题是适配器最初加载正确的值,但当我向下滚动并再次向上滚动时,位置中的值会被其他位置的值更改。代码如下: public class PublicationViewHolder extends RecyclerView.ViewHolder

我有一个
RecyclerView
,它显示了两种
视图,一种表示用户发布,另一种表示事件发布。两者都有共同的元素,例如显示时间戳的
TextView
。所以我创建了一个
PublicationViewHolder
,它将这个
TextView
时间戳放入一个变量并加载它。我的问题是适配器最初加载正确的值,但当我向下滚动并再次向上滚动时,位置中的值会被其他位置的值更改。代码如下:

public class PublicationViewHolder extends RecyclerView.ViewHolder {

    private TextView vTimeStamp;

    public PublicationViewHolder(View itemView) {
        super(itemView);
        this.vTimeStamp = (TextView) itemView.findViewById(R.id.txt_view_publication_timestamp);
    }

    public void load(Publication publication, int i) {
        load(publication);
        try {
            if (Publication.TYPE_USER_PUBLICATION == publication.getType()) {
                load((UserPublication) publication);
            } else if (Publication.TYPE_EVENT_PUBLICATION == publication.getType()) {
                load((EventPublication) publication);
            }
        } catch (ClassCastException e) {
            throw new RuntimeException("Publication type cast fail. See PublicationViewHolder.");
        }
    }

    public void load(Publication publication) {
        vTimeStamp.setText(DateFormatter.getTimeAgo(publication.getTimeStamp()));
    }

    public void load( UserPublication publication) {
        //This method is override by UserPublicationViewHolder
    };

    public void load( EventPublication publication) {
        //This method is override by EventPublicationViewHolder
    };

}
现在,我将使我的
UserPublicationViewHolder
仅适用于用户出版物

public class UserPublicationViewHolder extends PublicationViewHolder {
    private  ImageView vImageView, vLikeButton, vDislikeButton, vFavButton, vEditPost, vDeletePost;
    private  TextView vText, vUsername, vLikeCount, vDislikeCount, vFavCount;
    private  PostImagesLayout vImagesContainer;
    private TagCloudLocationFriends tagsView;

    public UserPublicationViewHolder(View itemView) {
        super(itemView);
        vImageView = (ImageView) itemView.findViewById(R.id.img_view_publication_user);
        vText = (TextView) itemView.findViewById(R.id.txt_view_publication_text);

        vLikeCount = (TextView) itemView.findViewById(R.id.txt_view_like_count);
        vFavCount = (TextView) itemView.findViewById(R.id.txt_view_fav_count);
        vDislikeCount = (TextView) itemView.findViewById(R.id.txt_view_dislike_count);

        vUsername = (TextView) itemView.findViewById(R.id.txt_view_publication_user_name);
        vLikeButton = (ImageView) itemView.findViewById(R.id.img_view_like);
        vDislikeButton  = (ImageView) itemView.findViewById(R.id.img_view_dislike);
        vFavButton  = (ImageView) itemView.findViewById(R.id.img_view_fav);
        vImagesContainer = (PostImagesLayout) itemView.findViewById(R.id.container_post_images);

        tagsView = (TagCloudLocationFriends) itemView.findViewById(R.id.location_friends_tag);

        // edit - remove icons
        vDeletePost = (ImageView) itemView.findViewById(R.id.img_view_delete_post);
        vEditPost = (ImageView) itemView.findViewById(R.id.img_view_edit_post);
    }


    @Override
    public void load(UserPublication publication) {
        //Load the UserPublicationViewHolder specific views.
    }
}
现在,除了活动出版物,我也将这样做

public class EventPublicationViewHolder extends PublicationViewHolder {

    private TextView vTextViewTitle;
    private TextView vTextViewText;

    public EventPublicationViewHolder(View itemView) {
        super(itemView);
        vTextViewTitle = (TextView) itemView.findViewById(R.id.txt_view_publication_event_title);
        vTextViewText = (TextView) itemView.findViewById(R.id.txt_view_publication_event_text);
    }

    @Override
    public void load(EventPublication publication) {
        //Load the EventPublicationViewHolder specifics views
    }
}
现在,这是我的RecyclerView适配器:

public class PublicationAdapter extends RecyclerView.Adapter<PublicationViewHolder> {

    public static final int USER_PUBLICATION_TYPE = 1;
    public static final int EVENT_PUBLICATION_TYPE = 2;
    private List<Publication> publications = new ArrayList<Publication>();

    public List<Publication> getPublications() {
        return publications;
    }

    public void setPublications(List<Publication> publications) {
        this.publications = publications;
    }

    @Override
    public int getItemViewType(int position) {
        if (publications.get(position) instanceof UserPublication) {
            return USER_PUBLICATION_TYPE;
        }
        if (publications.get(position) instanceof EventPublication) {
            return EVENT_PUBLICATION_TYPE;
        }
        throw new RuntimeException("Unknown view type in PublicationAdapter");
    }

    @Override
    public PublicationViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) {
        View v;
        switch (type) {
            case USER_PUBLICATION_TYPE:
                v = LayoutInflater.from(getActivity()).inflate(R.layout.view_holder_user_publication, viewGroup, false);
                return new UserPublicationViewHolder(v);
            case EVENT_PUBLICATION_TYPE:
                v = LayoutInflater.from(getActivity()).inflate(R.layout.view_holder_event_publication, viewGroup, false);
                return new EventPublicationViewHolder(v);
        }
        return null;
    }

    @Override
    public void onBindViewHolder(PublicationViewHolder aPublicationHolder, int i) {
        aPublicationHolder.load(publications.get(i), i);
    }

    @Override
    public long getItemId(int position) {
        //Here I tried returning only position or 0 without luck.
        //The id is unique BTW
        return publications.get(position).getId();
    }

    @Override
    public int getItemCount() {
        return publications.size();
    }

}
这是EventPublicationViewHolder的加载方法:

@Override
public void load(EventPublication publication) {
    vTimeStamp.setVisibility(View.GONE);
    itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //GoTo.eventDetail(getActivity(), publication);
        }
    });
    vTextViewTitle.setText(publication.getTitle());
    vTextViewText.setText(publication.getText());
}
我评论了一些代码,只是因为我正在测试,但正如您所看到的,我只做setTexts和assing一些图像

这就是我在片段的onViewCreated方法中设置适配器、LinearLayoutManager等的方法

vRecyclerView = (FixedRecyclerView) view.findViewById(R.id.recycler_view_publications);
        vSwipeRefresh = (SwipeRefreshLayout) view.findViewById(R.id.swipe_container);
        mFeedCallback.onScrollReady(vRecyclerView);
        mLayoutManager = buildLayoutManager();
        vRecyclerView.setLayoutManager(mLayoutManager);
        vRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
        mAdapter = new PublicationAdapter();
        vSwipeRefresh.setOnRefreshListener(this);
        vSwipeRefresh.setColorSchemeResources(R.color._SWIPER_COLOR_1, R.color._SWIPER_COLOR_2,
                R.color._SWIPER_COLOR_3, R.color._SWIPER_COLOR_4);
        vRecyclerView.setAdapter(mAdapter);
顺便说一句,适配器与数据集一起加载在我拥有的一个名为onHttpClientReady的自定义方法中,但这似乎不是问题所在

以下是一些屏幕截图:

当我第一次进入应用程序时,列表的顶部:

然后当我回来的时候:

顺便说一句,如果有人多次单击“喜欢”、“不喜欢”和“喜爱”按钮,则会显示一个数字值,如果这些值是错误的,则这些值也会被放错位置

更新: 现在我知道那不是因为嵌套的片段。我更改代码的方式是,现在,每个选项卡片段都位于活动中的ViewPager中的PageStateAdapter中。但问题仍然存在

更新:
我发现getItemId方法从未被执行,IDK为什么还没有执行。

绑定代码中的一个大变量是日期格式:
DateFormatter.getTimeAgo(publication.getTimeStamp())

如果不直接查看该类,很难确定,但是看起来,如果时间戳是不可变的,但格式化程序基于当前时间,那么这将与视图恢复时文本的更改一致

我认为一个更大的问题是代码的可读性,这使得很难直观地发现问题。这里的继承模式和重载使得很难对代码进行推理,并决定采用哪条路径,以及它是否做了正确的事情。下面是一些napkin代码(尚未构建或运行),使用了更为组合的方法,这可能会使组织更清晰,更易于调试问题:

通用视图持有者代码的新助手类,替换了
PublicationViewHolder

public class PublicationViewHolderHelper {
    private final TextView vTimeStamp;

    public PublicationViewHolder(View itemView) {
        super(itemView);
        this.vTimeStamp = (TextView) itemView.findViewById(R.id.txt_view_publication_timestamp);
    }

    /** Binds view data common to publication types. */
    public void load(Publication publication) {
        vTimeStamp.setText(DateFormatter.getTimeAgo(publication.getTimeStamp()));
    }
}
public class EventPublicationViewHolder extends ViewHolder {
    private final PublicationViewHolderHelper helper;

    // View fields...

    public EventPublicationViewHolder(View itemView) {
         super(itemView);
         helper = new PublicationViewHolderHelper(itemView);
         // Populated view fields...
    }

    @Override
    public void load(EventPublication publication) {
        helper.load(publication);
        //Load the EventPublicationViewHolder specifics views
    }
}
public class PublicationAdapter extends RecyclerView.Adapter<ViewHolder> {
    ...
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int position) {
        final Publication publication = publications.get(position);
        final int viewType = getItemViewType(position);
        switch (viewType) {
            case USER_PUBLICATION_TYPE:
                ((UserPublicationViewHolder) viewHolder).load((UserPublication) publication);
                break;
            case EVENT_PUBLICATION_TYPE:
                ((EventPublicationViewHolder) viewHolder).load((EventPublication) publication);
                break;
            default:
                // Blow up in whatever way you choose.
        }
    }
    ...
}
EventPublicationViewHolder
作为示例(对
UserPublicationViewHolder
执行相同的操作):

请注意,现在适配器中没有基类,也不需要进行类型检查,因此代码要少得多

现在适配器保持不变,除了泛型类型和onBindViewHolder

public class PublicationViewHolderHelper {
    private final TextView vTimeStamp;

    public PublicationViewHolder(View itemView) {
        super(itemView);
        this.vTimeStamp = (TextView) itemView.findViewById(R.id.txt_view_publication_timestamp);
    }

    /** Binds view data common to publication types. */
    public void load(Publication publication) {
        vTimeStamp.setText(DateFormatter.getTimeAgo(publication.getTimeStamp()));
    }
}
public class EventPublicationViewHolder extends ViewHolder {
    private final PublicationViewHolderHelper helper;

    // View fields...

    public EventPublicationViewHolder(View itemView) {
         super(itemView);
         helper = new PublicationViewHolderHelper(itemView);
         // Populated view fields...
    }

    @Override
    public void load(EventPublication publication) {
        helper.load(publication);
        //Load the EventPublicationViewHolder specifics views
    }
}
public class PublicationAdapter extends RecyclerView.Adapter<ViewHolder> {
    ...
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int position) {
        final Publication publication = publications.get(position);
        final int viewType = getItemViewType(position);
        switch (viewType) {
            case USER_PUBLICATION_TYPE:
                ((UserPublicationViewHolder) viewHolder).load((UserPublication) publication);
                break;
            case EVENT_PUBLICATION_TYPE:
                ((EventPublicationViewHolder) viewHolder).load((EventPublication) publication);
                break;
            default:
                // Blow up in whatever way you choose.
        }
    }
    ...
}
公共类PublicationAdapter扩展了RecyclerView.Adapter{ ... @凌驾 公共无效onBindViewHolder(ViewHolder ViewHolder,int位置){ 最终出版物=publications.get(位置); 最终int viewType=getItemViewType(位置); 开关(视图类型){ 案例用户\出版物\类型: ((UserPublicationViewHolder)viewHolder)。加载((UserPublication)出版物); 打破 案例事件发布类型: ((EventPublicationViewHolder)viewHolder)。加载((EventPublication)出版物); 打破 违约: //用你选择的任何方式爆炸。 } } ... }
请注意,它与您的
onCreateViewHolder
保持着非常相似的模式,因此不仅总体代码更少,而且内部一致性更高。这当然不是唯一的方法,只是基于您的特定用例的建议。

我建议查看您的类层次结构和用法。一般来说,如果您在基类中执行
type==type
类型的操作,那么您就违背了抽象和继承的目的。像这样的东西对你有用:

public abstract class PublicationViewHolder extends RecyclerView.ViewHolder {
    private TextView mTimeStamp;

    public PublicationViewHolder(View itemView) {
        mTimeStamp = (TextView)itemView.findViewById(R.id. txt_view_publication_timestamp);
    }

    public void bindViews(Publication publication) {
        mTimeStamp.setText(DateFormatter.getTimeAgo(publication.getTimeStamp()));
    }
}
现在,您的“事件”或“用户发布”只是从这个类派生出来,并实现构造函数和
bindwiews()
方法确保在这两种情况下都调用超类。另外,确保在
bindViews()
方法中为特定出版物设置布局中的每个视图

在适配器中,您只需根据数据集中该位置的发布类型创建正确的保持架:

public class PublicationAdapter extends RecyclerView.Adapter {
    private ArrayList<Publication> mPubs;

    //  Your other code here, like
    //  swapPublications(), getItemCount(), etc.
    ...

    public int getItemViewType(int position) {
        return mPubs.get(position).getType();
    }

    public PublicationViewHolder createViewHolder(ViewGroup parent, int type) {
        PublicationViewHolder ret;
        View root;
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());

        if (type == USER_PUBLICATION_TYPE) {
            root =
                inflater.inflate(R.layout.view_holder_user_publication,
                    parent,
                    false);

            ret = new UserPubHolder(root);
        } else {
            root =
                inflater.inflate(R.layout.view_holder_event_publication,
                    parent,
                    false);

            ret = new EventPubHolder(root);
        }

        return ret;
    }

    public bindViewHolder(PublicationViewHolder holder, int position) {
        holder.bindViews(mPubs.get(position));
    }
}
公共类PublicationAdapter扩展了RecyclerView.Adapter{ 私人ArrayList mPubs; //你的另一个代码,比如 //swapppublications()、getItemCount()等。 ... public int getItemViewType(int位置){ 返回mPubs.get(position.getType(); } 公共出版物ViewHolder createViewHolder(视图组父级,int类型){ 出版物视图持有者ret; 视图根; LayoutInflater充气器=LayoutInflater.from(parent.getContext()); if(type==用户\发布\类型){ 根= 充气机。充气(右布局。视图\支架\用户\出版物, 父母亲 假); ret=新用户pubholder(root); }否则{ 根= 充气机。充气(右布局。视图\支架\事件\出版物, 父母亲 假); ret=新的EventPubHolder(根); } 返回ret; } 公众阅览员