Canvas al ajillo

Preview:

Citation preview

@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?

Recommended