1

我有一个MessageAdapter扩展RecyclerView.Adapter的消息。这是它应该看起来就像它正常工作时一样。您点击该卡片并展开以显示图像。这只会发生在有图像信息:RecyclerView消息的附件没有正确显示附件

enter image description hereenter image description here

不过有时我向下滚动,滚动备份和图像只是消失,像这样:

enter image description here

有时我在RecyclerView上上下滚动,并且不应该有附件的消息有一个:

enter image description hereenter image description here

在我的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而试图保持一个手柄上的RecyclerViewoffset值。

更新:

我能够通过减少一些复杂性在我的适配器在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将改由headerViewHoldercommentViewHolder或类似的东西):

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的回答,使这个代码本周末更优化。一个仍然存在的问题是,一些项目将有itemSelectableBackgroundclickable而其他人不是,从我的理解removeClickListener()应该初始化一个空的View.OnClickListener,从而使项目可点击因此显示itemSelectableBackground,但事实并非如此? Log输出是说我正在初始化侦听器和图像。

+0

为什么会这样获得接近的选票?这是关于recyclerview中的动态数据的合理问题。请解释为什么你认为这应该被关闭,而不是仅仅投票。具体来说,我在这里看到一个设计实现问题 – AndyRoid

回答

1

这是回收商意见的一个常见问题,最近实际上是一个面试问题。当您使用回收商视图和查看持有人时,它会按照所述说...回收视图。因此,如果您开始向下滚动,并且一个视图附加了图像,并且显示该图像,然后在视图被回收并且您输入的新数据没有与其相关的图像时显示该图像,则代码不会显示明确告诉父视图在其中隐藏图像视图。所以你的图像显示在回收视图上,因为它已经在那里,只是回收。

这里是我的建议

if (message.getAttachment() != null) { 
//all your fun view binding stuff. 
} else { 
    messageViewHolder.mMessageImage.setVisibility(View.GONE); 
} 

而且我建议你把视图绑定代码到你的浏览持有者,如果你必须使用两种不同的观点持有人相同的布局。它会缩短您对BindViewHolder的调用,并将不同的视图绑定代码与与之关联的视图持有者链接起来。这是我的一个回收视图适配器的示例。

public class ProgramRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { 

/** 
This is an abstract class that all of my viewholders inherit from. 
This is a contract telling me that any subclasses that inherit from this 
base class are required to write their own `public void bind(int position, 
Program program);` method. 
*/ 
abstract class ProgramBaseViewHolder extends RecyclerView.ViewHolder { 

    public ProgramBaseViewHolder(View itemView) { 
     super(itemView); 
    } 

    public abstract void bindDataToView(int position, Program program); 
} 

我意识到关键字绑定在这个视图持有者中被多次使用,他们正在做不同的事情。 @Bind Butterknife.bind是一个名为Butterknife的视图绑定库的一部分,它由向您提供毕加索的同样好的人制作而成。 “绑定”的用法相当于您的findViewById()调用。抽象类的绑定只是将数据绑定到适配器中的视图的方法的通用名称。我已将bindDataToView重命名为更明确一些。

/** 
This is the Airtime view that holds airtimes. It is a view holder that 
inherits from my base view holder and implements its own version if bind. 
*/ 
class AirtimeViewHolder extends ProgramBaseViewHolder { 
    @Bind(R.id.program_airtimes) 
    TextView mProgramAirtimes; 

    static final int viewType = 0; 

    public AirtimeViewHolder(View itemView) { 
     super(itemView); 
     /**This call to butterknife can be replaced with an 
     itemView.findViewById(R.id.yourview) */ 
     ButterKnife.bind(this, itemView); 
    } 

    //This is where you set your text and hide or show your views. 
    @Override 
    public void bindDataToView(int position, Program program) { 
     List<Airtime> airtimes = program.getAirtimes(); 
     if (!airtimes.isEmpty()) { 
      mProgramAirtimes.setText(Utils.getFriendlyAirtimes(airtimes)); 
     } else { 
      mProgramAirtimes.setText(
        Utils.getFriendlyAirTime(program.getAirtime())); 
     } 
    } 
} 

/** 
This is the Description view that holds descriptions. It is a view holder 
that inherits from my base view holder and implements its own version if  
bind. 
*/ 
class DescriptionViewHolder extends ProgramBaseViewHolder { 
    @Bind(R.id.description_card_layout) 
    TextView mProgramDescription; 

