Advertisement
  1. Code
  2. Android SDK

Android SDK: Продвинутая галерея для изображений

Scroll to top
Read Time: 14 min

() translation by (you can also view the original English article)

С помощью Android Intents и Gallery View, можно разрешить пользователю выбирать изображения на их устройстве. В этом уроке мы объединим выбор изображений с Gallery View, который мы немного улучшим создав интерактивную функцию для показа выбранных пользователем изображений. Если вы создавали хотя бы одно приложение для Android, вы вполне справитесь со всеми этапами урока.

Приложение, которое мы создадим в этом уроке будет отображать прокручиваемую ленту изображений, с помощью Android Gallery View. Пользователь сможет импортировать изображения нажав на объект в галерее, которые будут взяты из галереи по-умолчанию или с помощью файлового менеджера. Когда изображение выбрано, мы обработаем его до того как показать, поэтому мы не задействуем лишние ресурсы памяти. При нажатии на эскиз, приложение покажет выбранное изображение в увеличенном виде.

Шаг 1: Создание проекта Android

Запустите новый проект в Eclipse. В main Activity классе вашего приложения добавьте в начале следующие операторы импорта, это нужно сделать до объявления класса:

1
import android.app.Activity;
2
import android.content.Context;
3
import android.content.Intent;
4
import android.content.res.TypedArray;
5
import android.database.Cursor;
6
import android.graphics.Bitmap;
7
import android.graphics.BitmapFactory;
8
import android.net.Uri;
9
import android.os.Bundle;
10
import android.provider.MediaStore;
11
import android.view.View;
12
import android.view.ViewGroup;
13
import android.widget.AdapterView;
14
import android.widget.AdapterView.OnItemClickListener;
15
import android.widget.AdapterView.OnItemLongClickListener;
16
import android.widget.BaseAdapter;
17
import android.widget.Gallery;
18
import android.widget.ImageView;

Объявление класса должно быть примерно такое, но с вашим названием Activity:

