Пишем свой кредитный калькулятор. Часть 3

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

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

Начальный экран

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

Либо, если список кредитов пуст, на экране будет показываться сообщение, уведомляющее об этом.

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

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

Боковое меню реализовано с помощью стандартного NavigationView.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    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:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start"
    >

  <include
      layout="@layout/app_bar_main"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      />

  <android.support.design.widget.NavigationView
      android:id="@+id/nav_view"
      android:layout_width="wrap_content"
      android:layout_height="match_parent"
      android:layout_gravity="start"
      android:fitsSystemWindows="true"
      app:headerLayout="@layout/nav_header_main"
      app:itemIconTint="@color/black"
      app:menu="@menu/activity_main_drawer"
      />

</android.support.v4.widget.DrawerLayout>

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

Создание кредита

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

Выбор типа платежа был заменён с RadioButton на более компактный Spinner.

<Spinner
    android:id="@+id/spinner"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="4dp"
    android:entries="@array/payment_types"
    />

Чтобы Spinner предлагал выбрать требуемый тип платежа, нужно с помощью атрибута android:entries в него загрузить массив строк, определённый в файле res/values/arrays.xml.

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string-array name="payment_types">
    <item>@string/payment_annuity</item>
    <item>@string/payment_differentiated</item>
  </string-array>
</resources>

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

Примечание: приложение написано с использованием модели MVP (Model-View-Presenter), поэтому здесь и далее будет уточняться, на каком уровне представлен код. Подробнее об этой модели можно прочитать в этой статье.

На уровне Presenter активности при нажатии на кнопку «Рассчитать» (либо при сохранении кредита) проверяется каждое поле на наличие данных.

private boolean checkFields(EditText amount, EditText term, EditText rate, EditText date) {
  if (TextUtils.isEmpty(amount.getText().toString())) {
    newPaymentView.onSetError(amount, R.string.empty_field);
    return false;
  } else if (TextUtils.isEmpty(term.getText().toString())) {
    newPaymentView.onSetError(term, R.string.empty_field);
    return false;
  } else if (TextUtils.isEmpty(rate.getText().toString())) {
    newPaymentView.onSetError(rate, R.string.empty_field);
    return false;
  } else if (TextUtils.isEmpty(date.getText().toString())) {
    newPaymentView.onSetError(date, R.string.empty_field);
    return false;
  }

  return true;
}

Если какое-либо из полей не заполнено, то вызывается метод onSetError() на уровне View, который выводит ошибку на экран пользователя.

@Override public void onSetError(EditText editText, int id) {
  editText.setError(getString(id));
}

Если пользователь заполнил все поля, то на экране отобразятся результаты расчётов по заданному кредиту.

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

Просмотр графика платежей

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

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

На уровне Presenter активности в методе createGraph() создаётся объект View, разметка которого загружается с помощью LayoutInflater. После этого определяются компоненты, расположенные на разметке, этим компонентам присваиваются нужные значения, взятые из объекта Payment, который содержит данные о кредите. Эти операции выполняются в цикле, количество итераций которого равно количеству месяцев.

@Override public void createGraph(LayoutInflater inflater) {
  for (int i = 1; i <= payment.getTerm(); i++) {
    if (payment.getType().equals(graphView.getStringFromResource(R.string.payment_annuity))) {
      Calculator.calculateCurrentAnnuity(i - 1, payment.getAmount(), payment.getTerm(),
          payment.getRate());
    } else {
      Calculator.calculateCurrentDifferentiated(i - 1, payment.getAmount(), payment.getTerm(),
          payment.getRate());
    }

    String currentPayment = String.format(Locale.getDefault(), "%.2f", Calculator.payment);
    String currentDebt = String.format(Locale.getDefault(), "%.2f", Calculator.debt);
    String currentPercent = String.format(Locale.getDefault(), "%.2f", Calculator.percent);
    String currentBalance = String.format(Locale.getDefault(), "%.2f", Calculator.balance);

    View cardLayout = inflater.inflate(R.layout.card_graph, null);
    TextView cardNumber = cardLayout.findViewById(R.id.card_number);
    cardNumber.setText(String.format(Locale.getDefault(), "%s %d", cardNumber.getText(), i));

    TextView cardDate = cardLayout.findViewById(R.id.card_date);
    cardDate.setText(payment.getDates().get(i - 1));

    TextView cardPayment = cardLayout.findViewById(R.id.card_payment);
    cardPayment.setText(
        String.format(Locale.getDefault(), "%s %s", cardPayment.getText(), currentPayment));

    TextView cardDebt = cardLayout.findViewById(R.id.card_debt);
    cardDebt.setText(
        String.format(Locale.getDefault(), "%s %s", cardDebt.getText(), currentDebt));

    TextView cardBalance = cardLayout.findViewById(R.id.card_balance);
    cardBalance.setText(
        String.format(Locale.getDefault(), "%s %s", cardBalance.getText(), currentBalance));

    TextView cardPercent = cardLayout.findViewById(R.id.card_percent);
    cardPercent.setText(
        String.format(Locale.getDefault(), "%s %s", cardPercent.getText(), currentPercent));

    graphView.onAddCard(cardLayout);
  }
}

При завершении каждой итерации цикла вызывается метод onAddCard(), который на уровне View добавляет к родительскому контейнеру сгенерированный объект View с помощью метода addView().

@Override public void onAddCard(View cardLayout) {
  linearLayout.addView(cardLayout);
}

Сохранение кредита

Чтобы не вводить каждый раз данные о кредите, его можно сохранить. Для этого на экране создания кредита в тулбаре есть кнопка «Сохранить«. При нажатии на неё аналогично вводу значений делается проверка на наличие всех данных. Если всё в порядке, то появляется диалог, предлагающий дать название кредиту.

Когда имя будет задано, в объект Payment, расположенный на уровне Model, будут добавлены все нужны данные, а сам объект будет добавлен в список объектов, который хранится в Shared Preferences приложения. Делается это в обработчике нажатия кнопки диалога на уровне Presenter.

@Override
public void setName(final String type, EditText et_amount, EditText et_term, EditText et_rate,
    EditText et_date) {
  if (!checkFields(et_amount, et_term, et_rate, et_date)) {
    return;
  }

  final String amount = et_amount.getText().toString();
  final String term = et_term.getText().toString();
  final String rate = et_rate.getText().toString();
  final String date = et_date.getText().toString();

  AlertDialog.Builder builder = new AlertDialog.Builder(newPaymentView.getContext());
  LayoutInflater inflater = newPaymentView.getInflater();
  View dialogView = inflater.inflate(R.layout.dialog_set_name, null);
  builder.setView(dialogView);
  final EditText editText = dialogView.findViewById(R.id.et_save);
  if (payment != null && payment.getName() != null) {
    editText.setText(payment.getName());
  }
  builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
    @Override public void onClick(DialogInterface dialogInterface, int i) {
    }
  });
  builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
    @Override public void onClick(DialogInterface dialogInterface, int i) {
    }
  });
  final AlertDialog dialog = builder.create();
  dialog.show();
  dialog.getButton(DialogInterface.BUTTON_POSITIVE)
      .setTextColor(ContextCompat.getColor(newPaymentView.getContext(), R.color.colorPrimary));
  dialog.getButton(DialogInterface.BUTTON_NEGATIVE)
      .setTextColor(ContextCompat.getColor(newPaymentView.getContext(), R.color.colorPrimary));
  dialog.getButton(DialogInterface.BUTTON_POSITIVE)
      .setOnClickListener(new View.OnClickListener() {
        @Override public void onClick(View view) {
          if (TextUtils.isEmpty(editText.getText().toString())) {
            newPaymentView.onSetError(editText, R.string.empty_field);
          } else {
            if (payment != null) {
              payment.setName(editText.getText().toString());
              payment.setType(type);
              payment.setRate(Integer.parseInt(rate));
              payment.setTerm(Integer.parseInt(term));
              payment.setAmount(Integer.parseInt(amount));
            } else {
              payment = new Payment(type, Integer.parseInt(amount), Integer.parseInt(term),
                  Integer.parseInt(rate));
              payment.setName(editText.getText().toString());
            }
            if (type.equals(newPaymentView.getStringFromResource(R.string.payment_annuity))) {
              Calculator.calculateAnnuity(payment, date);
            } else {
              Calculator.calculateDifferentiated(payment, date);
            }
            saveToList();
            dialog.dismiss();
            newPaymentView.onUpdateTitle(payment.getName());
          }
        }
      });
}

Чтобы диалоги были одной цветовой гаммы вместе с приложением, цвет кнопок был изменён. Делается это с помощью метода getButton().setTextColor() объекта AlertDialog.

dialog.getButton(DialogInterface.BUTTON_POSITIVE)
    .setTextColor(ContextCompat.getColor(newPaymentView.getContext(), R.color.colorPrimary));

Когда сохранение будет завершено, пользователь будет уведомлен об этом всплывающим сообщением.

Отправка уведомлений

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

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

Если кредит сохранён, появятся два диалога, предлагающих выбрать день напоминания и время.

Эти диалоги реализованны в методах showNotificationDialog() и showTimePickerDialog() на уровне Presenter.

private void showNotificationDialog() {
  final Calendar calendar = Calendar.getInstance();
  DatePickerDialog datePickerDialog =
      new DatePickerDialog(newPaymentView.getContext(), R.style.DialogTheme,
          new DatePickerDialog.OnDateSetListener() {
            @Override public void onDateSet(DatePicker datePicker, int i, int i1, int i2) {
              calendar.set(Calendar.YEAR, i);
              calendar.set(Calendar.MONTH, i1);
              calendar.set(Calendar.DAY_OF_MONTH, i2);
              showTimePickerDialog(calendar);
            }
          }, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
          calendar.get(Calendar.DAY_OF_MONTH));
  datePickerDialog.show();
}

private void showTimePickerDialog(final Calendar calendar) {
  TimePickerDialog timePickerDialog =
      new TimePickerDialog(newPaymentView.getContext(), R.style.DialogTheme,
          new TimePickerDialog.OnTimeSetListener() {
            @Override public void onTimeSet(TimePicker timePicker, int i, int i1) {
              calendar.set(Calendar.HOUR_OF_DAY, i);
              calendar.set(Calendar.MINUTE, i1);
              calendar.set(Calendar.SECOND, 0);
              setAlarm(calendar);
            }
          }, calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), true);
  timePickerDialog.show();
}

Здесь большое значение имеет объект класса Calendar, ему сначала задаются день, месяц и год, а затем время. Он будет нужен для того, чтобы точно определить момент показа уведомления, переведя стандартное время в Unix-время.

Затем календарь с выставленными значениями передаётся в метод setAlarm(), в котором с помощью объекта AlarmManager устанавливается устанавливается выполнение PendingIntent в определённый момент времени. PendingIntent в свою очередь будет отправлять Intent, содержащий объект Payment, в BroadcastReceiver, который и будет создавать уведомления.

private void setAlarm(Calendar calendar) {
  Intent intent = new Intent(newPaymentView.getContext(), AlarmReceiver.class);
  Bundle bundle = new Bundle();
  bundle.putSerializable(Constants.EXTRA_PAYMENT, payment);
  intent.setAction(AlarmReceiver.ACTION_ALARM);
  intent.putExtra(Constants.EXTRA_BUNDLE, bundle);
  PendingIntent pendingIntent =
      PendingIntent.getBroadcast(newPaymentView.getContext(), (int) System.currentTimeMillis(),
          intent, 0);
  AlarmManager alarmManager =
      (AlarmManager) newPaymentView.getContext().getSystemService(Context.ALARM_SERVICE);
  if (alarmManager != null) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
      alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
          pendingIntent);
    } else {
      alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
    }
    LinkedHashSet<Payment> set = Tools.getSet(newPaymentView.getContext(), SP.ALARM_LIST);
    payment.setMillis(calendar.getTimeInMillis());
    set.add(payment);
    SP.saveList(newPaymentView.getContext(), SP.ALARM_LIST, set);
    newPaymentView.onShowToast(R.string.remind_created);
  }
}

При передаче объекта Payment может возникнуть ситуация, когда он не дойдёт до ресивера, поэтому сериализуемые объекты лучше всего добавлять в Bundle.

Для версий Android M (API 23) и выше у AlarmManager вызывается метод setAndAllowWhileIdle() вместо стандартного set(), который выполняет те же самые функции, однако позволяет создавать событие даже если устройство находится в режиме экономии заряда.

AlarmManager.RTC_WAKEUP разбудит устройство, если его экран погас.

После создания события объект, для которого создано напоминание, сохраняется в отдельный список в Shared Preferences. Это нужно по следующей причине: AlarmManager работает до тех пор, пока устройство не будет перезагружено. В этом случае при запуске устройства нужно в BroadcastReceiver отлавливать соответствующее событие, чтобы загрузить сохранённый список и создать уведомления заново. Таким образом, можно избежать того, что уведомления потеряются и не будут воспроизведены.

Когда наступает заданный момент времени, событие посылается в ресивер, определённый в манифесте со следующим фильтром.

<receiver
    android:name=".AlarmReceiver"
    android:enabled="true"
    android:exported="false">
  <intent-filter>
    <action android:name="ACTION_ALARM"/>
    <action android:name="android.intent.action.BOOT_COMPLETED"/>
    <action android:name="android.intent.action.QUICKBOOT_POWERON"/>
  </intent-filter>
</receiver>

android.intent.action.BOOT_COMPLETED присылается тогда, когда устройство было запущено после холодного старта. android.intent.action.QUICKBOOT_POWERON же присылается после перезагрузки устройства.

Затем соответствующие интенты отлавливаются в методе onReceive() ресивера.

@Override public void onReceive(Context context, Intent intent) {
  if (intent.getAction() != null) {
    switch (intent.getAction()) {
      case ACTION_ALARM: {
        showNotification(context, intent);
        break;
      }
      case "android.intent.action.BOOT_COMPLETED":
      case "android.intent.action.QUICKBOOT_POWERON": {
        checkList(context);
        break;
      }
    }
  }
}

Метод checkList(), как уже говорилось выше, проверяет, есть ли в списке какие-либо объекты, для которых надо создать уведомление, и создаёт их с помощью того же AlarmManager.

private void checkList(Context context) {
  LinkedHashSet<Payment> set = Tools.getSet(context, SP.ALARM_LIST);
  if (set != null) {
    for (Payment payment : set) {
      setAlarm(context, payment);
    }
  }
}

private void setAlarm(Context context, Payment payment) {
  Intent intent = new Intent(context, AlarmReceiver.class);
  Bundle bundle = new Bundle();
  bundle.putSerializable(Constants.EXTRA_PAYMENT, payment);
  intent.setAction(AlarmReceiver.ACTION_ALARM);
  intent.putExtra(Constants.EXTRA_BUNDLE, bundle);
  PendingIntent pendingIntent =
      PendingIntent.getBroadcast(context, (int) System.currentTimeMillis(), intent, 0);
  AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
  if (alarmManager != null) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
      alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, payment.getMillis(),
          pendingIntent);
    } else {
      alarmManager.set(AlarmManager.RTC_WAKEUP, payment.getMillis(), pendingIntent);
    }
  }
}

Метод showNotification() создаёт уведомление, нажатие на которое откроет экран с данными о кредите, по которому было создано напоминание.

private void showNotification(Context context, Intent intent) {
  Bundle bundle = intent.getBundleExtra(Constants.EXTRA_BUNDLE);
  if (bundle != null) {
    Payment payment = (Payment) bundle.getSerializable(Constants.EXTRA_PAYMENT);
    final int NOTIFY_ID = 1002;
    String name = context.getString(R.string.notification_channel_name);
    String id = "my_package_channel_1";
    String description = context.getString(R.string.notification_channel_description);

    String app_name = context.getString(R.string.app_name);
    NotificationCompat.Builder builder;
    NotificationManager notifyManager =
        (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    Intent i = new Intent(context, NewPaymentActivity.class);
    i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    i.putExtra(Constants.EXTRA_PAYMENT, payment);
    PendingIntent pendingIntent =
        PendingIntent.getActivity(context, (int) System.currentTimeMillis(), i,
            PendingIntent.FLAG_UPDATE_CURRENT);

    if (notifyManager != null && payment != null) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        int importance = NotificationManager.IMPORTANCE_HIGH;
        NotificationChannel mChannel = notifyManager.getNotificationChannel(id);
        if (mChannel == null) {
          mChannel = new NotificationChannel(id, name, importance);
          mChannel.setDescription(description);
          mChannel.enableVibration(true);
          mChannel.setVibrationPattern(
              new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 });
          notifyManager.createNotificationChannel(mChannel);
        }
      }

      builder = new NotificationCompat.Builder(context, id);
      builder.setContentTitle(context.getString(R.string.remind_loan, payment.getName()))
          .setSmallIcon(R.mipmap.ic_launcher)
          .setContentText(app_name)
          .setDefaults(Notification.DEFAULT_ALL)
          .setContentIntent(pendingIntent)
          .setTicker(context.getString(R.string.remind_loan, payment.getName()))
          .setVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 });
      Notification notification = builder.build();
      notifyManager.notify(NOTIFY_ID, notification);

      LinkedHashSet<Payment> set = Tools.getSet(context, SP.ALARM_LIST);
      if (set.contains(payment)) {
        set.remove(payment);
        SP.saveList(context, SP.ALARM_LIST, set);
      }
    }
  }
}

Для того, чтобы при нажатии открыть нужную активность, создаётся PendingIntent, который затем передаётся в метод setContentIntent() билдера. Также в билдер передаются другие параметры уведомления, такие как описание, иконка, параметры вибрации и другое.

Поскольку в Android O (API 26) были добавлены каналы уведомлений, его нужно создать, вызвав getNotificationChannel(), задав необходимые параметры уведомлений и сохранив с помощью createNotificationChannel(). Созданный канал уведомления можно увидеть, зайдя в настройки приложения и выбрав «Уведомления приложения«.

Остаётся вызвать лишь метод notify(), после чего пользователь увидит у себя созданное уведомление.

После показа уведомления кредит, для которого оно было создано, удаляется из списка, в который он был добавлен в начале работы.

Сравнение кредитов

Также в приложение была добавлена возможность сравнить 2 кредита и показать разницу в размере платежа и переплате.

Если же нет ни одного сохранённого кредита, при переходе на этот экран пользователь увидит всплывающее сообщение об этом.

Здесь для выбора кредитов используется Spinner. Поскольку заранее неизвестно, какие элементы будут в спиннере, массив загружается в него динамически. Для этого на уровне Presenter в методе loadEntries() создаётся ArrayAdapter, в который передаётся список кредитов и задаётся разметка выпадающего списка и элемента в списке. После этого адаптер передаётся в спиннер.

@Override public void loadEntries(Spinner first, Spinner second) {
  if (paymentList.size() == 0) {
    mainView.onShowToast(R.string.toast_save_loan);
    return;
  }

  final List<Payment> list = new ArrayList<>(paymentList);
  ArrayAdapter<Payment> adapter =
      new ArrayAdapter<>(mainView.getContext(), android.R.layout.simple_spinner_item, list);
  adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
  first.setAdapter(adapter);
  second.setAdapter(adapter);
}

Поскольку здесь используются объекты, а не строки, для корректного отображения их в списке нужно в класс Payment на уровне Model переопределить метод toString(), возвращая в нём название кредита.

@Override public String toString() {
  return name;
}

Затем при нажатии на кнопку «Сравнить» на уровне Presenter вызывается метод compareLoans(), в котором из спиннеров с помощью метода getSelectedItem() забираются объекты Payment и затем сравниваются между собой. Результат затем выводится в TextView.

@Override
public void compareLoans(Spinner spinner_first, Spinner spinner_second, TextView tv_amount,
    TextView tv_overpayment) {
  first = (Payment) spinner_first.getSelectedItem();
  second = (Payment) spinner_second.getSelectedItem();

  if (first != null && second != null) {
    int amount = first.getAmount() - second.getAmount();
    double overpayment = first.getOverpayment() - second.getOverpayment();
    mainView.onSetText(tv_amount, String.format(Locale.getDefault(), "%d", amount));
    mainView.onSetText(tv_overpayment, String.format(Locale.getDefault(), "%.2f", overpayment));
  }
}

Редактирование кредита

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

Для этого при старте активности проверяется интент в методе checkIntent() на уровне Presenter на наличие в нём объекта Payment. Если он есть, значит активность открывается для редактирования, иначе пользователь хочет создать новый кредит.

@Override public void checkIntent(Intent intent) {
  if (intent.getSerializableExtra(Constants.EXTRA_PAYMENT) != null) {
    payment = (Payment) intent.getSerializableExtra(Constants.EXTRA_PAYMENT);
    newPaymentView.updateFields(payment);
    if (payment.getType()
        .equals(newPaymentView.getStringFromResource(R.string.payment_annuity))) {
      newPaymentView.onCalculatedResult(
          String.format(Locale.getDefault(), "%.2f", payment.getPayment()),
          String.format(Locale.getDefault(), "%.2f", payment.getOverpayment()),
          String.format(Locale.getDefault(), "%.2f", payment.getTotal()), payment.getDates());
    } else {
      newPaymentView.onCalculatedResult(
          String.format(Locale.getDefault(), "%.2f..%.2f", payment.getFirstPayment(),
              payment.getLastPayment()),
          String.format(Locale.getDefault(), "%.2f", payment.getOverpayment()),
          String.format(Locale.getDefault(), "%.2f", payment.getTotal()), payment.getDates());
    }
    newPaymentView.onUpdateTitle(payment.getName());
  } else {
    setDate();
  }
}

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

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

Заключение

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *