Создаём развивающее приложение при помощи Android Studio

Введение


Добрый день, господа! Я ученик 11 класса и разработкой занимаюсь только от того, что не хочу готовиться к ЕГЭ. Идею приложения можно описать тремя словами — увидел, запомнил, повторил. Перед игроком появляется квадратное поле с определённым число закрашенных элементов. Через некоторое время поле очищается. Надо выбрать какие элементы были закрашены. По мере прохождения уровней игры поле становится всё больше и запоминать приходится больше.

Теория


Для начала нам нужно определиться с архитектурой программного интерфейса. Я предпочитаю MVC (Model — View — Controller).

image

При таком подходе контроллер перехватывает событие извне и в соответствии с заложенной в него логикой, реагирует на это событие изменяя модель, посредством вызова соответствующего метода. После изменения модель использует событие о том что она изменилась, и все подписанные на это события представления, получив его, обращаются к Модели за обновленными данными, после чего их и отображают. Так, определились. Дальше нужно выбрать язык программирования. Мне нравиться Java. Java — это объектно ориентированный язык программирования, поэтому нам следует придерживаться принципов ООП:

1) Инкапсуляция — это компонент позволяющий объединить код и данный которыми он манипулирует.
2) Полиморфизм — свойство системы, позволяющее использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.
3) Наследование — свойство системы, позволяющее описать новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью. Класс, от которого производится наследование, называется базовым или родительским классом. Новый класс — потомком, наследником, дочерним или производным классом.

Теперь ограничим предметную область такими классами как:

Игра (Game), Игрок (Player), Поле (Field). Игра отвечает за данные формируемые непосредственно в игре. Поле хранит информацию о том, какие ячейки закрашены, а какие нет. Игра умеет создавать поле. Игрок умеет обрабатывать пользовательские данные, т.е. сохранять очки и рейтинг, когда это необходимо.

Основная часть


Хватит теории. Нам нужно выполнить следующую последовательность действий:

1) Скачать среду разработки и JDK.
2) Установить оба комплекта себе на компьютер.
3) Запустить среду разработки.

Создадим пустой проект и начнём творить. Определим структура пакетов таким образом:


Дальше мы проделываем небольшой трюк. Создаём файлы разметки:


Xml файл root отвечает за корень нашего представления. Внутри него будут динамически меняться разметки фрагментов, которые мы уже создали. Соответственно в пакете представления (view) создадим классы фрагменты:


Каждый фрагмент отвечает за один режим игры. При открытии приложения выполняется метод onCreate класса MainActivity(который мы тоже положили в пакет view). Изменим это метод до такого состояния:


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

Фрагменты созданы, но они ничего не делают. Самое время придать вдунуть в них жизнь. Создадим интерфейс:

Интерфейс
public interface SendingActivity {
    //Сообщает активности, какой пункт меню выбран
    void send(int a);
    //Сообщает активности, какой какое поле и какой уровень нужно запустить
    void send(Field field, int level);
    //Сообщает активности, что нужно запустить следующий уровень, соответствующего режима игры
    void nextLevel(GameModChallenge game);
    //Сообщает активности, что нужно запустить следующий уровень, соответствующего режима игры
    void nextLevel(GameModSprint game);
    //Сообщает активности, что нужно запустить следующий уровень, соответствующего режима игры
    void nextLevel(GameModTwisting game);
    //Сообщает активности, что нужно запустить следующий уровень, соответствующего режима игры
    void nextLevel(GameModEvidence game);
    //В данном режиме игры не при первом корректном вводе будет запускаться следующий уровень, поэтому нужен другой метод
    void next(GameModChallenge game);
    //Сообщает активности, что пользователь ошибся
    void failSprint(GameModSprint game);
    //Сообщает активности, что пользователь ошибся
    void failTwisting(GameModTwisting game);
    //Сообщает активности, что пользователь ошибся
    void failEvidence(GameModEvidence game);
    //Сообщает активности, что нужно обновить пользовательские данные
    void releaseToolbar();
}


Через методы данного интерфейса объекты будут сообщать о своих действиях активности, а она уже будет решать, что ей делать.

Создадим фрагмент главного меню:

