Привет! Меня зовут Ксавье Жувено, и вот вторая часть длинной серии статей The Modern C++ Challenge. В этой статье я собираюсь объяснить, как я решил вторую проблему на C++ и как я интегрировал это решение в проект Android.

Цель второй задачи проста. Мы должны вычислить наибольший общий делитель двух положительных целых чисел, заданных пользователем, и вывести его пользователю. Решение будет вычислено на C++, а интерфейс для получения пользовательского ввода и отображения результата будет обрабатываться с помощью Android Studio Framework.

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

Решение на С++

Реализовать алгоритм нахождения наибольшего общего делителя двух чисел непросто… он буквально задан стандартом! 😉 Ведь в C++17 функция std::gcd делает именно это!

Итак, все, что нам нужно сделать, это включить заголовок из std и использовать функцию «std::gcd.

Если вы используете более старую версию стандарта C++, вам придется ее реализовать. Я могу порекомендовать вам этот пост в Stack Exchange об этом, если хотите. 🙂

Интерфейс пользовательского интерфейса в Android Studio

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

Для достижения этой цели нам нужно будет сделать две вещи:

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

Новое действие для новой проблемы

Чтобы создать новую активность, все, что вам нужно сделать, это щелкнуть правой кнопкой мыши по архитектуре папки вашего приложения и перейти к Empty Activity, как показано ниже:

Затем укажите имя вашего нового действия (я назвал его Problem_2) и нажмите Finish, чтобы создать новое действие.

Хорошо, но конкретно, что это значит? Во-первых, если вы откроете файл AndroidManifest.xml, вы увидите, что здесь была объявлена ​​ваша активность:

<activity android:name=".Problem_2"></activity>

Затем, если вы посмотрите в свои файлы Java, новый файл должен быть создан с классом, названным в честь вашей новой активности. Это класс, в котором мы будем обрабатывать пользовательский ввод, чтобы передать его алгоритму C++ перед отображением результата пользователю.

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

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

Связывание двух действий

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

Для этого мы начнем с добавления обратного вызова, функции, которая будет вызываться кнопкой, в первую активность. Эта функция выглядит так:

public void goToNextProblem(View v) {
  Intent intent = new Intent(this, Problem_2.class);
  startActivity(intent);
}

В этой функции мы создаем Intent и передаем ему класс нашего второго действия. Затем приступаем к этой деятельности. Довольно прямолинейно, не так ли?! 🙂

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

<Button android:id="@+id/button"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:onClick="goToNextProblem"
  android:text="@string/button_next"
  app:layout_constraintBottom_toBottomOf="parent"
  app:layout_constraintEnd_toEndOf="parent"
  app:layout_constraintHorizontal_bias="0.95"
  app:layout_constraintStart_toStartOf="parent"
  app:layout_constraintTop_toTopOf="parent"
  app:layout_constraintVertical_bias="0.954" />

Здесь важно то, что у нас есть поле android:onClick с именем нашего обратного вызова. Таким образом, когда пользователь нажмет на кнопку, она вызовет нашу функцию.

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

Использование собственного кода C++

В этой части мы рассмотрим класс Java второго действия.

public native String Gcd(int i, int j);
private int extractNumberFromEditText(EditText et) {
  assert et.getText().length() != 0 : "The EditText must not be empty";
  return Integer.parseInt(et.getText().toString());
}
private void computeAndDisplayResult() {
  EditText et1 = findViewById(R.id.first_input_number);
  EditText et2 = findViewById(R.id.second_input_number);
  int firstNumber = extractNumberFromEditText(et1);
  int secondNumber = extractNumberFromEditText(et2);
  TextView tv = findViewById(R.id.result);
  tv.setText(getString(R.string.result_placeholder, Gcd(firstNumber, secondNumber)));
}

Если вы читали мой пост в блоге о решении первой проблемы, эта часть не приносит ничего нового. Во-первых, мы объявляем функцию Gcd связанной с библиотекой C++. Затем у нас есть метод, который получает EditText с пользовательским вводом, мы извлекаем из них ввод, мы получаем TextView, где мы собираемся отображать результат, мы вычисляем результат и отображаем его.

Но в отличие от первой задачи, этот код находится в приватной функции. Не в обратном вызове EditText. Это из-за одной важной вещи. Мы не можем вычислить алгоритм GCD, если есть только один пользователь ввода. Мы должны убедиться, что оба ввода пользователя заполнены.

Итак, вот как выглядит обратный вызов:

EditText et = findViewById(R.id.first_input_number);
et.addTextChangedListener(new TextWatcher() {
  @Override
  public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
  @Override
  public void onTextChanged(CharSequence s, int start, int before, int count) {
    EditText et = findViewById(R.id.second_input_number);
    if(et.getText().length() == 0 || count == 0) { return; }
    computeAndDisplayResult();
  }
  @Override
  public void afterTextChanged(Editable s) { }
});

Итак, для первого EditText, когда текст изменяется, мы проверяем, не является ли новый текст пустым (count == 0), а второй EditText не пуст (et.getText().length() == 0). Если ни один из них не пуст, мы вызываем нашу функцию для вычисления и отображения результата. Если он пуст, мы ничего не делаем, возвращаясь напрямую. Для второго EditText код аналогичен, только меняются EditText идентификаторы. 😉

Вывод

Итак, теперь у нас есть приложение, которое может решить первые две задачи The Modern C++ Challenge и узнать что-то новое в Android Studio, что для этой задачи было для меня настоящей целью, так как алгоритм C++ уже интегрирован в Стандарт С++.

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

Спасибо всем за прочтение этой статьи, и до моей следующей статьи, хорошего дня 😉

Интересные ссылки

- The Modern C++ Challenge
- Документация по std::gcd
- Выпуск на Github

Первоначально опубликовано на http://10xlearner.com 30 марта 2020 г.