Визначення розмірів

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

Фіксовані значення

Фіксовані значення розмірів вказуються у вигляді чисел з одиницями вимірювання. Основні одиниці:

  • px — пікселі поточного екрана. Однак ця одиниця виміру не рекомендується, оскільки реальне представлення зовнішнього вигляду може змінюватися залежно від пристрою; кожен пристрій має певний набір пікселів на дюйм, тому кількість пікселів на екрані може також змінюватися.
  • dp або dip — density-independent pixels (пікселі, незалежні від щільності). Абстрактна одиниця виміру, заснована на фізичній щільності екрана з роздільною здатністю 160 dpi (точок на дюйм). У цьому випадку 1dp = 1px. Якщо розмір екрана більший або менший за 160 dpi, кількість пікселів, що застосовуються для відтворення 1dp відповідно збільшується або зменшується. Наприклад, на екрані з 240 dpi 1dp=1,5px, а на екрані з 320dpi 1dp=2px. Загальна формула для отримання кількості фізичних пікселів із dp: px = dp * (dpi / 160)
  • sp — scale-independent pixels (незалежні від масштабування пікселі.). Допускають налаштування розмірів, здійснюване користувачем. Рекомендуються для роботи зі шрифтами.
  • mm — міліметри
  • in — дюйми

Наприклад, встановлення ширини елемента в 150 пікселів виглядає так:

android:layout_width="150px"

Проте, використання пікселів (px) не є рекомендованим, оскільки вони не адаптуються до різних екранів. Краще використовувати dp для розмірів та sp для тексту.

Кращими одиницями для використання є dp. Це пов'язано з тим, що світ мобільних пристроїв на Android сильно фрагментований у плані роздільної здатності та розмірів екрана. І що більша щільність пікселів на дюйм, то відповідно більше пікселів нам буде доступно:

Гнучкість у дизайні Android

Використовуючи ж стандартні фізичні пікселі, ми можемо зіткнутися з проблемою, що розміри елементів також будуть сильно варіюватися залежно від щільності пікселів пристрою. Наприклад, візьмемо 3 пристрої з різними характеристиками екрана Nexus 4, Nexus 5X і Nexus 6P і виведемо на екран квадрат розміром 300px на 300px: Фізичні пікселі в Android В одному випадку квадрат за шириною займатиме 40%, в іншому - третину ширини, у третьому - 20%.

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

Для спрощення роботи з розмірами всі розміри розбиті на кілька груп:

  • ldpi (low): ~120dpi
  • mdpi (medium): ~160dpi
  • hdpi (high): ~240dpi (до цієї групи можна віднести такий давній пристрій як Nexus One)
  • xhdpi (extra-high): ~320dpi (Nexus 4)
  • xxhdpi (extra-extra-high): ~480dpi (Nexus 5/5X, Samsung Galaxy S5)
  • xxxhdpi (extra-extra-extra-high): ~640dpi (Nexus 6/6P, Samsung Galaxy S6)

Встановлення розмірів

Основна проблема, пов'язана з розмірами, пов'язана з їх встановленням у коді Java. Наприклад, деякі методи приймають як значення фізичні пікселі, а не density-independent pixels. У цьому випадку може знадобитися перевести значення з одного типу одиниць в інший. Для цього потрібно застосувати метод TypedValue.applyDimension(), який приймає три параметри:

public static float applyDimension(int unit,
                                   float value,
                                   android.util.DisplayMetrics metrics)

Параметр unit представляє тип одиниць, з якої треба отримати значення в пікселях. Тип одиниць описується однією з констант TypedValue:

  • COMPLEX_UNIT_DIP - dp або незалежні від щільності екрана пікселі
  • COMPLEX_UNIT_IN - in або дюйми
  • COMPLEX_UNIT_MM - mm або міліметри
  • COMPLEX_UNIT_PT - pt або точки
  • COMPLEX_UNIT_PX - px або фізичні пікселі
  • COMPLEX_UNIT_SP - sp або незалежні від масштабування пікселі (scale-independent pixels) Параметр value представляє значення, яке треба перетворити. Параметр metrics представляє інформацію про метрику, в рамках якої треба виконати перетворення.

У підсумку метод повертає перетворене значення. Розглянемо абстрактний приклад. Наприклад, нам треба отримати з 60dp звичайні фізичні пікселі:

int valueInDp = 60;
int valueInPx = (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP, valueInDp, getResources().getDisplayMetrics());

Як третій аргумент передається виклик методу getResources().getDisplayMetrics(), який дає змогу отримати інформацію про метрику, пов'язану з поточним пристроєм. У підсумку ми отримаємо з 60dp деяку кількість пікселів.

Спеціальні параметри розмірів

Замість фіксованих значень можна застосовувати наступні параметри:

  • match_parent — елемент займає весь доступний простір свого контейнера.
  • wrap_content — елемент займає мінімальний простір, необхідний для відображення його вмісту.

Приклад використання в XML:

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

Пріоритетність розмірів

Параметри розмірів часто комбінуються з вагами (weight) для управління відносними розмірами елементів. Це особливо корисно в контейнерах, таких як LinearLayout.

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="Елемент 1" />

    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:text="Елемент 2" />

</LinearLayout>

У цьому прикладі:

  • Перший елемент займатиме 1 частину доступного простору.
  • Другий елемент — 2 частини.
Вага елементів

Мінімальні та максимальні розміри

Можна також задати обмеження:

  • android:minWidth / android:minHeight — мінімальні розміри.
  • android:maxWidth / android:maxHeight — максимальні розміри.

Приклад:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:minWidth="100dp"
    android:maxWidth="200dp"
    android:minHeight="50dp"
    android:maxHeight="100dp"
    android:text="Текст з обмеженнями" />
Розумне використання розмірів та ваг дозволяє створювати адаптивні інтерфейси для різних пристроїв.

Приклад коду

res/layout/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"
    android:padding="16dp">

    <!-- TextView з мінімальним і максимальним розміром шрифту -->
    <TextView
        android:id="@+id/titleText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Розміри у дизайні Android"
        android:textSize="24sp"
        android:minHeight="48dp"
        android:maxHeight="72dp"
        android:gravity="center"
        android:layout_marginBottom="16dp" />

    <!-- Button з мінімальними розмірами -->
    <Button
        android:id="@+id/actionButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:minWidth="150dp"
        android:minHeight="48dp"
        android:text="Натисни мене"
        android:textSize="18sp" />

    <!-- EditText із фіксованими розмірами -->
    <EditText
        android:id="@+id/inputField"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:hint="Введіть текст"
        android:textSize="16sp"
        android:minHeight="40dp"
        android:maxHeight="80dp" />

    <!-- TextView з текстом, який автоматично змінює розмір -->
    <TextView
        android:id="@+id/adaptiveText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Автоматичний розмір тексту"
        android:textSize="18sp"
        android:minHeight="40dp"
        android:maxHeight="80dp"
        android:layout_marginBottom="16dp" />
</LinearLayout>
MainActivity.java
package com.example.sizerexample;

import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

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

        // Отримання посилань на елементи
        TextView titleText = findViewById(R.id.titleText);
        Button actionButton = findViewById(R.id.actionButton);
        EditText inputField = findViewById(R.id.inputField);
        TextView adaptiveText = findViewById(R.id.adaptiveText);

        // Динамічне налаштування розмірів тексту
        adaptiveText.setTextSize(14); // Мінімальний розмір тексту
        adaptiveText.setMaxHeight(120); // Максимальна висота текстового блоку

        // Додавання події на кнопку
        actionButton.setOnClickListener(view -> {
            String input = inputField.getText().toString();
            if (input.isEmpty()) {
                Toast.makeText(this, "Введіть текст", Toast.LENGTH_SHORT).show();
            } else {
                // Виводимо текст у адаптивний TextView
                adaptiveText.setText(input);
                adaptiveText.setTextSize(input.length() > 20 ? 12 : 18); // Зменшення шрифту для довгого тексту
            }
        });

        // Зміна тексту titleText
        titleText.setText("Динамічні розміри в Android");
    }
}