Android User Interface (Part 1)

автор:

Татьяна Распутина

Содержание

  • Что такое View и ViewGroup?
  • Верстка элементов UI в XML
  • Настройка UI из кода Activity/Fragments и взаимодействие с пользователем

Overview


Что такое View?

Любой элемент интерфейса, с которым пользователь может взаимодействовать.

Что такое ViewGroup?

Наследник View, представляющий собой невидимый пользователю контейнер, определяющий порядок, размеры и местоположение View на экране.

Каждый ViewGroup реализует вложенный класс - наследник ViewGroup.LayoutParams, который распространяет свои layout-атрибуты на все дочерние View и ViewGroup.

Верстка UI в XML


  • Декларативность: позволяет определять и комбинировать View без деталей реализации
  • Разделение: позволяет разрабатывать интерфейс отдельно от логики экрана
  • Стандартизация: упрощает понимание и поддержку визуальной структуры пользовательского интерфейса
  • Скорость: быстрая и легкая реализация, возможность переиспользования
  • Адаптация: позволяет настроить UI под разные конфигурации Android устройств

Android Layouts

Know Android Layouts

  • FrameLayout
  • LinearLayout
  • RelativeLayout
  • ConstraintLayout
  • FlexboxLayout
  • Specific: WebView, SearchView
  • Scroll containers: ScrollView, NestedScrollView, HorizontalScrollView
  • List, paging: RecyclerView, ViewPager, AdapterView
  • Animations: MotionLayout, DrawerLayout, CoordinatorLayout
  • Outdated: TableLayout, GridLayout, ListView, GridView, AbsoluteLayout

FrameLayout

View Common Attributes


                    android:id - уникальный идентификатор
                    android:layout_width - ширина элемента (wrap_content, match_parent)
                    android:layout_height - высота элемента
                    android:layout_margin - отступы снаружи элемента
                    android:padding - отступы внутри элемента
                    android:layout_gravity - расположение элемента внутри родителя
                    android:gravity - расположение контента внутри элемента (top, start, center, center_vertical...)
                    android:visibility - показывать элемент или нет (visible, invisible, gone)
                    android:background - фоновое изображение

TextView Unique Attributes


                    android:text – текст
                    android:textSize – размер
                    android:textColor – цвет
                    android:fontFamily - фонт
                    android:textStyle - стиль (bold, italic, normal)
                    android:singleLine - максимум одна строка текста
                    android:maxLines - максимальное число строк текста
                    android:ellipsize - для сокращения длинного текста многоточием
                    android:drawableTop - добавить drawable к тексту (Compound Drawable)
                    android:drawablePadding - отступ от текста до Compound Drawable

ImageView Unique Attributes


                    android:src - что показывать
                    android:scaleType - как масштабировать
                    android:adjustViewBounds - размеры View устанавливаются от размеров изображения

Create View programmatically


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

                    class SplashActivity : AppCompatActivity() {
                        override fun onCreate(savedInstanceState: Bundle?) {
                            super.onCreate(savedInstanceState)
                            setContentView(R.layout.activity_splash)
                            frameLayout.addView(TextView(this).apply {
                                // View Common Attributes
                                id = ViewCompat.generateViewId()
                                layoutParams = FrameLayout.LayoutParams(
                                    FrameLayout.LayoutParams.MATCH_PARENT,
                                    FrameLayout.LayoutParams.WRAP_CONTENT
                                ).also {
                                    it.bottomMargin = resources.getDimensionPixelSize(R.dimen.splash_text_margin_bottom)
                                    it.gravity = Gravity.BOTTOM
                                }
                                rotation = 30f
                                // TextView Unique Attributes
                                text = resources.getString(R.string.hello_android)
                                setTextColor(ContextCompat.getColor(context, R.color.white))
                                gravity = Gravity.CENTER
                                setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.size_xlarge_plus_plus))
                                typeface = ResourcesCompat.getFont(context, R.font.minimo_regular)
                                setTypeface(typeface, Typeface.BOLD)
                            })
                            frameLayout.addView(ImageView(this).apply {
                                // ImageView Unique Attributes
                                scaleType = ImageView.ScaleType.FIT_XY
                                setImageResource(R.drawable.bg_splash)
                            }, 0)
                        }
                    }

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

                        <ImageView
                            android:id="@+id/imageView"
                            android:layout_width="match_parent"
                            android:layout_height="match_parent"
                            android:scaleType="fitXY"
                            android:src="@drawable/bg_splash"/>

                        <TextView
                            android:id="@+id/textView"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="@dimen/splash_text_margin_bottom"
                            android:padding="@dimen/size_small"
                            android:layout_gravity="bottom"
                            android:fontFamily="@string/roboto_font_family_regular"
                            android:gravity="center"
                            android:text="@string/hello_android"
                            android:textColor="@color/white"
                            android:textSize="@dimen/size_xlarge_plus_plus"
                            android:textStyle="bold"/>
                    </FrameLayout>

                    class SplashActivity : AppCompatActivity() {

                        override fun onCreate(savedInstanceState: Bundle?) {
                            super.onCreate(savedInstanceState)
                            setContentView(R.layout.activity_splash)
                        }
                    }

TextView & Spannable

Spannable - это интерфейс, который позволяет задать разное форматирование выделенным частям текста

Стиль задается экземплярами классов, которые реализуют ParcelableSpan
  • AbsoluteSizeSpan - размер текста
  • RelativeSizeSpan - размер текста относительно текста строки
  • BackgroundColorSpan - цвет фона
  • ForegroundColorSpan - цвет текста
  • LocaleSpan - Locale текста
  • StyleSpan - Typeface текста (bold, italic)
  • TypefaceSpan - Typeface текста (font)
  • URLSpan (: ClickableSpan) - реакция на нажатие
  • UnderlineSpan - нижнее подчеркивание
  • ...

                    abstract fun setSpan(what: Any!, start: Int, end: Int, flags: Int): Unit
                    abstract fun removeSpan(what: Any!): Unit
                

                    val text = SpannableString("This is underline and bold text.")
                    text.setSpan(UnderlineSpan(), 8, 17, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
                    text.setSpan(StyleSpan(Typeface.BOLD), 22, 26, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
                

                    val text = SpannableString("This is red monospace text.")
                    text.setSpan(TypefaceSpan("monospace"), 7, 20, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
                    text.setSpan(ForegroundColorSpan(Color.RED), 7, 20, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
                

                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE - xxXXXxx - xxNXXXNxx
                    Spanned.SPAN_EXCLUSIVE_INCLUSIVE - xxXXXxx - xxNXXXNxx
                    Spanned.SPAN_INCLUSIVE_EXCLUSIVE - xxXXXxx - xxNXXXNxx
                    Spanned.SPAN_INCLUSIVE_INCLUSIVE - xxXXXxx - xxNXXXNxx
                

LinearLayout

LinearLayout Unique Attributes


                    android:orientation – horizontal/vertical
                    android:weightSum – назначает суммарный вес для дочерних элементов
                    android:layout_weight – назначает индивидуальный вес для дочернего элемента

EditText Unique Attributes


                    android:hint - подсказка, видна пока EditText пустой
                    android:textColorHint - цвет подсказки
                    android:inputType - тип клавиатуры (email, телефон и т.д.)
                    android:digits - список допустимых символов

Взаимодествие с клавиатурой

1) Клавиатура - это отдельное приложение
2) Изменять размер клавиатуры не нужно - нужно следить за изменениями размера layout
3) UI компоненты лучше разместить в ScrollView
4) Помогайте пользователю с помощью атрибутов inputType и imeOptions
                

Handle User Input


                    class LoginActivity : AppCompatActivity() {

                        override fun onCreate(savedInstanceState: Bundle?) {
                            super.onCreate(savedInstanceState)
                            setContentView(R.layout.activity_login)

                            button.setOnClickListener { login() }
                            emailEditText.addTextChangedListener(object : TextWatcher {
                                override fun afterTextChanged(text: Editable?) {
                                     // text is modified and is editable: can be corrected (formatted) here
                                }

                                override fun beforeTextChanged(text: CharSequence?, start: Int, count: Int, after: Int) {
                                    // text is in previous state
                                }

                                override fun onTextChanged(text: CharSequence?, start: Int, before: Int, count: Int) {
                                    // text is modified, is not editable
                                }
                            })
                            passwordEditText.setOnEditorActionListener { _, actionId, _ ->
                                when (actionId) {
                                    EditorInfo.IME_ACTION_DONE -> {
                                        login()
                                        true
                                    }
                                    else -> false
                                }
                            }
                        }
                    }

RelativeLayout

Атрибуты, связанные с родителем


                    android:layout_alignParentBottom - выравнивание относительно нижнего края
                    android:layout_alignParentLeft - выравнивание относительно левого края
                    android:layout_alignParentRight - выравнивание относительно правого края
                    android:layout_alignParentTop - выравнивание относительно верхнего края
                    android:layout_centerInParent - выравнивание по центру родителя
                    android:layout_centerHorizontal - выравнивание по центру + по горизонтали
                    android:layout_centerVertical - выравнивание по центру + по вертикали

Атрибуты, связанные с соседними View


                    android:layout_above - размещается над указанным View
                    android:layout_below - размещается под указанным View
                    android:layout_alignLeft - выравнивается по левому краю
                    android:layout_alignRight - выравнивается по правому краю
                    android:layout_alignTop - выравнивается по верхнему краю
                    android:layout_alignBottom - выравнивается по нижнему краю
                    android:layout_toLeftOf - правый край компонента размещается слева
                    android:layout_toRightOf - левый край компонент размещается справа

ConstraintLayout

  • Relative positioning
  • Margins
  • Centering positioning
  • Circular positioning
  • Visibility behavior
  • Dimension constraints
  • Chains
  • Virtual Helpers objects

Centering & Positioning


                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintLeft_toLeftOf="@id/view"
                    app:layout_constraintRight_toRightOf="@id/view"
                    app:layout_constraintTop_toTopOf="parent"
                    app:layout_constraintBottom_toBottomOf="@id/view"

                    app:layout_constraintBaseline_toBaselineOf="@id/view"
                    app:layout_constraintStart_toEndOf="@id/view"
                    app:layout_constraintLeft_toRightOf="@id/view"
                    app:layout_constraintEnd_toStartOf="@id/view"
                    app:layout_constraintRight_toLeftOf="@id/view"
                    app:layout_constraintTop_toBottomOf="@id/view"
                    app:layout_constraintBottom_toTopOf="@id/view"

                    app:layout_goneMarginTop="@dimen/dimen"
                    app:layout_goneMarginBottom="@dimen/dimen"
                    app:layout_goneMarginStart="@dimen/dimen"
                    app:layout_goneMarginEnd="@dimen/dimen"
                    app:layout_goneMarginLeft="@dimen/dimen"
                    app:layout_goneMarginRight="@dimen/dimen"

                    app:layout_constraintHorizontal_bias="0.4"
                    app:layout_constraintDimensionRatio="4:3"

                    app:layout_constraintCircle="@id/view"
                    app:layout_constraintCircleAngle="90"
                    app:layout_constraintCircleRadius="100dp"

ConstraintLayout docs

Chains & Virtual Helpers objects

Chains


                    app:layout_constraintHorizontal_chainStyle="spread"
                    app:layout_constraintVertical_chainStyle="spread"
                    app:layout_constraintHorizontal_chainStyle="spread_inside"
                    app:layout_constraintVertical_chainStyle="spread_inside"
                    app:layout_constraintHorizontal_chainStyle="packed"
                    app:layout_constraintVertical_chainStyle="packed"

                    app:layout_constraintHorizontal_weight="1"
                    app:layout_constraintVertical_weight="1"

GuideLine


                    app:layout_constraintGuide_percent="0.5"
                    app:layout_constraintGuide_begin="10dp"
                    app:layout_constraintGuide_end="150dp"

Barriers


                    app:barrierDirection="right"
                    app:constraint_referenced_ids="button,text_view"

FlexboxLayout

Позиционирование в FlexboxLayout:

flexWrap - однострочный или многострочный контейнер
    (nowrap(default), wrap, wrap_reverse)
flexDirection - направление главной оси
    (row(default), row_reverse, column, column_reverse)
justifyContent - выравнивание вдоль главной оси
    (flex_start(default), flex_end, center, space_between, space_around)
alignItems - выравнивание вдоль поперечной оси
    (stretch(default), flex_start, flex_end, center, baseline)
alignContent - выравнивание строк в контейнере
    (stretch(default), flex_start, flex_end, center,
     space_between, space_around)

Разделители в FlexboxLayout:

showDividerHorizontal - none | beginning | middle | end
showDividerVertical - none | beginning | middle | end
dividerDrawableHorizontal - горизонтальные разделители
dividerDrawableVertical - вертикальные разделители
showDivider, dividerDrawable - горизонтальные и вертикальные разделители

Атрибуты дочерних View в FlexboxLayout:

layout_flexGrow - насколько View будет увеличиваться,
    если распределяется положительное пространство в строке
layout_flexShrink - насколько View будет уменьшаться,
    если распределяется отрицательное пространство в строке
layout_alignSelf - выравнивание View вдоль поперечной оси
layout_flexBasisPercent - View будет пытаться увеличиться
    согласно проценту от размера родителя
layout_minWidth / layout_minHeight - ограничивает min размеры View
layout_maxWidth / layout_maxHeight - ограничивает max размеры View
layout_order - порядок View в контейнере
layout_wrapBefore - перенос View на новую строку

FlexboxLayout docs

Дополнительные ссылки


Reveal.initialize({ center: true })