Новое…

Программирование 1С. Урок 2

Ссылка на эту мою статью в Дзене: https://dzen.ru/media/elvinto/programmirovanie-1s-urok-2-65151ce8a4ed063b0afa9d35

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

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

Кликнем правой кнопкой по объекту метаданных “Документы”, создадим новый тип документа и дадим ему наименование “ПоступлениеТоваровУслуг”. Сразу здесь хочу сказать, что слово “Товары” здесь не совсем корректно, с точки зрения фирмы, товары – это имущество предназначенное для перепродажи. Для нас же товары – это имущество, которое мы купили, но перепродать не сможем, т.к. закон не предусматривает чтобы физическое лицо (обычный человек, не фирма и не индивидуальный предприниматель) что-то покупал и перепродавал. Но будем всё равно называть это товарами. Услуги тоже к нам будут поступать. Например, вызов сантехника – это на самом деле в деятельности организации поступление услуг, т.к. он окажет нам услуги по ремонту, а мы заплатим ему за это деньги. Отличие от товара в том, что при поступлении услуг не формируется материального остатка (если мы купили 5 кусков мыла, они лежат на полке и их можно всегда пересчитать, если мы 5 раз вызвали сантехника, то мы не можем материально это пересчитать, на полке ничего не лежит, а вода в кране либо идет, либо нет, хоть 10 раз его вызови)) ). Итак, вот он наш документ:

Перейдите теперь во вкладку “Нумерация” и тоже укажите, что номер будет числом (похожее мы делали и для справочников, только там был код и находился во вкладке “Данные”):

Вникать в смысл других полей пока рано. Нажмите “Закрыть” и обратите внимание на дерево метаданных. Раскройте знаком “+” наш новый тип документов “ПоступлениеТоваровУслуг”:

Каждый документ состоит из нескольких составных частей:

  • Реквизиты
  • Табличные части
  • Формы
  • Команды
  • Макеты

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

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

Посмотрите, документ мы делаем чтобы вносить поступающие к нам товары и услуги, но извините, а куда их тут вносить? Тут только номер и дата, больше что-то вносить физически некуда. Конечно, это так. Для того чтобы вносить ещё другие данные, кроме номера и даты, нужны реквизиты и табличные части. Закроем рабочую среду и возвратимся к конфигуратору. Добавим две новых табличных части. Для это кликните правой кнопкой по соответствующему разделу:

Укажите имя табличной части как “Товары”, для этого тоже правой кнопкой вызываем её свойства:

После этого появится знакомое с прошлого урока окно, где меняем наименование объекта по всем правилам:

Сделайте тоже самое и с табличной частью “Услуги”. В результате должно получиться так:

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

Дайти имя этому реквизиту “Товар” в окне свойств:

Теперь нужно правильно задать тип этого реквизита, для этого напротив атрибута “Тип” справа кликните по многоточию. Откроется окно следующего вида:

Раскройте иерархию “СправочникСсылка” и поставьте флажок напротив “Бытовая техника”, далее нажмите “ОК”:

Теперь тип реквизита стал таким:

Давайте обновим конфигурацию и снова перезайдем в 1С, попробуем снова создать новый документ:

Теперь документ приобрел две вкладки с табличными частями и туда уже что-то можно вносить. Попробуем добавить в табличную часть “Товары” новую строку:

Значит так, что сейчас происходит…Вы кликнули по кнопке “Добавить”, программа поняла, что сейчас будет добавление строк в табличную часть. Увидела, что в табличной части первая после номера строки колонка является справочником (мы ведь именно это и указали, когда только что выбирали тип реквизита “Товар”). Что-то другое ввести в этот реквизит физически не получится, т.к. мы сделали жёсткую привязку типа и сказали, что сюда можно только выбирать что-то из конкретного справочника “Бытовая техника”. Вот программа сразу и выкинула нам окно, мол хотим мы создать прямо отсюда новый элемент справочника “Бытовая техника” или нужно “Показать все” элементы и выбрать из существующих. Пусть покажет все:

Ага! Я помню – на прошлом уроке вводил данные этого справочника, и теперь я выберу по очереди их всех, т.к. всё это я некогда покупал. Я не помню даты, когда это было, по этому буду вносить сегодняшней датой в одном документе. Если бы я всё же поднял все чеки и хотел бы внести строго когда это происходило, то мне потребовалось бы внести 4 отдельных документа на каждую покупку и просто указать в документе соответствующую дату.

Нажмите либо “Провести и закрыть”, либо “Записать”, либо “Провести”, все эти кнопки запишут документ. Здесь сразу нужно остановиться на терминологии. Слово “Провести” (от слова “провОдки”) идёт ещё с времён до появления 1С. Дело в том, что документ в фирме можно зарегистрировать в два этапа. Первый – это когда он создан, но не участвует в учете хозяйственной деятельности компании, в том смысле, что его сделали, например, на бумаге, но бухгалтер чувствует, что его ещё нужно проверить несколько раз, а потом уже ставить на нём печать и свою подпись. Приходит начальник спрашивает, где этот документ, бухгалтер показывает, мол вот он сделан, но ещё проверяю, не могу пока печать поставить и подписать, ответственность всё-таки. Так же и в программе 1С – документ можно создать, но не подписать (не провести по учету). По этому все эти кнопки записывают документ, но проводят по учету только кнопки “Провести” или “Провести и закрыть”. Нажмите провести и закрыть, должно получиться так:

У нас появился первый документ по хозяйственной деятельности, который учитывает приход бытовой техники. Но погоди-ка, автор! – скажите вы,- А где сумму-то вносить за сколько купили? Кликните дважды по этой строке документа и оно вновь откроется. Поглядим и убедимся, что сумму действительно вносить физически некуда, но кто-то возможно уже догадался,- правильно, нужно добавить ещё реквизит в табличную часть. Давайте это сделаем. Закройте рабочую среду и вновь вернёмся к конфигуратору. Добавить в табличную часть товары ещё реквизит, назовите его “Цена”:

Тип этого реквизита будет уже не какой-то там справочник, а число, ведь цена – это числовое значение. Измените тип (кликнув по тому же многоточию) на число, и сразу же укажите, сколько знаков будет после запятой (это учет копеек, по этому нужно выбрать 2 знака после запятой):

Если хотите, можно также поставить тут же флажок “Неотрицательное”, т.к. невозможно что-то купить в магазине, скажем, за минус двадцать рублей, это типа подходишь к продавцу, ну-ка отвесь мне свои просроченные яблоки, да приплати мне 20 рублей, что вообще что-то беру))). Тут сразу заинтересуется либо люди в синей форме, либо в белых халатах))). Ну ладно, норм, должно получиться так:

Снова перезайдем в рабочую среду 1С, откроем наш созданный документ и посмотрим что изменилось:

Ну вот, совсем другое дело. Появилась колонка с ценой, в которую можно вводить значение. Однако, если говорить не просто о документе домашнего бюджета, а предположить, что у нас тут целая фирма, то регистрация только цены – это только половина дела. Дело в том, что редко кто в фирме закупает товаров по одной штуке. По этому нужны ещё колонки с количеством и общей суммой. Ну вот мало ли мы захотим купить одинаковых чайников или утюгов для себя, для брата, для свата. Знаете ведь как те же новогодние подарки делаются, купят всем 20 кружек для кваса и рады, что подарки сделали)))…Вот и мы тоже сделаем всё правильно, добавим ещё две колонки с типом число – “количество” и “сумма”, предварительно закрыв рабочую среду и вновь возвратившись в конфигуратор. Я не буду наверно больше акцентировать внимание на том, что нужно закрыть рабочую среду, а возвратиться к конфигуратору, т.к. это должно быть уже понятно, что просто изменив что-то в конфигураторе и не закрыв рабочую среду, то в ней само ничего не появится. Нужно перезайти в 1С для того чтобы изменения вступили в силу (да, и не забудьте перед выходом сохранить документ, а то введенные суммы не сохранятся)… Количество бытовой техники не может быть дробным, мы не можем купить половину телевизора, по этому оставим его целым (с точность 0), а вот сумма будет аналогично цене – точность 2 знака. В результате получится как-то так:

Снова перезайдем и введем теперь количество. Конечно, вряд ли вы некогда купили два одинаковых холодильника или телика, по этому так по честному и поставьте их по одной штуке:

Ты-ы-ы-кс, ну что-то тут всё равно не то. Вообще-то мы ожидали, что после введения цены или количества, сумма будет считаться сама. Нам что теперь вызывать калькулятор и считать самим??? Вот, если бы нельзя было бы это считать средствами 1С, то он был бы ещё хуже того же Экселя. Только нужно ему правильно объяснить когда что считать и по какой формуле. Настала теперь очередь учить программирование. Но не всё так просто. Дело в том, что сейчас 1С отображает наш документ автоматически. Правильно сказать, отображает форму нашего документа автоматически. Да, и справочники и документы и ещё много объектов метаданных подразумевают два вида отображения форм – автоматически и не автоматически (созданные программистом). Нам нужно как-то встроить сюда формулу расчета суммы, по этому автоматическая форма никак не знает что это будет за формула и в какой момент её считать, а считать её надо тоже не постоянно и не непрерывно, а только когда мы меняем цену или количество. По этому в конфигураторе кликнем правой (опять-таки) кнопкой мыши по разделу “Формы” и добавим новую:

В появившимся окне оставим всё без изменения, т.к. при создании первой формы документ, 1С понимает, что скорее всего мы хотим сделать – это создать “Форму документа”, а не списка или выбора (об этих позже):

После нажатие на “Готово” получаем форму:

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

Появится окно отвечающее за это событие:

Пока тут ничего не меняйте, а просто нажмите “ОК”, после чего откроется модуль формы. Измените содержимое метода вот так:

Перезапустите 1С, в документе попробуйте изменить сумму (можно просто ввести её заново) и нажмите на “Enter”. В результате получится как-то так:

Да, программа сама рассчитала сумму по формуле Количество * Цена.

Давайте теперь разберем что мы собственно сделали, что так программа стала работать.

Началось всё с того, что мы выбрали свойства элемента формы “Цена”. Потом мы указали процедуру для выполнения события “ПриИзменении”, тем самым сказали программе, что теперь мы будем отслеживать, не изменил ли пользователь это поле с ценой, и если измение произошло, то вызвать программный метод, который будет что-то делать. А что он будет делать? Он сначала командой

Стр = Элементы.Товары.ТекущиеДанные;

получает строку (Стр – это переменная, можно было сюда написать любое слово) с текущими данными, т.е. теми данными, где пользователь сейчас что-то меняет. После этого вычисляем сумму этой строки по простой формуле. А теперь главное!

Если вы раньше вообще ни на чём никогда не программировали и вообще не знаете что тут происходит, первый раз видите программу, то извините, вам нужно сейчас отвлечься от освоения 1С и перейти к моим статьям “Начало программирование вообще с нуля” для ознакомления специфики написания программ ТУТ.

Если вы раньше всё же программировали или вернулись к этому разделу почитав указанные выше статьи, то вам уже становится понятно, что Стр – это объект данных, в данном случае объект строки (класс строки), имеющий переменные Сумма, Цена и Количество, и мы просто обращаемся к этим переменным. Сами же объекты Элементы, Товары и ТекущиеДанные – это иерархия. Т.е. родительский класс Элементы. В этом классе есть переменная Товары (отражение нашей табличной части “Товары”), у этого элемента Товары есть переменная ТекущиеДанные, которая хранит в себе строку пользовательского интерфейса, где сейчас пользователь что-то делает. Соответственно, у класса Элементы есть и табличная часть “Услуги”, которая хотя сейчас и не содержит ни одного реквизита, но как переменная объекта всё же есть. Тут же в объекте Элементы, содержатся и все остальные реквизиты нашего документа отображаемые на форме. Тут главное запомнить, что корневой объект “Элементы”, а когда вы начнёте в нем что-то выбирать (после ввода точки), будет вот так:

Т.е. программа 1С подсказывает что вы хотите выбрать в этом объекте “Элементы”. Если вернуться из модуля снова к графическому представлению:

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

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

И код тоже добавить:

Теперь обратим внимание, что код для изменения количества и цены совершенно одинаковый и делает одно и тоже. В таких случаях код принято выделять в отдельный метод (а по правилам языка 1С – в отдельные процедуры и функции). Сделаем как надо:

Перезапустим 1С и проверим, что сумма пересчитывается и при изменении цены и при изменении количества:

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

Это будет тоже число с точность 2 знака:

Для того чтобы найти эту общую сумму, нужно при том же изменении и расчете суммы по строке, найти и общую сумму:

Теперь остался один штрих. Форма-то у нас уже не автоматическая, по этому вывод всех новых реквизитов на форму программист должен делать сам. По этому открываем снова графическое представление формы и просто перетаскиваем этот наш реквизит с общей суммой из правой части влевую на нужное место, например, после даты. Делается это по методу Drag&Drop (зажимаем на нём левую кнопку мыши, тянем, а потом в нужном месте отпускаем кнопку – “потянуть и бросить”):

Получается так:

Но погодите! Не перед датой, после даты! Кликаем по специальным кнопкам порядка чтобы расположить реквизит:

В результате получили так:

Перезапускаем 1С, меняем где-нибудь количество и смотрим что происходит с этой суммой:

Она стала пересчитываться.

Задание по уроку:

1. Сделайте аналогичную табличную часть для услуг, но только без количества

2. Сделайте подобно документ поступления мелочевки.

3. Добавьте в документы реквизит с типом “Домочадцы”, чтобы документ хранил информацию о том, кто из них сделал эту покупку. Ведь документ 1С также как и обычный бумажный документ должен отражать какую-то сделку или элемент хозяйственной деятельности, и важно понимать кто её совершил.

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

Урок 1 Урок 3

El Vinto, 2023 (Copyright)

Программирование 1С. Урок 1

Ссылка на мою статью на Дзене: https://dzen.ru/media/elvinto/programmirovanie-1s-urok-1-650fc821e500226085ecaecc

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

Среда 1С предназначена в основном для расчета и удобного систематизированного представления набора данных, участвующих в функционировании какой-либо фирмы или организации. Это чаще всего следующие данные:

  • Денежные средства на счетах и в наличности
  • Товары собственные, взятые на хранение, взятые под реализацию, их движение между складами, их хранение, закупка и реализация, операции регламентированные законом (учет молочной продукции, алкогольной продукции, кассовые операции в розничной торговле и т.п.)
  • Материалы и сырьё
  • Учет сотрудников организации, их заработная плата
  • Учет по налогам, их правильным начислениям и оплатам
  • Организационные документы, хранение приказов, распоряжений, контроль их выполнения
  • Отчетность в различных разрезах данных

Средства 1С многое из перечисленного позволяют делать либо полностью в автоматическом, либо в полуавтоматическом режиме. Это снимает нагрузку с персонала фирмы, а также требования к квалификации сотрудников. Задача программиста 1С делать учет таким, чтобы это правило сохранялось при постоянном увеличении объёма функционала и возможностей программы.

Среда работы 1С состоит из двух основных частей:

  • Платформа – это выполняемая среда (программа), которая представляет из себя исполняемый файл и запускается пользователем при работе.
  • Конфигурации – это наборы данных и управляющего кода для реализации тех или иных задач. Например, конфигурация “1С Бухгалтерия” предназначена для решений задач, связанных с бухгалтерским учетом фирмы, регламентированной отчетности согласно законодательству, учет первичной документации и т.п., конфигурация “1С Торговля и склад” даёт возможность учитывать деятельности фирмы по товарному обороту, использовать специализированное торговое оборудование (терминалы сбора данных, электронные весы, принтеры для печати этикеток/штрихкода и пр.), включает комплекс функционала складского учета, даёт оперативную отчетность по остаткам товаров на складах, в магазинах, их маркетинговый анализ и многое другое, конфигурация “1С Зарплата и Учет персонала” даёт возможность учитывать сотрудников и все необходимые для этого данные и отчеты, включая те, которые предусмотрены законодательством. Есть ещё много других конфигураций адаптированных под определенные специализированные задачи.

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

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

  • Версия платформы 1С
  • Версия каждой отдельной конфигурации 1С

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

Запуск среды 1С возможен в одном из двух вариантов – в рабочем (пользовательском) режиме, когда непосредственно выполняется работа функционала той или иной конфигурации, и в режиме конфигуратора, через который осуществляется доступ к объектам и коду конфигурации и его изменению, т.е. программирование и доработка. После изменений, внесенных в конфигуратор, эти изменения сохраняются и запускается рабочий режим. Для того чтобы изменения вступили в силу, все рабочие сеансы предварительно нужно закрыть (процесс называется “Перезайти в 1С”).

Короткое введение в 1С закончено, пока этого должно быть достаточно.

Как наверно вы уже поняли, для изучение 1С нам потребуется Платформа 1С. Тут для получения доступа к ней есть несколько вариантов, например:

  • Скачать бесплатно учебную Платформу тут: https://online.1c.ru/catalog/free/28765768/
  • Поговорить со знакомым программистом 1С чтобы он обеспечил удалённый доступ к средствам 1С.
  • Купить у какой-нибудь закрывающейся или неработающей фирмы продукт 1С вместе к лицензионными ключами.
  • Купить наиболее простую рабочую конфигурацию типа “Управление небольшой фирмой”.

Итак, после короткой вводной части, преступим к освоению программирования в среде 1С. Как я только что уже сказал – есть платформа и есть множество конфигураций. В освоении программирования 1С была бы большая трудность, если бы нам пришлось сейчас осваивать программирование в какой-нибудь существующей конфигурации. Однако, в среде 1С есть возможность создавать свои собственные конфигурации, вот именно это мы и будем делать. Но как я говорил ранее, 1С в основном предназначен для ведения деятельности какой-либо фирмы, но фирмы скорее всего, никакой у нас нет. Ничего в этом нет страшного, мы будем делать конфигурацию по учету нашего домашнего бюджета. Чем не фирма?- Есть денежные средства (поступления – зарплата, расходы – покупки, оплата инета, мобильной связи), есть материалы (еда, моющие средства, канцелярия), есть сырьё (полуфабрикаты, сырые продукты), есть активы (телевизор, холодильник и т.п.), есть сотрудники (наши домочадцы и мы сами), есть оказание услуг и работа (домашняя работа, уборка, готовка, стирка). Чем не фирма??? Вот и начнём адаптировать нашу домашнюю жизнь под работу организации, а заодно учить 1С и ещё сопряженные с этим технологии.

Не буду останавливаться на установке самой 1С, сложного в этом ничего нет (если всё же сложности возникнут – пишите вопросы в комментах). После запуска платформы,появится список конфигураций (в моём случае он уже заполнен некоторым количеством баз, у вас наверно будет пустой), нажмите кнопку “Добавить”:

Сделайте выбор создания новой базы:

После этого выберите, что база будет создаваться не из шаблона, а пустая, без конфигурации; ведь мы будем делать её, что называется “с нуля”:

Далее даём название “Мой домашний бюджет” и выбор типа создания “На данном компьютере” (если конечно выбор типа вообще есть):

Путь можно оставить по-умолчанию или дать свой:

Тут оставить как есть и нажать “Готово”:

Теперь пора выбрать конфигурацию:

