Android GridView | o7planning.org

1- What is GridView?

GridView is a view group that shows items in two-dimensional scrolling grid.

1.1- GridItem

An Android GridView is made from a group of GridItem(s). GridItems are individual cels in gridview where the data will be displayed. Any data in gridview is displayed only through griditem.

A Griditem is a piece of the interface which can be created by a number of View.

Android builds some several different Grid Item forms, called the pre-defined layout, which will be mentioned in the examples of this document.

1.2- Adapter

Android Adapter is a bridge between the View and the underlying data for that view. An adapter manages the data and adapts the data to the individual cell (gridItems) of the view.

You can bind the adapter with Android gridview via setAdapter method. Now, Let us see how Adapter works with the help of the following image.

1.3- GridView Selector

To make GridView become more beautiful, you need to customize the effects, such as changing the background color of GridItem when cursor moves over it, or change background color when it is selected. You can see an example for customizing GridView Selector in the end of this document.

2- Basic GridView using ArrayAdapter

2.1- ArrayAdapter

ArrayAdapter used to display the GridView  with simple GridItem, GridItem can be made from only one TextView, CheckedTextView, EditText, …

In case where you want to have a GridView with more complex  GridItem, you can manually create a customized Adapter.

2.2- GridView with ArrayAdapter example

Create Android project named SimpleGridView.

  • File > New > New Project > Empty Activity
    • Name: SimpleGridView
    • Package name: org.o7planning.simplegridview
    • Language: Java

Design the UI:


Change GridView properties:

GridView

  • android:numColumns =”2″
  • android:stretchMode =”columnWidth”

activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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"
    tools:context=".MainActivity">

    <GridView
        android:id="@+id/gridView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="16dp"
        android:numColumns="2"
        android:stretchMode="columnWidth"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Website.java


package org.o7planning.simplegridview;

public class Website {

    private String name;
    private String url;

    public Website(String name, String url)  {
        this.name= name;
        this.url= url;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString()  {
        return name;
    }
}

MainActivity.java


package org.o7planning.simplegridview;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

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

        this.gridView = (GridView)findViewById(R.id.gridView);

        //

        Website o7planning = new Website("o7planning","http://o7planning.org");
        Website google = new Website("Google","http://google.com");
        Website facebook = new Website("Facebook","http://facebook.com");
        Website eclipse = new Website("Eclipse","http://eclipse.org");
        Website yahoo = new Website("Yahoo","http://yahoo.com");

        Website[] websites = new Website[]{o7planning,google, facebook,eclipse, yahoo};

        // android.R.layout.simple_list_item_1 is a constant predefined layout of Android.
        // used to create a GridView with simple GridItem (Only one TextView).

        ArrayAdapter<Website> arrayAdapter
                = new ArrayAdapter<Website>(this, android.R.layout.simple_list_item_1 , websites);


        gridView.setAdapter(arrayAdapter);

        // When the user clicks on the GridItem
        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> a, View v, int position, long id) {
                Object o = gridView.getItemAtPosition(position);
                Website website = (Website) o;
                Toast.makeText(MainActivity.this, "Selected :" + " " + website.getName()+"\n("+ website.getUrl()+")",
                        Toast.LENGTH_LONG).show();
            }
        });
    }

}

Running the example:

2.3- The layouts is available to work with ArrayAdapter

Android built some Layout (for Grid Item, List Item, ..) can work with ArrayAdapter.

android.R.layout.simple_list_item_1

  • This is a simple layout of GridItem, created by single TextView (You can see examples above).

android.R.layout.simple_list_item_checked & android.R.layout.simple_list_item_multiple_choice

  • Two Layout above are the simple layouts to create a GridView with GridItem created by single checkbox.
  • TODO

3- Customizing GridView using BaseAdapter

You can customize a GridView. Your Adapter should extend from BaseAdapter class.

3.1- Custom GridView example

Create new project named CustomGridView.

  • File > New > New Project > Empty Activity
    • Name: CustomGridView
    • Package name: org.o7planning.customgridview
    • Language: Java

Preview application:

Firstly you need to prepare some images:

Copy and paste the image file into the mipmap folder:





vn.png
us.png
ru.png
jp.png
au.png 



You need to create a layout for griditem. In Android Studio right-click the res/layout and selecte:

  • New/Layout resource file

Enter:

  • File name: grid_item_layout.xml
  • Root element: androidx.constraintlayout.widget.ConstraintLayout


Interface Design for Grid-Item.

Slider, interface design steps for GridItem:


  • ID: imageView_flag

TextView 1:

  • ID: textView_countryName
  • Text: Country Name

TextView 2:

  • ID: textView_population
  • Text: Population ….

ImageViewTextView 1:TextView 2:

grid_item_layout.xml


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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">

    <ImageView
        android:id="@+id/imageView_flag"
        android:layout_width="80dp"
        android:layout_height="60dp"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/ic_launcher_foreground" />

    <TextView
        android:id="@+id/textView_countryName"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:text="Country Name"
        android:textSize="18sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/imageView_flag"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView_population"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="3dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:text="Population"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/imageView_flag"
        app:layout_constraintTop_toBottomOf="@+id/textView_countryName" />
</androidx.constraintlayout.widget.ConstraintLayout>

activity_main.xml


GridView

  • android:numColumns =”2″
  • android:stretchMode =”columnWidth”

activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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"
    tools:context=".MainActivity">

    <GridView
        android:id="@+id/gridView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="8dp"
        android:numColumns="2"
        android:stretchMode="columnWidth"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

CustomGridAdapter is a class extending from BaseAdapter, it is used to display data on the GridItem.

Country.java


package org.o7planning.customgridview;

public class Country {

    private String countryName;

    // Image name (Without extension)
    private String flagName;
    private int population;

    public Country(String countryName, String flagName, int population) {
        this.countryName= countryName;
        this.flagName= flagName;
        this.population= population;
    }

    public int getPopulation() {
        return population;
    }

    public void setPopulation(int population) {
        this.population = population;
    }

    public String getCountryName() {
        return countryName;
    }

    public void setCountryName(String countryName) {
        this.countryName = countryName;
    }

    public String getFlagName() {
        return flagName;
    }

    public void setFlagName(String flagName) {
        this.flagName = flagName;
    }

    @Override
    public String toString()  {
        return this.countryName+" (Population: "+ this.population+")";
    }
}

CustomGridAdapter.java


package org.o7planning.customgridview;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;

public class CustomGridAdapter  extends BaseAdapter {

    private List<Country> listData;
    private LayoutInflater layoutInflater;
    private Context context;

    public CustomGridAdapter(Context aContext,  List<Country> listData) {
        this.context = aContext;
        this.listData = listData;
        layoutInflater = LayoutInflater.from(aContext);
    }

    @Override
    public int getCount() {
        return listData.size();
    }

    @Override
    public Object getItem(int position) {
        return listData.get(position);
    }

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

    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            convertView = layoutInflater.inflate(R.layout.grid_item_layout, null);
            holder = new ViewHolder();
            holder.flagView = (ImageView) convertView.findViewById(R.id.imageView_flag);
            holder.countryNameView = (TextView) convertView.findViewById(R.id.textView_countryName);
            holder.populationView = (TextView) convertView.findViewById(R.id.textView_population);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        Country country = this.listData.get(position);
        holder.countryNameView.setText(country.getCountryName());
        holder.populationView.setText("" + country.getPopulation());

        int imageId = this.getMipmapResIdByName(country.getFlagName());

        holder.flagView.setImageResource(imageId);

        return convertView;
    }

    // Find Image ID corresponding to the name of the image (in the directory mipmap).
    public int getMipmapResIdByName(String resName)  {
        String pkgName = context.getPackageName();

        // Return 0 if not found.
        int resID = context.getResources().getIdentifier(resName , "mipmap", pkgName);
        Log.i("CustomGridView", "Res Name: "+ resName+"==> Res ID = "+ resID);
        return resID;
    }

    static class ViewHolder {
        ImageView flagView;
        TextView countryNameView;
        TextView populationView;
    }

}

ActivityMain.java


package org.o7planning.customgridview;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

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


        List<Country> image_details = getListData();
        final GridView gridView = (GridView) findViewById(R.id.gridView);
        gridView.setAdapter(new CustomGridAdapter(this, image_details));

        // When the user clicks on the GridItem
        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> a, View v, int position, long id) {
                Object o = gridView.getItemAtPosition(position);
                Country country = (Country) o;
                Toast.makeText(MainActivity.this, "Selected :"
                        + " " + country, Toast.LENGTH_LONG).show();
            }
        });
    }

    private  List<Country> getListData() {
        List<Country> list = new ArrayList<Country>();
        Country vietnam = new Country("Vietnam", "vn", 98000000);
        Country usa = new Country("United States", "us", 320000000);
        Country russia = new Country("Russia", "ru", 142000000);
        Country australia = new Country("Australia", "au", 23766305);
        Country japan = new Country("Japan", "jp", 126788677);

        list.add(vietnam);
        list.add(usa);
        list.add(russia);
        list.add(australia);
        list.add(japan);

        return list;
    }

}

Running the app:

3.2- Custom Selector example

To make GridView become more beautiful, you need to customize the effects, such as changing the background color of GridItem when cursor moves over it, or change background color when it is selected. We continue with the example above.
 

Create the configuration files:

  • File name: item_state_normal.xml
  • Directory: drawable

Similarly create three different files:

  • item_state_pressed.xml
  • item_state_selected.xml
  • list_selector.xml


When the grid item in a normal state, styles which is set up in item_state_normal.xml will be applied to the GridItem.

item_state_normal.xml


<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:shape="rectangle">

  <gradient
      android:startColor="#f1f1f2"
      android:centerColor="#e7e7e8"
      android:endColor="#cfcfcf"
      android:angle="270" />

</shape>

When Grid Item is pressed, the styles set up in item_state_pressed.xml  will be applied to the GridItem

item_state_pressed.xml


<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:shape="rectangle">

  <gradient
      android:startColor="#18d7e5"
      android:centerColor="#16cedb"
      android:endColor="#09adb9"
      android:angle="270" />

</shape>

When Grid Item is selected, the styles set up in item_state_selected.xml  will be applied to the GridItem

item_state_selected.xml


<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android"
   android:shape="rectangle">

   <gradient
       android:startColor="#18d7e5"
       android:centerColor="#16cedb"
       android:endColor="#09adb9"
       android:angle="270" />

</shape>

Mapping the specific status of the ListItem with xml files.

list_selector.xml


<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">

       <item
           android:state_selected="false"
           android:state_pressed="false"
           android:drawable="@drawable/item_state_normal" />

       <item android:state_pressed="true"
           android:drawable="@drawable/item_state_pressed" />

       <item android:state_selected="true"
           android:state_pressed="false"
           android:drawable="@drawable/item_state_selected" />


</selector>

Set ListSelector for GridView:


<GridView
     ...
     android:listSelector="@drawable/list_selector" />

activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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"
    tools:context=".MainActivity">

    <GridView
        android:id="@+id/gridView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="8dp"
        android:numColumns="2"
        android:stretchMode="columnWidth"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:listSelector="@drawable/list_selector" />

</androidx.constraintlayout.widget.ConstraintLayout>

Rerun your application: