Перейти к содержимому

Choose activity как убрать андроид

  • автор:

Решение проблем при разработке под Android

Unity не удаётся установить ваше приложение на ваше устройство

  1. Убедитесь, что ваш компьютер видит ваше устройство и может взаимодействовать с ним. Для деталей см. Публикация сборок.
  2. Проверьте консоль Unity на наличие сообщений об ошибках. Это часто помогает в определении проблемы.

Если у вас появляется ошибка “Unable to install APK, protocol failure” во время сборки, то это значит, что ваше устройство подключено через USB-порт с низким питанием (возможно порт на клавиатуре или ещё на какой-нибудь периферии). Если такое случается, то попробуйте подсоединить устройство в USB порт на самом компьютере.

Ваше приложение падает сразу после запуска.

  1. Убедитесь, что вы не пытаетесь использовать NativeActivity с устройствами, которые это не поддерживают.
  2. Попробуйте убрать все нативные плагины, что у вас есть.
  3. Попробуйте отключить stripping.
  4. Используйте adb logcat чтобы получить отчёт о крахе с вашего устройства.

Building DEX Failed

Это ошибка, которая выдаёт сообщение, вроде следующего:-

Building DEX Failed! G:\Unity\JavaPluginSample\Temp/StagingArea> java -Xmx1024M -Djava.ext.dirs="G:/AndroidSDK/android-sdk_r09-windows\platform-tools/lib/" -jar "G:/AndroidSDK/android-sdk_r09-windows\platform-tools/lib/dx.jar" --dex --verbose --output=bin/classes.dex bin/classes.jar plugins Error occurred during initialization of VM Could not reserve enough space for object heap Could not create the Java virtual machine. 

Обычно это вызвано неверной версией Java на вашем компьютере. Обновление Java до последней версии обычно решает проблему.

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

Убедитесь, что Settings->Developer Options->Don’t keep activities не включено на вашем телефоне. Проигрыватель видео — это отдельное приложение и поэтому обычное игровое приложение будет закрыто, если проигрыватель видео включён.

Моя игра закрывается, когда я жму кнопку сна

Измените тег activity в файле AndroidManifest.xml так , чтобы он содержал тег android:configChanges , сделайте это таким образом, как описано здесь.

Пример тега activity может выглядеть, например, вот так:-

Как убрать всплывающие рекламные окна на Андроид (в браузере Chrome и др.)

Илья – главный редактор сайта softdroid.net. Является автором нескольких сотен руководств и статей по настройке Android. Около 15 лет занимается ремонтом техники и решением технических проблем iOS и Android. Имел дело практически со всеми более-менее популярными марками мобильных смартфонов и планшетов Samsung, HTC, Xiaomi и др. Для тестирования используется iPhone 12 и Samsung Galaxy S21 с последней версией прошивки.

Всплывающие окна с навязчивой рекламой – до жути раздражающее явление. Попытки насладиться любимым видеороликом или прочитать интересующую статью превращаются в битву с «крестиками» в Chrome или любом другом браузере. В итоге и настроение испорчено, и желание пользоваться телефоном пропадает. Как навсегда убрать всплывающие окна рекламы на телефоне – читайте далее.

Содержание:

  1. Чем опасны всплывающие окна
  2. Как вирус «заражает» телефон
  3. Удаление всплывающей рекламы: первые действия
  4. Проверка уведомлений (Ad Notifications)
  5. Избавляемся от рекламного вируса в браузере
  6. Приложения для удаления popup-рекламы

Что такое всплывающие окна и чем они опасны?

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

Владельцы сайтов/ разработчики приложений часто размещают в своём проекте баннеры с целью заработка. Это естественное явление. Иногда от неё можно отказаться, просто оплатив услугу «Просмотр без рекламы» или купив полную версию продукта.

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

Как рекламный вирус попадает на мобильное устройство?

Пути попадания вируса на телефон:

  1. Посещение потенциально опасных / зараженных сайтов;
  2. Скачивание программного обеспечения от неизвестных разработчиков.

Не путайте вредоносные программы с adware-программами, которые тоже занимаются показом рекламы, но только делают это с вашего согласия. Если вы его не давали — нежелательное ПО нужно удалить.

Первые шаги по удалению всплывающей рекламы

  1. Первое действие – закрыть все приложения на телефоне. Для этого воспользуйтесь кнопкой Очистить все (Clear All) или закройте их по очереди, сделав свайп каждого приложения в сторону (swiping apps to the side). Можно просто перезагрузить телефон.
  2. Закрыв все окна, вспомните: какие программы / игры вы ставили до момента появления всплывающей рекламы.
  3. Зайдите в Google Play и почитайте отзывы пользователей: нет ли негативных откликов, связанных с баннерами? Если есть – немедленно удаляйте приложение с телефона.
  4. Если реклама не исчезает, еще раз пересмотрите все приложения и удалите ненужные.

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

Настройка панели уведомлений Android

К счастью, последние версии Андроид позволяют гибко настраивать права доступа приложений, а также вывод уведомлений в соответствующую android-панель.

Если вы заметили, что приложение не было открыто, но в области уведомлений появилось “левое” уведомление с рекламой,

  1. Смахните шторку панели уведомлений свайпом вниз;
  2. Удерживайте нажатие на строке приложения;
  3. Появится меню — тапните на кнопку “Сведения”.

Появится экран разрешений, где можно настроить права доступа для конкретного приложения. Доступ к этим настройкам можно также получить через Настройки – Приложения — [название] – Настройки приложений – Уведомления.

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

Удаление рекламного вируса в мобильном браузере

Любой браузер начнёт выдавать ненужную рекламу, если вы схватили вирус или посещаете заражённые сайты. В браузерах известных компаний ее меньше, так как разработчики дорожат репутацией и тщательно следят за безопасностью. Но зато они являются лакомым куском для злоумышленников, так как количество пользователей Google Chrome или Firefox огромно.

Есть разные способы избавления телефона от появления всплывающих окон, но самый эффективный — поиск и полное удаление/блокирование вредоносной или adware- программы. Для этого нужно скачать антивирус (если вы вдруг не сделали этого раньше) или иные специальные приложения, направленные на выявление «шпиона».