1
public class PicSelectActivity extends Activity {

Внутри класса, до метода «onCreate», который Eclipse должен автоматически включить, добавьте следующие переменные:

1
//variable for selection intent

2
private final int PICKER = 1;
3
//variable to store the currently selected image

4
private int currentPic = 0;
5
//gallery object

6
private Gallery picGallery;
7
//image view for larger display

8
private ImageView picView;

Первая переменная - константа, которая используется для идентификации изображения, после того как пользователь его выбрал. Когда пользователь нажимает на эскиз и переходит к выбору изображения, мы будем использовать переменную «currentPic» чтобы отслеживать какие эскиз выбран. Две другие переменные необходимы для элементов пользовательского интерфейса, которые указаны в классе.

В файле манифеста, единственное дополнение, которые вы должны сделать, указать элемент main Activity для вашего приложения:

1
android:configChanges="orientation|keyboardHidden"

Это позволит приложению сохранять изображения при смене ориентации устройства. Теперь давайте настроим текстовые строки, которые мы будем использовать в пользовательском интерфейсе. Откройте файл «res/values/strings.xml». Eclipse должен был уже добавить имя вашего приложения, которое вы можете изменить, если захотите. Добавьте следующие строки:

1
<string name="picture">Picture</string>
2
<string name="select_intro">Long-press a thumbnail to add an image</string>
3
<string name="show_intro">Press a thumbnail to display at larger size</string>

Это все будет использовано в интерфейсе. Теперь вернемся к нашему классу Activity.

Шаг 2: Дизайн приложения

Прежде чем мы напишем функционал на Java, давайте оформим элементы приложения. Откройте «res/layout/main.xml» файл. Мы будем использовать Linear Layout, поэтому добавьте следующие строки в файл XML:

1
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
2
    android:layout_width="fill_parent"
3
    android:layout_height="fill_parent"
4
    android:orientation="vertical" >
5
   
6
</LinearLayout>

Мы добавим ещё больше элементов в наш Linear Layout. Начнём с Text View как первого элемента в Linear Layout:

1
<TextView android:layout_width="fill_parent"
2
  android:layout_height="wrap_content"
3
	android:padding="5dp"
4
	android:text="@string/select_intro"
5
	android:gravity="center"
6
	android:textStyle="bold" />

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

1
<Gallery
2
	android:id="@+id/gallery"
3
	android:layout_width="fill_parent"
4
	android:layout_height="wrap_content" />

Мы используем атрибут ID, поэтому мы сможем обратиться к объекту в Java коде. Затем добавим еще один Text View:

1
<TextView android:layout_width="fill_parent"
2
	android:layout_height="wrap_content"
3
	android:gravity="center"
4
	android:padding="5dp"
5
	android:textStyle="italic"
6
	android:text="@string/show_intro" />

Опять, мы используем строку, которую уже определили. Наконец, последним пунктом в нашем Linear Layout будет Image View, который будет показывать увеличенное изображение галереи:

1
<ImageView 
2
	android:id="@+id/picture"
3
	android:layout_width="fill_parent"
4
	android:layout_height="fill_parent"
5
	android:contentDescription="@string/picture" />

Мы должны будем сослаться на этот View в Java коде, поэтому мы используем атрибут ID. Также, мы используем строку в качестве описания содержимого. Теперь нам нужно определить стиль для галереи. Создайте новый файл в каталоге "res/values", и назовите его "attrs.xml". Напишите следующий код:

1
<resources>
2
	<declare-styleable name="PicGallery">
3
		<attr name="android:galleryItemBackground" />
4
	</declare-styleable>
5
</resources>

Мы собираемся использовать некоторые из ресурсов платформы Android для оформления элементов нашей галереи и будем ссылаться к ним в Java коде.

Теперь вернемся к нашему Activity классу. Внутри метода "onCreate" существующий код должен выглядеть следующим образом:

1
super.onCreate(savedInstanceState);
2
setContentView(R.layout.main);

После этого, добавьте ссылки на два элемента пользовательского интерфейса:

1
//get the large image view

2
picView = (ImageView) findViewById(R.id.picture);
3
        
4
//get the gallery view

5
picGallery = (Gallery) findViewById(R.id.gallery);

Теперь мы готовы создать галерею и обработать взаимодействия с пользователем.

Шаг 3: Создаём Base Adapter

Чтобы создать Gallery View, мы собираемся создать класс, который расширяет Base Adapter. В вашем классе Activity после метода «onCreate», создайте новый подкласс следующим образом:

1
public class PicAdapter extends BaseAdapter {
2
3
}

Здесь мы задействуем несколько методов, которые понадобятся для Base Adapter и позже добавим несколько методов в приложение. Для начала добавим в класс несколько переменных:

1
//use the default gallery background image

2
int defaultItemBackground;
3
        
4
//gallery context

5
private Context galleryContext;
6
7
//array to store bitmaps to display

8
private Bitmap[] imageBitmaps;
9
10
//placeholder bitmap for empty spaces in gallery

11
Bitmap placeholder;

Первая переменная представляет фоновый цвет элемента Android Gallery, на которую мы указываем в нашем "attrs.xml" файле. Context указывает на пользовательский интерфейс при добавлении элементов. Массив Bitmap хранит изображения, которые мы будем отображать в Gallery View. Чтобы начать с чего-то, мы будем использовать изображение "заглушку", в качестве элементов галереи, которые пользователь ещё не выбрал.

Пойдём дальше и и создадим метод конструктора для нашего нового класса PicAdapter, после переменных:

1
public PicAdapter(Context c) {
2
3
	//instantiate context

4
	galleryContext = c;
5
6
	//create bitmap array

7
	imageBitmaps  = new Bitmap[10];
8
            
9
	//decode the placeholder image

10
	placeholder = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
11
	
12
	//more processing

13
	
14
}

Мы добавим больше кода в метод конструктора позже. Сначала мы создадим переменные массивы Context и Bitmap - мы собираемся показать галерею с 10 объектами в ней, но вы можете изменить эту величину, если хотите. Для демонстрации мы используем стандартную иконку проекта в Eclipse, в качестве "заглушки" изображения, но вы конечно же можете создать свою. Если вы хотите использовать ваше изображение, сохраните его в папках drawable и измените значение строки "decodeResource" в соответствии с новым именем.

Теперь мы можем назначить "заглушку" изображения для каждой позиции в массиве, так что все эскизы галереи первоначально будут отображать её - находясь еще внутри метода-конструктора:

1
//set placeholder as all thumbnail images in the gallery initially

2
for(int i=0; i<imageBitmaps.length; i++)
3
	imageBitmaps[i]=placeholder;

Чтобы завершить метод конструктора, мы установим фоновое изображение для элемента галереи:

1
//get the styling attributes - use default Andorid system resources

2
TypedArray styleAttrs = galleryContext.obtainStyledAttributes(R.styleable.PicGallery);
3
4
//get the background resource

5
defaultItemBackground = styleAttrs.getResourceId(
6
	R.styleable.PicGallery_android_galleryItemBackground, 0);
7
8
//recycle attributes

9
styleAttrs.recycle();

Здесь мы указываем на содержание нашего "attrs.xml" файла.

Теперь нам нужно задействовать несколько стандартных методов, расширяя класс Base Adapter. Для начала подключим метод "getCount", после метода конструктора:

1
//return number of data items i.e. bitmap images

2
public int getCount() {
3
	return imageBitmaps.length;
4
}

Этот метод возвращает количество объектов в галерее. После этого метода добавьте стандартный метод "getItem":

1
//return item at specified position

2
public Object getItem(int position) {
3
	return position;
4
}

Далее добавим метод "getItemId":

1
//return item ID at specified position

2
public long getItemId(int position) {
3
	return position;
4
}

Вам не нужно слишком беспокоиться о содержании этих методов, поскольку они являются стандартными. Теперь мы добавим немного более сложный метод, необходимый для класса:

1
//get view specifies layout and display options for each thumbnail in the gallery

2
public View getView(int position, View convertView, ViewGroup parent) {
3
4
	//create the view

5
	ImageView imageView = new ImageView(galleryContext);
6
	//specify the bitmap at this position in the array

7
	imageView.setImageBitmap(imageBitmaps[position]);
8
	//set layout options

9
	imageView.setLayoutParams(new Gallery.LayoutParams(300, 200));
10
	//scale type within view area

11
	imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
12
	//set default gallery item background

13
	imageView.setBackgroundResource(defaultItemBackground);
14
	//return the view

15
	return imageView;
16
}

В методе "getView" мы определяем, что мы хотим показать в каждом элементе галереи. Наш массив Bitmap будет содержать изображения, в каждой позиции с индексом массива Bitmap, соответствующий его позиции в галерее. Также как при создании View, мы устанавливаем Bitmap для отображения, вместе с некоторыми свойствами. Также мы установим фоновый цвет элемента, который мы упоминали ранее. При желании вы можете изменить размер эскизов. Каждый эскиз будет отображаться отцентрированным, если его размер меньше, чем объект View - вы можете выбрать другую опцию, к примеру, растянуть изображение, чтобы соответствовать размеру поля, изменив параметр "ScaleType".

Теперь вернёмся к началу класса main Activity вашего приложения. Добавим новую переменную, представляющую объект нового класса, который вы только что создали:

1
//adapter for gallery view

2
private PicAdapter imgAdapt;

Теперь в методе "onCreate" после кода, который вы добавили, задействуйте переменную Base Adapter и установите его в качестве адаптера для галереи:

1
//create a new adapter

2
imgAdapt = new PicAdapter(this);
3
4
//set the gallery adapter

5
picGallery.setAdapter(imgAdapt);

При первоначальном запуске приложения, всё должно выглядеть примерно так:

Pic Select LaunchPic Select LaunchPic Select Launch

Теперь мы готовы обрабатывать взаимодействия с пользователем.

Шаг 4: Разрешаем пользователю выбрать изображения

Чтобы выбрать изображения для галереи, пользователь должен нажать на эскиз. В методе "onCreate" после существующего кода, добавьте следующий обработчик события для каждого элемента в Gallery View:

1
//set long click listener for each gallery thumbnail item

2
picGallery.setOnItemLongClickListener(new OnItemLongClickListener() {
3
	//handle long clicks

4
	public boolean onItemLongClick(AdapterView<?> parent, View v, int position, long id) {
5
		//take user to choose an image

6
			
7
	}
8
});

Мы создаем новый обработчик "OnItemLongClickListener" с методом "onItemLongClick" внутри него. Добавьте следующий код внутри метода:

1
//update the currently selected position so that we assign the imported bitmap to correct item

2
currentPic = position;
3
4
//take the user to their chosen image selection app (gallery or file manager)

5
Intent pickIntent = new Intent();
6
pickIntent.setType("image/*");
7
pickIntent.setAction(Intent.ACTION_GET_CONTENT);
8
//we will handle the returned data in onActivityResult

9
startActivityForResult(Intent.createChooser(pickIntent, "Select Picture"), PICKER);
10
11
return true;

Найдите минутку и взгляните на то, что получилось. Сначала, мы записываем выбранный элемент, теперь мы знаем, какой элемент в галерее выбран. Затем код сообщает приложению, что необходимо дать пользователю выбрать приложение для выбора изображения, это может быть Галерея Android или файловый менеджер. Указав "ACTION_GET_CONTENT", мы даём задание приложению вернуть результат выбора пользователя. Поскольку мы запускаем событие выбора с помощью "startActivityForResult", мы сможем обработать возвращаемые изображения в Activity метода "onActivityResult".

То, что произойдёт, когда событие выбора изображения запущено будет зависеть от того какие приложения установил пользователь. На моем устройстве я могу выбрать из двух приложений:

Image Chooser App ChoiceImage Chooser App ChoiceImage Chooser App Choice

Файл изображения который я выберу в любом из этих приложений будет возвращён в приложение.

Шаг 5: Управление полученными изображениями

Когда пользователь выбирает изображение из галереи или файлового менеджера приложения, данные будут возвращены в метод "onActivityResult". Добавьте метод к классу Activity, используя следующую структуру:

1
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
2
3
}
4
/java]
5
6
Inside the method, add the following:
7
8
[java]
9
if (resultCode == RESULT_OK) {
10
	//check if we are returning from picture selection

11
	if (requestCode == PICKER) {
12
            //import the image

13
            
14
	}
15
}
16
//superclass method

