Руководство Android GridView

1- Что такое GridView?

GridView это viewgroup, который показывает элеметы в двумерной сетке.

1.1- GridItem

GridView создан из списка  GridItem. GridItem это отдельная ячейка (cell) в gridview там где будут показаны данные. Любые данные в gridview показываются только через griditem.
GridItem это часть интерфейса, который может быть создан некоторыми  View.
Android создает некоторые формы  GridItem, они называются предопределенным  Layout, я упомяну о них в примерах этой статьи.

1.2- Adapter

Android Adapter (Адаптер) это мост соединяющий  View и базовые данные этого  ViewAdapter контролирует данные и адаптирует данные к отдельным ячейкам (GridItems)  view.

Вы можете привязать  Adapter к  GridView используя метод  setAdapter. Теперь, пoсмотрим как работает  Adapter с помощью следующих изображений.

1.3- GridView Selector

Чтобы лучше украсить  GridView, вам нужно персонализировать эффекты, такие как изменение фонового цвета GridItem когда курсор переменяется на нет или изменить цвет фона когда выбран GridItem. Можете посмотреть пример персонализации  GridView Selector в конце данной статьи.

2- Базовый GridView используя ArrayAdapter

2.1- ArrayAdapter

ArrayAdapter используется для отображения  GridView с простыми  GridItem, GridItem можно создать только из  TextView, CheckedTextView, EditText,...

Если вы хотите  GridView со более сложным  GridItem, вы можете сами создать кастомизированный  Adapter.

2.2- Пример GridView с ArrayAdapter

Создать Android project с названием  SimpleGridView.
Дизайн интерфейса:
Изменить настройки:
GridView
  • columnWidth: 120
  • gravity
    • center: <check>
  • numColumns: auto_fit
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="org.o7planning.simplegridview.MainActivity">

    <GridView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/gridView"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:columnWidth="120dp"
        android:gravity="center"
        android:numColumns="auto_fit" />


</RelativeLayout>
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 android.support.v7.app.AppCompatActivity;
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 {

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

        final GridView 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();
            }
        });
    }

}
Запуск примера:

2.3- Готовые Layout для работы с ArrayAdapter

Android создал некоторые  Layout (для  GridItem, ListItem,..) которые могут работать с  ArrayAdapter.

android.R.layout.simple_list_item_1

  • Это простой layout GridItem, созданный единственным TextView (Смотрите примеры выше).

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

  • 2 Layout выше это простые layout чтобы создать GridView с GridItem имеющий checkbox.
  • TODO

3- Кастомизировать GridView используя BaseAdapter

Вы можете создать кастомизированный  GridViewer. Ваш  Adapter нужно расширить из класса  BaseAdapter.

3.1- Пример кастомизированного GridView 

Создать  "Empty Activity" project с названием  CustomGridView.
Предварительный просмотр приложения:
Приготовить несколько изображений:
Копировать и вставить изображения в папку  mipmap:
  1. vi.png 
  2. us.png
  3. ru.png
  4. jp.png
  5. au.png
Вам нужно создать  layout для Griditem. На  Android Studio нажмите на правую мышь на  res/layout и выберите:
  • New/Layout resource file
Ввод:
  • File name: grid_item_layout.xml
  • Root element: RelativeLayout
Дизайн интерфейса GridItem.
Slider шаги дизайна интерфейса для  GridItem:
  • SLIDER
ImageView
  • ID: imageView_flag

TextView 1:
  • ID: textView_countryName
  • Text: Country Name

TextView 2:
  • ID: textView_population
  • Text: Population ....
grid_item_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <ImageView
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:id="@+id/imageView_flag"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:layout_marginTop="5dp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="Country Name"
        android:id="@+id/textView_countryName"
        android:layout_alignTop="@+id/imageView_flag"
        android:layout_toRightOf="@+id/imageView_flag"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:text="Population.."
        android:id="@+id/textView_population"
        android:layout_below="@+id/textView_countryName"
        android:layout_alignLeft="@+id/textView_countryName"
        android:layout_alignStart="@+id/textView_countryName"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true" />

</RelativeLayout>
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+")";
   }
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
   android:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   tools:context="org.o7planning.customgridview.MainActivity">

   <GridView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:id="@+id/gridView"
       android:layout_alignParentTop="true"
       android:layout_centerHorizontal="true"
       android:columnWidth="120dp"
       android:gravity="center"
       android:numColumns="auto_fit" />


</RelativeLayout>
CustomGridAdapter это класс расширенный из  BaseAdapter, выполняет обязанность отображать данные на Grid Item.
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 android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.support.v7.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;
   }

}
Запуск приложения:

3.2- Пример кастомизираванного Selector

Чтобы улучшить  GridView, вам нужно кастомизировать эффекты, например при изменении цвета фона GridItem при движении курсора или при выборе GridItem. Продолжим с примером.
Создать конфигурационные файлы:
  • File name: item_state_normal.xml
  • Directory: drawable
Индентично создайте другие 3 файла:
  • item_state_pressed.xml
  • item_state_selected.xml
  • list_selector.xml
Когда Grid Item находится в обычном состоянии, стили (style) настроенные в  item_state_normal.xml будт применены к 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>
При нажатии на Grid Item стили настроенные в  item_state_pressed.xml будут применены к 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>
Когда GridItem выбирает  style (стили) настройки в  item_state_selected.xml будут применены к 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>
Сопоставить определенные статусы  GridItem с вышеуказанными файлами xml.
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>
Настроить  ListSelector для  GridView:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
   android:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   tools:context="org.o7planning.customgridview.MainActivity">

   <GridView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:id="@+id/gridView"
       android:layout_alignParentTop="true"
       android:layout_centerHorizontal="true"
       android:columnWidth="120dp"
       android:gravity="center"
       android:numColumns="auto_fit"
       android:listSelector="@drawable/list_selector" />


</RelativeLayout>
Перезапустить приложение.