Mobile Zone is brought to you in partnership with:

http://www.technotalkative.com/about/ Paresh is a DZone MVB and is not an employee of DZone and has posted 20 posts at DZone. You can read more from them at their website. View Full User Profile

Be a Lazy but Productive Android Developer, Part 6: Staggered GridView

04.14.2014
| 3678 views |
  • submit to reddit

Welcome to the part 6 of “Be a lazy but a productive android developer” series. If you are a lazy android developer and looking for a library and there by a ready made code :) for displaying Staggered GridView in your android application then this article is gonna interesting for you!

This series so far:

  • Part 1: We looked at RoboGuice, a dependency injection library by which we can reduce the boiler plate code, save time and there by achieve productivity during Android app development.
  • Part 2: We saw and explored about Genymotion, which is a rocket speed emulator and super-fast emulator as compared to native emulator. And we can use Genymotion while developing apps and can quickly test apps and there by can achieve productivity.
  • Part 3: We understood and explored about JSON Parsing libraries (GSON and Jackson), using which we can increase app performance, we can decrease boilerplate code and there by can optimize productivity.
  • Part 4: We talked about Card UI and explored card library, also created a basic card and simple card list demo.
  • Part 5: We talked about libraries for loading images asynchronously, we did comparison of some libraries too, like Picasso, volley, Universal Image loader.

In this part

In this episode, we are going to talk about Staggered GridView and making app awesome by including it in our Android app. There are 2-3 open source libraries available, either of which we can use for displaying Staggered GridView into our application.

Staggered GridView

Staggered GridView is nothing but a GridView having multiple columns with rows of varying sizes. You might have used Pinterest, Expedia or Etsy android app where you have noticed this kind of Staggered GridView, if you haven’t used then just look at the below snaps to have exact idea.

staggered gridview in android

I might be sure you have either started thinking about programming behind it, well we lazy android developers don’t need to write a single line of code as there are already 2-3 awesome libraries (open-sourced) available, either of we can integrate and can get the same kind of awesome looking UI.

Staggered GridView Libraries

  1. Staggered GridView by Etsy
  2. Staggered GridView by Maurycy Wojtowicz

Let’s have a look into the Etsy’s staggered gridview as it’s more popular and widely used (1572 stars and 272 forks) in android app, as compared to maurycy’s staggered gridview (750 stars 411 forks).

Etsy’s Staggered GridView in Android

As per the library description, they have developed this customized view due to requirements for their Etsy android app not met by any existing android libraries. This library provides an ability to have a different number of columns in landscape & portrait, to sync grid position across orientation changes and support for headers & footers.

They have developed this customized Staggered GridView by extending AbsListView class and it supports AbsListView.OnScrollListener too.

Features

  • Configurable column count for portrait and landscape orientations.
  • Sync’d row position across orientation changes.
  • Configurable item margin.
  • Support for headers & footers.
  • Internal padding that does not affect the header & footer.
Etsy’s staggered gridview doesn’t support Item long press event and selector drawables, where as Maurycy’s staggered gridview supports item long press event.

Example: Etsy’s Staggered GridView

Output
technotalkative staggered gridview in android

Step 1: Download/Include library in project
This library is currently configured to be built via Gradle only, so if you are using either Android Studio then you can straight way include the library as a gradle aar dependency to your build. If you are still using Eclipse/Ant build then you have to perform extra steps.

For Android Studio:

repositories {
mavenCentral()
}
dependencies {
compile 'com.etsy.android.grid:library:x.x.x' // read below comment
}

// see CHANGELOG.md

For Eclipse/Ant build: Download this library project and import it into Eclipse as an existing project.

Step 2:Take StaggeredGridView inside your layout

<com.etsy.android.grid.StaggeredGridView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/grid_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:item_margin="8dp"
app:column_count="@integer/grid_column_count" />
<!-- define grid_column_count as '2' inside integers.xml file -->

Step 3: Define custom row layout for the StaggeredGridView
Custom row should either include DynamicHeightImageView or DynamicHeightTextView.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/panel_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"
android:orientation="horizontal" >
<com.etsy.android.grid.util.DynamicHeightImageView
android:id="@+id/imgView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
</FrameLayout>

Step 4: Define custom adapter class
Define a custom adapter class for displaying dynamic height image view inside staggered gridview. I have used Universal image loader library to load images asynchronously.

package com.technotalkative.staggeredgriddemo;
import java.util.ArrayList;
import java.util.Random;
import android.content.Context;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import com.etsy.android.grid.util.DynamicHeightImageView;
import com.nostra13.universalimageloader.core.ImageLoader;
public class SampleAdapter extends ArrayAdapter<String> {
private static final String TAG = "SampleAdapter";
private final LayoutInflater mLayoutInflater;
private final Random mRandom;
private static final SparseArray<Double> sPositionHeightRatios = new SparseArray<Double>();
public SampleAdapter(Context context, int textViewResourceId,
ArrayList<String> objects) {
super(context, textViewResourceId, objects);
this.mLayoutInflater = LayoutInflater.from(context);
this.mRandom = new Random();
}
@Override
public View getView(final int position, View convertView,
final ViewGroup parent) {
ViewHolder vh;
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.row_grid_item,
parent, false);
vh = new ViewHolder();
vh.imgView = (DynamicHeightImageView) convertView
.findViewById(R.id.imgView);
convertView.setTag(vh);
} else {
vh = (ViewHolder) convertView.getTag();
}
double positionHeight = getPositionRatio(position);
vh.imgView.setHeightRatio(positionHeight);
ImageLoader.getInstance().displayImage(getItem(position), vh.imgView);
return convertView;
}
static class ViewHolder {
DynamicHeightImageView imgView;
}
private double getPositionRatio(final int position) {
double ratio = sPositionHeightRatios.get(position, 0.0);
// if not yet done generate and stash the columns height
// in our real world scenario this will be determined by
// some match based on the known height and width of the image
// and maybe a helpful way to get the column height!
if (ratio == 0) {
ratio = getRandomHeightRatio();
sPositionHeightRatios.append(position, ratio);
Log.d(TAG, "getPositionRatio:" + position + " ratio:" + ratio);
}
return ratio;
}
private double getRandomHeightRatio() {
return (mRandom.nextDouble() / 2.0) + 1.0; // height will be 1.0 - 1.5
// the width
}
}

Step 5: Set defined custom adapter to StaggeredGridView

package com.technotalkative.staggeredgriddemo;
import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.Toast;
import com.etsy.android.grid.StaggeredGridView;
public class StaggeredGridActivity extends Activity implements AbsListView.OnScrollListener, AbsListView.OnItemClickListener {
private static final String TAG = "StaggeredGridActivity";
public static final String SAVED_DATA_KEY = "SAVED_DATA";
private StaggeredGridView mGridView;
private boolean mHasRequestedMore;
private SampleAdapter mAdapter;
private ArrayList<String> mData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sgv);
setTitle("TechnoTalkative - SGV Demo");
mGridView = (StaggeredGridView) findViewById(R.id.grid_view);
mAdapter = new SampleAdapter(this,android.R.layout.simple_list_item_1, generateData());
// do we have saved data?
if (savedInstanceState != null) {
mData = savedInstanceState.getStringArrayList(SAVED_DATA_KEY);
}
if (mData == null) {
mData = generateData();
}
for (String data : mData) {
mAdapter.add(data);
}
mGridView.setAdapter(mAdapter);
mGridView.setOnScrollListener(this);
mGridView.setOnItemClickListener(this);
}
@Override
protected void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
outState.putStringArrayList(SAVED_DATA_KEY, mData);
}
@Override
public void onScrollStateChanged(final AbsListView view, final int scrollState) {
Log.d(TAG, "onScrollStateChanged:" + scrollState);
}
@Override
public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount, final int totalItemCount) {
Log.d(TAG, "onScroll firstVisibleItem:" + firstVisibleItem +
" visibleItemCount:" + visibleItemCount +
" totalItemCount:" + totalItemCount);
// our handling
if (!mHasRequestedMore) {
int lastInScreen = firstVisibleItem + visibleItemCount;
if (lastInScreen >= totalItemCount) {
Log.d(TAG, "onScroll lastInScreen - so load more");
mHasRequestedMore = true;
onLoadMoreItems();
}
}
}
private void onLoadMoreItems() {
final ArrayList<String> sampleData = generateData();
for (String data : sampleData) {
mAdapter.add(data);
}
// stash all the data in our backing store
mData.addAll(sampleData);
// notify the adapter that we can update now
mAdapter.notifyDataSetChanged();
mHasRequestedMore = false;
}
private ArrayList<String> generateData() {
ArrayList<String> listData = new ArrayList<String>();
listData.add("http://i62.tinypic.com/2iitkhx.jpg");
listData.add("http://i61.tinypic.com/w0omeb.jpg");
listData.add("http://i60.tinypic.com/w9iu1d.jpg");
listData.add("http://i60.tinypic.com/iw6kh2.jpg");
listData.add("http://i57.tinypic.com/ru08c8.jpg");
listData.add("http://i60.tinypic.com/k12r10.jpg");
listData.add("http://i58.tinypic.com/2e3daug.jpg");
listData.add("http://i59.tinypic.com/2igznfr.jpg");
return listData;
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Toast.makeText(this, "Item Clicked: " + position, Toast.LENGTH_SHORT).show();
}
}

Download source code

You can download source code of above examples from here. Hope you liked this part of “Lazy android developer: Be productive” series. Till the next part, keep exploring both the staggered gridview libraries and try to customized it more. Enjoy!

Published at DZone with permission of Paresh Mayani, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)