我有一个MessageAdapter
扩展RecyclerView.Adapter
的消息。这是它应该看起来就像它正常工作时一样。您点击该卡片并展开以显示图像。这只会发生在有图像信息:RecyclerView消息的附件没有正确显示附件
不过有时我向下滚动,滚动备份和图像只是消失,像这样:
有时我在RecyclerView
上上下滚动,并且不应该有附件的消息有一个:
在我的MessageAdapter
我有两个ViewType
s,一个用于标题消息,另一个用于注释消息。
这是我的MessageAdapter
样子:
public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_MESSAGE_HEADER = 0;
private static final int TYPE_MESSAGE_COMMENT = 1;
private Context mContext;
private Message mOriginalMessage;
private List<Message> mMessages;
public MessageAdapter(Context context) {
this.mContext = context;
}
public void setOriginalMessage(Message originalMessage) {
this.mOriginalMessage = originalMessage;
}
public void setMessages(List<Message> messages) {
this.mMessages = new ArrayList<>(messages);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_MESSAGE_HEADER) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.message_header,
parent, false);
return new MessageViewHolder(v, viewType);
} else {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.message_comment,
parent, false);
return new MessageViewHolder(v, viewType);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final MessageViewHolder messageViewHolder = (MessageViewHolder) holder;
int viewType = holder.getItemViewType();
switch (viewType) {
case TYPE_MESSAGE_HEADER:
if (messageViewHolder.mIsViewExpanded && mOriginalMessage.getAttachment() != null)
animateHeader(messageViewHolder);
// Other initialization stuff
// Set the image
if (mOriginalMessage.getAttachment() != null) {
messageViewHolder.mHeaderImage.setVisibility(View.INVISIBLE);
messageViewHolder.mHeaderShowTextView.setVisibility(View.VISIBLE);
messageViewHolder.mHeaderShowTextView.setText("Show Attachment");
String attachmentUrl = mOriginalMessage.getAttachment().getImageUrl();
if (messageViewHolder.mIsViewExpanded) {
Picasso.with(mContext)
.load(attachmentUrl)
.into(messageViewHolder.mHeaderImage);
}
messageViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
animateHeader(messageViewHolder);
}
});
}
break;
case TYPE_MESSAGE_COMMENT:
Message message = mMessage.get(position - 1);
if (messageViewHolder.mIsViewExpanded && message.getAttachment() != null)
animateComment(messageViewHolder);
// Other initialization stuff
// Show attachment if there is an attachment
if (message.getAttachment() != null) {
messageViewHolder.mMessageImage.setVisibility(View.INVISIBLE);
messageViewHolder.mMessageShowTextView.setVisibility(View.VISIBLE);
messageViewHolder.mMessageShowTextView.setText("Show Attachment");
String attachmentUrl = message.getAttachment().getImageUrl();
if (messageViewHolder.mIsViewExpanded) {
Picasso.with(mContext)
.load(attachmentUrl)
.into(messageViewHolder.mMessageImage);
}
messageViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
animateComment(messageViewHolder);
}
});
}
break;
default:
break;
}
}
@Override
public int getItemViewType(int position) {
if (isPositionHeader(position)) {
return TYPE_MESSAGE_HEADER;
}
return TYPE_MESSAGE_COMMENT;
}
private boolean isPositionHeader(int position) {
return position == 0;
}
// GUESSING SOMETHING WRONG HERE?
@Override
public int getItemCount() {
if (mOriginalMessage != null && mMessages != null) {
if (!mMessages.isEmpty())
return mMessages.size() + 1;
else
return 1;
} else if (mMessages != null) {
if (!mMessages.isEmpty())
return mMessages.size();
}
return 0;
}
private void animateHeader(final MessageViewHolder messageViewHolder) {
if (messageViewHolder.mOriginalHeight == 0)
messageViewHolder.mOriginalHeight = messageViewHolder.itemView.getHeight();
ValueAnimator valueAnimator;
if (!messageViewHolder.mIsViewExpanded) {
messageViewHolder.mHeaderImage.setVisibility(View.VISIBLE);
messageViewHolder.mHeaderImage.setEnabled(true);
messageViewHolder.mIsViewExpanded = true;
valueAnimator = ValueAnimator
.ofInt(messageViewHolder.mOriginalHeight, commentViewHolder.mOriginalHeight
+ (int) (messageViewHolder.mOriginalHeight * 0.8) + 10);
messageViewHolder.mHeaderShowTextView.setText("Hide Attachment");
} else {
messageViewHolder.mIsViewExpanded = false;
valueAnimator = ValueAnimator.ofInt(messageViewHolder.mOriginalHeight + (int) (messageViewHolder.mOriginalHeight * 0.8)
+ 10, messageViewHolder.mOriginalHeight);
Animation a = new AlphaAnimation(1.00f, 0.00f);
a.setDuration(200);
a.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
messageViewHolder.mHeaderShowTextView.setText("Show Attachment");
}
@Override
public void onAnimationEnd(Animation animation) {
messageViewHolder.mAttachmentImage.setVisibility(View.INVISIBLE);
messageViewHolder.mHeaderImage.setEnabled(false);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
messageViewHolder.mHeaderImage.startAnimation(a);
}
valueAnimator.setDuration(400);
valueAnimator.setInterpolator(new BakedBezierInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
messageViewHolder.itemView.getLayoutParams().height = (int) animation.getAnimatedValue();
messageViewHolder.itemView.requestLayout();
}
});
valueAnimator.start();
}
private void animateComment(final MessageViewHolder messageViewHolder) {
if (messageViewHolder.mOriginalHeight == 0)
messageViewHolder.mOriginalHeight = messageViewHolder.itemView.getHeight();
ValueAnimator valueAnimator;
if (!messageViewHolder.mIsViewExpanded) {
messageViewHolder.mMessageImage.setVisibility(View.VISIBLE);
messageViewHolder.mMessageImage.setEnabled(true);
messageViewHolder.mIsViewExpanded = true;
valueAnimator = ValueAnimator
.ofInt(messageViewHolder.mOriginalHeight, messageViewHolder.mOriginalHeight
+ (int) (messageViewHolder.mOriginalHeight * 0.8) + 10);
messageViewHolder.mMessageShowTextView.setText("Hide Attachment");
} else {
messageViewHolder.mIsViewExpanded = false;
valueAnimator = ValueAnimator
.ofInt(messageViewHolder.mOriginalHeight + (int) (messageViewHolder.mOriginalHeight * 0.8)
+ 10, messageViewHolder.mOriginalHeight);
Animation a = new AlphaAnimation(1.00f, 0.00f);
a.setDuration(200);
a.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
messageViewHolder.mMessageShowTextView.setText("Show Attachment");
}
@Override
public void onAnimationEnd(Animation animation) {
messageViewHolder.mMessageImage.setVisibility(View.INVISIBLE);
messageViewHolder.mMessageImage.setEnabled(false);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
messageViewHolder.mMessageImage.startAnimation(a);
}
valueAnimator.setDuration(300);
valueAnimator.setInterpolator(new BakedBezierInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
messageViewHolder.itemView.getLayoutParams().height = (int) animation.getAnimatedValue();
messageViewHolder.itemView.requestLayout();
}
});
valueAnimator.start();
}
public class MessageViewHolder extends RecyclerView.ViewHolder {
// Header
private ImageView mHeaderImage;
private TextView mHeaderShowTextView;
// Comment
private ImageView mMessageImage;
private TextView mMessageShowTextView;
// Variables for View
private int mOriginalHeight = 0;
private boolean mIsViewExpanded = false;
private int mHolderId;
public MessageViewHolder(View itemView, int viewType) {
super(itemView);
if (viewType == TYPE_MESSAGE_HEADER)
initHeaderViews(itemView);
else if (viewType == TYPE_MESSAGE_COMMENT)
initCommentViews(itemView);
}
private void initHeaderViews(View view) {
mHeaderImage = (ImageView) view.findViewById(R.id.header_image);
mHeaderShowTextView = (TextView) view.findViewById(R.id.header_show_textview);
mHeaderShowTextView.setVisibility(View.INVISIBLE);
if (!mIsViewExpanded) {
mHeaderImage.setEnabled(false);
mHeaderImage.setVisibility(View.GONE);
}
mHolderId = TYPE_MESSAGE_HEADER;
}
private void initCommentViews(View view) {
mMessageImage = (ImageView) view.findViewById(R.id.itemAttachmentImage);
mMessageShowTextView = (TextView) view.findViewById(R.id.showItemAttachment);
mMessageShowTextView.setVisibility(View.INVISIBLE);
if (!mIsViewExpanded) {
mMessageShowTextView.setText("Show Attachment");
mMessageImage.setEnabled(false);
mMessageImage.setVisibility(View.GONE);
}
mHolderId = TYPE_MESSAGE_COMMENT;
}
}
}
反正有没有做到这一点更好,更准确?具体而言,最大的问题是消除任何不一致,如果这个代码可以解耦。
我怎样才能得到正确的消息,只显示他们的附件正确?即使在向上或向下滚动时,如何将图像保存在卡中?当我添加新评论时,这也开始变得混乱,因为现在有一个N + 1
问题。
具体来说,我想知道是否有更好的方式来处理多个ViewHolders
而试图保持一个手柄上的RecyclerView
offset
值。
更新:
我能够通过减少一些复杂性在我的适配器在Fragment
下面我初始化我RecyclerView.Adapter
在:
public void setParentMessage(Message parentMessage) {
this.mParentMessage = parentMessage;
mAllMessages = new ArrayList<>();
mAllMessages.add(mParentMessage);
}
public void setMessages(List<Messages> messages) {
this.mMessages = messages;
mAllMessages.addAll(mMessages);
}
然后我初始化我的适配器开始:
mMessageAdapter.setMessages(mAllMessages);
然后,如果我必须添加一个新的Message
对象到我的清单,我可以简单地做到以下几点:
public void addComment(Message message) {
mMessageAdapter.addItem(mMessageAdapter.getItemCount(), message);
mRecyclerView.scrollToPosition(mMessageAdapter.size() - 1);
}
里面我MessageAdapter
我有以下添加一个新的消息评论:
public void addItem(int position, Message message) {
mMessages.add(position, message);
notifyItemInserted(position);
}
这意味着,我能够改变这个:
@Override
public int getItemCount() {
if (mOriginalMessage != null && mMessages != null) {
if (!mMessages.isEmpty())
return mMessages.size() + 1;
else
return 1;
} else if (mMessages != null) {
if (!mMessages.isEmpty())
return mMessages.size();
}
return 0;
}
要这样:
@Override
public int getItemCount() {
if (mMessages != null) {
if (!mMessages.isEmpty())
return mMessages.size();
}
return 0;
}
而且我onBindViewHolder
方法内我不需要跟踪过补偿了,所以这改变:
Message message = mMessage.get(position - 1);
要:
Message message = mMessage.get(position);
而且我去耦MessageViewHolder
成两个独立的ViewHolder
类:
public class MessageHeaderViewHolder extends RecyclerView.ViewHolder {
// Header
private ImageView mHeaderImage;
private TextView mHeaderShowTextView;
// Variables for View
private int mOriginalHeight = 0;
private boolean mIsViewExpanded = false;
private int mHolderId;
public MessageHeaderViewHolder(View itemView, int viewType) {
super(itemView);
initHeaderViews(itemView);
}
private void initHeaderViews(View view) {
mHeaderImage = (ImageView) view.findViewById(R.id.header_image);
mHeaderShowTextView = (TextView) view.findViewById(R.id.header_show_textview);
mHeaderShowTextView.setVisibility(View.INVISIBLE);
if (!mIsViewExpanded) {
mHeaderImage.setEnabled(false);
mHeaderImage.setVisibility(View.GONE);
}
mHolderId = TYPE_MESSAGE_HEADER;
}
private void initOnClickListener() {
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
animate(v);
}
});
}
private void removeClickListener() {
if (itemView.hasOnClickListeners())
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// THIS CODE DOESN'T WORK AS THOUGHT.
// Empty click listener to keep itemSelectableBackground.
}
});
}
private void animate(View v) {
// All animation code for header moved here
}
}
相同的东西ViewHolder
:
public class MessageCommentViewHolder extends RecyclerView.ViewHolder {
// Comment
private ImageView mMessageImage;
private TextView mMessageShowTextView;
// Variables for View
private int mOriginalHeight = 0;
private boolean mIsViewExpanded = false;
private int mHolderId;
public MessageCommentViewHolder(View itemView, int viewType) {
super(itemView);
initCommentViews(itemView);
}
private void initCommentViews(View view) {
mMessageImage = (ImageView) view.findViewById(R.id.itemAttachmentImage);
mMessageShowTextView = (TextView) view.findViewById(R.id.showItemAttachment);
mMessageShowTextView.setVisibility(View.INVISIBLE);
if (!mIsViewExpanded) {
mMessageShowTextView.setText("Show Attachment");
mMessageImage.setEnabled(false);
mMessageImage.setVisibility(View.GONE);
}
mHolderId = TYPE_MESSAGE_COMMENT;
}
private void initOnClickListener() {
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
animate(v);
}
});
}
private void removeClickListener() {
if (itemView.hasOnClickListeners())
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// THIS CODE DOESN'T WORK AS THOUGHT.
// Empty click listener to keep itemSelectableBackground.
}
});
}
private void animate(View v) {
// All animation code for header moved here
}
}
这意味着,我的onBindViewHolder
方法内我能为每种类型的项目如下(记住会有两种类型的现在ViewHolder
这么messageViewHolder
将改由headerViewHolder
或commentViewHolder
或类似的东西):
if (message.getAttachment() != null) {
messageViewHolder.mMessageImage.setVisibility(View.INVISIBLE);
messageViewHolder.mMessageShowTextView.setVisibility(View.VISIBLE);
messageViewHolder.mMessageShowTextView.setText("Show Attachment");
String attachmentUrl = message.getAttachment().getImageUrl();
Picasso.with(mContext)
.load(attachmentUrl)
.into(messageViewHolder.mMessageImage);
messageViewHolder.initOnClickListener();
} else {
messageViewHolder.removeClickListener();
messageViewHolder.mMessageImage.setVisibility(View.GONE);
messageViewholder.mMessageShowTextView.setVisibility(View.GONE);
}
它现在工作正常,虽然这是一个非常哈克解决方案,我打算使用doubleA的回答,使这个代码本周末更优化。一个仍然存在的问题是,一些项目将有itemSelectableBackground
和clickable
而其他人不是,从我的理解removeClickListener()
应该初始化一个空的View.OnClickListener
,从而使项目可点击因此显示itemSelectableBackground
,但事实并非如此? Log
输出是说我正在初始化侦听器和图像。
为什么会这样获得接近的选票?这是关于recyclerview中的动态数据的合理问题。请解释为什么你认为这应该被关闭,而不是仅仅投票。具体来说,我在这里看到一个设计实现问题 – AndyRoid