Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Grouping images in a RecyclerView row if they are in a sequence of 4 or more

I have a WhatsApp like chat in my application and recently they added a new functionality that groups images in albums if they are sent in a sequence of 4 or more pictures and there aren't messages between them. Like in the picture below:

enter image description here

So I have implemented my RecyclerView adapter that displays all the contents separately which means that whether it's a message, an image, an audio, etc, each one of them would be in a separated row in my adapter. So I would like to do what WhatsApp did and implement this grouping of images into albums if there are more than 4 sent in a row. How can I achieve this?

I have successfully implemented the getItemViewType() method in my adapter and it's working fine. But I don't know what to do now.

PS: Feed is my object that can be either a text message, an image, and audio file, etc. So mItems is a List of Feed.

This is my Adapter:

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);
  }
}

And this is my 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);
  }
}
like image 546
Marcos Guimaraes Avatar asked Oct 10 '17 18:10

Marcos Guimaraes


People also ask

Is recycler view bound single view or multiple view?

Android RecyclerView Multiple ViewType Project Structure We'll be implementing three view types (text, image, audio) that are inflated by three different layouts. Each has its own implementation specified in the adapter class.

What is ViewHolder in RecyclerView?

A ViewHolder describes an item view and metadata about its place within the RecyclerView. Adapter implementations should subclass ViewHolder and add fields for caching potentially expensive findViewById results. While LayoutParams belong to the LayoutManager , ViewHolders belong to the adapter.

How many times is called onCreateViewHolder?

By default it have 5. you can increase as per your need. Save this answer.

Which methods you would override if you have to implement a RecyclerView having different types of views provide proper method names?

You will have to override two main methods: one to inflate the view and its view holder, and another one to bind data to the view. The good thing about this is that the first method is called only when we really need to create a new view. No need to check if it's being recycled.


1 Answers

For feed types, you currently have "HOLDER_COMMENT", "HOLDER_IMAGE", etc. Add a type "HOLDER_ALBUM" to this list. In onCreateViewHolder() you will inflate a layout that corresponds to your grouping of four or more images. Something like this:

enter image description here

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>

Now let's address the feed: Each image currently takes one feed position in mItems. You will need to map runs of four or more images to a single view holder that can them be mapped into the layout above. The easiest way to do this would be to create another variable of type List<Feed> that will hold all the feed entries that are not images that form part of an album plus a single position that corresponds to runs of four or more images in the original feed list. Your RecyclerView will be driven off of this new feed list.


Demo App

Here is a demo app that shows the technique mentioned above. I have incorporated the gist of your code for the purpose of the example. The key processing occurs in the constructor for the RecyclerView adapter. The code for "album_layout.xml" is shown above.

One thing to note: If the base feed changes, you will need to modify or recreate mNewItems.

enter image description here

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>
like image 100
Cheticamp Avatar answered Oct 27 '22 07:10

Cheticamp