How to create GridView and show photos from SDCard

Android GridView is a view group which allows us to display items in a two dimensional grid.

How to create basic ListView or GridView you can read here.

In this tutorial you'll learn how to make material design android application using GridView with header view and material ripple effect to show images from Download folder.

android_gridview.png

In this tutorial example project I have used android design support library and a material ripple library.

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support:design:23.1.0'
    compile 'com.github.traex.rippleeffect:library:1.3'
}

Add two items windowActionBar and windowNoTitle to styles.xml file.

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        ...
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>
</resources>

Open dimense.xml file from res/values and add dimen for app bar height.

<resources>
    ...
    <dimen name="app_bar_height">170dp</dimen>
</resources>

Add new permission to AndroidManifest.xml

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Open your main XML layout file and add CoordinatorLayout, AppBarLayout, CollapsingToolbarLayout, ImageView, Toolbar, NestedScrollView, LinearLayout and GridView.

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/android_coordinator_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar_layout"
        android:layout_width="match_parent"
        android:layout_height="@dimen/app_bar_height"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar_android_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:expandedTitleGravity="bottom|center_horizontal"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:id="@+id/image_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerInside"
                android:src="@android:drawable/ic_dialog_info"
                app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="0.7" />

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:id="@+id/nestedscrollview"
        android:layout_height="match_parent"
        android:fillViewport="true"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:id="@+id/linearLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <GridView
                android:id="@+id/grid"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:columnWidth="200dp"
                android:gravity="center"
                android:listSelector="#00000000"
                android:numColumns="auto_fit"
                android:stretchMode="columnWidth" />
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>

Create a new XML layout file called gridview_item.xml to make custom GridView image and text design and add a LinearLayout as root layout. Inside LinearLayout add RippleView with proper attribute like the below and inside RippleView add ImageView and TextView in LinearLayout.

<?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"
    android:id="@+id/android_gridview_custom_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="10dp">

    <com.andexert.library.RippleView
        android:id="@+id/more"
        rv_centered="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        app:rv_color="#fff"
        app:rv_rippleDuration="200">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/linearLayout"
            android:orientation="vertical">

            <ImageView
                android:id="@+id/gridview_image"
                android:layout_width="190dp"
                android:layout_height="150dp"
                android:src="@mipmap/ic_launcher" />

            <TextView
                android:id="@+id/gridview_text"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/grid_image"
                android:layout_marginTop="10dp"
                android:gravity="center"
                android:text="Grid View Item"
                android:textColor="#444"
                android:textSize="12sp"
                android:textStyle="bold" />
        </LinearLayout>
    </com.andexert.library.RippleView>
</LinearLayout>

Following is the complete code of MainActivity.java file. In this activity, we will check for the SD Card existence and if exist will load images from Download folder. The image file paths and names from the SD Card are stored in an array and passed to the PhotoAdapter to set it into the GridView. On GridView item click will show the name of image.

import android.os.Environment;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.Toast;

import java.io.File;

public class MainActivity extends AppCompatActivity {
    Toolbar toolbar;
    CollapsingToolbarLayout toolbarLayout;
    GridView gridView;

    private File[] files;
    private String[] filesPaths;
    private String[] filesNames;

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

        // Check for SD Card
        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            Toast.makeText(this, "Error! No SDCARD Found!", Toast.LENGTH_LONG).show();
        } else {
            File dirDownload = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
            if (dirDownload.isDirectory()) {
                files = dirDownload.listFiles();
                filesPaths = new String[files.length];
                filesNames = new String[files.length];

                for (int i = 0; i < files.length; i++) {
                    filesPaths[i] = files[i].getAbsolutePath();
                    filesNames[i] = files[i].getName();
                }
            }
        }

        toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        toolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar_android_layout);
        toolbarLayout.setTitle("Photo gallery");

        gridView = (GridView) findViewById(R.id.grid);
        gridView.setAdapter(new PhotoAdapter(this, filesNames, filesPaths));

        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Toast.makeText(getApplicationContext(), filesNames[position], Toast.LENGTH_LONG).show();
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_email:
                Toast.makeText(this, "EMail", Toast.LENGTH_LONG).show();
                break;
            default:
                break;
        }
        return true;
    }
}

Create a new java file with the name PhotoAdapter.java and extend it to BaseAdapter. Following is the complete code of custom GridView adapter file.

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class PhotoAdapter extends BaseAdapter {
    private Context ctx;
    private final String[] filesNames;
    private final String[] filesPaths;

    public PhotoAdapter(Context ctx, String[] filesNames, String[] filesPaths) {
        this.ctx = ctx;
        this.filesNames = filesNames;
        this.filesPaths = filesPaths;
    }

    @Override
    public int getCount() {
        return filesNames.length;
    }

    @Override
    public Object getItem(int pos) {
        return pos;
    }

    @Override
    public long getItemId(int pos) {
        return pos;
    }

    @Override
    public View getView(int p, View convertView, ViewGroup parent) {
        View grid;
        LayoutInflater inflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        if (convertView == null) {
            grid = inflater.inflate(R.layout.gridview_item, null);

            TextView textView = (TextView) grid.findViewById(R.id.gridview_text);
            ImageView imageView = (ImageView)grid.findViewById(R.id.gridview_image);
            textView.setText(filesNames[p]);

            Bitmap bmp = BitmapFactory.decodeFile(filesPaths[p]);
            imageView.setImageBitmap(bmp);
        } else {
            grid = (View) convertView;
        }
        return grid;
    }
}

Useful links

comments powered by Disqus