我有MessageAdapter
扩展RecyclerView.Adapter
邮件。它应该看起来就像它正常工作时一样。您单击卡,它会展开以显示图像。这只会发生在有图像的消息上:
然而,有时我向下滚动并向上滚动,图像就像这样消失:
有时我会在RecyclerView
上上下滚动,而不应该有附件的邮件会有一个:
在我的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
将被更改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);
}
现在它工作正常,虽然这是一个非常hacky的解决方案,我打算使用doubleA的答案,并使这个代码在本周末更加优化。一个仍然存在的问题是,有些项目会itemSelectableBackground
而clickable
而其他项目则不会,而根据我的理解,removeClickListener()
应该初始化一个空的View.OnClickListener
,从而制作项目因此可以点击itemSelectableBackground
,但事实并非如此? Log
输出表示我正在初始化侦听器和图像。
答案 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>