带有附件的邮件的RecyclerView无法正确显示附件

时间:2015-07-11 21:57:47

标签: android android-adapter android-recyclerview android-viewholder

我有MessageAdapter扩展RecyclerView.Adapter邮件。它应该看起来就像它正常工作时一样。您单击卡,它会展开以显示图像。这只会发生在有图像的消息上:

enter image description here enter image description here

然而,有时我向下滚动并向上滚动,图像就像这样消失:

enter image description here

有时我会在RecyclerView上上下滚动,而不应该有附件的邮件会有一个:

enter image description here enter image description here

在我的MessageAdapter我有两个ViewType,一个用于标题消息,另一个用于评论消息。

这就是我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,而不是试图处理offset的{​​{1}}值。

更新

我可以通过RecyclerView中的以下内容使用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);
}

然后我只是在start:

初始化我的适配器
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);
                }

现在它工作正常,虽然这是一个非常hacky的解决方案,我打算使用doubleA的答案,并使这个代码在本周末更加优化。一个仍然存在的问题是,有些项目会itemSelectableBackgroundclickable而其他项目则不会,而根据我的理解,removeClickListener()应该初始化一个空的View.OnClickListener,从而制作项目因此可以点击itemSelectableBackground,但事实并非如此? Log输出表示我正在初始化侦听器和图像。

1 个答案:

答案 0 :(得分:1)

这是回收者观点的一个常见问题,最近对我来说实际上是一个面试问题。当您使用回收站视图和查看持有者时,它会执行它所说的...回收视图。因此,如果您开始向下滚动并且一个视图附加了一个图像,并且您显示该图像,然后在该视图被回收时稍微向下,并且您放入的新数据没有与之关联的图像,则您的代码不会明确告诉父视图在其中隐藏图像视图。因此,您的图像会显示在已回收的视图上,因为它已经存在并且已经回收。

这是我的建议

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

另外,我建议您将视图绑定代码放入View持有者中,如果必须,请使用具有相同布局的2个不同视图持有者。它将缩短您对onBindViewHolder的调用,并将不同的视图绑定代码链接到与其关联的视图持有者。以下是我的一个回收器视图适配器的示例。

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,他们正在做不同的事情。 @Bind和Butterknife.bind是一个名为Butterknife的视图绑定库的一部分,由相同的好人为你提供毕加索。这种用法&#34; bind&#34;等同于您的findViewById()来电。抽象类&#39; bind只是将数据绑定到适配器中的视图的方法的通用名称。我已经将bind重命名为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;
}
}

这是Airtime布局

<?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.