Android Development Tutorial. Часть 2/?

Автор оригинала: Lars Vogel
  • Перевод
Ларс Вогель — евангелист Eclipse.
Под катом Вы обнаружите продолжение перевода его статьи, которая описывает процесс создания Android-приложений с помощью Eclipse. Используется Eclipse 3.6, Java 1.6 и Android 2.3 (Gingerbread).
Часть 1

1.7. Активити и жизненный цикл

Операционная система контролирует жизненный цикл Вашего приложения. В любое время Android может остановить или уничтожить процесс Вашего приложения, например, из-за входящего звонка. Android определяет жизненный цикл активити с помощью предопределенных методов. Наиболее важные методы:
  • onSaveInstanceState() — вызывает, если активити остановлено. Используется для сохранения данных при восстановлении состояния активити, если активити возобновлено
  • onPause() — всегда вызывается, если активити завершилось, может быть использовано, для освобождения ресурсов или сохранения данных
  • onResume() — вызвано, если активити возобновлено, может быть использовано для инициализации полей

1.8. Контекст

Класс android.content.Context представляет связи с системой Android. Это интерфейс для глобальной информации про окружение приложения. Контекст также предоставляет метод getSystemService, позволяющий получить объект управления для различных частей оборудования. Так как Activities и Services расширяют класс «Context», вы можете получить прямой доступ к контексту с помощью «this».

2. Установка


Допустим, Вы уже установили Eclipse.

2.1. Android SDK

Скачайте Android SDK со страницы Android по ссылке Android SDK.

Скачанный zip-архив вы можете распаковать в любое место своей файловой системы, автор, например, распаковал в «c:\android-sdk-windows».

2.2. Eclipse

Используйте менеджер обновлений Eclipse для установки всех доступных плагинов для Android Development Tools (ADT) по ссылке dl-ssl.google.com/android/eclipse.

2.3. Configuration

В Eclipse откройте диалог Preferences через Windows -> Preferences. Выберите Android и укажите путь установки Android SDK.

Если Вам каждый раз или просто часто предлагается заново выбрать расположение плагина Android, присоединяйтесь к автору в решении бага Bug 3210
Теперь выберите из меню Window -> Android SDK и AVD Manager.

Выберите доступные пакеты и выберите свежайшую версию SDK.

Нажмите «Install selected» и согласитесь с лицензиями всех пакетов. После установки перезапустите Eclipse.

2.4. Device

Android tools включает в себя эмулятор. Этот эмулятор ведет себя как реальное Android-устройства в большинстве случаев и позволит Вам тестировать свое приложение без физически реального устройства. Вы можете эмулировать несколько устройств с разными конфигурациями. Каждая конфигурация определяется с помощью «Android Virtual Device» (AVD).

Для определения AVD нажмите кнопку менеджера устройств (device manager), нажмите «New» и совершите следующее.





Нажмите «Create AVD». Это создаст устройство. Для проверки корректности настройки, выберите ваше устройство и нажмите «Start».

После (спустя долгое время) ваше устройство будет запущено.


2.5. Android Source Code

Следующий шаг опционален.

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

Haris Peco поддерживает плагины, предоставляющие доступ к исходникам Android. Воспользуйтесь менеджером обновлений Eclipse для установки двух плагинов. Сайты с обновлений: «adt-addons.googlecode.com/svn/trunk/source/com.android.ide.eclipse.source.update» и «adt-addons.googlecode.com/svn/trunk/binedit/com.android.ide.eclipse.binedit.update».

3. Обработка ошибок



Вещи не всегда работают, как должны. Несколько отчетов пользователей описывают следующие ошибки:
  1. Project… is missing required source folder: 'gen'
  2. The project could not be built until build path errors are resolved.
  3. Unable to open class file R.java.

Для решения этих проблем выберите меню Project -> Clean.

Если у вас проблемы с собственным кодом, можете использвать LogCat viewer, описанный далее (в третьей или четвертой части перевода).

4. Ваш первый Android-проект


4.1. Создание проекта

Это приложение также доступно в Android Marketplace. Введите в поле поиска «vogella» для поиска этого примера.

Выберите File -> New -> Other -> Android -> Android Project и создайте Android-проект «de.vogella.android.temperature».

Придерживайтесь следующего:

Нажмите «Finish». Это создаст следующую структуру каталогов.

В платформе Android структурированные значения, которые содержит каталог «res», умеет так же хранить и каталог «assets», предназначенный для хранения любых данных. В Java Вы можете получить доступ к этим данным через AssetsManager и метод getAssets().

4.2. Две стороны вещей

Android SDK позволяет создавать точно определенные объекты, например, строки и пользовательские интерфейсы, двумя путями, с помощью тяжелого редактора, или прямо на XML. Постараемся использовать не только пользовательский интерфейс, но также и XML для проверки списков. Можете переключаться между этими двумя режимами вкладкой на нижней части экрана. Пример:


4.3. Создание атрибутов

Android позволяет создавать атрибуты ресурсов, например строки и/или цвета. Эти атрибуты могут быть использованы в определении UI с помощью XML или Java-кода.

Выберите файл «res/values/string.xml» а нажмите «Add». Выберите «Color» и сохраните с именем «myColor» и значением "#3399CC".



Добавьте также следующие атрибуты строки, они позже позволят переводить приложение.
Таблица 1. Атрибуты строки

Name Value
myClickHandler myClickHandler
celsius to Celsius
fahrenheit to Fahrenheit
calc Calculate


Переключитесь в режим работы с XMLи проверьте корректность заданных вами значений.
<?xml version="1.0" encoding="utf-8"?>
<resources>
	<string name="hello">Hello World, Convert!</string>
	<string name="app_name">Temperature Converter</string>
	<color name="myColor">#3399CC</color>
	<string name="myClickHandler">myClickHandler</string>
	<string name="celsius">to Celsius</string>
	<string name="fahrenheit">to Fahrenheit</string>
	<string name="calc">Calculate</string>
</resources>


4.4. Добавление элементов UI

Выберите «res/layout/main.xml» и откройте двойным кликом в Android editor. Этот редактор позволит задавать UI путем drag and drop, или напрямую в XML-коде. Вы можете переключаться между этими двумя вкладками кнопкой внизу редактора. Для изменения позиции и группировки элементов вы можете использовать просмотр эскиза.

Удалите «Hello World, Hello!» правым кликом. Перетяните «EditText» с панели «Views». Добавьте на макет «RadioGroup» с двумя переключателями RadioButtons (Вы можете удалить один RadioButton), добавьте одну «Button». Результат будет похожим на следующий скриншот, и соответствующий XML, написанный ниже.

Переключитесь на «main.xml» и проверьте, что XML-код похож на нижеприведенный.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="fill_parent">
	<EditText android:layout_height="wrap_content" android:id="@+id/editText1"
		android:layout_width="match_parent" android:text="EditText"></EditText>
	<RadioGroup android:layout_height="wrap_content" android:id="@+id/radioGroup1"
		android:layout_width="match_parent">
		<RadioButton android:text="RadioButton"
			android:layout_width="wrap_content" android:id="@+id/radio0"
			android:layout_height="wrap_content" android:checked="true"></RadioButton>
		<RadioButton android:text="RadioButton"
			android:layout_width="wrap_content" android:id="@+id/radio1"
			android:layout_height="wrap_content"></RadioButton>
	</RadioGroup>
	<Button android:text="Button" android:id="@+id/button1"
		android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
</LinearLayout>


4.5. Поддерржка свойств UI

Если Вы выберите элемент UI, то можете изменять его свойства через просмотр свойств. Большинство свойств могут также изменяться через меню, вызываемое правым кликом. Выберите EditText и удалите это свойство текста. Выберите поле, сделайте по нему правый клик, выберите Properties-> Text и удалите содержимое. Это значит, что никакого текста не будет показано в текстовом поле.



Удаление свойства текста в EditText ()

Назначьте строковый атрибут «celsius» Вашему свойству «text» первого переключателя и «fahrenheit» второго.



С этого момента автор предполагает, что вы можете использовать меню свойств на элементы UI. Установите свойство «Checked» в «true» для первого переключателя RadioButton. Назначить «calc» текстовому свойству Вашей кнопки и назначить «myClickHandler» свойству «onClick». Задать свойству «Input type» параметры «numberSigned» и «numberDecimal» в Вашем EditText.

Выберите фон (Ваш ViewGroup / LinearLayout) и задайте свойству «Background» атрибут цвета "@color/myColor".

Переключитесь во вкладку «main.xml», чтобы проверить, что XML-код правилен.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="fill_parent" android:background="@color/myColor">
	<EditText android:layout_height="wrap_content" android:id="@+id/editText1"
		android:layout_width="match_parent" android:inputType="numberDecimal|numberSigned"></EditText>
	<RadioGroup android:layout_height="wrap_content" android:id="@+id/radioGroup1"
		android:layout_width="match_parent">
		<RadioButton android:layout_width="wrap_content"
			android:id="@+id/radio0" android:layout_height="wrap_content"
			android:text="@string/celsius" android:checked="true"></RadioButton>
		<RadioButton android:layout_width="wrap_content"
			android:id="@+id/radio1" android:layout_height="wrap_content"
			android:text="@string/fahrenheit"></RadioButton>
	</RadioGroup>
	<Button android:id="@+id/button1" android:layout_width="wrap_content"
		android:layout_height="wrap_content" android:text="@string/calc"
		android:onClick="myClickHandler"></Button>
</LinearLayout>


4.6. Код вашего приложения

Измените Ваш код в «Convert.java» на следующий. Обратите внимание, что вызов «myClickHandler» будет основан на свойстве «OnClick» Вашей кнопки.
package de.vogella.android.temperature;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.Toast;

public class Convert extends Activity {
	private EditText text;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		text = (EditText) findViewById(R.id.editText1);

	}

	// This method is called at button click because we assigned the name to the
	// "On Click property" of the button
	public void myClickHandler(View view) {
		switch (view.getId()) {
		case R.id.button1:
			RadioButton celsiusButton = (RadioButton) findViewById(R.id.radio0);
			RadioButton fahrenheitButton = (RadioButton) findViewById(R.id.radio1);
			if (text.getText().length() == 0) {
				Toast.makeText(this, "Please enter a valid number",
						Toast.LENGTH_LONG).show();
				return;
			}

			float inputValue = Float.parseFloat(text.getText().toString());
			if (celsiusButton.isChecked()) {
				text.setText(String
						.valueOf(convertFahrenheitToCelsius(inputValue)));
			} else {
				text.setText(String
						.valueOf(convertCelsiusToFahrenheit(inputValue)));
			}
			// Switch to the other button
			if (fahrenheitButton.isChecked()) {
				fahrenheitButton.setChecked(false);
				celsiusButton.setChecked(true);
			} else {
				fahrenheitButton.setChecked(true);
				celsiusButton.setChecked(false);
			}
			break;
		}
	}

	// Converts to celsius
	private float convertFahrenheitToCelsius(float fahrenheit) {
		return ((fahrenheit - 32) * 5 / 9);
	}

	// Converts to fahrenheit
	private float convertCelsiusToFahrenheit(float celsius) {
		return ((celsius * 9) / 5) + 32;
	}
}


4.7. Запуск Проекта

Для запуска Android-приложения, Выберите Ваш проект, сделайте по нему правый клик, Run-As-> Android Application. Будьте терпеливы, эмулятор запускается очень медленно. Вы должны получить следующий результат.

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

4.8. Использование «домашнего» меню

Если вы нажмете кнопку Home, вы также можете выбрать свое приложение.




5. Меню, Настройки и Интенты


5.1. Меню

Для использования меню Android предоставляет два пути. Первый — меню вариантов, которое может быть открыто через кнопку меню. Меню вариантов Ваших действий наполняется методом onCreateOptionsMenu() Ваших активити. Вы можете зарегистрировать здесь меню через Ваш Java-код или используя ресурсы меню в XML, которое Вы наполняете через «MenuInflator». Вы получите MenuInflator через действия с методом getMenuInflator().

onCreateContextMenu() вызывается единожды. Если Вы хотите воздействовать на меню позже — используйте метод onPrepareOptionsMenu().

Второй вариант отображения меню использует контекстное меню для виджетов UI (Вид). Контекстное меню активируется, если пользователь «долго нажимает» на Виде.

Контекстное меню для Вида регистрируется через метод registerForContextMenu(view). Метод onCreateContextMenu() вызывает каждый раз активированное контекстное меню, как контекстное меню, сброшенное после использования. Платформа Android может также добавлять варианты к Вашему Виду, например, «EditText» предоставляет контекстные варианты для выбора текста, и прочее.

5.2. Проект

Эта часть покажет, как создать и определить количество вариантов меню, как определять настройки и как пермещаться между активити через интенты. Создайте проект «de.vogella.android.preferences» с активити «HelloPreferences». Измените UI в файле "/res/layout/main.xml" на следующее:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<Button android:id="@+id/Button01" android:layout_width="wrap_content" android:layout_height="wrap_content" 

android:text="Show Preferences"></Button>
<Button android:id="@+id/Button02" android:layout_width="wrap_content" android:layout_height="wrap_content" 

android:text="Change Preferences" android:onClick="changePreferences"></Button>
</LinearLayout>


5.3. Добавление меню в XML-ресурсах

Меню могут быть определены через XML-файлы. Выберите Ваш проект, сделайте правый клик на нем и выберите New -> Other -> Android -> «Android XML File». Выберите опцию «Menu», введите как файл «menu.xml» и нажмите кнопку «Finish».

Нажмите Add и выберите «Item». Задайте следующее значение. Это определяет записи в Вашем меню. У нас будет одна запись.

Измените Ваш класс «HelloPreferences» на следующий. Метод OnCreateOptionsMenu используют для создания меню. Поведение в «onOptionsItemSelected» в настоящий момент жестко закодировано для показа Тостов (Toast — уведомление, сообщение, которое всплывает на поверхности окна), чтобы вызывать настройки параметров. В случае, если вы хотите отключить или скрыть объекты меню, Вы можете использовать метод «onPrepareOptionsMenu», который вызывает каждый раз ранее вызванное меню.
package de.vogella.android.preferences;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;

public class HelloPreferences extends Activity {
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.menu, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		Toast.makeText(this, "Just a test", Toast.LENGTH_SHORT).show();
		return true;
	}
}

Запустите Ваше приложение и нажмите «Menu» в эмуляторе. Должно отобразиться Ваше меню. Если Вы выберите запись меню — то Вы должны увидеть небольшое информационное сообщение.


5.4. Использование настроек

Значения настроек могут также быть сортированы как XML-ресурсы. Создайте другой Android XML-файл «preferences.xml» на этот раз, типа «Preference».

Нажмите Add, добавьте категорию, и две настройки «EditTextPreferences» в эту категорию: «User» и «Password».





Для поддержки настроек Вы можете определить Активити с расширениями PreferenceActivity. Это активити может загружать определение ресурсов настройки через метод addPreferencesFromResource(). Создайте класс «Preferences», который будет загружать «preference.xml».
package de.vogella.android.preferences;

import android.os.Bundle;
import android.preference.PreferenceActivity;

public class Preferences extends PreferenceActivity {

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
	    addPreferencesFromResource(R.xml.preferences);
	}
	
	

}

Для того, чтобы сделать этот класс доступным как активити для Android, Вам нужно зарегистрировать его в Вашем файле «AndroidManifest.xml». Выберите «AndroidManifest.xml» и вкладку «Application». Добавьте активити «Preferences».

Мы обновим метод onOptionsItemSelected() для открытия активити «Preferences», единожды выбрав опцию меню. Хотя у нас сейчас есть только один вариант в нашем меню, мы используем переключение для готовности к нескольким новым записям меню. Чтобы увидеть поддерживаемые параметры, мы также определяем кнопку и используем класс «PreferenceManager» для получения sharedPreferences.

Первая кнопка покажет поддерживаемые сейчас параметры через Toast, а вторая кнопка вернет поддерживаемое имя пользователя для показа, как вы сможете изменить параметры через Java-код.
package de.vogella.android.preferences;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class HelloPreferences extends Activity {
	SharedPreferences preferences;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		Button button = (Button) findViewById(R.id.Button01);
		// Initialize preferences
		preferences = PreferenceManager.getDefaultSharedPreferences(this);

		button.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				String username = preferences.getString("username", "n/a");
				String password = preferences.getString("password", "n/a");
				Toast.makeText(
						HelloPreferences.this,
						"You entered user: " + username + " and password: "
								+ password, Toast.LENGTH_LONG).show();

			}
		});

		Button buttonChangePerferences = (Button) findViewById(R.id.Button02);
		buttonChangePerferences.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Editor edit = preferences.edit();
				String username = preferences.getString("username", "n/a");
				// We will just revert the current user name and save again
				StringBuffer buffer = new StringBuffer();
				for (int i = username.length() - 1; i >= 0; i--) {
					buffer.append(username.charAt(i));
				}
				edit.putString("username", buffer.toString());
				edit.commit();
				// A toast is a view containing a quick little message for the
				// user. We give a little feedback
				Toast.makeText(HelloPreferences.this,
						"Reverted string sequence of user name.",
						Toast.LENGTH_LONG).show();
			}
		});
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.menu, menu);
		return true;
	}

	// This method is called once the menu is selected
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		// We have only one menu option
		case R.id.preferences:
			// Launch Preference activity
			Intent i = new Intent(HelloPreferences.this, Preferences.class);
			startActivity(i);
			// Some feedback to the user
			Toast.makeText(HelloPreferences.this,
					"Here you can maintain your user credentials.",
					Toast.LENGTH_LONG).show();
			break;

		}
		return true;
	}
}


5.5. Запуск

Запустите Ваше приложение. Нажмите аппаратную кнопку «menu», затем выберите пункт меню «Preferences». Вам должен быть доступен ввод Ваших пользовательских настроек, затем нажмите аппаратную кнопку «Назад» для возврата в Ваше главное активити. Сохраненные значения должны быть отображены в небольших окнах сообщениях (Toast), если вы нажмете Вашу первую кнопку. Если Вы нажмете вторую кнопку, будет возвращено имя пользователя.


6. Диалоги через AlertDialog


На сегодня я остановился здесь. Буду рад критике.
Поделиться публикацией

Комментарии 15

    0
    Отловлена ошибка, из-за которой предыдущая часть рендерилась с ошибкой.
    проблема в коде, только я его стремаюсь в комментарий постить. сейчас в пред просмотре поиграюсь и поищу решение
      0
      Отсутствовал тег закрывающий (li который), во втором элементе списка из раздела 1.7
      +3
      Проект… отсутствует требуемый исходный каталог: 'gen'
      Проект не может собраться, пока не будут решены ошибки в пути сборки.
      Невозможно открыть файл классов R.java.

      Я думаю, это надо оставить на английском языке
        +4
        Я, конечно, понимаю, что это перевод статьи, но это ужасно смотреть на огромные картинки с подробными объяснениями куда нажимать, чтобы добавить тот же строковый ресурс. Лучше так: открыл string.xml, добавил строку такого-то формата с именем и значением ресурса и пошел дальше.
          0
          Отключение картинок на странице упростит Вам чтение статьи.
          Примерно так и пишет Ларс.
          +1
          «Вы можете эмулировать несколько устройства» — опечаточка, исправьте.
            0
            Спасибо. Сейчас исправлю.
            +1
            Вопрос к тем, кто много кодит в Ecipse. Сейчас изучаю программинг для андроида. Очень часто бывает, что Ecipse просто «схлопывается». Каких-то особых причин к этому нет. Обычно это происходит, когда я начинаю писать название какой-либо функции и появляется автокомплит. У вас такое бывает и как с этим бороться? (Убунту 10.10, Eclipse Java EE IDE for Web Developers. Version: Helios Service Release 1 Build id: 20100917-0705, последняя версия ADT).

            И в догонку оффтоп. Постал nbandroid на Netbeans, все бы хорошо, но нет автокомплита. Кто-то знает как это побороть?

            На перечисленные выше вопросы, пытался найти ответы в гугле, но увы и ах (
              +1
              intellij наше все. А эклипсу скорее всего памяти не хватает, попробуйте разные -Xms -Xmx ему схавать при запуске.
                +1
                спасибо, попробую. Пока что нашлось отличное решение, NetBeans с новой бетой nbandroid, поддержка автокомплита работает. Удобно. В том же NetBeans пишу на пхп, не надо привыкать к новой IDE :)
                  +1
                  решение с -Xms -Xmx не помогло.
                  наглядный пример, когда закрывается Eclipse:
                  берем строчку
                  Paint dark = new Paint();
                  dark.setColor(getResources().getColor(R.color.puzzle_dark));

                  пишем
                  Paint dark = new Paint();
                  dark.setColor…

                  и моментальное закрытие. Видимо это баги какие-то, а не нехватка ресурсов имхо.
                    +1
                    Раньше тоже мучался с этими вылетаниями эклипса, но вроде после того как выставлял эклипсу при запуске «eclipse -Xms=256m -Xmx=512m» например, это лечилось. Сейчас использую сборку «Eclipse IDE for Java EE Developers» и вроде таких проблем не встречал с ней.

                    Вы ведь памяти дали сожрать именно эклипсу при запуске, не компилятору? Падает он не из-за того, что вы там колбасите в своей программе, а из-за его внутренностей, это да.
                    А вообще юзайте нетбинс, в самом то деле :)
                  0
                  Если еще актуально, то какой у вас JDK версия? Если 21 без апдейтов, то апдейт JDK должен исправить праблему.
                  +1
                  Чтобы понять суть жизненного цикла активити, вообще понять, как он работает, можна написать очень простую программу. Переопределить onCreate(), onStart(), onRestart() и т.д. и при вызове этих методов показывать тосты с текстом типа «Activity was restarted»… Менять конфигурацию, нажимать Home, смотреть, что будет когда телефон в sleep уйдет. Очень интересно!
                  P.S. Рекомендую перейти в ландскейп, а потом обратно в портрет =)
                    0
                    >>onSaveInstanceState() — вызывает, если активити остановлено.
                    Наверное все-таки ВЫЗЫВАЕТСЯ а не вызывает?

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

                    Самое читаемое