如果图像在 4 个或更多的序列中,则在 RecyclerView 行中对图像进行分组
Grouping images in a RecyclerView row if they are in a sequence of 4 or more
我的应用程序中有一个类似于聊天的 WhatsApp,最近他们添加了一项新功能,如果它们以 4 张或更多图片的顺序发送并且它们之间没有消息,则可以将图像分组到相册中。如下图所示:
所以我实现了我的 RecyclerView 适配器,它单独显示所有内容,这意味着无论是消息、图像、音频等,它们中的每一个都将在我的适配器中单独的一行中。所以我想做 WhatsApp 所做的事情,如果连续发送的图像超过 4 张,我会将这组图像实施到相册中。我怎样才能做到这一点?
我已经在我的适配器中成功实现了 getItemViewType()
方法,并且工作正常。但是我现在不知道怎么办。
PS: Feed 是我的对象,可以是文本消息、图像和音频文件等。所以 mItems 是一个 Feed 列表。
这是我的适配器:
public class FeedAdapter extends BaseSkeletonAdapter<Feed> implements FeedHolder.FeedHolderListener{
private static final int HOLDER_COMMENT = 1;
private static final int HOLDER_IMAGE = 2;
private static final int HOLDER_FILE = 3;
private static final int HOLDER_AUDIO = 4;
private static final int HOLDER_MARKER = 5;
private static final int HOLDER_EMPTY = 6;
private final FeedItemListener mListener;
private final int mAvatarSize;
private final String mUserId;
private final int mPictureSize;
private final int mSkeletonColor;
public FeedAdapter(FeedItemListener listener, String userId, int avatarSize, int pictureSize, int skeletonColor) {
super(2);
mListener = listener;
mUserId = userId;
mAvatarSize = avatarSize;
mPictureSize = pictureSize;
mSkeletonColor = skeletonColor;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType){
case HOLDER_COMMENT:
case HOLDER_IMAGE:
System.out.println("It is an image!");
case HOLDER_FILE:
case HOLDER_MARKER:
case HOLDER_AUDIO:
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_feed, parent, false);
return new FeedHolder(view, this, mPictureSize);
case HOLDER_EMPTY:
default:
View empty = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_empty, parent, false);
return new EmptyPlaceholderViewHolder(empty, R.string.placeholder_feed_empty_title, R.string.placeholder_feed_empty_description, R.drawable.ic_feed_placeholder);
}
}
@Override
protected void onBind(RecyclerView.ViewHolder holder, int position) {
if(!(holder instanceof EmptyPlaceholderViewHolder)){
Feed feed = mItems.get(position);
//for (int i = 0; i < mItems.size(); i++) {
// System.out.println("Tamanho: " + mItems.size() + "\nDado: " + mItems.get(i).getText() + "\nDado 2: " + mItems.get(i).getUrl());
//}
if (holder instanceof FeedHolder) {
if (mUserId.equals(feed.getCreatedById())) {
((FeedHolder) holder).onBind(feed, mUserId, mAvatarSize);
}else {
((FeedHolder) holder).onBind(feed, mUserId, mAvatarSize);
}
}
}
}
@Override
protected void onBind(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {
if (payloads.isEmpty()) {
onBindViewHolder(holder, position);
}else {
if (holder instanceof FeedHolder) {
((FeedHolder) holder).onBind(mItems.get(position), payloads, mUserId, mAvatarSize);
}
}
}
@Override
protected void setHolderSkeleton(RecyclerView.ViewHolder holder) {
if (holder instanceof FeedHolder) {
((FeedHolder) holder).setHolderSkeleton(R.drawable.rounded_skeleton, mSkeletonColor);
}
}
@Override
protected void clearHolderSkeleton(RecyclerView.ViewHolder holder) {
if (holder instanceof FeedHolder) {
((FeedHolder) holder).clearHolderSkeleton();
}
}
@Override
public int getItemViewType(int position) {
if(mSkeletonMode){
return HOLDER_COMMENT;
} if (mItems != null && mItems.size() > 0 && position >= 0 && position < mItems.size()) {
Feed feed = mItems.get(position);
if (feed != null) {
String type = feed.getFeedType();
if (type != null) {
switch (type) {
case FEED_IMAGE:
return HOLDER_IMAGE;
case FEED_AUDIO:
return HOLDER_AUDIO;
case FEED_FILE:
return HOLDER_FILE;
case FEED_MARKER:
return HOLDER_MARKER;
case FEED_COMMENT:
default:
return HOLDER_COMMENT;
}
}
}
return HOLDER_COMMENT;
}else {
return HOLDER_EMPTY;
}
}
public List<Feed> getItems() {
return mItems;
}
public void swap(List<Feed> feedList) {
if (mItems == null) {
mItems = new ArrayList<>();
}
mItems.clear();
mItems.addAll(feedList);
}
@Override
public void toggleLike(final int pos){
if(mListener != null && pos >= 0 && pos < mItems.size()){
mListener.toggleLike(mItems.get(pos));
}
}
@Override
public void onLongClick(final int pos, final View v) {
if (mListener != null && pos >= 0 && pos < mItems.size()) {
mListener.onLongClick(mItems.get(pos), v);
}
}
@Override
public int onAudioActionClicked(final int pos, final int progress) {
if (mListener != null) {
return mListener.onAudioActionClicked(pos, mItems.get(pos), progress);
}else {
return 0;
}
}
@Override
public void onClick(int pos) {
if (mItems!=null && pos >= 0 && pos<mItems.size()) {
Feed feed = mItems.get(pos);
if (feed != null && mListener != null) {
mListener.onClick(feed);
}
}
}
public interface FeedItemListener {
void toggleLike(@NonNull Feed feed);
void onLongClick(@NonNull Feed feed, @NonNull View v);
void onClick(@NonNull Feed feed);
int onAudioActionClicked(int pos, @NonNull Feed feed, final int progress);
}
}
这是我的 FeedHolder class:
public class FeedHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
public static final String SEEK_BAR_PROGRESS_BUNDLE = "bundle_seek_bar_progress";
public static final String ACTION_ICON_BUNDLE = "bundle_icon";
public static final String RESET_AUDIO = "bundle_reset_audio";
private static final int MINE_BG_COLOR = R.color.feed_mine_bg;
private static final int MINE_TEXT_COLOR = R.color.feed_mine_text;
private static final int MINE_PLAY_ICON = R.drawable.ic_play_white_24dp;
private static final int MINE_FILE_ICON = R.drawable.ic_file_white_18dp;
private static final int THEIR_BG_COLOR = R.color.feed_others_bg;
private static final int THEIR_TEXT_COLOR = R.color.feed_others_text;
private static final int THEIR_PLAY_ICON = R.drawable.ic_play_black_24dp;
private static final int THEIR_FILE_ICON = R.drawable.ic_file_black_18dp;
private final SimpleDraweeView mAvatar;
private final TextView mCreator;
private final TextView mDate;
private final CardView mCardView;
private final TextView mText;
private final TextView mLike;
private final FeedHolderListener mListener;
private final SimpleDraweeView mPicture;
private final int mSize;
private final View mAudioLayout;
private final ImageButton mAudioAction;
private final SeekBar mAudioBar;
private final TextView mAudioLength;
public FeedHolder(@NonNull final View itemView, final FeedHolderListener listener, final int size) {
super(itemView);
mAvatar = (SimpleDraweeView) itemView.findViewById(R.id.avatar);
mCreator = (TextView) itemView.findViewById(R.id.creator);
mDate = (TextView) itemView.findViewById(R.id.date);
mCardView = (CardView) itemView.findViewById(R.id.card);
mCardView.setOnLongClickListener(this);
mCardView.setOnClickListener(this);
mText = (TextView) itemView.findViewById(R.id.text);
mLike = (TextView) itemView.findViewById(R.id.like);
mPicture = (SimpleDraweeView) itemView.findViewById(R.id.picture);
mLike.setOnClickListener(this);
mAudioLayout = itemView.findViewById(R.id.audioLayout);
mAudioAction = (ImageButton) itemView.findViewById(R.id.audioAction);
mAudioBar = (SeekBar) itemView.findViewById(R.id.audioBar);
mAudioLength = (TextView) itemView.findViewById(R.id.length);
mAudioAction.setOnClickListener(this);
itemView.setOnLongClickListener(this);
mPicture.setOnLongClickListener(this);
mPicture.setOnClickListener(this);
mListener = listener;
mSize = size;
}
@Override
public void onClick(final View v) {
int id = v.getId();
if (id == R.id.like) {
v.setEnabled(false);
v.postDelayed(new Runnable() {
@Override
public void run() {
v.setEnabled(true);
}
}, 1000);
if (mListener != null) {
mListener.toggleLike(getAdapterPosition());
}
}else if(id == R.id.audioAction) {
if (mListener != null) {
mAudioAction.setImageResource(mListener.onAudioActionClicked(getAdapterPosition(), mAudioBar.getProgress()));
}
}else {
if (mListener != null) {
mListener.onClick(getAdapterPosition());
}
}
}
@Override
public boolean onLongClick(View v) {
if (mListener != null) {
mListener.onLongClick(getAdapterPosition(), v);
return true;
}else {
return false;
}
}
public void onBind(@NonNull Feed feed, @NonNull final String userId, final int avatarSize){
setCreatedBy(feed.getCreatedBy(), avatarSize);
setCreatedAt(feed.getCreatedAt());
setLiked(feed.isLiked(userId));
setLikedCount(feed.getLikedCount());
boolean mine = feed.getCreatedById().equals(userId);
setColors(mine);
switch (feed.getFeedType()) {
case FEED_COMMENT:
setText(feed.getShowingText());
mPicture.setVisibility(View.GONE);
mAudioLayout.setVisibility(View.GONE);
break;
case FEED_IMAGE:
setText(feed.getCaption());
setPicture(feed.getUrl(), feed.getPath(), feed.getUri());
break;
case FEED_FILE:
setFile(feed, mine);
break;
case FEED_MARKER:
setMarker();
break;
case FEED_AUDIO:
setAudio(feed.getLength(), mine);
break;
}
}
public void onBind(@NonNull Feed feed, List<Object> payloads, @NonNull final String userId, final int avatarSize){
Bundle bundle = (Bundle) payloads.get(0);
UserResource createdBy = bundle.getParcelable(NAMES.Server.CREATED_BY_ID);
boolean mine = createdBy != null && createdBy.getUserId().equals(userId);
for (String key : bundle.keySet()) {
switch (key) {
case NAMES.Server.CREATED_AT:
setCreatedAt(feed.getCreatedAt());
break;
case NAMES.Server.CREATED_BY_ID:
if (createdBy != null) {
setCreatedBy(createdBy, avatarSize);
setColors(userId.equals(createdBy.getUserId()));
}
break;
case NAMES.Server.TEXT:
setText(feed.getShowingText());
break;
case NAMES.Server.COUNT:
setLikedCount(bundle.getInt(NAMES.Server.COUNT, 0));
break;
case NAMES.MY_LIKE:
setLiked(bundle.getBoolean(NAMES.MY_LIKE, false));
break;
case NAMES.Server.LENGTH:
setAudio(bundle.getLong(NAMES.Server.LENGTH, 0), createdBy != null && createdBy.getUserId().equals(userId));
break;
case NAMES.Server.PLAN_ID:
setMarker();
break;
case NAMES.Server.CAPTION:
setText(bundle.getString(NAMES.Server.CAPTION));
break;
case NAMES.Server.URL:
case NAMES.DB.PATH:
String mimeType = bundle.getString(NAMES.Server.MIME_TYPE);
if (mimeType != null && mimeType.contains(Constants.MimeType.IMAGE)) {
setPicture(bundle.getString(NAMES.Server.URL), bundle.getString(NAMES.DB.PATH), null);
}
break;
case SEEK_BAR_PROGRESS_BUNDLE:
mAudioBar.setProgress(bundle.getInt(SEEK_BAR_PROGRESS_BUNDLE));
mAudioAction.setImageResource(bundle.getInt(ACTION_ICON_BUNDLE));
break;
case RESET_AUDIO:
resetAudio(bundle.getBoolean("mine"));
break;
}
}
}
private void setCreatedBy(UserResource createdBy, final int avatarSize) {
if (createdBy != null) {
ImageLoader.newLoad(createdBy.getUrl(), Constants.Thumbnails.T72, mAvatar, avatarSize, avatarSize, R.drawable.unknown_user);
mCreator.setText(createdBy.getName());
}
}
private void setCreatedAt(@NonNull Date createdAt) {
mDate.setText(DateUtils.formatDate(createdAt, DateUtils.COMMENT_DATE));
}
@SuppressWarnings("deprecation")
private void setText(String text){
if (text != null) {
mText.setVisibility(View.VISIBLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mText.setText(Html.fromHtml(text.replace("\n", "<br>"), Html.FROM_HTML_MODE_LEGACY), TextView.BufferType.SPANNABLE);
}else {
mText.setText(Html.fromHtml(text.replace("\n", "<br>")), TextView.BufferType.SPANNABLE);
}
}else {
mText.setVisibility(View.GONE);
}
}
private void setColors(boolean mine){
Context context = mCardView.getContext();
if (mine) {
mCardView.setBackgroundColor(ContextCompat.getColor(context, MINE_BG_COLOR));
mText.setTextColor(ContextCompat.getColor(context, MINE_TEXT_COLOR));
mAudioLength.setTextColor(ContextCompat.getColor(context, MINE_TEXT_COLOR));
}else {
mCardView.setBackgroundColor(ContextCompat.getColor(context, THEIR_BG_COLOR));
mText.setTextColor(ContextCompat.getColor(context, THEIR_TEXT_COLOR));
mAudioLength.setTextColor(ContextCompat.getColor(context, THEIR_TEXT_COLOR));
}
}
private void setLikedCount(final int count){
mLike.setText(String.format(Locale.getDefault(), "%d", count));
}
private void setLiked(final boolean isLiked){
if (isLiked) {
mLike.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_like, 0);
}else {
mLike.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_like_empty, 0);
}
}
private void setPicture(final String url, final String path, final Uri uri){
mAudioLayout.setVisibility(View.GONE);
if(url != null) {
mPicture.setVisibility(View.VISIBLE);
ImageLoader.newLoad(url, Constants.Thumbnails.T480, mPicture, mSize, mSize, R.color.white);
}else if(path != null){
mPicture.setVisibility(View.VISIBLE);
ImageLoader.newLoad(path, mPicture, mSize, mSize, R.color.white);
}else if (uri != null) {
mPicture.setVisibility(View.VISIBLE);
ImageLoader.newLoad(uri, mPicture, mSize, mSize);
}else {
mPicture.setVisibility(View.GONE);
}
}
private void setFile(@NonNull final Feed feed, final boolean mine){
mPicture.setVisibility(View.GONE);
mText.setVisibility(View.VISIBLE);
mAudioLayout.setVisibility(View.GONE);
if (mine) {
mText.setCompoundDrawablesRelativeWithIntrinsicBounds(MINE_FILE_ICON, 0, 0, 0);
}else {
mText.setCompoundDrawablesRelativeWithIntrinsicBounds(THEIR_FILE_ICON, 0, 0, 0);
}
mText.setText(feed.getName());
}
private void setMarker(){
mPicture.setVisibility(View.GONE);
mText.setVisibility(View.VISIBLE);
mAudioLayout.setVisibility(View.GONE);
mText.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_plan_gray, 0, 0, 0);
mText.setText(R.string.feed_marker_placeholder);
}
private void setAudio(final long length, final boolean mine){
mPicture.setVisibility(View.GONE);
mText.setVisibility(View.GONE);
mAudioLayout.setVisibility(View.VISIBLE);
if (mine) {
mAudioAction.setImageResource(MINE_PLAY_ICON);
}else {
mAudioAction.setImageResource(THEIR_PLAY_ICON);
}
mAudioLength.setText(AndroidUtils._String.audioLength(length));
mAudioBar.setMax((int) (length / 1000) * 1000); //round
mAudioBar.setProgress(0);
}
public void resetAudio(boolean mine){
if (mine) {
mAudioAction.setImageResource(MINE_PLAY_ICON);
mAudioBar.setProgress(0);
}else {
mAudioAction.setImageResource(THEIR_PLAY_ICON);
mAudioBar.setProgress(0);
}
}
public void setHolderSkeleton(int avatarImageResource, int bgColor){
mAvatar.setImageResource(avatarImageResource);
mCreator.setText("");
mCreator.setBackgroundColor(bgColor);
mDate.setText("");
mDate.setBackgroundColor(bgColor);
mText.setText("");
mText.setBackgroundColor(bgColor);
mPicture.setVisibility(View.GONE);
mAudioLayout.setVisibility(View.GONE);
mLike.setText("");
mLike.setBackgroundColor(bgColor);
mLike.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
public void clearHolderSkeleton(){
mCreator.setBackgroundColor(0);
mDate.setBackgroundColor(0);
mText.setBackgroundColor(0);
mLike.setBackgroundColor(0);
}
interface FeedHolderListener {
void toggleLike(final int pos);
void onLongClick(final int pos, final View v);
void onClick(final int pos);
int onAudioActionClicked(final int pos, final int progress);
}
}
对于 Feed 类型,您目前有 "HOLDER_COMMENT"、"HOLDER_IMAGE" 等。将类型 "HOLDER_ALBUM" 添加到此列表。在 onCreateViewHolder()
中,您将扩充与您的四张或更多图片分组相对应的布局。像这样:
album_layout.xml
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<ImageView
android:id="@+id/albumView1"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="fitCenter"
tools:src="@drawable/sample_image"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/albumView2"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/albumView2"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="fitCenter"
tools:src="@drawable/sample_image"
app:layout_constraintStart_toEndOf="@id/albumView1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/albumView1" />
<ImageView
android:id="@+id/albumView3"
android:layout_width="100dp"
android:layout_height="100dp"
tools:src="@drawable/sample_image"
app:layout_constraintStart_toStartOf="@+id/albumView1"
app:layout_constraintTop_toBottomOf="@id/albumView1" />
<ImageView
android:id="@+id/albumView4"
android:layout_width="100dp"
android:layout_height="100dp"
tools:src="@drawable/sample_image"
android:background="@android:color/black"
app:layout_constraintBottom_toBottomOf="@id/albumView3"
app:layout_constraintStart_toEndOf="@id/albumView3"
app:layout_constraintTop_toTopOf="@id/albumView3" />
<View
android:id="@+id/albumView4Overlay"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0.5"
android:background="@android:color/black"
app:layout_constraintBottom_toBottomOf="@id/albumView4"
app:layout_constraintStart_toStartOf="@id/albumView4"
app:layout_constraintEnd_toEndOf="@id/albumView4"
app:layout_constraintTop_toTopOf="@id/albumView4"
tools:text="+ 5" />
<TextView
android:id="@+id/albumView4OverlayText"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
tools:src="@drawable/sample_image"
android:textColor="@android:color/white"
android:textSize="25sp"
app:layout_constraintBottom_toBottomOf="@id/albumView4"
app:layout_constraintStart_toStartOf="@id/albumView4"
app:layout_constraintEnd_toEndOf="@id/albumView4"
app:layout_constraintTop_toTopOf="@id/albumView4"
tools:text="+ 5" />
</android.support.constraint.ConstraintLayout>
现在让我们解决提要问题:每张图片目前在 mItems
中占据一个提要位置。您将需要将四个或更多图像的运行映射到一个可以将它们映射到上面的布局中的视图持有者。最简单的方法是创建另一个 List<Feed>
类型的变量,该变量将保存所有不是构成相册一部分的图像的提要条目,以及对应于相册中四张或更多图像的单个位置原始提要列表。您的 RecyclerView
将从这个新的 Feed 列表中删除。
演示应用程序
这是一个展示上述技术的演示应用程序。为了示例的目的,我已经合并了您的代码的要点。关键处理发生在 RecyclerView
适配器的构造函数中。 "album_layout.xml" 的代码如上所示。
需要注意一件事:如果基本提要发生变化,您将需要修改或重新创建 mNewItems
。
MainActivity.java
public class MainActivity extends AppCompatActivity {
List<Feed> mItems;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Drawable image = getDrawable(R.drawable.sample_image);
mItems = Arrays.asList(new Feed("This is comment #1"),
new Feed("This is comment #2"),
new Feed(image),
new Feed("This is comment #3"),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed("This is comment #4"),
new Feed("This is comment #5"),
new Feed("This is comment #6"),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed("This is comment #7"),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed("This is comment #8"),
new Feed("This is comment #9")
);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
RecyclerViewAdapter adapter = new RecyclerViewAdapter(mItems);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
}
}
RecyclerViewAdapter.java
class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private List<Feed> mNewItems = new ArrayList<>();
RecyclerViewAdapter(List<Feed> items) {
int i = 0;
Feed feed;
int feedType;
List<Feed> album; // Run of 4 or more images in items
// Process the incoming list to consolidate runs of 4 or more images into
// a single Feed entry.
while (i < items.size()) {
feed = items.get(i);
feedType = feed.getFeedType();
if (feedType == Feed.FEED_IS_IMAGE && (album = getAlbum(items, i)) != null) {
feed = new Feed(album);
i += album.size();
} else {
i++;
}
mNewItems.add(feed);
}
}
// Identify runs of 4 or more images and return an album (List<Feed>) of those images.
private List<Feed> getAlbum(List<Feed> feedList, int offset) {
int i = offset + 1;
while (i < feedList.size() && feedList.get(i).getFeedType() == Feed.FEED_IS_IMAGE) {
i++;
}
return (i - offset < 4) ? null : feedList.subList(offset, i);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int layoutId;
switch (viewType) {
case Feed.FEED_IS_IMAGE:
layoutId = R.layout.image_layout;
break;
case Feed.FEED_IS_ALBUM:
layoutId = R.layout.album_layout;
break;
default: // comment
layoutId = R.layout.comment_layout;
break;
}
View view = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
return new ViewHolder(view, viewType);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Feed feed = mNewItems.get(position);
switch (getItemViewType(position)) {
case Feed.FEED_IS_ALBUM:
for (int i = 0; i < 4; i++) {
holder.mAlbumViews.get(i).setImageDrawable(feed.mAlbum.get(i).mDrawable);
}
holder.mAlbumImageCount.setText("+ " + (feed.mAlbum.size() - 4));
break;
case Feed.FEED_IS_IMAGE:
holder.mImage.setImageDrawable(feed.mDrawable);
break;
default:
holder.mComment.setText(feed.mComment);
break;
}
}
@Override
public int getItemViewType(int position) {
return mNewItems.get(position).getFeedType();
}
@Override
public int getItemCount() {
return mNewItems.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView mImage;
TextView mComment;
List<ImageView> mAlbumViews;
TextView mAlbumImageCount;
ViewHolder(View itemView, int viewType) {
super(itemView);
switch (viewType) {
case Feed.FEED_IS_IMAGE:
mImage = itemView.findViewById(R.id.imageView);
break;
case Feed.FEED_IS_ALBUM:
mAlbumViews = new ArrayList<>();
mAlbumViews.add((ImageView) itemView.findViewById(R.id.albumView1));
mAlbumViews.add((ImageView) itemView.findViewById(R.id.albumView2));
mAlbumViews.add((ImageView) itemView.findViewById(R.id.albumView3));
mAlbumViews.add((ImageView) itemView.findViewById(R.id.albumView4));
mAlbumImageCount = itemView.findViewById(R.id.albumView4OverlayText);
break;
default: // is comment layout
mComment = itemView.findViewById(R.id.comment);
break;
}
}
}
}
Feed.java
class Feed {
String mComment;
Drawable mDrawable;
List<Feed> mAlbum;
Feed(Drawable drawable) {
mDrawable = drawable;
}
Feed(List<Feed> album) {
mAlbum = album;
}
Feed(String comment) {
mComment = comment;
}
int getFeedType() {
if (mDrawable != null) {
return FEED_IS_IMAGE;
}
if (mAlbum != null) {
return FEED_IS_ALBUM;
}
return FEED_IS_COMMENT;
}
final static int FEED_IS_COMMENT = 1;
final static int FEED_IS_IMAGE = 2;
final static int FEED_IS_ALBUM = 3;
}
activity_main.xml
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.albumwork.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.constraint.ConstraintLayout>
comment_layout.xml
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<TextView
android:id="@+id/comment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="A comment" />
</android.support.constraint.ConstraintLayout>
image_layout.xml
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:padding="16dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginEnd="8dp"
android:scaleType="fitCenter"
tools:src="@drawable/sample_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
我的应用程序中有一个类似于聊天的 WhatsApp,最近他们添加了一项新功能,如果它们以 4 张或更多图片的顺序发送并且它们之间没有消息,则可以将图像分组到相册中。如下图所示:
所以我实现了我的 RecyclerView 适配器,它单独显示所有内容,这意味着无论是消息、图像、音频等,它们中的每一个都将在我的适配器中单独的一行中。所以我想做 WhatsApp 所做的事情,如果连续发送的图像超过 4 张,我会将这组图像实施到相册中。我怎样才能做到这一点?
我已经在我的适配器中成功实现了 getItemViewType()
方法,并且工作正常。但是我现在不知道怎么办。
PS: Feed 是我的对象,可以是文本消息、图像和音频文件等。所以 mItems 是一个 Feed 列表。
这是我的适配器:
public class FeedAdapter extends BaseSkeletonAdapter<Feed> implements FeedHolder.FeedHolderListener{
private static final int HOLDER_COMMENT = 1;
private static final int HOLDER_IMAGE = 2;
private static final int HOLDER_FILE = 3;
private static final int HOLDER_AUDIO = 4;
private static final int HOLDER_MARKER = 5;
private static final int HOLDER_EMPTY = 6;
private final FeedItemListener mListener;
private final int mAvatarSize;
private final String mUserId;
private final int mPictureSize;
private final int mSkeletonColor;
public FeedAdapter(FeedItemListener listener, String userId, int avatarSize, int pictureSize, int skeletonColor) {
super(2);
mListener = listener;
mUserId = userId;
mAvatarSize = avatarSize;
mPictureSize = pictureSize;
mSkeletonColor = skeletonColor;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType){
case HOLDER_COMMENT:
case HOLDER_IMAGE:
System.out.println("It is an image!");
case HOLDER_FILE:
case HOLDER_MARKER:
case HOLDER_AUDIO:
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_feed, parent, false);
return new FeedHolder(view, this, mPictureSize);
case HOLDER_EMPTY:
default:
View empty = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_empty, parent, false);
return new EmptyPlaceholderViewHolder(empty, R.string.placeholder_feed_empty_title, R.string.placeholder_feed_empty_description, R.drawable.ic_feed_placeholder);
}
}
@Override
protected void onBind(RecyclerView.ViewHolder holder, int position) {
if(!(holder instanceof EmptyPlaceholderViewHolder)){
Feed feed = mItems.get(position);
//for (int i = 0; i < mItems.size(); i++) {
// System.out.println("Tamanho: " + mItems.size() + "\nDado: " + mItems.get(i).getText() + "\nDado 2: " + mItems.get(i).getUrl());
//}
if (holder instanceof FeedHolder) {
if (mUserId.equals(feed.getCreatedById())) {
((FeedHolder) holder).onBind(feed, mUserId, mAvatarSize);
}else {
((FeedHolder) holder).onBind(feed, mUserId, mAvatarSize);
}
}
}
}
@Override
protected void onBind(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {
if (payloads.isEmpty()) {
onBindViewHolder(holder, position);
}else {
if (holder instanceof FeedHolder) {
((FeedHolder) holder).onBind(mItems.get(position), payloads, mUserId, mAvatarSize);
}
}
}
@Override
protected void setHolderSkeleton(RecyclerView.ViewHolder holder) {
if (holder instanceof FeedHolder) {
((FeedHolder) holder).setHolderSkeleton(R.drawable.rounded_skeleton, mSkeletonColor);
}
}
@Override
protected void clearHolderSkeleton(RecyclerView.ViewHolder holder) {
if (holder instanceof FeedHolder) {
((FeedHolder) holder).clearHolderSkeleton();
}
}
@Override
public int getItemViewType(int position) {
if(mSkeletonMode){
return HOLDER_COMMENT;
} if (mItems != null && mItems.size() > 0 && position >= 0 && position < mItems.size()) {
Feed feed = mItems.get(position);
if (feed != null) {
String type = feed.getFeedType();
if (type != null) {
switch (type) {
case FEED_IMAGE:
return HOLDER_IMAGE;
case FEED_AUDIO:
return HOLDER_AUDIO;
case FEED_FILE:
return HOLDER_FILE;
case FEED_MARKER:
return HOLDER_MARKER;
case FEED_COMMENT:
default:
return HOLDER_COMMENT;
}
}
}
return HOLDER_COMMENT;
}else {
return HOLDER_EMPTY;
}
}
public List<Feed> getItems() {
return mItems;
}
public void swap(List<Feed> feedList) {
if (mItems == null) {
mItems = new ArrayList<>();
}
mItems.clear();
mItems.addAll(feedList);
}
@Override
public void toggleLike(final int pos){
if(mListener != null && pos >= 0 && pos < mItems.size()){
mListener.toggleLike(mItems.get(pos));
}
}
@Override
public void onLongClick(final int pos, final View v) {
if (mListener != null && pos >= 0 && pos < mItems.size()) {
mListener.onLongClick(mItems.get(pos), v);
}
}
@Override
public int onAudioActionClicked(final int pos, final int progress) {
if (mListener != null) {
return mListener.onAudioActionClicked(pos, mItems.get(pos), progress);
}else {
return 0;
}
}
@Override
public void onClick(int pos) {
if (mItems!=null && pos >= 0 && pos<mItems.size()) {
Feed feed = mItems.get(pos);
if (feed != null && mListener != null) {
mListener.onClick(feed);
}
}
}
public interface FeedItemListener {
void toggleLike(@NonNull Feed feed);
void onLongClick(@NonNull Feed feed, @NonNull View v);
void onClick(@NonNull Feed feed);
int onAudioActionClicked(int pos, @NonNull Feed feed, final int progress);
}
}
这是我的 FeedHolder class:
public class FeedHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
public static final String SEEK_BAR_PROGRESS_BUNDLE = "bundle_seek_bar_progress";
public static final String ACTION_ICON_BUNDLE = "bundle_icon";
public static final String RESET_AUDIO = "bundle_reset_audio";
private static final int MINE_BG_COLOR = R.color.feed_mine_bg;
private static final int MINE_TEXT_COLOR = R.color.feed_mine_text;
private static final int MINE_PLAY_ICON = R.drawable.ic_play_white_24dp;
private static final int MINE_FILE_ICON = R.drawable.ic_file_white_18dp;
private static final int THEIR_BG_COLOR = R.color.feed_others_bg;
private static final int THEIR_TEXT_COLOR = R.color.feed_others_text;
private static final int THEIR_PLAY_ICON = R.drawable.ic_play_black_24dp;
private static final int THEIR_FILE_ICON = R.drawable.ic_file_black_18dp;
private final SimpleDraweeView mAvatar;
private final TextView mCreator;
private final TextView mDate;
private final CardView mCardView;
private final TextView mText;
private final TextView mLike;
private final FeedHolderListener mListener;
private final SimpleDraweeView mPicture;
private final int mSize;
private final View mAudioLayout;
private final ImageButton mAudioAction;
private final SeekBar mAudioBar;
private final TextView mAudioLength;
public FeedHolder(@NonNull final View itemView, final FeedHolderListener listener, final int size) {
super(itemView);
mAvatar = (SimpleDraweeView) itemView.findViewById(R.id.avatar);
mCreator = (TextView) itemView.findViewById(R.id.creator);
mDate = (TextView) itemView.findViewById(R.id.date);
mCardView = (CardView) itemView.findViewById(R.id.card);
mCardView.setOnLongClickListener(this);
mCardView.setOnClickListener(this);
mText = (TextView) itemView.findViewById(R.id.text);
mLike = (TextView) itemView.findViewById(R.id.like);
mPicture = (SimpleDraweeView) itemView.findViewById(R.id.picture);
mLike.setOnClickListener(this);
mAudioLayout = itemView.findViewById(R.id.audioLayout);
mAudioAction = (ImageButton) itemView.findViewById(R.id.audioAction);
mAudioBar = (SeekBar) itemView.findViewById(R.id.audioBar);
mAudioLength = (TextView) itemView.findViewById(R.id.length);
mAudioAction.setOnClickListener(this);
itemView.setOnLongClickListener(this);
mPicture.setOnLongClickListener(this);
mPicture.setOnClickListener(this);
mListener = listener;
mSize = size;
}
@Override
public void onClick(final View v) {
int id = v.getId();
if (id == R.id.like) {
v.setEnabled(false);
v.postDelayed(new Runnable() {
@Override
public void run() {
v.setEnabled(true);
}
}, 1000);
if (mListener != null) {
mListener.toggleLike(getAdapterPosition());
}
}else if(id == R.id.audioAction) {
if (mListener != null) {
mAudioAction.setImageResource(mListener.onAudioActionClicked(getAdapterPosition(), mAudioBar.getProgress()));
}
}else {
if (mListener != null) {
mListener.onClick(getAdapterPosition());
}
}
}
@Override
public boolean onLongClick(View v) {
if (mListener != null) {
mListener.onLongClick(getAdapterPosition(), v);
return true;
}else {
return false;
}
}
public void onBind(@NonNull Feed feed, @NonNull final String userId, final int avatarSize){
setCreatedBy(feed.getCreatedBy(), avatarSize);
setCreatedAt(feed.getCreatedAt());
setLiked(feed.isLiked(userId));
setLikedCount(feed.getLikedCount());
boolean mine = feed.getCreatedById().equals(userId);
setColors(mine);
switch (feed.getFeedType()) {
case FEED_COMMENT:
setText(feed.getShowingText());
mPicture.setVisibility(View.GONE);
mAudioLayout.setVisibility(View.GONE);
break;
case FEED_IMAGE:
setText(feed.getCaption());
setPicture(feed.getUrl(), feed.getPath(), feed.getUri());
break;
case FEED_FILE:
setFile(feed, mine);
break;
case FEED_MARKER:
setMarker();
break;
case FEED_AUDIO:
setAudio(feed.getLength(), mine);
break;
}
}
public void onBind(@NonNull Feed feed, List<Object> payloads, @NonNull final String userId, final int avatarSize){
Bundle bundle = (Bundle) payloads.get(0);
UserResource createdBy = bundle.getParcelable(NAMES.Server.CREATED_BY_ID);
boolean mine = createdBy != null && createdBy.getUserId().equals(userId);
for (String key : bundle.keySet()) {
switch (key) {
case NAMES.Server.CREATED_AT:
setCreatedAt(feed.getCreatedAt());
break;
case NAMES.Server.CREATED_BY_ID:
if (createdBy != null) {
setCreatedBy(createdBy, avatarSize);
setColors(userId.equals(createdBy.getUserId()));
}
break;
case NAMES.Server.TEXT:
setText(feed.getShowingText());
break;
case NAMES.Server.COUNT:
setLikedCount(bundle.getInt(NAMES.Server.COUNT, 0));
break;
case NAMES.MY_LIKE:
setLiked(bundle.getBoolean(NAMES.MY_LIKE, false));
break;
case NAMES.Server.LENGTH:
setAudio(bundle.getLong(NAMES.Server.LENGTH, 0), createdBy != null && createdBy.getUserId().equals(userId));
break;
case NAMES.Server.PLAN_ID:
setMarker();
break;
case NAMES.Server.CAPTION:
setText(bundle.getString(NAMES.Server.CAPTION));
break;
case NAMES.Server.URL:
case NAMES.DB.PATH:
String mimeType = bundle.getString(NAMES.Server.MIME_TYPE);
if (mimeType != null && mimeType.contains(Constants.MimeType.IMAGE)) {
setPicture(bundle.getString(NAMES.Server.URL), bundle.getString(NAMES.DB.PATH), null);
}
break;
case SEEK_BAR_PROGRESS_BUNDLE:
mAudioBar.setProgress(bundle.getInt(SEEK_BAR_PROGRESS_BUNDLE));
mAudioAction.setImageResource(bundle.getInt(ACTION_ICON_BUNDLE));
break;
case RESET_AUDIO:
resetAudio(bundle.getBoolean("mine"));
break;
}
}
}
private void setCreatedBy(UserResource createdBy, final int avatarSize) {
if (createdBy != null) {
ImageLoader.newLoad(createdBy.getUrl(), Constants.Thumbnails.T72, mAvatar, avatarSize, avatarSize, R.drawable.unknown_user);
mCreator.setText(createdBy.getName());
}
}
private void setCreatedAt(@NonNull Date createdAt) {
mDate.setText(DateUtils.formatDate(createdAt, DateUtils.COMMENT_DATE));
}
@SuppressWarnings("deprecation")
private void setText(String text){
if (text != null) {
mText.setVisibility(View.VISIBLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mText.setText(Html.fromHtml(text.replace("\n", "<br>"), Html.FROM_HTML_MODE_LEGACY), TextView.BufferType.SPANNABLE);
}else {
mText.setText(Html.fromHtml(text.replace("\n", "<br>")), TextView.BufferType.SPANNABLE);
}
}else {
mText.setVisibility(View.GONE);
}
}
private void setColors(boolean mine){
Context context = mCardView.getContext();
if (mine) {
mCardView.setBackgroundColor(ContextCompat.getColor(context, MINE_BG_COLOR));
mText.setTextColor(ContextCompat.getColor(context, MINE_TEXT_COLOR));
mAudioLength.setTextColor(ContextCompat.getColor(context, MINE_TEXT_COLOR));
}else {
mCardView.setBackgroundColor(ContextCompat.getColor(context, THEIR_BG_COLOR));
mText.setTextColor(ContextCompat.getColor(context, THEIR_TEXT_COLOR));
mAudioLength.setTextColor(ContextCompat.getColor(context, THEIR_TEXT_COLOR));
}
}
private void setLikedCount(final int count){
mLike.setText(String.format(Locale.getDefault(), "%d", count));
}
private void setLiked(final boolean isLiked){
if (isLiked) {
mLike.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_like, 0);
}else {
mLike.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_like_empty, 0);
}
}
private void setPicture(final String url, final String path, final Uri uri){
mAudioLayout.setVisibility(View.GONE);
if(url != null) {
mPicture.setVisibility(View.VISIBLE);
ImageLoader.newLoad(url, Constants.Thumbnails.T480, mPicture, mSize, mSize, R.color.white);
}else if(path != null){
mPicture.setVisibility(View.VISIBLE);
ImageLoader.newLoad(path, mPicture, mSize, mSize, R.color.white);
}else if (uri != null) {
mPicture.setVisibility(View.VISIBLE);
ImageLoader.newLoad(uri, mPicture, mSize, mSize);
}else {
mPicture.setVisibility(View.GONE);
}
}
private void setFile(@NonNull final Feed feed, final boolean mine){
mPicture.setVisibility(View.GONE);
mText.setVisibility(View.VISIBLE);
mAudioLayout.setVisibility(View.GONE);
if (mine) {
mText.setCompoundDrawablesRelativeWithIntrinsicBounds(MINE_FILE_ICON, 0, 0, 0);
}else {
mText.setCompoundDrawablesRelativeWithIntrinsicBounds(THEIR_FILE_ICON, 0, 0, 0);
}
mText.setText(feed.getName());
}
private void setMarker(){
mPicture.setVisibility(View.GONE);
mText.setVisibility(View.VISIBLE);
mAudioLayout.setVisibility(View.GONE);
mText.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_plan_gray, 0, 0, 0);
mText.setText(R.string.feed_marker_placeholder);
}
private void setAudio(final long length, final boolean mine){
mPicture.setVisibility(View.GONE);
mText.setVisibility(View.GONE);
mAudioLayout.setVisibility(View.VISIBLE);
if (mine) {
mAudioAction.setImageResource(MINE_PLAY_ICON);
}else {
mAudioAction.setImageResource(THEIR_PLAY_ICON);
}
mAudioLength.setText(AndroidUtils._String.audioLength(length));
mAudioBar.setMax((int) (length / 1000) * 1000); //round
mAudioBar.setProgress(0);
}
public void resetAudio(boolean mine){
if (mine) {
mAudioAction.setImageResource(MINE_PLAY_ICON);
mAudioBar.setProgress(0);
}else {
mAudioAction.setImageResource(THEIR_PLAY_ICON);
mAudioBar.setProgress(0);
}
}
public void setHolderSkeleton(int avatarImageResource, int bgColor){
mAvatar.setImageResource(avatarImageResource);
mCreator.setText("");
mCreator.setBackgroundColor(bgColor);
mDate.setText("");
mDate.setBackgroundColor(bgColor);
mText.setText("");
mText.setBackgroundColor(bgColor);
mPicture.setVisibility(View.GONE);
mAudioLayout.setVisibility(View.GONE);
mLike.setText("");
mLike.setBackgroundColor(bgColor);
mLike.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
public void clearHolderSkeleton(){
mCreator.setBackgroundColor(0);
mDate.setBackgroundColor(0);
mText.setBackgroundColor(0);
mLike.setBackgroundColor(0);
}
interface FeedHolderListener {
void toggleLike(final int pos);
void onLongClick(final int pos, final View v);
void onClick(final int pos);
int onAudioActionClicked(final int pos, final int progress);
}
}
对于 Feed 类型,您目前有 "HOLDER_COMMENT"、"HOLDER_IMAGE" 等。将类型 "HOLDER_ALBUM" 添加到此列表。在 onCreateViewHolder()
中,您将扩充与您的四张或更多图片分组相对应的布局。像这样:
album_layout.xml
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<ImageView
android:id="@+id/albumView1"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="fitCenter"
tools:src="@drawable/sample_image"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/albumView2"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/albumView2"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="fitCenter"
tools:src="@drawable/sample_image"
app:layout_constraintStart_toEndOf="@id/albumView1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/albumView1" />
<ImageView
android:id="@+id/albumView3"
android:layout_width="100dp"
android:layout_height="100dp"
tools:src="@drawable/sample_image"
app:layout_constraintStart_toStartOf="@+id/albumView1"
app:layout_constraintTop_toBottomOf="@id/albumView1" />
<ImageView
android:id="@+id/albumView4"
android:layout_width="100dp"
android:layout_height="100dp"
tools:src="@drawable/sample_image"
android:background="@android:color/black"
app:layout_constraintBottom_toBottomOf="@id/albumView3"
app:layout_constraintStart_toEndOf="@id/albumView3"
app:layout_constraintTop_toTopOf="@id/albumView3" />
<View
android:id="@+id/albumView4Overlay"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0.5"
android:background="@android:color/black"
app:layout_constraintBottom_toBottomOf="@id/albumView4"
app:layout_constraintStart_toStartOf="@id/albumView4"
app:layout_constraintEnd_toEndOf="@id/albumView4"
app:layout_constraintTop_toTopOf="@id/albumView4"
tools:text="+ 5" />
<TextView
android:id="@+id/albumView4OverlayText"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
tools:src="@drawable/sample_image"
android:textColor="@android:color/white"
android:textSize="25sp"
app:layout_constraintBottom_toBottomOf="@id/albumView4"
app:layout_constraintStart_toStartOf="@id/albumView4"
app:layout_constraintEnd_toEndOf="@id/albumView4"
app:layout_constraintTop_toTopOf="@id/albumView4"
tools:text="+ 5" />
</android.support.constraint.ConstraintLayout>
现在让我们解决提要问题:每张图片目前在 mItems
中占据一个提要位置。您将需要将四个或更多图像的运行映射到一个可以将它们映射到上面的布局中的视图持有者。最简单的方法是创建另一个 List<Feed>
类型的变量,该变量将保存所有不是构成相册一部分的图像的提要条目,以及对应于相册中四张或更多图像的单个位置原始提要列表。您的 RecyclerView
将从这个新的 Feed 列表中删除。
演示应用程序
这是一个展示上述技术的演示应用程序。为了示例的目的,我已经合并了您的代码的要点。关键处理发生在 RecyclerView
适配器的构造函数中。 "album_layout.xml" 的代码如上所示。
需要注意一件事:如果基本提要发生变化,您将需要修改或重新创建 mNewItems
。
MainActivity.java
public class MainActivity extends AppCompatActivity {
List<Feed> mItems;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Drawable image = getDrawable(R.drawable.sample_image);
mItems = Arrays.asList(new Feed("This is comment #1"),
new Feed("This is comment #2"),
new Feed(image),
new Feed("This is comment #3"),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed("This is comment #4"),
new Feed("This is comment #5"),
new Feed("This is comment #6"),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed("This is comment #7"),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed(image),
new Feed("This is comment #8"),
new Feed("This is comment #9")
);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
RecyclerViewAdapter adapter = new RecyclerViewAdapter(mItems);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
}
}
RecyclerViewAdapter.java
class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private List<Feed> mNewItems = new ArrayList<>();
RecyclerViewAdapter(List<Feed> items) {
int i = 0;
Feed feed;
int feedType;
List<Feed> album; // Run of 4 or more images in items
// Process the incoming list to consolidate runs of 4 or more images into
// a single Feed entry.
while (i < items.size()) {
feed = items.get(i);
feedType = feed.getFeedType();
if (feedType == Feed.FEED_IS_IMAGE && (album = getAlbum(items, i)) != null) {
feed = new Feed(album);
i += album.size();
} else {
i++;
}
mNewItems.add(feed);
}
}
// Identify runs of 4 or more images and return an album (List<Feed>) of those images.
private List<Feed> getAlbum(List<Feed> feedList, int offset) {
int i = offset + 1;
while (i < feedList.size() && feedList.get(i).getFeedType() == Feed.FEED_IS_IMAGE) {
i++;
}
return (i - offset < 4) ? null : feedList.subList(offset, i);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int layoutId;
switch (viewType) {
case Feed.FEED_IS_IMAGE:
layoutId = R.layout.image_layout;
break;
case Feed.FEED_IS_ALBUM:
layoutId = R.layout.album_layout;
break;
default: // comment
layoutId = R.layout.comment_layout;
break;
}
View view = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
return new ViewHolder(view, viewType);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Feed feed = mNewItems.get(position);
switch (getItemViewType(position)) {
case Feed.FEED_IS_ALBUM:
for (int i = 0; i < 4; i++) {
holder.mAlbumViews.get(i).setImageDrawable(feed.mAlbum.get(i).mDrawable);
}
holder.mAlbumImageCount.setText("+ " + (feed.mAlbum.size() - 4));
break;
case Feed.FEED_IS_IMAGE:
holder.mImage.setImageDrawable(feed.mDrawable);
break;
default:
holder.mComment.setText(feed.mComment);
break;
}
}
@Override
public int getItemViewType(int position) {
return mNewItems.get(position).getFeedType();
}
@Override
public int getItemCount() {
return mNewItems.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView mImage;
TextView mComment;
List<ImageView> mAlbumViews;
TextView mAlbumImageCount;
ViewHolder(View itemView, int viewType) {
super(itemView);
switch (viewType) {
case Feed.FEED_IS_IMAGE:
mImage = itemView.findViewById(R.id.imageView);
break;
case Feed.FEED_IS_ALBUM:
mAlbumViews = new ArrayList<>();
mAlbumViews.add((ImageView) itemView.findViewById(R.id.albumView1));
mAlbumViews.add((ImageView) itemView.findViewById(R.id.albumView2));
mAlbumViews.add((ImageView) itemView.findViewById(R.id.albumView3));
mAlbumViews.add((ImageView) itemView.findViewById(R.id.albumView4));
mAlbumImageCount = itemView.findViewById(R.id.albumView4OverlayText);
break;
default: // is comment layout
mComment = itemView.findViewById(R.id.comment);
break;
}
}
}
}
Feed.java
class Feed {
String mComment;
Drawable mDrawable;
List<Feed> mAlbum;
Feed(Drawable drawable) {
mDrawable = drawable;
}
Feed(List<Feed> album) {
mAlbum = album;
}
Feed(String comment) {
mComment = comment;
}
int getFeedType() {
if (mDrawable != null) {
return FEED_IS_IMAGE;
}
if (mAlbum != null) {
return FEED_IS_ALBUM;
}
return FEED_IS_COMMENT;
}
final static int FEED_IS_COMMENT = 1;
final static int FEED_IS_IMAGE = 2;
final static int FEED_IS_ALBUM = 3;
}
activity_main.xml
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.albumwork.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.constraint.ConstraintLayout>
comment_layout.xml
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<TextView
android:id="@+id/comment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="A comment" />
</android.support.constraint.ConstraintLayout>
image_layout.xml
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:padding="16dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginEnd="8dp"
android:scaleType="fitCenter"
tools:src="@drawable/sample_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>