Розробка під 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 використовується програмний пісок», іншими словами — це скорочений запис коду. Не всім відома ця конструкція, тому я вирішив що треба звернути на неї увагу. Особливо це може згодиться новачкам.

Читайте також  Telegram Desktop теж локально зберігає листування в доступному вигляді

Працює ця конструкція таким чином

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>

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

Читайте також  Кібердемони: штучний інтелект DOOM 2016

У нашому додатку клавіатура виглядає так

Багатопоточність

Многопотчность — зрозуміло з назви, це багато потоків. А багато потоків — це означає виконання декількох операцій одночасно. Багатопоточність — це досить проблемна тема в програмуванні. Що в 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() зрозуміло, що створюється «новий потік», точніше виділяється процесорний час під новий потік. І потім в наступній сходинці ми запускаємо новий потік, точніше старий, який був запущений при виклику конструктора нашого класу.

Читайте також  Склейка двох apk файлів в один

Все вищевикладене я перевіряв на своєму телефоні, так що, якщо ви зробили все правильно, то проблем або помилок у вас не повинно виникнути.

Як ми бачимо, цей метод досить зручний і простий в розумінні, я постарався якомога простіше описати, так що сподіваюся вам все зрозуміло, а я нічого не упустив.

І тепер мабуть перейдемо до 3 пункту нашої статті, а саме до інтеграції реклами.

Інтеграція реклами

Насправді з цього приводу я не можу багато розповісти, я можу тільки порадити. З рекламою не все так легко і прозоро. Особисто я б порадив використовувати вам Appodeal, він не так давно на ринку, але досить успішно працює.

Так само варто відзначити, що там дуже хороша російськомовна підтримка, яка майже завжди відразу відповідає на твоє запитання або на твою проблему. Це відразу дає зрозуміти наскільки ця мережа лояльна.

Хочу відразу сказати, якщо ви раптом будете нею користуватися, обов’язково налаштуйте оплату AdMob і в Appodeal, інакше реклама просто не вантажиться. З-за того, що я не налаштував рахунку я даремно витратив цілий день, і потім мені в підтримці сказали: «А налаштував я рахунку?». І після того, як я це зробив, то реклама через 2 години з’явилася.

Висновок

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

Так як ця стаття для початківців програмістів, хочу відзначити одну очевидну річ. Якщо вам дійсно подобається програмування, і ви готові тонни годин витрачати на вирішення тієї чи іншої проблеми, то програмування це ваше, інакше немає. Так само не варто виходити за рамки. Так як теж занадто довгий рішення, чогось не надто складного і не дуже простого — не є добре. Але це і так очевидний факт. Насправді, я цю фразу прочитав десь в інтернеті. За фактом вона вірна. Якщо задуматися, то дійсно, якщо б програмування, серйозне програмування було б таким легким, то їм би не займалася мала частина людей від загальної кількості всіх людей.

Сподіваюся, що  стаття була комусь корисною і комусь дійсно допомогла, допомогла заощадити час.

Степан Лютий

Обожнюю технології в сучасному світі. Хоча частенько і замислююся над тим, як далеко вони нас заведуть. Не те, щоб я прям і знаюся на ядрах, пікселях, коллайдерах і інших парсеках. Просто приходжу в захват від того, що може в творчому пориві вигадати людський розум.

You may also like...

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *