Разработка под Android и решение проблем связанных с разработкой

Цель статьи

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

В данной статье будут рассмотрены следующие вопросы:

  • Кастомная клавиатура под Android
  • Многопоточность
  • Интеграция рекламы в программу

Кастомная клавиатура под Android

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

Начнём:

  1. Создадим новый проект в android studio.
  2. Создадим пару классов
  3. Протестируем приложение

Сделаем простенькую клавиатуру из 9 символов с возможностью удалять эти символы.
Назовём наш проект KeyBoardTest

Выберем пустую активити и начнём

Готово мы создали наш новый проект. Теперь займёмся самым основным, а именно создадим layout файл в папке — res/layout и назовём его keyboard, тут у нас будет размещён внешний вид клавиатуры.

Нарисуем эту клавиатуру:

<?xml version="1.0" encoding="utf-8"?> <FrameLayout     xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"     android:layout_height="match_parent">      <LinearLayout         android:layout_gravity="bottom"         android:background="@color/colorBlue"         android:layout_width="match_parent"         android:layout_height="100dp"         android:orientation="vertical">         <LinearLayout             android:layout_width="match_parent"             android:layout_height="50dp"             android:orientation="horizontal">             <TextView                 android:id="@+id/one"                 android:textColor="@color/colorWhite"                 android:gravity="center"                 android:textSize="30sp"                 android:text="@string/one"                 android:layout_weight="1"                 android:layout_width="0dp"                 android:layout_height="match_parent"/>             <TextView                 android:id="@+id/two"                 android:textColor="@color/colorWhite"                 android:gravity="center"                 android:textSize="30sp"                 android:text="@string/two"                 android:layout_weight="1"                 android:layout_width="0dp"                 android:layout_height="match_parent"/>             <TextView                 android:id="@+id/three"                 android:textColor="@color/colorWhite"                 android:gravity="center"                 android:textSize="30sp"                 android:text="@string/three"                 android:layout_weight="1"                 android:layout_width="0dp"                 android:layout_height="match_parent"/>             <TextView                 android:id="@+id/four"                 android:textColor="@color/colorWhite"                 android:gravity="center"                 android:textSize="30sp"                 android:text="@string/four"                 android:layout_weight="1"                 android:layout_width="0dp"                 android:layout_height="match_parent"/>             <TextView                 android:id="@+id/five"                 android:textColor="@color/colorWhite"                 android:gravity="center"                 android:textSize="30sp"                 android:text="@string/five"                 android:layout_weight="1"                 android:layout_width="0dp"                 android:layout_height="match_parent"/>         </LinearLayout>         <LinearLayout             android:layout_width="match_parent"             android:layout_height="50dp"             android:orientation="horizontal">             <TextView                 android:id="@+id/six"                 android:textColor="@color/colorWhite"                 android:gravity="center"                 android:textSize="30sp"                 android:text="@string/six"                 android:layout_weight="1"                 android:layout_width="0dp"                 android:layout_height="match_parent"/>             <TextView                 android:id="@+id/seven"                                 android:textColor="@color/colorWhite"                 android:gravity="center"                 android:textSize="30sp"                 android:text="@string/seven"                 android:layout_weight="1"                 android:layout_width="0dp"                 android:layout_height="match_parent"/>             <TextView                 android:id="@+id/eight"                 android:textColor="@color/colorWhite"                 android:gravity="center"                 android:textSize="30sp"                 android:text="@string/eight"                 android:layout_weight="1"                 android:layout_width="0dp"                 android:layout_height="match_parent"/>             <TextView                 android:id="@+id/nine"                 android:textColor="@color/colorWhite"                 android:gravity="center"                 android:textSize="30sp"                 android:text="@string/nine"                 android:layout_weight="1"                 android:layout_width="0dp"                 android:layout_height="match_parent"/>             <TextView                 android:id="@+id/delete"                 android:textColor="@color/colorWhite"                 android:gravity="center"                 android:textSize="30sp"                 android:text="@string/delete"                 android:layout_weight="1"                 android:layout_width="0dp"                 android:layout_height="match_parent"/>         </LinearLayout>      </LinearLayout>  </FrameLayout> 

