Тонкости реализации
Custom View в Android
Илья Демидов, Tinkoff Bank
Структура
Пример
➔ CustomLayout
➔ TextView
➔ CustomView
➔ ImageView
➔ CustomLayout
➔ ImageView
➔ ImageView
Типы компонентов и их свойства
- View.
- ViewGroup extends View.
void onDraw(Canvas canvas); - отрисовка;
void onMeasure(i...
Отрисовка
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(backgroundColor);
if (!isEmpty()) {
canvas.dra...
Расчет размеров
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMod...
Расчет размеров
MeasureSpec.EXACTLY; - точные размеры
MeasureSpec.AT_MOST; - не больше, чем размеры родителя
MeasureSpec.U...
Расчет размеров для ViewGroup
private void measureViews(int widthMeasureSpec, int heightMeasureSpec) {
for (int i = 0; i <...
Custom LayoutParams
LayoutParams extends ViewGroup.LayoutParams
LayoutParams extends ViewGroup.MarginLayoutParams
ViewGrou...
Расстановка детей
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
int ...
Взаимодействие с пользователем
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
cas...
Взаимодействие с пользователем
MotionEvent.ACTION_DOWN- первое касание
MotionEvent.ACTION_MOVE - движение пальца
MotionEve...
Диспетчеризация событий
События пользователя обрабатываются с верхнего уровня до нижнего. Если
старший контейнер может обр...
Диспетчеризация событий
Activity.
dispatchTouchEvent()
Activity.
onTouchEvent()
GdgLayout.
dispatchTouchEvent()
GdgLayout....
<?xml version="1.0" encoding="utf-8"?>
<....gdgvrn2015.widget.GdgLayout
android:id="@+id/gdg_group1"
android:layout_width=...
Ссылки
https://developer.android.com/intl/ru/training/custom-views/index.html - Creating
custom View;
https://developer.an...
«Custom View. Делаем быстро, красиво, чисто». Илья Демидов
«Custom View. Делаем быстро, красиво, чисто». Илья Демидов
Upcoming SlideShare
Loading in …5
×

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

556 views

Published on

Доклад с GDG DEvFest Voronezh 2015.

Published in: Software
1 Comment
0 Likes
Statistics
Notes
  • Be the first to like this

No Downloads
Views
Total views
556
On SlideShare
0
From Embeds
0
Number of Embeds
8
Actions
Shares
0
Downloads
4
Comments
1
Likes
0
Embeds 0
No embeds

No notes for slide

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

  1. 1. Тонкости реализации Custom View в Android Илья Демидов, Tinkoff Bank
  2. 2. Структура
  3. 3. Пример ➔ CustomLayout ➔ TextView ➔ CustomView ➔ ImageView ➔ CustomLayout ➔ ImageView ➔ ImageView
  4. 4. Типы компонентов и их свойства - 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); - взаимодействие с пользователем;
  5. 5. Отрисовка @Override protected void onDraw(Canvas canvas) { canvas.drawColor(backgroundColor); if (!isEmpty()) { canvas.drawText(text, textBound.left, -textBound.top, textPaint); } } setWillNotDraw(false) - позволяет вызывать отрисовку у ViewGroup invalidate() - служит для перерисовки View. Нельзя производить инициализацию объектов и выполнять очень тяжелые операции. Все данные для отрисоки уже должны быть готовы к моменту вызова метода.
  6. 6. Расчет размеров 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); }
  7. 7. Расчет размеров MeasureSpec.EXACTLY; - точные размеры MeasureSpec.AT_MOST; - не больше, чем размеры родителя MeasureSpec.UNSPECIFIED; - любые размеры setMeasuredDimension(width, height); - после расчета данные обязательно должны публиковаться с помощью этого метода requestLayout(); - служит для перерасчета и перерисовки View
  8. 8. Расчет размеров для 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.
  9. 9. 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();
  10. 10. Расстановка детей 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(); } }
  11. 11. Взаимодействие с пользователем @Override public 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; } }
  12. 12. Взаимодействие с пользователем MotionEvent.ACTION_DOWN- первое касание MotionEvent.ACTION_MOVE - движение пальца MotionEvent.ACTION_UP - пользователь убрал палец MotionEvent.ACTION_CANCEL - текущее действие было отменено Так-же содержит индекс касания, время, координаты и т.д. Если View обработала событие, метод должен вернуть true, иначе false.
  13. 13. Диспетчеризация событий События пользователя обрабатываются с верхнего уровня до нижнего. Если старший контейнер может обработать событие, то дети это событие не получат. boolean dispatchTouchEvent(); - решает, что делать с событием. Если контейнер может сам обработать событие, вызывает onTouchEvent(), иначе вызывает dispatchTouchEvent() своих детей. boolean interceptTouchEvent() - решает, прокидывать событие дальше или обработать его самому. void requestDisallowInterceptTouchEvent(boolean) - запрещает родителям перехватывать события
  14. 14. Диспетчеризация событий 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
  15. 15. <?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.GdgableTextView android:id="@+id/gdg_tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textStyle="bold" /> <...gdgvrn2015.widget.GdgView android:id="@+id/gdg_id1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ImageView android:layout_width="75dp" android:layout_height="50dp" android:src="@drawable/gdg"/> <...gdgvrn2015.widget.GdgLayout android: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"/> <ImageView android:layout_width="75dp" android:layout_height="50dp" android:src="@drawable/gdg"/> </...gdgvrn2015.widget.GdgLayout> </...gdgvrn2015.widget.GdgLayout>
  16. 16. Ссылки 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;

×