17
super.onActivityResult(requestCode, resultCode, data);

Здесь мы проверяем, что мы получили корректные данные и что данные вернулись после события выбора. Мы вызовем метод суперкласса в конце метода. Внутри второго оператора "if" мы добавим весь код который нужен для импорта выбранного изображения, начиная с URI изображения:

1
//the returned picture URI

2
Uri pickedUri = data.getData();

Нам нужно немного больше чтобы импортировать изображение. Начнём с объявления нескольких переменных:

1
//declare the bitmap

2
Bitmap pic = null;
3
4
//declare the path string

5
String imgPath = "";

Теперь, давайте, попытаемся получить путь изображения:

1
//retrieve the string using media data

2
String[] medData = { MediaStore.Images.Media.DATA };
3
//query the data

4
Cursor picCursor = managedQuery(pickedUri, medData, null, null, null);
5
if(picCursor!=null)
6
{
7
	//get the path string

8
	int index = picCursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
9
	picCursor.moveToFirst();
10
	imgPath = picCursor.getString(index);
11
}
12
else
13
	imgPath = pickedUri.getPath();

Теперь мы получим данные изображения, затем отправим запрос используя курсор, передадим URI изображения выбранного пользователем. Условия "if" и "else" обрабатывают выбор изображения пользователем через стандартную галерею или с помощью файлового менеджера. Конечным результатом кода будет путь к выбранному изображению.

Шаг 6: Отображение обработанных изображений для снижения нагрузки на использование памяти

Вместо того, чтобы просто показать выбранные пользователем изображения, мы должны свести к минимуму количество памяти которое будет использовать наше приложение. В большинстве случаев изображения на устройстве будут гораздо больше, чем мы можем отобразить в приложении. По этой причине нам нужно обработать изображения перед его отображением в приложении. Находясь внутри второго оператора "if" в "onActivityResult" добавьте следующее:

1
//if we have a new URI attempt to decode the image bitmap

2
if(pickedUri!=null) {
3
4
}

Внутри этого выражения мы обработаем и импортируем изображение. Начнём с определения необходимой ширины и высоты для изображений, это будет самый большой размер для отображения изображения:

1
//set the width and height we want to use as maximum display

2
int targetWidth = 600;
3
int targetHeight = 400;

Теперь нам нужно настроить объект Bitmap чтобы декодировать изображение:

1
//create bitmap options to calculate and use sample size

2
BitmapFactory.Options bmpOptions = new BitmapFactory.Options();

Сначала узнаем больше о размере изображения:

1
//first decode image dimensions only - not the image bitmap itself

2
bmpOptions.inJustDecodeBounds = true;
3
BitmapFactory.decodeFile(imgPath, bmpOptions);
4
5
//image width and height before sampling

6
int currHeight = bmpOptions.outHeight;
7
int currWidth = bmpOptions.outWidth;

Теперь передадим настройки Bitmap для обработки размеров изображения, но не самого изображения. Мы будем использовать эту информацию для определения подходящего размера для импорта изображения, и начнём с задействования переменной которую сохраним:

1
//variable to store new sample size

2
int sampleSize = 1;

Теперь вычислим размер образца, и если необходимый размер меньше, чем размер изображения по-умолчанию:

1
//calculate the sample size if the existing size is larger than target size

2
if (currHeight>targetHeight || currWidth>targetWidth) 
3
{
4
	//use either width or height

5
	if (currWidth>currHeight)
6
		sampleSize = Math.round((float)currHeight/(float)targetHeight);
7
	else 
8
		sampleSize = Math.round((float)currWidth/(float)targetWidth);
9
}

Теперь мы можем установить размер образца Bitmap:

1
//use the new sample size

2
bmpOptions.inSampleSize = sampleSize;

Теперь нам нужно настроить параметры Bitmap для того, чтобы декодировать само содержимое файла, а не только его размеры:

1
//now decode the bitmap using sample options

2
bmpOptions.inJustDecodeBounds = false;

Наконец мы можем обработать Bitmap используя наши настройки:

1
//get the file as a bitmap

2
pic = BitmapFactory.decodeFile(imgPath, bmpOptions);

Теперь переменная "pic" содержит Bitmap изображения, которое мы будем использовать в приложении.

Шаг 7: Добавляем выбранные изображения в галерею

Теперь у нас есть выбранное пользователем изображение, и мы можем добавить его в массив Gallery. Для этого нам нужно добавить метод в класс Base Adapter. Внутри класса "PicAdapter" добавьте следующий метод после "getView":

1
//helper method to add a bitmap to the gallery when the user chooses one

2
public void addPic(Bitmap newPic)
3
{
4
	//set at currently selected index

5
	imageBitmaps[currentPic] = newPic;
6
}

Теперь вернёмся к "onActivityResult", и после строки, в которой мы обработали файл изображения и сохранили результат в переменной "pic", добавьте следующее:

1
//pass bitmap to ImageAdapter to add to array

2
imgAdapt.addPic(pic);
3
//redraw the gallery thumbnails to reflect the new addition

4
picGallery.setAdapter(imgAdapt);

Сначала мы вызовем новый метод, чтобы добавить новое изображение в массив Gallery, а затем мы установим Adapter так, что новое изображение мгновенно появится в Gallery View.

Шаг 8: Показываем большое изображение

Наконец, мы покажем одно изображение большего размера. Изображение будет показано в larger Image View в момент импорта в приложение и затем когда пользователь выбирает его в галерее. Для начала, настроим показ изображения большего размера, когда пользователь импортирует его. В методе "onActivityResult", после строк в которой мы вызывали "setAdapter", добавьте следующее:

1
//display the newly selected image at larger size

2
picView.setImageBitmap(pic);
3
//scale options

4
picView.setScaleType(ImageView.ScaleType.FIT_CENTER);

У нас есть переменная, содержащая увеличенное изображение, поэтому можно просто объединить их вместе с параметрами обработки. Теперь давайте обработаем показ увеличенного изображения, когда пользователь нажимает на эскиз. В классе "PicAdapter" добавьте еще один метод после "addPic":

1
//return bitmap at specified position for larger display

2
public Bitmap getPic(int posn)
3
{
4
	//return bitmap at posn index

5
	return imageBitmaps[posn];
6
}

Этот метод просто возвращает изображение в указанную позицию. Теперь перейдём к Activity методу "onCreate". После того как вы добавили обработчик нажатия, добавьте обработчик для каждого объекта Gallery View. 

1
//set the click listener for each item in the thumbnail gallery

2
picGallery.setOnItemClickListener(new OnItemClickListener() {
3
	//handle clicks

4
	public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
5
		//set the larger image view to display the chosen bitmap calling method of adapter class

6
		picView.setImageBitmap(imgAdapt.getPic(position));
7
	}
8
});

Метод "onItemClick" будет срабатывать каждый раз когда пользователь будет нажимать на эскиз в галерее. Теперь мы вызываем новый метод "getPic", который добавили в класс "PicAdapter" чтобы получить Bitmap выбранной позиции, и устанавливаем в качестве Image View Bitmap для его отображения в увеличенном размере.

Заключение

Итак, мы создали наше приложение галерею. Протестируйте его на вашем устройстве, импортируйте изображения нажимая на эскизы и при нажатии на каждый эскиз, в свою очередь, просматривайте увеличенное изображение. Когда пользователь выбирает несколько изображений, приложение должно выглядеть примерно следующим образом:

Gallery with Selected ImagesGallery with Selected ImagesGallery with Selected Images

Конечно, вы можете улучшить приложение в соответствии с вашими потребностями, например внести дополнительные действия для изображения. Исходный код можно скачать по ссылке, поэтому вы сможете проверить, всё ли у вас правильно.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.