Так как некоторое рекламное ПО может находиться на смартфоне по умолчанию, необходимо в настройках антивируса выставить значение «Обнаруживать Adware-программы», после чего указать, что с ними делать — удалять, помещать в карантин и т. д. Если же вы считаете программу нужной, то просто добавьте ее в список исключений.

Важно! Есть программы, в которых реклама исчезает после приобретения лицензии. Но бывает и так, что они содержат сторонние утилиты, которые незаметно проникают на компьютер и активируются. Их удаление может привести к сбоям в работе.

Альтернативный вариант борьбы с popup-баннерами — изменение настроек сайтов:

  1. Зайдите в Гугл Хром, нажмите на расположенные вертикально 3 точки в правом верхнем углу,
  2. Перейдите в «Настройки» — «Настройки сайтов»,
  3. Поочерёдно зайдите в разделы «Всплывающие окна и переадресация» и «Реклама».
  4. Переведите ползунки вправо, активировав защитный режим. Эта мера поможет убрать рекламу, появляющуюся в уведомлениях.

Вместо factory reset – приложения для вычисления вирусов

Конечно, всегда можно выполнить сброс настроек (factory reset), это универсальный способ возврата телефона к состоянию “как после покупки”. Но вам придется заново настраивать телефон и выполнять резервное копирование данных.

Вместо factory reset можно воспользоваться детекторами вирусной рекламы. Рассмотренные ниже приложения были скачаны несколько сотен тысяч раз, имеют массу отзывов, с которыми можно ознакомиться перед загрузкой. Если какое-то из них не сможет справиться с задачей, — напишите разработчикам и оставьте свой отзыв.

Ad Detect Plugin

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

Ad Detect Plugin собирает информацию о каждом интернет-подключении и позволяет вычислить, какое именно приложение использует подключение в рекламных целях. После такого анализа “нарушителя спокойствия” можно вычислить и вручную удалить с устройства.

Kaspersky Internet Security

Популярное антивирусное ПК. Находит множество шпионских программ, которые не видят другие антивирусы, блокирует подключения к рекламным сетям. Имеет встроенный инструмент «Adguard Content Blocker», отображаемый на домашнем экрана отдельной иконкой, который работает только в браузерах «Самсунг» и «Яндекс».

  1. Блокировка нежелательного ПО в Kaspersky работает по умолчанию — «Меню» — «Сканер». Если галочка отсутствует, поставьте её.
  2. Запустите в главном окне «Проверка» и дождитесь её окончания.
  3. Удалите найденные вирусы.
  4. Чтобы поиск был автоматическим, перейдите в раздел «Постоянная защита» и купите лицензию. В противном случае придётся каждый раз запускать сканирование вручную.

Super Security

Программа сканирует телефон на наличие вирусов, плюс оптимизирует его работу посредством очистки мусора и дополнительных настроек.

  1. Скачайте Super Security в Гугл Плей, запустите,
  2. Нажмите «Поиск». Дождитесь завершение процесса.
  3. Нажмите «Включить»/«Очистка» или «Устранить всё» чтобы удалить найденные угрозы. Это могут быть как вирусы, так и отсутствие защиты в реальном времени, и другие проблемы.

Ещё одна хорошая функция — отключение уведомлений. После очистки, вам будет предложено это сделать.

Нажмите «Отключить уведомления» — «Ясно», затем переместите ползунок вправо напротив «Super Security».

Free Adblocker Browser

Это браузер, который можно использовать в качестве альтернативы классическому. Приложение позволяет экономить до 100 мегабайт в месяц на отсутствии загрузки объявлений, а также блокирует слежение и сбор данных сайтами, которые вы посещаете. В настройках Free Adblocker можно установить его браузером по умолчанию. Реклама исчезает даже при просмотре видео в Ютуб.

AdGuard

Известное приложение, но отныне его нельзя скачать в Гугл Плей, так как компания Google запретила размещение программ, скрывающих рекламу в утилитах. Поэтому единственный вариант — установка AdGuard в ручном режиме. Для этого загрузите сайт Adguard.com и скачайте ПО оттуда. Но перед этим разрешите в настройках инсталляцию из неизвестных источников. Для этого:

  1. Зайдите в «Настройки», перейдите в раздел «Безопасность и конфиденциальность».
  2. Прокрутите список вниз и нажмите «Ещё». Перейдите в «Установка неизвестных приложений».
  3. Напротив браузера Google Chrome переместите ползунок вправо.
  4. После скачивания Adguard можете поставить запрет обратно.
  5. Вернитесь на сайт и нажмите «Скачать». Запустите приложение.

Часто задаваемые вопросы

Какой антивирус лучше для устранения всплывающих окон?

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

Что лучше выбрать для защиты от рекламы: специальный браузер типа Free AdblockerBrowser, приложение Ad Detect Plugin или достаточно изменить настройки сайтов?

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

Приложение Ad Detect Plugin обнаруживает источники рекламы, а удалять их нужно вручную. Придется разбираться, нужно это приложение или нет или искать, как в нем отключить рекламу.

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

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

Тормозят ли блокировщики рекламы работу браузера?

Зачастую да. Браузер будет загружать страницы медленнее, потому что нужно время на анализ всех фрагментов веб-ресурса и работу с ними. Но не всегда. Если сайт «тяжелый», с большим количеством данных, то время его обработки будет дольше, чем не нагруженной страницы.

Сколько памяти устройства расходуют утилиты и плагины для блокировки рекламы?

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

ListView

В ранних версиях Android компонент ListView был одним из самым популярных элементов интерфейса. Но теперь его время ушло, недаром на панели инструментов студии он находится в разделе Legacy (устаревший код).

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

Кот работает с сенсорным экраном

Компонент ListView более сложен в применении по сравнению с TextView и другим простыми элементами. Работа со списком состоит из двух частей. Сначала мы добавляем на форму сам ListView, а затем заполняем его элементами списка.

Рассмотрим для начала самый простой пример. Поместите на форму компонент ListView и присвойте идентификатор. Вы увидите, что список будет содержать несколько элементов Item и Sub Item.

ListView

Однако, если посмотрим XML-код, то там ничего не увидим.

Переходим в класс активности и пишем следующий код:

 // код пишется в методе onCreate() // получаем экземпляр элемента ListView ListView listView = findViewById(R.id.listView); // определяем строковый массив final String[] catNames = new String[] < "Рыжик", "Барсик", "Мурзик", "Мурка", "Васька", "Томасина", "Кристина", "Пушок", "Дымка", "Кузя", "Китти", "Масяня", "Симба" >; // используем адаптер данных ArrayAdapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, catNames); listView.setAdapter(adapter); 

Вот и всё. Давайте разберёмся с кодом.

Адаптеры — заполнение списка данными

Компоненту ListView требуются данные для наполнения. Источником наполнения могут быть массивы, базы данных. Чтобы связать данные со списком, используется так называемый адаптер.

Адаптер для стандартного списка обычно создаётся при помощи конструкции new ArrayAdapter(Context context, int textViewResourceId, String[] objects).

  • context — текущий контекст
  • textViewResourceId — идентификатор ресурса с разметкой для каждой строки. Можно использовать системную разметку с идентификатором android.R.layout.simple_list_item_1 или создать собственную разметку
  • objects — массив строк

Метод setAdapter(ListAdapter) связывает подготовленный список с адаптером.

Переходим к java-коду. Сначала мы получаем экземпляр элемента ListView в методе onCreate(). Далее мы определяем массив типа String. И, наконец, используем адаптер данных, чтобы сопоставить данные с шаблоном разметки. Выбор адаптера зависит от типа используемых данных. В нашем случае мы использовали класс ArrayAdapter.

Отступление

Если вы будете брать строки из ресурсов, то код будет таким:

 final String[] catNames = < getResources().getString(R.string.name1), getResources().getString(R.string.name2), getResources().getString(R.string.name3), getResources().getString(R.string.name4), getResources().getString(R.string.name5), >; 

А будет еще лучше, если вы воспользуетесь специально предназначенным для этого случая типом ресурса . В файле res/values/strings.xml добавьте следующее:

  Рыжик Барсик Мурзик Мурка Васька Томасина Кристина Пушок Дымка Кузя Китти Масяня Симба  

И тогда в коде используйте для объявления массива строк:

 String[] catNames = getResources().getStringArray(R.array.cat_names); 

Запустив проект, вы увидите работающий пример прокручиваемого списка. Правда, созданный список пока не реагирует на нажатия. Но при нажатии выбранный элемент выделяется цветным прямоугольником (в версии Android 2.3 был оранжевый, а в Android 4.0 — синий, потом был серый цвет и т.д.).

ListView на эмуляторе

Собственная разметка

В примере мы используем готовую системную разметку android.R.layout.simple_list_item_1, в которой настроены цвета, фон, высота пунктов и другие параметры. Но нет никаких препятствий самому создать собственную разметку под своё приложение.

Но для начала неплохо бы взглянуть на содержание системной разметки. Студия позволяет увидеть исходный код, достаточно в коде поставить курсор на simple_list_item_1 и нажать на комбинацию клавиш Ctrl+B. Наш simple_list_item_1 выглядит так (в одной из версий):

Мы видим, что в качестве разметки используется TextView с набором атрибутов.

Если говорить о системных разметках, то имеется несколько вариантов. Вкратце ознакомимся с ними.

android.R.layout.simple_list_item_1

Состоит из одного TextView (см. выше)

android.resource.id.text1

android.R.layout.simple_list_item_2

Состоит из двух TextView — один побольше сверху и второй поменьше под ним.

android.resource.id.text1
android.resource.id.text2

android.R.layout.simple_list_item_checked

Справа от CheckedTextView будет находиться флажок

android.resource.id.text1

android.R.layout.activity_list_item

Слева от TextView находится значок ImageView с идентификатором android.resource.id.Icon.

android.resource.id.text1

Создадим свой шаблон для отдельного пункта списка. Для этого в папке res/layout/ создадим новый файл list_item.xml:

В некоторых случаях желательно установить атрибут android:background=»?android:attr/activatedBackgroundIndicator» у родительского элемента, чтобы элементы списка реагировали на нажатие изменением цвета. Можно задать и собственное поведение.

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

 ArrayAdapter adapter = new ArrayAdapter(this, R.layout.list_item, catNames); 

При создании собственного элемента списка, состоящего из TextView можете использовать специальный стиль для минимального размера текста.

Динамическое заполнение списка

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

 @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // получаем экземпляр элемента ListView ListView listView = (ListView) findViewById(R.id.listView); final EditText editText = (EditText) findViewById(R.id.editText); // Создаём пустой массив для хранения имен котов final ArrayListcatNames = new ArrayList<>(); // Создаём адаптер ArrayAdapter, чтобы привязать массив к ListView final ArrayAdapter adapter; adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, catNames); // Привяжем массив через адаптер к ListView listView.setAdapter(adapter); // Прослушиваем нажатия клавиш editText.setOnKeyListener(new View.OnKeyListener() < public boolean onKey(View v, int keyCode, KeyEvent event) < if (event.getAction() == KeyEvent.ACTION_DOWN) if (keyCode == KeyEvent.KEYCODE_ENTER) < catNames.add(0, editText.getText().toString()); adapter.notifyDataSetChanged(); editText.setText(""); return true; >return false; > >); > 

При нажатии на Enter мы получаем текст из текстового поля и заносим его в массив. А также оповещаем адаптер об изменении, чтобы список автоматически обновил своё содержание.

Динамическое заполнение списка

У нас получился каркас для чата, когда пользователь вводит текст и он попадает в список. Далее надо получить текст от другого пользователя и также добавить в список. К слову сказать, слово chat с французского означает «кошка». Но это уже совсем другая история.

Прослушивание событий элемента ListView

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

В этом нам поможет метод setOnItemClickListener элемента ListView и метод OnItemClick() интерфейса AdapterView.OnItemClickListener.

 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() < @Override public void onItemClick(AdapterViewparent, View itemClicked, int position, long id) < Toast.makeText(getApplicationContext(), ((TextView) itemClicked).getText(), Toast.LENGTH_SHORT).show(); >>); 

Теперь при нажатии на любой элемент списка мы получим всплывающее сообщение, содержащее текст выбранного пункта.

Естественно, мы можем не только выводить сообщения, но и запускать новые активности и т.п.

 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() < @Override public void onItemClick(AdapterViewparent, View itemClicked, int position, long id) < TextView textView = (TextView) itemClicked; String strText = textView.getText().toString(); // получаем текст нажатого элемента if(strText.equalsIgnoreCase(getResources().getString(R.string.name1))) < // Запускаем активность, связанную с определенным именем кота startActivity(new Intent(this, BarsikActivity.class)); >> >); 

