Android Advance Image Gallery Example

JSON, Image url loading, nested image views, and PlaceHolderView

Before Moving on to this tutorial I would request you to go through the AndroidBeginner Image Gallery Example.

Android Advance Image Gallery Example

Objectives Outline for this tutorial:

  1. We would be building a list of images with two types of views. One on the top with horizontal image list and below with vertical image list and apply animations on these view items as shown in the above display image.
  2. We will be loading the images from urls and setting it in the view display. For this purpose we will be using a library Glide.
  3. The Urls list will be seeded in the application and this seed json file will be stored in the assets folder.
  4. The seed file will be parsed into Image object using another library gson.
  5. This structure will also be compatible if we are pulling url json data from a live server.

Let’s start building:

Step 1:

Set up the project in android studio with default activity.

In app’s build.gradle add the dependencies.

android {
    ...
    sourceSets {
        main {
            assets.srcDirs = ['src/main/assets', 'src/main/assets/']
            res.srcDirs = ['src/main/res', 'src/main/res/drawable']
        }
    }
}

dependencies {
    ...
    compile 'com.mindorks:placeholderview:0.7.1'
    compile 'com.android.support:cardview-v7:25.3.1'
    compile 'com.github.bumptech.glide:glide:3.7.0'
    compile 'com.google.code.gson:gson:2.7'
}

Notes:

  1. Add an assets folder in the main directory and point to it in gradle assets.srcDirs
  2. CardView is used to display the image in the list

Add Internet permission in the app’s AndroidManifest.xml

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

Step 2:

Create src/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <com.mindorks.placeholderview.PlaceHolderView
        android:id="@+id/galleryView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="vertical"/>
</LinearLayout>

Step 3:

Create src/layout/activity_item_big.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"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        app:cardCornerRadius="6dp"
        app:cardElevation="4dp">
        <ImageView
            android:id="@+id/imageView"
            android:layout_width="match_parent"
            android:layout_height="250dp"
            android:scaleType="centerCrop"
            android:src="@android:color/holo_orange_dark"/>
    </android.support.v7.widget.CardView>
</LinearLayout>

Step 4:

Create src/layout/activity_item_small.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"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <android.support.v7.widget.CardView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        app:cardElevation="3dp"
        app:cardCornerRadius="6dp">
        <ImageView
            android:id="@+id/imageView"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:scaleType="centerCrop"
            android:src="@android:color/holo_orange_dark"/>
    </android.support.v7.widget.CardView>
</LinearLayout>

Step 5:

Create src/layout/activity_item_small_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <com.mindorks.placeholderview.PlaceHolderView
        android:id="@+id/placeholderview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="vertical"/>
</LinearLayout>

Step 6:

Place images.json file in the assets folder created in the above step 1.

[
    {
        "url" : "http://www.allbesttop10.com/wp-content/uploads/2015/01/Venezuela-Beautiful-Girls.jpg"
    },
    {
        "url" : "http://hdwallpaperbackgrounds.net/wp-content/uploads/2015/10/Most-Beautiful-Girls-In-The-World-Wallpapers-1024x683.jpg"
    },
    {
        "url" : "http://photographyinspired.com/media/2014/01/Beautiful-Girl-Wallpapers.jpg"
    },
    {
        "url" : "http://img1.rnkr-static.com/list_img_v2/8192/1228192/C520/famous-men-you-d-want-to-have-a-beer-with-u2.jpg"
    },
    {
        "url" : "https://files.brightside.me/files/news/part_14/145555/1519055-650-1461578948-1.jpg"
    },
    {
        "url" : "http://fashion.lilithezine.com/images/Supermodel-Paris-Hilton.jpg"
    },
    {
        "url" : "http://tutzone.org/wp-content/uploads/2013/04/flirting-middle-shool.jpg"
    },
    {
        "url" : "http://photographyinspired.com/media/2014/01/Top-15-Portraits-of-Beautiful-Girls2.jpg"
    },
    {
        "url" : "http://cdn.playbuzz.com/cdn/67f930d0-8371-46d2-8867-08ade74e7ca0/518055f4-f558-4282-944f-1d30a86de875_560_420.jpg"
    },
    {
        "url" : "http://unbelievable.com/wp-content/uploads/2015/12/IMG_2583.jpg"
    },
    {
        "url" : "http://img2.izismile.com/img/img5/20120210/640/there_are_beautiful_girls_here_640_08.jpg"
    },
    {
        "url" : "http://3.bp.blogspot.com/-ZyV0-iKl24k/Tl9lB0p2U8I/AAAAAAAANTk/JaY0YbP0FWI/s1600/Super+Guns+HD+Wallpapers++%25285%2529.jpg"
    },
    {
        "url" : "https://s-media-cache-ak0.pinimg.com/736x/92/ba/b8/92bab89e660ac4f1a7b27cff4882ffe2.jpg"
    },
    {
        "url" : "http://cdn8.staztic.com/app/a/2552/2552630/hot-guns-super-hd-2-2-s-307x512.jpg"
    },
    {
        "url" : "http://www.nirapadnews.com/english/wp-content/uploads/2015/03/miranda-kerr-wonderbra-lingerie-2015-photos4-710x470.jpg"
    },
    {
        "url" : "https://cdn2.wtvox.com/wp-content/uploads/human-robots-e1433163724309.jpg"
    },
    {
        "url" : "http://s6.favim.com/610/151023/pretty-inspiration-inspirations-beautiful-Favim.com-3466352.jpg"
    },
    {
        "url" : "https://cnet4.cbsistatic.com/hub/i/2011/10/27/a66dfbb7-fdc7-11e2-8c7c-d4ae52e62bcc/android-wallpaper5_2560x1600_1.jpg"
    },
    {
        "url" : "http://pixel.nymag.com/imgs/fashion/daily/2016/01/20/20-clueless-male-read.w529.h352.jpg"
    },
    {
        "url" : "http://g01.a.alicdn.com/kf/HTB1f3DYHFXXXXarapXXq6xXFXXXH/European-And-American-Brands-Nb-Bra-Underwear-Lace-Sexy-Girls-Gather-Lady-Adjustable-Bra-Set.jpg"
    },
    {
        "url" : "http://1mhowto.com/wp-content/uploads/2014/12/Android.jpg"
    },
    {
        "url" : "https://lh4.googleusercontent.com/-9vNpxw3oiR8/UTBfHmBxKVI/AAAAAAAAAPw/Pi0j4ofE3mw/w800-h800/photo.jpg"
    }
]

Notes:

  1. This strategy is very useful in bundling app with seed files. Seed files contain data build in the app package and can be used to populate database or used to display default data to the user. Placing seed files in the form of json makes is extremely easy to parse into models.

Step 7:

Create Utils.java

public class Utils {

    private static final String TAG = "Utils";

    public static List<Image> loadImages(Context context){
        try{
            GsonBuilder builder = new GsonBuilder();
            Gson gson = builder.create();
            JSONArray array = new JSONArray(loadJSONFromAsset(context, "images.json"));
            List<Image> imageList = new ArrayList<>();
            for(int i=0;i<array.length();i++){
                Image image = gson.fromJson(array.getString(i), Image.class);
                imageList.add(image);
            }
            return imageList;
        }catch (Exception e){
            Log.d(TAG,"seedGames parseException " + e);
            e.printStackTrace();
            return null;
        }
    }

    private static String loadJSONFromAsset(Context context, String jsonFileName) {
        String json = null;
        InputStream is=null;
        try {
            AssetManager manager = context.getAssets();
            Log.d(TAG,"path "+jsonFileName);
            is = manager.open(jsonFileName);
            int size = is.available();
            byte[] buffer = new byte[size];
            is.read(buffer);
            is.close();
            json = new String(buffer, "UTF-8");
        } catch (IOException ex) {
            ex.printStackTrace();
            return null;
        }
        return json;
    }
}

Note:

  1. Utils contain the methods required to parse seed json file and also populate the model Image.java

Step 8:

Create model Image.java

public class Image {

    @SerializedName("url")
    @Expose
    private String imageUrl;

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }
}

Notes:

  1. @SerializedName annotation belongs to gson class and used to read json file variable and bind it to the model variable.
  2. @Expose is used to make the variable readable to the gson

Step 9:

We will now create the class to bind the item views and its operations.

Create ImageTypeBig.java

@Animate(Animation.ENTER_LEFT_DESC)
@NonReusable
@Layout(R.layout.gallery_item_big)
public class ImageTypeBig {

    @View(R.id.imageView)
    private ImageView imageView;

    private String mUlr;
    private Context mContext;
    private PlaceHolderView mPlaceHolderView;

    public ImageTypeBig(Context context, PlaceHolderView placeHolderView, String ulr) {
        mContext = context;
        mPlaceHolderView = placeHolderView;
        mUlr = ulr;
    }

    @Resolve
    private void onResolved() {
        Glide.with(mContext).load(mUlr).into(imageView);
    }

    @LongClick(R.id.imageView)
    private void onLongClick(){
        mPlaceHolderView.removeView(this);
    }
}

Notes:

  1. @NonReusable is annotation in PlaceHolderView, to be used in cases where we want to release all the resources and references if removed from the list and won’t use the same object in addView() method in PlaceHolderView.
  2. @layout is used to bind the layout with this class
  3. @View is used to bind the views in this layout we want to refer to
  4. @Resolve is used to operate on the view references obtained from @View, in short if we want to define any operation on the view it should be put in a method and annotated with @Resolve
  5. @Animate() is used to assign defined animations in the Animation class
  6. @LongClick is used to attach the long click listener and the item is removed from the list on event in this case

For detailed explanations view PlaceHolderView at GitHub repository

Step 10:

Create ImageTypeSmall.java

@Animate(Animation.CARD_TOP_IN_DESC)
@NonReusable
@Layout(R.layout.gallery_item_small)
public class ImageTypeSmall {

    @View(R.id.imageView)
    private ImageView imageView;

    private String mUlr;
    private Context mContext;
    private PlaceHolderView mPlaceHolderView;

    public ImageTypeSmall(Context context, PlaceHolderView placeHolderView, String ulr) {
        mContext = context;
        mPlaceHolderView = placeHolderView;
        mUlr = ulr;
    }

    @Resolve
    private void onResolved() {
        Glide.with(mContext).load(mUlr).into(imageView);
    }

    @LongClick(R.id.imageView)
    private void onLongClick(){
        mPlaceHolderView.removeView(this);
    }
}

Step 11:

Create ImageTypeSmallList.java

@Animate(Animation.CARD_TOP_IN_DESC)
@NonReusable
@Layout(R.layout.gallery_item_small_list)
public class ImageTypeSmallList {

    @View(R.id.placeholderview)
    private PlaceHolderView mPlaceHolderView;

    private Context mContext;
    private List<Image> mImageList;

    public ImageTypeSmallList(Context context, List<Image> imageList) {
        mContext = context;
        mImageList = imageList;
    }

    @Resolve
    private void onResolved() {
        mPlaceHolderView.getBuilder()
                .setHasFixedSize(false)
                .setItemViewCacheSize(10)
                .setLayoutManager(new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false));

        for(Image image : mImageList) {
            mPlaceHolderView.addView(new ImageTypeSmall(mContext, mPlaceHolderView, image.getImageUrl()));
        }
    }
}

Note:

  1. This class is required to create a nested list view
  2. The Nesting is done by using the PlaceHolderView inside main view
  3. The PlaceHolderView is instantiated and views are added to it.

Step 12:

Create MainActivity.java

public class MainActivity extends AppCompatActivity {

    private PlaceHolderView mGalleryView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mGalleryView = (PlaceHolderView)findViewById(R.id.galleryView);
        setupGallery();
    }

    private void setupGallery(){

        List<Image> imageList = Utils.loadImages(this.getApplicationContext());
        List<Image> newImageList = new ArrayList<>();
        for (int i = 0; i <  (imageList.size() > 10 ? 10 : imageList.size()); i++) {
            newImageList.add(imageList.get(i));
        }
        mGalleryView.addView(new ImageTypeSmallList(this.getApplicationContext(), newImageList));

        for (int i = imageList.size() - 1; i >= 0; i--) {
            mGalleryView.addView(new ImageTypeBig(this.getApplicationContext(), mGalleryView, imageList.get(i).getImageUrl()));
        }
    }
}

Notes:

  1. We obtain the instance of the PlaceHolderView and add views using the url from the model list.
  2. Top 10 images are added to top list
  3. Rest are added to bottom view in reverse order.

PlaceHolderView GitHub repository is here

The source code for this example is here

That’s All

Coders Rock!!