Программирование мобильных устройств. Ч.1. Учебно-методическое пособие
.pdf
результатTextView.text = результат.toString()
Предусмотрите обработку возможных ошибок, таких как деление на ноль или неверный ввод от пользователя.
Проверьте работоспособность приложения, вводя различные значения и выполняя различные операции
3.1.Дополнительные теоретические сведения
Впроцессе выполнения лабораторной работы будут полезные
некоторые теоретические сведения, представленные ниже.
3.1.1. ProgressBar – Индикатор прогресса. Элемент управления
ProgressBar (Индикатор прогресса) показывает пользователю выполнение программой некоторой продолжительной работы. Помогает понять, что программа не зависла. Этот элемент находится в папке Form Widgets.
Типы ProgressBar (Android предлагает несколько видов индикаторов):
A)ProgressBar (Large)
B)ProgressBar (Normal)
C)ProgressBar (Small)
D)ProgressBar (Horizontal)
Стандартным является второй индикатор (Normal). Остальные являются модификациями, и их отличия определены в стилях с использованием XMLописания виджетов.
Методы для работы с ProgressBar:
1)setProgress(value: Int) – устанавливает заданное значение индикатора прогресса;
2)getProgress(): Int – возвращает текущее значение индикатора прогресса;
3)incrementProgressBy(increment: Int)` – устанавливает величину дискретизации приращения значения индикатора;
4)setMax(max: Int)` – устанавливает максимальное значение величины прогресса.
Примеры использования:
- Круговые индикаторы Normal и Large могут быть размещены на экране без дополнительного кода и будут бесконечно выводить анимацию вращения.
- Горизонтальный индикатор обычно используется для демонстрации индикатора в действии. При длительной операции пользователь видит, как полоска индикатора постоянно увеличивается.
3.1.2. Единицы Измерения. Исторически разработчики использовали пиксели (px) при создании компьютерных интерфейсов. Однако с развитием технологий и появлением высоких разрешений, этот подход стал проблематичным. Android поддерживает несколько стандартных единиц измерения:
- px (pixels): Пиксели, минимальные единицы измерения;
11
-dp (density-independent pixels): Независимые от плотности пиксели, абстрактная единица, основанная на физической плотности экрана с разрешением 160 dpi;
-sp (scale-independent pixels): Независимые от масштабирования пиксели, используемые для размеров шрифтов, позволяющие настройку размеров пользователем;
-in (inches): Дюймы, базируются на физических размерах экрана;
-mm (millimeters): Миллиметры, также базируются на физических размерах экрана;
-pt (points): 1/72 дюйма, также базируются на физических размерах экрана.
3.1.3. Настройка Шрифтов. Android предоставляет возможность настройки размеров шрифтов в четырех вариантах: Мелкий, Обычный, Крупный, Огромный, которые можно установить в настройках устройства. Для установки размера шрифта в XML файле используйте атрибут `android:textSize` с указанием размера в `sp` или `dp`.
Пример использования `sp`:
<TextView
android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Текст с размером 26sp" android:textSize="26sp" />
Использование системных размеров шрифтов:
<TextView
android:id="@+id/textView3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Пример текста. textAppearanceSmall"
android:textAppearance="?android:attr/textAppearanceSmall" />
3.1.4. TextView в Android: Отображение Текста и Настройки. Виджет
TextView предназначен для отображения текста без возможности редактирования, часто используется для представления описательного текста. Находится в папке Form Widgets под четырьмя вариантами: TextView, Large Text,
Medium Text, Small Text.
Для отображения текста в TextView в XML файле используйте атрибут `android:text`. Рекомендуется использовать текстовые ресурсы:
android:text="@string/hello"
Текст можно устанавливать программно с использованием метода
`setText()`:
12
TextView text = findViewById(R.id.text1); text.setText("Hello, Android!");
3.1.5. Обработка Событий Нажатия на Кнопку в Android. Если у вас есть кнопка на экране, нажатие на которую должно вызвать определенное действие, существует несколько способов обработки этого события:
1. Атрибут `onClick`
Пример использования в XML-разметке:
android:onClick="onMyButtonClick"
Пример реализации в коде:
public void onMyButtonClick(View view) { // Ваш код обработки нажатия
Toast.makeText(this, "Зачем вы нажали?", Toast.LENGTH_SHORT).show();
}
При использовании этого метода не нужно явно объявлять кнопку в коде, Android автоматически обработает нажатие.
2. Анонимный обработчик Этот способ позволяет более явно контролировать обработку событий и
применим для различных видов элементов.
Button myButton = findViewById(R.id.button1); myButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) { // Ваш код обработки нажатия
Toast.makeText(MainActivity.this, "Еще один способ!", Toast.LENGTH_SHORT).show();
}
});
3. Имплементация `View.OnClickListener`
Наиболее удачный вариант, если ваша activity обрабатывает события для нескольких элементов.
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
Button myButton = findViewById(R.id.button1);
13
myButton.setOnClickListener(this);
}
@Override
public void onClick(View view) { // Ваш код обработки нажатия
Toast.makeText(this, "Еще один способ!", Toast.LENGTH_SHORT).show();
}
}
4. Абстрактные классы и интерфейсы Абстрактный класс представляет собой общий шаблон поведения для
группы классов. Он содержит как абстрактные, так и конкретные методы. Абстрактные классы определяют общие методы и свойства для группы
классов, запрещено создавать экземпляры таких классов.
abstract class Shape {
// Общие методы и свойства
abstract fun calculateArea(): Double // Абстрактный метод
open fun printName() { |
// Конкретный метод с реализацией |
println("This is a shape.") |
|
} |
|
} |
|
Интерфейс определяет условия для класса, указывая, какие методы должны быть реализованы. Методы интерфейса могут быть как абстрактными, так и иметь реализацию по умолчанию.
interface Drawable {
// Методы и свойства интерфейса
fun draw() |
// Абстрактный метод |
fun show() { |
// Метод с реализацией по умолчанию |
println("Showing drawable.")
}
}
Для более глубокого понимания далее приведем небольшой сравнительный анализ интерфейса и абстрактного класса по некоторым параметрам.
Параметры |
Интерфейс |
Абстрактный класс |
|
Скорость |
Медленный |
Быстрый |
|
Наследование |
Реализация нескольких |
Только |
один |
|
интерфейсов |
абстрактный класс |
|
Структура |
Абстрактные методы |
Абстрактные |
и |
|
|
конкретные методы |
|
|
14 |
|
|
Когда использовать |
Будущие улучшения |
Избежание |
|
|
|
|
|
независимости |
|
Реализация |
Класс |
может |
Класс |
может |
|
реализовать |
несколько |
наследовать только один |
|
|
интерфейсов |
|
абстрактный класс |
|
Методы доступа |
Интерфейс |
не имеет |
Абстрактный |
класс |
|
модификаторов доступа |
может |
иметь |
|
|
|
|
модификаторы доступа |
|
15
4. Создание приложения «Вкладки»
Целью занятия является реализация приложения, состоящего из трех вкладок. Интерфейс каждой вкладки задается отдельным xml файлом. Первая вкладка содержит изображение и надпись. Вторая вкладка содержит элемент ListView с произвольным интерфейсом. Третья вкладка содержит программно создаваемый TableLayout.
Создайте новый проект в Android Studio с использованием Kotlin. Откройте макет (layout) для вашей активности с использованием `TabLayout` и `ViewPager`, которые будут использоваться для управления вкладками.
Примечание. "ViewPager" и "TabLayout" используются для создания интерфейса с вкладками в Android. "ViewPager" предоставляет прокрутку между фрагментами, а "TabLayout" – переключение между вкладками. "Fragment" в Android представляют собой модули, которые можно использовать для построения многокомпонентных пользовательских интерфейсов внутри одной активности. "PagerAdapter" – это адаптер, который предоставляет фрагменты для `ViewPager`. Он определяет, какой фрагмент должен отображаться на какой позиции.
Создайте отдельные макеты для каждой вкладки, как описано в задании:
1)Первая вкладка с изображением и надписью;
2)Вторая вкладка с `ListView`;
3)Третья вкладка с программно созданным `TableLayout`.
Создайте адаптер для `ViewPager, который будет управлять фрагментами для каждой вкладки.
class PagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) { override fun getItem(position: Int): Fragment {
return when (position) {
0 -> FirstTabFragment()
1 -> SecondTabFragment()
2 -> ThirdTabFragment()
else -> throw IllegalArgumentException("Invalid position")
}
}
override fun getCount(): Int { return 3
}
override fun getPageTitle(position: Int): CharSequence? { return when (position) {
0 -> "Вкладка 1"
1 -> "Вкладка 2"
2 -> "Вкладка 3"
16
else -> null
}
}
}
Для создания `TableLayout` программно вы можете создать экземпляр `TableLayout`, добавить строки (`TableRow`) и ячейки (`TextView` или другие элементы) в соответствующем порядке.
val tableLayout = TableLayout(context) val tableRow = TableRow(context)
val textView = TextView(context) tableRow.addView(textView) tableLayout.addView(tableRow)
Свяжите `ViewPager` с `TabLayout`, чтобы они взаимодействовали и обеспечивали переключение между вкладками.
val viewPager: ViewPager = findViewById(R.id.viewPager) val tabs: TabLayout = findViewById(R.id.tabs) viewPager.adapter = PagerAdapter(supportFragmentManager) tabs.setupWithViewPager(viewPager)
`ListView` используется для отображения списка элементов. Для связывания данных с `ListView` создайте адаптер, например, `ArrayAdapter` или собственный адаптер.
val listView: ListView = findViewById(R.id.listView)
val данные = arrayOf("Элемент 1", "Элемент 2", "Элемент 3")
val адаптер = ArrayAdapter(this, android.R.layout.simple_list_item_1,
данные)
listView.adapter = адаптер
Создайте отдельные классы фрагментов для каждой вкладки и настройте их интерфейсы и поведение.
class FirstTabFragment : Fragment() {
//Реализация для первой вкладки
}
class SecondTabFragment : Fragment() {
//Реализация для второй вкладки
}
class ThirdTabFragment : Fragment() {
// Реализация для третьей вкладки
}
17
4.1.Дополнительные теоретические сведения
Впроцессе выполнения лабораторной работы будут полезные некоторые теоретические сведения, представленные ниже.
4.1.1.Исключения в Kotlin: краткий обзор с примерами
1) Все исключения в Kotlin являются наследниками класса `Throwable`.
У каждого исключения есть сообщение, трассировка стека и, опционально, причина возникновения. Исключение можно вызвать явным образом с использованием оператора `throw`:
throw Exception("Hi There!")
Для перехвата исключений используется выражение `try...catch`:
try {
//некоторый код
}catch (e: SomeException) {
//обработчик
}finally {
//опциональный блок finally
2)Try в Kotlin является выражением и может иметь возвращаемое значение.
Возвращаемым значением является последнее выражение в блоке `try` или последнее выражение в блоках `catch`. Блок `finally` не влияет на результат `try`- выражения.
val a: Int? = try { input.toInt() } catch (e: NumberFormatException) { null }
3) Проверяемые исключения
В Kotlin нет проверяемых исключений, что избавляет от необходимости обрабатывать их в каждом месте использования. Это сделано для того, чтобы упростить код и избежать его бесполезного оборачивания в блоки `try...catch`.
4)Тип `Nothing`
Выражение `throw` в Kotlin является выражением и имеет тип `Nothing`. Используется для обозначения кода, который не может быть достигнут.
Пример использования `Nothing` в функции, которая всегда вызывает исключение:
fun fail(message: String): Nothing { throw IllegalArgumentException(message)
}
18
4.1.2. Геттеры и сеттеры
Доступ к свойствам класса осуществляется через методы геттера (getter) и сеттера (setter). Они позволяют контролировать доступ к свойствам и выполнить дополнительные действия при чтении или записи значений.
Геттеры – это специальные методы, которые вызываются при чтении значения свойства объекта. Они определяют, какое значение будет возвращено при обращении к свойству.
class Person {
val fullName: String
get() = "$firstName $lastName"
var age: Int = 0 get() {
println("Геттер вызван") return field
}
}
Здесь `fullName` представляет собой свойство, которое состоит из `firstName` и `lastName`, а `age` – просто целочисленное свойство. Обратите внимание, что `field` – это специальное ключевое слово, которое представляет собой текущее значение поля.
Сеттеры – это методы, вызываемые при записи нового значения в свойство объекта. Они позволяют контролировать и проверять новые значения перед их назначением. Ниже мы определяем сеттер для свойства `age`, который проверяет, что новый возраст неотрицательный, и только в этом случае устанавливает его.
class Person { var age: Int = 0
set(value) {
if (value >= 0) { field = value
} else {
println("Недопустимый возраст")
}
}
}
Здесь мы создаем объект `Person` и используем геттер для получения полного имени и сеттер для установки возраста. Обратите внимание, что вызов сеттера и геттера выглядит как обычное обращение к свойству.
fun main() {
val person = Person()
19
println(person.fullName) // Используется геттер person.age = 30 // Используется сеттер println(person.age) // Используется геттер
}
Иногда в сеттере или геттере может потребоваться обратиться к фактическому полю, которое они представляют. В Kotlin для этого используется ключевое слово `field`. В Kotlin можно определить пользовательские геттеры и сеттеры для свойств, позволяя выполнять дополнительные операции при доступе к ним.
class Person { var age: Int = 0
get() {
println("Геттер вызван") return field
}
set(value) { println("Сеттер вызван") field = value
}
}
Таким образом, геттеры и сеттеры обеспечивают гибкость и контроль над доступом к свойствам объектов, что делает их очень мощным инструментом при проектировании классов и работы с данными.
4.1.3. Свойства
Свойства в классах Kotlin могут быть объявлены как изменяемые (mutable) и неизменяемые (read-only) – var и val соответственно.
class Address {
var name: String = "Holmes, Sherlock" var street: String = "Baker"
var city: String = "London" var state: String? = null var zip: String = "123456"
}
Строго говоря, полный синтаксис объявления свойства выглядит следующим образом:
var <propertyName>[: <PropertyType>] [= <property_initializer>] [<getter>]
[<setter>]
20
