Форум программистов, компьютерный форум CyberForum.ru

Программирование Android

Войти
Регистрация
Восстановить пароль
 
Рейтинг: Рейтинг темы: голосов - 12, средняя оценка - 5.00
CoolMind
418 / 401 / 65
Регистрация: 06.10.2012
Сообщений: 1,727
#1

PopupWindow. Что осталось за кадром? (Tutorial) - Программирование Android

27.08.2014, 17:46. Просмотров 1834. Ответов 7
Метки нет (Все метки)

Здравствуйте!
Недавно потребовалось сделать выпадающий список более вменяемым, чем это бывает у Spinner. Поискав, понял, что лучше всего в этом деле пригодится связка EditText + PopupWindow с ListView. Вариант с Dialog показался мне сложным или нереализуемым, поэтому выбор пал на PopupWindow.
Подпрограмма должна:
1) при нажатии на EditText открывать выпадающий список,
2) при нажатии на список сохранять выбранный элемент в EditText и закрывать список,
3) при нажатии вне списка закрывать список.
К сожалению, нормальных документов найти не смог и потому написал свою подпрограмму. Большинство авторов воспринимают PopupWindow как разновидность диалогового окна, в то время как там можно нарисовать не только надпись с кнопкой. Буду рад, если кому пригодится. Весь код пишу по памяти, он может не работать, но общую концепцию понять можно.
Итак, для начала научимся показывать всплывающее окно с какой-нибудь кнопкой (потом переделаем). Добавим файл с разметкой, назовём её popup_layout.xml:
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" 
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Привет!"
        android:layout_gravity="center_horizontal" />
 
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Закрыть"
        android:layout_gravity="center_horizontal" />
</LinearLayout>
В форму, из которой будете вызывать PopupWindow, добавьте текстовое поле. Под ним будет появляться PopupWindow. Назовём его, например, txtCategory.
Java
1
2
3
4
5
6
7
8
9
private void showCategoriesWindow(LayoutInflater inflater) {
    /*Поскольку я открывал PopupWindow из Fragment, то инициализировать inflater не пришлось (передаём его из 
            onCreateView(final LayoutInflater inflater, ViewGroup viewGroup, Bundle SavedInstanceState)
    Если же вы вызываете PopupWindow из Activity, то инициализируем её так:
            LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);*/
    View popupView = inflater.inflate(R.layout.popup_layout, null);
    popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    popupWindow.showAsDropDown(txtCategory, 50, -20);
}
Далее запускаем:
Java
1
2
3
4
5
6
7
8
9
@Override
public View onCreateView(final LayoutInflater inflater, ViewGroup viewGroup, Bundle SavedInstanceState) {
    View v = inflater.inflate(R.layout.fragment, viewGroup, false);
    ...
    EditText txtCategory = (EditText) v.findViewById(R.id.category);
    ...
    showCategoriesWindow(inflater);
    ...
}
Должно появиться окно с прозрачным фоном, находящееся недалеко от txtCategory (координаты можете заменить на свои). Закроем программу и напишем код для закрытия всплывающего окна.
Java
1
2
3
4
5
6
7
    Button btnClose = (Button) popupView.findViewById(R.id.button);
    btnClose.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            popupWindow.dismiss();
        }
    });
Теперь при нажатии кнопки PopupWindow будет исчезать. Если PopupWindow не закрыть, а затем закрыть приложение или перейти на другую форму, то приложение будет выдавать в LogCat ошибку. Большинство авторов про неё не упоминают.
Исправим сей недостаток:
Java
1
2
3
4
5
6
    @Override
    public void onDestroy() {
        super.onDestroy();
        if (popupWindow != null)
            popupWindow.dismiss();
    }
Теперь попробуем улучшить разметку для PopupWindow. Сделаем не банальную кнопку, а список. Создайте разметку и назовите файл category_list.xml.
Например, так:
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white">
 
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Закрыть"
        android:layout_gravity="center_horizontal" />
 
    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>
Добавим также описание каждого элемента списка. Будем сокращать не влезающие в экран строки (назовём файл category_list_item.xml):
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="utf-8"?>
 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white" >
 
    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textSize="17sp"
        android:ellipsize="end"
        android:singleLine="true"
        android:text="sample"
        android:gravity="center_vertical" />
 
</LinearLayout>
Свяжем PopupWindow со списком:
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void showCategoriesWindow(LayoutInflater inflater) {
    View popupView = inflater.inflate(R.layout.category_list, null);
    popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    popupWindow.showAsDropDown(txtCategory, 0, 0);
 
    final ArrayList<String> arrList = new ArrayList<String>();
    // Заполните здесь arrList несколькими строчками.
    arrList.add("Россия");
    arrList.add("Беларусь");
    arrList.add("Украина");
    ArrayAdapter<String> adapter = new ArrayAdapter<String>(
        getActivity().getApplicationContext(), R.layout.category_list_item, R.id.textView, arrList);
    final ListView listView = (ListView) popupView.findViewById(R.id.listView);
    listView.setAdapter(adapter);
 
    Button btnClose = (Button) popupView.findViewById(R.id.button);
    btnClose.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            popupWindow.dismiss();
        }
    });
}
Теперь при вызове showCategoriesWindow будет открываться окно с белым фоном, кнопкой и списком. Доработаем пример. Хочется, чтобы окно закрывалось при нажатии на элемент списка, а само значение попадало бы в текстовую надпись на форме.
Для этого вместо последних строк (закрытия окна по нажатию кнопки) напишем другие.
Java
1
2
3
4
5
6
7
    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parentView, View selectedItemView, int position, long id) {
            txtCategory.setText(arrList.get(position));
            popupWindow.dismiss();
        }
    });
Теперь кнопку btnClose можно убрать из кода и разметки. Но если мы вызовем получившуюся процедуру, то увидим, что окно не закрывается. Оказывается (об этом тоже мало где написано), ListView не получает фокуса.
Исправим недочёт. Для этого можно, например, создание окна сделать с параметром true в конце:
Java
1
popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
Кстати, второй параметр при вызове этой процедуры - ширина окна, можете поменять её.
Кроме того, оказалось, что если пользователь нажимает пальцем за пределами PopupWindow, то ничего не происходит. А хотелось бы, чтобы список закрывался. Исправим процедуру.
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private void showCategoriesWindow(LayoutInflater inflater) {
    View popupView = inflater.inflate(R.layout.category_list, null);
    popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
    popupWindow.setBackgroundDrawable(new ColorDrawable());
    popupWindow.setOutsideTouchable(true);
    popupWindow.showAsDropDown(txtCategory, (int)(txtCategory.getWidth() * 0.1), 0);
 
    final ArrayList<String> arrList = new ArrayList<String>();
    arrCategoryId = new ArrayList<Integer>();
 
    ArrayAdapter<String> adapter = new ArrayAdapter<String>(
        getActivity().getApplicationContext(), R.layout.category_list_item, R.id.textView, arrList);
    final ListView listView = (ListView) popupView.findViewById(R.id.listView);
    listView.setAdapter(adapter);
    arrList.add("Россия");
    arrList.add("Беларусь");
    arrList.add("Украина");
    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parentView, View selectedItemView, int position, long id) {
            txtCategory.setText(arrList.get(position).trim());
            iCategory = arrCategoryId.get(position);
            popupWindow.dismiss();
        }
    });
}
Многие авторы в четвёртой строке пишут new BitmapDrawable(), но этот код устарел. Я пробовал посоветованный на stackoverflow вариант ShapeDrawable(), но у него есть один небольшой недостаток. Если ваше PopupWindow будет иметь, например, скруглённые углы, то в самых краях этих углов появятся чёрные точки. Поэтому я использовал ColorDrawable().
Осталось научить программму открывать и закрывать всплывающее окно по нажатию на текстовое поле.
Java
1
2
3
4
5
6
7
8
9
10
11
txtCategory.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if (popupWindow == null) {
            showCategoriesWindow(inflater);
        }
        if (!popupWindow.isShowing()) {
            popupWindow.showAsDropDown(txtCategory, 0, 0);
        }
    }
});
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
CoolMind
418 / 401 / 65
Регистрация: 06.10.2012
Сообщений: 1,727
27.08.2014, 17:59  [ТС]     PopupWindow. Что осталось за кадром? (Tutorial) #2
Длина сообщения ограничена, допишу сюда.
Цель создания PopupWindow состояла в том, чтобы улучшить выпадающий список (Spinner). Слишком длинные строки на экране смартфона должны быть усечены. Также планируется добавить функционал: при нажатии на элемент списка ниже должны появляться дочерние элементы, а уже потом пользователь выбирает нужный элемент. Пока работаю над этой задачей.
Spelcrawler
526 / 496 / 111
Регистрация: 12.03.2014
Сообщений: 1,648
Завершенные тесты: 1
27.08.2014, 18:24     PopupWindow. Что осталось за кадром? (Tutorial) #3
Эх ещеб скриншоты результата) Спасибо, интересно.
CoolMind
418 / 401 / 65
Регистрация: 06.10.2012
Сообщений: 1,727
28.08.2014, 19:18  [ТС]     PopupWindow. Что осталось за кадром? (Tutorial) #4
Spelcrawler, спасибо за ваш интерес. Да, согласен, без снимков не то, завтра постараюсь добавить.

Добавлено через 22 часа 16 минут
Прикрепляю полученный снимок экрана. Пришлось стереть все данные из списка.
Единственная проблема данного решения - пока из-за того, что запретил пересоздание Activity, при повороте экрана ширина списка не меняется.
CoolMind
418 / 401 / 65
Регистрация: 06.10.2012
Сообщений: 1,727
28.08.2014, 19:21  [ТС]     PopupWindow. Что осталось за кадром? (Tutorial) #5
Прикрепляю картинку...
Миниатюры
PopupWindow. Что осталось за кадром? (Tutorial)  
Nea14
1 / 1 / 0
Регистрация: 25.08.2015
Сообщений: 8
29.01.2017, 17:40     PopupWindow. Что осталось за кадром? (Tutorial) #6
Я понял так, что PopupWindow как отдельный класс не существует? Для фрагмента нужен класс, жизненный цикл и тд...Тут ничего такого нет, задаю только view, вся работа в вызываемой активности, правильно понял?
CoolMind
418 / 401 / 65
Регистрация: 06.10.2012
Сообщений: 1,727
31.01.2017, 11:09  [ТС]     PopupWindow. Что осталось за кадром? (Tutorial) #7
Цитата Сообщение от Nea14 Посмотреть сообщение
Я понял так, что PopupWindow как отдельный класс не существует?
Пока что не устарел, всё на месте.
Цитата Сообщение от Nea14 Посмотреть сообщение
Для фрагмента нужен класс, жизненный цикл и тд...Тут ничего такого нет, задаю только view, вся работа в вызываемой активности, правильно понял?
Да, это обычный widget, т.е. по сути, view. Я тогда только начинал Андроид-программирование, поэтому статья несколько пафосна, может быть, сейчас бы сделал через Dialog или ещё как-то.
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
31.01.2017, 17:22     PopupWindow. Что осталось за кадром? (Tutorial)
Еще ссылки по теме:
Перевод Vulkan Tutorial и кое-что по GLSL OpenGL
C (СИ) Найти в матрице подматрицу такую, что бы сумма в подматрице была похожа на сумму всего что осталось
Windows 7 Что делать вин 7 говорит что осталось 3 дня до заканчивания срока лицензии хотя ключ стоит отсчет начался относительно не давно...
Matlab Работа с кадром
Двусвязный список, удалить 1 элемент и вывести то что осталось C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Nea14
1 / 1 / 0
Регистрация: 25.08.2015
Сообщений: 8
31.01.2017, 17:22     PopupWindow. Что осталось за кадром? (Tutorial) #8
Нормальная статья, теперь с материальным UI PopupWindow как-то не вяжется, я с ним правда еще толком и не разобрался, где бы найти полное описание. Popup окно мне не подошло: не раскрывается в нем Spinner. Взял slideUp и в него выдвигающимся сделал фрагмент, который и подменяю на разные view.
Yandex
Объявления
31.01.2017, 17:22     PopupWindow. Что осталось за кадром? (Tutorial)
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru