Gallery-like RecyclerView + CardView example


A long long time ago, we have android.widget.Gallery to shows items (ex. photos) in a center-locked, horizontally scrolling list. My example "Implement Android Gallery widget". But it deprecated and no longer supported.

So in long time ago at 2012, I have another example to "Implement Gallery-like HorizontalScrollView".

Now, with RecyclerView and CardView, it's another example to implement Gallery-like RecyclerView + CardView. It display files in ExternalStorageDirectoryPath + "/test/" folder.
(If you want to load from other user selectable folder, refer "Intent.ACTION_OPEN_DOCUMENT (from API level 19) to load images in RecyclerView + CardView".)


To use RecyclerView + CardView, we have to "Add Support Libraries of RecyclerView, CardView to Android Studio Project".

Create our RecyclerView.Adapter, MyRecyclerViewAdapter.java
package com.blogspot.android_er.androidgallery;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;

public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ItemHolder>{

private List<Uri> itemsUri;
private LayoutInflater layoutInflater;
private Context context;
private OnItemClickListener onItemClickListener;
MainActivity mainActivity;

public MyRecyclerViewAdapter(Context context, MainActivity mainActivity){
this.context = context;
layoutInflater = LayoutInflater.from(context);
itemsUri = new ArrayList<Uri>();

this.mainActivity = mainActivity;
}

@Override
public MyRecyclerViewAdapter.ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
CardView itemCardView = (CardView)layoutInflater.inflate(R.layout.layout_cardview, parent, false);
return new ItemHolder(itemCardView, this);
}

@Override
public void onBindViewHolder(MyRecyclerViewAdapter.ItemHolder holder, int position) {
Uri targetUri = itemsUri.get(position);
holder.setItemUri(targetUri.getPath());

if (targetUri != null){

try {
//! CAUTION !
//I'm not sure is it properly to load bitmap here!
holder.setImageView(loadScaledBitmap(targetUri));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}

/*
reference:
Load scaled bitmap
http://android-er.blogspot.com/2013/08/load-scaled-bitmap.html
*/
private Bitmap loadScaledBitmap(Uri src) throws FileNotFoundException {

//display the file to be loadScaledBitmap(),
//such that you can know how much work on it.
mainActivity.textInfo.append(src.getLastPathSegment() + "\n");

// required max width/height
final int REQ_WIDTH = 150;
final int REQ_HEIGHT = 150;

Bitmap bm = null;

// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(context.getContentResolver().openInputStream(src),
null, options);

// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, REQ_WIDTH,
REQ_HEIGHT);

// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeStream(
context.getContentResolver().openInputStream(src), null, options);

return bm;
}

public int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {

// Calculate ratios of height and width to requested height and
// width
final int heightRatio = Math.round((float) height
/ (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);

// Choose the smallest ratio as inSampleSize value, this will
// guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}

return inSampleSize;
}

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

public void setOnItemClickListener(OnItemClickListener listener){
onItemClickListener = listener;
}

public OnItemClickListener getOnItemClickListener(){
return onItemClickListener;
}

public interface OnItemClickListener{
public void onItemClick(ItemHolder item, int position);
}

public void add(int location, Uri iUri){
itemsUri.add(location, iUri);
notifyItemInserted(location);
}

public void clearAll(){
int itemCount = itemsUri.size();

if(itemCount>0){
itemsUri.clear();
notifyItemRangeRemoved(0, itemCount);
}
}


public static class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener{

private MyRecyclerViewAdapter parent;
private CardView cardView;
ImageView imageView;
String itemUri;

public ItemHolder(CardView cardView, MyRecyclerViewAdapter parent) {
super(cardView);
itemView.setOnClickListener(this);
this.cardView = cardView;
this.parent = parent;
imageView = (ImageView) cardView.findViewById(R.id.item_image);
}

public void setItemUri(String itemUri){
this.itemUri = itemUri;
}

public String getItemUri(){
return itemUri;
}

public void setImageView(Bitmap bitmap){
imageView.setImageBitmap(bitmap);
}

@Override
public void onClick(View v) {
final OnItemClickListener listener = parent.getOnItemClickListener();
if(listener != null){
listener.onItemClick(this, getLayoutPosition());
//or use
//listener.onItemClick(this, getAdapterPosition());
}
}
}
}


layout/layout_cardview.xml
<?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:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
card_view:cardCornerRadius="5sp"
card_view:cardElevation="5sp">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<ImageView
android:id="@+id/item_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>
</android.support.v7.widget.CardView>


MainActivity.java
package com.blogspot.android_er.androidgallery;

import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.method.ScrollingMovementMethod;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;

public class MainActivity extends AppCompatActivity
implements MyRecyclerViewAdapter.OnItemClickListener{

private RecyclerView myRecyclerView;
private MyRecyclerViewAdapter myRecyclerViewAdapter;
private LinearLayoutManager linearLayoutManager;

TextView textInfo;

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

textInfo = (TextView)findViewById(R.id.info);
textInfo.setMovementMethod(new ScrollingMovementMethod());

myRecyclerView = (RecyclerView)findViewById(R.id.myrecyclerview);
linearLayoutManager =
new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
myRecyclerViewAdapter = new MyRecyclerViewAdapter(this, this);
myRecyclerViewAdapter.setOnItemClickListener(this);
myRecyclerView.setAdapter(myRecyclerViewAdapter);
myRecyclerView.setLayoutManager(linearLayoutManager);

prepareGallery();
}

private void prepareGallery(){
String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();
String targetPath = ExternalStorageDirectoryPath + "/test/";

Toast.makeText(getApplicationContext(), targetPath, Toast.LENGTH_LONG).show();
File targetDirector = new File(targetPath);

File[] files = targetDirector.listFiles();
for (File file : files){
Uri uri = Uri.fromFile(file);
myRecyclerViewAdapter.add(
myRecyclerViewAdapter.getItemCount(),
uri);
}
}

@Override
public void onItemClick(MyRecyclerViewAdapter.ItemHolder item, int position) {

String stringitemUri = item.getItemUri();
Toast.makeText(MainActivity.this, stringitemUri, Toast.LENGTH_SHORT).show();
}
}


layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
tools:context=".MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://android-er.blogspot.com/"
android:textStyle="bold" />

<android.support.v7.widget.RecyclerView
android:id="@+id/myrecyclerview"
android:layout_width="match_parent"
android:layout_height="170dp" />

<TextView
android:id="@+id/info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:typeface="monospace"
android:gravity="bottom"/>

</LinearLayout>


To read diles in ExternalStorage, uses-permission of "android.permission.READ_EXTERNAL_STORAGE" is needed in AndroidManifest.xml.

download filesDownload the files (Android Studio Format) .

Next:
RecyclerView + CardView example: with Button

~ More example of RecyclerView + CardView.