Программирование под android
- Войдите на сайт для отправки комментариев
Приветствую!
Стар становлюсь и ленив - не хочется метаться по форумам, регистрироваться, получать сертификаты ****** :) - поэтому спрошу для начала здесь.
Итак: есть тут кто, кто баловался (или сурьёзно) программингом под андроид в Android Studio? Хочется немного пообщаться, позадавать разные вопросы, пополучать ответы.
Из первых вопросов - есть парочку. Но для начала вводные: хочу освоить андроид, уже пару лет как в полный рост стоит вопрос написания приложения для контроля/мониторинга теплицы (конфигуратор под винду - есть, вебморда - есть, поддержка MQTT - есть, удалённое управление через внешний сервис - есть, а вот приложухи под андроид - нема).
Первый этап, как известно - это продумывание архитектуры. Приложение должно уметь соединяться с контроллером как по IP-адресу в локальной сети, так и напрямую к Wi-Fi-точке доступа, которую выставляет наружу контроллер. Со всякими permission в манифесте приложения - более-менее понятно.
Вопрос в архитектуре. Как это вижу я: есть глобальный класс, скажем TransportFactory, который, по запросу - создаёт транспорт нужного типа, возвращает его singleton. Например, попросили мы подключиться по IP, вызвали (псевдокод):
ITransport ipTransport = TransportFactory.createTransport(TransportType.IP);
Затем подписываемся на события транспорта, типа OnConnect и т.п. Коннектимся, куда надо, и вперёд - для простоты архитектуры не будем вводить слой Endpoint, т.к. конечная точка подсоединения - у нас одна, одновременно работаем только с одним контроллером, при необходимости - просто переподключаемся, и всё.
Транспорт нам предоставляет метод отсыла запроса на контроллер, например, Transport.get(command). И по коллбэку он дёргает вызывающую сторону, когда разбирает ответ от контроллера. Тут тоже более менее всё очевидно, механизм знаком.
Собственно, чего я пока не догоняю:
1. Как сделать, чтобы в масштабе всего приложения (неважно, какое Activity сейчас на экране) тикал какой-нибудь таймер (когда приложение активно, конечно), по тику которого я буду обрабатывать очередь команд к транспорту? Насколько правильно будет использовать в этом случае Service (что-то мне подсказывает, что сервис - не для того)?
2. Как быть с life cycle у Activity, ведь если Activity плюнет запрос на команду в транспорт (работа асинхронная), указав себя, как получателя ответа, то может произойти ситуация, когда Activity либо уже убито, либо - пересоздано при повороте экрана. Чо делать? Делать своё глобальное хранилище активных Activity (сорри за тавтологию), и лепить посредника, разруливающего эту ситуацию?
3. Простой способ сделать все Activity реагирующими на события транспорта? Наследовать их от своего интерфейса?
4. Если у кого под рукой ссылка - дайте пж инфу по Design guide для GUI приложух: что принято, какие пожелания и т.п.
5. Если кто может предложить ссылочку на хорошие текстовые уроки (кроме https://startandroid.ru/ - его читаю) - буду признателен.
Короче - если кому интересно тут пообсуждать и поучить дурачка уму-разуму - я только "за". Заранее благодарен.
По поводу таймера - вроде вопрос снят: https://ru.stackoverflow.com/questions/25779/%D0%A2%D0%B0%D0%B9%D0%BC%D0%B5%D1%80-%D0%B2-android-%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B8
регистрироваться, получать сертификаты ******** :)
ОФФ.
Не, если нужен, только скажи.))))
Я кое-чего делаю в Студии ;))) Задавай свои вопросы. На первый ты ответил.
2. не делай так. делай работу с транспортом от фонового объекта, тогда он не умрет и не перезапустится. Нужны подробности, скорее всего ты велосипед изобретаешь. Ты не поверишь, сколько всего уже под Андроид написано. ;)))
3. например так. а можно, как я написал выше, обрабатывать события в фоне, а в активити кидать что-то умное.
4. мода всё время меняется. Вроде уже и материал-дезайн выходит из моды. Мне нравятся примочки из материала, только их часто проще самому написать. Я вот себе надписи в "пулях" - сделал за день, вместе с лейаутом, адаптирующимся под надпись размером, реакцией на нажатие ... блекджеком и арфистками... Щас подумал - надо их еще "дрожать" научить, с вибрацией ;)))
Вообще всегда модна лаконичность. Дизайн а-ла хром браузер под Андроид - по мне - так лучший на все времена... Хотя - на вкус и цвет...
======================
самое главное - вообще всё есть в developer.android.com. Остальное есть в куче уроков, которые просто по яндеху открваются.
-----------------------
Как пишу я:
Я работаю под Линухом. Тачка под Студию особенная, я долго шел к конфигурации и осталось куда апгрейлить. Я скажу честно, моя конфигурация - минимально допустимая, когда была слабее - был онанизм, вместо программирования.
Итак: CoreI5 4 ядра, 16Гиг, SSD240. Еще раз - это минимум!
на Линухе 4 рабочих стола. На одном - студия, на остальных трёх - файерфоксы, у каждого открыто по 20 вкладок, в основном поиск, примеры и developer.android.com в разных местах. В Студии открыто НЕСКОЛЬКО проектов. Для отработки кусочков проекта. Разных активити, или даже только частей.
Итак: CoreI5 4 ядра, 16Гиг, SSD240. Еще раз - это минимум!
Так точно,это минимум. Ещё пару вопросов тогда: линукс какой и разрешение на мониторе какое?
Итак: CoreI5 4 ядра, 16Гиг, SSD240. Еще раз - это минимум!
Так точно,это минимум. Ещё пару вопросов тогда: линукс какой и разрешение на мониторе какое?
Ты для дополнения совета интересуешься или самому нужно?
1. Линух нужен 64-битный, иначе очень неспешно эмуляция тилипона будет работать, без поддержки ядром. У меня давняя любовь к дистрибутиву Минт, им и пользуюсь.
2. Очень желательно 1080. В Линухе это решается виртуальным экраном. Если под Виндой - строго FullHD надо.Но у меня 768, из-за этого есть места в Студии, итить её писателей в сраку, где вне экрана одна из важных настроек... честно говоря пока только одна такая нашлась. ;)) ФулШД - системые требования к Студии. Я на 768 живу вроде без особого напряга.
Я кое-чего делаю в Студии ;))) Задавай свои вопросы. На первый ты ответил.
Спасибо ;) Собственно, потихоньку разбираюсь в текущей моде :)). Решил делать на Navigation Drawer и фрагментах, а для связи элементов UI и обновляемых данных - буду юзать ViewModel, как показали примеры, которые набросал - вещь чрезвычайно мощная (ну кто бы мог сомневаться, там унутре паттерн Observer, и подписка на изменения - легчайшая).
С ViewModel можно снять львиную долю головняков с жизненным циклом Activity и Fragment, имхо. Ну а с остальным - разберусь.
Снял видосик, там по таймеру в модель приходят "новые" данные, при этом видно, что один Fragment, выводящий эти данные - юзается и как отдельный фрагмент, так и в составе динамически созданных страниц компонента ViewPager (который, в свою очередь, лежит внутри Fragment - прям Кащеева игла :)) ). Короче, то, что мне нужно было, т.к. интерфейс придётся создавать "на лету", поскольку у контроллера, в зависимости от прошивки - доступны те или иные модули.
Само видео, вдруг кому интересно: https://youtu.be/lJgbM1EDCBI
Убил полдня, как показала практика, сам язык - ну шоб я так жил, он простой, как три рубля. А вот SDK - надо читать, там чего только не наворочено. Ну и - привыкать к архитектуре, как обычно.
Надеюсь, всё получится.
Ты для дополнения совета интересуешься или самому нужно?
Самому нужно. Вот думаю на 2К монитор пересесть, вот и спрашиваю.
Вдруг кому будет интересно - столкнулся с одним неочевидным нюансом при использовании таймеров внутри фрагментов, а именно: хочется, чтобы каждый фрагмент (по сути, он отображает какой-то статус, например, состояние полива) - мог запрашивать с контроллера информацию по таймеру, пока фрагмент активен.
Так вот - если фрагмент просто помещается в корневой фрагмент - то перекрыв onPause, внутри можно легко остановить таймер, и всё - видно, что таймер не тикает, вызывается onDestroy у фрагмента.
Однако, если внутри фрагмента поюзать ViewPager, в который через свой адаптер поместить несколько фрагментов, как вкладки - то если у такого размещённого фрагмента есть унутре тикающий таймер, то при переключении на другой экран - вызывается только onPause у родительского фрагмента, и... всё. То есть несмотря на то, что ViewPager вроде бы как владеет помещёнными тудыть фрагментами - он ни слухом ни духом про то, что родительский фрагмент - уже не алё. Т.е. нет всплытия событий, скажем так.
Решил (не знаю, правильно или нет) - добавлением в адаптер метода pause, в котором делаю просто:
Но что-то меня терзают смутные сомнения. Дело в том, что при таком раскладе не вызывается событие onDestroy, т.е. андроид сам ничего с этими вложенными фрагментами не делает. Как бы память не засрать. Хотя, с другой стороны - родительский фрагмент уничтожается, и, по идее - память, выделенная под дочерние, тоже должна почистится.
Короче: буду в андроид студии смотреть память, для начала.
А так - песня, а не возможности! Транспорты сделал наследуемыми от абстрактного класса, коллбэк событий транспорта - интерфейсом. Фабрика транспортов, на будущее. Ну и самое классное - модель с LiveData событиями транспорта. Даёт сия дичь чудное поведение: кто захотел - подписался, и будет получать все события транспорта. Ну а там уже - смотри, тебе или не тебе ответ, например.
А отсыл сообщений - вообще класс: в таймере плюнул в транспорт - и забыл. Куски кода - ниже.
Модель с LiveData событий транспорта:
Туть - подписка на события транспорта:
Ну и - отправка сообщений по таймеру в транспорт, через фабрику:
Нраицца. Очень нраицца. Чем-то напоминает C# - только позамороченней архитектура немного. Но - нраицца.
Набор буковок в предыдущем сообщении - делает чудные вещи: не надо заботиться об актуальности данных, LiveData всё само сделает, и позовёт подписчика, когда надо.
Что касается транспорта, то он вызывает коллбэки типа
Работать с этим - тоже крайне просто: в MainActivity создали транспорт, и подписались на его события:
Потом - получили ссылку на общую модель событий транспорта:
А в событии от транспорта - лёгким движением руки сообщаем куче страждущих, что пришёл ответ от сервера:
И всё. Вообще всё. Не надо возиться с самописными костылями - так, определил с десяток интерфейсов и врапперов, чтобы таскать туда/сюда композитные данные - и жить весело. Я даже в этом разрезе не понимаю - нафига нужны какие-то Bundle для таскания данных между Activity или фрагментами, если есть LiveData, в котором тупо плюсом Observer? Оно, конечно, LiveData вроде как на UI заточено, типа, чтобы состояние держать - но кого останавливали такие мелочи? :)))
Буду дальше рыть это чудо.
З.Ы. И да - всякие Transport и прочие TransportMessage - это самопис, заточенный под проект. Обвязка - минимальна, сам в шоке.
Молодец! Кино посмотрел, код почитал.
Единственное замечание, просто дело вкуса: Я не люблю рассылать событие. Можно поднять флаг о том, что есть что-то полезное. А получатели "возбудятся" от поднятия флага и сами прочтут, что положено. Хотя при современных скоростях java-машины это уже похеру.
Я сегодня полдня убил с фрагментами. У них свой life cycle, и LiveData с этим зоопарком - работает крайне занимательно. Приложение крашилось, наверное, раз 100, из-за этого грёбаного цикла жизни фрагментов.
Вот тут как раз впору вспомнить процитированное :) Но я не сдаюсь - уж больно LiveData интересная штука. Да и вообще: там путей решения задачи - вагон и тележка, чего только уже не наколбасили в SDK. Не получится с LiveData - да не вопрос, сделаю свой механизм.
Фрагменты - меня когда-нибудь добьют :) Но отказываться от них - не собираюсь, ооочень вкусная вещь для UI. Короче - как обычно: надо пару месяцев мордой в грязь попадать, набить шишки - и потом уже пойдёт, как по маслу (ттт).
А получатели "возбудятся" от поднятия флага
Как они возбудятся? Там есть что-то типа семафоров? Поделись, пж, поподробнее. Надо сразу по прибытию данных уведомить всех подписчиков, идеальный вариант для этого - паттерн Observer, он как раз уже есть из коробки в LiveData.
Короче - колись, разные методы решения насущных задач - всегда полезно почитать ;)
Штоп я советы давал, мне нужно понять, что ты делаешь. Пока я не совсем понимаю.
Если ты делаешь визуализацию для мониторинга теплицы, как написано в первом посте, то я не понимаю, зачем тебе сообщения во фрагменты передавать? Это же не посадка на Марс в реальном времени? Ты в нормальном фоновом процессе получаешь данные, нормализуешь, скидываешь в SQL базу. А Визуализатор - ну просто отдельная игрушка. По таймеру проверяет наличие изменений и перерисовывает текущее окно, если изменения затронули изображение. А у тебя, мне показалось, подход как к веб-морде на JS. Все данные - сразу обработчику, и пусть отрисовывает. Так многие пишут, но современный тилипон - это довольно мощный компьютер.
Но возможно мы мыслим немного по-разному, что и хорошо.
Штоп я советы давал, мне нужно понять, что ты делаешь. Пока я не совсем понимаю.
Если ты делаешь визуализацию для мониторинга теплицы, как написано в первом посте, то я не понимаю, зачем тебе сообщения во фрагменты передавать? Это же не посадка на Марс в реальном времени? Ты в нормальном фоновом процессе получаешь данные, нормализуешь, скидываешь в SQL базу. А Визуализатор - ну просто отдельная игрушка. По таймеру проверяет наличие изменений и перерисовывает текущее окно, если изменения затронули изображение. А у тебя, мне показалось, подход как к веб-морде на JS. Все данные - сразу обработчику, и пусть отрисовывает. Так многие пишут, но современный тилипон - это довольно мощный компьютер.
Но возможно мы мыслим немного по-разному, что и хорошо.
Пойми - там есть нюансы, тот же Life cycle у Activity. Когда приложение свернули и потом развернули - Activity с фрагментами меняют состояние. Когда приложение закрыли по кнопке "назад", потом с общего списка достали - там другая песня: Activity пересоздаётся. И есть немного геморроя с поддержанием актуальных данных. Именно под это и заточена LiveData.
А что касается хранения данных - так это похрен, где, тут ты прав. Хоть в базе, хоть где. Но вот LiveData даёт тебе возможность автоматом получать last known good данные при создании Activity или фрагмента. Автоматически. Т.е. просто пихаешь туда данные - и всё, кому надо - получат.
Короче - это тупо удобно. Более того - концепция фрагментов очень помогает при формировании layout для различных ориентаций экрана, как минимум. Фрагменты - можно скрывать/показывать, когда надо. Очень мощно.
Что касается "по таймеру" - вот этого как раз и не хочется, по таймеру интерфейс обновлять. Смотри: допустим, проверяется факт наличия данных по таймеру, раз в 5 секунд. Свернули приложение, таймер остановился, до следующей проверки - 4 секунды. Развернули приложение, и оп - в полях пусто, патамушта андроид пересоздал Activity. И шо делать? Ждать 4 секунды, пока таймер не тикнет и не обновит нам данные? Не, так не очень, совсем не очень. Потому как если нет изменений - он, судя по твоему сообщению - ничего нам не обновит, изменений-то нету. А состояние фрагмента или Activity при пересоздании - пустое, нет там в полях нихрена, грубо говоря. Это ж не взрослый комп, где пока приложуха работает - всё сохраняется для отображения, хоть сворачивай, хоть разворачивай. В андроиде - другие лыжи, совсем.
А вот LiveData - сразу придёт при пересоздании Activity, и мы там сразу выцепим последние известные данные. Как-то так. Правда, стоит сказать, что я реализовал и свой механизм, типа last known good в классе обработки данных: он при приходе ответа от железяки в теплице просто сохраняет его у себя в HashMap, и всё. Ну а кому надо - тот в onResume может позвать этот синглтон и выцепить последние данные. Так что можно и без LiveData, совсем. Просто это удобно, и ничего больше.
Вот, смотри, новый видосик снял, там продемонстрирована эмуляция парочки ответов от контроллера, плюс видно, как работают фрагменты (показываются только тогда, когда есть в контроллере теплицы модуль, дающий информацию, нужную фрагменту). Плюс - продемонстрировал, что состояние сохраняется и по кнопке "назад", и при сворачивании приложения.
Собственно, вот: https://youtu.be/Pdi70kCVXzE
А в базе хранить - мне не надо, не того полёта птица.
З.Ы, ЗА интересный диалог - гран мерси ;)
Я про фрагменты всё знаю, спасибо! ;))) Лайвдата - не пользовал, не было пока нужды. Просмтотрел описание - да, удобно. Кто ж говорит.
Теперь про активити: там есть много способов отслеживание пересоздания и запуска. Есть небольшой стринг, который передается при перезапуске, если его хватает. Можно сделать объект. Можно в фоновом потоке отслеживать запуски, выходы и перезапуски. Можно, кстати это правда удобно, запретить смену ориентации экрана. При запуске нужно не по таймеру отрисовывать, а просто - по факту запуска. Но это все мелочи.
А так - всё у тебя отлично! Я никоим образом не критикую! Здорово, что на одного java любителя стало больше. Это ты еще анимации не писал! Там вообще волшебство.
Можно, кстати это правда удобно, запретить смену ориентации экрана.
Да, в первой версии так и сделаю - только портретная, и всё. А то я с ума сойду, пока всё освою :)
Это ты еще анимации не писал! Там вообще волшебство.
Ты про Action? Вскользь пролистал, учёл, что можно кучу всего интересного делать, но пока - пропущу. Украшательства оставим на потом. Хотя.... :))))))
Это ты еще анимации не писал! Там вообще волшебство.
Попробовал для фрагмента поюзать
Добавление одной строчки кода - и как забавно они теперь выезжают при показе! Действительно - волшебство :)
Я еще подумал: нужно повторять типовые элементы управления:
потянуть вниз для перезагрузки данных. При запуске - ластноунгуд из базы. Так и Почта России и браузер и Гмайл ...да всё, что я юзаю.
Поворот экрана - перехватывать и обрабатывать самостоятельно, как браузер делает.
Ну со всякими тонкостями мы постепенно разберёмся ;) Зырь, чего уже получается - сделал анимацию мал-мала, и ViewPager с динамически обновляемыми данными. Пока на всех трёх страницах - один и то же адаптер, но архитектурно - всё предусмотрел, надо только адаптеры вывода написать, и усё будет ;)
Видос захвата экрана эмулятора: https://youtu.be/drQi7DJtf9w
потянуть вниз для перезагрузки данных.
Yes, sir! Сделано, т-щ генерал! Вот: https://youtu.be/bjK5eB2Tryk
Напомните пж старый мем программерский, хоть убей, забыл и не могу найти. Суть примерная: чувак прочитал самоучитель для чайников, и побёг обновлять резюме, в котором написал что-то типа "Advanced C++ skills".
Вот я щас так же себя чувствую: прямо так и тянет резюме обновить, добавив строчки про андроид :)))))))))
Напомните пж старый мем программерский, хоть убей, забыл и не могу найти. Суть примерная: чувак прочитал самоучитель для чайников, и побёг обновлять резюме, в котором написал что-то типа "Advanced C++ skills".
Вот я щас так же себя чувствую: прямо так и тянет резюме обновить, добавив строчки про андроид :)))))))))
;))))
Точно так! Андроид и java так затягивают и на первых же шагах дают такой высокий уровень отдачи (если раньше ООП не было для тебя матерным выражением!), что кажется, будто всё вообще элементарно и щаз я стану гуру андроида!!! Потом трезвеешь.
Ты еще потом свой апк начни рефакторить до нормальных 50к хотя бы, вместо штатных Студийных мегабайт ;))). Заранее не стану пугать... всё решаемо.
Ты еще потом свой апк начни рефакторить до нормальных 50к хотя бы, вместо штатных Студийных мегабайт ;))). Заранее не стану пугать... всё решаемо.
Ты этта - незнакомыми словами не ругайся :)) Рано ещё до рефакторинга, я щас борюсь с ViewPager и переименованием датчика. Эта зараза при перелистывании страниц пересоздаёт фрагменты, как бог на душу положит. И контекст из-за этого постоянно меняется. Пока поборол введением статических членов.
Если интересно - по long click на list item, который суть фрагмент с CardView внутри - надо отобразить всплывающее меню, а по клику на пункт меню - тупо перерисовать этот item с другим именем. Так вот: если не сохранять контекст adapterView в обработчике OnItemLongClickListener, то потом, по приходу onContextItemSelected - контекст может быть не тот. В общем - неочевидное поведение, если не знать жизненный цикл фрагментов (а я его не знаю, только самые основные понятия).
А ты говоришь - рефакторинг. Я тут ещё на живом смартфоне ещё не тестировал, всё в эмуляторе сижу. И чуется мне, что весь секас ещё впереди, есть подозрение, что многие вещи я делаю через жопу. Вот например, про контексты:
Вот даже не знаю, насколько это правильно - так вольно делать статические члены, чтобы сохранить туда View? Ничего не потечёт?
Ничего не потечёт?
Подробно потом посмотрю, прости. А так - тыж утечку сразу в отладчике видишь? Странный вопрос ты задаешь. Что в Студии хорошо - так это то, что все ресурсы она показывает очень подробно.
Сижу в машине у магазина, жду супругу ;))) пересмотрел твой код. Да ты действительно не до конца понимаешь жизненный цикл. Я на такое тоже назывался. Не развернут новый фрагмент полностью, ты рано проверял контекст. В андроид девелопер уточни - там есть какой-то колбэк, который точно вызывается когда все устаканилось.
Я на похожее назывался, когда спрашивал размеры рут окна... Рано спрашивал. :((
А так - тыж утечку сразу в отладчике видишь? Странный вопрос ты задаешь. Что в Студии хорошо - так это то, что все ресурсы она показывает очень подробно.
Да вроде в профайлере всё спокойно, все графики горизонтальны. Но лучше перебздеть и спросить, для надёжи :)
Не, там дело не в этом, имхо. Смотри: список - динамически обновляемый. Т.е. по приходу новых данных - вызывается notifyDataChanged у моего адаптера списка, и он - может раздуть новое View для записи в списке, т.к. это поведение - оговорено в доке.
Activity, с которым я работаю - оно вот оно, главное, на экране, в состоянии Resumed. ViewPager, который держит фрагменты, описывающие страницы - при этом сам занимается менеджментом этих фрагментов, приаттачивая/убирая их для своего контейнера.
Получаем следующее: список обновился - и нет гарантии, что у нужного там list item остался тот же view, а не был раздут новый в методе onCreateView адаптера. Соответственно, надо этот вопрос как-то решить, решение - выше, оно работает.
Далее, что мне непонятно. По сути, во ViewPager расположены три фрагмента одного класса SensorPageFragment. У его layout для ListView прописан id - list_sensors. Т.е. при добавлении страниц во ViewPager - создаются три экземпляра одного фрагмента, которые в своём методе onCreateView получают ссылку на ListView своего layout, и хранят его в _нестатическом_ поле класса.
И вот что получается, если по долгому клику просто в лог выводить переданный индекс выбранного элемента списка, сохранить этот индекс в _нестатический_ член класса, а по выбору пункта меню - выводить этот же индекс из _нестатического_ члена класса:
1. Удержали курсор на втором элементе списка, на первой вкладке. В логе - 1, т.е. его индекс. Кликаем выпадающее меню - в логе 1, всё верно.
2. Переходим на вторую страницу, удерживаем элемент номер 8, ожидаем в логе увидеть 7 - видим 7, т.е. индекс элемента. Кликаем на выпадающем меню, ожидая увидеть в логе 7 - а видим 1! Т.е. какой-то эффективный менеджер позвал НЕ ТОТ экземпляр класса нашего фрагмента! При этом совершенно точно, что фрагмент первой страницы находится в состоянии Paused - но почему вызывается его экземпляр?
Подчеркну - по событию ОТ СИСТЕМЫ, я сам ничего не делаю на этих этапах, и описал только минимальный пример. Что я понимаю не так, и почему эта хрень тыкается не в экземпляр класса активного на данный момент на экране фрагмента?
Как может быть не развернут полностью фрагмент, если события по клику на ListView - отрабатывают? Я же вижу это на экране.
З.Ы. Ок, проверю на isAdded(), и если где-то косяк с неполной подготовкой фрагмента - будет видно, выброшу исключение.
Вот наглядная демонстрация. Вводные: ViewPager, в адаптере FragmentStatePagerAdapter создаются три экземпляра одного класса SensorsPageFragment, помещаются в локальный список адаптера, и далее этот список скармливается ViewPager по его требованиям.
Внутри SensorsPageFragment есть ListView, в котором, в методе onCreateView:
1. Цепляется setOnItemLongClickListener на ListView;
2. Переопределяются onCreateContextMenu и onContextItemSelected;
3. Есть член класса int selectedIndex;
4. Есть член класса ListView context;
5. В OnItemLongClickListener - просто сохраняем переданный нам индекс нажатого пункта меню в переменную selectedIndex класса, а также сохраняем переданный обработчиком OnItemLongClickListener в параметре adapterView контекст - в нашу переменную context;
6. В методе onContextItemSelected - просто выводим значение этой переменной.
Вот что в логе, по комментариям - понятно, надеюсь:
Если непонятно, объясню: кликаем на первом элементе списка второй вкладки, видим, что индекс - 0, всё верно. Выпадающее меню не щёлкаем. Перемещаемся на третью вкладку, кликаем на четвёртом элементе списка, в OnItemLongClickListener - видим, как и ожидалось - 3, кликаем выпадающее меню, попадаем в обработчик onContextItemSelected ДРУГОГО ЭКЗЕМПЛЯРА КЛАССА!!!
WTF, спрашивается? Почему контекст ВНЕЗАПНО поменялся? Страница как была на экране, так и есть, своё состояние она не меняла (нет вызовов onPause и т.п.). ПА-ЧИ-МУ?
Дальше - всё ещё интересней: начало крэшится в эмуляторе, достаточно только выбрать один раз в одном списке пункт - и вот такое:
И тут ннна по мордасам:
Патамушта для вкладки освещённости ни разу не вызывался лонгклик и там контекст, сохраняемый в нестатический метод класса - пустой.
Вопрос: объясни, почему так вольно контекст меняется? Если я кликнул по одному списку, два обработчика пришли для одного списка, а третий - для другого? Хрень какая-то.
З.Ы. Если context и selectedIndex сделать статическими членами - всё работает как часы. Но эта херня меня убивает, т.к. не покидает ощущение, что это НЕ-ПРА-ВИЛЬ-НО. Хочу понять, что не так.
сели с супругой бухать... завтра утром погляжу, сорян.
Да, крашится, ессно, вот здесь:
Где contextView - тот самый контекст, сохранённый в методе OnItemLongClickListener. Где-то какой-то transition effect произошёл, и почему-то съезжает контекст.
- Бен, ай нид хэлп! (с) "Брат-2"
сели с супругой бухать... завтра утром погляжу, сорян.
Ну как обычно :) Приятного!
Хха, млять! Ннате: https://stackoverflow.com/questions/9753213/wrong-fragment-in-viewpager-receives-oncontextitemselected-call
Цитата оттуда:
So this is some sort of idiotic design decision by Google
Чота ржу :)))
З.Ы. Собственно, там и решение проблемы, не надо статические члены больше.
З.З.Ы. Хотя getUserVisibleHint - объявлен deprecated, надо ещё рыть. Проблема-то есть, и она - точно не моя ;)
Короче, пока оставил getUserVisibleHint(), будем посмотреть, надо дорыть эту проблему до конца.
Во, сделал переименование датчиков, и тренды больших изменений значений - иконки подмаргивают при этом:
https://youtu.be/ZbMdrt3cvV8
Эдак скоро я дойду до момента, когда не надо будет эмулировать ответы контроллера, а реально надо будет уже к нему коннектиться. Страшшшна! :))
Хоть я и не очень трезв, но круто.
Про стековерфлоу - все там берем советы! ;)) только их оценивать и фильтровать надо. Смотри, что подходит лучше.
кстати, ты спрашивал про Линух, посмотри XUBUNTU, может понравится
кстати, ты спрашивал про Линух, посмотри XUBUNTU, может понравится
Будь добр отвечать тому, кто спрашивал. Иначе реплика повисает в воздухе.
Так кто спрашивал? Ни я ни Даймон, а кто?
кстати, ты спрашивал про Линух, посмотри XUBUNTU, может понравится
Я? Я не спрашивал, совсем.
Открываю для себя новые особенности андроида. Взять, например, Toast. Пока эта подсказка висит на экране - другие не показываются. В условиях сложного приложения, когда Toast могут попросить показать из разных мест - надо быть уверенным, что он покажется, несмотря на то, что там висит предыдущий.
Пример для моего проекта: наполняю транспорт работой с Socket. В эмуляторе нет поддержки сети (придётся таки уже переползать на железный смартфон). При старте соединения один фрагмент показывает Toast с текстом "Соединяемся...". Далее - уже MainActivity подписывается на события транспорта, и там показывает Toast, например, "Ошибка соединения".
Поскольку поддержки сети в эмуляторе не видно (может, я не нашёл), то не успевает исчезнуть сообщение "Соединяемся", как уже происходит событие "ошибка соединения". В этом событии - надо показать Toast, перекрыв им тот, что сейчас на экране - иначе всё будет рассинхронизировано по времени показа.
Посему - написал простенький синглтон, который хранит в себе ссылку на предыдущий показанный Toast, и перед вызовом makeText - отменяет показ предыдущего. Таким образом, если взаимодействовать с Toast через этот синглтон, то в рамках всего приложения неважно, кто показал Toast. Короче - маленький личный удобняк.
Собственно, вот:
мило.
А поддержка сети в эмуляторе конечно есть. Я там браузер открывал ;)))) Так что - ищите и обрящите, толците и да отверзится вам! ;))) Вот!
А поддержка сети в эмуляторе конечно есть. Я там браузер открывал ;)))) Так что - ищите и обрящите, толците и да отверзится вам! ;))) Вот!
Спс, буду разбираться ;)
А поддержка сети в эмуляторе конечно есть. Я там браузер открывал ;))))
Ок, браузер я там и правда открыл, и даже на arduino.ru зашёл. Значит ли это, что эмулятор сможет видеть все устройства в локалке? Для ясности - рабочий комп и контроллер теплицы - соединяются к точке доступа, которую предоставляет смартфон. Т.е. они, естественно, в локалке, что подтверждается работоспособностью вебморды (развёрнутой на рабочем компе), которая запрашивает с контроллера данные.
Насколько в этом функционален эмулятор? Есть опыт?
Нет, не писал кода требующего IP.
Эмулятор телефона - железный, основан на QEMU. То есть если браузер работает - весь IP стек должен работать.
То есть если браузер работает - весь IP стек должен работать.
Спс, будем надеяться, что так и есть :)
кстати, ты спрашивал про Линух, посмотри XUBUNTU, может понравится
Я? Я не спрашивал, совсем.
ТЫШ и
Здесь
кстати, ты спрашивал про Линух, посмотри XUBUNTU, может понравится
Я? Я не спрашивал, совсем.
ТЫШ и
Здесь
Протри глаза и разгляди КТО там спрашивал? (для радистов спойлер - пользователь с ником -NMi-)
(Мне всегда казалось, что коротковолновики имеют альтернативную одаренность.)
Граф, а ответь мне на простой вопрос, если сможешь, а? Короче есть NavigationView, настраивается вот так:
Далее - в графе Navigation прописаны global action для фрагментов, чтобы была анимация переходов:
Далее: есть условный второй фрагмент, на котором кнопка. По клику на кнопке вызываем action:
Всё чудесно, анимация slide_in_left отрабатывает, и можно бы жить весело и припеваючи, но! По кликам на пунктах меню в NavigationView (хрень, которая слева выезжает) - никакой анимации переходов нет. Почему - понятно: в AppBarConfiguration указаны ID пунктов меню, а не ID глобальных action. Если указать там ID глобальных action - пердец, крэш и содомия, потому как такое указание неверно, ессно.
Вопрос: как дать этой сучке понять, что по клику на пунктах меню надо выполнять global action? Ручками переопределять методы - не хочется. Неужели гугл не предусмотрел штатного подхода?
Я бы - переопределял. Я вообще не люблю использовать XML. Часто бросил несколько вью в релатив лейаут, а всю внутрянку - просто руками на java. И даже новые контролы я часто создаю из кода, а не в XML. Я так и не смог его полюбить :(((.
Если я пишу для себя, или для одного клиента, я даже стринг ресурсы редко использую, а все строки - прям в коде ;))))). Не поверишь, но я часто дневную и ночную темы вручную делаю. Мне удобнее. Жуть, да?
-----------------
Это объяснение того, что я даже не искал "штатного Гуглевского" решения через XML. Наверно я неправ. Библиотек анимации листвью, переопределяя методы, добавляя адаптер анимации - полный Гугль, чесслово. На Хабре тока щас читал как прицепить анимацию "листания книжки".
-----------------
ПО ДЕЛУ:
А зачем ты пытаешься акции, описанные внутри навигейшина, прицепить к пунктам меню? Опиши для лейаута конкретного фрагмента свои акции, пусть с теми же действиями. Андроидный XML тем меня и раздражает, что не прозрачный. Акция, описанная внутри навигейшина - это не общее имя акции slide_in_left, а привязанное к конкретике.
Это не проверенное мнение, я бы так попробовал.
Я какую-то ерунду выше написал!
Я так понимаю, что НЕ работает при нажатии в "выдвижном ящике"?
=============
Ты не можешь в AppBarConfiguration.Builder анимации поключить? Только переопределяя методы навигации. Что-то типа onNavDestinationSelected... как-то похоже оно зовется.
Ну и желания у тебя!!! Вот писал бы всё без XML - одинаковым образом бы анимировал все свои менюшки! ;)))
Короче, решил переопределением двух методов: onNavigationItemSelected и onOptionsItemSelected, и биндингом ID меню к глобальному action.
Раз:
Два:
Биндинг:
Всё робит. Щас только перепишу биндинг на какой-нибудь map, чтобы не было такой простыни - и всё ;)