DRAFT [2019-2020][ua] at 2023-01-05 17:17:43 +0200
Logo-do [errata] Profile

Програмування для мобільних пристроїв

Стилі та теми

Конспект лекції


Стилі та теми

Стилі

Ми можемо налаштувати елемент за допомогою різних атрибутів, які задають висоту, ширину, колір фону, тексту і так далі. Але якщо у нас кілька

елементів використовують одні і ті ж налаштування, то ми можемо об'єднати ці налаштування в стилі.

Наприклад, нехай у нас є кілька елементів TextView:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:textSize="22sp"
android:textColor="#3f51b5"
android:text="Android Lollipop"/>

<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:textSize="22sp"
android:textColor="#3f51b5"
android:text="Android Marshmallow"/>

<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:textSize="22sp"
android:textColor="#3f51b5"
android:text="Android Nougat"/>

</LinearLayout>

 

 

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

TextView. Даний підхід не є оптимальним, а більш оптимальний підхід представляє використання стилів.

За замовчуванням при створенні проекту в Android Studio в папку res / values вже додається файл для стилів styles.xml:

Якщо такого файлу немає, то його можна додати, або можна додати ще один файл стилів. Головне щоб стилі перебували в каталозі проекту res / values

/ в файлі xml. При цьому файл може мати довільне ім'я, необов'язково styles.xml.

Наявний файл styles.xml має наступне визначення:

<resources>

<!-- Base application theme. -->

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

<!-- Customize your theme here. -->

<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>

</style>

</resources>

Стиль задається за допомогою елемента <style>. Атрибут name вказує на назву стилю, через яку потім можна посилатися на нього. Необов'язковий

атрибут parent встановлює для даного стилю батьківський стиль, від якого дочірній стиль буде наслідувати всі свої характеристики.

За допомогою елементів item встановлюються конкретні властивості віджета, який приймає в якості значення атрибута name ім'я встановлюваної

властивості.

Повернемося до нашого завдання по стилізації елементів TextView і для її вирішення змінимо файл styles.xml:

<resources>

<!-- Base application theme. -->

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

<!-- Customize your theme here. -->

<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>

</style>

<style name="TextViewStyle">

<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">50dp</item>
<item name="android:textColor">#3f51b5</item>
<item name="android:textSize">22sp</item>
<item name="android:gravity">center</item>

</style>

</resources>

Тут визначено новий стиль TextViewStyle, який за допомогою елементів item задає значення для атрибутів TextView.

Тепер застосуємо стиль, змінимо файл activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
style="@style/TextViewStyle"
android:text="Android Lollipop"/>

<TextView
style="@style/TextViewStyle"
android:text="Android Marshmallow"/>

<TextView
style="@style/TextViewStyle"
android:text="Android Nougat"/>

</LinearLayout>

Використовуючи визначення style = "@ style / TextView" текстове поле пов'язується з визначенням стилю. Підсумковий результат буде той же, що і

раніше, тільки коду стає менше. А якщо ми захочемо поміняти якісь характеристики, то досить змінити потрібний елемент item у визначенні стилю.

 

Теми

Крім застосування окремих стилів до окремих елементів, ми можемо задавати стилі для всієї програми або activity у вигляді тем.

Ми можемо самі створити тему. Однак Android вже надає кілька попередньо встановлених тем для стилізації додатків, наприклад,

Theme.AppCompat.Light.DarkActionBar і ряд інших.

Для визначення теми додатка відкриємо файл AndroidManifest.xml. У ньому ми можемо побачити наступне визначення елемента application, що

представляє додаток:

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:theme="@style/AppTheme">

Задання теми відбувається за допомогою атрибута android: theme. В даному випадку використовується ресурс, визначений в стилях - в файлі res /

values / styles.xml:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>

Стиль AppTheme використовує вбудовану тему Theme.AppCompat.Light.DarkActionBar, яка надає візуальні характеристики нашому додатку.

Тепер визначимо стиль, який використовує іншу тему:

<resources>

<style name="NoActionBarTheme" parent="Theme.AppCompat.DayNight.NoActionBar">

</style>

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

<!-- Customize your theme here. -->

<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>

</style>

<style name="TextViewStyle">

<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">50dp</item>
<item name="android:textColor">#3f51b5</item>
<item name="android:textSize">22sp</item>
<item name="android:gravity">center</item>

</style>

</resources>

Нехай новий стиль називається NoActionBarTheme, який посилається на тему Theme.AppCompat.DayNight.NoActionBar. Тепер встановимо його в якості

теми додатка в файлі AndroidManifest.xml:

<application

android:theme="@styles/NoActionBarTheme"

 

 

Створення власної теми

Замість використання вбудованих тем ми можемо створити свою. Для цього створимо в файлі res / values / styles.xml новий стиль:

<resources>

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

<!-- Customize your theme here. -->

<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>

</style>

<style name="TextViewStyle">

<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">50dp</item>
<item name="android:gravity">center</item>

</style>

<style name="CustomTheme" parent="Theme.AppCompat.Light">

<item name="android:textColor">#333333</item>
<item name="android:textSize">22sp</item>

</style>

</resources>

Отже, ми створили стиль "CustomTheme", який успадкований від стилю Theme.AppCompat.Light. У цьому стилі ми перевизначили дві властивості:

висоту шрифту (textSize) - 22sp, а також колір тексту (textColor) - він тепер світло-сірий.

Тепер визначимо цей стиль як тема додатка в файлі AndroidManifest.xml:

<application android:theme="@style/CustomTheme"

 

Редактор тем

Для спрощення визначення тем в Android Studio є вбудований графічний редактор тем. Для переходу до нього необхідно вибрати пункт меню Tools ->

Android -> Theme Editor:

Після цього відкриється редактор тим, де ми зможемо вибрати будь-яку тему і підредагувати її окремі значення, наприклад, колір:

 

Меню

Меню в додатках представляє клас android.view.Menu, і кожна activity асоціюється з об'єктом цього типу. Об'єкт android.view.Menu може включати різну

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

 

Визначення меню в xml

Меню, як і файли інтерфейсу або зображень, також являє собою ресурс. За замовчуванням файли меню знаходяться в проекті в каталозі res / menu.

При створенні нового проекту з Empty Activity у нас немає ніякого каталогу res / menu і відповідно немає ресурсів меню, але ми можемо їх додати вручну.

Для цього натиснемо правою кнопкою миші в проекті на каталог res і далі, відкривши список виберемо пункт New -> Android Resource File:

Далі у вікні вкажемо для імені файлу назва main_menu, а для типу ресурсу також виберемо Menu:

 

Після цього в каталозі res буде створений підкаталог menu, в якому буде знаходитися файл main_menu.xml.

За замовчуванням цей файл визначає один порожній елемент menu:

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android">

</menu>

Змінимо вміст файлу, визначивши кілька пунктів:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<item
android:id="@+id/action_settings" android:orderInCategory="1"
android:title="Налаштування" />

<item
android:id="@+id/save_settings" android:orderInCategory="3"
android:title="Зберегти" />

<item
android:id="@+id/open_settings" android:orderInCategory="2"
android:title="Відкрити" />

</menu>

Тег <menu> є кореневим вузлом файлу і визначає меню, що складається з одного або декількох елементів <item> і <group>.

Елемент <item> представляє об'єкт MenuItem, який є одним з елементів меню. Цей елемент може містити внутрішні піделементи <menu>, за допомогою

якого створюється підменю.

Елемент <item> включає наступні атрибути, які визначають його зовнішній вигляд і поведінку:

 

Наповнення меню елементами

Ми визначили меню з трьома елементами, але саме визначення елементів у файлі ще не створює меню. Це всього лише декларативний опис. Щоб

вивести його на екран, нам треба використовувати його в класі Activity. Для цього треба перевизначити метод onCreateOptionsMenu. Отже, перейдемо

до класу MainActivity і змінимо його наступним чином:

package com.example.menuapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.view.Menu;

public class MainActivity extends AppCompatActivity {
  @Override

  protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

  }

  @Override

  public boolean onCreateOptionsMenu(Menu menu) {

    getMenuInflater().inflate(R.menu.main_menu, menu);
    return true;

  }

}

Метод getMenuInflater отримує об'єкт MenuInflater і викликаємо його метод inflate (). Цей метод в якості першого параметра приймає ресурс, який

представляє наш декларативний опис меню в xml, і наповнює їм об'єкт menu, переданий в якості другого параметра. Запустимо додаток за

замовчуванням і натиснемо на кнопку меню в правому верхньому куті:

 

 

Обробка натискань в меню

Якщо ми натиснемо на будь-який з пунктів меню, то нічого не станеться. Щоб прив'язати до меню дії, нам треба перевизначити в класі activity

onOptionsItemSelected.

Для виведення обраного елемента меню в файлі activity_main.xml визначимо текстове поле з id = header:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp" />

</RelativeLayout>

І змінимо клас MainActivity:

package com.example.menuapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
  @Override

  protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

  }

  @Override

  public boolean onCreateOptionsMenu(Menu menu) {

    getMenuInflater().inflate(R.menu.main_menu, menu);
    return true;

  }

  @Override

  public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    TextView headerView = (TextView) findViewById(R.id.header);
    switch (id) {

    case R.id.action_settings:

      headerView.setText("Налаштування");
      return true;

    case R.id.open_settings:

      headerView.setText("Відкрити");
      return true;

    case R.id.save_settings:

      headerView.setText("Зберегти");
      return true;

    }

    return super.onOptionsItemSelected(item);

  }

}

Щоб зрозуміти, який пункт меню обраний, спочатку отримуємо його ідентифікатор int id = item.getItemId (). Потім проходимо в конструкції switch..case і

вибираємо потрібний варіант і в залежності від вибору робимо певні дії - в даному випадку встановлюємо текст TextView.

 

Групи, підменю і програмне створення меню. Створення підменю

Для створення підменю в файлі розмітки меню визначимо внутрішній елемент menu:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<item
android:id="@+id/action_settings" 
android:title="Налаштування">

<menu>
<item android:id="@+id/save_settings" 
android:title="Зберегти" />

<item 
android:id="@+id/open_settings"
android:title="Відкрити" />

</menu>

</item>

<item
android:id="@+id/action_move" 
android:title="Перехід">

<menu>

<item 
android:id="@+id/forward" 
android:title="Вперед" />

<item a
ndroid:id="@+id/back" 
android:title="Назад" />

</menu>

</item>

</menu>

Після натискання на меню відобразяться елементи верхнього рівня, після натискання на які ми можемо перейти до підменю:

 

Групи в меню

Використання елемента group дозволяє оформити елементи меню в групу:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<group android:checkableBehavior="single">

<item
android:id="@+id/action_settings"
android:title="Налаштування"
android:checked="true" />

<item
android:id="@+id/save_settings"
android:title="Зберегти" />

<item
android:id="@+id/open_settings"
android:title="Відкрити" />

</group>

</menu>

У визначенні групи ми можемо встановити атрибут android: checkableBehavior. Цей атрибут може набувати таких значень: single (у кожного елемента

створюється радіокнопка), all (для кожного елемента створюється прапорець) і none.

В даному випадку для кожного елемента буде створюватися радіокнопка. І для першого елемента встановлюється зазначена радіокнопки (android:

checked = "true").

У файлі розмітки інтерфейсу activity_main.xml також нехай буде визначено текстове поле:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp" />

</RelativeLayout>

А в класі MainActivity визначимо виділення радиокнопки у обраного пункту меню:

package com.example.menuapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
  @Override

  protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

  }

  @Override

  public boolean onCreateOptionsMenu(Menu menu) {

    getMenuInflater().inflate(R.menu.main_menu, menu);
    return true;

  }

  @Override

  public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    TextView headerView = (TextView) findViewById(R.id.header);
    if (!item.isChecked()) item.setChecked(true);

    switch (id) {

    case R.id.action_settings:

      headerView.setText("Налаштування");
      return true;

    case R.id.open_settings:

      headerView.setText("Відкрити");
      return true;

    case R.id.save_settings:

      headerView.setText("Зберегти");
      return true;

    }

    return super.onOptionsItemSelected(item);

  }

}

 

Програмне створення меню

Крім визначення елементів меню в xml, можна також створити меню програмним способом.

Для додавання нових пунктів меню використовується метод add.

Отже, змінимо код MainActivity:

package com.example.menuapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
  @Override

  protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

  }

  @Override

  public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);

    menu.add(0 // Група

      , 1 // id

      , 0 // порядок

      , "Створити"); // Заголовок

    menu.add(0, 2, 1, "Відкрити");

    menu.add(0, 3, 2, "Зберегти");
    return true;

  }

  @Override

  public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    TextView headerView = (TextView) findViewById(R.id.header);

    switch (id) {

    case 1:

      headerView.setText("Створити документ");
      return true;

    case 2:

      headerView.setText("Відкрити документ");
      return true;

    case 3:

      headerView.setText("Зберегти документ");
      return true;

    }

    // можна було й так зробити

    //String title = item.getTitle().toString();

    // headerView.setText(title);

    return super.onOptionsItemSelected(item);

  }

}

Метод add додає пункт в меню, приймаючи такі параметри: номер групи, id, порядок елемента в меню і заголовок елемента.

 

Фрагменти

Введення у фрагменти

Організація додатків на основі декількох activity не завжди може бути оптимальною. Світ ОС Android досить сильно фрагментований і складається з

багатьох пристроїв. І якщо для мобільних апаратів з невеликими екранами взаємодія між різними activity виглядає досить непогано, то на великих

екранах - планшетах, телевізорах вікна activity виглядали б не дуже в силу великого розміру екрана. Власне тому і з'явилася концепція фрагментів.

Фрагмент існує в контексті activity і має свій життєвий цикл, поза activity одноосібно він існувати не може. Кожна activity може мати кілька фрагментів.

Для початку роботи з фрагментами створимо новий проект з порожньою MainActivity. І спочатку створимо перший фрагмент.

По-перше, фрагменти містять ті ж елементи управління, що і activity. Тому створимо в папці res / layout новий файл fragment_content.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<Button
android:id="@+id/updateButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Оновити" />

<TextView
android:id="@+id/textBox"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

</LinearLayout>

Тут визначені кнопка і текстове поле, які будуть складати інтерфейс фрагмента.

Тепер створимо сам клас фрагмента. Для цього додамо в одну папку з MainActivity новий клас. Для цього натиснемо на папку правою кнопкою миші і

виберемо в меню New -> Java Class. Назвемо новий клас ContentFragment і визначимо у нього такий зміст:

package com.example.fragmentapp;

import android.view.View;

import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.os.Bundle;

import android.support.v4.app.Fragment;
import android.widget.Button;

import android.widget.TextView;
import java.util.Date;

public class ContentFragment extends Fragment {
  @Override

  Public View onCreateView(LayoutInflater inflater, ViewGroup container,

    Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.fragment_content, container, false);

    Button updateButton = (Button) view.findViewById(R.id.updateButton);
    final TextView updateBox = (TextView) view.findViewById(R.id.textBox);

    updateButton.setOnClickListener(new View.OnClickListener() {
      @Override

      public void onClick(View v) {

        String curDate = New Date().toString();
        updateBox.setText(curDate);

      }

    });

    return view;

  }

}

Клас фрагмента повинен успадковуватися від класу Fragment.

Для створення візуального інтерфейсу фрагмент перевизначає батьківський метод onCreateView (). Він приймає три параметри:

Для створення інтерфейсу застосовується метод inflate () об'єкта LayoutInflater. Він отримує ресурс розмітки layout для даного фрагмента, контейнер, в

який буде укладений інтерфейс, і третій булевий параметр вказує, чи треба прикріплювати розмітку до контейнера з другого параметра.

І як і в activity, тут ми можемо встановлювати обробники для елементів управління, обробляти їх події. В даному випадку в текстовому полі виводиться

поточна дата.

Також варто відзначити, що в Android Studio є спеціальний шаблон для додавання фрагмента. Для його використання також треба натиснути на папку

пакета класів правою кнопкою миші і через меню, що випадає вибрати New -> Fragment -> Fragment (Blank):

Даний шаблон відразу додасть в проект і клас фрагмента і пов'язаний з ним клас розмітки інтерфейсу.

І в кінці треба додати фрагмент в activity. Для цього змінимо файл activity_main.xml, який визначає інтерфейс для MainActivity:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<fragment
android:id="@+id/listFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.fragmentapp.ContentFragment"/>

</RelativeLayout>

Кожен фрагмент задається за допомогою елемента <fragment>. Для кожного фрагмента має бути встановлено висота, ширина, id, а також ім'я. Як ім'я

встановлюється повне ім'я класу з урахуванням пакета: android: name = "com.example.fragmentapp.ContentFragment"

Крім фрагмента ми можемо додати в розмітку activity_main.xml ми можемо додати інші елементи або фрагменти, але в даному випадку обмежимося

одним фрагментом.

Код класу MainActivity залишається тим же, що і при створенні проекту:

package com.example.fragmentapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
  @Override

  protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

  }

}

Весь проект буде виглядати наступним чином:

 

Якщо ми запустимо додаток, то ми побачимо фактично той же самий інтерфейс, який ми могли б зробити і через activity, тільки в даному випадку

інтерфейс буде визначено у фрагменті:

Додавання фрагмента в коді

Крім визначення фрагмента в xml-файлі інтерфейсу ми можемо додати його динамічно в activity.

Для цього змінимо файл activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/container">

</RelativeLayout>

І також змінимо клас MainActivity:

package com.example.fragmentapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

  @Override

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if (savedInstanceState == null) {
      getSupportFragmentManager().beginTransaction()

        .add(R.id.container, new ContentFragment())

        .commit();

    }

  }

}

Метод getSupportFragmentManager () повертає об'єкт FragmentManager, який управляє фрагментами. Але в даному випадку треба зробити уточнення. В

даному коді використовується фрагмент - спадкоємець класу android.support.v4.app.Fragment. Але для створення фрагментів ми також можемо

використовувати клас android.app.Fragment з API 11. У разі використання android.app.Fragment менеджер фрагментів викликається за допомогою

методу getFragmentManager ().

Об'єкт FragmentManager за допомогою методу beginTransaction () створює об'єкт FragmentTransaction.

FragmentTransaction виконує два методи: add () і commit (). Метод add () додає фрагмент: add (R.id.container, new ContentFragment ()) - першим

аргументом передається ресурс розмітки, в який треба додати фрагмент. І метод commit () підтверджує і завершує операцію додавання.

Підсумковий результат такого додавання фрагмента буде тим же, що і при явному визначенні фрагмента через елемент <fragment> в розмітці

інтерфейсу.

 

Взаємодія між фрагментами

Одна activity може використовувати кілька фрагментів, наприклад, з одного боку список, а з іншого - детальний опис вибраного елементу списку. У такій

конфігурації activity використовує два фрагмента, які між собою повинні взаємодіяти. Розглянемо базові принципи взаємодії фрагментів в додатку.

Створимо новий проект з порожньою MainActivity. Далі створимо розмітку layout для фрагментів. Нехай у нас в додатку буде два фрагмента. Додамо в

папку res / layout новий xml-файл fragment_list.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<Button
android:id="@+id/update_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Оновити" />

</LinearLayout>

Розмітка інтерфейсу для фрагмента виглядає точно також, як і стандартна для activity з тими ж контейнерами і елемента управління.

І також додамо для іншого фрагмента файл розмітки fragment_detail.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<TextView
android:id="@+id/detailsText"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_marginTop="16dp"
android:text="Привіт світ"
android:textSize="20sp" />

</LinearLayout>

Обидва фрагмента будуть гранично простими: один буде містити кнопку, а другий - текстове поле.

Потім додамо в проект в одну папку з MainActivity власне класи фрагментів. Додамо новий клас ListFragment наступного змісту:

package com.example.fragmentapp;

import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;

import android.view.LayoutInflater;
import android.view.View;

import android.view.ViewGroup;
import android.widget.Button;
import java.util.Date;

public class ListFragment extends Fragment {

  private OnFragmentInteractionListener mListener;
  @Override

  Public View onCreateView(LayoutInflater inflater, ViewGroup container,

    Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.fragment_list, container, false);
    Button button = (Button) view.findViewById(R.id.update_button);

    // задаємо обробник кнопки 
    button.setOnClickListener(new View.OnClickListener() {

    @Override

    public void onClick(View v) {
      updateDetail();

    }

  });

return view;

}

interface OnFragmentInteractionListener {

  void onFragmentInteraction(String link);

}

@Override

public void onAttach(Context context) {
  super.onAttach(context);

  try {

    mListener = (OnFragmentInteractionListener) context;

  } catch (ClassCastException e) {

    throw new ClassCastException(context.toString()

      +
      "має реалізовувати інтерфейс OnFragmentInteractionListener");

  }

}

public void updateDetail() {

  // генеруємо деякі дані
  String curDate = New Date().toString();

  // Надсилаємо дані Activity
  mListener.onFragmentInteraction(curDate);

}

}

Тут визначається обробник натискання кнопки - в даному випадку метод updateDetail, завдання якого - взаємодія з другим фрагментом.

Фрагменти не можуть безпосередньо взаємодіяти між собою. Для цього треба звертатися до контексту, в якості якого виступає клас Activity. Для

звернення до activity, як правило, створюється вкладений інтерфейс. В даному випадку він називається OnFragmentInteractionListener з одним методом.

При обробці натискання кнопки в метод updateDetail () передається в цей метод деяке строкове значення:

private OnFragmentInteractionListener mListener;

public void updateDetail() {

  String curDate = new Date().toString();
  mListener.onFragmentInteraction(curDate);

}

Але щоб взаємодіяти з іншим фрагментом через activity, нам треба прикріпити поточний фрагмент до activity. Для цього в класі фрагмента визначено

метод onAttach (Context context). У ньому відбувається установка об'єкта OnFragmentInteractionListener:

mListener = (OnFragmentInteractionListener) context;

Тепер визначимо клас для другого фрагмента. Назвемо його DetailFragment:

package com.example.fragmentapp;

import android.app.Fragment;
import android.os.Bundle;

import android.view.LayoutInflater;
import android.view.View;

import android.view.ViewGroup;
import android.widget.TextView;

public class DetailFragment extends Fragment {
  @Override

  Public View onCreateView(LayoutInflater inflater, ViewGroup container,

    Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.fragment_detail, container, false);
    return view;

  }

  // Овлення текстового поля 
  public void setText(String item) {

  TextView view = (TextView) getView().findViewById(R.id.detailsText);
  view.setText(item);

}

}

Завдання цього фрагмента - виведення деякої інформації. Так як він не повинен передавати ніяку інформацію іншому фрагменту, тут ми можемо

обмежитися тільки перевизначенням методу onCreateView (), який в якості візуального інтерфейсу встановлює розмітку з файлу fragment_detail.xml

Але щоб імітувати взаємодію між двома фрагментами, тут також визначено метод setText (), який оновлює текст на текстовому полі.

У підсумку вийде наступна структура:

 

Тепер змінимо файл розмітки activity_main.xml:

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >

<fragment
android:id="@+id/listFragment"
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="match_parent"
android:name="com.example.fragmentapp.ListFragment"/>

<fragment
android:id="@+id/detailFragment"
android:layout_width="wrap_content"
android:layout_weight="2"
android:layout_height="match_parent"
android:name="com.example.fragmentapp.DetailFragment"/>

</LinearLayout>

І в кінці змінимо код MainActivity:

package com.example.fragmentapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity implements ListFragment.OnFragmentInteractionListener {

  @Override

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

  }

  @Override

  public void onFragmentInteraction(String link) {

    DetailFragment fragment = (DetailFragment) getFragmentManager()

      .findFragmentById(R.id.detailFragment);
    if (fragment != null && fragment.isInLayout()) {

      fragment.setText(link);

    }

  }

}

Для взаємодії фрагмента ListFragment з іншим фрагментом через MainActivity треба, щоб ця activity реалізовувала інтерфейс

OnFragmentInteractionListener. Для цього реалізуємо метод onFragmentInteraction (), який отримує фрагмент DetailFragment і викликає у ньому метод

setText ()

У підсумку вийде, що при натисканні кнопки на фрагменті ListFragment буде спрацьовувати метод updateDetail (), який викличе метод

mListener.onFragmentInteraction (newTime). mListener встановлюється як activity, тому при цьому буде викликаний метод setText у фрагмента

DetailFragment. Таким чином, відбудеться взаємодія між двома фрагментами.

Якщо ми запустимо проект, то на екран будуть виведені обидва фрагмента, які зможуть взаємодіяти між собою.

Однак поки фрагменти однаково виводяться в одній activity як в альбомній, так і в портретній орієнтації незалежно від пристрою. Тому оптимізуємо

додаток.

 

Фрагменти в альбомному і портретному режимі

У минулій темі було розроблено програму, яка виводить обидва фрагмента на екран. Але відображення двох і більше фрагментів при портретній

орієнтації не дуже оптимально.

Створимо в проекті в папці res, де зберігаються всі ресурси, підкаталог layout-port, який буде зберігати файли інтерфейсу для портретної орієнтації.

Додамо в нього файл activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<fragment
android:id="@+id/listFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.fragmentapp.ListFragment"/>

</LinearLayout>

Цей файл буде використовуватися для портретної орієнтації MainActivity. І також додамо в цей же каталог res / layout-port ще один файл

activity_detail.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<fragment
android:id="@+id/detailFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.example.fragmentapp.DetailFragment" />

</LinearLayout>

Це файл інтерфейсу для нової activity, яку зараз створимо. Тепер додамо нову activity, яку назвемо DetailActivity:

package com.example.fragmentapp;

import android.content.res.Configuration;
import android.os.Bundle;

import android.support.v7.app.AppCompatActivity;

public class DetailActivity extends AppCompatActivity {
  public static final String EXTRA_URL = "url";
  @Override

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)

    {

      finish();
      return;

    }

    setContentView(R.layout.activity_detail);
    Bundle extras = getIntent().getExtras();
    if (extras != null) {

      String url = extras.getString(EXTRA_URL);

      DetailFragment detailFragment = (DetailFragment) getFragmentManager()

        .findFragmentById(R.id.detailFragment);
      detailFragment.setText(url);

    }

  }

}

Тут в першу чергу перевіряємо конфігурацію. Так як ця activity призначена тільки для портретного режиму, то при альбомної орієнтації здійснюємо вихід:

if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
  finish();

  return;

}

Інакше отримуємо через метод getFragmentManager () фрагмент DetailFragment і викликаємо його метод setText (). Як аргумент на цей метод

передається строкове значення, передане через Intent.

І також змінимо головну activity - MainActivity:

package com.example.fragmentapp;

import android.content.Intent;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity

implements ListFragment.OnFragmentInteractionListener {

  @Override

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

  }

  @Override

  public void onFragmentInteraction(String link) {

    DetailFragment fragment = (DetailFragment) getFragmentManager()

      .findFragmentById(R.id.detailFragment);
    if (fragment != null && fragment.isInLayout()) {

      fragment.setText(link);

    } else {

      Intent intent = new Intent(getApplicationContext(), DetailActivity.class);

      intent.putExtra(DetailActivity.EXTRA_URL, link);
      startActivity(intent);

    }

  }

}

За допомогою методу fragment.isInLayout () ми можемо дізнатися, чи бере участь певний фрагмент в розмітці інтерфейсу. Якщо фрагмента

DetailFragment не визначено, то використовується портретний режим, і тому запускається DetailActivity. Інакше йде робота з фрагментом всередині

MainActivity, котра в альбомному режимі відображає одразу два фрагмента - ListFragment і DetailFragment. У результаті проект виглядатиме так:

 

Запустимо програму і перейдемо в альбомний режим:

А при портретній орієнтації екран буде виглядати інакше:

 

Життєвий цикл і типи фрагментів

Кожен клас фрагмента успадковується від базового класу Fragment і має свій життєвий цикл, що складається з ряду етапів:

метод findViewById ()

У коді класу фрагмента ми можемо перевизначити всі або частину з цих методів.

Оскільки фрагменти часто використовуються для певних цілей, наприклад, для виведення списку якихось об'єктів, то за замовчуванням нам доступні

класи, похідні від Fragment, які вже володіють певними можливостями:

Для створення нового фрагмента можна використовувати звичайні класи. Однак середовище Android Studio вже пропонує ряд вбудованих шаблонів:

При створенні буде запропоновано встановити ім'я проекту, назву файлу розмітки інтерфейсу і ще ряд опцій:

Згенерований Android Studio клас фрагмента буде багато в чому аналогічний тим, що раніше використовувалися:

package com.example.testapp;

import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.app.Fragment;

import android.view.LayoutInflater;
import android.view.View;

import android.view.ViewGroup;

public class BlankFragment extends Fragment {

  private static final String ARG_PARAM1 = "param1";
  private static final String ARG_PARAM2 = "param2";

  private String mParam1;
  private String mParam2;

  private OnFragmentInteractionListener mListener;

  // Фабрика до створення фрагмента
  public static BlankFragment newInstance(String param1, String param2) {
    BlankFragment fragment = new BlankFragment();

    Bundle args = New Bundle();
    args.putString(ARG_PARAM1, param1);
    args.putString(ARG_PARAM2, param2);
    fragment.setArguments(args);
    return fragment;

  }

  public BlankFragment() {

    // Конструктор

  }

  @Override

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (getArguments() != null) {

      mParam1 = getArguments().getString(ARG_PARAM1);
      mParam2 = getArguments().getString(ARG_PARAM2);

    }

  }

  @Override

  Public View onCreateView(LayoutInflater inflater, ViewGroup container,

    Bundle savedInstanceState) {

    return inflater.inflate(R.layout.fragment_blank, container, false);

  }

  public void onButtonPressed(Uri uri) {
    if (mListener != null) {

      mListener.onFragmentInteraction(uri);

    }

  }

  @Override

  public void onAttach(Activity activity) {
    super.onAttach(activity);

    try {

      mListener = (OnFragmentInteractionListener) activity;

    } catch (ClassCastException e) {

      throw new ClassCastException(activity.toString()

        +
        "must implement OnFragmentInteractionListener");

    }

  }

  @Override

  public void onDetach() {
    super.onDetach();
    mListener = null;

  }

  public interface OnFragmentInteractionListener {

    public void onFragmentInteraction(Uri uri);

  }

}

Тому ми можемо створювати фрагменти вручну, або використовувати один з шаблонів, що надається Android Studio.

 

Багатопоточність та асинхронність. Клас AsyncTask

Коли ми запускаємо додаток на Android, система створює потік, який називається основним потоком додатка або UI-потік. Цей потік обробляє всі зміни і

події призначені для користувача інтерфейсу. Однак для допоміжних операцій, таких як відправка або завантаження файлу, тривалі обчислення і т.д., ми

можемо створювати додаткові потоки.

Для створення нових потоків нам доcтупний стандартний функціонал класу Thread з базової бібліотеки Java з пакета java.util.concurrent. Але

використання цього класу має обмеження - ми не можемо з вторинного потоку змінювати призначений для користувача інтерфейс.

Для вирішення цієї проблеми в Android SDK є клас AsyncTask Щоб використовувати AsyncTask, нам треба:

Отже, створимо найпростіший додаток з використанням AsyncTask. Визначимо наступну розмітку інтерфейсу в файлі activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="16dp"
android:orientation="vertical">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp"
android:id="@+id/clicksView"
android:text="Clicks: 0"/>

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/clicksBtn"
android:text="Click" />

</LinearLayout>

<Button
android:id="@+id/progressBtn"
android:text="Запуск"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<TextView
android:id="@+id/statusView"
android:text="Статус"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<ProgressBar
android:id="@+id/indicator"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"
android:progress="0" />

</LinearLayout>

Тут визначена кнопка для запуску фонового потоку, а також текстове поле і прогрессбар для індикації виконання завдання. Крім того, тут визначені

додаткова кнопка, яка збільшує кількість кліків, і текстове поле, котре виводить число кліків.

Далі визначимо в класі MainActivity наступний код:

import android.os.AsyncTask;
import android.os.SystemClock;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.view.View;
import android.widget.Button;

import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
  int[] integers = null;

  int clicks = 0;

  ProgressBar indicatorBar;
  TextView statusView;
  TextView clicksView;
  Button progressBtn;
  button clicksBtn;
  @Override

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    integers = new int[100];
    for (int i = 0; i < 100; i++) {
      integers[i] = i + 1;

    }

    indicatorBar = (ProgressBar) findViewById(R.id.indicator);
    statusView = (TextView) findViewById(R.id.statusView);
    progressBtn = (Button) findViewById(R.id.progressBtn);
    progressBtn.setOnClickListener(new View.OnClickListener() {

      @Override

      public void onClick(View v) {

        new ProgressTask().execute();

      }

    });

    clicksView = (TextView) findViewById(R.id.clicksView);
    clicksBtn = (Button) findViewById(R.id.clicksBtn);
    clicksBtn.setOnClickListener(new View.OnClickListener() {

      @Override

      public void onClick(View v) {

      }

    });

  }

  clicks++;

  clicksView.setText("Clicks: " + clicks);

  class ProgressTask extends AsyncTask < Void, Integer, Void > {
    @Override

    protected Void doInBackground(Void...unused) {
      for (int i = 0; i < integers.length; i++) {

        publishProgress(i);
        SystemClock.sleep(400);

      }

      return (null);

    }

    @Override

    protected void onProgressUpdate(Integer...items) {
      indicatorBar.setProgress(items[0] + 1);

      statusView.setText("Статус:" + String.valueOf(items[0] + 1));

    }

    @Override

    protected void onPostExecute(Void unused) {

      Toast.makeText(getApplicationContext(), "Завдання завершено", Toast.LENGTH_SHORT)

        .show();

    }

  }

}

Клас завдання ProgressTask визначено як внутрішній клас. Він успадковується не просто від AsyncTask, а від його типізованої версії AsyncTask <Void,

Integer, Void>. Вона Типізуються трьома типами:

Ці типи можуть бути представлені різними класами. В даному випадку сутність завдання полягатиме в переборі масиву integers, що представляє набір

елементів Integer. І тут нам не треба передавати в задачу ніякого об'єкта, тому перший тип йде як Void.

Для індикації перебору використовуються цілі числа, які показують, який об'єкт з масиву ми в даний момент перебираємо. Тому в якості другого типу

використовується Integer.

В якості третього типу використовується знову Void, оскільки в даному випадку не треба нічого повертати з завдання.

AsyncTask містить чотири методи, які можна перевизначити:

Так як метод doInBackground () не приймає нічого і не повертає нічого, то в якості його параметра використовується Void ... - масив Void, і в якості

повертається тип - теж Void. Ці типи відповідають першому і третьому типам в AsyncTask <Void, Integer, Void>.

Метод doInBackground () перебирає масив і при кожній ітерації повідомляє систему за допомогою методу publishProgress (item). Так як в нашому випадку

для індикації використовувалися цілі числа, то параметр item повинен представляти ціле число.

Метод onProgressUpdate (Integer ... items) отримує передане вище число і застосовує його для установки текстового поля та прогрессбар.

Метод onPostExecute () виконується після завершення завдання і як параметр приймає об'єкт, що повертається методом doInBackground () - тобто в

даному випадку об'єкт типу Void. Щоб сигналізувати закінчення роботи, тут виводиться на екран спливаюче повідомлення.

Запустимо програму. Запустимо завдання, натиснувши на кнопку:

При цьому поки виконується завдання, ми можемо паралельно натискати на другу кнопку і збільшувати число кліків, або виконувати якусь іншу роботу в

додатку.

 

AsyncTask і фрагменти

При використанні AsyncTask слід враховувати наступний момент. Більш оптимальним способом є робота AsyncTask з фрагментом, ніж безпосередньо з

activity. Наприклад, якщо ми візьмемо проект з минулої теми, запустимо програму і змінимо орієнтацію мобільного пристрою, то станеться

перестворення activity. У разі зміни орієнтації пристрою потік AsyncTask буде продовжувати звертатися до старої activity, замість нової. Тому в цьому

випадку краще використовувати фрагменти.

Отже, візьмемо проект з минулого теми і додамо в нього новий фрагмент, який назвемо ProgressFragment.

Визначимо для нього новий файл розмітки інтерфейсу fragment_progress.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_progress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="16dp"
android:orientation="vertical">

<Button
android:id="@+id/progressBtn"
android:text="Запуск"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<TextView
android:id="@+id/statusView"
android:text="Статус"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<ProgressBar
android:id="@+id/indicator"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"
android:progress="0" />

</LinearLayout>

Сам клас фрагмента ProgressFragment змінимо наступним чином:

package com.example.asyncapp;

import android.widget.Button;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Fragment;
import android.os.SystemClock;

import android.view.LayoutInflater;
import android.view.View;

import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import android.view.View.OnClickListener;
public class ProgressFragment extends Fragment {

  int[] integers = null;
  ProgressBar indicatorBar;
  TextView statusView;
  @Override

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true);

  }

  @Override

  Public View onCreateView(LayoutInflater inflater, ViewGroup container,

    Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.fragment_progress, container, false);
    integers = new int[100];

    for (int i = 0; i < 100; i++) {
      integers[i] = i + 1;

    }

    indicatorBar = (ProgressBar) view.findViewById(R.id.indicator);
    statusView = (TextView) view.findViewById(R.id.statusView);
    Button btnFetch = (Button) view.findViewById(R.id.progressBtn);
    btnFetch.setOnClickListener(new OnClickListener() {

      @Override

      public void onClick(View v) {

        new ProgressTask().execute();

      }

    });

    return view;

  }

  class ProgressTask extends AsyncTask < Void, Integer, Void > {
    @Override

    protected Void doInBackground(Void...unused) {
      for (int i = 0; i < integers.length; i++) {

        publishProgress(i);
        SystemClock.sleep(400);

      }

      return null;

    }

    @Override

    protected void onProgressUpdate(Integer...items) {
      indicatorBar.setProgress(items[0] + 1);

      statusView.setText("Статус:" + String.valueOf(items[0] + 1));

    }

    @Override

    protected void onPostExecute(Void unused) {

      Toast.makeText(getActivity(), "Завдання завершено", Toast.LENGTH_SHORT)

        .show();

    }

  }

}

Тут визначені всі ті дії, які були розглянуті в минулій темі і які раніше перебували в класі MainActivity. Особливо варто відзначити виклик setRetainInstance

(true) в методі onCreate (), який дозволяє зберігати стан фрагмента незалежно від зміни орієнтації.

Тепер зв'яжемо фрагмент з activity. Для цього визначимо в файлі activity_main.xml наступний код:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<fragment
android:id="@+id/progressFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.asyncapp.ProgressFragment"/>

</LinearLayout>

А сам клас MainActivity скоротимо:

package com.example.asyncapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
  @Override

  protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

  }

}

Тепер якщо ми запустимо додаток, то незалежно від зміни орієнтації моільного пристрою фонове завдання буде продовжувати свою роботу:


© 2006—2023 СумДУ