    static final int viewType = 1; 

    public DescriptionViewHolder(View itemView) { 
     super(itemView); 
     ButterKnife.bind(this, itemView); 
    } 

    @Override 
    public void bindDataToView(int position, Program program) { 
     mProgramDescription.setText(Html.fromHtml(program.getFullDescription())); 
    } 
} 
//This is another type of view with another different type of layout. 
class HostsViewHolder extends ProgramBaseViewHolder { 
    @Bind(R.id.card_view_host_name) 
    TextView mProgramHostName; 

    static final int viewType = 2; 

    public HostsViewHolder(View itemView) { 
     super(itemView); 
     ButterKnife.bind(this, itemView); 
    } 

    @Override 
    public void bindDataToView(int position, Program program) { 
     mProgramHostName.setText(program.getHosts().get(position - 2).getDisplayName()); 
    } 
} 
//Again another type of view extending my base view. 
class CategoriesViewHolder extends ProgramBaseViewHolder { 
    @Bind(R.id.program_categories) 
    TextView mProgramCategories; 
    static final int viewType = 42; 

    public CategoriesViewHolder(View itemView) { 
     super(itemView); 
     ButterKnife.bind(this, itemView); 
    } 

    @Override 
    public void bindDataToView(int position, Program program) { 
     List<Category> categoryList = program.getCategories(); 
     StringBuilder stringBuilder = new StringBuilder(); 
     for (Category category : categoryList) { 
      stringBuilder.append(category.getTitle()) 
        .append(" "); 
     } 
     mProgramCategories.setText(stringBuilder.toString()); 
    } 
} 

//This is where the normal looking recycler view code comes in. 
private Context mContext; 
private LayoutInflater mInflater; 
private Program mProgramData; 
private int mNextProgramId; 

public ProgramRecyclerAdapter(Context context) { 
    mContext = context; 
    mInflater = LayoutInflater.from(mContext); 
} 

/**This method is where I am determining what view type each item in my list 
will be. I wanted a single airtimes view followed by a single description 
view and then X amount of hosts views and a single category view. I return 
position in the third else if because the position helps me determine which 
host name to display in the bindDataToViews call of the HostViewHolder.*/ 
@Override 
public int getItemViewType(int position) { 
    if (position == AirtimeViewHolder.viewType) { 
     return AirtimeViewHolder.viewType; 
    } else if (position == DescriptionViewHolder.viewType) { 
     return DescriptionViewHolder.viewType; 
    } else if (position > DescriptionViewHolder.viewType 
      && position <= DescriptionViewHolder.viewType + getHostsNum()) { 
     return position; 
    } else { 
     return CategoriesViewHolder.viewType; 
    } 
} 

//This method figures out how many hosts will be displayed 
private int getHostsNum() { 
    if (mProgramData != null) { 
     return mProgramData.getHosts().size(); 
    } 
    return 0; 
} 
// This method determines if I will show a category view or not. 
private int getCategoriesNum() { 
    if (mProgramData != null && mProgramData.getCategories().size() > 0) { 
     return 1; 
    } 
    return 0; 
} 

/**This returns haw many items will be in the list. 1 Airtime view, 1 
Description view, x amount of Host views and 0 or 1 category views */ 
@Override 
public int getItemCount() { 
    return 2 + getHostsNum() + getCategoriesNum(); 
} 

/** This returns the appropriate View holder for the requested view type that 
was set by getItemViewType(). I pass the inflated parent view and the data.  
*/ 
@Override 
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
    if (viewType == AirtimeViewHolder.viewType) { 
     return new AirtimeViewHolder(mInflater.inflate(R.layout.airtime_card_layout, parent, false)); 
    } else if (viewType == DescriptionViewHolder.viewType) { 
     return new DescriptionViewHolder(mInflater.inflate(R.layout.description_card_layout, parent, false)); 
    } else if (viewType > DescriptionViewHolder.viewType 
      && viewType <= DescriptionViewHolder.viewType + getHostsNum()) { 
     return new HostsViewHolder(mInflater.inflate(R.layout.hosts_card_layout, parent, false)); 
    } else 
     return new CategoriesViewHolder(mInflater.inflate(R.layout.categories_card_layout, parent, false)); 
} 

