View
565
Download
3
Category
Preview:
Citation preview
Тонкости реализации
Custom View в AndroidИлья Демидов, Tinkoff Bank
Структура
Пример
➔ CustomLayout
➔ TextView
➔ CustomView
➔ ImageView
➔ CustomLayout
➔ ImageView
➔ ImageView
Типы компонентов и их свойства
- 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); - взаимодействие с пользователем;
Отрисовка
@Overrideprotected void onDraw(Canvas canvas) {
canvas.drawColor(backgroundColor);if (!isEmpty()) {
canvas.drawText(text, textBound.left, -textBound.top, textPaint);}
}
setWillNotDraw(false) - позволяет вызывать отрисовку у ViewGroup
invalidate() - служит для перерисовки 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);
}
Расчет размеров
MeasureSpec.EXACTLY; - точные размеры
MeasureSpec.AT_MOST; - не больше, чем размеры родителя
MeasureSpec.UNSPECIFIED; - любые размеры
setMeasuredDimension(width, height); - после расчета данные обязательно
должны публиковаться с помощью этого метода
requestLayout(); - служит для перерасчета и перерисовки 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.
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();
Расстановка детей
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();
}}
Взаимодействие с пользователем
@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;
}}
Взаимодействие с пользователем
MotionEvent.ACTION_DOWN- первое касание
MotionEvent.ACTION_MOVE - движение пальца
MotionEvent.ACTION_UP - пользователь убрал палец
MotionEvent.ACTION_CANCEL - текущее действие было отменено
Так-же содержит индекс касания, время, координаты и т.д.
Если View обработала событие, метод должен вернуть true, иначе false.
Диспетчеризация событий
События пользователя обрабатываются с верхнего уровня до нижнего. Если
старший контейнер может обработать событие, то дети это событие не
получат.
boolean dispatchTouchEvent(); - решает, что делать с событием. Если
контейнер может сам обработать событие, вызывает onTouchEvent(), иначе
вызывает dispatchTouchEvent() своих детей.
boolean interceptTouchEvent() - решает, прокидывать событие дальше или
обработать его самому.
void requestDisallowInterceptTouchEvent(boolean) - запрещает родителям
перехватывать события
Диспетчеризация событий
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
<?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>
Ссылки
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;
Recommended