Подписаться на получение новых статей на почту:

Передача данных с Android на stm32. Всплывающее меню. Шаг №90

Всем привет. Продолжим разработку Android-приложения SmartHouse. Давайте немного вспомним, что в прошлой записи мы закончили разбор основ приема данных на ОС Android. «Сегодня» же мы с Вами разберем передачу ОС — Wi-Fi ESP8266. Разберем механизм сокетов на передачу, потоки, создадим всплывающее меню и собственно осуществим передачу данных. Как Вы помните в качестве управления модулем, мы используем отладочную плату на основе stm32.

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

 

 

 

активность

Разделим функции кнопок на две части — это связь и поток. Соответственно добавим два пункта Link and Flow, а все кнопки сделаем как подменю. Как создавать меню, ставить флажки читаем по следующей  ссылке:  https://developer.android.com/guide/topics/ui/menus?hl=ru. Я решил остановится на всплывающем меню, привязав его к кнопке «Configuration». Хотя может быть позже добавим в приложение и контекстное меню. Разница между ними на рисунке ниже.

 

 

контекстное vs всплывающее

Для реализации данных действий создадим папку menu в ресурсах, а в ней файл config.xml — пропишем следующий код:

<?xml version=«1.0» encoding=«utf-8»?>
<menu xmlns:android=«http://schemas.android.com/apk/res/android» >
        <group android:id=«@+id/menugroup1» >
               <item
                      android:id=«@+id/Link»
                      android:title=«Link»>
                      <menu>
                             <item
                                   android:id=«@+id/Connect»
                                   android:title=«Connect» />
                              <item
                                   android:id=«@+id/IP»
                                   android:title=«IP» />
                               <item
                                   android:id=«@+id/Stop_Net»
                                   android:title=«Stop_Net» />
                        </menu>
                 </item>
                 <item
                        android:id=«@+id/Flow»
                        android:title=«Flow»>
                        <menu>
                               <item
                                    android:id=«@+id/StopData»
                                    android:title=«StopData» />
                               <item
                                    android:id=«@+id/Read»
                                    android:title=«Read» />
                                <item
                                    android:id=«@+id/Clear»
                                    android:title=«Clear» />
                        </menu>
                </item>
       </group>
</menu>
где:
menu — определяет класс Menu, который является контейнером для пунктов меню;
group – необязательный невидимый контейнер, который разделяет пункты меню на категории и     назначает им одинаковые свойства, например показывает или скрывает все пункты с помощью   setGroupVisible ();
item — создает класс MenuItem, который представляет один пункт меню. Этот элемент может содержать   вложенный элемент <menu> для создания вложенных меню, что мы и сделали – использовали подменю.
android:id — идентификатор ресурса, который является уникальным для этого пункта, что позволяет   приложению распознавать пункт, когда его выбирает пользователь.
android:title — ссылка на строку, которая будет использоваться в качестве названия пункта меню.

Это то, что мы используем. Но также здесь возможно применять флажки, различные выделения, иконки и многое другое. Есть также программный способ создания меню. Но пока «пойдем» более простым путем. В файле активности создаем кнопку «Configuration» и привязываем ее к объекту в программе.
Button config = findViewById (R.id.config);
В обработчике события прописываем функцию, которая будет загружать меню, и обрабатывать действия по нажатию:
case R.id.config:
       showPopupMenu (v);
