Евгений Колесников 4 роки тому
батько
коміт
c92bf49a86
5 змінених файлів з 127 додано та 3 видалено
  1. 9 1
      articles/api_php.md
  2. 83 0
      articles/regex.md
  3. 2 0
      readme.md
  4. 31 0
      shpora/image_scale.md
  5. 2 2
      shpora/startActivity.md

+ 9 - 1
articles/api_php.md

@@ -321,9 +321,17 @@ class ApiServer
 Запустить локальный сервер для отладки можно из командной строки в каталоге проекта
 
 ```
-php -S localhost:8000
+php -S 127.0.0.1:8080
 ```
 
+Либо, если нам нужен доступ к этому АПИ с другого устройства (а эмулятор андроида это другое устройство) 
+
+```
+php -S 0.0.0.0:8080
+```
+
+Но в этом случае нужно и обращаться к апи не по *localhost*, а по IP-адресу (можно узнать командой ipconfig)
+
 ### Параметры GET-запроса
 
 Параметры GET-запроса передаются прямо в URL. Отделяются от пути знаком вопроса. Между собой разделяются знаком &. Представляют собой пары `ключ=значение`. Например, так может выглядеть запрос материала по нужному продукту:

+ 83 - 0
articles/regex.md

@@ -0,0 +1,83 @@
+# Использование регулярных выражений для разбора данных в любом формате
+
+Данные от АПИ могут приходить в разных форматах, и не всегда удобно парсить тот же XML штатными средствами. 
+
+Но так как от АПИ мы получаем строку, то можно сделать разбор этой строки с помощью регулярных выражений (Потренироваться в написании "регулярок" можно [тут](https://regex101.com/)). 
+
+Например, ответ от банка с курсом валют:
+
+```xml
+<ValCurs Date="08.12.2021" name="Foreign Currency Market">
+    <Valute ID="R01010">
+        <NumCode>036</NumCode>
+        <CharCode>AUD</CharCode>
+        <Nominal>1</Nominal>
+        <Name>Австралийский доллар</Name>
+        <Value>52,6319</Value>
+    </Valute>
+    <Valute ID="R01020A">
+        <NumCode>944</NumCode>
+        <CharCode>AZN</CharCode>
+        <Nominal>1</Nominal>
+        <Name>Азербайджанский манат</Name>
+        <Value>43,6374</Value>
+    </Valute>
+    <Valute ID="R01035">
+        <NumCode>826</NumCode>
+        <CharCode>GBP</CharCode>
+        <Nominal>1</Nominal>
+        <Name>Фунт стерлингов Соединенного королевства</Name>
+        <Value>98,4578</Value>
+    </Valute>
+    <Valute ID="R01060">
+        <NumCode>051</NumCode>
+        <CharCode>AMD</CharCode>
+        <Nominal>100</Nominal>
+        <Name>Армянских драмов</Name>
+        <Value>15,0921</Value>
+    </Valute>
+</ValCurs>
+```
+
+Нам из всего этого, допустим, нужны **CharCode**, **Nominal**, **Value** (на самом деле нам нужен и **Name**, но я специально его пропустил)
+
+Пишем регулярное выражение
+
+```regexp
+<CharCode>(.*?)</CharCode>.*?<Nominal>(.*?)</Nominal>.*?<Value>(.*?)</Value>
+```
+
+* **()** - группа, т.е. значение того что внутри скобок мы сможем получить после поиска
+* **.*?** - означает всё что угодно в не "жадном" режиме 
+
+В коде создаем экземпляр регулярного выражения и вытаскиваем из исходной строки все совпадения:
+
+```kt
+val re = """<CharCode>(.*?)</CharCode>.*?<Nominal>(.*?)</Nominal>.*?<Value>(.*?)</Value>""".
+            toRegex(RegexOption.DOT_MATCHES_ALL)
+val seq = re.findAll(result)
+seq.forEach {
+    Log.d("KEILOG", it.groupValues[1]+" "+it.groupValues[2]+" "+it.groupValues[3])
+}
+```
+
+* в параметрах "регулярки" обязательно добавляем опцию `RegexOption.DOT_MATCHES_ALL`, иначе переносы строки не воспринимаются как "любой" символ и поиск закончится на первой строке
+* **result** тут та строка, котрую вы получили в ответ на HTTP-запрос
+* **findAll** позвращает sequence (последовательность) результатов поиска
+* последовательность можно перебрать в цикле **forEach** и достать в нём значения (*groupValues*), которые нам нужны
+
+Естественно в реальном приложении нужно не выводить в лог, а вставить в массив валют.
+
+То же самое можно сделать и с JSON.
+
+Например, распарсим данные о погоде:
+
+```json
+{"cod":"200","message":0,"cnt":40,"list":[{"dt":1638964800,"main":{"temp":-0.63,"feels_like":-5.67,"temp_min":-0.63,"temp_max":1.28,"pressure":1021,"sea_level":1021,"grnd_level":1009,"humidity":98,"temp_kf":-1.91},"weather":[{"id":500,"main":"Rain","description":"небольшой дождь","icon":"10d"}],"clouds":{"all":100},"wind":{"speed":4.95,"deg":193,"gust":12.54},"visibility":10000,"pop":1,"rain":{"3h":0.87},"sys":{"pod":"d"},"dt_txt":"2021-12-08 12:00:00"}
+```
+
+Мне лень писать всю строку, но уже должно быть понятно
+
+```regexp
+\{\"dt\":(.*?),\"main\":\{\"temp\":(.*?),
+```

+ 2 - 0
readme.md

@@ -146,6 +146,8 @@ http://sergeyteplyakov.blogspot.com/2014/01/microsoft-fakes-state-verification.h
 
 8. [Разбор задания предыдущего чемпионата](./articles/wsrf6_1.md)
 
+9. [Использование регулярных выражений для разбора данных в любом формате](./articles/regex.md)
+
 ## Тема 5.1.5. Разработка своего API.
 
 1. [API. PHP-сервер. GET-запрос.](./articles/api_php.md)

+ 31 - 0
shpora/image_scale.md

@@ -0,0 +1,31 @@
+**android:adjustViewBounds** - Установите это значение в true, если вы хотите, чтобы ImageView изменил свои границы, чтобы сохранить соотношение сторон для его рисования.
+
+---
+
+У вас есть ImageView , который вы хотите иметь шириной fill_parent и высотой, масштабируемой пропорционально:
+
+Добавьте эти два атрибута в свой ImageView :
+
+```
+android:adjustViewBounds="true"
+android:scaleType="centerCrop"
+```
+
+И установите ширину ImageView на fill_parent , а высоту на wrap_content .
+
+Кроме того, если вы не хотите, чтобы ваше изображение было обрезано, попробуйте это:
+
+```
+android:adjustViewBounds="true"
+android:layout_centerInParent="true"
+```
+
+```
+android:adjustViewBounds="true"
+android:scaleType="fitCenter"
+```
+
+```
+android:adjustViewBounds="true"
+android:scaleType="fitXY"
+```

+ 2 - 2
shpora/startActivity.md

@@ -22,11 +22,11 @@ intent.getStringExtra("city_name")
 // запуск
 startActivityForResult( 
     Intent(this, CityListActivity::class.java), 
-    1
+    1 // идентификатор окна
 )
 
 // получение результата
-// здесь requestCode = 1 (ответы от всех запущенных activity приходят в одно место)
+// здесь requestCode = <идентификатор окна> (ответы от всех запущенных activity приходят в одно место)
 // resultCode задается при ответе
 // data необязательные данные из newIntent
 @SuppressLint("MissingSuperCall")