В метод onItemClick() передаётся вся необходимая информация, необходимая для определения нажатого пункта в списке. В приведенном выше примере использовался простой способ — приводим выбранный элемент к объекту TextView, так как известно, что в нашем случае все пункты являются элементами TextView (Для дополнительной проверки можете использовать оператор instanceOf). Мы извлекаем текст из выбранного пункта и сравниваем его со своей строкой.

Также можно проверять атрибут id для определения нажатия пункта списка.

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

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

 public void onClick(View view) < int activePosition = 0; // первый элемент списка listView.performItemClick(listView.getAdapter(). getView(activePosition, null, null), activePosition, listView.getAdapter(). getItemId(activePosition)); >

Код громоздкий, но работоспособный.

ListView не реагирует на нажатия

В некоторых случаях нажатия на пунктах меню не срабатывают. Ниже приводятся несколько возможных причин.

Элемент списка содержит CheckBox, который также имеет свой слушатель нажатий. Попробуйте удалить фокус у него:

 android:focusable="false" android:focusableInTouchMode="false" 

Попробуйте переместить OnItemClickListener перед установкой адаптера. Иногда помогает.

Элемент списка содержит ImageButton. Установите фокус в false:

 ImageButton imageButton = (ImageButton) convertView.findViewById(R.id.imageButton); imageButton.setFocusable(false); 

Элемент списка содержит TextView. Если вы используете атрибут android:inputType=»textMultiLine», то замените его на android:minLines/android:maxLines.

Элемент списка содержит TextView, содержащий ссылку на веб-страницу или электронный адрес. Удалите атрибут android:autoLink.

Настраиваем внешний вид ListView

У ListView есть несколько полезных атрибутов, позволяющих сделать список более привлекательным. Например, у него есть атрибут divider, который отвечает за внешний вид разделителя, а также атрибут dividerHeight, отвечающий за высоту разделителя. Мы можем установить какой-нибудь цвет или даже картинку для разделителя. Например, создадим для разделителя цветовой ресурс с красным цветом, а также ресурс размера для его высоты:

 #FF0000 2dp 

Далее присвоим созданный ресурс атрибуту divider, а также зададим его высоту в атрибуте dividerHeight у нашего элемента ListView:

ListView с красным разделителем

Если вас не устраивает стандартный разделитель, что можете нарисовать какую-нибудь волнистую черту, сохранить ее в PNG-файле и использовать как drawable-ресурс. Проделайте это самостоятельно.

Можно работать с данными атрибутами программно:

 ColorDrawable divcolor = new ColorDrawable(Color.DKGRAY); listView.setDivider(divcolor); listView.setDividerHeight(2); 

Если хотите убрать разделители, то используйте прозрачный цвет.

 listView.setDivider(getResources().getDrawable(android.R.color.transparent)); 

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

Обратите внимание, что по умолчанию разделитель не выводится перед первым и последним элементом списка. Если вы хотите изменить эти настройки, то используйте свойства Footer dividers enabled (атрибут footerDividersEnabled) и Header dividers enabled (атрибут headerDividersEnabled):

 . android:footerDividersEnabled="true" android:headerDividersEnabled="true" . 

Пользовательский селектор

Мы уже видели, что по умолчанию выбранный элемент списка выделяется при помощи цветной полоски. Данный селектор также можно настроить через атрибут android:listSelector. Создайте какую-нибудь текстуру для селектора и привяжите его через ресурс. Вот образец текстурированного ореола желтого цвета для селектора.

ListView с пользовательским селектором

Нужно подготовить сначала файл res/drawable/selector.xml:

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

 listView.requestFocusFromTouch(); listView.setSelection(4); // выбираем 5 пункт списка 

Множественный выбор

ListView позволяет выбирать не только один пункт, но и несколько. В этом случае нужно установить свойство Choice Mode в значение multiplyChoice, что соответствует атрибуту android:choiceMode=»multipleChoice».

Также множественный выбор можно установить программно при помощи метода setChoiceMode(ListView.CHOICE_MODE_MULTIPLE).

Теперь, если создать массив строк, например список продуктов для кошачьего завтрака, то получим следующий результат.

 public class MultiChoiceListViewActivity extends Activity < ListView choiceList; TextView selection; String[] foods = < "Молоко", "Сметана", "Колбаска", "Сыр", "Мышка", "Ананас", "Икра черная", "Икра кабачковая", "Яйцо" >; @Override public void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.main); selection = (TextView) findViewById(R.id.textView1); choiceList = (ListView) findViewById(R.id.listView1); ArrayAdapteradapter = new ArrayAdapter(this, android.R.layout.simple_list_item_multiple_choice, foods); // choiceList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); choiceList.setAdapter(adapter); > > 

Множественный выбор

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

 choiceList.setOnItemClickListener(new OnItemClickListener() < @Override public void onItemClick(AdapterViewparent, View v, int position, long id) < // Очистим TextView selection.setText(""); // получим булев массив для каждой позиции списка // Объект SparseBooleanArray содержит массив значений, к которым можно получить доступ // через valueAt(index) и keyAt(index) SparseBooleanArray chosen = ((ListView) parent).getCheckedItemPositions(); for (int i = 0; i < chosen.size(); i++) < // если пользователь выбрал пункт списка, // то выводим его в TextView. if (chosen.valueAt(i)) < selection.append(foods[chosen.keyAt(i)] + " "); >> > >); 

Множественный выбор

Если нужно получить отдельно список выбранных и невыбранных элементов списка, то можно написать следующее:

 choiceList.setOnItemClickListener(new OnItemClickListener() < @Override public void onItemClick(AdapterViewparent, View v, int position, long id) < // Очистим TextView перед вставкой нового контента. selection.setText(""); int cntChoice = choiceList.getCount(); String checked = ""; String unchecked = ""; SparseBooleanArray sparseBooleanArray = choiceList .getCheckedItemPositions(); for (int i = 0; i < cntChoice; i++) < if (sparseBooleanArray.get(i) == true) < checked += choiceList.getItemAtPosition(i).toString() + "\n"; // выводим список выбранных элементов //selection.setText(checked); >else if (sparseBooleanArray.get(i) == false) < unchecked += choiceList.getItemAtPosition(i).toString() + "\n"; // выводим список невыбранных элементов selection.setText(unchecked); >> > >); 

Переменная checked будет содержать список выбранных элементов, а переменная unchecked — список невыбранных элементов.

Следует отметить, что в примерах использовался старый метод getCheckedItemPositions(), доступный с Android 1. В Android 2.2 появился новый метод getCheckedItemIds(). Учтите, что с новым методом можно получить массив только выбранных элементов, хотя в большинстве случаев этого достаточно. Но данный метод требует своих заморочек и в данном моём примере он не заработал.

Подсветка нажатий

На данный момент используется следующая техника подсвечивания элементов списка при нажатии. Здесь учитывается версия Android (до и после API 21).

res/values/colors.xml

   #cccccc #ff64c2f4  

res/drawable/item_selector.xml

res/drawable-v21/item_selector.xml

Разница заключается в том, что в версии 21 рекомендуется использовать серый цвет с применением ripple.

Созданные ресурсы следует применить для фона элемента списка (list_item.xml): android:background=»@drawable/item_selector».

Для проверки установим режим singleChoice для активации выбранного элемента списка.

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

Создадим стиль для планшетов.

res/values-sw600dp/styles.xml

      

А в обычном styles.xml оставим заглушку.

Теперь применим стиль к списку и нужное поведение с активацией будет применяться только на планшетах.

При повороте выбранный пункт списка может оказаться за пределами экрана. С помощью метода smoothScrollToPosition() мы можем автоматически прокрутить список к нужному месту. Код показан в продвинутых приёмах.

Кнопка под списком

Если вы хотите разместить кнопку под списком, которая бы не зависела от количества элементов в ListView, то воспользуйтесь весом (layout_weight).

Плавная прокрутка в начало списка или любую позицию

У списка есть специальный метод smoothScrollToPosition(), позволяющий плавно прокрутить до нужного места. Достаточно в методе указать номер позиции для прокрутки:

 ListView listView = (ListView) findViewById(R.id.listView); int n = 0; // прокручиваем до начала listView.smoothScrollToPosition(n); 

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

Настраиваем прокрутку

У ListView есть атрибуты для настройки внешнего вида полосы прокрутки

android:scrollbarTrackVertical="@drawable/scrool_bg" android:scrollbarThumbVertical="@drawable/scroll"

Аналогично это применимо к полосам прокрутки у ScrollView, EditText и т.д.

ListActivity

Если вам нужна форма, состоящая только из списка, то вам проще воспользоваться системным классом ListActivity вместо стандартного Activity. Именно такой подход описан в документации по ListView. Пример работы описан в статье ListActivity — создаём прокручиваемый список.

Настройка цвета при достижении края прокрутки

Когда прокрутка достигает конца (сверху или снизу), то выводится цветовой эффект. В API 21 появилась возможность управлять этим эффектом через атрибут colorEdgeEffect:

   

Далее применяем как тему.

Для более старых устройств такую настройку сделать нельзя.

Нет данных для отображения

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

 listView.setEmptyView(view); 

Добавляем шапку и подвал

В подвалах часто живут коты, поэтому списки желательно также снабжать подвалом (и шапкой). У ListView есть такая возможность, давайте воспользуемся этим.

Элементами шапки и подвала могут послужить View, которые могут быть добавлены к списку сверху и снизу через методы addHeaderView() и addFooterView(). В принципе оба элемента одинаковы, только добавляются в разные места списка.

Полный варианты методов:

 addHeaderView (View v, Object data, boolean isSelectable) addFooterView (View v, Object data, boolean isSelectable) 
  • v – компонент, который отобразится, как пункт списка
  • data – объект, связанный с этим пунктом списка
  • isSelectable – можно ли будет кликать на пункт или выделять его

Интересно, что до версии API 19 шапку и подвал можно было добавлять только до присвоения списку адаптера, иначе возникала ошибка. Теперь можно добавлять в любое время, ошибки не будет и список будет работать.

Для демонстрации подойдёт любой урок со списком. Дополнительно добавим два файла для шапки и подвала (можно также воспользоваться системной разметкой). Если по структуре шапка и подвал одинаковы, то можно обойтись и одним файлом. Но для разнообразия сделаем их немного разными.

res/layout/list_header.xml

res/layout/list_footer.xml

Осталось подключить шапку и подвал к списку. Напоминаю, что можете использовать любой пример со списком. Поэтому часть кода опущу.

 View header; View footer; // Их может быть несколько // View header2; // View footer2; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ListView listView = findViewById(R.id.listView); // создаем шапку и подвал header = createHeader("Текст для шапки"); footer = createFooter("Подвал"); // можете добавлять несколько шапок и подвалов //header2 = createHeader("Шапка 2"); //footer2 = createFooter("Подвал 2"); listView.addHeaderView(header); listView.addFooterView(footer); //listView.addHeaderView(header2, "Текст для другой шапки", false); //listView.addFooterView(footer2, "Текст для другого подвала", false); // Какой-то адаптер mAdapter = new ZodiacAdapter(this); listView.setAdapter(mAdapter); >// создание шапки View createHeader(String text) < View view = getLayoutInflater().inflate(R.layout.list_header, null); ((TextView)view.findViewById(R.id.textViewHeaderText)).setText(text); return view; >// создание подвала View createFooter(String text)

Никто не запрещает использовать несколько шапок и подвалов в списке. Соответствующий код закомментирован.

Шапка состоит из двух текстовых меток. В первой метке текст задан в макете, а во второй текст устанавливается программно. Подвал сделан ещё проще — только одна метка, текст задан в макете. Естественно, вы можете реализовать более сложный макет в вашем приложении.

Шапку и подвал можно удалить программно через методы removeHeaderView() и removeFooterView().

О RecyclerView и выделении элементов

Привет, %username%!
В этой статье я хочу рассказать немного о новом виджете в Android SDK — RecyclerView, о реализации выделения элементов и нескольких полезных «рецептах» при работе с ним.

1. Немного о ViewHolder’ах

До выхода в свет Android SDK 5.0 Lollipop для отображения списков и таблиц использовались виджеты ListView и GridView. Общей рекомендацией при работе с этим виджетом было использование паттерна ViewHolder. Суть паттерна заключается в том, что для каждого элемента списка создаётся объект, хранящий ссылки на отдельные вьюхи внутри элемента. Таким образом, приходится выполнять достаточно дорогой вызов findViewById(int) только один раз при создании элемента.

Пример типичного ViewHolder’а прямиком из руководств гугла:

static class ViewHolder

Cсылка на такой холдер для каждого элемента сохраняется в корневом layout’е, используя метод setTag(int, Object) (с моей точки зрения тот ещё костыль).

2. Вкратце о RecyclerView

К выходу Android SDK 5.0 Lollipop разработчиков Google наконец-то озарило, что два вышеперечисленных виджета морально устарели и нужно бы заменить их на нечто более стильное, модное и молодёжное. Было принято решение не переделывать старые виджеты, а написать новый. Так и появился на свет RecyclerView. Так в чём же его отличия, спросите вы?

  1. Сам виджет больше не берёт на себя обязанность по размещению элементов. Для этого появились LayoutManager’ы.
  2. Паттерн ViewHolder стал обязательным. Причём виджет научился заново использовать уже созданные ViewHolder’ы и удалять уже не используемые (отсюда и название), что благоприятно сказывается на быстродействии и размере используемой памяти.
  3. Новый, удобный способ работы с анимацией.

Что я понимаю под сложностью? Если что-то не работало в ListView (или работало не так как задумано) всегда можно было залезть в исходники, разобраться, в чём ошибка, исправить её, подоткнуть костылей тут и там и всё начинало работать. RecyclerView гораздо сложнее в плане логики работы, мозг сломаешь, пока разберёшься. Я пытался, но забросил, уж слишком много времени и сил для этого нужно.

Вторая проблема — банальное отсутствие функционала, присутствовавшего в ListView и GridView. За примерами далеко ходить не надо — стандартный функционал выделения элементов (дальнейшая тема этой статьи), отступы между элементами. Раньше, чтобы добавить всё это, нужно было написать буквально пару строчек кода, теперь на это уйдут уже десятки строк. Есть анимации, но только для добавления/удаления/редактирования элемента. Если же вы хотите, например, анимировать частичное изменение элемента, то к вам в дверь уже стучится птица обломинго. Виджет не поддерживает анимацию части элемента, и если анимировать элемент извне (из адаптера, например), то лучше этого не делать — подобные манипуляции оставляют элементы виджета (те самые ViewHolder’ы) в неопределённом состоянии, что приводит к совершенно фантастическому поведению вашего списка.

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

3. Выделяем элементы

Итак, перейдём к главному — к технической части статьи. Поговорим о том, как выделять элементы в RecyclerView. Сразу оговорюсь — идея реализации почерпнута из замечательной серии статей Билла Филлипса про RecyclerView (ссылки в конце), так что всё нижеследующее можно считать вольным кратким пересказом.
В ListView для выделения элементов использовался метод setChoiceMode(int), RecyclerView же понятия не имеет, что элементы могут выделяться, поэтому мы должны научить этому наш адаптер.

Схема такая:
На диаграмме я схематично обозначил связи между объектами. Пунктирные стрелки — ссылки, остальные — вызовы методов. Зелёным я обозначил объекты, которые непосредственно реализуют логику выделения.

  1. ViewHolderWrapper устанавливает себя в качестве ClickListener’а для корневой вьюхи ViewHolder’а и начинает получать события onClick и onLongClick. В зависимости от реализации он может просто проксировать эти события в HolderClickObservable (ViewHolderClickWrapper), либо, исходя из текущего статуса SelectionHelper’а выделять элемент вызовом setItemSelected(ViewHolder, boolean) (ViewHolderMultiSelectionWrapper).
  2. SelectionHelper сохраняет информацию о выделенных элементах и оповещает слушателей (SelectionObserver) об изменении выделения.
  3. Слушатель (в нашем случае адаптер) отвечает за визуальное отображение выделения элемента, а также взаимодействия с ним (на диаграмме вызов startActionMode у Activity).

1. Создать SelectionHelper и зарегистрировать слушателей (в данном случае сам адаптер, но это может быть и Activity, например)

mSelectionHelper = new SelectionHelper(mHolderTracker); mSelectionHelper.registerSelectionObserver(this);

2. Обернуть создаваемые ViewHolder’ы во ViewHolderWrapper нужного типа.

@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int position)

Метод wrapSelectable(ViewHolder) у SelectionHelper’а:

public H wrapSelectable(H holder)

3. Не забывать прицеплять наши ViewHolder’ы к SelectionHelper’у в методе onBindViewHolder(ViewHolder, int) нашего адаптера!

@Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position)

Это нужно по причине того, что пока нет другого способа получить от RecyclerView список используемых в настоящий момент ViewHolder’ов. Если не вести их учёт, при необходимости обновить отрисовку выделения у всех выбранных элементов (пользователь закрыл ActionMode, например), SelectionHelper просто не сможет этого сделать. Вьюхи останутся выглядеть выделенными, когда по факту таковыми не будут.

Вы спросите — «А почему бы просто не запоминать выделяемые ViewHolder’ы в методе setItemSelected(ViewHolder, boolean)?». Тут как раз сказывается особенность RecyclerView — он использует заново уже созданные ViewHolder’ы.

  1. Открываем приложение. На экране 10 элементов — 10 ViewHolder’ов создано для них.
  2. Запускаем ActionMode, начинаем выделять элементы — 1,2,3.
  3. Прокручиваем вьюху вниз, видим элементы с 10 по 20. Думаете, что теперь в памяти висит 20 ViewHolder’ов? Как бы ни так! Для части данных RecyclerView создаст новые ViewHolder’ы, а для другой заново использует уже имеющиеся. Причём неизвестно в каком порядке.
  4. Теперь если мы прокрутим вьюху обратно вверх, часть из наших 10 ViewHolder’ов будет уничтожена, вместо них будут созданы новые. Оставшаяся часть будет использована заново и совершенно не обязательно для тех же позиций.
  5. Отменяем ActionMode. SelectionHelper должен раскидать слушателям уведомления о сменившемся выделении на элементах с указанием ViewHolder’а для каждого элемента, но он уже не владеет актуальными данными, все Holder’ы поменялись!

И здесь становится очевидным ещё один важный момент — нельзя сохранять строгие ссылки (strong reference) на ViewHolder’ы! Они могут быть удалены из RecyclerView в зависимости от фазы Луны и желания левой пятки Ларри Пейджа. В этом случае, если мы будем хранить строгие ссылки на них, случится утечка памяти. Поэтому для хранения ссылок в ViewHolderWrapper и WeakHolderTracker используются только WeakReference.

private abstract class ViewHolderWrapper implements android.view.View.OnClickListener < protected final WeakReferencemWrappedHolderRef; protected ViewHolderWrapper(RecyclerView.ViewHolder holder) < mWrappedHolderRef = new WeakReference<>(holder); > >

WeakHolderTracker

public class WeakHolderTracker < private final SparseArray> mHoldersByPosition = new SparseArray<>(); public void bindHolder(RecyclerView.ViewHolder holder, int position) < mHoldersByPosition.put(position, new WeakReference<>(holder)); > @Nullable private RecyclerView.ViewHolder getHolder(int position) < WeakReferenceholderRef = mHoldersByPosition.get(position); if (holderRef == null) < mHoldersByPosition.remove(position); return null; >RecyclerView.ViewHolder holder = holderRef.get(); if (holder == null || (holder.getAdapterPosition() != position && holder.getAdapterPosition() != RecyclerView.NO_POSITION)) < mHoldersByPosition.remove(position); return null; >return holder; > public List getTrackedHolders() < Listholders = new ArrayList<>(); for (int i = 0; i < mHoldersByPosition.size(); i++) < int key = mHoldersByPosition.keyAt(i); RecyclerView.ViewHolder holder = getHolder(key); if (holder != null) < holders.add(holder); >> return holders; > >

4. Также важно не забыть в onBindViewHolder(ViewHolder, int) визуально отобразить выделение если оно есть (если нет — не забыть убрать!). Вы же помните, что для не выделенного элемента может быть использован ViewHolder, ранее использовавшийся для не выделенного и наоборот?
У меня это реализовано следующим образом:

4.1. SelectableRecyclerViewAdapter.onBindViewHolder(ViewHolder, int)

Checkable view = (Checkable) viewHolder.itemView; view.setChecked(mSelectionHelper.isItemSelected(position));

4.2. layout-файл для элемента
4.3. Виджет CheckableAutofitHeightFrameLayout

public class CheckableAutofitHeightFrameLayout extends FrameLayout implements Checkable < private static final int[] CHECKED_STATE_SET = ; private boolean mIsChecked; private boolean mIsCheckable; public CheckableAutofitHeightFrameLayout(Context context) < super(context); >public CheckableAutofitHeightFrameLayout(Context context, AttributeSet attrs) < super(context, attrs); >public CheckableAutofitHeightFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) < super(context, attrs, defStyleAttr); >@TargetApi(Build.VERSION_CODES.LOLLIPOP) public CheckableAutofitHeightFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) < super(context, attrs, defStyleAttr, defStyleRes); >@Override protected int[] onCreateDrawableState(int extraSpace) < final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); if (isChecked()) < mergeDrawableStates(drawableState, CHECKED_STATE_SET); >return drawableState; > @Override public boolean isCheckable() < return mIsCheckable; >@Override public void setCheckable(boolean isCheckable) < boolean wasCheckable = isCheckable(); mIsCheckable = isCheckable; if (!isCheckable && isChecked()) < setChecked(false); >else if (wasCheckable ^ mIsCheckable) < refreshDrawableState(); >> @Override public void setChecked(boolean isChecked) < boolean wasChecked = isChecked(); mIsChecked = isCheckable() && isChecked; if (wasChecked ^ mIsChecked) < refreshDrawableState(); >> @Override public boolean isChecked() < return mIsChecked; >@Override public void toggle() < setChecked(!mIsChecked); >@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) < //noinspection SuspiciousNameCombination super.onMeasure(widthMeasureSpec, widthMeasureSpec); >>

CheckableAutofitHeightFrameLayout добавляет к FrameLayout всего 2 вещи: во-первых, он всегда квадратный (смотри onMeasure(int, int)) и, во-вторых, добавляет к DrawableStates (те самые, которые используются в xml) состояние state_checked. В результате, для отображения выделения у такого layout’а можно использовать StateListDrawable на вроде этого:

и все детали отображения уползают в xml-ки, в Java только нужно установить соответствующие состояния.

5. Передать событие onSelectableChanged(boolean) в Activity и запустить ActionMode:

@Override public void onSelectableChanged(boolean isSelectable) < if (isSelectable) < mActivity.startActionMode(); >>

Запуск ActionMode в Activity

public class GalleryActivity extends Activity < private final ActionModeCallback mActionModeCallback = new ActionModeCallback(); private SelectableRecyclerViewAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.gallery); int columnWidth = getResources().getDimensionPixelSize(R.dimen.column_width); int vSpacing = getResources().getDimensionPixelSize(R.dimen.grid_spacing_vertical); int hSpacing = getResources().getDimensionPixelSize(R.dimen.grid_spacing_horizontal); recyclerView.setLayoutManager(new GridAutofitLayoutManager(this, columnWidth)); recyclerView.addItemDecoration(new GridSimpleSpacingDecoration(hSpacing, vSpacing)); DataSetdataSet = new DummyImagesDataSet(); mAdapter = new SelectableRecyclerViewAdapter(this, dataSet); recyclerView.setAdapter(mAdapter); > public void startActionMode() < startActionMode(mActionModeCallback); >private class ActionModeCallback implements ActionMode.Callback, SelectionObserver < private ActionMode mActionMode; @Override public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) < return false; >@Override public void onDestroyActionMode(ActionMode actionMode) < SelectionHelper selectionHelper = mAdapter.getSelectionHelper(); selectionHelper.unregisterSelectionObserver(this); mActionMode = null; selectionHelper.setSelectable(false); >@Override public boolean onCreateActionMode(ActionMode actionMode, Menu menu) < mActionMode = actionMode; mActionMode.getMenuInflater().inflate(R.menu.gallery_selection, menu); mAdapter.getSelectionHelper().registerSelectionObserver(this); return true; >@Override public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) < switch (menuItem.getItemId()) < case R.id.menu_toast: Toast.makeText(GalleryActivity.this, R.string.text_simple_toast, Toast.LENGTH_SHORT).show(); break; >return true; > @Override public void onSelectedChanged(RecyclerView.ViewHolder holder, boolean isSelected) < if (mActionMode != null) < int checkedImagesCount = mAdapter.getSelectionHelper().getSelectedItemsCount(); mActionMode.setTitle(String.valueOf(checkedImagesCount)); >> @Override public void onSelectableChanged(boolean isSelectable) < if (!isSelectable) < mActionMode.finish(); >> > >

Как вы видите, при запуске ActionMode, она регистрирует себя как SelectionObserver. Таким образом, можно обновлять количество выделенных элементов в заголовке. Не забудьте вызвать unregisterSelectionObserver(SelectionObserver) при закрытии!

4. Заключение + Бонус

Кажется, с выделением разобрались. Весь исходный код также можно посмотреть на GitHub.

В заключение вкратце приведу ещё несколько фишек для работы с RecyclerView, которые вы можете найти в примере.

1. Если не нужно выделять элементы, а нужно просто обрабатывать нажатия, вместо ViewHolderMultiSelectionWrapper оборачивайте элементы в ViewHolderClickWrapper методом wrapClickable(ViewHolder). Сам адаптер в таком случае будет выглядеть примерно так:

Скрытый текст

public class SelectableRecyclerViewAdapter extends RecyclerView.Adapter implements SelectionObserver < private final SelectionHelper mSelectionHelper; public SelectableRecyclerViewAdapter() < mSelectionHelper = new SelectionHelper(); mSelectionHelper.registerSelectionObserver(this); >public SelectionHelper getSelectionHelper() < return mSelectionHelper; >@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) < LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext()); ImageViewHolder holder = new ImageViewHolder( inflater.inflate(R.layout.gallery_item, viewGroup, false)); return mSelectionHelper.wrapClickable(holder); >@Override public void onHolderClick(RecyclerView.ViewHolder holder) < // perform item click >@Override public boolean onHolderLongClick(RecyclerView.ViewHolder holder) < // perform item long click return false; >//. >

2. GridLayoutManager не умеет автоматически подбирать количество столбцов в зависимости от ширины контента. Я добавил этот функционал в

GridAutofitLayoutManager

public class GridAutofitLayoutManager extends GridLayoutManager < private int mColumnWidth; private boolean mColumnWidthChanged = true; public GridAutofitLayoutManager(Context context, int columnWidth) < /* Initially set spanCount to 1, will be changed automatically later. */ super(context, 1); setColumnWidth(checkedColumnWidth(context, columnWidth)); >public GridAutofitLayoutManager(Context context, int columnWidth, int orientation, boolean reverseLayout) < /* Initially set spanCount to 1, will be changed automatically later. */ super(context, 1, orientation, reverseLayout); setColumnWidth(checkedColumnWidth(context, columnWidth)); >private int checkedColumnWidth(Context context, int columnWidth) < if (columnWidth return columnWidth; > public void setColumnWidth(int newColumnWidth) < if (newColumnWidth >0 && newColumnWidth != mColumnWidth) < mColumnWidth = newColumnWidth; mColumnWidthChanged = true; >> /* I don't actually remember why I choose to set span count in onLayoutChildren, I wrote this class some time ago. But the point is we need to do so after view get measured, so we can get its height and width. */ @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) < if (mColumnWidthChanged && mColumnWidth >0) < int totalSpace; if (getOrientation() == VERTICAL) < totalSpace = getWidth() - getPaddingRight() - getPaddingLeft(); >else < totalSpace = getHeight() - getPaddingTop() - getPaddingBottom(); >int spanCount = Math.max(1, totalSpace / mColumnWidth); setSpanCount(spanCount); mColumnWidthChanged = false; > super.onLayoutChildren(recycler, state); > >

Виджет подбирает ширину столбцов в зависимости от параметра columnWidth. Важный момент: если доступная ширина 330 пикселей, а мы передадим желаемую ширину 100, в итоге в таблице будет 3 столбца по 110 пикселей и элементы будут этой ширины. Именно поэтому я также сделал CheckableAutofitHeightFrameLayout автоматически изменяющим свою высоту в зависимости от ширины.

3. Для добавления отступов между элементами можно выставить paddingTop/Left у RecyclerView и marginRight/Bottom у элементов, однако это выглядит как костыль. Рекомендуемым способом является добавление ItemDecoration к RecyclerView. В примере можно найти несколько. Для добавления отступов к обычному GridLayoutManager (под «обычным» я имею ввиду GridLayoutManager со стандартным SpanSizeLookup, в нём каждый элемент занимает 1 span) можно использовать

GridSimpleSpacingDecoration

 public class GalleryActivity extends Activity < private final ActionModeCallback mActionModeCallback = new ActionModeCallback(); private SelectableRecyclerViewAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.gallery); int columnWidth = getResources().getDimensionPixelSize(R.dimen.column_width); int vSpacing = getResources().getDimensionPixelSize(R.dimen.grid_spacing_vertical); int hSpacing = getResources().getDimensionPixelSize(R.dimen.grid_spacing_horizontal); recyclerView.setLayoutManager(new GridAutofitLayoutManager(this, columnWidth)); recyclerView.addItemDecoration(new GridSimpleSpacingDecoration(hSpacing, vSpacing)); DataSetdataSet = new DummyImagesDataSet(); mAdapter = new SelectableRecyclerViewAdapter(this, dataSet); recyclerView.setAdapter(mAdapter); > //. 

Вот, кажется, и всё. Спасибо за внимание и чтобы ваши списки никогда не тормозили!

5. Полезные ссылки

  1. Первая часть статьи Билла Филлипса о RecyclerView. Базовая информация (на английском)
  2. Вторая часть статьи Билла Филлипса о RecyclerView. О выделении элементов (на английском)
  3. Обзорная статья про RecyclerView (на английском)
  4. Информация о паттерне ViewHolder для ListView (на английском)
  5. Делаем parallax header в RecyclerView
  6. Базовая информация о RecyclerView и CardView
  7. О переходе на RecyclerView
  8. Типы ссылок в Java
  9. Полезная библиотека для работы с RecyclerView (спасибо пользователю artemgapchenko)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *