Разработка основного функционала

Основной функционал приложение разбит на 3 составляющие.

1. Переводчик текста

2. История передов и избранные переводы

3. Настройки переводов

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

Фрагмент (класс Fragment) - представляет поведение или часть пользовательского интерфейса в операции (класс Activity). Разработчик может объединить несколько фрагментов в одну операцию для построения многопанельного пользовательского интерфейса и повторного использования фрагмента в нескольких операциях. Фрагмент можно рассматривать как модульную часть операции. Такая часть имеет свой жизненный цикл и самостоятельно обрабатывает события ввода. Кроме того, ее можно добавить или удалить непосредственно во время выполнения операции. Это нечто вроде вложенной операции, которую можно многократно использовать в различных операциях.

Листинг фрагмента перевода текста:


* Метод перевода текста с последующим отображением
* @param text Текст для перевода
* @param fromLang С какого языка перевод
* @param toLang На какой язык перевод
* @param isDictionaryWord Нужно ли заменить текст в поле ввода
* */
public void translate(final String text, final String fromLang, final String toLang, boolean isDictionaryWord)
{
/** Нужно ли показывать словарь */
final boolean showDictionary = getPreferences().getBoolean("showDictionary",true);
/** Офлайн перевод */
final boolean offlineTranslate = getPreferences().getBoolean("offlineTranslate",true);


/** Проверка текста перевода на пустату */
if(TextUtils.isEmpty(text)) {
/** Скрываю frame с результатом перевода и словарем */
dictionatyResultFrame.setVisibility(View.GONE);
translateResultFrame.setVisibility(View.GONE);
return;
}

/** Язык перевода */
final String translateLang = fromLang + "-" + toLang;

/** Замена текста в поле ввода */
if(isDictionaryWord)
{
editText.removeTextChangedListener(editTextWatcher);
editText.setTextKeepState(text);
editText.addTextChangedListener(editTextWatcher);
clearText.setVisibility((text.length() > 0) ? View.VISIBLE : View.INVISIBLE);
}

/** Смена языков перевода spinners */
sFirst.setOnItemSelectedListener(null);
sSecond.setOnItemSelectedListener(null);

sFirst.setSelection(Arrays.asList(Translator.langsCode).indexOf(fromLang),false);
sSecond.setSelection(Arrays.asList(Translator.langsCode).indexOf(toLang),false);

sFirst.setOnItemSelectedListener(onSpinnerChanged);
sSecond.setOnItemSelectedListener(onSpinnerChanged);

/** Скрываю frame с результатом перевода и словарем */
dictionatyResultFrame.setVisibility(View.GONE);
translateResultFrame.setVisibility(View.GONE);

/** Включаю ProgressBar */
translateProgressBar.setVisibility(View.VISIBLE);


/** Офлайн перевод */
if(offlineTranslate) {
/** Проверяем в БД перевод такого текста и языка */
ArrayList<TranslateResultModel> dbTranslate = dataBaseHelper.getTranslate(text, translateLang);

/** Если пеервод встечается, то выводим и делаем запрос на словарь
* Словарь в БД не хранится
* */
if (dbTranslate.size() != 0) {
translateProgressBar.setVisibility(View.GONE);
translateResultFrame.setVisibility(View.VISIBLE);
translateText.setText(dbTranslate.get(0).to);
translateResultFrame.startAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.move));
if(showDictionary)
dictionary(text, fromLang, toLang);
return;
}
}

/** Онлайн перевод */
translator.translate(text, translateLang, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
try {
/** Вытаксиваем результат перевода */
JSONObject jObject = new JSONObject(response);
JSONArray jArray = jObject.getJSONArray("text");

final String translatedText = jArray.getString(0);
final String origText = text;


/** Добавляю в БД результат пеервода */
dataBaseHelper.insert(new TranslateResultModel(origText,translatedText,translateLang,false));

/** Обновляю историю переводов */
((MainActivity)getActivity()).getHistoryFragment().updateHistoryList();

/** Включаю фреймы */
translateResultFrame.setVisibility(View.VISIBLE);
translateProgressBar.setVisibility(View.GONE);

/** Ставлю текст перевода */
translateText.setText(translatedText);
translateResultFrame.startAnimation(AnimationUtils.loadAnimation(getContext(),R.anim.move));

if(showDictionary)
dictionary(text,fromLang,toLang);

} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
/** В случае ошибки ответа */
if(error.networkResponse != null)
translateText.setText(getResources().getString(R.string.failedTranslateText)
+ "\n" + getResources().getString(R.string.errorCode)
+ error.networkResponse.statusCode);
else
translateText.setText(getResources().getString(R.string.failedTranslateText)
+ "\n" + getResources().getString(R.string.checkInternet));

translateProgressBar.setVisibility(View.GONE);
translateResultFrame.setVisibility(View.VISIBLE);

translateResultFrame.startAnimation(AnimationUtils.loadAnimation(getContext(),R.anim.move));
}
});
}

/**
* Метод получения словаря, с последующим отображением
* @param text Текст для перевода
* @param fromLang С какого языка перевод
* @param toLang На какой язык перевод
* */
public void dictionary(final String text, final String fromLang, final String toLang)
{
/** Язык перевода */
final String translateLang = fromLang + "-" + toLang;

/** Скрываю фрейм словаря */
dictionatyResultFrame.setVisibility(View.GONE);

/** Запрос к словарю */
dictionary.getWords(text, translateLang, new Response.Listener<String>() {
@Override
public void onResponse(String response) {

/** Создание SpannableString
* Слова будут окрашены и начнут нажиматься
* */
SpannableString dict = dictionary.getDictionarySpannableString(response,fromLang,toLang);

/** Проверка на пустую строку */
if(!TextUtils.isEmpty(dict.toString())) {
dictionatyResultFrame.setVisibility(View.VISIBLE);
dictionaryText.setText(dict);

dictionatyResultFrame.startAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.move));
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
/** Ошибки словаря не выводятся */
}
});
}

@Override
public View onCreateView(final LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.translator_main, container, false);
((MainActivity)getActivity()).translatorFragment = this;

translator = ((MainActivity)getActivity()).getTranslator();
dictionary = ((MainActivity)getActivity()).getDictionary();

sFirst = (Spinner) rootView.findViewById(R.id.spinnerFromLang);
sSecond = (Spinner) rootView.findViewById(R.id.spinnerToLang);


translateText = (TextView)rootView.findViewById(R.id.translatedText);
dictionaryText = (TextView)rootView.findViewById(R.id.dictionaryText);
dictionaryText.setMovementMethod(LinkMovementMethod.getInstance());

translateResultFrame = (FrameLayout) rootView.findViewById(R.id.translateResult);
dictionatyResultFrame = (FrameLayout) rootView.findViewById(R.id.dictionaryResult);

translateProgressBar = (ProgressBar) rootView.findViewById(R.id.translateProgressBar);

TextView translatedByYandex = (TextView)rootView.findViewById(R.id.translatedByYandex);
translatedByYandex.setText(Html.fromHtml(getResources().getString(R.string.translatedByYandex)));
translatedByYandex.setMovementMethod(LinkMovementMethod.getInstance());

switchLang = (ImageButton) rootView.findViewById(R.id.switchLang);
clearText = (ImageButton) rootView.findViewById(R.id.clearText);

ImageButton openFullscreen = (ImageButton) rootView.findViewById(R.id.openFullscreen);
openFullscreen.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getContext(), TextReaderActivity.class);
intent.putExtra("text",translateText.getText().toString());
startActivity(intent);
}
});

/** Листеер на очистку поля ввода */
clearText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
editText.setText("");
}
});


/** Листеер на смену языков местами */
switchLang.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/** Анимации */
switchLang.setRotation(0);
switchLang.animate().rotation(180).setDuration(500);
sFirst.animate().alpha(0).setDuration(300);
sSecond.animate().alpha(0).setDuration(300);
}
});

editText = (EditText)rootView.findViewById(R.id.editText);

/** TextWatcher */
editTextWatcher = new TextWatcher() {
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
/** Включаем скопку очистки поля если поле не пустое */
clearText.setVisibility((s.length() > 0) ? View.VISIBLE : View.INVISIBLE);
}
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { }

/** Наймер на задержку после ввода текста
* Лучше наверно сделать через Handler */
private Timer timer=new Timer();

/** Время через которое начнется перевод текста, после окончания ввода */
private final long DELAY = 1000;

@Override
public void afterTextChanged(final Editable s) {
if(s.toString().trim().length()>0) {
timer.cancel();
timer = new Timer();
timer.schedule(
new TimerTask() {
@Override
public void run() {
/** В UI потоке вызываем метод перевода текста */
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
translate(editText.getText().toString(),
Translator.langsCode[sFirst.getSelectedItemPosition()],
Translator.langsCode[sSecond.getSelectedItemPosition()],false);
}
});
}
},
DELAY
);
}
}
};

/** Додавляем листенер */
editText.addTextChangedListener(editTextWatcher);


ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
getContext(), R.array.langs, R.layout.spinner_lang_item);
sFirst.setAdapter(adapter);
sSecond.setAdapter(adapter);

onSpinnerChanged = new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
translate(editText.getText().toString(),
Translator.langsCode[sFirst.getSelectedItemPosition()],
Translator.langsCode[sSecond.getSelectedItemPosition()],false);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {}
});

/** Установка начальных языков перевода */
String sysLang = Locale.getDefault().getLanguage();
switch (sysLang)
{
case "en":
sFirst.setSelection(Arrays.asList(Translator.langsCode).indexOf("en"));
sSecond.setSelection(Arrays.asList(Translator.langsCode).indexOf("ru"));
break;
case "ru":
sFirst.setSelection(Arrays.asList(Translator.langsCode).indexOf("ru"));
sSecond.setSelection(Arrays.asList(Translator.langsCode).indexOf("en"));
break;
default:
sFirst.setSelection(Arrays.asList(Translator.langsCode).indexOf("auto"));
sSecond.setSelection(Arrays.asList(Translator.langsCode).indexOf("en"));
break;
}
return rootView;
}
}

Листинг фрагмента с историей и избранными переводами:

public static HistoryFragment newInstance() {

return new HistoryFragment();

}

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

View rootView = inflater.inflate(R.layout.history_main, container, false);

dbHelper = ((MainActivity)getActivity()).getDbHelper();

((MainActivity)getActivity()).historyFragment = this;

noTranslateHistory = (LinearLayout) rootView.findViewById(R.id.noTranslateHistory);

noHistoryText = (TextView) rootView.findViewById(R.id.noTranslateHistoryText);

clearHistory = (ImageButton) rootView.findViewById(R.id.clearHistory);

/** Вкладки для переключения межно историей и избранным */

mTabLayout = (TabLayout) rootView.findViewById(R.id.historyTab);

mTabLayout.addTab(mTabLayout.newTab().setText(R.string.history));

mTabLayout.addTab(mTabLayout.newTab().setText(R.string.favorite));

/** Листенер на смену вкладок */

mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {

@Override

public void onTabSelected(TabLayout.Tab tab) {

/** Устанавливаю текст с SearchView

* Просто мелочь...

* */

switch (tab.getPosition()) {

case 0:

searchView.setQueryHint(getResources().getString(R.string.searchInHistory));

break;

case 1:

searchView.setQueryHint(getResources().getString(R.string.searchInFavorite));

break;

default:

ListView lv = (ListView)rootView.findViewById(R.id.searchList);

adapter = new HistoryArrayAdapter(getContext());

lv.setAdapter(adapter);

searchView = (SearchView)rootView.findViewById(R.id.searchView);

/** Начальное обновления списка */

updateHistoryList();

return rootView;

}

public void updateHistoryList()

{

/** Очищаю список*/

adapter.clear();

/** Добавляю в список значения из БД */

adapter.addAll(dbHelper.getHistory(searchView.getQuery().toString(),

(mTabLayout.getSelectedTabPosition() == 1)));

/** Обновляю в UI*/

adapter.notifyDataSetChanged();

/** Если в БД есть значения, то включаю кнопку очистки истории */

clearHistory.setVisibility((dbHelper.dbCount() > 0) ? View.VISIBLE : View.GONE);

/** Если есть элементы в БД */

if(adapter.getCount() > 0)

noTranslateHistory.setVisibility(View.GONE);

else

{

noTranslateHistory.setVisibility(View.VISIBLE);

noHistoryText.setText((mTabLayout.getSelectedTabPosition() == 0) ?

getResources().getString(R.string.noTranslateHistory) :

getResources().getString(R.string.noFavorites));

}

Заключение

Мною было разработано полноценное приложение в точности по ТЗ имеющее полный функционал переводчика включая:

1. Онлайн/оффлайн перевод текста

2. Показа словаря

3. История и избранные переводы

В качестве референса для дизайна использовалось официальное приложение Yandex Переводчик.

Также использовались технологии API Yandex Translate и API Yandex Dictionary через которые был реализован перевод текста и показ словаря.

Для сетевых запросов использовалась библиотека Volley.

Поддержка старых версий ОС Android реализована через библиотеку Support Library

Список литературы

1. IDE Android Studio -https://ru.wikipedia.org/wiki/Android_Studio

2. Сервис перевода текста API Yandex Translate - https://tech.yandex.ru/translate/

3. Yandex Mobilization - https://academy.yandex.ru/events/mobdev/msk-2017/

4. Сервис словаря API Yandex Dictionary - https://tech.yandex.ru/dictionary/

5. Библиотека сетевых запросов Volley - https://developer.android.com/training/volley/index.html

6. Фрагменты в ОС Android - https://developer.android.com/guide/components/fragments.html?hl=ru

Наши рекомендации