Конфигуратор откроется и теперь нужно открыть нашу конфигурацию. Делается это через главное меню:

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

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

  • Денежные средства, если конечно ваши домашние согласятся сообщать вам сколько у кого в кармане наличности и куда он потратил. По этому будем учёт вести с себя, и на собственном примере показывать как это эффективно делать
  • Учет продуктов питания
  • Учет мелких покупок типа канцелярии или зубной пасты, обновили новые тапки, приобрели вешалку для костюма…
  • Учет активов, которые делают жизнь комфортнее или лучше, а может просто участвуют в нашем досуге – телевизор, холодильник, пылесос и т.п.
  • Учет покупки услуг – вызвали электрика, сантехника и т.д.
  • Учет кредитов – взяли кредит, отдаём кредит, дали кому-то взаймы…
  • Готовимся встречать Новый год, планируем затраты, составляем меню, будут гости, подарки, выделяем денежные средства на представительские расходы)))
  • Учитываем доходы – заработная плата, халтурка…
  • Делаем отчет помесячно, поквартально сколько мы заработали, сколько тратили, сможем ли отдавать ежемесячный взнос по кредиту, если возьмём его в банке с определенным процентом, сколько денег не хватает, сколько можно потратить ещё до конца года и пр.

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

Итак, справочные данные, это например, холодильник с указанием его модели и фирмы, телевизор – аналогично, также и пылесос, стиралка, посудомойка, кофемашина…Стиральный порошок, мыло, зубная паста – это тоже справочная информация, но как понимаете другого типа, чем холодильник и телевизор. Стиральный порошок и паста – это расходные материалы, которые быстро используются и следить за маркой и производителем не так важно с точки зрения учета, а вот телевизор и холодильник – это более дорогостоящие вещи, совершающие определенную работу по устройству комфорта. Разделение здесь, конечно, весьма условное, например, к чему отнести новый купленный пульт для телевизора – он стоит относительно недорого, но влияет на комфорт. По этому здесь нужно просто принять волевое решение и проявить своё творчество, сказать, что такие расходы мы будем просто списывать, ну потратились мы просто, как на зубную пасту или на мыло, потратились и забыли, но в программу всё-таки внесём что это было. Есть ещё другой тип справочной информации – это например, мы сами и наши домочадцы – родители, брат, сестра, бабушка, деда и т.д. Вообще, в фирме человеческие ресурсы – это сотрудники, но бабульку с её пенсией и вкладом в домашний бюджет если вы назовёте сотрудницей, то скорее всего получите скалкой на орехи, и возмущенные бурчания))). Так, с этой справочной информацией вроде определились, так вот в 1С это, что характерно, таким типом объекта и называется – Справочники:

Кликните правой кнопкой мыши по этому типу и выберите “Добавить”:

Появится окно нового справочника:

Тут много всяких полей, но нам пока на первом уроке нужно изучить только несколько. Внесем имя первого справочника “БытоваяТехника”. Обратите внимание, что имя не должно содержать пробелов или иных символов, кроме букв, цифр и знаков подчеркивания, а вот в синоним можно написать обычный текст, он и будет далее отображаться в нашей программе, когда будет запущена рабочая среда, а не конфигуратор:

Имена объектов принято давать в особом формате, где каждое отдельное слово начинается с большой буквы, например, ЭтоНекоеИмяОбъектаГдеВсеОтдельныеСловаСБольшойБуквы

Далее перейдите на вкладку “Данные” и укажите следующие поля:

Длина наименование нужна для того, чтобы когда мы начнем вводить наименование типа “Пылесос Samsung VC18M31A0 Blue (тёмно-синий, с оранжевым фильтром, а сам он причудливой формы)”, то чтобы длинные названия влезли, нужно указать достаточное количество допустимых символов. Тут как раз и будет задаваться, сколько же максимально символов наименования будет храниться. В 1С это поле строго регламентируется по длине, т.е. тут не как в блокноте, нельзя название сделать из миллиона символов. 1С работает с базой данных и лучше если мы заранее определимся сколько памяти выделять под хранение тех или иных полей. У нас база небольшая, но мы программировать на 1С наверно учимся не просто для себя, а хотим работать по этой специальности в различных компаниях, а там справочники бывают с тысячами и даже миллионами элементов, лишние символы – это большие объёмы баз данных. Конечно считать с точностью до одного смысла, чаще всего, нет, но всё же. Я дам размеру этого наименования предел 100 символов, но если вдруг потом не хватит, можно будет изменить этот размер. Длину кода я выбрал как число. Вообще этот код нужен только есть речь идет об обменах данными между 1С и каких-то ещё других программ, либо тех же 1С, но с другими конфигурациями. Этот реквизит перешёл по наследству от предыдущих версий платформ 1С, ещё тех, которые были лет 20 назад. Последние несколько лет программисты 1С весьма неоднозначно относятся к этому реквизиту справочника, кто-то считает его по прежнему нужным, кто-то бесполезным, а кто-то считает даже вредным. Я же считаю так – если компания 1С в своих платформах его до сих пор поддерживает, значит он для чего-то нужен. Код в числовом виде я поставил только для того, чтобы привлечь к этому реквизиту ваше внимание. Дело в том, что по-умолчанию программа 1С следит за уникальностью этого реквизита, и частая проблема невозможности внесения данных по какому-нибудь справочнику связана с тем, что другой элемент в базе есть уже с таким же кодом. Сейчас просто обратим на него внимание, но пока использовать его никак не будем. Всё – наш первый справочник готов, закрываем это окно и видим, что он появился в дереве объектов, называемым чаще всего метаданными. Кликните по нему правой кнопкой мыши и выберите свойства:

Справа появится новое окно, которое по содержимому отражает ту форму, которую сейчас заполняли:

Однако, если кликнуть дважды левой кнопкой мыши по справочнику “Бытовая техника”, то снова откроется знакомое окно:

Сделано это для удобства работы программиста, когда в одних случаях удобнее свойства видеть справа, а в других – отдельной формой.

Обновите конфигурацию специальной кнопкой:

Теперь произведите запуск рабочей среды прямо из конфигуратора, а на все вопросы ответим “Да” и “Принять”:

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

Откроем его и увидим, что он пуст; но это и понятно, ведь никаких данных мы в него ещё не вносили. По этому кликните кнопку “Создать” и внесите первоначальные данные, а после этого нажмите кнопку”Записать и закрыть”:

Заметьте, что если мы не меняем код (а менять его нам и не надо), когда вносим данные и оставляем его в значении ноль, то программа сама назначает его по порядку:

Задание по уроку:

1. Внесите некоторые первоначальные данные по справочнику “Бытовая техника”

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

3. Создайте справочник для хранения ваших домочадцев и продуктов питания

4. Заполните все созданные справочники соответствующими первоначальными данными.

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

Урок 2

El Vinto, 2023 (Copyright)

Ссылка на мою статью на Дзене: https://dzen.ru/media/elvinto/programmirovanie-1s-urok-1-650fc821e500226085ecaecc

Анализ публикации и монетизации на Дзене

На днях меня Дзен просит пройти небольшой опрос о том, оправдались ли мои ожидания после вступления с силу новый правил монетизации, которые были введены 1 августа 2023 года. Об этом событии Дзен сообщал заранее, и если помните, я взялся его проверить, правда ли так оно всё хорошо на самом деле. У меня был к тому времени уже опубликовано определенное количество статей, которые пользовались достаточно большой популярностью, но ввиду того, что Дзен предлагает пользователям “свежие” статьи с намного более высокой релевантностью, то читателей этих статей постепенно становилось всё меньше, т.к. мне с трудом верится, что активных интересующихся людей было всего 1000 человек на весь земной шар, они все всё сразу прочитали, и других больше не нашлось. Я решил проверить, что будет если я опубликую список этих же историй со ссылкой на них, дабы привлечь ещё читателей. По идее здесь можно было бы ожидать снова небольшой всплеск, а потом снова спад.

Никакого всплеска в результате не оказалось, а сам список прочитало за 2 месяца только 13 человек. Кстати, если вы просматривали этот список (не сами статьи, а именно этот список статей) за последние два месяца, поставьте пожалуйста здесь в комментах и в этой статье на Дзене (ссылка) +, интересно посмотреть статистику с ещё другой стороны. Тоже самое происходит и с другими статьями, все интересные темы (которые ещё полгода назад люди постоянно читали), стали резко неинтересными. Если вы блогер на Дзене, напишите как у вас обстоят дела с этим, заметили ли вы подобное у себя на канале. Я не хочу сказать, что тема моего канала невероятно какая популярная, а контент привлекателен и интересен, я просто следую логике и статистике, и если были в какой-то теме небольшие просмотры материала с какой-то периодичностью, то вряд ли резко они могли куда деться. Причём речь идет не о просмотре подписчиками, а вообще.

Я взял для анализа период с 1 декабря 2022 года по 28 февраля 2023 года:

Однако Дзен теперь отдельно от Яндекса и от его метрики, а метрика у меня была подключена почти с самого начала:

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

Топ моих страниц по данным Яндекс-метрики за этот период:

Это всё(!) страницы связанные с историями из моей личной жизни о программировании (кроме адреса самой страницы в Дзене и заметки о том сложно ли учиться на программиста). Подчеркиваю, что популярность некоторых статей очень высока (по меркам моего канала):

После включения нового метода монетизации, видать, включили и новые алгоритмы таргетирования. Напоминаю, новый метод Дзена – это берётся некоторая сумма денег, Дзен пишет, что это общий фонд полученный с рекламы и выделенный на оплату блогинга, делится между всеми авторами Дзена в пропорции соответствующей времени просмотра контента у каждого блогера. Соответственно, ту долю каждый блогер себе и забирает. Например, есть фонд 100 рублей, есть 10 блогеров, у одного блогера 910 минут просмотра, у 9 других – по 10 минут просмотров. Первый получает 91 руб дохода, а остальные – каждый по рублю. Таким образом теперь на Дзене так, на чьём канале большая пользователей “зависло” подольше, того и бапки. Дзен не будет определять, что у тебя на канале! Популярные фильмы, мульти-пультики, научные статьи, новости, короткие видео, различные челленджи, все делятся по времени наравне. И вот тут-то я и обратил внимание, что таргетирует, например, мне Дзен, когда я просматриваю его титульную страницу; напоминаю, мне, человеку с высшим образованием, интересущимся ардуинками, станками с числовым программным управлением, реактивными двигателями, делающему огромную кучу запросов по программированию на Java, 1C, C++ и т.п., вот что:

Значит, что тут есть:

  • Баба показывает профиль задницы – 45 тыс.просмотров
  • Баба снимает под платьем трусы и показывает это – почти 1 млн.просмотров
  • Баба в мини юбке показывает фигуру – почти 1 млн.просмотров
  • Молодая пигалица учит когда пользоваться гондонами – 280 тыс.просмотров
  • Быба пляшет аля калинка-малинка – 100 тыс. просмотров
  • Баба ходит туда сюда – 1.2 млн. просмотров
  • Баба показывает жопу – 70 тыс.просмотров

И вот я думаю, канал у меня технический, основные зрители – мужчины. А когда им смотреть мои уроки по программированию, читать статьи о программировании, да и вообще заходить на мой канал))))…У них просто на это не остаётся времени))))

Но речь о монетизации,- теперь можно сопоставить, например, с моими уроками по программированию на Java Android:

А что, неплохо – где-то около 40 просмотров, а Дзен пишет, что люди на них зависли в общей сложности на 2 часа (120 минут), и кстати, погодите, я заработал на этих 10 уроках по программированию примерно 80 копеек, хотя и потратил на составление этих уроков около 30 часов (1800 минут) своего личного времени! Интересно, а сколько заработала баба снимающая трусы в 1 минутном ролике? Общее времени просмотров этого ролика (грубо прикинув):

ОбщееВремя = 1 млн * 1 минута = 1 000 000 минут

Заработок = 1 000 000 минут / 120 минут * 0.8 руб. = 6666 руб.

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

ЗаСнятиеТрусовЗа30часовРаботы = 6666 * 1800 = 12 млн.руб

Конечно, знатоки могут мне давать тут различные советы, но скажу сразу – трусы я снимать на камеру не буду ни за какие деньги (да там и смотреть особо не на что) ))), а вот к Дзену у меня самого много вопросов, какую политику он пропагандирует и скоро ли станет русским аналогом Тик-Тока со всей его “красотой”? )))

Так вот, когда меня Дзен стал опрашивать, оправдались ли мои ожидания от введенной 1 августа монетизации, я много чего ему интересного написал, вряд ли он вообще будет теперь таргетировать мой канал)))).

Java Android. Урок 10 (“Создание apk”)

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

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

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

Теперь, когда приложение готово и протестировано, пора его отправить в ваш мобильный телефон или планшет и проверить окончательно.-2

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

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

Теперь, когда приложение готово и протестировано, пора его отправить в ваш мобильный телефон или планшет и проверить окончательно.-3

Далее будет как-то так:

Теперь, когда приложение готово и протестировано, пора его отправить в ваш мобильный телефон или планшет и проверить окончательно.-4

Укажите, что это релиз вашего приложения (готовый для публикации продукт) и нажмите Create:

Теперь, когда приложение готово и протестировано, пора его отправить в ваш мобильный телефон или планшет и проверить окончательно.-5

После того как ваш ПК пошуршит диском, справа внизу будет выдано что-то вроде этого:

Теперь, когда приложение готово и протестировано, пора его отправить в ваш мобильный телефон или планшет и проверить окончательно.-6

Теперь осталось только скопировать этот файл на ваш смартфон. Сам файл находится где-то вот тут (в папке вашего проекта):

Теперь, когда приложение готово и протестировано, пора его отправить в ваш мобильный телефон или планшет и проверить окончательно.-7

Т.е. внутри вашей пользовательской папки находится папка с проектами, затем внутри папки этого определенного проекта, а там ещё пройти пару папок и вот он app-release.apk.

Закинуть в смартфон можно несколькими способами, я это могу делать либо через Телеграмм – с компа отправляю файл в Избранное, а на телефоне из Изранного сохраняю в телефоне и кликаю этому файлу. Второй вариант – просто подключить кабель к ПК и закинуть непосредственно в телефон как на флешку.

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

Далее наслаждайтесь запуском и работой вашего первого приложения на Android!

Java Android. Урок 9 (“Диалог”)

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

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

Простые диалоговые окна обычно используются чтобы задать пользователю какой-то вопрос и предложить несколько вариантов ответа, тип “Да”, “Нет” и “Отмена”. По этому они и называются диалоговые, т.к. компьютер с пользователем вступает в диалог и ему для продолжения работы нужны со стороны пользователя какие-то действия и решения. В данном случае диалоговое окно будет сообщать пользователю, что игра закончена, ему удалось угадать число и можно начать новую игру. Однако, мы не будем ограничиваться стандартным механизмом диалоговых окон, а сделаем свое собственное. Такие элементы называются кастомизированными (от анг.слова Custom – собственный, пользовательский, настраиваемый), т.е. мы будем делать не просто вариант “Да-Нет”, а со своими кнопками и внешним видом.

Добавим внутрь нашего класса MainActivity следующий код:

DialogData dialogData;
public static class DialogData {
public AlertDialog.Builder alertDialogBuilder null;
public AlertDialog alertDialog null;
public TextView textViewButtonNewGame;
public Context context;
public static DialogData CreateDialog(Context context) {
DialogData dialogDataYesNo = new DialogData();

dialogDataYesNo.context = context;

View promptsViewYesNo = LayoutInflater.from(context).inflate(R.layout.dialog_endgamenull);

dialogDataYesNo.alertDialogBuilder new AlertDialog.Builder(context);
dialogDataYesNo.alertDialogBuilder.setView(promptsViewYesNo);

dialogDataYesNo.textViewButtonNewGame = promptsViewYesNo.findViewById(R.id.buttonNewGame);

dialogDataYesNo.textViewButtonNewGame.setOnClickListener((View.OnClickListener) context);

dialogDataYesNo.alertDialogBuilder
.setCancelable(false).setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
((MainActivity) context).dialogButtonNewGame();
}
});

dialogDataYesNo.alertDialog = dialogDataYesNo.alertDialogBuilder.create();

return dialogDataYesNo;
}
}

В результате получится, что один класс (DialogData) расположен внутри другого класса (MainActivity). Также объявим сразу же переменную класса MainActivity, которая будет хранить экземпляр класса DialogData:

DialogData dialogData;

Теперь нужно добавить новый layout (мы это уже делали), дайте ему аналогичное имя dialog_endgame:

Продолжаем разработку нашего первого приложения на Андроид. На этом уроке займемся таким необходимым функционалом, как отображение диалоговых окон.-2

Содержание его будет крайне простое, нужно просто разместить заголовок со словом “УГАДАЛ!” и кнопку для начало новой игры:

Продолжаем разработку нашего первого приложения на Андроид. На этом уроке займемся таким необходимым функционалом, как отображение диалоговых окон.-3

В метод onCreate не забудьте добавить создание экземпляра диалогового окна и его инициализацию, кстати, заметьте, если вы захотите менять текст элементов, то делать это нужно будет примерно так, т.к. экземпляр элемента объявлен не в основном классе MainActivity, а в классе DialogData:

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

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

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

Продолжаем разработку нашего первого приложения на Андроид. На этом уроке займемся таким необходимым функционалом, как отображение диалоговых окон.-5

И теперь остался последний штрих – обработать нажатие на кнопку “Ещё играть”. Добавьте этот метод куда-нибудь внутрь класса MainActivity, я разместил его сразу за классом DialogData, хотя можно было и более аккуратно – после всех методов:

Продолжаем разработку нашего первого приложения на Андроид. На этом уроке займемся таким необходимым функционалом, как отображение диалоговых окон.-6

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

dialogData.alertDialog.cancel();

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

Продолжаем разработку нашего первого приложения на Андроид. На этом уроке займемся таким необходимым функционалом, как отображение диалоговых окон.-7

Обычно, если пользователь ткнет пальцем по экрану мимо окна, то оно закроется, это получается, когда setCancelable(true), в ином случае, хоть пользовать затыкается по экрану, окно так и будет оставаться открытым, закрыть его можно будет только программным путём через код, либо принудительно завершить приложение. Нам нужно как раз сделать именно так, потому что если пользователь закроет без вызова метода dialogButtonNewGame то не будет ничего что начало бы новую игру. Пользователю останется только созерцать на результат предыдущей. Это нехорошо, вряд ли это не начнёт бесить даже самого выдержанного))). По этому нужно стремиться делать так, чтобы пользователю всё нравилось.

Ещё один момент касаемый обработки вызова нажатия кнопки диалогового окна. Начинает он отсюда:

Продолжаем разработку нашего первого приложения на Андроид. На этом уроке займемся таким необходимым функционалом, как отображение диалоговых окон.-8

Помните, как мы добавляли обработчик нажатия кнопок через имплементацию? Там мы указывали ключевое слово this и компилятор кидал вызов метода onClick в наш класс. Здесь мы делаем по другому. Переменной класса нет, по этому такой класс называется анонимным, а имплементация нужна в любом случае. Деть её некуда кроме как запихнуть прямо туда. Однако, управление нам нужно передать в основной класс каким-то образом, чтобы мы могли генерировать новый код игры, очистить список и т.п. По этому, если бы мы заранее не позаботились об этом при создании класса и не передали ему переменную context с указанием this на наш класс MainActivity, то сделать это было бы невозможным. Таким же образом, класс DialogData содержит context и “знает” куда передать. Тип можно было бы использовать и не Context, а объявить, например, MainActivity context, тогда бы приводить к типу ((MainActivity) context) не пришлось бы. Однако, это дело вкуса, и я стараюсь делать более универсальный метод и использовать Context там, где это возможно.

Задание по уроку:

1. Разберитесь самостоятельно как программным путем (не по кнопке возврат) завершить работу Активити. Добавьте в диалоговое окно ещё одну кнопку “Выйти из игры”

2. Сделайте так чтобы при первом окончании игры надпись первой кнопки была “Ещё играть!”, а при следующих текст был бы “Может сыграем ещё?!”.

3. Попробуйте сделать так чтобы кнопка “Выйти из игры” случайным образом выводила один из вариантов текста: “Выйти из игры”, “Покинуть игру”, “Хватит, больше не хочу играть”, “Сыграю ещё потом”

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

Урок 8 Урок 10

El Vinto, 2023 (Copyright)

Java Android. Урок 8 (“Новый Активити”)

Продолжим разрабатывать наше приложение-игру “Быки и коровы”. Нам осталось сделать совсем немного. Сама игра уже сделана, работает, но не хватает только нескольких улучшений. Начнем с простого. Добавим ещё один активити, который будет пользователю отображать правила игры. Для этого правой кнопкой мыши кликнем по названию пакета в дереве проекта и выберем следующий пункт контекстного меню:

Продолжим разрабатывать наше приложение-игру "Быки и коровы". Нам осталось сделать совсем немного. Сама игра уже сделана, работает, но не хватает только нескольких улучшений. Начнем с простого.

Дайте название RulesActivity.

Теперь перейдите в манифест (это мы уже делали) и добавьте портретный режим тоже и для этого нового Активити:

Продолжим разрабатывать наше приложение-игру "Быки и коровы". Нам осталось сделать совсем немного. Сама игра уже сделана, работает, но не хватает только нескольких улучшений. Начнем с простого.-2

В дереве проекта выберите ресурс с описанием пользовательского интерфейса этого Активити и откройте уже известную подвкладку “Code”:

Продолжим разрабатывать наше приложение-игру "Быки и коровы". Нам осталось сделать совсем немного. Сама игра уже сделана, работает, но не хватает только нескольких улучшений. Начнем с простого.-3

Видим, что пока здесь только корневой контейнер. Теперь сюда нужно добавить TextView с описанием нашей игры. Однако, не всё так просто. Как понимаете, объем описания зависит только от ваших возможностей и возможно вы захотите описать всё в мельчайших деталях, а заодно и включить туда какую-то контактную информацию о себе, мол куда обращаться, если что не понятно или нашлись какие-то ошибки в процессе игры. Этот текст может оказать таким, что будет намного превышать размеры экрана. В этом случае пользователь увидит только текст, который вместился у него на устройстве. Это плохо. По этому, когда возникает подобная ситуация и что-то на экране гарантированно может не уместиться, используют такой контейнер как ScrollView. Если помните, до этого мы использовали только контейнер ConstraintLayout. Вернёмся в подвкладку “Design”. Найдите ScrollView в дереве элементов и добавьте его в описание представления:

Продолжим разрабатывать наше приложение-игру "Быки и коровы". Нам осталось сделать совсем немного. Сама игра уже сделана, работает, но не хватает только нескольких улучшений. Начнем с простого.-4

Вернитесь в подвкладку “Code”. Код теперь может выглядеть так:

Продолжим разрабатывать наше приложение-игру "Быки и коровы". Нам осталось сделать совсем немного. Сама игра уже сделана, работает, но не хватает только нескольких улучшений. Начнем с простого.-5

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

Продолжим разрабатывать наше приложение-игру "Быки и коровы". Нам осталось сделать совсем немного. Сама игра уже сделана, работает, но не хватает только нескольких улучшений. Начнем с простого.-6

Теперь внутрь можно уже что-то поместить:

<ScrollView
android:layout_width=”match_parent”
android:layout_height=”0dp”
android:orientation=”vertical”
app:layout_constraintEnd_toEndOf=”parent”
app:layout_constraintStart_toStartOf=”parent”
app:layout_constraintTop_toBottomOf=”@+id/textViewTitle”>

<TextView
android:id=”@+id/textView”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_margin=”5dp”
android:textSize=”16sp”
android:text=”Компьютер загадывает число из 4 разных цифр. Цель игры – делая попытки, узнать это число. Совершить попытку можно только если заполнены все четыре разряда (после этого появится справа внизу соответствующая кнопка). После каждой попытки компьютер будет сообщать сколько в указанном вами числе быков и коров. Корова – это когда в вашем варианте есть цифра, которая присутвтвует в загаданном, но не соответствуют разряду (позиции), бык – это когда в вашем варианте есть цифра и она также находится в правильном разряде. Получается, что игрок выигрывает когда в его варианте оказывает четыре быка. Например, компьютер загадал вариант 1234, а вы предлагаете вариант 4235. В этом случае будет два быка – цифры 2 и 3, т.к. они есть в загадонном и расположены в таких же разрядах, и одна корова – цифра 4, т.к. она тоже есть, но находится не на своём месте; цифра 5 в вашем варианте отсутствует в загаданном, по этому это и не бык и не корова.”
app:layout_constraintEnd_toEndOf=”parent”
app:layout_constraintStart_toStartOf=”parent”
app:layout_constraintTop_toTopOf=”parent” />
</ScrollView>

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

Продолжим разрабатывать наше приложение-игру "Быки и коровы". Нам осталось сделать совсем немного. Сама игра уже сделана, работает, но не хватает только нескольких улучшений. Начнем с простого.-7

Перезапустите приложение и кликните по кнопке со знаком вопроса (кнопке отвечающей за вывод подсказки):

Продолжим разрабатывать наше приложение-игру "Быки и коровы". Нам осталось сделать совсем немного. Сама игра уже сделана, работает, но не хватает только нескольких улучшений. Начнем с простого.-8

Задание по уроку:

1. Сделайте заготовку для нового Активити, который будет запускаться при старте программы. Назовите его PreActivity. Добавьте на него текст с названием игры, картинкой игры и какой-нибудь информацией о вас как о разработчике. Добавьте на него кнопку “Продолжить”, по нажатию которой будет осуществлён переход в саму игру (в Активити MainActivity).

2. Самостоятельно погуглите назначение диалоговых окон в Андроид, чтобы иметь преставление как они выглядят и для чего нужны (более подробно будем рассматривать на следующем уроке).

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

Урок 7 Урок 9

El Vinto, 2023 (Copyright)

Java Android. Урок 7 (“Списки”)

Напоминаю, что на наших уроках мы разрабатывает приложение – логическую игру “Быки и коровы”. На прошлом уроке мы остановились перед тем, как начать знакомиться с таким функционалом, как списки. Да, это не просто объект или элемент пользовательского интерфейса, а целый функционал, т.к. комплекс взаимодействующих между собой методов и классов для отображения списков. Казалось бы что проще – вывести на экран один за другим элементы, содержащие строки, но вот тут и кроется вся сложность. Дело в том, что при выводе списка, Андроид выводит не строку, а целый набор элементов пользовательского интерфейса (текст, картинки, флажки и т.п.). Причём меняться они могут, что называется, налету, т.е. в процессе подготовки к выводу на экран. Сам список, тоже может меняться, модифицироваться. Данные списка – это не просто строки, это могут быть массивы различных типов. Однако, давайте начнем по порядку.

Первое, что нужно сделать, это поместить в наш пользовательский интерфейс сам элемент списка и дать ему соответствующие привязки (вообще-то мы это уже сделали ранее, просто не вникали зачем):

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

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

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

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

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

public static class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
ArrayList<RecycleElement> recycleElements;

private Context context;
private LayoutInflater inflater;

class ViewHolder extends RecyclerView.ViewHolder {
private TextView bulls;
private TextView number;
private TextView cows;

public ViewHolder(View itemView) {
super(itemView);

bulls = (TextView) itemView.findViewById(R.id.textViewBulls);
number = (TextView) itemView.findViewById(R.id.textViewBCNumber);
cows = (TextView) itemView.findViewById(R.id.textViewCows);
}
}

RecyclerViewAdapter(Context context, ArrayList<RecycleElement> recycleElements) {
this.context = context;
this.inflater = LayoutInflater.from(context);
this.recycleElements = recycleElements;
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = inflater.inflate(R.layout.recycleelement_bc, parent, false);

return new ViewHolder(v);
}

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
RecycleElement recycleElement = recycleElements.get(position);

String bulls = “”;
String cows = “”;

if (recycleElement.bulls == 1) {
bulls = “1 бык”;
else if (recycleElement.bulls > 1) {
bulls = “”+recycleElement.bulls+” быка”;
}

if (recycleElement.cows == 1) {
cows = “1 корова”;
else if (recycleElement.cows > 1) {
cows = “”+recycleElement.cows+” коровы”;
}

holder.bulls.setText(bulls);
holder.number.setText(recycleElement.number);
holder.cows.setText(cows);
}

@Override
public int getItemCount() {
return recycleElements.size();
}
}

В этом коде, возможно, вам покажется всё сложным, по этому пока особо не вникайте в некоторые конструкции языка, чуть позже всё это разберем. Сейчас остановимся на некоторых моментах. Это класс RecyclerViewAdapter, который мы создаём, наследуется от стандартного RecyclerView с той целью, чтобы могли взять некоторые части управления на себя, а именно – формирование каждого элемента списка из макета (аналогично active_main.xml для формирования пользовательского интерфейса). За формирование отображение отвечает специальный функционал inflater. В качестве одного из параметров ему передаётся ресурс с xml, который и хранит в себе набор элементов пользовательского интерфейса для каждого элемента списка. В данном случае это ресурс R.layout.recycleelement_bc. Добавьте его к проекту следующим образом. Кликните правой кнопкой мыши по папке res и выберите следующий пункт контекстного меню:

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

Дайте ему имя recycleelement_bc в поле “Layout File Name”. После этого будет создан новый файл проекта и он появится в уже знакомой вкладке; перейдем в ней сразу в подвкладку Code:

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

Замените находящейся в ней XML на следующий:

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

Заметьте, что у элементов появились незнакомые атрибуты:

  • gravity – указывает к какому краю TextView будет прислоняться находящийся внутри текст. “end|center_vertical” указывает что одновременно по концу текста (в данном случае по правому краю) по горизонтали и по центру по вертикали.
  • layout_margin – указывает, что между краями TextView и самим текстом должен быть отступ со всех 4-х сторон равный 4 условные единицы (это чтобы текст одного TextView визуально не примыкал вплотную к тексту другого TextView или иного элемента.

Тут есть ещё одна особенность. Раньше значения параметра gravity были не start и end (начало и конец), а назывались left и right (лево и право). Но потом почему-то называние было изменено. Найти где-то конкретное описание этому изменению мне не удалось, но полагаю связано это с тем, что письменность в некоторых иностранных языках идет не слева-направо, а справа-налево, как например, в арабском. Я думаю, новые значения параметров сделаны как раз для адаптации к этому, что бы в зависимости от языка не приходилось менять left-right местами вручную. Как понимаете, на форумах можно найти по прежнему старые варианты значений.

Теперь вновь возвратимся к классу RecyclerViewAdapter. Заметьте ещё одну интересную конструкцию:

ArrayList<RecycleElement> recycleElements;

В Java Android есть массивы, вы их уже видели, но есть и ещё один похожий объект – ArrayList. Особой разницы между ними нет, кроме того, что массив заранее имеет определенный размер, а ArrayList динамически может менять размер. Т.е. если бы в данном случае мы захотели использовать массив, он выглядел бы как-то так:

RecycleElement recycleElements[ТутКакоетоЧислоЭлементов];

Однако, для списка мы не можем использовать массив, т.к. не знаем заранее до какого размера будет разрастаться наш список. Сразу резервировать память под большое число не рационально. Вообще, если сравнивать их между собой с точки зрения рациональности, то получается такая зависимость (имеется ввиду для ArrayList и массивов большого размера):

Массив:

  • Высокая производительность
  • Большой расход памяти

ArrayList:

  • Низкая производительность
  • Низкий расход памяти

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

Далее, метод onBindViewHolder в этом нашем классе списка – это имплементация (реализация) базового класса, которую, как мы помним, нужно делать всегда. Этот метод как раз отвечает за вывод на экран каждого элемента списка буквально перед самим его выводом. По этому мы можем кое что поменять и заполнить элементы списка так, как нам нужно. В качестве одно из параметров этого метода выступает целочисленный параметр position, в котором система сообщает какой именно сейчас выводится элемент списка. И вот как раз мы в этот момент берём данные этого элемента из нашего ArrayList, немного модифицируем и выводим в нужные элементы элемента списка (извините за тавтологию).

Взять элемент по текущей позиции:

RecycleElement recycleElement = recycleElements.get(position);

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

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

Запихнуть нужный текст в нужный элемент:

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

Теперь давайте разберемся, откуда программа знает что такое вот это:

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

Это мы объявили и проинициализировали в нашем классе чуть ранее:

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

В параметр itemView системой будет передан как раз тот набор элементов, которые мы описали в файле recycleelement_bc.xml. По этому мы должны уже знакомой функцией findViewById() искать не в activity_main или где-то ещё, а именно в нашем recycleelement_bc.xml, ведь его мы тоже передали нашему списку, если помните, вот тут:

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

Теперь осталось только правильно объявиться и все переменные и список будет готов. Объявите три переменные классе MainActivity:

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

Да, всё правильно, если вы заметили recycleElements мы уже объявляли, но внутри нашего класса списка и без инициализации, здесь инициализацию (new ArrayList() ) важна, без неё не заработает.

Далее в метод onCreate() добавьте код:

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

recyclerView – будет хранить в себе элемент пользовательского интерфейса – список; вспомните файл activity_main.xml. Создайте для него менеджер LinearLayoutManager как показано, это тоже необходимо, иначе список не будет обновляться.

recyclerViewAdapter будет хранить экземпляр нашего класса RecyclerViewAdapter, в качестве одного параметра мы передаём текущий класс, а в качестве второго наш объявленный и проинициализированный список. Посмотрите сейчас почему именно два параметра, ведь когда мы создавали наш класс, мы именно так и указали, что для создания экземпляра нам потребуется именно два этих параметра:

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

Первый был Context (к которому компилятор автоматически привёл наш this, а второй – список ArrayList).

Далее выполняем:

recyclerView.setAdapter(recyclerViewAdapter);

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

Следующая команда

recyclerViewAdapter.notifyDataSetChanged();

запускает процесс регенерации списка. Именно тут она не нужна (только если вы не хотите задать начальный список и сразу же это показать). Однако, познакомить с ней я решил сейчас. Т.е. как понимаете, это команда сообщает системе следующее – “Система, мой список уже недостоверен, какие-то данные там изменены, может какие-то элементы уже удалены, может что-то появилось новое, перерисуй мне весь список заново”.

Теперь измените немного код обработки кнопки ввода числа в методе onClick():

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

Заметьте, что здесь notifyDataSetChanged() как раз выполняет свою функцию – перезапускает отрисовку элементов (у нас появился новый элемент списка и надо отобразить обновленный список). Перезапустите приложение, после ввода чисел и нажатия на кнопку ввода, должно получаться как-то так:

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

Задание по уроку:

1. Сделайте так, чтобы если в варианте числа 4 коровы, то в списке он отмечались всегда зеленым цветов

2. Добавьте в список ещё колонку со значком галочка. Сделайте так, чтобы если в варианте числа сумма быков и коров была равно четырем, то у всей этой строки фон был жёлтого цвета.

3. Самостоятельно разберитесь с тем, как правильно получить размер списка ArrayList, как его очистить. Сделайте так, что если игрок совершил 12 попыток и не смог угадать число, то он проиграл, а на экран будет выведена соответствующая надпись при помощи Toast, и игра начнётся заново.

4. Вопрос “на засыпку”. Почему для правил русского языка я сделал только варианты “1 бык”, а если больше единицы, то окончание “-а” в слове “быка”, ну типа 2 быка, 3 быка, почему я не сделал ещё, ведь правильно говорить “5 быкОВ”, “6 быкОВ”, почему нет такого варианта? Также и с коровами.

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

Урок 6 Урок 8

El Vinto, 2023 (Copyright)

Java Android. Урок 6 (“Третий код”)

Вернемся к разработке нашего первого приложения под Андроид. На прошлом уроке мы начали делать функционал (набор исполняемого кода и данных необходимых для выполнения определенных вычислительных операций) генерации числа, которое компьютер по правилом игры должен “загадать”. Напоминаю, нам нужны следующие условия для такого числа:

  • Число должно состоять только из 4-х цифр
  • Все цифры числа должны быть разными

Методов решения этой задачи несколько, по этому нужно просто какой-то выбрать. Заранее скажу как я буду это делать. Сначала я получу 4 цифры числа случайным образом таким образом, чтобы каждое следующее случайное число – значение цифры загадываемого числа, отличалось от уже всех ранее сгенерированных. Это даст выполнение правила, что все цифры числа должны быть разными. С первым условием всё просто, мы просто будем генерировать от 1 до 4-х.

Вернемся к разработке нашего первого приложения под Андроид.

Однако, прежде чем разбираться как же тут получается число со всеми необходимыми условиями, необходимо отвлечься и познакомиться с некоторыми конструкциями кода. В Java есть 3 вида циклов, т.е. конструкций кода, позволяющих несколько раз выполнять один и тот же код (циклическое выполнение).

Первый из них мы уже много раз встречали – это обычный цикл for. В области параметров этого цикла есть три части кода, разделенные точкой с запятой. Первая часть инициализирует переменную (задаёт ей начальное значение), вторая часть проверяет соблюдается ли условие (будет ли результат этого условия равен значению true), третья часть – выражение, которое должно выполняться после каждой итерации (этап прохождения по циклу). Пока значение второй части true, цикл будет совершать итерации (хоть до бесконечности). Давайте это рассмотрим прямо из нашего нового метода:

for (int i = 1; i < 5; i++) {}

Сначала объявляем и инициализируем переменную i значением 1. Во второй части будем сравнивать – значение переменной i для выполнения итерации должно быть меньше 5. В третьей части производим инкремент этой переменной (тема прошлого урока). Таким образом, каждую итерацию i будет увеличиваться на единицу и в конечном итоге достигнет значения 5. Условие i < 5 перестанет выполняться, цикл завершится и управление будет передано первому выражению следующему за циклом (за закрывающей скобкой этого цикла).

Вторая конструкция цикла у нас тоже в этом методе есть и выглядит так:

do {

} while (isNoOk);

Тут цикл начнёт выполняться без всяких условий сразу же, но вот будет ли выполнен повторно определяется условием после завершения этого цикла, что не скажешь о предыдущем, где чтобы начать выполнение цикла (вхождение в цикл) нам требовалось условие i < 5, это же условие требовалось и для продолжения его выполнения. Тут, как понимаете, по логике получается немного по другому. Тут стоит обратить внимание, что если переменная i в цикле for (который ещё иногда называют по-старинке “цикл for…next”) видима как в объявлении цикла, так и внутри него, то в цикле “do…while” нет инициализации, а все используемые там переменный должны быть либо объявлены где-то перед этим циклом, либо иметь более глобальную видимость, например, быть переменными класса (один из прошлых уроков). В связи с этим получается, что переменная isNoOK должна быть объявлена перед этим циклом. Если посмотрите на код метода generateComNumber(), я именно так и сделал. Итак, подытожим. Цикл do…while начинает выполняться без всяких условий и будет выполняться до тех пор, пока значение внутри while будет true, т.е. например, цикл

do { }

while(true);

будет выполняться “вечно” и никогда не завершиться, пока работа приложения не будет прервана извне этого приложения (действиями пользователя или действиями операционной системы). По этому задача программиста сделать так, чтобы внутри этого цикла было что-то, что может поменять условия для выхода из цикла. Для этого я выполняю действия влияющие на это:

do {
num = random.nextInt(10);

isNoOK = compNumber.indexOf(“” + num) > -1;
} while (isNoOK);

Сначала я получаю случайное число num в диапазоне 0…9, затем преобразовываю его в строку и пытаюсь найти в уже сформированной итоговой строке compNumber. Функция indexOf как раз это и делает. Если искомая строка не найдена, результат будет равен минус 1, а если найдена, то позиции искомой строки и строке поиска. Сама позиция нам не нужна, тут нам просто нужно проверить есть ли это в строке. Обратите внимание как я получаю из числа строку. На одном из прошлых уроков я говорил, что иногда компилятор правильно делает такое преобразование если ничего не указывать, но по всем правилам преобразование нужно делать через String.valueOf(). Но есть и ещё метод – это прибавить число к пустой строке:

“”+num

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

Стоит сразу же сказать и о ещё одном виде цикла, который используется наравне с этими двумя. Это цикл

while(условие) {

}

Он является почти аналогом цикла do…while, но отличается только тем, что для первого вхождения в цикл нужно выполнения условия (для do…while этого не требовалось).

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

while (true) {
if (какое-то условие) {
break;
}

/* ничего этого выполнено не будет, если сработает break */
}
/* сюда будете передано управление после выполнения break */

В этом примере казалось бы условие цикла всегда будет true и цикл будет выполняться “вечно”, но это не так, т.к. внутри цикла встроена конструкция из условия, по истинному результату которой, будет выполнена команда break. Это команда прерывает цикл, выходит из него прямо с этой команды.

ВАЖНО! Команда break выходит только из текущего цикла. Если один цикл вложен в другой, выход будет осуществлён только из текущего цикла, но НЕ(!) из вышестоящего.

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

while (true) {
if (какое-то условие) {
continue;
}

/* ничего этого выполнено не будет, если сработает continue */
}
/* в данном примере здесь никогда управление не окажется, т.к. нет ничего чтобы прервало цикл. Это “вечный” цикл */

В следующем примере:

Вернемся к разработке нашего первого приложения под Андроид.-2

Цикл станет “вечным”, т.к. выход по break будет только из внутреннего цикла, но внешний продолжит выполнение. Это касается циклов всех типов.

Теперь, основываясь на наших новых знаниях о циклах, рассмотрим более подробно мой метод генерации числа. Сначала я объявил экземпляр класса Random и поместил его в переменную random. Этот класс позволяет программисту работать с генератором случайных чисел. Далее следует сброс compNumber – это строкова переменная класса, которую вы должны были добавить в домашнем задании. Далее делаем цикл от 1 до 4, а внутрь этого цикла помещаем ещё один, который будет выполняться до тех пор, пока не сгенерирует цифру числа, которой ещё нет в загадываемом числе. Когда функционал находит такую цифру, он конкатенирует её к compNumber и процесс повторяется 4 раза. Таким образом, в результате будет необходимое число. Посмотрите внимательно каждую команду этого метода generateComNumber(). Вы должны хорошо понимать какое действие будет выполняться в каждом выражении. Попробуйте мысленно или на бумаге воспроизвести как будет меняться значение compNumber при каждой итерации каждого цикла; мысленно воспроизведите как будет осуществляться вхождение в цикл do…while и выход из него при каких словиях.

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

Вернемся к разработке нашего первого приложения под Андроид.-3

Теперь осталось дело за малым – при вводе варианта пользователя просто посчитать число быков и коров, и если количество быков равно 4, то значит пользователь отгадал число, в ином случае сказать пользователю каков результат его попытки.

Для начала напишем две эти функции, одна будет находить число быков, а вторая – число коров.

Вернемся к разработке нашего первого приложения под Андроид.-4

В домашнем задании вы должны были хорошо познакомиться с функцией substring() и что она делает в этом функционале, вы наверно уже поняли. Да, одна функция берет один символ из строки по индексу i из строки textNumber, другая тоже самое из строки compNumber. Первая переменная содержит число, которое ввел пользователь, а второе – которое “загадал” компьютер. Обратите внимание как происходит сравнение двух строк:

Строка1.equals(Строка2)

значение выражения истина, если Строка1 содержит тот же набор символов в той же последовательности, что и Строка2. В данном случае строки содержат по одному символу.

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

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

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

Вернемся к разработке нашего первого приложения под Андроид.-5

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

Вернемся к разработке нашего первого приложения под Андроид.-6

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

Задание по уроку:

1. Ответьте на вопрос, в функции подсчета коров сколько раз будет выполнена проверка условия “if (number.substring…”?

2. Чему будет равна сумма p+q после выполнения следующего кода:

Вернемся к разработке нашего первого приложения под Андроид.-7

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

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

Урок 5 Урок 7

El Vinto, 2023 (Copyright)

Java Android. Урок 5 (“Второй код”)

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

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.

Кто читал мою статью о программировании с нуля (Часть 1), то помнит, что обычно принято повторяющиеся команды помещать внутрь цикла. Это делает код рациональным и более близким к восприятию (программист, читающий код, будет понимать, что внутри цикла всё тоже самое, только как-то зависит от самого цикла). В нашем примере (а у вас должна быть уже написана обработка на каждую клавишу), мы должны объявить инициализацию реализации нажатия на каждую кнопку. Давайте теперь мы попробуем сделать так. Сначала создадим массив целых чисел и сразу же заполним его значениями:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-2

Т.е. мы просто в массив перенесли идентификаторы всех наших цифровых(!) кнопок. Обратите внимание, что идентификатор – это целое число (иначе бы среда разработки начала “ругаться”).

Теперь внутри onCreate() поменяйте код инициализации на такой:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-3

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

Теперь, конечно, хотело бы также оптимизировать и обработку нажатия. И тут появляется несколько возможных решений. Если бы у нас массив содержал сразу соответствие с цифрой числа и содержал бы только кнопки цифр 0-9 мы могли бы как-то это представить циклом. Можно конечно расписать каждую кнопку отдельно. Я так тоже часто делаю, чтобы легко было искать нужное с точки зрения программирования. Хотя если проявить творчество, то можно сделать и по третьему варианту, давайте его рассмотрим (но все варианты одинаково возможны); заодно сразу сделаем заготовки для служебных кнопок:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-4

Рассмотрим какие изменения произошли. Первое, что мы сделали – это вынесли получение идентификатора кнопки в отдельную переменную id. Благодаря этому вызов функции getId() будет происходить только один раз. Это мы оптимизировали с точки зрения скорости выполнения. Далее определяем, если переменная textNumber, которая накапливает нажатия на цифровые кнопки, по длине меньше 4, то можно добавить ещё одну, ведь по правилам игрок должен загадать именно 4-х значное число. Далее запускаем цикл, в котором перебираем весь массив идентификаторов наших кнопок и сравнивает, идентификатор нажатой кнопки не равен ли идентификатору из массива. Если это так, то прибавим к textNumber индекс элемента массива.

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

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-5

Однако, это очень плохой вариант. Мы бы стали строить идеологию проекта исходя из того, что массив кроме цифровых содержит и другие кнопки, а потом, по прошествии какого-то времени, забыли бы, что мы должны использовать первые 10 элементов только(!) для цифровых кнопок и сделали бы, например, вот так:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-6

По этому будем делать так, чтобы исключить из программирования пагубные привычки. Дело в том, что если это интерфейс пользователя, то ошибку такую мы сразу найдём (у нас по цифре ноль начнёт появляться на экране единица, а по единице – двойка). А вот если это какой-то другой массив, который например формирует какие-то расчетные данные, то заметить это можно будет только по прошествии многих часов поиска проблемы, ну или например, когда…Луна-XX вдруг полетит по другой траектории и накроет труд тысяч людей медным тазом…

Идем по анализу кода далее. Внимательный слушатель курса заметит один момент. У нас индекс массива – это целое число, а переменная textNumber – это строка, но при этом я делаю так:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-7

Да, компилятор позволяет так делать, хотя по всем канонам вообще правильным было бы так:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-8

Но такое делать компилятор умеет не всегда правильно. Например, если вы сделаете как-то так:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-9

это приведёт к крэшу приложения (остановка работы по фатальной ошибке с принудительным выходом из приложения) . Вся проблема тут была в том, что компилятор подумал, что в качестве целочисленного значения вы передаёте ему не число 34, а идентификатор ресурса (кто разобрался как делать домашнее задание одного из прошлых уроков, понял о чём это я), который как раз тоже является целым числом. Поскольку идентификатора ресурса с ID=34 в приложении не существует, это вызвало ошибку выполнения. Т.е. вот следующий код прошел бы на “Ура!”:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-10

Так, слишком отвлеклись…В нашем примере textNumber += i компилятор понял, что мы конечно же хотим именно число привести к типу String (просто здесь наиболее вероятным было бы ожидать от программиста, что он поставит сюда именно число, а не идентификатор ресурса, потому разработчики компилятора и сделали так), а потом произвести конкатенацию (сложение строк, когда к одной строке присоединяется другая строка справа или слева).

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

Если пользователь нажал кнопку Backspace (стереть последний символ), т.е. кнопку с идентификатором textViewBackspace и при этом текущая длина строки больше нуля (ну, когда там вообще есть что стирать), то выполняем выражение, которое стирает последний символ строки. Кто по честному выполняет домашние задания, уже знает, что функция substring() получает часть строки. В качестве первого параметра указывается позиция в строке, с которой нужно взять эту часть, а в качестве второго параметра указывается позиция до которой взять (НЕ включительно).

Теперь измените значение по-умолчанию для поля (кто этого ещё не сделал):

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-11

и перезапустите приложение. Проверьте ввод цифр и возможность их стирания. Всё должно работать.

Что мне не особо нравится в работе этого кода, так это отображение знаков “_”. Сначала они есть и это смотрится красиво, но по мере набора и стирания они пропадают, а по идее хорошо бы, если они выглядели как-то так (да и хорошо бы введенные цифры блокировались, а при стирании разблокировались):

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-12

Для начала немного изменим код обработки нажатия:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-13

Тут среда разработки начнёт подсказывать, типа “А что это такая за странная функция? Я её не знаю”. Конечно же не знает, мы её ещё только сейчас начнём писать. По этому добавьте её ниже метода onClick() и сделайте её сразу правильно:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-14

В этом методе мы должны сначала создать отдельную переменную numberAsText, которая будет хранить число вместе со знаками подчеркивания “_”. Так же сразу выполним цикл, который отметит все кнопки доступными для нажатия setEnabled(true) и укажем для них зеленоватый цвет. Далее выполним цикл от 1 до 4 в котором будем делать следующее. Если индекс цикла больше длины строки введенного уже нами числа, то заполнить это знаком подчеркивания (эти знаки в конце после цифр), в ином случае взять цифру из нашего числа по этому индексу (в виде целого числа) и поместить в эту нашу новую переменную numberAsText, после чего взять по этому числу как по индексу идентификатор элемента пользовательского интерфейса и отметить кнопку как недоступную для нажатия и установить на неё серый цвет фона. Далее выполняется заодно код, который определяет длину строки (длина строки по логике – это как раз число разрядов числа), и если эта длина равна 4, то отобразить кнопку “Enter”, в ином случае скрыть её. Обязательно разберитесь с этой частью кода чтобы хорошо понимать каждое действие, что мы делаем каждым выражением. Обращаю внимание, ранее мы представляли из целого числа строку и компилятор это смог сделать самостоятельно, а вот наоборот из строки сделать число он уже не может, по этому необходимо выполнить соответствующую функцию Integer.parseInt(). Это нужно нам как число, т.к. дальше нам нужно обратиться к элементу массива digButtons, где аргументом не может быть строка.

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

String a = “”; // это строка, мы уже несколько раз такое делали в нашем коде

int b = 0; // что-то похожее делали буквально только что, когда преобразовывали из строки в целое число

boolean c = false; // Это булева переменная, которая может принимать значение только истина или ложь (true / false)

long d = 123; // длинная целая переменная, позволяет хранить максимальное целое число в два раза большее, чем int.

float e = 12.3; // позволяет хранить нецелые числа (дробная часть отделяется точкой)

double f = 12.34353453; // Позволяет хранить нецелые числа, но в два раза с большей точностью, чем float

ClassVariable g = null; // Переменная, которая хранит ссылку на какой-то класс. Например, вот так:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-15

Есть и ещё, но пока этих достаточно.

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

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-16

Такая переменная называется переменной класса и видна из любого метода класса (к ней можно обратиться из любого метода, мы так и делаем, то из onClick(), то из setNumber() ).

Есть переменные метода, например int id в методе onClick() или String numberAsText в методе setNumber(). Они видны только из того метода, где объявлены. Т.е. я, например, не могу обратиться к numberAsText из метода onClick(), среда разработки не даст это сделать.

Переменные, которые объявлены внутри объявления цикла видны в этом объявлении и везде внутри этого цикла, снаружи уже не видны. Например, int i в цикле:

for (int i = 0; i < digButtons.length; i++ /* да, тут видна i */) {

/* где-то тут тоже видна i */

}

/* а тут уже i не видна, здесь её использовать не получится */ .

Переменные, которые объявлены внутри условия, видны везде внутри этого условия, например, int num внутри else:

if (i > textNumber.length()) {

/* тут вообще не знает что такое num */

} else {

/* кстати, если что, и тут не известно что такое num */

int num;

/* тут видна num */

}

Идем теперь далее. Перед началом каждого раунда, игра должна сгенерировать 4-х значное случайное число. Это будет то самое число, которое загадал компьютер и которое предстоит узнать человеку в процессе определенного числа попыток. Функционал Java позволяет сгенерировать целое случайное число. Делается это в два этапа. Сначала объявляется переменная класса Random, а затем выполняется один из её методов, отвечающих за генерацию числа в определенном диапазоне:

Random random = new Random();
int rndNum = random.nextInt(10);

В данном примере rndNum будет содержать случайное значение в диапазоне от 0 до 9, т.е. как раз одну случайную цифру. Однако, тут вы возможно воскликните,- Э-э-э, автор, у тебя что-то с математикой не в порядке! Нам надо 4-х разрядное число, а это тогда диапазон не до 10, а до 10 000. )))…Но я наверно сделал так тоже не спроста, наверно потому что задача не так тривиальна. Помимо того, что нам нужно сгенерировать 4-х значное число, нам нужно ещё и чтобы все цифры были в нём разные)). По этому объективных решения здесь минимум два, я просто выбрал одно из них, которое счет наиболее простым в реализации…В домашнем задании попробуйте разные способы.

Иногда возникает ситуация, когда в процессе разработки нужно посмотреть значение какой-либо переменной, но выводить это на экран телефона не всегда удобно – для этого придётся создавать какой-то элемент textView для отображения, а это вносит сумятицу в разработку. По этому разработчиками был придуман метод логгирования. Давайте, например, проверим какие числа будут сгенерированы случайным методом, описанным только что, например, с 10 попыток. Делается это примерно так:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-17

Кстати, тут может возникнуть ещё вот такая ситуация:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-18

Т.е. среда разработки не находит функцию Log. Наведите на это слово курсор мыши, в результате появится знакомая кнопка решения проблемы:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-19

Нужно импортировать класс. Вся проблема тут в том, что изначально в приложение на загружаются весь функционал Андроида, т.е. например, зачем загружать в приложения огромный пласт кода для работе с трехмерной графикой, если вы делаете обычный калькулятор. Также и тут, изначально среда разработки не включила функционал логгирования в ваше приложение, но уж коли вы хотите его использовать, то скажите об этом наверняка. Иногда может получиться так, что библиотека импорта на выбор будет не одна (например, если эта функция с таким именем встречается сразу в нескольких различных библиотеках и выполняет несколько разные по смыслу действия), то в этом случае придётся разбираться и возможно гуглить, откуда же её тянуть. В данном случае, библиотека для импорта только одна: android.util.Log.

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

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-20

Тут спрашивает, мол это из android.util.Log? Тогда жми одновременно на клавиши Alt+Enter!

В действительности, будет добавлена библиотека импорта (в самом верху кода):

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-21

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

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-22

Ещё давайте рассмотрим небольшую конструкцию, которую я использовал, это вот такое выражение:

i++

На самом деле – это упрощённая запись i = i+1 (до этого вы могли и догадаться), т.е. взять переменную i, прибавить к ней 1, а результат снова поместить в переменную i. Например, если до этого выражения в переменной i было значение 4, то после будет значение 5.

Нельзя пройти стороной и похожую конструкцию:

++i

Которая делает абсолютно тоже самое, но только как чесать левой рукой правое ухо.

Сделайте так чтобы посмотреть разницу и перезапустите приложение (мы немного отвлеклись и поиспользуем наше приложение для некоторых тестов):

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-23

Нет, здесь нет признаков корпускулярно-волнового дуализма, когда присутствие наблюдателя меняет результат. Возможно вы догадались, почему так произошло. Правильно! num1++ прибавило к num1 значение после выполнения всего выражения, которым был в конечном счете вывод лога, а выражение ++num2 сделало инкремент (увеличение на единицу) до того как начало выполнять выражение. В этом и вся разница. Если мы сделаем так:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-24

То результат будет абсолютно одинаковый:

На прошлом уроке вы сделали обработку нажатия пользователем на кнопку для каждой кнопки пользовательского интерфейса. Однако, код стал быстро расти в объёме.-25

По этому я часто выношу p++ или ++p за пределы выражения как самостоятельные выражения…но не всегда)). Например, добавлять значения какого-нибудь массива удобно вот так:

ar[i++] = 5;

ar[i++] = 6;

ar[i++] = 7;

А брать значение с последнего вот так:

int len = ar.length;

while (len > 0) {

value = ar[–len];

// …

}

Задание по уроку:

1. Попробуйте самостоятельно сделать метод для генерации 4-х значного числа. Для его хранения сделайте строковую переменную класса compNumber.

2. Сделайте так, чтобы при нажатии на кнопку Enter программа считала число “Быков” в угадываемом числе. Проверьте метод логгирования в разных вариантах.

3. Ответьте, какое значение p будет после выполнения следующих действий, после этого проверьте своё решение при помощи метода логгирования:

p = 3;

p = p+++p;

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

Урок 4 Урок 6

El Vinto, 2023 (Copyright)