18

«Custom View. Делаем быстро, красиво, чисто». Илья Демидов

  • Upload
    dataart

  • View
    565

  • Download
    3

Embed Size (px)

Citation preview

Page 1: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов
Page 2: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

Тонкости реализации

Custom View в AndroidИлья Демидов, Tinkoff Bank

Page 3: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

Структура

Page 4: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

Пример

➔ CustomLayout

➔ TextView

➔ CustomView

➔ ImageView

➔ CustomLayout

➔ ImageView

➔ ImageView

Page 5: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

Типы компонентов и их свойства

- View.

- ViewGroup extends View.

void onDraw(Canvas canvas); - отрисовка;

void onMeasure(int widthMeasureSpec, int heightMeasureSpec); - расчет

размеров;

void onLayout(boolean changed, int l, int t, int r, int b); - расстановка

детей;

void onTouchEvent(MotionEvent event); - взаимодействие с пользователем;

Page 6: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

Отрисовка

@Overrideprotected void onDraw(Canvas canvas) {

canvas.drawColor(backgroundColor);if (!isEmpty()) {

canvas.drawText(text, textBound.left, -textBound.top, textPaint);}

}

setWillNotDraw(false) - позволяет вызывать отрисовку у ViewGroup

invalidate() - служит для перерисовки View.

Нельзя производить инициализацию объектов и выполнять очень тяжелые

операции. Все данные для отрисоки уже должны быть готовы к моменту

вызова метода.

Page 7: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

Расчет размеров

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int width = 0;switch (widthMode) {

case MeasureSpec.EXACTLY:width = widthSize;break;

case MeasureSpec.AT_MOST:width = Math.min(widthSize, textBound.left + textBound.right);break;

case MeasureSpec.UNSPECIFIED:width = textBound.left + textBound.right;

}setMeasuredDimension(width, height);

}

Page 8: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

Расчет размеров

MeasureSpec.EXACTLY; - точные размеры

MeasureSpec.AT_MOST; - не больше, чем размеры родителя

MeasureSpec.UNSPECIFIED; - любые размеры

setMeasuredDimension(width, height); - после расчета данные обязательно

должны публиковаться с помощью этого метода

requestLayout(); - служит для перерасчета и перерисовки View

Page 9: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

Расчет размеров для ViewGroup

private void measureViews(int widthMeasureSpec, int heightMeasureSpec) {for (int i = 0; i < getChildCount(); i++) {

int childWidthSpec;View child = getChildAt(i);LayoutParams lp = child.getLayoutParams();if (lp.width == LayoutParams.MATCH_PARENT) {

childWidthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);

} else {childWidthSpec = getChildMeasureSpec(widthMeasureSpec,

0, lp.width);}child.measure(childWidthSpec, childHeightSpec);

}}

getChildMeasureSpec(int spec, int padding, int childDimension) - does the hard

part of measureChildren: figuring out the MeasureSpec to pass to a particular child.

Page 10: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

Custom LayoutParams

LayoutParams extends ViewGroup.LayoutParams

LayoutParams extends ViewGroup.MarginLayoutParams

ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs);

ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p);

boolean checkLayoutParams(ViewGroup.LayoutParams p);

ViewGroup.LayoutParams generateDefaultLayoutParams();

Page 11: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

Расстановка детей

protected void onLayout(boolean changed, int l, int t, int r, int b) {int count = getChildCount();int childLeft = 0;int childTop = 0;for (int i = 0; i < count; i++) {

View child = getChildAt(i);int lc = (int) (childLeft + offset);if (lc + child.getMeasuredWidth() > r - l) {

lc = (r - l) - child.getMeasuredWidth();} else if (lc < 0) {

lc = 0;}child.layout(lc, childTop, lc + child.getMeasuredWidth(),

childTop + child.getMeasuredHeight());childLeft += child.getMeasuredWidth();childTop += child.getMeasuredHeight();

}}

Page 12: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

Взаимодействие с пользователем

@Overridepublic boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {case MotionEvent.ACTION_DOWN:

performPressedState();return true;

case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:

performUnpressedState();return true;

default:return false;

}}

Page 13: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

Взаимодействие с пользователем

MotionEvent.ACTION_DOWN- первое касание

MotionEvent.ACTION_MOVE - движение пальца

MotionEvent.ACTION_UP - пользователь убрал палец

MotionEvent.ACTION_CANCEL - текущее действие было отменено

Так-же содержит индекс касания, время, координаты и т.д.

Если View обработала событие, метод должен вернуть true, иначе false.

Page 14: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

Диспетчеризация событий

События пользователя обрабатываются с верхнего уровня до нижнего. Если

старший контейнер может обработать событие, то дети это событие не

получат.

boolean dispatchTouchEvent(); - решает, что делать с событием. Если

контейнер может сам обработать событие, вызывает onTouchEvent(), иначе

вызывает dispatchTouchEvent() своих детей.

boolean interceptTouchEvent() - решает, прокидывать событие дальше или

обработать его самому.

void requestDisallowInterceptTouchEvent(boolean) - запрещает родителям

перехватывать события

Page 15: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

Диспетчеризация событий

Activity.

dispatchTouchEvent()

Activity.

onTouchEvent()

GdgLayout.

dispatchTouchEvent()

GdgLayout.

onTouchEvent()

GdgView.

dispatchTouchEvent()

GdgView.

onTouchEvent()

Activity.

dispatchTouchEvent()

Activity.

onTouchEvent()

GdgLayout.

dispatchTouchEvent()

GdgLayout.

onTouchEvent()

GdgView.

dispatchTouchEvent()

GdgView.

onTouchEvent()

ACTION_CANCEL

Page 16: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

<?xml version="1.0" encoding="utf-8"?><....gdgvrn2015.widget.GdgLayout

android:id="@+id/gdg_group1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center">

<...gdgvrn2015.widget.GdgableTextViewandroid:id="@+id/gdg_tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textStyle="bold" />

<...gdgvrn2015.widget.GdgViewandroid:id="@+id/gdg_id1"android:layout_width="wrap_content"android:layout_height="wrap_content"

/>

<ImageViewandroid:layout_width="75dp"android:layout_height="50dp"android:src="@drawable/gdg"/>

<...gdgvrn2015.widget.GdgLayoutandroid:id="@+id/gdg_group2"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageView

android:layout_width="75dp"android:layout_height="50dp"android:src="@drawable/gdg"/>

<ImageViewandroid:layout_width="75dp"android:layout_height="50dp"android:src="@drawable/gdg"/>

</...gdgvrn2015.widget.GdgLayout></...gdgvrn2015.widget.GdgLayout>

Page 17: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов
Page 18: «Custom View. Делаем быстро, красиво, чисто». Илья Демидов

Ссылки

https://developer.android.com/intl/ru/training/custom-views/index.html - Creating

custom View;

https://developer.android.com/intl/ru/guide/topics/ui/how-android-draws.html - How

Android Draws Views;

https://youtu.be/EZAoJU-nUyI - Mastering the Android Touch System;

https://github.com/dem1d/GDGfestVoronezh2015 - пример из презентации;

Исходный код View, ViewContainer и стандартных компонентов SDK;

https://twitter.com/dem1d - мой twitter;