Joe
Joe

Reputation: 343

Section recyclerview photos by date

I have created a simple gallery that shows all the images on a phone. I would like to section off the images by date (month), and would also like a top section with the most recent photos taken. Please refer to the following image:

enter image description here

How can I go about doing this?

My Code:

SelectFileActivity.Java

public class SelectFileActivity extends AppCompatActivity {

  RecyclerView recyclerView;
  GalleryAdapter galleryAdapter;
  List < String > images;
  TextView gallery_number;
  ImageView back_arrow;

  private static final int REQUEST_CODE_STORAGE_PERMISSION = 101;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_select_file);

    // Remove status bar
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

    // Initialize content
    gallery_number = findViewById(R.id.gallery_number);
    recyclerView = findViewById(R.id.recyclerview_gallery_images);
    back_arrow = findViewById(R.id.select_file_back_arrow);

    // Check for permission
    if (ContextCompat.checkSelfPermission(SelectFileActivity.this,
        Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {

      ActivityCompat.requestPermissions(SelectFileActivity.this,
        new String[] {
          Manifest.permission.READ_EXTERNAL_STORAGE
        }, REQUEST_CODE_STORAGE_PERMISSION);
    } else {
      loadImages();
    }
  }

  @Override
  public void onBackPressed() {
    startActivity(new Intent(this, HomeActivity.class));
    finish();
  }

  private void loadImages() {

    recyclerView.setHasFixedSize(true);

    // Set the number of pictures per a row
    recyclerView.setLayoutManager(new GridLayoutManager(this, 3));

    images = SelectImagesGallery.listOfImages(this);
    galleryAdapter = new GalleryAdapter(this, images, new GalleryAdapter.PhotoListener() {
      @Override
      public void onPhotoClick(String path) {
        // Do something with the selected photo
      }
    });

    recyclerView.setAdapter(galleryAdapter);
  }

  @Override
  public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (requestCode == REQUEST_CODE_STORAGE_PERMISSION) {
      if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {

        loadImages();

      } else {
        Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show();
      }
    }
  }
}

GalleryAdapter.java

public class GalleryAdapter extends RecyclerView.Adapter < GalleryAdapter.ViewHolder > {

  private Context context;
  private List < String > images;
  protected PhotoListener photoListener;

  public GalleryAdapter(Context context, List < String > images, PhotoListener photoListener) {
    this.context = context;
    this.images = images;
    this.photoListener = photoListener;
  }

  @NonNull
  @Override
  public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    return new ViewHolder(
      LayoutInflater.from(context).inflate(R.layout.gallery_item, parent, false)
    );
  }

  @Override
  public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

    String image = images.get(position);

    // Load images to Glide
    Glide.with(context)
      .load(image)
      .transform(new CenterCrop(), new RoundedCorners(15))
      .into(holder.image);

    holder.itemView.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View view) {
        photoListener.onPhotoClick(image);
      }
    });
  }

  @Override
  public int getItemCount() {
    return images.size();
  }

  public class ViewHolder extends RecyclerView.ViewHolder {

    ImageView image;

    public ViewHolder(@NonNull View itemView) {
      super(itemView);

      image = itemView.findViewById(R.id.image);
    }
  }

  public interface PhotoListener {
    void onPhotoClick(String path);
  }
}

activity_select_file.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/windowBackground"
    android:orientation="vertical"
    tools:context=".upload.SelectFileActivity">
    
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerview_gallery_images"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

Upvotes: 2

Views: 2598

Answers (1)

ali73
ali73

Reputation: 415

TLDR

Create two xmls and two viewholders for them. In your data list also add months as objects and in your onCreateViewHolder method, check viewType and inflate proper xml for that.

Detailed answer

I had to implement something similar in one of my projects and this was how my adapter looked like. It is just a sample application and of course these functionalities could be implemented far more better. You need to implement them as you want with a better design.

First you need to implement your own RecyclerView adapter:

public class RvAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<GalleryBaseModel> _data;
    Context context ;

    public RvAdapter(Context context){
        this.context = context;
        this._data = new ArrayList<>();
    }
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (viewType == 1){ //DATE
            return new DateViewHolder(
                    LayoutInflater.from(context).inflate(R.layout.date_layout, parent, false)
            );
        }
        else { //Image
            return new ImageViewHolder(
                    LayoutInflater.from(context).inflate(R.layout.image_layout, parent, false)
            );
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        if (getItemViewType(position) == 1){
//            set date here
            FlexboxLayoutManager.LayoutParams layoutParams = (FlexboxLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
            layoutParams.setFlexGrow(1.0f);
            layoutParams.width = MATCH_PARENT;
            holder.itemView.setLayoutParams(layoutParams);
        }
        else {
//        set image here
            FlexboxLayoutManager.LayoutParams layoutParams = (FlexboxLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
            layoutParams.setFlexGrow(1.0f);
            layoutParams.width = getWidth();
            holder.itemView.setLayoutParams(layoutParams);
        }

    }

    /**
     * A function to calculate width of your image cardViews.
     * As it is a sample application I just return constant value, but you should calculate it.
     * For example screenWidth/3
     * @return Width of image cardView
     */
    public int getWidth(){
        return 200;
    }

    @Override
    public int getItemCount() {
        return _data.size();
    }

    @Override
    public int getItemViewType(int position) {
        return _data.get(position).getType();
    }

    class DateViewHolder extends RecyclerView.ViewHolder{
        TextView dateTv;
        public DateViewHolder(@NonNull View itemView) {
            super(itemView);
            dateTv = itemView.findViewById(R.id.dateTv);
        }
    }

    class ImageViewHolder extends RecyclerView.ViewHolder{
        ImageView mImage;
        public ImageViewHolder(@NonNull View itemView) {
            super(itemView);
            mImage = itemView.findViewById(R.id.mImage);
        }
    }

    public void addItem(GalleryBaseModel item){
        this._data.add(item);
        notifyDataSetChanged();
    }
}

This is my image_view.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView android:layout_width="match_parent"
    android:background="@android:color/black"
    android:layout_height="120dp"
    android:layout_marginLeft="5dp"
    android:layout_marginRight="5dp"
    android:layout_marginTop="10dp"
    android:layout_marginBottom="10dp"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:app="http://schemas.android.com/apk/res-auto">

        <ImageView
            android:id="@+id/mImage"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

And this is my date_layout.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
    android:id="@+id/dateTv"
    android:text="June 2020"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Here is my GalleryBaseModel.java which is base model class for objects that need to be inflated in my adapter.

public abstract class GalleryBaseModel {
    abstract int getType();
}

All classes inheriting this abstract class need to implement getType() method and return the type of model they actually are.For example here 1 was for my date model and 0 was for my image model.Since these are pretty simple I don't include them in the answer.

And finally here is how I setup my recyclerView:

//this could be onCreate method of your activity or any other proper method in activity or fragment or ...
        adapter = new RvAdapter(getApplicationContext());
        flexboxLayoutManager = new FlexboxLayoutManager(getApplicationContext());
        flexboxLayoutManager.setJustifyContent(JustifyContent.SPACE_BETWEEN);
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(flexboxLayoutManager);

And for managing layout I used FlexBoxLayoutManager which you can find more about it here and if you intend to use it, read the docs and use proper configuration for it. But you could use any other layout managers you wish.

Hope this was helpful for you.

Upvotes: 2

Related Questions