Теперь пропишем, само меню и обработку событий по нажатию:

 private void showPopupMenu(View v) {  
   PopupMenu popupMenu = new PopupMenu(this, v); 
    popupMenu.inflate(R.menu.config);// получить из XML-файла (config.xml) всплыающее меню    
    popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {                
    @Override        
    public boolean onMenuItemClick(MenuItem item) { //При выборе пункта меню, выполняется обратный вызов onMenuItemClick().
             switch (item.getItemId()) { //Запрашиваем ID для выбранного пункта
                case R.id.Link:                    
                     Toast.makeText(getApplicationContext(), "Вы выбрали Link", Toast.LENGTH_SHORT).show();                    
                     return true;          
                case R.id.Flow:     
                     Toast.makeText(getApplicationContext(), "Вы выбрали Flow", Toast.LENGTH_SHORT).show();          
                     return true;          
                case R.id.Connect://Поиск сети (См. №88) 
                     final WifiManager wifi = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);     
                     ScanReceiver scanReceiver = new ScanReceiver();                    
                     registerReceiver(scanReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));  
                     wifi.startScan();    
                     return true;       
                case R.id.IP:     //Производим чтение адреса сервера (См. №89) 
                     new ScanIpTask().execute();    
                     return true;            
                case R.id.Stop_Net:                    
                     ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);     
                     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {                        
                           connectivityManager.bindProcessToNetwork(null);              
                     } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
                           connectivityManager.setProcessDefaultNetwork(null);  
                     }
                     return true; 
                 case R.id.StopData:
                        myClientTask.cancel(true);
                        onStop();
                     return true; 
                 case R.id.Read:         
                     myClientTask.execute();             
                     return true;              
                 case R.id.Clear:               
                     textResponse.setText("");        
                     return true;        
                 default:            
                     return false;         
              }       
           }  
       });    
       popupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {//Закрытие меню, при выборе одного из пунктов или касанием экрана. 
          @Override     
          public void onDismiss(PopupMenu menu) {       
                  Toast.makeText(getApplicationContext(), "onDismiss", Toast.LENGTH_SHORT).show();     
          } 
       }); 
       popupMenu.show(); //Отображение на экране.
  }

всплывающее менюФункции пунктов меню соответствуют функциям кнопок. Кнопки убираем. Назначаем их функциональность пунктам меню. Убеждаемся, что они работают – идем далее. Подготовим файл activity_main – добавим кнопку SEND и поле для ввода информации, здесь, временно, используем ручной ввод данных для проверки работоспособности. Для ввода используем компонент EditText — это текстовое поле для пользовательского ввода, является наследником TextView.

<EditText
android:id=«@+id/editText»
android:layout_width=«match_parent»
android:layout_height=«wrap_content»
android:inputType=«text»
android:minWidth=«100dp»
android:hint=«Input text» />

где: android:inputType=«text» — стандартная клавиатура и android:hint="Input text" — подсказка в поле ввода. На рисунке ниже добавление поля и кнопки.

Поле ввода Android

Теперь при нажатии на кнопку в буфер message помещается содержимое поля ввода, перед этим преобразовав его к типу String, т.к. возвращаемое значение основного метода getText () класса EditText () – типа Editable.
case R.id.send:
message = editText.getText ().toString ();
Toast.makeText (this, message, Toast.LENGTH_LONG).show ();
Log.e («TCP Client», «Send Button»);

Для проверки можно добавить всплывающее сообщение, что переменная приняла показания в поле ввода, потом можно удалить. Все подготовительные работы произвели – приступим к сокету на передачу. В этот раз при передачи нам не требуется никакого взаимодействия с основным пользовательским потоком UI, никаких обновлений интерфейса и тому подобное. Поэтому сегодня мы запустим фоновый поток используя экземпляр класса Runnable, его метод run (). Для чего необходимо создать объект Thread, в конструкторе у которого указывается созданный Runnable. После этого можем запускать новый поток с помощью метода start (). Здесь хочется оговорится что для повторного запуска потока, необходима создавать новый тред (Thread), чтобы избежать исключения, например IllegalThreadStateException. О чем говорит литература: It is never legal to start a thread more than once. In particular, a thread may not be restarted once it has completed execution.

Немного остановимся и разложим по полкам  использование Thread и Thread (Runnable). Тема объемная и сложная. Но все же постепенно будем вносить ясность в данные процессы. В чем разница. Thread — это класс, который реализует поток. Runnable — это интерфейс описывающий метод Run, с помощью которого Вы можете передать в другой класс свой код для выполнения. Thread и  Runnable никак не связаны. Использование данным классом интерфейса позволяет наследовать какой — то другой класс, т.е. использовать множественное наследование (что не поддерживается JAVA). Т.е. в данном случае Runnable — это интерфейс для наследования от какого то другого родителя. Если достаточно наследование от класса то используем Thread. Thread — это абстракция над физическим потоком.
Runnable — это абстракция над выполняемой задачей. Ниже примеры использования конструкций. В первом случае это объявить класс подклассом Thread, и переопределить его метод run, который вызывается из метода start класса Thread, который мы и вызываем для запуска потока.
Например наш код выглядел бы так:
Создание и вызов:
SocetThread mySocet = new SocetThread ();
mySocet.start ();
Сам класс:
public class SocetTread extern Thread{ ...public void run (){ .......}...}

Второй — объявить класс, реализующий интерфейс Runnable. Затем этот класс реализует метод run. Затем экземпляр класса может быть выделен, передан как аргумент при создании Thread и запущен.
Например наш код выглядел бы так:
Создание и вызов:
Thread mySocet = new Thread (new SocetRunnable ());
mySocet.start ();
Сам класс:
public class SocetRunnable implements Runnable{...public void run ()...}

Из форума: https://ru.stackoverflow.com/questions/399989

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

В этой записи я показал пример использования потока через другие инструменты и в последствии вернемся к AsyncTask. Ниже пример отправки данных через сокет используя интерфейс Runnable

В кнопку, которая выше добавляем:
mySocet.start ();
создание потока и открытие socket:

Thread mySocet = new Thread(new Runnable() {
    public void run() {
            Socket socket = null;
            Log.e("TCP Client", "C: Connecting...");
            while(true){
                  if(bufer == message){
                  }
                  else{
                      try{
                         socket = new Socket(SERVERIP, SERVERPORT);
                         out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
                         out.println(message);
                         out.flush();//для очистки буфера
                         bufer = message;
                      } catch (IOException e) {
                         e.printStackTrace();
                      }
                      try{
                         assert socket != null;
                         socket.close();
                      } catch (IOException e) {
                         e.printStackTrace();
                      }
                  }
              }
          }
      });

Логика следующая. Создается объект сокет, происходит соединение, получение выходного потока и запись в него буфера, очистка буфера, закрытие сокета. Здесь также как и в предыдущей статье необходимо поизвращаться над остановкой потока. Но главной задачей было показать работоспособность передачи и приема модулем инфы. Так что оставляю как есть и переносим логику в AsyncTask.

Со стороны stm32 мы используем уже написанный нами код, а именно протокол USART/UART на CMSIS. Если вкратце, то мы использовали прерывание по приему, только тогда мы посылали данные с терминала, через usb-uart преобразователь. В этот раз на рисунке ниже, я отправляю цифру три с ОС Android, где по приему светодиод затухает. Настройка модуля аналогична, описана в конце прошлой cтаnьи.

stm32-esp8266-Android

Подведем итог. Был разобран вариант всплывающего меню. Как вариант удобного сокрытия функциональных кнопок, при постоянном добавления функций. Также удобство данного меню – вызов с любого места. Научились создавать поле для ввода. Был разобран пример отправки данных с Android, используя фоновый поток c помощью Thread and Runnable. Рассмотрели их отличия. Для примера использовали их для отправки данных на микроконтроллер (используя ранее написанный код для него). Следующим шагом мы адаптируем наше приложение для подключения к WEB-ресурсу. Попробуем использовать ранее написанный web-интерфейс для приема данных с микроконтроллера AVR через модуль ESP8266 и отправку данных на Android. На этом мы сегодня и остановимся. Всем пока.

Просмотрено 668 раз.

Я на Google+

Передача данных с Android на stm32. Всплывающее меню. Шаг №90: Один комментарий

  1. Здравствуйте! Для передачи строки по UART служит функция UARTSend (), а при приеме данных происходит прерывание и срабатывает функция USART1_IRQHandler (). В данной функции, в зависимости от принятой цифры 1 или 0, подается команда на 8-ногу платы для включения или выключения подключенного к ней светодиода. А также, происходит передача строки «LED ON» или «LED OFF» обратно в UART. Как видите ничего сложного нет.

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

Ваш e-mail не будет опубликован.

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Subscribe without commenting