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