После того, как наша клавиатура нарисована, мы можем создать класс, который будет регистрировать все наши нажатия и писать то, что нам нужно. Обратите внимание на то, что у каждого textview есть id — это очень важно!

Назовём этот класс KeyBoardListener.

Пропишем конструктор нашего класса, он в качестве аргументов принимает MainActivity — поле в котором мы работаем, иными словами место расположения нашего editText, и так же он принимает сам editText, в котором мы будем печатать символы с нашей клавиатуры.

package keyboard.develop.keyboardtest;  import android.content.Context; import android.view.View; import android.widget.EditText; import android.widget.TextView;  public class KeyBoardListener {      EditText editText;     private StringBuilder finalText = new StringBuilder();      KeyBoardListener(MainActivity view, final EditText editText) {         this.editText = editText;         TextView one = view.findViewById(R.id.one);         TextView two = view.findViewById(R.id.two);         TextView three = view.findViewById(R.id.three);         TextView four = view.findViewById(R.id.four);         final TextView five = view.findViewById(R.id.five);         TextView six = view.findViewById(R.id.six);         TextView seven = view.findViewById(R.id.seven);         TextView eight = view.findViewById(R.id.eight);         TextView nine = view.findViewById(R.id.nine);         TextView delete = view.findViewById(R.id.delete);          one.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View view) {                 int selection = editText.getSelectionEnd();                 finalText.insert(selection, "1");                 setTextSelection();             }         });          two.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View view) {                 int selection = editText.getSelectionEnd();                 finalText.insert(selection, "2");                 setTextSelection();             }         });         three.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View view) {                 int selection = editText.getSelectionEnd();                 finalText.insert(selection, "3");                 setTextSelection();             }         });         four.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View view) {                 int selection = editText.getSelectionEnd();                 finalText.insert(selection, "4");                 setTextSelection();             }         });         five.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View view) {                 int selection = editText.getSelectionEnd();                 finalText.insert(selection, "5");                 setTextSelection();             }         });         six.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View view) {                 int selection = editText.getSelectionEnd();                 finalText.insert(selection, "6");                 setTextSelection();             }         });         seven.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View view) {                 int selection = editText.getSelectionEnd();                 finalText.insert(selection, "7");                 setTextSelection();             }         });         eight.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View view) {                 int selection = editText.getSelectionEnd();                 finalText.insert(selection, "8");                 setTextSelection();             }         });         nine.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View view) {                 int selection = editText.getSelectionEnd();                 finalText.insert(selection, "9");                 setTextSelection();             }         });         delete.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View view) {                 int selection = editText.getSelectionEnd();                 if (finalText.length() > 0) {                     finalText = stringToBuilder((selection == editText.length() - 1) ? finalText.substring(0, finalText.length() - 2) : finalText.substring(0, selection - 1) + finalText.substring(selection));                     editText.setText(finalText);                 }                 editText.setSelection(selection > editText.length() - 1 ? finalText.length() : selection - 1 <= 1 ? 1 : selection - 1);             }         });      }      private StringBuilder stringToBuilder(String s) { return new StringBuilder(s); }      private void setTextSelection() {         int selection = editText.getSelectionEnd();         editText.setText(finalText);         editText.setSelection(selection + 1);     }  } 

Теперь подробно рассмотрим этот код. В сам конструктор мы передали editText и активити для того, чтобы взять кнопки и назначить им выполнение ввода. Хочется отметить, что в методе кнопки delete используется «программный песок», иными словами — это сокращённая запись кода. Не всем известна эта конструкция, поэтому я решил что надо обратить на неё внимание. Особенно это может пригодится новичкам.

Работает эта конструкция таким образом

int p = (условие) ? вариант 1 : вариант 2; int p = k == 2 ? 7 : 3; // это можно записать и так if (k == 2)     p = 7; else      p = 3; 

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

package keyboard.develop.keyboardtest;  import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.view.WindowManager; import android.widget.EditText; import android.widget.LinearLayout;   public class MainActivity extends AppCompatActivity {     private LinearLayout layout;      @Override     protected void onCreate(Bundle savedInstanceState) {         getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         final EditText editText = findViewById(R.id.edit);         KeyBoardListener keyBoardListener = new KeyBoardListener(this, editText);         layout = findViewById(R.id.keyBoard);         editText.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View view) {                 layout.setTranslationY(0); // Наша клавиатура появляется             }         });     }      @Override     public void onBackPressed() {         layout.setTranslationY(250); // Клавиатура уезжает за пределы экрана - исчезает     } } 

Стоит обратить внимание на эту строку

getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); 

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

Так же очень важно, что мы не вызываем метод hide() для нашей клавиатуры, а просто убираем её за пределы экрана. Вы, наверное, зададите вопрос: «А почему мы не сделаем hide или не вызовем метод gone?» А я вам отвечу, это связано с жадным сборщиком мусора android. Если мы вызовем этот метод, то все указатели на кнопки пропадут, и все отклики не будет работать.
Теперь же вы можете спросить: «А почему мы не можем вызвать заново конструктор и просто всё переназначить?». А я вам отвечу, что всё дело в том, что это довольно требовательный процесс, и в этом процессе перевызова возникают некоторые проблемы при работе с памятью, что вызывает падение приложения, а просто вызов setTranslationY() удобнее и проще, тем более это нам не мешает убрать клавиатуру или поднять её. За счёт того, что весь этот метод вызывается в виде отдельного класса, мы можем добавить его куда-угодно и использовать в любых программах. Таким образом получается объектность кода.

Но я не показал вам, как сделать это в xml коде, как нам указать расположение этой клавиатуры, а всё очень просто

<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto"     xmlns:tools="http://schemas.android.com/tools"     android:layout_width="match_parent"     android:layout_height="match_parent"     tools:context="keyboard.develop.keyboardtest.MainActivity">     <LinearLayout         android:layout_width="match_parent"         android:layout_height="match_parent"         android:orientation="vertical">         <TextView             android:textColor="@color/colorPrimary"             android:gravity="center"             android:textSize="25sp"             android:layout_width="match_parent"             android:layout_height="50dp"             android:text="Write your text here!"/>         <EditText             android:id="@+id/edit"             android:layout_width="match_parent"             android:layout_height="50dp"/>     </LinearLayout>      <LinearLayout // Вот тут и располагается наша клавиатура         android:translationY="100dp"         android:id="@+id/keyBoard"         android:layout_height="wrap_content"         android:layout_width="match_parent"         android:layout_gravity="bottom">         <include layout="@layout/keyboard"/> // А точнее тут, в этой строчке     </LinearLayout>  </FrameLayout> 

Теперь лёгкими манипуляциями мы можем использовать нашу клавиатур где-угодно, даже в фрагментах.

В нашем же приложении клавиатура выглядит так

Многопоточность

Многопотчность — понятно из название, что это много потоков. А много потоков — это значит выполнение нескольких операций одновременно. Многопоточность — это довольно проблемная тема в программировании. Что в C++, что в Java, что в других языках с многопоточностью всегда были проблемы. Благо почти во всех языках есть высокоуровневое решение этой проблемы. Но мы сейчас занимаемся разработкой под Android, поэтому про Anroid, а точнее про язык программирования Java мы будем говорить.

В ЯП Java есть такая вещь как Thread — она удобна при рисовании, при частой мгновенной перерисовки изображения, есть много статей на эту тему, в том числе и на Хабре, поэтому я не буду рассматривать этот вариант. Меня интересует так называемый — «засыпающий поток», поток который ждёт своего вызова для решение какой-либо поставленной задачи. Это тот случай, когда ты вызвал поток, он отработал и уснул, не тратя при этом ресурсов устройства во время ожидания новой задачи.

И название тому, что я описал выше — это библиотека в Android Java ExecutorServise

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

Для этого создадим новый класс, который будет называться ExecutorThread. Поставим нам задачу, что нам надо прибавлять к цифре p единицу, пока это p не будет равно введённое нами числу в 4 степени. Всё делается для того, чтобы вычисление было более долгое. И чтобы весь интерфейс не зависал, мы всё вынесем это в отдельный поток.

package keyboard.develop.keyboardtest;  import android.widget.TextView;  import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;  public class ExecutorThread {     private ExecutorService executorService;     private Thread thread;     private int p = 0;     private int pow = 0;     private int k = 0;     private TextView textView;     ExecutorThread(final TextView text) {         p = 0;         textView = text;         thread = new Thread(new Runnable() {             @Override             public void run() {                 for (int i = 0; i < pow; i++)                     p++;                 String s = "Result " + k + "^4 = " + String.valueOf(p);                 textView.setText(s);             }         });         thread.start();     }      void doWork() {         executorService = Executors.newSingleThreadExecutor();         executorService.submit(thread);     }      void setK(int k) {         p = 0;         this.k = k;         pow = (int) Math.pow(k, 4);         textView.setText("Please wait. We are calcing!");     } } 

Как мы видим, в тот момент пока мы не посчитали ещё, то у нас виднеется запись «Please wait. We are calcing!», что ясно — «Подождите пожалуйста. Мы считаем!». И после того, как мы посчитаем, то мы выведем текст в наш textView, который мы передали в конструктор нашего класса. Для того, чтобы всё заработало мы должны в наше activity_main, которое у нас было при создании проекта, добавить textView после нашего editText.

<TextView             android:gravity="center"             android:textColor="@color/colorPrimary"             android:textSize="25sp"             android:id="@+id/textView"             android:layout_width="match_parent"             android:layout_height="50dp"/> 

И мы так же должны добавить код ниже в наш основной класс — MainActivity.

executorThread = new ExecutorThread((TextView)findViewById(R.id.textView));         editText.addTextChangedListener(new TextWatcher() {             @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}             @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}             @Override             public void afterTextChanged(Editable editable) {                 if (!editable.toString().equals("")) {                     executorThread.setK(Integer.parseInt(editText.getText().toString()));                     executorThread.doWork();                 }             }         }); 

Стоит отметить, что метод addTextChangeListener отвечает за то, что текст в нашем editText меняется. Как мы видим, внтури этого метода мы вызываем функцию doWork(), которая в свою очередь выполняет вот эти строки

executorService = Executors.newSingleThreadExecutor(); executorService.submit(thread); 

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

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

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

И теперь пожалуй перейдём к 3 пункту нашей статьи, а именно к интеграции рекламы.

Интеграция рекламы

На самом деле по этому поводу я не могу много рассказать, я могу только посоветовать. С рекламой не всё так легко и прозрачно. Лично я бы посоветовал использовать вам Appodeal, он не так давно на рынке, но довольно успешно работает.

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

Хочу сразу сказать, если вы вдруг будете ею пользоваться, обязательно настройте оплату в AdMob и в Appodeal, иначе реклама просто не будет грузится. Из-за того, что я не настроил счета я впустую потратил целый день, и потом мне в поддержке сказали: «А настроил ли я счета?». И после того, как я это сделал, то реклама спустя 2 часа появилась.

Заключение

Позже я соберу zip-архив нашего примера и закину куда-нибудь на диск. И оставлю потом ссылку здесь на скачивание.

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

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


ссылка на оригинал статьи https://habr.com/post/426223/

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

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