我有一個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