Главное меню
public final class MainMenuFragment extends Fragment implements View.OnClickListener {
    Button sprint, challenge, twisting,evidence, exit;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.menu, container, false);
        //region findViewById
        sprint = (Button) view.findViewById(R.id.sprint);
        challenge = (Button) view.findViewById(R.id.challenge);
        twisting = (Button) view.findViewById(R.id.twist);
        evidence = (Button) view.findViewById(R.id.evidence);
        exit = (Button) view.findViewById(R.id.exit);
        //endregion
        //region setOnClickListener
        sprint.setOnClickListener(this);
        challenge.setOnClickListener(this);
        twisting.setOnClickListener(this);
        evidence.setOnClickListener(this);
        exit.setOnClickListener(this);
        //endregion
        return view;
    }

    @Override
    public void onClick(View view) {
        int id = view.getId();
        switch (id) {
            case R.id.sprint: {
                SendingActivity mess = (SendingActivity) getActivity();
                mess.send(0);
                break;
            }
            case R.id.challenge: {
                SendingActivity mess = (SendingActivity) getActivity();
                mess.send(1);
                break;
            }
            case R.id.twist: {
                SendingActivity mess = (SendingActivity) getActivity();
                mess.send(2);
                break;
            }
            case R.id.evidence: {
                SendingActivity mess = (SendingActivity) getActivity();
                mess.send(3);
                break;
            }
            case R.id.exit: {
                SendingActivity mess = (SendingActivity) getActivity();
                mess.send(4);
                break;
            }
        }
    }
}


Тут я создал нашёл кнопки и повесил на них слушателя событий. В слушателе выбирается какая кнопка была нажата. В зависимости от этого объекту интерфейса посылается нужное сообщение. Но наша активность ещё не реагирует на посылаемые фрагментом сообщения. Исправим это позже, а сейчас напишем логику нашей игры. Наша логика содержится в пакете model. Там создадим вспомогательный класс Field:

Field
public final class Field {
    private  int rightAnswer;
    private int sizeField;
    private int paintedSquare;
    private int square[][];
    private Random randX;
    private Random randY;

    public int[][] getSquare() {
        return square;
    }

    public Field createSquare(int sizeField, int paintedSquare) {
        this.sizeField = sizeField;
        this.paintedSquare = paintedSquare;
        this.square = new int[sizeField][sizeField];
        randX = new Random(System.nanoTime());
        randY = new Random(System.nanoTime());
        for (int i = 0; i < sizeField; i++) {
            for (int j = 0; j < sizeField; j++) {
                if (paintedSquare > 0) {
                    paintedSquare--;
                    this.square[i][j] = 1;
                } else {
                    this.square[i][j] = 0;
                }
            }
        }
        for (int i = 0; i < sizeField; i++) {
            for (int j = 0; j < sizeField; j++) {
                mixing(square);
            }
        }
        return this;
    }
   
    //region mixing
    private void mixing(int[][] arr) {         //Метод,который перемешивает массив
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                swap(arr, i, j, randX.nextInt(sizeField), randY.nextInt(sizeField));
            }
        }
    }

    private void swap(int[][] arr, int i, int j, int newI, int newJ) {
        int tmp = arr[i][j];
        arr[i][j] = arr[newI][newJ];
        arr[newI][newJ] = tmp;
    }
    //endregion

    public int getSizeField() {
        return sizeField;
    }

    public int getRightAnswer() {
        return rightAnswer;
    }

    public void setRightAnswer(int rightAnswer) {
        this.rightAnswer = rightAnswer;
    }

    public int getPaintedSquare() {
        return paintedSquare;
    }
}


Как вы видите этот класс нам нужен для работы с массивом правильных ответов. Если элемент массива содержит 1, то клетка закрашена. Поле умеет себя создавать. После создания оно себя сразу перемешивает. Теперь создадим базовый класс Game для режимов игры.

Game
public abstract class Game {
    private Field field;
    private int currentLevel;
    private Activity a;


    Game(Activity a) {
        this.a = a;
    }

    Activity getActivity() {
        return a;
    }


    public int getCurrentLevel() {
        return currentLevel;
    }

    public void setCurrentLevel(int currentLevel) {
        this.currentLevel = currentLevel;
    }

    public void setField(Field field) {
        this.field = field;
    }

    public Field getField() {
        return field;
    }

    public Field createFieldInLevel(int level) {
        switch (level) {
            case 1: {
                field = new Field().createSquare(3, 2);
                break;
            }
            case 2: {
                field = new Field().createSquare(3, 3);
                break;
            }
            case 3: {
                field = new Field().createSquare(4, 4);
                break;
            }
            case 4: {
                field = new Field().createSquare(4, 6);
                break;
            }
            case 5: {
                field = new Field().createSquare(4, 8);
                break;
            }
            case 6: {
                field = new Field().createSquare(5, 10);
                break;
            }
            case 7: {
                field = new Field().createSquare(5, 12);
                break;
            }
            case 8: {
                field = new Field().createSquare(6, 14);
                break;
            }
            case 9: {

                field = new Field().createSquare(6, 16);
                break;
            }
            case 10: {

                field = new Field().createSquare(7, 18);
                break;
            }
            case 11: {

                field = new Field().createSquare(7, 20);
                break;
            }
            case 12: {
                field = new Field().createSquare(7, 22);
                break;
            }
            case 13: {

                field = new Field().createSquare(7, 24);
                break;
            }
            case 14: {

                field = new Field().createSquare(8, 26);
                break;
            }
            case 15: {

                field = new Field().createSquare(8, 28);
                break;
            }
            case 16: {

                field = new Field().createSquare(8, 30);
                break;
            }
            case 17: {

                field = new Field().createSquare(8, 32);
                break;
            }
            case 18: {
                field = new Field().createSquare(9, 34);
                break;
            }
            case 19: {

                field = new Field().createSquare(9, 36);
                break;
            }
            case 20: {
                field = new Field().createSquare(9, 40);
                break;
            }
            case 21: {
                field = new Field().createSquare(10, 42);
                break;
            }
            case 22: {

                field = new Field().createSquare(10, 44);
                break;
            }
            case 23: {
                field = new Field().createSquare(10, 46);
                break;
            }
            case 24: {
                field = new Field().createSquare(10, 48);
                break;
            }
            case 25: {
                field = new Field().createSquare(10, 50);
                break;
            }
        }
        return field;
    }

    public abstract void createVictoryDialog(LayoutInflater inflater);

    public abstract void createLooseDialog(LayoutInflater inflater);

    public abstract void showDialog();
}


Данный класс определяет базовую функциональность режимов игры. Он умеет создавать поле, устанавливать текущий уровень игры, создавать диалоговые окна (именно для этого ему и нужна ссылка на активность) и показывать их. Объекты данного класса мы создавать не можем, поэтому создадим классы наследники. К примеру класс GameModSprint:

GameModSprint
private AlertDialog.Builder builder;
    private AlertDialog alert;

    public GameModSprint(Activity a) {
        super(a);
    }

    @Override
    public void createVictoryDialog(LayoutInflater inflater) {
        builder = new AlertDialog.Builder(getActivity());
        builder.setCancelable(false);
        View dialogV = inflater.inflate(R.layout.dialog_view, null, false);
        View dialogTitle = inflater.inflate(R.layout.dialog_title, null, false);
        TextView title = (TextView) dialogTitle.findViewById(R.id.dialog_title);
        TextView message = (TextView) dialogTitle.findViewById(R.id.message);
        message.setText(getActivity().getResources().getString(R.string.progress)+ getCurrentLevel());
        title.setText(getActivity().getResources().getString(R.string.good));
        title.setText(getActivity().getResources().getString(R.string.good));
        ImageView imageView = (ImageView) dialogV.findViewById(R.id.image);
        imageView.setImageResource(R.drawable.victory);
        ImageButton next = (ImageButton) dialogV.findViewById(R.id.next);
        ImageButton restart = (ImageButton) dialogV.findViewById(R.id.restart);
        View.OnClickListener listener = new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                switch (view.getId()) {
                    case R.id.next: {
                        SendingActivity send = (SendingActivity) getActivity();
                        send.nextLevel(self());
                        alert.dismiss();
                        break;
                    }
                    case R.id.restart: {
                        SendingActivity send = (SendingActivity) getActivity();
                        send.failSprint(self());
                        alert.dismiss();
                        break;
                    }
                    case R.id.in_menu: {
                        alert.dismiss();
                        getActivity().onBackPressed();
                        break;
                    }
                }
            }
        };
        next.setOnClickListener(listener);
        restart.setOnClickListener(listener);
        ImageButton inMenu = (ImageButton) dialogV.findViewById(R.id.in_menu);
        inMenu.setOnClickListener(listener);
        builder.setView(dialogV);
        builder.setCustomTitle(dialogTitle);
    }

    @Override
    public void createLooseDialog(LayoutInflater inflater) {
        builder = new AlertDialog.Builder(getActivity());
        builder.setCancelable(false);
        View dialogV = inflater.inflate(R.layout.dialog_view, null, false);
        View dialogTitle = inflater.inflate(R.layout.dialog_title, null, false);
        TextView title = (TextView) dialogTitle.findViewById(R.id.dialog_title);
        TextView message = (TextView) dialogTitle.findViewById(R.id.message);
        message.setText(getActivity().getResources().getString(R.string.not_progress)+ getCurrentLevel());
        title.setText(getActivity().getResources().getString(R.string.bad));
        ImageView imageView = (ImageView) dialogV.findViewById(R.id.image);
        imageView.setImageResource(R.drawable.loose);
        ImageButton next = (ImageButton) dialogV.findViewById(R.id.next);
        ImageButton restart = (ImageButton) dialogV.findViewById(R.id.restart);
        next.setBackgroundColor(getActivity().getResources().getColor(R.color.background_no_access));
        View.OnClickListener listener = new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                switch (view.getId()) {
                    case R.id.restart: {
                        SendingActivity send = (SendingActivity) getActivity();
                        send.failSprint(self());
                        alert.dismiss();
                        break;
                    }
                    case R.id.in_menu: {
                        alert.dismiss();
                        getActivity().onBackPressed();
                        break;
                    }
                }
            }
        };
        next.setOnClickListener(listener);
        restart.setOnClickListener(listener);
        ImageButton inMenu = (ImageButton) dialogV.findViewById(R.id.in_menu);
        inMenu.setOnClickListener(listener);
        builder.setView(dialogV);
        builder.setCustomTitle(dialogTitle);
    }

    @Override
    public void showDialog() {
        alert = builder.create();
        alert.show();
    }

    private GameModSprint self() {
        return this;
    }
}


Этот класс определяет каким образом будут появляться диалоговые окна. Дело в том, что не всегда мне нужно просто перенести игрока на следующий уровень. К примеру в других режимах игры я захочу сделать прибавление рейтинга игроку или ещё какие-либо манипуляции с пользовательскими данными которые нужно выполнять только в данном режиме игры. Остальные классы создаются аналогичным образом.

И наконец мы можем реализовать наш интерфейс.

Реализация интерфейса
<img src="" alt=«image»/>
@Override
    public void send(int a) {
        switch (a) {
            case 0: {
                sprintFragment = new SprintFragment();
                game = new GameModSprint(this);
                game.createFieldInLevel(1);
                game.setCurrentLevel(1);
                sprintFragment.setGame(game);
                getFragmentManager().beginTransaction().replace(R.id.activity_main, sprintFragment).addToBackStack(null).commit();
                break;
            }
            case 1: {
                getFragmentManager().beginTransaction().replace(R.id.activity_main, levelChallengeFragment).addToBackStack(null).commit();
                break;
            }
            case 2: {
                twistingFragment = new TwistingFragment();
                game = new GameModTwisting(this);
                game.createFieldInLevel(1);
                game.setCurrentLevel(1);
                twistingFragment.setGame(game);
                getFragmentManager().beginTransaction().replace(R.id.activity_main, twistingFragment).addToBackStack(null).commit();
                break;
            }
            case 3: {
                evidenceFragment = new EvidenceFragment();
                game = new GameModEvidence(this);
                game.createFieldInLevel(1);
                game.setCurrentLevel(1);
                evidenceFragment.setGame(game);
                getFragmentManager().beginTransaction().replace(R.id.activity_main, evidenceFragment).addToBackStack(null).commit();
                break;
            }
            case 4: {
                finish();
                break;
            }
        }
    }
    @Override
    public void send(Field field, int level) {
        challengeFragment = new ChallengeFragment();
        game = new GameModChallenge(this);
        game.setField(field);
        game.setCurrentLevel(level);
        challengeFragment.setGame(game);
        getFragmentManager().beginTransaction().replace(R.id.activity_main, challengeFragment).commit();
    }
    @Override
    public void nextLevel(GameModChallenge game) {
        challengeFragment = new ChallengeFragment();
        game.setCurrentLevel(game.getCurrentLevel() + 1);
        game.createFieldInLevel(game.getCurrentLevel());
        textPoint.setText(String.valueOf(point));
        textRating.setText(String.valueOf(rating));
        challengeFragment.setGame(game);
        getFragmentManager().beginTransaction().replace(R.id.activity_main, challengeFragment).commit();
    }
    @Override
    public void nextLevel(GameModSprint game) {
        sprintFragment = new SprintFragment();
        textPoint.setText(String.valueOf(point));
        game.setCurrentLevel(game.getCurrentLevel() + 1);
        game.createFieldInLevel(game.getCurrentLevel());
        sprintFragment.setGame(game);
        getFragmentManager().beginTransaction().replace(R.id.activity_main, sprintFragment).commit();

    }
    @Override
    public void nextLevel(GameModTwisting game) {
        twistingFragment = new TwistingFragment();
        textPoint.setText(String.valueOf(point));
        game.setCurrentLevel(game.getCurrentLevel() + 1);
        game.createFieldInLevel(game.getCurrentLevel());
        twistingFragment.setGame(game);
        getFragmentManager().beginTransaction().replace(R.id.activity_main, twistingFragment).commit();
    }
    @Override
    public void nextLevel(GameModEvidence game) {
        evidenceFragment = new EvidenceFragment();
        textPoint.setText(String.valueOf(point));
        game.setCurrentLevel(game.getCurrentLevel() + 1);
        game.createFieldInLevel(game.getCurrentLevel());
        evidenceFragment.setGame(game);
        getFragmentManager().beginTransaction().replace(R.id.activity_main, evidenceFragment).commit();
    }
    @Override
    public void next(GameModChallenge game) {
        //Возможно,что игрок потратил игровую валюту
        textPoint.setText(String.valueOf(point));
        challengeFragment = new ChallengeFragment();
        game.createFieldInLevel(game.getCurrentLevel());
        challengeFragment.setGame(game);
        getFragmentManager().beginTransaction().replace(R.id.activity_main, challengeFragment).commit();
    }
    @Override
    public void failSprint(GameModSprint game) {
        sprintFragment = new SprintFragment();
        game = new GameModSprint(this);
        game.createFieldInLevel(1);
        game.setCurrentLevel(1);
        sprintFragment.setGame(game);
        getFragmentManager().beginTransaction().replace(R.id.activity_main, sprintFragment).commit();

    }
    @Override
    public void failTwisting(GameModTwisting game) {
        twistingFragment = new TwistingFragment();
        game = new GameModTwisting(this);
        game.createFieldInLevel(1);
        game.setCurrentLevel(1);
        twistingFragment.setGame(game);
        getFragmentManager().beginTransaction().replace(R.id.activity_main, twistingFragment).commit();
    }
    @Override
    public void failEvidence(GameModEvidence game) {
        evidenceFragment = new EvidenceFragment();
        game = new GameModEvidence(this);
        game.createFieldInLevel(1);
        game.setCurrentLevel(1);
        evidenceFragment.setGame(game);
        getFragmentManager().beginTransaction().replace(R.id.activity_main, evidenceFragment).commit();
    }


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

Код фрагмента игры
public final class SprintFragment extends Fragment implements View.OnClickListener {
    private Game game;
    private Field field;
    private int fieldSize;
    private Button sq[][];
    private LayoutInflater inflater;
    private Runnable runner;
    private View root;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        root = inflater.inflate(R.layout.game, container, false);
        TextView title = (TextView) root.findViewById(R.id.game_challenge_title);
        title.setText(String.valueOf(getActivity().getResources().getString(R.string.current_level) + game.getCurrentLevel()));
        this.inflater = inflater;
        final View.OnClickListener listener = this;
        int doNotDuplicateId = 0;
        final TableLayout fieldView = (TableLayout) root.findViewById(R.id.field);
        ProgressBar progressBar = (ProgressBar) root.findViewById(R.id.progress_bar);
        progressBar.setVisibility(View.INVISIBLE);
        fieldSize = field.getSizeField();
        sq = new Button[fieldSize][fieldSize];
        fieldView.setWeightSum(fieldSize);
        TableRow tableRows[] = new TableRow[fieldSize];
        TableRow.LayoutParams paramsRow = new TableRow.LayoutParams();
        paramsRow.height = TableRow.LayoutParams.MATCH_PARENT;
        paramsRow.width = TableRow.LayoutParams.WRAP_CONTENT;
        paramsRow.weight = 1;
        for (int i = 0; i < fieldSize; i++) {
            tableRows[i] = new TableRow(getActivity().getApplicationContext());
            tableRows[i].setLayoutParams(paramsRow);
            for (int j = 0; j < fieldSize; j++) {
                sq[i][j] = (Button) inflater.inflate(R.layout.one_field_button, null, false);
                sq[i][j].setId(doNotDuplicateId);
                sq[i][j].setWidth(getActivity().getWindow().getWindowManager().getDefaultDisplay().getWidth() / fieldSize - getActivity().getWindow().getWindowManager().getDefaultDisplay().getWidth() / (fieldSize * fieldSize));
                sq[i][j].setHeight(getActivity().getWindow().getWindowManager().getDefaultDisplay().getWidth() / fieldSize - getActivity().getWindow().getWindowManager().getDefaultDisplay().getWidth() / (fieldSize * fieldSize));
                doNotDuplicateId++;
                if (field.getSquare()[i][j] == 1) {
                    sq[i][j].setBackgroundDrawable(getActivity().getApplicationContext().getResources().getDrawable(R.drawable.sq_painted));
                }

                tableRows[i].addView(sq[i][j], j);
                tableRows[i].setWeightSum(fieldSize);
            }
            fieldView.addView(tableRows[i], i);
        }

        root.postDelayed(runner = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < fieldSize; i++) {
                    for (int j = 0; j < fieldSize; j++) {
                        sq[i][j].setOnClickListener(listener);
                        if (field.getSquare()[i][j] == 1) {
                            sq[i][j].setBackgroundDrawable(getActivity().getApplicationContext().getResources().getDrawable(R.drawable.sq));
                        }
                    }
                }
            }
        }, 2000L);
        return root;
    }

    public void setGame(Game game) {
        this.game = game;
        this.field = game.getField();
    }

    @Override
    public void onClick(View view) {
        int id = view.getId();
        for (int i = 0; i < fieldSize; i++) {
            for (int j = 0; j < fieldSize; j++) {
                if (id == sq[i][j].getId()) {
                    System.out.println(sq[i][j].getId());
                    if (field.getSquare()[i][j] == 1) {
                        field.setRightAnswer(field.getRightAnswer() + 1);
                        view.setOnClickListener(null);
                        view.setBackgroundDrawable(getActivity().getApplicationContext().getResources().getDrawable(R.drawable.sq_painted));
                        if (field.getRightAnswer() == field.getPaintedSquare()) {
                            game.createVictoryDialog(inflater);
                            game.showDialog();
                        }
                    } else if (field.getSquare()[i][j] == 0) {
                        view.setBackgroundDrawable(getActivity().getApplicationContext().getResources().getDrawable(R.drawable.sq_wrong));
                        game.createLooseDialog(inflater);
                        game.showDialog();
                    }
                }
            }
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        root.removeCallbacks(runner);
    }
}


Обратите внимание, на этот метод:

    public void setGame(Game game) {
        this.game = game;
        this.field = game.getField();
    }

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

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

Таким образом при определении нажатой кнопки нужно проверить какая цифра лежит в массиве объекта класса Fild. Если 0, то пользователь сделал неверный ввод и надо закончить игру вызовом

game.createLooseDialog(inflater);
game.showDialog();

который создаст и откроет нужное нам диалоговое окно с выборов действий.

Если игрок выбрал все элементы равное 1, то создаётся победное диалоговое окно. Несмотря на то, что у нас ссылка типа Game объект создавался активностью типа GameModSprint, значит и вызовется соответствующая переопределённая версия этого метода:

Победа
if (field.getRightAnswer() == field.getPaintedSquare()) {
game.createVictoryDialog(inflater);
game.showDialog();
}


Таким образом мы пользуемся мощнейшим средством языка Java — динамической диспетчеризацией методов.

Заключение


Итак, мы с вами познакомились с разработкой мобильных приложений и вот, что получилось:
режим игры спринт
Выбор уровня:

И сама игра:


Ссылка на скачивание игры с Google Play
play.google.com/store/apps/details?id=com.thekingames.memorize
Удачи всем!
Поделиться публикацией
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама
Комментарии 12
  • +3
    Я ученик 11 класса и разработкой занимаюсь только от того, что не хочу готовиться к ЕГЭ

    Ну, хоть прокрастинация с пользой! Это здорово.

    А учиться по этому «приложению» я бы не рекомендовал никому.
    • 0
      Это приложение можно было написать намного проще, но я хотел продемонстрировать разделение кода по пакетам, использование принципов ООП, динамическую смену фрагментом и передачу данных от фрагмента к активности.
      • 0
        Ну вот советы вам:
        Для начала научитесь выкладывать код на github.
        разделение кода по пакетам

        В реальности так как сделали вы не помогает. Код лучше разделять по уровням, а потом по «фичам». Лучше разделить пакеты на уровни «model» и «ui». Далее, к примеру, внутри пакета ui, для каждого экрана свой пакет
        использование принципов ООП

        Если подразумевается ООПрограммирование, то такие принципы проще показывать на маленьких примерах.
        А к ООПроектированию вы пока даже близко не подошли, начните с GoF и SOLID…

        Честно, вам рано кого-то учить, вам надо учиться самому…
        • 0
          Спасибо за критику, учту.
          Честно, вам рано кого-то учить, вам надо учиться самому…

          Я учусь, но часто вижу людей, которым бы пригодились мои объяснения и примеры кода
          • 0
            Код лучше разделять по уровням, а потом по «фичам».

            вопрос спорный. Для больших проектов однозначно лучше по фичам бить, особенно если их не очень много. Для небольших в инете можно встретить разные подходы в sample проектах (причем авторы- разработчики фреймворков)
      • 0
        В чем идея вашего MVC, если в абстракции Game, которая является моделью, есть ссылка на Activity?
        • –2
          Может быть MVC я не совсем придерживаюсь, потому что местами у меня выполняется и логика и изменение пользовательского интерфейса. MVC мне помогает создать правильную структуру пакетов.Код в одном пакете, а фрагменты и активности в другом.
        • 0
          Я думаю зря они на вас накинулись, Константин! В 17 лет написать статью, практически без грамматических ошибок… Это круто. Плюс вы уж точно заслужили. У меня не все опытные разработчики так могли.

          Что же касается, собственно, содержимого, то вот вам еще совет. Не пытайтесь (пока) писать о том, как надо делать. Попробуйте написать какие проблемы у вас возникли и как вы их решали. Ну, к примеру, — хотел разнести фрагменты и логику по пакетам (кстати, зачем?) — услышал про MVC и попробовал применить. Попросите критики — уверен, без холивара не обойдется, но для вас-то будет очевидная польза.

          Затем поправьте свое приложение и напишите статью «версия два — спасибо Хабру и ЕГЭ»

          Удачи!
          • 0
            В 17 лет написать статью, практически без грамматических ошибок… Это круто. Плюс вы уж точно заслужили. У меня не все опытные разработчики так могли.

            Это был сейчас сарказм? Ошибок очень много, и орфографических, и пунктуационных (а вот грамматические – да, в глаза не бросились). Разве не предполагается, что в 11 классе человек, собственно, весь базовый курс русского языка уже прошел, и способен писать грамотно?


            Не пытайтесь (пока) писать о том, как надо делать. Попробуйте написать какие проблемы у вас возникли и как вы их решали.

            Да, вот тут согласен. Как школьный реферат – неплохо. Как туториал на ресурсе для программистов – категорически, нет.

            • 0
              Разве не предполагается, что в 11 классе человек, собственно, весь базовый курс русского языка уже прошел, и способен писать грамотно?


              Вообще да, но в данный момент — нет (С). Берем навскидку три рандомных корпоративных блога на Хабре, готов поспорить, как минимум в одном из них с грамотностью будет сильно хуже. А ведь _предполагается_, что их ведут супер-профи копирайтеры со 100% врожденной грамотностью и вторым техническим высшим.
          • 0
            Ребят, не судите строго паренька, он старается, учится, не поленился написать статью, быть может другие неопытные разработчики наткнуться на его статью в самом начале своего пути, прочтут, потом прочтут ваши комментарии и будут знать куда им двигаться дальше :)
            • 0
              Понравилось начало
              разработкой занимаюсь только от того, что не хочу готовиться к ЕГЭ
              . Типа, «не хотелось учиться, потому стал миллиардером» )))
              Считаю что для школьного возраста парень молодец! Желаю успехов!

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