- Пишем первое приложение для Android
- Установка необходимых программ и утилит
- Запуск виртуального устройства
- Список устройств
- Создание проекта
- Структура проекта
- Первые строки
- Square.java
- Player.java
- Game.java
- Определение победителя
- WinnerCheckerHorizontal.java
- WinnerCheckerVertical.java
- WinnerCheckerDiagonalLeft.java
- WinnerCheckerDiagonalRight.java
- Видео готового приложения
- Создаем приложение для ANDROID быстро и просто
Пишем первое приложение для Android
В любом деле самое сложное — это начало. Часто бывает тяжело войти в контекст, с чем столкнулся и я, решив разработать свое первое Android-приложение. Настоящая статья для тех, кто хочет начать, но не знает с чего.
Статья затронет весь цикл разработки приложения. Вместе мы напишем простенькую игру “Крестики-Нолики” с одним экраном (в ОС Android это называется Activity).
Отсутствие опыта разработки на языке Java не должно стать препятствием в освоении Android. Так, в примерах не будут использоваться специфичные для Java конструкции (или они будет минимизированы на столько, на сколько это возможно). Если Вы пишете, например, на PHP и знакомы с основополагающими принципами в разработке ПО, эта статья будет вам наиболее полезна. В свою очередь так как, я не являюсь экспертом по разработке на Java, можно предположить, что исходный код не претендует на лейбл “лучшие практики разработки на Java”.
Установка необходимых программ и утилит
- JDK — набор для разработки на языке Java;
- Android SDK and AVD Manager — набор утилит для разработки + эмулятор;
- IDE c поддержкой разработки для Android:
- Eclipse + ADT plugin;
- IntelliJ IDEA Community Edition;
- Netbeans + nbandroid plugin;
Утилиты устанавливаются в определенном выше порядке. Ставить все перечисленные IDE смысла нет (разве только если Вы испытываете затруднения с выбором подходящей). Я использую IntelliJ IDEA Community Edition, одну из самых развитых на данный момент IDE для Java.
Запуск виртуального устройства
Запустив AVD Manager и установив дополнительные пакеты (SDK различных версий), можно приступить к созданию виртуального устройства с необходимыми параметрами. Разобраться в интерфейсе не должно составить труда.
Список устройств
Создание проекта
Мне всегда не терпится приступить к работе, минимизируя подготовительные мероприятия, к которым относится создание проекта в IDE, особенно, когда проект учебный и на продакшн не претендует.
По нажатию кнопки F6 проект соберется, откомпилируется и запустится на виртуальном девайсе.
Структура проекта
На предыдущем скриншоте видна структура проекта. Так как в этой статье мы преследуем сугубо практические цели, заострим внимание лишь на тех папках, которые будем использовать в процессе работы. Это следующие каталоги: gen, res и src.
В папке gen находятся файлы, которые генерируются автоматически при сборке проекта. Вручную их менять нельзя.
Папка res предназначена для хранения ресурсов, таких как картинки, тексты (в том числе переводы), значения по-умолчанию, макеты (layouts).
src — это папка в которой будет происходить основная часть работы, ибо тут хранятся файлы с исходными текстами нашей программы.
Первые строки
Как только создается Activity (экран приложения), вызывается метод onCreate(). IDE заполнила его 2 строчками:
super.onCreate(savedInstanceState); setContentView(R.layout.main);
Метод setContentView (равносильно this.setContentView) устанавливает xml-макет для текущего экрана. Далее xml-макеты будем называть «layout», а экраны — «Activity». Layout в приложении будет следующий:
Для этого приложения идеально подойдет TableLayout. Id можно присвоить любому ресурсу. В данном случае, TableLayout присвоен При помощи метода findViewById() можно получить доступ к виду:
private TableLayout layout; // это свойство класса KrestikinolikiActivity public void onCreate(Bundle savedInstanceState)
Теперь необходимо реализовать метод buildGameField(). Для этого требуется сгенерировать поле в виде матрицы. Этим будет заниматься класс Game. Сначала нужно создать класс Square для ячеек и класс Player, объекты которого будут заполнять эти ячейки.
Square.java
package com.example; public class Square < private Player player = null; public void fill(Player player) < this.player = player; >public boolean isFilled() < if (player != null) < return true; >return false; > public Player getPlayer() < return player; >>
Player.java
package com.example; public class Player < private String name; public Player(String name) < this.name = name; >public CharSequence getName() < return (CharSequence) name; >>
Все классы нашего приложения находятся в папке src.
Game.java
package com.example; public class Game < /** * поле */ private Square[][] field; /** * Конструктор * */ public Game() < field = new Square[3][3]; squareCount = 0; // заполнение поля for (int i = 0, l = field.length; i < l; i++) < for (int j = 0, l2 = field[i].length; j < l2; j++) < field[i][j] = new Square(); squareCount++; >> > public Square[][] getField() < return field; >>
Инициализация Game в конструкторе KrestikinolikiActivity.
public KrestikinolikiActivity() < game = new Game(); game.start(); // будет реализован позже >
Метод buildGameField() класса KrestikinolikiActivity. Он динамически добавляет строки и колонки в таблицу (игровое поле):
private Button[][] buttons = new Button[3][3]; //(. ) private void buildGameField() < Square[][] field = game.getField(); for (int i = 0, lenI = field.length; i < lenI; i++ ) < TableRow row = new TableRow(this); // создание строки таблицы for (int j = 0, lenJ = field[i].length; j < lenJ; j++) < Button button = new Button(this); buttons[i][j] = button; button.setOnClickListener(new Listener(i, j)); // установка слушателя, реагирующего на клик по кнопке row.addView(button, new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT)); // добавление кнопки в строку таблицы button.setWidth(107); button.setHeight(107); >layout.addView(row, new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT)); // добавление строки в таблицу > >
В строке 8 создается объект, реализующий интерфейс View.OnClickListener. Создадим вложенный класс Listener. Он будет виден только из KrestikinolikiActivity.
public class Listener implements View.OnClickListener < private int x = 0; private int y = 0; public Listener(int x, int y) < this.x = x; this.y = y; >public void onClick(View view) < Button button = (Button) view; >>
public class Game < /** * игроки */ private Player[] players; /** * поле */ private Square[][] field; /** * начата ли игра? */ private boolean started; /** * текущий игрок */ private Player activePlayer; /** * Считает колличество заполненных ячеек */ private int filled; /** * Всего ячеек */ private int squareCount; /** * Конструктор * */ public Game() < field = new Square[3][3]; squareCount = 0; // заполнение поля for (int i = 0, l = field.length; i < l; i++) < for (int j = 0, l2 = field[i].length; j < l2; j++) < field[i][j] = new Square(); squareCount++; >> players = new Player[2]; started = false; activePlayer = null; filled = 0; > public void start() < resetPlayers(); started = true; >private void resetPlayers() < players[0] = new Player("X"); players[1] = new Player("O"); setCurrentActivePlayer(players[0]); >public Square[][] getField() < return field; >private void setCurrentActivePlayer(Player player) < activePlayer = player; >public boolean makeTurn(int x, int y) < if (field[x][y].isFilled()) < return false; >field[x][y].fill(getCurrentActivePlayer()); filled++; switchPlayers(); return true; > private void switchPlayers() < activePlayer = (activePlayer == players[0]) ? players[1] : players[0]; >public Player getCurrentActivePlayer() < return activePlayer; >public boolean isFieldFilled() < return squareCount == filled; >public void reset() < resetField(); resetPlayers(); >private void resetField() < for (int i = 0, l = field.length; i < l; i++) < for (int j = 0, l2 = field[i].length; j < l2; j++) < field[i][j].fill(null); >> filled = 0; > >
Определение победителя
К. О. подсказывает, что в крестики-нолики выирывает тот, кто выстроет X или O в линию длиной, равной длине поля по-вертикали, или по-горизонтали, или по-диагонали. Первая мысль, которая приходит в голову — это написать методы для каждого случая. Думаю, в этом случае хорошо подойдет паттерн Chain of Responsobility. Определим интерфейс
package com.example; public interface WinnerCheckerInterface
Так как Game наделен обязанностью выявлять победителя, он реализует этот интерфейс. Настало время создать виртуальных «лайнсменов», каждый из которых будет проверять свою сторону. Все они реализует интерфейс WinnerCheckerInterface.
WinnerCheckerHorizontal.java
package com.example; public class WinnerCheckerHorizontal implements WinnerCheckerInterface < private Game game; public WinnerCheckerHorizontal(Game game) < this.game = game; >public Player checkWinner() < Square[][] field = game.getField(); Player currPlayer; Player lastPlayer = null; for (int i = 0, len = field.length; i < len; i++) < lastPlayer = null; int successCounter = 1; for (int j = 0, len2 = field[i].length; j < len2; j++) < currPlayer = field[i][j].getPlayer(); if (currPlayer == lastPlayer && (currPlayer != null && lastPlayer !=null)) < successCounter++; if (successCounter == len2) < return currPlayer; >> lastPlayer = currPlayer; > > return null; > >
WinnerCheckerVertical.java
package com.example; public class WinnerCheckerVertical implements WinnerCheckerInterface < private Game game; public WinnerCheckerVertical (Game game) < this.game = game; >public Player checkWinner() < Square[][] field = game.getField(); Player currPlayer; Player lastPlayer = null; for (int i = 0, len = field.length; i < len; i++) < lastPlayer = null; int successCounter = 1; for (int j = 0, len2 = field[i].length; j < len2; j++) < currPlayer = field[j][i].getPlayer(); if (currPlayer == lastPlayer && (currPlayer != null && lastPlayer !=null)) < successCounter++; if (successCounter == len2) < return currPlayer; >> lastPlayer = currPlayer; > > return null; > >
WinnerCheckerDiagonalLeft.java
package com.example; public class WinnerCheckerDiagonalLeft implements WinnerCheckerInterface < private Game game; public WinnerCheckerDiagonalLeft(Game game) < this.game = game; >public Player checkWinner() < Square[][] field = game.getField(); Player currPlayer; Player lastPlayer = null; int successCounter = 1; for (int i = 0, len = field.length; i < len; i++) < currPlayer = field[i][i].getPlayer(); if (currPlayer != null) < if (lastPlayer == currPlayer) < successCounter++; if (successCounter == len) < return currPlayer; >> > lastPlayer = currPlayer; > return null; > >
WinnerCheckerDiagonalRight.java
package com.example; public class WinnerCheckerDiagonalRight implements WinnerCheckerInterface < private Game game; public WinnerCheckerDiagonalRight(Game game) < this.game = game; >public Player checkWinner() < Square[][] field = game.getField(); Player currPlayer; Player lastPlayer = null; int successCounter = 1; for (int i = 0, len = field.length; i < len; i++) < currPlayer = field[i][len - (i + 1)].getPlayer(); if (currPlayer != null) < if (lastPlayer == currPlayer) < successCounter++; if (successCounter == len) < return currPlayer; >> > lastPlayer = currPlayer; > return null; > >
//(. ) /** * "Судьи" =). После каждого хода они будут проверять, * нет ли победителя */ private WinnerCheckerInterface[] winnerCheckers; //(. ) public Game() < //(. ) winnerCheckers = new WinnerCheckerInterface[4]; winnerCheckers[0] = new WinnerCheckerHorizontal(this); winnerCheckers[1] = new WinnerCheckerVertical(this); winnerCheckers[2] = new WinnerCheckerDiagonalLeft(this); winnerCheckers[3] = new WinnerCheckerDiagonalRight(this); //(. ) >
public Player checkWinner() < for (WinnerCheckerInterface winChecker : winnerCheckers) < Player winner = winChecker.checkWinner(); if (winner != null) < return winner; >> return null; >
public void onClick(View view) < Button button = (Button) view; Game g = game; Player player = g.getCurrentActivePlayer(); if (makeTurn(x, y)) < button.setText(player.getName()); >Player winner = g.checkWinner(); if (winner != null) < gameOver(winner); >if (g.isFieldFilled()) < // в случае, если поле заполнено gameOver(); >>
private void gameOver(Player player) < CharSequence text = "Player \"" + player.getName() + "\" won!"; Toast.makeText(this, text, Toast.LENGTH_SHORT).show(); game.reset(); refresh(); >private void gameOver()
Для Java, gameOver(Player player) и gameOver() — разные методы. Воспользовавшись Builder’ом Toast.makeText, можно быстро создать и показать уведомление. refresh() обновляет состояние поля:
private void refresh() < Square[][] field = game.getField(); for (int i = 0, len = field.length; i < len; i++) < for (int j = 0, len2 = field[i].length; j < len2; j++) < if (field[i][j].getPlayer() == null) < buttons[i][j].setText(""); >else < buttons[i][j].setText(field[i][j].getPlayer().getName()); >> > >
Готово! Надеюсь, эта статья помогла Вам освоиться в мире разработки под OS Android. Благодарю за внимание!
Видео готового приложения
PS: статья была опубликована по просьбе комментаторов этого поста.
Создаем приложение для ANDROID быстро и просто
Сегодня я хотел бы поделиться с Вами, как быстро и просто можно создать приложение для Android с базовыми знаниями HTML CSS и JS. По данному примеру код на Java для Android будет минимальным. Благодаря платформе XAMARIN приложения для мобильных телефонов можно делать в Visual Studio.
▍Шаг 1 — Переходим на сайт и Скачиваем бесплатную версию Community.
▍Шаг 2 — Запускаем установку и выбираем параметры. Нас интересует XAMARIN. Но Вы также можете выбрать другие параметры.
После успешной установки мы можем создать свой первый проект.
▍Шаг 3 — Запускаем Visual Studio. Создать проект. В фильтре пишем xamarin, платформа Android, язык c# (Если желаете другой язык можете его выбрать)
▍Шаг 4 — Далее. Указываете имя для своего приложения, выбираете каталог где его сохранить. Создать.
▍Шаг 5 — Указываем пустое приложение и выбираем минимальную версию андроида для запуска этого приложения.
▍Шаг 6 — Жмем ок. Visual Studio автоматически создает код для приложения
Мы можем его запустить в эмуляторе, который идет комплекте с Visual Studio нажав клавишу F5.
▍Шаг 7 — Теперь немного модифицируем код. В данном случае мы вообще не будем использовать Java. Так как мы будем кодить на C#.
Приводим код к такому виду. Здесь мы создаем WebView контейнер который будет грузить локальный HTML файл, который находится в проекте в папке Assets.
public class MainActivity : AppCompatActivity < WebView mWebview; //это контейнер для просмотра HTML protected override void OnCreate(Bundle savedInstanceState) < base.OnCreate(savedInstanceState); Xamarin.Essentials.Platform.Init(this, savedInstanceState); mWebview = new WebView(this); mWebview.Settings.JavaScriptEnabled = true; //это разрешение работа JS скриптов mWebview.Settings.DomStorageEnabled = true; //это разрешение на запись в память браузера mWebview.Settings.BuiltInZoomControls = true; //это разрешение на масштабирование пальцами щипком mWebview.Settings.DisplayZoomControls = false; //это запрет вывода кнопок масштаба mWebview.Settings.CacheMode = CacheModes.NoCache; //это отключает либо включает кэширование данных mWebview.LoadUrl($"file:///android_asset/Content/login.html"); //это загрузка локального файла из папки Asset/Content SetContentView(mWebview); >public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) < Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults); base.OnRequestPermissionsResult(requestCode, permissions, grantResults); >>
▍Шаг 8 — Создадим там папку Content.
▍Шаг 9 — Добавим в папку Content файл login.html
▍Шаг 10 — Далее уже пишем на привычном нам HTML CSS JS. Можем нажать на F5 и увидеть результат нашей работы.
По такому принципу можно создать приложение быстро и просто. Файлы html будут выглядеть одинаково на всех устройствах. То есть, Вы можете сделать приложения для Android и iOS с одинаковым интерфейсом. Не надо изучать сложные языки разметки, не надо изучать сложные макеты (сториборды) на iOS. Все можно сделать на HTML.
В идеале, вместо локальных файлов можно сделать загрузку со стороннего сайта. В этом случае Вы можете менять контент приложения без его обновления в AppStore и Google Play.
Q: Но как быть с функциями самой платформы? Пуш сообщения? Как взаимодействовать с самой платформой?
Все очень просто! JavaScript можно использовать для вызова функций Android:
▍Шаг 1 — Немного модифицируем наш файл MainActivity
//добавляем интерфейс для javascript mWebview.AddJavascriptInterface(new JavaScriptInterface(), "interface"); //
▍Шаг 2 — Далее создаем класс JavaScriptInterface на который будет ругаться Visual Studio
public class JavaScriptInterface : Java.Lang.Object < [JavascriptInterface] [Export("alert")] //здесь мы указываем название функции вызываемой из html файла interface.alert('сообщение пользователю'); public void alert(string data) < Toast.MakeText(Application.Context, data, ToastLength.Short).Show();//здесь Андроид выведет сообщение посредством Toast >>
Мы видим, что теперь программа ругается на Export так как не знает что это такое.
▍Шаг 3 — Добавим нужную библиотеку
▍Шаг 4 — В фильтре напишем mono
▍Шаг 5 — Найдем Export и поставим галочку
▍Шаг 6 — Жмем ок и видим что ошибка пропала.
Так вы можете подключать библиотеки если вдруг Visual Studio ругается на что то.
Toast.MakeText(Application.Context, data, ToastLength.Short).Show();
Данная функция это показ всплывающей информации на экране. Она выполняется именно на платформе Андроида. То есть мы можем написать в HTML файле вызов функции Андроида. Получается полное дружелюбие двух платформ по JavaScript интерфейсу. Данные можно передавать туда сюда. Вызывать переход от одной активити в другую. Все через HTML + JavaScript.
Немного модифицируем файл login.htm:
h1 Привет мир
Теперь при нажатии на кнопку HTML вызывается функция Toast андроида и выводиться сообщение пользователю.
P.s. Полный листинг MainActivity
using Android.App; using Android.OS; using Android.Runtime; using Android.Webkit; using Android.Widget; using AndroidX.AppCompat.App; using Java.Interop; namespace MyFirstApp < [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)] public class MainActivity : AppCompatActivity < WebView mWebview; //это контейнер для просмотра HTML protected override void OnCreate(Bundle savedInstanceState) < base.OnCreate(savedInstanceState); Xamarin.Essentials.Platform.Init(this, savedInstanceState); mWebview = new WebView(this); mWebview.Settings.JavaScriptEnabled = true; //это разрешение работа JS скриптов mWebview.Settings.DomStorageEnabled = true; //это разрешение на запись в память браузера mWebview.Settings.BuiltInZoomControls = true; //это разрешение на масштабирование пальцами щипком mWebview.Settings.DisplayZoomControls = false; //это запрет вывода кнопок масштаба mWebview.Settings.CacheMode = CacheModes.NoCache; //это отключает либо включает кэширование данных //добавляем интерфейс для javascript mWebview.AddJavascriptInterface(new JavaScriptInterface(), "interface"); // mWebview.LoadUrl($"file:///android_asset/Content/login.html"); //это загрузка локального файла из папки Asset/Content SetContentView(mWebview); >public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) < Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults); base.OnRequestPermissionsResult(requestCode, permissions, grantResults); >> public class JavaScriptInterface : Java.Lang.Object < [JavascriptInterface] [Export("alert")] public void alert(string data) < Toast.MakeText(Application.Context, data, ToastLength.Short).Show(); >> >