Статьи

Android SDK: реализация функции перетаскивания

Функция перетаскивания в Android SDK — это интерактивная функция, от которой могут выиграть многие приложения, а начать работу просто. В этом руководстве мы реализуем простую операцию перетаскивания, поясняющую, какие дополнительные опции у вас есть для ваших собственных проектов!

Простое приложение, которое мы создадим, позволит пользователю перетаскивать список элементов в порядке предпочтения. Пользователю будет казаться, что видимый элемент пользовательского интерфейса перетаскивается в другое место. Однако в действительности данные, связанные с одним представлением Android, перемещаются в другое представление. Вы, конечно, можете использовать код в любом приложении, над которым работаете, но вам нужно будет установить минимальную версию SDK 11, чтобы использовать обработку перетаскиванием.


Создайте новый проект Android в Eclipse. Введите настройки приложения по вашему выбору, обязательно создав пустое действие и макет для него. Прежде чем мы начнем использовать функцию перетаскивания, давайте разберем макет. Мы не будем тратить много времени на дизайн, чтобы сосредоточиться на функциональной части приложения.

В файле макета, который вы указали при создании проекта, введите контур линейного макета, заменив любой существующий контент:

1
2
3
4
5
6
7
8
9
<LinearLayout xmlns:android=»http://schemas.android.com/apk/res/android»
    android:layout_width=»fill_parent»
    android:layout_height=»wrap_content»
    android:orientation=»vertical»
    android:padding=»10dp»
    android:paddingLeft=»50dp»
    android:paddingRight=»50dp» >
 
</LinearLayout>

Внутри линейного макета начните с небольшого информативного текстового представления:

1
2
3
4
5
6
<TextView
    android:layout_width=»fill_parent»
    android:layout_height=»wrap_content»
    android:gravity=»center»
    android:paddingBottom=»10dp»
    android:text=»@string/intro» />

Добавьте указанную строку в файл приложения «res / values ​​/ strings.xml»:

1
<string name=»intro»>Place these foods in your order of preference.</string>

Пока у вас есть открытый файл строк, добавьте следующие три элемента:

1
2
3
<string name=»option_1″>Apple</string>
<string name=»option_2″>Cake</string>
<string name=»option_3″>Cheese</string>

Эти строки будут отображаться на элементах, которые пользователи будут перетаскивать. У пользователей будет три слота для перетаскивания, поэтому добавьте строки для меток в следующие:

1
2
3
<string name=»choice_1″>Most favorite</string>
<string name=»choice_2″>-</string>
<string name=»choice_3″>Least favorite</string>

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<TextView
    android:id=»@+id/option_1″
    android:layout_width=»fill_parent»
    android:layout_height=»wrap_content»
    android:layout_margin=»5dp»
    android:background=»@drawable/option»
    android:gravity=»center»
    android:text=»@string/option_1″
    android:textStyle=»bold» />
 
    <TextView
    android:id=»@+id/option_2″
    android:layout_width=»fill_parent»
    android:layout_height=»wrap_content»
    android:layout_margin=»5dp»
    android:background=»@drawable/option»
    android:gravity=»center»
    android:text=»@string/option_2″
    android:textStyle=»bold» />
 
    <TextView
    android:id=»@+id/option_3″
    android:layout_width=»fill_parent»
    android:layout_height=»wrap_content»
    android:layout_margin=»5dp»
    android:background=»@drawable/option»
    android:gravity=»center»
    android:text=»@string/option_3″
    android:textStyle=»bold» />

У каждого из них есть идентификатор, чтобы мы могли ссылаться на него в классе Activity. Мы также используем строки, которые мы определили. Мы скоро создадим фон для рисования. Затем добавьте три текстовых представления, в которые можно перетаскивать перетаскиваемые элементы:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<TextView
    android:id=»@+id/choice_1″
    android:layout_width=»fill_parent»
    android:layout_height=»wrap_content»
    android:layout_margin=»5dp»
    android:background=»@drawable/choice»
    android:gravity=»center»
    android:text=»@string/choice_1″ />
 
    <TextView
    android:id=»@+id/choice_2″
    android:layout_width=»fill_parent»
    android:layout_height=»wrap_content»
    android:layout_margin=»5dp»
    android:background=»@drawable/choice»
    android:gravity=»center»
    android:text=»@string/choice_2″ />
 
    <TextView
    android:id=»@+id/choice_3″
    android:layout_width=»fill_parent»
    android:layout_height=»wrap_content»
    android:layout_margin=»5dp»
    android:background=»@drawable/choice»
    android:gravity=»center»
    android:text=»@string/choice_3″ />

Они также используют идентификаторы и строковые ресурсы. В папке или папках res / drawables вашего приложения создайте первый фоновый рисунок, который мы указали для перетаскиваемых текстовых видов. Выберите папку (и) drawables и выберите «Файл», «Новый», «Файл» — введите «option.xml» в качестве имени файла, чтобы соответствовать тому, что мы включили в макет. Вставьте следующий код в файл drawables, определяя форму фона:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
<shape xmlns:android=»http://schemas.android.com/apk/res/android»
    android:dither=»true» >
 
    <solid android:color=»#ff00ccff» />
 
    <corners android:radius=»2dp» />
 
    <stroke
        android:width=»2dp»
        android:color=»#ff0099cc» />
 
    <padding
        android:bottom=»5dp»
        android:left=»10dp»
        android:right=»10dp»
        android:top=»5dp» />
 
</shape>

Теперь создайте другой файл drawables, назовите его «choice.xml», на этот раз и введите следующую форму:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
<shape xmlns:android=»http://schemas.android.com/apk/res/android»
    android:dither=»true» >
 
    <solid android:color=»#ffffff99″ />
 
    <corners android:radius=»2dp» />
 
    <stroke
        android:dashGap=»4dp»
        android:dashWidth=»2dp»
        android:width=»2dp»
        android:color=»#ffffff00″ />
 
    <padding
        android:bottom=»5dp»
        android:left=»5dp»
        android:right=»5dp»
        android:top=»5dp» />
 
</shape>

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

Первый экран приложения

Давайте обратимся к функциональности приложения — откройте ваш класс Activity. Вашему классу потребуется следующий импорт, добавленный перед строкой объявления открывающего класса:

01
02
03
04
05
06
07
08
09
10
11
import android.os.Bundle;
import android.app.Activity;
import android.content.ClipData;
import android.graphics.Typeface;
import android.view.DragEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnDragListener;
import android.view.View.OnTouchListener;
import android.widget.TextView;

В объявлении класса перед методом onCreate (который Eclipse должен был автоматически заполнить для вас) добавьте несколько переменных экземпляра:

1
private TextView option1, option2, option3, choice1, choice2, choice3;

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

1
2
3
4
5
6
7
8
9
//views to drag
option1 = (TextView)findViewById(R.id.option_1);
option2 = (TextView)findViewById(R.id.option_2);
option3 = (TextView)findViewById(R.id.option_3);
 
//views to drop onto
choice1 = (TextView)findViewById(R.id.choice_1);
choice2 = (TextView)findViewById(R.id.choice_2);
choice3 = (TextView)findViewById(R.id.choice_3);

Мы используем атрибуты ID, включенные в макет XML.


Чтобы использовать перетаскивание внутри Activity, мы создадим два внутренних класса. Эти классы будут реализовывать интерфейсы Android, необходимые для перетаскивания. Начните с создания схемы объявления класса для Touch Listener после метода Activity onCreate :

1
2
3
private final class ChoiceTouchListener implements OnTouchListener {
 
}

Этот класс позволит приложению обнаруживать пользователей, которые касаются определенных видов, рассматривая касание как начало операции перетаскивания. Внутри класса добавьте метод onTouch :

1
2
3
public boolean onTouch(View view, MotionEvent motionEvent) {
 
}

Затем проверьте, что событие Motion вызвало метод onTouch :

1
2
3
4
5
6
7
8
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
    //setup drag
 
    return true;
}
else {
    return false;
}

Нас интересуют только случаи, когда пользователь прикасался к представлению, чтобы перетащить его, поэтому внутри оператора if мы настроим операцию перетаскивания. Перед оператором return true подготовьте перетаскивание следующим образом:

1
2
ClipData data = ClipData.newPlainText(«», «»);
DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);

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

1
2
//start dragging the item touched
view.startDrag(data, shadowBuilder, view, 0);

Если вам нужен только базовый процесс перетаскивания, вам не нужно слишком беспокоиться о деталях Touch Listener.


Мы создали класс слушателя для перетаскивания, теперь нам нужен класс для отбрасывания. После класса Touch Listener добавьте объявление класса Drag Listener:

1
2
3
private class ChoiceDragListener implements OnDragListener {
 
}

Добавьте метод onDrag внутри этого нового класса:

1
2
3
4
5
6
@Override
public boolean onDrag(View v, DragEvent event) {
    //handle drag events
 
    return true;
}

Здесь мы реагируем на события перетаскивания. Слушатель перетаскивания может использоваться для обнаружения и реагирования на различные этапы перетаскивания, каждый из которых обрабатывается в операторе switch / case. Добавьте его в метод onDrag перед оператором return следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
switch (event.getAction()) {
    case DragEvent.ACTION_DRAG_STARTED:
        //no action necessary
        break;
    case DragEvent.ACTION_DRAG_ENTERED:
        //no action necessary
        break;
    case DragEvent.ACTION_DRAG_EXITED:
        //no action necessary
        break;
    case DragEvent.ACTION_DROP:
        //handle the dragged view being dropped over a drop view
 
        break;
    case DragEvent.ACTION_DRAG_ENDED:
        //no action necessary
        break;
    default:
        break;
}

Большинство событий перетаскивания не имеют отношения к этому приложению, однако вы можете использовать их, если вам нужен более подробный контроль. Мы добавим в раздел ACTION_DROP позже.


У нас определены классы Touch и Drag Listener, теперь нам нужно добавить их экземпляры в представления, которые мы перетаскиваем. Вернувшись в метод onCreate , после существующего кода сначала добавьте Touch Listeners в три текстовых представления, которые пользователи смогут перетаскивать:

1
2
3
4
//set touch listeners
option1.setOnTouchListener(new ChoiceTouchListener());
option2.setOnTouchListener(new ChoiceTouchListener());
option3.setOnTouchListener(new ChoiceTouchListener());

Теперь добавьте Drag Listeners к трем представлениям, на которые можно будет перетаскивать:

1
2
3
4
//set drag listeners
choice1.setOnDragListener(new ChoiceDragListener());
choice2.setOnDragListener(new ChoiceDragListener());
choice3.setOnDragListener(new ChoiceDragListener());

Мы реализуем экземпляр классов Touch или Drop, которые мы создали для каждого из текстовых представлений. Пользователи смогут перетащить любой из трех верхних текстовых видов на любой из трех нижних.

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

Теперь у нас есть настройка приложения для пользователей, чтобы перетаскивать элементы, мы просто должны определить, что должно произойти, когда элемент будет сброшен в одном из трех нижних видов. Вернувшись в свой класс Drag Listener, в операторе case ACTION_DROP (до разрыва ) сначала получите ссылку на отбрасываемое представление:

1
2
//handle the dragged view being dropped over a target view
View view = (View) event.getLocalState();

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

1
2
//stop displaying the view where it was before it was dragged
view.setVisibility(View.INVISIBLE);

Метод onDrag также получает параметр, представляющий целевое представление, на которое перетаскивается перетаскиваемый элемент, поэтому приведите его как текстовое представление:

1
2
//view dragged item is being dropped on
TextView dropTarget = (TextView) v;

Давайте также приведем отброшенное представление к текстовому представлению:

1
2
//view being dragged and dropped
TextView dropped = (TextView) view;

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

1
2
//update the text in the target view to reflect the data being dropped
dropTarget.setText(dropped.getText());

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

1
2
//make it bold to highlight the fact that an item has been dropped
dropTarget.setTypeface(Typeface.DEFAULT_BOLD);

Вот как приложение появляется после того, как пользователь перетаскивает элемент «Apple» в нижнюю позицию списка:

Приложение после перетаскивания

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

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

По- прежнему в разделе ACTION_DROP проверьте, есть ли у текущего целевого представления установленный тег:

1
2
//if an item has already been dropped here, there will be a tag
Object tag = dropTarget.getTag();

Мы хотим сбросить один из исходных видов обратно видимым, если есть набор тегов:

1
2
3
4
5
6
7
8
//if there is already an item here, set it back visible in its original place
if(tag!=null)
{
    //the tag is the view id already dropped here
    int existingID = (Integer)tag;
    //set the original view visible again
    findViewById(existingID).setVisibility(View.VISIBLE);
}

Теперь мы можем обновить тег для представления новых данных View, которые были здесь отброшены:

1
2
//set the tag in the target view to the ID of the view being dropped
dropTarget.setTag(dropped.getId());

Это позволяет пользователю передумать, бросая предметы поверх существующих предметов, и при этом делать выбор для ранее удаленных предметов.

Приложение после завершения перетаскивания

Это наше основное приложение завершено. Есть много способов улучшить это приложение. Например, вы также можете разрешить пользователям перетаскивать «Виды» из нижнего раздела после их перетаскивания туда, либо в другое место в нижнем разделе, либо обратно в положение в верхнем разделе. Чтобы сделать это, вам нужно добавить Touch and Drag Listeners ко всем текстовым представлениям в верхней и нижней частях, так что логика немного сложнее. Вы также можете улучшить приложение, читая данные динамически или создавая собственные дизайны для различных этапов процесса перетаскивания. Что бы вы ни делали, обязательно тщательно проверяйте операции перетаскивания.