Освой программирование играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

GridView

Знакомьтесь - GridView
Базовый пример
GridView с картинками
GridView с картинками и пояснительным текстом
Убрать вертикальную прокрутку
Галерея

Знакомьтесь - GridView

Компонет GridView представляет собой плоскую таблицу. Для GridView можно использовать собственные поля для отображения элементов данных, создав класс, производный от класса ArrayAdapter или BaseAdapter и т.п, и переопределив его метод getView().

Находится в разделе Containers.

Число столбцов для GridView чаще задается статически. Число строк в элементе определяется динамически на основании числа элементов, которые предоставляет адаптер.

Свойства

  • android:numColumns — определяет количество столбцов. Если поставлено значение auto_fit, то система вычислит количество столбцов, основанное на доступном пространстве
  • android:verticalSpacing — устанавливает размер пустого пространства между ячейками таблицы
  • android:columnWidth — устанавливает ширину столбцов
  • android:stretchMode — указывает, куда распределяется остаток свободного пространства для таблицы с установленным значением android:numColumns="auto_fit". Принимает значения columnWidth для paспределения остатка свободного пространства между ячейками столбцов для их увеличения или spacingWidth — для увеличения пространства между ячейками

Базовый пример

Если поместить GridView на форму, то увидим следующую картину.

GridView

Внесём небольшие правки


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/info"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

    <GridView
        android:id="@+id/gridView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:columnWidth="100dp"
        android:gravity="center"
        android:horizontalSpacing="5dp"
        android:numColumns="auto_fit"
        android:stretchMode="columnWidth"
        android:verticalSpacing="35dp" >
    </GridView>

</LinearLayout>

В коде реализуем наполнение таблицы через адаптер. Создадим новый файл DataAdapter.java:


package ru.alexanderklimov.gridview;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class DataAdapter extends ArrayAdapter<String> {

	private static final String[] mContacts = { "Рыжик", "Барсик", "Мурзик",
			"Мурка", "Васька", "Полосатик", "Матроскин", "Лизка", "Томосина",
			"Бегемот", "Чеширский", "Дивуар", "Тигра", "Лаура" };

	Context mContext;

	// Конструктор
	public DataAdapter(Context context, int textViewResourceId) {
		super(context, textViewResourceId, mContacts);
		// TODO Auto-generated constructor stub
		this.mContext = context;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub

		TextView label = (TextView) convertView;

		if (convertView == null) {
			convertView = new TextView(mContext);
			label = (TextView) convertView;
		}
		label.setText(mContacts[position]);
		return (convertView);
	}

	// возвращает содержимое выделенного элемента списка
	public String getItem(int position) {
		return mContacts[position];
	}

}

Теперь напишем код для основного окна приложения. Как и у ListView, нам нужно использовать метод setAdapter(), а также методы setOnItemSelectedListener(), onItemSelected(), onNothingSelected().


package ru.alexanderklimov.gridview;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.GridView;
import android.widget.TextView;

public class GridViewDemoActivity extends Activity implements
		AdapterView.OnItemSelectedListener {

	private TextView mSelectText;
	private DataAdapter mAdapter;

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

		mSelectText = (TextView) findViewById(R.id.info);
		final GridView g = (GridView) findViewById(R.id.gridView1);
		mAdapter = new DataAdapter(getApplicationContext(),
				android.R.layout.simple_list_item_1);
		g.setAdapter(mAdapter);
		g.setOnItemSelectedListener(this);
		g.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View v,
					int position, long id) {
				// TODO Auto-generated method stub
				mSelectText.setText("Выбранный элемент: "
						+ mAdapter.getItem(position));
			}
		});
	}

	@Override
	public void onItemSelected(AdapterView<?> parent, View v, int position,
			long id) {
		mSelectText.setText("Выбранный элемент: " + mAdapter.getItem(position));
	}

	@Override
	public void onNothingSelected(AdapterView<?> parent) {
		mSelectText.setText("Выбранный элемент: ничего");
	}
}

Запустите проект и начинайте выбирать любой элемент - его название отобразится в текстовой метке в верхней части экрана. Я обратил внимание, что в эмуляторе с помощью джойстика можно выбрать нужный элемент, но в современных телефонах джойстика нет, поэтому я позже добавил метод setOnItemClickListener(), чтобы можно было щёлкать по элементам в GridView и выводить их названия в метке.

GridView с картинками

Вместо текста можно использовать и картинки. Немного модифицируем проект. В шаблоне разметки изменим GridView:


<GridView
    android:id="@+id/gridView1"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:columnWidth="90dp"
    android:gravity="center"
    android:horizontalSpacing="10dp"
    android:numColumns="auto_fit"
    android:stretchMode="columnWidth"
    android:verticalSpacing="10dp" >
</GridView>

Создадим новый адаптер ImageAdapter.java, в котором будет возможность подключать картинки


package ru.alexanderklimov.gridview;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;

public class ImageAdapter extends BaseAdapter {
	private Context mContext;

	public ImageAdapter(Context c) {
		mContext = c;
	}

	public int getCount() {
		return mThumbIds.length;
	}

	public Object getItem(int position) {
		return mThumbIds[position];
	}

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

	// create a new ImageView for each item referenced by the Adapter
	public View getView(int position, View convertView, ViewGroup parent) {
		ImageView imageView;
		if (convertView == null) {
			// if it's not recycled, initialize some attributes
			imageView = new ImageView(mContext);
			imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
			imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
			imageView.setPadding(8, 8, 8, 8);
		} else {
			imageView = (ImageView) convertView;
		}

		imageView.setImageResource(mThumbIds[position]);
		return imageView;
	}

	// references to our images
	public	Integer[] mThumbIds = { R.drawable.card1, R.drawable.card2,
			R.drawable.card3, R.drawable.card4, R.drawable.card5,
			R.drawable.card6, R.drawable.card7, R.drawable.card8,
			R.drawable.card9, R.drawable.card10, R.drawable.card11,
			R.drawable.card12, R.drawable.card13, R.drawable.card14,
			R.drawable.card15, R.drawable.card16, R.drawable.card17,
			R.drawable.card18, R.drawable.card19, R.drawable.card20,
			R.drawable.card21 };
}

Теперь код в основной активности:


package ru.alexanderklimov.gridview;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.TextView;

public class GridViewDemoActivity extends Activity {

	private TextView mSelectText;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		mSelectText = (TextView) findViewById(R.id.info);
		GridView gridview = (GridView) findViewById(R.id.gridView1);
		gridview.setAdapter(new ImageAdapter(this));

		gridview.setOnItemClickListener(gridviewOnItemClickListener);
	}

	private GridView.OnItemClickListener gridviewOnItemClickListener = new GridView.OnItemClickListener() {

		@Override
		public void onItemClick(AdapterView<?> parent, View v, int position,
				long id) {
			// TODO Auto-generated method stub
			// выводим номер позиции
			mSelectText.setText(String.valueOf(position));
		}
	};
}

Результат ниже

GridView

Зная номер позиции можно доработать программу, чтобы при щелчке на картинке, она открывалась на весь экран. Давайте так и сделаем. Создадим новый XML-файл разметки в папке res/layout под именем full_image.xml:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/full_image_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</LinearLayout>

Создадим новую активность, в которой будет выводиться изображение на весь экран (файл FullImageActivity.java):


package ru.alexanderklimov.gridview;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.ImageView;

public class FullImageActivity extends Activity {
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.full_image);

		// get intent data
		Intent intent = getIntent();

		// Selected image id
		int position = intent.getExtras().getInt("id");
		ImageAdapter imageAdapter = new ImageAdapter(this);

		ImageView imageView = (ImageView) findViewById(R.id.full_image_view);
		imageView.setImageResource(imageAdapter.mThumbIds[position]);
	}
}

Класс получает от намерения номер позиции и выводит по этому номеру изображение из ресурсов.

Теперь в основной активности модифицируем код для щелчка


private GridView.OnItemClickListener gridviewOnItemClickListener = new GridView.OnItemClickListener() {
	@Override
	public void onItemClick(AdapterView<?> parent, View v, int position,
			long id) {
		// TODO Auto-generated method stub
		
		// Sending image id to FullScreenActivity
		Intent i = new Intent(getApplicationContext(),
				FullImageActivity.class);
		// passing array index
		i.putExtra("id", position);
		startActivity(i);
	}
};

Осталось добавить в манифест новую активность:


<activity android:name=".FullImageActivity"></activity>

У нас получилась галерея с просмотром отдельной картинки.

GridView с картинками и пояснительным текстом

Модифицируем предыдущий пример и создадим сетку, состоящую из картинок с сопроводительным текстом внизу.

Можно было оставить предыдущую разметку для активности, но я решил чуть её изменить, убрав лишние элементы


<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/gridview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:columnWidth="90dp"
    android:gravity="center"
    android:horizontalSpacing="10dp"
    android:numColumns="auto_fit"
    android:stretchMode="columnWidth"
    android:verticalSpacing="10dp" />

Теперь создадим разметку для отдельной ячейки сетки - нам нужны ImageView и TextView:

res/layout/cellgrid.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/imagepart"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/textpart"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

Создадим новый класс ImageTextAdapter. Он практически не отличается от класса ImageAdapter, поменялся только метод getView(), разницу в коде я закоментировал для сравнения


package ru.alexanderklimov.gridview;

import ...

public class ImageTextAdapter extends BaseAdapter {
	private Context mContext;

	public ImageTextAdapter(Context c) {
		mContext = c;
	}

	public int getCount() {
		return mThumbIds.length;
	}

	public Object getItem(int position) {
		return mThumbIds[position];
	}

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

	// create a new ImageView for each item referenced by the Adapter
	// public View getView(int position, View convertView, ViewGroup parent) {
	// ImageView imageView;
	// if (convertView == null) {
	// // if it's not recycled, initialize some attributes
	// imageView = new ImageView(mContext);
	// imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
	// imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
	// imageView.setPadding(8, 8, 8, 8);
	// } else {
	// imageView = (ImageView) convertView;
	// }
	//
	// imageView.setImageResource(mThumbIds[position]);
	// return imageView;
	// }

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub

		View grid;

		if (convertView == null) {
			grid = new View(mContext);
			//LayoutInflater inflater = getLayoutInflater();
			LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
			grid = inflater.inflate(R.layout.cellgrid, parent, false);
		} else {
			grid = (View) convertView;
		}

		ImageView imageView = (ImageView) grid.findViewById(R.id.imagepart);
		TextView textView = (TextView) grid.findViewById(R.id.textpart);
		imageView.setImageResource(mThumbIds[position]);
		textView.setText("Картинка " + String.valueOf(position));

		return grid;
	}

	// references to our images
	public Integer[] mThumbIds = { R.drawable.card1, R.drawable.card2,
			R.drawable.card3, R.drawable.card4, R.drawable.card5,
			R.drawable.card6, R.drawable.card7, R.drawable.card8,
			R.drawable.card9, R.drawable.card10, R.drawable.card11,
			R.drawable.card12, R.drawable.card13, R.drawable.card14,
			R.drawable.card15, R.drawable.card16, R.drawable.card17,
			R.drawable.card18, R.drawable.card19, R.drawable.card20,
			R.drawable.card21 };
}

Осталось вызвать нужный адаптер в активности:


package ru.alexanderklimov.gridview;

import ...

public class GridViewDemoActivity extends Activity {

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		GridView gridview = (GridView) findViewById(R.id.gridview);
		gridview.setAdapter(new ImageTextAdapter(this));

		gridview.setOnItemClickListener(gridviewOnItemClickListener);
	}

	private GridView.OnItemClickListener gridviewOnItemClickListener = new GridView.OnItemClickListener() {

		@Override
		public void onItemClick(AdapterView<?> parent, View v, int position,
				long id) {
			// TODO Auto-generated method stub
			
			// Sending image id to FullScreenActivity
			Intent i = new Intent(getApplicationContext(),
					FullImageActivity.class);
			// passing array index
			i.putExtra("id", position);
			startActivity(i);
		}
	};
}

Получим результат:

GridView

Убрать вертикальную прокрутку

Прочитал заметку про убирание вертикальной прокрутки, которая возникает при движении пальцем вверх. Может пригодится кому-то:


gridview.setOnTouchListener(new OnTouchListener(){
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_MOVE){
            return true;
        }
        return false;
    }
});

Галерея

Рассмотрим вариант создания галереи на основе GridView.

Создаём новый проект. Также нужно подготовить фотографии для галереи, которые следует поместить в папку res/drawable-hdpi.

Поместим на главном экране приложения GridView:


<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/grid_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:columnWidth="90dp"
    android:gravity="center"
    android:horizontalSpacing="10dp"
    android:numColumns="auto_fit"
    android:stretchMode="columnWidth"
    android:verticalSpacing="10dp" >

</GridView>

Создадим новый класс ImageAdapter.java, наследующий от BaseAdapter, для размещения изображений в сетке GridView через собственный адаптер.


package ru.alexanderklimov.gridview;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;

public class ImageAdapter extends BaseAdapter {

	private Context mContext;

	// Keep all Images in array
	public Integer[] mThumbIds = { R.drawable.photo1, R.drawable.photo2,
			R.drawable.photo3, R.drawable.photo4, R.drawable.photo5,
			R.drawable.photo6, R.drawable.photo7, R.drawable.photo8,
			R.drawable.photo9, R.drawable.photo10, R.drawable.photo11,
			R.drawable.photo12, R.drawable.photo13, R.drawable.photo14,
			R.drawable.photo15 };

	// Constructor
	public ImageAdapter(Context c) {
		mContext = c;
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return mThumbIds.length; // длина массива
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return mThumbIds[position];
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		ImageView imageView = new ImageView(mContext);
		imageView.setImageResource(mThumbIds[position]);
		imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
		imageView.setLayoutParams(new GridView.LayoutParams(120, 110));
		return imageView;
	}
}

Открываем основной класс приложения и связываем через созданный адаптер изображения с GridView:


package ru.alexanderklimov.gridview;

import android.app.Activity;
import android.os.Bundle;
import android.widget.GridView;

public class GridLayoutDemoActivity extends Activity {
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

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

		// устанавливаем адаптер через экземпляр класса ImageAdapter
		gridView.setAdapter(new ImageAdapter(this));
	}
}

Запустим проект и проверим, что всё отображается нормально.

Галерея

Не обращайте внимания, что картинки на скриншоте повторяются, просто было лень готовить пятнадцать разных фотографий.

На этом урок можно было закончить, но давайте доработаем приложение, чтобы выбранная картинка отображалась в полном размере на весь экран. Для этого нужно передать идентификатор выбранного изображения новой активности.

Создадим в папке layout файл разметки full_image.xml для этой цели.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/full_image_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</LinearLayout>

Создадим новый класс FullImageActivity.java для активности, которая будет отображать картинку на весь экран. Активность через намерение будет получать идентификатор картинки и выводить её на экран.


package ru.alexanderklimov.gridlayout;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.ImageView;

public class FullImageActivity extends Activity {

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.full_image);

		// get intent data
		Intent intent = getIntent();

		// Selected image id
		int position = intent.getExtras().getInt("id");
		ImageAdapter imageAdapter = new ImageAdapter(this);

		ImageView imageView = (ImageView) findViewById(R.id.full_image_view);
		imageView.setImageResource(imageAdapter.mThumbIds[position]);
	}
}

Не забываем прописать новый класс в манифесте проекта.

Возвращаемся к главной активности и добавляем обработчик щелчков на сетке:


package ru.alexanderklimov.gridlayout;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.GridView;
import android.view.View;

public class GridLayoutDemoActivity extends Activity {
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

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

		// устанавливаем адаптер через экземпляр класса ImageAdapter
		gridView.setAdapter(new ImageAdapter(this));

		gridView.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View v,
					int position, long id) {

				// посылаем идентификатор картинки в FullScreenActivity
				Intent i = new Intent(getApplicationContext(),
						FullImageActivity.class);
				// передаем индекс массива
				i.putExtra("id", position);
				startActivity(i);
			}
		});
	}
}

Снова запускаем проект и щёлкаем по любой миниатюре - должен запуститься новый экран с картинкой во весь рост. Теперь можно разглядеть кота получше.

Галерея Галерея

Дополнительное чтение

Продвинутые приёмы работы с GridView (закрытая зона/2-й месяц)

Загружаем картинки из MediaStore через свой SimpleCursorAdapter

Библиотеки

AndroidStaggeredGrid - позволяет использовать сетку разной высоты. Описание от автора библиотеки

Реклама