/*This method is what ties everything together. After I ensure that the data 
is not null I call bindDataToView on a ProgramBaseViewHolder. Depending on 
which type of subclass it is will determine which overridden bindData code to 
use. */ 
@Override 
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 
    ProgramBaseViewHolder baseViewHolder = (ProgramBaseViewHolder) holder; 
    if (mProgramData != null) { 
     baseViewHolder.bindDataToView(position, mProgramData); 
    } 
} 

//This is used to set the data for this program 
public void setProgramData(Program program) { 
    mProgramData = program; 
} 

public Program getProgramData() { 
    return mProgramData; 
} 

public boolean isEmpty() { 
    return mProgramData == null; 
} 
} 

这是通话时间布局

<?xml version="1.0" encoding="utf-8"?> 
<android.support.v7.widget.CardView 
xmlns:android="http://schemas.android.com/apk/res/android" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
android:layout_margin="@dimen/card_margin"> 
<LinearLayout 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:padding="16dp" 
    android:orientation="vertical"> 
    <TextView 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:text="@string/airtimes_label" 
     android:textSize="18dp" 
     android:textStyle="bold" 
     android:textAppearance="@style/TextAppearance.AppCompat.Body2" 
     android:layout_marginBottom="4dp"/> 
    <TextView 
     android:id="@+id/program_airtimes" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:textSize="18sp" 
     android:textAppearance="@style/TextAppearance.AppCompat.Title" /> 
</LinearLayout> 

这是我的主人布局。您会注意到我没有使用大部分视图,因为这是一个正在进行的应用程序。

<?xml version="1.0" encoding="utf-8"?> 
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:card_view="http://schemas.android.com/apk/res-auto" 
    android:id="@+id/host_card_view" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:layout_margin="@dimen/card_margin" 
    card_view:cardBackgroundColor="@color/white" 
    card_view:cardCornerRadius="2dp"> 
    <RelativeLayout 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:padding="8dp" /> 
     <ImageView 
      android:id="@+id/host_image" 
      android:layout_width="112dp" 
      android:layout_height="112dp" 
      android:layout_alignParentLeft="true" 
      android:visibility="gone" 
      android:layout_centerVertical="true" 
      android:layout_marginRight="8dp" /> 
     <LinearLayout 
      android:id="@+id/details" 
      android:layout_width="fill_parent" 
      android:layout_height="wrap_content" 
      android:layout_alignParentTop="true" 
      android:layout_toRightOf="@id/host_image" 
      android:orientation="vertical"> 
      <TextView 
       android:id="@+id/card_view_host_name" 
       android:layout_width="wrap_content" 
       android:layout_height="wrap_content" 
       android:textSize="18sp" 
       android:layout_margin="16dp" 
       android:textAppearance="@style/TextAppearance.AppCompat.Body2" 
       android:layout_gravity="left" /> 
      <TextView 
       android:id="@+id/card_view_hosts_programs" 
       android:layout_width="wrap_content" 
       android:layout_height="wrap_content" 
       android:visibility="gone" 
       android:textStyle="bold" 
       android:textSize="12sp" 
       android:layout_marginBottom="16dp" 
       android:layout_gravity="left"/> 
     </LinearLayout> 
    </RelativeLayout> 
</android.support.v7.widget.CardView> 

This is the view that is produced with some data. The Categories view is cut off but it is there.

+0

你可以分开RecyclerView.Adapter类中的每个方法并相应地描述每种方法吗?我认为这会帮助包括我在内的很多人。 – AndyRoid

+0

@AndyRoid我添加了评论,图片和一些更多的代码。我希望有所帮助。 – doubleA

+0

这是非常深思熟虑的doubleA,我几乎已经修复了我遇到的错误。一旦我理解了你的代码并实现了它,我会接受这个答案。 doubleA你有什么想法,为什么mHeaderShowTextView和mMessageShowTextView出现在不应该有附件开头的项目上?我试着将onClickListener代码转移到ViewHolder中,并且只在消息有附件时才初始化它,但它似乎会被初始化,而不管某些非常奇怪的项目。 – AndyRoid