Русский
Русский
English
Статистика
Реклама

Как написать простое Android ToDo-приложение на Java

Предисловие

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

Я расскажу вам как написать простенькое ToDo-приложение на Android с тремя активностями (рабочими экранами).

Ссылка на проект на Github будет в конце данной статьи.

Установка и первичная настройка

Для разработки приложения я рассмотрю использование бесплатной IDEIntellij от разработчиков JetBrains - Android Studio, у меня версия 4.1.1.

После успешной установки IDE и запуска нажимаем на самую первую кнопкуStart a new Android Studio Project. Далее появится мастер первичной подготовки проекта:

  • выберем подходящий шаблон, в моем случае это Empty Activity - он самый простой для новичков, так как при первом запуске будет всего 1 XML файл с версткой и один java файл MainActivity.

  • На следующем экране придумываем имя приложению; помните, что package name, после публикации на Google Play изменить нельзя (иначе Google Play посчитает это другим приложением (поправьте меня, если я ошибаюсь). Выбираем язык Java, так как по нему данная статья, а также, по нему больше информации в Интернете, чем по Kotlin.

  • Минимальный SDK выбираем под Android 5.0, так как данного API будет предостаточно для наших задач, заодно мы получим большой охват, в том числе старых устройств: планшеты, смартфоны, встроенные системы.

Скриншоты: установка и первичная настройка

Далее раскрываем вкладку Project и находим в каталоге Java><Ваш_Проект> файл MainActivity.java, в котором мы будем описывать все происходящее на главном экране.

Подготовка макетов (layouts) - внешний облик приложения

После рассмотрим файл MainActivity.xml, для этого нам нужно найти каталог res>layout>. Откроем MainActivity.xml для создания облика первой - главной страницы и перетягивая спанели Palette необходимые нам типы объектов.

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

Кстати, вместо px, тут используется другая величина - dp, позволяющая на разных экранах видеть одинаковый и желаемый результат.

Кстати, также, советую названия Текст полей переназначать в String значения, чтобы в дальнейшем было проще делать перевод интерфейса - подобный функционал уже встроен в Android Studio. Для этого нажимаем на объект, далее в меню Свойств объекта находим поле text и нажимаем на маленькую плашку-кнопку справа от текста. В открывшимся окне, нажимаем на плюсик слева сверху и создаем название String-переменной и ее значение по умолчанию:

Создание String-переменнойСоздание String-переменной

Для перевода интерфейса, необходимо сохранить изменения и над нашим конструктором Layout нажать на кнопку Default (en-us) и выбрать Edit Translations, далее найти слева сверху значок глобуса и нажать на него для добавления нового языка:

Переводы для интерфейсовПереводы для интерфейсов

Таким образом создадим дополнительные макеты (layouts) для оставшихся двух окон:

Скриншоты: еще два макета
Макет Activity_Settings.xmlМакет Activity_Settings.xmlМакет Activity_Advanced.xmlМакет Activity_Advanced.xml

Программируем на Java под Android

Еще раз повторюсь, что это Tutorial больше для новичков; дальше я буду комментировать практически каждую строчку. Ссылка на проект на Github будет в конце данной статьи.

Открываем файл Main_Activity.java, который будет отвечать за логику наших переключателей и главного экрана в целом, а она такова:

  • В самом верху должен отображаться пользовательский заголовок, если он настроен.

  • На переключателях должен отображаться тот текст, который пользователь настраивает из окна с макетом Activity_Settings.xml

  • Количество переключателей должно соответствовать заданному числу из окна макета Activity_Advanced.xml

  • После выхода из приложения и повторного запуска все переключатели должны оставаться в том же положении, в котором пользователь их оставил

  • Сброс переключателей возможен только, если переключатель Уверен/-а? включен.

  • А также, должны работать оставшиеся кнопки меню.


    Пишем следующее:

Код под спойлером: 156 строчек
package com.bb.myapplication;import androidx.appcompat.app.AppCompatActivity;import androidx.appcompat.widget.SwitchCompat;import android.content.Intent;import android.content.SharedPreferences;import android.graphics.Color;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends AppCompatActivity {    //Создаем 9 переключателей с помощью массива    SwitchCompat[] switcharray = new SwitchCompat[9];    Boolean Reset; //Булев для выключения переключателя    Button NextButton;    public int[] list_of_switches = {            R.id.switch_compat1,            R.id.switch_compat2,            R.id.switch_compat3,            R.id.switch_compat4,            R.id.switch_compat5,            R.id.switch_compat6,            R.id.switch_compat7,              R.id.switch_compat8,             R.id.switch_compat10, //переключатель "Вы уверены?" //8    };    //Нажатие кнопки Сброс    public void ResetButtonClick (View view) throws IllegalAccessException {        Reset =false;        if (switcharray[8].isChecked()) { //Если переключатель "Вы уверены?" нажат, то разрешаем переключить в false остальные переключатели            SharedPreferences.Editor editor = getSharedPreferences("save"                    ,MODE_PRIVATE).edit();            //Сохраняем в Intent значения всех переключателей в False            for (int k=0; k<10; k++) {                editor.putBoolean("value"+k, false);            }            editor.apply();            //Устанавливаем все переключатели в значение False            for (int i=0;i<9;i++){                switcharray[i].setChecked(false);            }            //Reset background color of checked SwitchCompats            for (int i = 0; i < 9; i++) {                findViewById(list_of_switches[i]).setBackgroundColor(Color.TRANSPARENT);            }        }    }    //Создание формы / открытие приложения    @Override    protected void onCreate(final Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //Назначаем полям значения по умолчанию и сохраняем их в Intent        String[] tsfield = new String[8];        SharedPreferences prefs = getSharedPreferences("MY_DATA", MODE_PRIVATE);        tsfield[0] = prefs.getString("KEY_F0", "Выключил газ");        tsfield[1] = prefs.getString("KEY_F1", "Выключил воду");        tsfield[2] = prefs.getString("KEY_F2", "Покормил кошек");        tsfield[3] = prefs.getString("KEY_F3", "Закрыл окна");        tsfield[4] = prefs.getString("KEY_F4", "Выключил Интернет");        tsfield[5] = prefs.getString("KEY_F5", "Закрыл дверь");        tsfield[6] = prefs.getString("KEY_F6", "Выключил везде свет");        tsfield[7] = prefs.getString("KEY_F7", "Вынес мусор");        //Получаем настройки текста заголовка        String hellotext = prefs.getString("hellotitletext", "");        switcharray[6] = findViewById(list_of_switches[6]);        switcharray[7] = findViewById(list_of_switches[7]);        //Получаем настройки количества полей        String sixfields = prefs.getString("sixfields", "true");        String sevenfields = prefs.getString("sevenfields", "false");        String eightfields = prefs.getString("eightfields", "false");        if (sixfields.equals("true")){            switcharray[6].setVisibility(View.GONE);            switcharray[7].setVisibility(View.GONE);        }        else if (sevenfields.equals("true")) {            switcharray[6].setVisibility(View.VISIBLE);            switcharray[7].setVisibility(View.GONE);        }        else if (eightfields.equals("true")) {            switcharray[6].setVisibility(View.VISIBLE);            switcharray[7].setVisibility(View.VISIBLE);        }        //Создаем массив из TextView        TextView[] textarr = new TextView[8];        //Каждому переключателю назначаем текст из итерации поля tsfield        for (int i=0; i<8;i++){            textarr[i] = (TextView) findViewById(list_of_switches[i]);            textarr[i].setText(tsfield[i]);        }        //Назначаем текст заголовка            TextView textView5 = (TextView) findViewById(R.id.textView5);            textView5.setText(hellotext);        //Отображать заголовок, если соотв. поле заполнено        if(!hellotext.matches(""))        {            textView5.setVisibility(View.VISIBLE);        }        //Создаем связь каждого элемента переключателя по id из XML с соответствующей переменной типа SwitchCompat        for (int i=0;i<9;i++) {            switcharray[i] = findViewById(list_of_switches[i]);        }        //Создаем связь кнопки по id bt_next из xml переменной NextButton        NextButton = findViewById(R.id.bt_next);        //Используем SharedPreferences = "save"        SharedPreferences sharedPreferences = getSharedPreferences("save"                , MODE_PRIVATE);        //При первом запуске - все переключатели в False        for (int k=0; k<9; k++) {        switcharray[k].setChecked(sharedPreferences.getBoolean("value"+k, false));        }        //При переключении переключателей сохраняем данные, а также, проверяем их при повторном запуске        for (int k=0; k<9; k++) {            final int finalK = k;            switcharray[k].setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View view) {                    if (switcharray[finalK].isChecked()) {                        //когда переключатель включен                        Reset = true; //                        switcharray[finalK].setBackgroundColor(Color.parseColor("#c8a2c6"));                        SharedPreferences.Editor editor = getSharedPreferences("save"                                , MODE_PRIVATE).edit();                        editor.putBoolean("value" + finalK, true);                        editor.apply();                        switcharray[finalK].setChecked(true);                    } else {                        //когда переключатель выключен                        SharedPreferences.Editor editor = getSharedPreferences("save"                                , MODE_PRIVATE).edit();                        editor.putBoolean("value" + finalK, false);                        Reset = false;                        switcharray[finalK].setBackgroundColor(Color.TRANSPARENT);                        editor.apply();                        switcharray[finalK].setChecked(false);                    }                }            });        }        //Кнопка открытия страницы настроек        NextButton.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                //Go to next activity                Intent intent2 = new Intent(MainActivity.this, Activity_settings.class);                startActivity(intent2);            }        });    }}

Следующим этапом будет написание кода для корректной работы макета Activity_Settings.XML, а логика его такова:

  • Введенные пользователь записи сохраняются даже после перезапуска приложения

  • Количество полей соответствуют числу, заданному в настройках из макета Activity_Advanced.xml

  • А также, должны работать оставшиеся кнопки меню.

Код по спойлером: 124 строчки
package com.bb.myapplication;import androidx.appcompat.app.AppCompatActivity;import android.content.Intent;import android.content.SharedPreferences;import android.net.Uri;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;public class Activity_settings extends AppCompatActivity {    //Initialize Variable    Button btBack;    Button fcSubmit;    Button btAdvanced;    //Ассоциируем поля ввода с переменными с помощью массива    EditText[] InputFields = new EditText[8];    //Назначаем полям значения по умолчанию и сохраняем их в Intent    String[] tsfield = new String[8];    //Создаем массив элементов из XML по id    public int[] list_of_fields = {            R.id.inputField0,            R.id.inputField1,            R.id.inputField2,            R.id.inputField3,            R.id.inputField4,            R.id.inputField5,            R.id.inputField6,            R.id.inputField7,    };    private SharedPreferences prefs;    //Создание формы / открытие приложения    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_settings);        //Назначаем полям значения по умолчанию и сохраняем их в Intent        prefs = getSharedPreferences("MY_DATA", MODE_PRIVATE);        //Получаем данные по количеству используемых полей        String sixfields = prefs.getString("sixfields", "true");        String sevenfields = prefs.getString("sevenfields", "false");        String eightfields = prefs.getString("eightfields", "false");        EditText inputField71var = (EditText) findViewById(list_of_fields[6]);        EditText inputField81var = (EditText) findViewById(list_of_fields[7]);        if (sixfields.equals("true")){            inputField71var.setVisibility(View.INVISIBLE);            inputField81var.setVisibility(View.INVISIBLE);        }        else if (sevenfields.equals("true")) {            inputField71var.setVisibility(View.VISIBLE);            inputField81var.setVisibility(View.INVISIBLE);        }        else if (eightfields.equals("true")) {            inputField71var.setVisibility(View.VISIBLE);            inputField81var.setVisibility(View.VISIBLE);        }        tsfield[0] = prefs.getString("KEY_F0", "Выключил газ");        tsfield[1] = prefs.getString("KEY_F1", "Выключил воду");        tsfield[2] = prefs.getString("KEY_F2", "Покормил кошек");        tsfield[3] = prefs.getString("KEY_F3", "Закрыл окна");        tsfield[4] = prefs.getString("KEY_F4", "Выключил Интернет");        tsfield[5] = prefs.getString("KEY_F5", "Закрыл дверь");        tsfield[6] = prefs.getString("KEY_F6", "Выключил везде свет");        tsfield[7] = prefs.getString("KEY_F7", "Вынес мусор");        //Назначаем полям ввода текст из SharedPreferences        for (int i=0; i<8; i++) {            InputFields[i] = (EditText) findViewById(list_of_fields[i]);            InputFields[i].setText(tsfield[i]);        }        //Создаем переменные для кнопок        btBack = findViewById(R.id.bt_back);        fcSubmit = findViewById(R.id.submit_fc);        btAdvanced = findViewById(R.id.btAdvanced);        //Кнопка Назад        btBack.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                //Go back                Intent intent = new Intent (                        Activity_settings.this,MainActivity.class                );                startActivity(intent);            }        });        //Кнопка Расширенные настройки/Дополнительно        btAdvanced.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                //Open Advanced Settings                Intent intent = new Intent (                        Activity_settings.this,Activity_advanced.class                );                startActivity(intent);            }        });    }    //Ссылка-значок на внешний ресурс - ссылка на мой телеграм    public void tglink(View view){        Intent myWebLink = new Intent(android.content.Intent.ACTION_VIEW);        myWebLink.setData(Uri.parse("https://t.me/EndlessNights"));            startActivity(myWebLink);    }    //Кнопка Сохранить данные    public void SaveData(View view)    {        for (int i=0; i<8;i++) {            tsfield[i] = InputFields[i].getText().toString();        SharedPreferences.Editor editor = prefs.edit();        editor.putString("KEY_F"+i, tsfield[i]);            editor.apply();        }        // Открываем главную страницу        startActivity(new Intent(getApplicationContext(), MainActivity.class));    }}

И наконец опишем логику работы последнего окна в приложении - с Дополнительными настройками:

  • Количество полей для отображения - в данном случае выбор с помощью радиокнопок - 6, 7 или 8 полей.

  • Текстовый заголовок, который пользователь может ввести и который будет отображаться на главной странице/активности.

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

  • И наконец должны работать оставшиеся кнопки меню.

Код под спойлером: 134 строчки
package com.bb.myapplication;import androidx.appcompat.app.AppCompatActivity;import android.content.Intent;import android.content.SharedPreferences;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.CompoundButton;import android.widget.EditText;import android.widget.RadioButton;import android.widget.RadioGroup;import android.widget.Switch;import android.widget.TextView;public class Activity_advanced extends AppCompatActivity {    Button btBack;    //Назначаем радиокнопкам значения по умолчанию    Boolean sixbool = true;    Boolean sevenbool = false;    Boolean eightbool = false;    private SharedPreferences prefsadv;    //Поле ввода текста для заголовка    private EditText hellotitletext;    RadioGroup rdGroup;    //Переменные для радиокнопок    public RadioButton r1, r2, r3;    //Переменные для передачи состояния из boolean в sharedPrefs    String sixdata;    String sevendata;    String eightdata;    Switch bgswitchvar;    private SharedPreferences prefs;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_advanced);        bgswitchvar = findViewById(R.id.bgswitch);        prefsadv = getSharedPreferences("MY_DATA", MODE_PRIVATE);        rdGroup = (RadioGroup)findViewById(R.id.radioGroup);        //Поле заголовка        String hellotitletext1 = prefsadv.getString("hellotitletext","");        hellotitletext = (EditText) findViewById(R.id.hellotitletext);        hellotitletext.setText(hellotitletext1);        //Ассоциируем переменные с полями по id из xml        r1 = findViewById(R.id.sixfields);        r2 = findViewById(R.id.sevenfields);        r3 = findViewById(R.id.eightfields);        //При нажатии на радиокнопку, вызываем функцию Update с заданным ключом        r1.setChecked(Update("rbsix"));        r2.setChecked(Update("rbseven"));        r3.setChecked(Update("rbeight"));        //При нажатии первой кнопки добавляем True с ключом rbsix в RBDATA        r1.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {            @Override            public void onCheckedChanged(CompoundButton compoundButton, boolean r1_isChecked) {                SaveIntoSharedPrefs("rbsix", r1_isChecked);            }        });        //При нажатии второй кнопки добавляем True с ключом rbsix в RBDATA        r2.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {            @Override            public void onCheckedChanged(CompoundButton compoundButton, boolean r2_isChecked) {                SaveIntoSharedPrefs("rbseven", r2_isChecked);            }        });        //При нажатии третьей кнопки добавляем True с ключом rbsix в RBDATA        r3.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {            @Override            public void onCheckedChanged(CompoundButton compoundButton, boolean r3_isChecked) {                SaveIntoSharedPrefs("rbeight", r3_isChecked);            }        });        //Back button        btBack = findViewById(R.id.btBackadvanced);        btBack.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                //Go back                Intent intent = new Intent (                        Activity_advanced.this,Activity_settings.class                );                startActivity(intent);            }        });    }    //Сохранение данных в SharedPreferences - ожидая ключ и значение булева типа    private void SaveIntoSharedPrefs(String key, boolean value){        SharedPreferences sp = getSharedPreferences("RBDATA",MODE_PRIVATE);        SharedPreferences.Editor editor = sp.edit();        editor.putBoolean(key,value);        editor.apply();    }    //Функция обновления значения в SharedPreferences    private boolean Update(String key){        SharedPreferences sp = getSharedPreferences("RBDATA",MODE_PRIVATE);        return sp.getBoolean(key, false);    }    //Сохраняем данные по количеству полей    public void SaveDataAdvanced(View view)    {        int checkedId = rdGroup.getCheckedRadioButtonId();        if(checkedId == R.id.sixfields) {            sixbool = true;            sevenbool = Boolean.FALSE;            eightbool = Boolean.FALSE;        }        else if (checkedId == R.id.sevenfields){            sevenbool = true;            sixbool = Boolean.FALSE;            eightbool = Boolean.FALSE;        }        else if (checkedId == R.id.eightfields){            eightbool = true;            sevenbool = Boolean.FALSE;            sixbool = Boolean.FALSE;        }        sixdata = String.valueOf(sixbool);        sevendata = String.valueOf(sevenbool);        eightdata = String.valueOf(eightbool);        String hellofield = hellotitletext.getText().toString();        SharedPreferences.Editor editor = prefsadv.edit();        editor.putString("sixfields", sixdata);        editor.putString("sevenfields", sevendata);        editor.putString("eightfields", eightdata);        editor.putString("hellotitletext", hellofield);        editor.apply();        startActivity(new Intent(getApplicationContext(), MainActivity.class));    }}

Подготовка приложения к публикации

Для отладки и проверки работоспособности приложения советую вам использовать настоящее устройство на Android, так вы сразу сможете отследить наличие, как минимум проблем с оформлением.

Здесь я приложил видео-инструкцию, как подключить свой смартфон к Android studio для отладки вашего приложения. На видео вы можете заметить первую версию данного приложения с очень плохим кодом:

Регистрация в Google Play

Для публикации приложения нам следует создать специальный аккаунт разработчика, вот прямая ссылка.

Далее вам предстоит оплатить пошлину в $35 за возможность публиковать приложения, это почти в 3 раза дешевле, чем в Steam, при том, что Steam просит $100 за каждое публикуемое приложение/игру, даже бесплатное, а с аккаунтом разработка, в Google Play вы можете публиковать несчётное множество приложений.

После оплаты и успешной авторизации в консоли разработчика, необходимо нажать на синюю кнопку "Создать приложение", далее заполнить все необходимые поля:

После создания приложения в консоли разработчика Google Play, необходимо перейти в раздел Рабочая версия и нажать на кнопку Создать новый выпуск. Вам предложат получить электронную подпись для вашего приложения с расширением *.jks, с помощью которой вам предстоит подписать свое первое приложение, а также, все дальнейшие выпуски с обновлениями.

Возвращаемся в Android Studio и необходимо заполнить немного информации о нашем приложении, для этого нажимаем File>Project Structure и заполняем поля Version Code и Version Name - без них Google Play Google Play не допустит ваше приложение до публикации:

Наконец, переходим в следующий раздел: пункт меню Build>Generate Signed Bundle / APK

В открывшимся окне выбираем APK. В подразделе Key Store Path выбираем Create new, далее заполняем все поля (прямая ссылка на официальную инструкцию), далее данный ключ потребуется загрузить в консоль Google Play. Затем вернемся в Android Studio и после ввода всех необходимых данных, нажимаем Next

В следующем окне отмечаем все чекбоксы, выбираем release и нажимаем Finish - Android Studio скомпилирует подписанное приложение, которое можно опубликовать в Google Play.

Итог

После загрузки файла приложения APK потребуется заполнить множество форм и подготовить множество материалов: описание на разных языках (если необходимо), изображения на разных языках (надписи на изображениях я имею в виду), логотипы, иконки разных размеров, скриншоты со смартфона и планшета.

Наконец отправляем приложение в публикацию. Сотрудники Google Play будут проверять ваше приложение в течении 2 недель, судя по официальным данным. Данное приложение рассматривали в течении 5 суток. Также, стоит учесть, что каждое обновление, также, будут проверять, но на обновления уходит не более 2-3 суток.

Ссылка на GitHub, как обещано. Ссылка на приложение в Google Play.

Источник: habr.com
К списку статей
Опубликовано: 15.03.2021 22:22:10
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Программирование

Java

Разработка мобильных приложений

Разработка под android

Android

Android studio

Google

Google play

Разработка

Разработка приложений

Андроид

Андроид для начинающий

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru