Post on 08-Feb-2017
@glomadrian
Adrián García Lomas
Canvas al ajillo
Lo que me gustaría que me hubieran contado sobre Canvas y Custom Views
Los detalles importan
Adapta las vistas a las necesidades no las necesidades a las vistasUn mundo diferente y divertido
Ciclo de vidade una vista
Constructor
onAttachedToWindow()
onMeassure()
onLayout()
dispatchDraw()
draw()
onDraw()
invalidate()
Animación de carga
public Loading(Context context) { super(context); initialize();}
public Loading(Context context, AttributeSet attrs) { super(context, attrs); initialize();}
public Loading(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initialize();}
private void initialize() { circlePaint = new Paint(); circlePaint.setColor(circleColor); circlePaint.setAntiAlias(true); circlePaint.setStyle(Paint.Style.STROKE); circlePaint.setStrokeWidth(circleStroke);}
¿Qué pasa con el tamaño de la vista?
@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); width = w; height = h; createCircle();}
private void createCircleArea() {int circleAreawidth = width - strokeWidth;int circleAreaHeight = heihgt - strokeWidth;circleArea = new RectF(strokeWidth, strokeWidth, circleAreawidth, circleAreaHeight);}
onDraw(Canvas canvas)
16ms (60 fps)
Evitar nuevas instancias
No usar invalidate()
X
Y
● width / 2 , height / 2
● 0 , 0
private float circleStartDegree = 0;private float circleEndDegree = 360;private float actualCircleEndDegree = circleEndDegree;
@Overrideprotected void onDraw(Canvas canvas) { drawCircle(canvas);}
private void drawCircle(Canvas canvas) {canvas.drawArc(circleArea, circleStartDegree, actualCircleEndDegree, false, circlePaint);}
circlePaint.setPathEffect(new DashPathEffect(new float[]{dashWidth, dashSpace}, 0));
Animación
private void initializeAnimator() { valueAnimator = ValueAnimator.ofInt(circleStartDegree, circleEndDegree); valueAnimator.setDuration(1000); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { int degreeValue = (int) valueAnimator.getAnimatedValue(); actualCircleEndDegree = degreeValue; invalidate(); } });}
private void initialize() { initializePainter(); initializeAnimator();}
public void start(){ valueAnimator.start(); } ValueAnimato
r
onUpdatedegree = value
invalidate
onDraw()
Interpolators Acelerate
Acelerate Decelerate
BounceInterpolator
OvershootInterpolator
...
valueAnimator.setInterpolator(new BounceInterpolator());
valueAnimator.setInterpolator(new DecelateInterpolator());
blurPainter = new Paint(circlePaint);blurPainter.setColor(Color.RED);blurPainter.setStrokeWidth(circleStroke * 1.20F);blurPainter.setMaskFilter(new BlurMaskFilter(blurRadius, BlurMaskFilter.Blur.NORMAL));
private void drawCircle(Canvas canvas) { canvas.drawArc(internalCircle, circleStartDegree, circleEndDegree, false, blurPainter); canvas.drawArc(internalCircle, circleStartDegree, circleEndDegree, false, circlePaint);}
setLayerType(LAYER_TYPE_SOFTWARE, null);
Muerte a las vistas cuadradas
@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); width = w; height = h; createPath();}
private void createPath() { curvedPath = new Path(); curvedPath.moveTo(0, 0); curvedPath.lineTo(width, 0);}
@Overrideprotected void onDraw(Canvas canvas) { canvas.drawPath(curvedPath, pathPaint);}
curvedPath.quadTo(x1, y1, x2, y2);
x1, y1
x2, y2
private void createPath() { float pathCurvatureY = height / 2; float middleX = width / 2; curvedPath = new Path(); curvedPath.moveTo(0, 0); curvedPath.quadTo(middleX, pathCurvatureY, width, 0);
} 0,0
middleX, pathCurvatureY
width, 0
private void createPath() { float pathCurvatureY = height / 2; float middleX = width / 2; curvedPath = new Path(); curvedPath.moveTo(0, 0); curvedPath.quadTo(middleX, pathCurvatureY, width, 0); curvedPath.lineTo(width, height);
}
width, height
private void createPath() { float pathCurvatureY = height / 2; float middleX = width / 2; curvedPath = new Path(); curvedPath.moveTo(0, 0); curvedPath.quadTo(middleX, pathCurvatureY, width, 0); curvedPath.lineTo(width, height); curvedPath.lineTo(0, height); }
0, height
private void createPath() { float pathCurvatureY = height / 2; float middleX = width / 2; curvedPath = new Path(); curvedPath.moveTo(0, 0); curvedPath.quadTo(middleX, pathCurvatureY, width, 0); curvedPath.lineTo(width, height); curvedPath.lineTo(0, height); curvedPath.close();}
pathPaint.setStyle(Paint.Style.FILL);
Interceptando touch events dentro de un área
@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); width = w; height = h; createPath(); createTouchRegion();}
private void createTouchRegion() { RectF pathBoundsRect = new RectF(); curvedPath.computeBounds(rectF, true); regionToCheck = new Region(pathBoundsRect.left, pathBoundsRect.top, pathBoundsRect.right,
pathBoundsRect.bottom); regionToCheck.setPath(curvedPath, regionToCheck);}
@Overridepublic boolean onTouchEvent(MotionEvent event) { boolean result = false; int pointX = (int) event.getX(); int pointY = (int) event.getY(); if (isPointInsidePathArea(pointX, pointY)) { result = super.onTouchEvent(event); } return result;}
private boolean isPointInsidePathArea(int x, int y) { return regionToCheck.contains(x, y);}
Usando path para animar
private float[] getPathCoordinates(Path path, float fraction) { float aCoordinates[] = { 0f, 0f }; PathMeasure pm = new PathMeasure(path, false); pm.getPosTan(pm.getLength() * fraction, aCoordinates, null); return aCoordinates;}
@Overridepublic void onAnimationUpdate(ValueAnimator animation) { float value = animation.getAnimatedFraction(); float[] coordinates = getPathCoordinates(infinitePath, value); this.ballX = coordinates[0]; this.ballY = coordinates[1]; invalidate()}
Midiendo el rendimiento
onDraw()
Recursos
OpenGL
Espera CPU
16 ms
private void onDraw(Canvas canvas) { heavyWork(); canvas.drawArc(internalCircle, circleStartDegree, circleEndDegree, false, circlePaint);}
Clean code también en vistas
@Overrideprotected void onDraw(Canvas canvas) { if (mRunning) { if (mShowLeftEye) { canvas.drawCircle(mLeftEyePos[0], mLeftEyePos[1], mEyeCircleRadius,mCirclePaint); } if (mShowRightEye) { canvas.drawCircle(mRightEyePos[0], mRightEyePos[1], mEyeCircleRadius, mCirclePaint); } if (mFirstStep) { mArcPath.reset(); mArcPath.addArc(mRectF, mStartAngle, mSweepAngle); canvas.drawPath(mArcPath, mArcPaint); } else { mArcPath.reset(); mArcPath.addArc(mRectF, mStartAngle, mSweepAngle); canvas.drawPath(mArcPath, mArcPaint); }} else { canvas.drawCircle(mLeftEyePos[0], mLeftEyePos[1], mEyeCircleRadius, mCirclePaint); canvas.drawCircle(mRightEyePos[0], mRightEyePos[1], mEyeCircleRadius,mCirclePaint); canvas.drawPath(mArcPath, mArcPaint);}}
@Overrideprotected void onDraw(Canvas canvas) { super.onDraw(canvas); externalCirclePainter.draw(canvas); internalCirclePainter.draw(canvas); progressPainter.draw(canvas); iconPainter.draw(canvas);}
¿Preguntas?