5. Типы компонентов и их свойства
- 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); - взаимодействие с пользователем;
6. Отрисовка
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(backgroundColor);
if (!isEmpty()) {
canvas.drawText(text, textBound.left, -textBound.top, textPaint);
}
}
setWillNotDraw(false) - позволяет вызывать отрисовку у ViewGroup
invalidate() - служит для перерисовки View.
Нельзя производить инициализацию объектов и выполнять очень тяжелые
операции. Все данные для отрисоки уже должны быть готовы к моменту
вызова метода.
7. Расчет размеров
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);
}
8. Расчет размеров
MeasureSpec.EXACTLY; - точные размеры
MeasureSpec.AT_MOST; - не больше, чем размеры родителя
MeasureSpec.UNSPECIFIED; - любые размеры
setMeasuredDimension(width, height); - после расчета данные обязательно
должны публиковаться с помощью этого метода
requestLayout(); - служит для перерасчета и перерисовки View
9. Расчет размеров для 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.
11. Расстановка детей
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();
}
}
12. Взаимодействие с пользователем
@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;
}
}
13. Взаимодействие с пользователем
MotionEvent.ACTION_DOWN- первое касание
MotionEvent.ACTION_MOVE - движение пальца
MotionEvent.ACTION_UP - пользователь убрал палец
MotionEvent.ACTION_CANCEL - текущее действие было отменено
Так-же содержит индекс касания, время, координаты и т.д.
Если View обработала событие, метод должен вернуть true, иначе false.
14. Диспетчеризация событий
События пользователя обрабатываются с верхнего уровня до нижнего. Если
старший контейнер может обработать событие, то дети это событие не
получат.
boolean dispatchTouchEvent(); - решает, что делать с событием. Если
контейнер может сам обработать событие, вызывает onTouchEvent(), иначе
вызывает dispatchTouchEvent() своих детей.
boolean interceptTouchEvent() - решает, прокидывать событие дальше или
обработать его самому.
void requestDisallowInterceptTouchEvent(boolean) - запрещает родителям
перехватывать события