SlideShare a Scribd company logo
S’il te plait, dessine moi
une vue
Florent Champigny
Florent37
florent_champ
http://bit.ly/AndroidDevFr
Slack : Android Dev FR
Why talking about
canvas ?
One day, on my twitter…
“
One day, on my twitter…
“
@florent_champ can you teach me how to draw a custom view ?
@florent_champ please teach me how to draw a custom view ?
@florent_champ I saw a video on youtube, a man became rich by building
it own app, and displaying ads $$$$$
@florent_champ I need to draw custom views, but I don’t know where to start
@florent_champ Please !!!!!!! Please !!!!!!! Please !!!!!!!
K3v1n :
FORTNIGHT <3 <3
Love to code on Windows
Learn Android, want to become billionaire with an
Android app displaying ads
$
$
$
HTML is its favorite programming language
@k3v1n can you please stop spamming me ?
@florent_champ I will offer you beers 🍺🍺🍺
@k3v1n when can we start ? ☺☺☺
“WHERE DO I
START“ ? ComicsSansMS
“First thing, create a
extension of View“ Not
ComicsSansMS
class MyCustomView(context: Context) : View(context) {
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
//when the view has a size
}
override fun onDraw(canvas: Canvas) {
//where we draw
}
}
Custom View
class MyCustomViewGroup(context: Context) : FrameLayout(context) {
init {
setWillNotDraw(false)
}
override fun onDraw(canvas: Canvas) {
//where we draw
}
}
Custom ViewGroup
class MyCustomViewGroup(context: Context) : FrameLayout(context) {
override fun dispatchDraw(canvas: Canvas) {
//where we draw
}
}
Custom ViewGroup
“Next thing you need to
know : Paint“
“YEEES !
I ALREADY KNOW PAINT“
Paint
The Paint class holds the style and color
information about how to draw geometries,
text and bitmaps.
https://developer.android.com/reference/android/graphics/Paint
PAINT - Style
FILL
FILL AND
STROKE
STROKE
paint.setStyle(FILL)
paint.setStyle(STROKE)
paint.setStyle(FILL_AND_STROKE)
PAINT - StrokeCap
BUTT
SQUARE
ROUND
paint.setStrokeCap(BUTT)
PAINT - Stroke Width
paint.setStrokeWidth(3)
1px
2px
3px
PAINT - Stroke Join
BEVEL
SQUARE
ROUND
paint.setStrokeJoin(BEVEL)
paint.setStrokeJoin(ROUND)
paint.setStrokeJoin(SQUARE)
“And with this paint, you
can draw shapes on a
canvas“
Canvas
The Canvas class holds the "draw" calls. To draw something, you
need 4 basic components: A Bitmap to hold the pixels, a Canvas to
host the draw calls (writing into the bitmap), a drawing primitive (e.g.
Rect, Path, text, Bitmap), and a paint (to describe the colors and
styles for the drawing).
https://developer.android.com/reference/android/graphics/Canvas
val paint = Paint().apply{
color = Color.WHITE
}
fun onDraw(canvas: Canvas) {
canvas.drawRect(0, 0, 30, 10, paint)
}
Draw a Rect
30
10
val paint = Paint().apply{
color = Color.WHITE
}
fun onDraw(canvas: Canvas) {
canvas.drawRect(0, 0, 30, 10, paint)
}
Draw a Rect
30
10
Values in pixels !
Draw a Rect
30
10
val myRect = Rect()
fun onSizeChanged(width:Int, height: Int,
oldWidth: Int, oldHeight: Int) {
myRect.right = width
myRect.bottom = height
}
fun onDraw(canvas: Canvas) {
canvas.drawRect(myRect, paint)
}
Draw a Circle
canvas.drawCircle(cx: float, cy: float, radius: float, paint: Paint)
fun onDraw(canvas: Canvas) {
canvas.drawCircle(100, 100, 15, paint)
}
30
15
Draw a Line
canvas.drawLine(startX: float, startY: float, endX: float,
endY: float, paint: Paint)
fun onDraw(canvas: Canvas) {
canvas.drawLine(0, 30, 100, 50, paint)
}
0,30
100,50
Draw a Line
init {
paint.setStrokeWidth(10)
paint.setStrokeCap(ROUND)
}
fun onDraw(canvas: Canvas) {
canvas.drawLine(0, 30, 100, 50, paint)
}
0,30
100,50
Draw a Custom Shape
val path = Path()
fun onSizeChanged(width:Int, height: Int,
oldWidth: Int, oldHeight: Int) {
path.reset()
path.move(0,0)
path.lineTo(width, height)
path.lineTo(0, height)
path.close()
}
fun onDraw(canvas: Canvas) {
canvas.drawPath(path, paint)
}
Width
height
Everything is a shape !
Google fit
Everything is a shape !
Line + StrokeCap round
Arcs
Lines
Rectangle
“SO CANVAS IS
USELESS ?“
Canvas Transformations
TRANSLATION
canvas.translate(x, y)
SCALE
ROTATION
canvas.scale(x, y)
canvas.scale(x, y, pivotX, pivotY)
canvas.rotate(degrees)
canvas.rotate(degrees, pivotX, pivotY)
Translations
val myRect = Rect(5, 5, 100, 90)
fun onDraw(canvas: Canvas) {
canvas.drawRect(myRect, paint)
}
0 100
100
Translations
val myRect = Rect(5, 5, 100, 90)
fun onDraw(canvas: Canvas) {
canvas.translate(100, 0)
canvas.drawRect(myRect, paint)
}
0 100
100
0
Translations
val myRect = Rect(5, 5, 100, 90)
fun onDraw(canvas: Canvas) {
canvas.translate(100, 0)
canvas.drawRect(myRect, paint)
canvas.drawCircle(50, 50, 15, paint)
}
0 100
100
Translations
val myRect = Rect(5, 5, 100, 90)
fun onDraw(canvas: Canvas) {
canvas.save()
canvas.translate(100, 0)
canvas.drawRect(myRect, paint)
canvas.restore()
canvas.drawCircle(50, 50, 15, paint)
}
0 100
100
0
Translations
val myRect = Rect(5, 5, 100, 90)
fun onDraw(canvas: Canvas) {
canvas.save()
canvas.translate(100, 0)
canvas.drawRect(myRect, paint)
canvas.restore()
canvas.drawCircle(50, 50, 15, paint)
}
0 100
100
new 0
Translations
val myRect = Rect(5, 5, 100, 90)
fun onDraw(canvas: Canvas) {
canvas.save()
canvas.translate(100, 0)
canvas.drawRect(myRect, paint)
canvas.restore()
canvas.drawCircle(50, 50, 15, paint)
}
0 100
100
0
Translations
val myRect = Rect(5, 5, 100, 90)
fun onDraw(canvas: Canvas) {
canvas.save()
canvas.translate(100, 0)
canvas.drawRect(myRect, paint)
canvas.restore()
canvas.drawCircle(50, 50, 15, paint)
}
0 100
100
0
Translations
val myRect = Rect(5, 5, 100, 90)
fun onDraw(canvas: Canvas) {
canvas.save()
canvas.translate(100, 0)
canvas.drawRect(myRect, paint)
canvas.restore()
canvas.drawCircle(50, 50, 15, paint)
}
0 100
100
Translations
0 100
100
0 100
100
Without restore With restore
Translations
val myRect = Rect(5, 5, 100, 90)
fun onDraw(canvas: Canvas) {
canvas.save()
canvas.translate(10, 0)
canvas.save()
canvas.translate(90, 0)
canvas.drawRect(myRect, paint)
canvas.restore()
canvas.drawCircle(50, 50, 15, paint)
}
0 100
100
Translations
val myRect = Rect(5, 5, 100, 90)
fun onDraw(canvas: Canvas) {
val count = canvas.save()
canvas.translate(10, 0)
canvas.save()
canvas.translate(90, 0)
canvas.drawRect(myRect, paint)
canvas.restoreToCount(count)
canvas.drawCircle(50, 50, 15, paint)
}
0 100
100
Translations + rotations
val myRect = Rect(0, 0, 10, 10)
fun onDraw(canvas: Canvas) {
val count = canvas.save()
for(angle in 0..360 step 30) {
canvas.rotate(angle)
canvas.translate(100, 0)
canvas.drawRect(myRect, paint)
canvas.restoreToCount(count)
}
}
Center
Translations + rotations
val myRect = Rect(0, 0, 10, 10)
fun onDraw(canvas: Canvas) {
val count = canvas.save()
for(angle in 0..360 step 30) {
canvas.rotate(angle)
canvas.translate(100, 0)
canvas.drawRect(myRect, paint)
canvas.restoreToCount(count)
}
}
Center
Translations + rotations
val myRect = Rect(0, 0, 10, 10)
fun onDraw(canvas: Canvas) {
val count = canvas.save()
for(angle in 0..360 step 30) {
canvas.rotate(angle)
canvas.translate(100, 0)
canvas.drawRect(myRect, paint)
canvas.restoreToCount(count)
}
}
Center
Translations + rotations
val myRect = Rect(0, 0, 10, 10)
fun onDraw(canvas: Canvas) {
val count = canvas.save()
for(angle in 0..360 step 30) {
canvas.rotate(angle)
canvas.translate(100, 0)
canvas.drawRect(myRect, paint)
canvas.restoreToCount(count)
}
}
Center
Translations + rotations
val myRect = Rect(0, 0, 10, 10)
fun onDraw(canvas: Canvas) {
val count = canvas.save()
for(angle in 0..360 step 30) {
canvas.rotate(angle)
canvas.translate(100, 0)
canvas.drawRect(myRect, paint)
canvas.restoreToCount(count)
}
}
Center
Translations + rotations
val myRect = Rect(0, 0, 10, 10)
fun onDraw(canvas: Canvas) {
val count = canvas.save()
for(angle in 0..360 step 30) {
canvas.rotate(angle)
canvas.translate(100, 0)
canvas.drawRect(myRect, paint)
canvas.restoreToCount(count)
}
}
Center
Translations + rotations
val myRect = Rect(0, 0, 10, 10)
fun onDraw(canvas: Canvas) {
val count = canvas.save()
for(angle in 0..360 step 30) {
canvas.rotate(angle)
canvas.translate(100, 0)
canvas.drawRect(myRect, paint)
canvas.restoreToCount(count)
}
}
Center
Translations + rotations
val myRect = Rect(0, 0, 10, 10)
fun onDraw(canvas: Canvas) {
val count = canvas.save()
for(angle in 0..360 step 30) {
canvas.rotate(angle)
canvas.translate(100, 0)
canvas.drawRect(myRect, paint)
canvas.restoreToCount(count)
}
}
Center
“I NEED TO DRAW
TEXT BY HAND ?“
val paint = Paint().apply{
color = Color.parseColor("#3E3E3E")
typeface = Typeface.DEFAULT_BOLD
textSize = 24f //px
}
fun onDraw(canvas: Canvas) {
canvas.drawText("android", 10, 10, paint)
}
Draw Text
android
val paint = Paint().apply{
color = Color.parseColor("#3E3E3E")
typeface = Typeface.DEFAULT_BOLD
textSize = 24f //px
}
fun onDraw(canvas: Canvas) {
canvas.drawText("android KitKat Lollipop Marshmallow Nougat
Oreo",
10, 10, paint)
}
Draw Text
android KitKat Lollipop Marshmal
“I JUST HAVE TO ADD
n ON MY TEXT !“
StaticLayout
StaticLayout is a Layout for text that will not be edited after it is laid
out. Use DynamicLayout for text that may change.
This is used by widgets to control text layout. You should not need to
use this class directly unless you are implementing your own widget
or custom display object, or would be tempted to call
Canvas.drawText() directly.
https://developer.android.com/reference/android/text/StaticLayout
StaticLayout
StaticLayout(source: CharSequence,
paint: TextPaint,
width: Int,
align: Layout.Alignment,
spacingmult: Float,
spacingadd: Float,
includepad: Boolean)
https://developer.android.com/reference/android/text/StaticLayout
val paint = Paint().apply{
color = Color.parseColor("#3E3E3E")
typeface = Typeface.DEFAULT_BOLD
textSize = 24f //px
}
fun onDraw(canvas: Canvas) {
canvas.drawText("android KitKat Lollipop Marshmallow Nougat
Oreo",
10, 10, paint)
}
Draw Text
android KitKat Lollipop Marshmal
val paint = TextPaint().apply{
color = Color.parseColor("#3E3E3E")
typeface = Typeface.DEFAULT_BOLD
textSize = 24f //px
}
var text = "android KitKat Lollipop Marshmallow Nougat Oreo"
var staticLayout: StaticLayout? = null
Draw Text
android KitKat Lollipop Marshmal
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val textWidth = Math.min(width, textPaint.measureText(text).toInt())
//warning : allocation ondraw, don’t do this
staticLayout = StaticLayout(text, textPaint, textWidth,
Layout.Alignment.ALIGN_NORMAL, 1.0f, 0f, false)
staticLayout!!.draw(canvas)
}
Draw Text
android KitKat Lollipop
Marshmallow Nougat
Oreo
Text Width
“YESSS !!! I CAN DRAW
ALL MY VIEWS !!!!!“
“mmm can you draw
this ?“
CAN YOU DRAW THIS ?
Material Design 2 - ShrineGoogle Fit
CAN YOU DRAW THIS ?
Google Fit
“For 90% of cases, you will need a
custom shape, so let’s see what we can
do with Path?“
“
Path
The Path class encapsulates compound (multiple contour) geometric
paths consisting of straight line segments, quadratic curves, and
cubic curves. It can be drawn with canvas.drawPath(path, paint),
either filled or stroked (based on the paint's Style), or it can be used
for clipping or to draw text on a path.
https://developer.android.com/reference/android/graphics/Path
Draw a Custom Shape
var path = Path()
fun onSizeChanged(width:Int, height: Int,
oldWidth: Int, oldHeight: Int) {
path.reset()
path.move(0,0)
path.lineTo(width, height)
path.lineTo(0, height)
path.close()
}
fun onDraw(canvas: Canvas) {
canvas.drawPath(path, paint)
}
Width
height
Draw a Custom Shape
var path = Path()
fun onSizeChanged(width:Int, height: Int,
oldWidth: Int, oldHeight: Int) {
path.reset()
path.move(0,0)
path.lineTo(width, height)
path.lineTo(0, height)
path.close()
}
fun onDraw(canvas: Canvas) {
canvas.drawPath(path, paint)
}
Width
height
Draw a Custom Shape
var path = Path()
fun onSizeChanged(width:Int, height: Int,
oldWidth: Int, oldHeight: Int) {
path.reset()
path.move(0,0)
path.lineTo(width, height)
path.lineTo(0, height)
path.close()
}
fun onDraw(canvas: Canvas) {
canvas.drawPath(path, paint)
}
Width
height
Draw a Custom Shape
var path = Path()
fun onSizeChanged(width:Int, height: Int,
oldWidth: Int, oldHeight: Int) {
path.reset()
path.move(0,0)
path.lineTo(width, height)
path.lineTo(0, height)
path.close()
}
fun onDraw(canvas: Canvas) {
canvas.drawPath(path, paint)
}
Width
height
Draw a Custom Shape
var path = Path()
fun onSizeChanged(width:Int, height: Int,
oldWidth: Int, oldHeight: Int) {
path.reset()
path.move(0,0)
path.lineTo(width, height)
path.lineTo(0, height)
path.close()
}
fun onDraw(canvas: Canvas) {
canvas.drawPath(path, paint)
}
Width
height
Draw a Custom Shape
var path = Path()
fun onSizeChanged(width:Int, height: Int,
oldWidth: Int, oldHeight: Int) {
path.reset()
path.move(0,0)
path.lineTo(width, height)
path.lineTo(0, height)
path.close()
}
fun onDraw(canvas: Canvas) {
canvas.drawPath(path, paint)
}
Width
height
Draw a Custom Shape
Material Design Study
Shrine
Draw a Custom Shape
path.reset()
path.moveTo(0, 0)
path.lineTo(90, 0)
path.lineTo(100, 10)
path.lineTo(100, 100)
path.lineTo(0, 100)
path.close()
0 100
100
Draw a Custom Shape
path.reset()
path.moveTo(0, 0)
path.lineTo(90, 0)
path.lineTo(100, 10)
path.lineTo(100, 100)
path.lineTo(0, 100)
path.close()
0 100
100
Draw a Custom Shape
path.reset()
path.moveTo(0, 0)
path.lineTo(90, 0)
path.lineTo(100, 10)
path.lineTo(100, 100)
path.lineTo(0, 100)
path.close()
0 100
100
Draw a Custom Shape
path.reset()
path.moveTo(0, 0)
path.lineTo(90, 0)
path.lineTo(100, 10)
path.lineTo(100, 100)
path.lineTo(0, 100)
path.close()
0 100
100
Draw a Custom Shape
path.reset()
path.moveTo(0, 0)
path.lineTo(90, 0)
path.lineTo(100, 10)
path.lineTo(100, 100)
path.lineTo(0, 100)
path.close()
0 100
100
Draw a Custom Shape
path.reset()
path.moveTo(0, 0)
path.lineTo(90, 0)
path.lineTo(100, 10)
path.lineTo(100, 100)
path.lineTo(0, 100)
path.close()
0 100
100
Draw a Custom Shape
path.reset()
path.moveTo(0, 0)
path.lineTo(90, 0)
path.lineTo(100, 10)
path.lineTo(100, 100)
path.lineTo(0, 100)
path.close()
0 100
100
Draw a Custom Shape
path.reset()
path.moveTo(0, 0)
path.lineTo(90, 0)
path.lineTo(100, 10)
path.lineTo(100, 100)
path.lineTo(0, 100)
path.close()
0 100
100
Draw a Custom Shape
???
???
Draw a Custom Shape
0°
-90°
Draw a Custom Shape
0°
-90°
90°
StartAngle = -90
SweepAngle = 90
80
80
100
30
10
10
Draw a Custom Shape
0°
-90°
80
80
100
30
10
10
StartAngle = -90
SweepAngle = 90
Center = (80, 30)
Radius = 20
Rect =
left: 80-20
top: 30-20
right: 80+20
bottom: 30+20
Draw a Custom Shape
0°
-90°
80
80
100
30
10
10
Center = (80, 30)
Radius = 20
Rect =
left: 80-20
top: 30-20
right: 80+20
bottom: 30+20
path.reset()
path.moveTo(10, 10)
path.lineTo(80, 10)
//radius = 20
path.arcTo(Rect(80, 0, 100, 20), -90, 90)
path.lineTo(100, 80)
path.lineTo(10, 80)
path.close()
StartAngle = -90
SweepAngle = 90
StartAngle
SweepAngle
Draw a Custom Shape
path.reset()
path.moveTo(10, 10)
path.lineTo(80, 10)
path.arcTo(Rect(80, 0, 100, 20), -90, 90)
path.lineTo(100, 80)
path.lineTo(10, 80)
path.close()
“and now, you can add
some elevation“
Draw a Custom Shape
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
outlineProvider = object: ViewOutlineProvider() {
override fun getOutline(view: View?, outline: Outline?) {
outline?.setConvexPath(path)
}
}
}
Draw a Custom Shape
Material Design Study
Owl
“another way to draw
curves“
Draw a Custom Shape
path.quadTo(quadX, quadY, endX, endY)
Quadratic curve
endX, endY
Draw a Custom Shape
path.cubicTo(bezierX1, bezierY1, bezierX2, bezierY2, endX, endY)
Bezier curve
endX, endY
“And now, we can animate
this“
Animate Shapes
Google FitAndroid OK… that’s a sample
var rect = RectF(120f, 120f, 400f, 400f)
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawRect(rect, paint)
}
Animations
var rect = RectF(120f, 120f, 400f, 400f)
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawRect(rect, paint)
}
fun animateMe(){
ValueAnimator.ofFloat(rect.right, 700f).apply{
addUpdateListener{
rect.right = it.animatedValue as Float
invalidate()
}
start()
}
}
Animations
var rect = RectF(120f, 120f, 400f, 400f)
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawRect(rect, paint)
}
fun animateMe(){
ValueAnimator.ofFloat(rect.right, 700f).apply{
addUpdateListener{
rect.right = it.animatedValue as Float
invalidate()
}
start()
}
}
Animations
Animations
Arc1
Arc2
Arc3
Background Arc
5° 25°
6°
8°
110°
300°
0° 360°
Start Angle Final Angle
Current Angle
class Arc(val startAngle: Float, val finalAngle: Float, val color: Int) {
val rect = RectF()
val radius = 200f
var currentAngle = 5f
}
Animations
class Arc(val startAngle: Float, val finalAngle: Float, val color: Int) {
val rect = RectF()
val radius = 200f
var currentAngle = 5f
}
Animations
StartAngle
FinalAngle
Color
Radius
class Arc(…) {
…
fun setup(parentWidth: Int, parentHeight: Int){
val centerX = parentWidth/2f
val centerY = parentHeight / 2f
rect.set(centerX - radius, centerY - radius,
centerX + radius, centerY + radius)
}
Animations
Radius
+
class Arc(…) {
…
fun onDraw(paint: Paint, canvas: Canvas){
paint.color = color //update current drawing color
canvas.drawArc(rect, -90f, angleToDraw, false, paint)
}
}
Animations
-90
class ArcsView(context: Context, attributes: AttributeSet) : View(context, attributes) {
val paint = Paint().apply {
strokeWidth = 30f
style = Paint.Style.STROKE
strokeCap = Paint.Cap.ROUND
isAntiAlias = true
}
Animations
class ArcsView(context: Context, attributes: AttributeSet) : View(context, attributes) {
val paint = Paint().apply {
strokeWidth = 30f
style = Paint.Style.STROKE
strokeCap = Paint.Cap.ROUND
isAntiAlias = true
}
Animations
Anti Aliasing
Disabled Enabled
“
I always disable it
in fortnite
“
It slow down
my PC
class ArcsView … {
val arcs = listOf<Arc>(
Arc(8f, 300f, Color.parseColor("#FA8A26")),
Arc(6f, 110f, Color.parseColor("#F5464E")),
Arc(5f, 25f, Color.parseColor("#6498F4"))
)
Animations
class ArcsView … {
val arcs = listOf<Arc>(
Arc(8f, 300f, Color.parseColor("#FA8A26")),
Arc(6f, 110f, Color.parseColor("#F5464E")),
Arc(5f, 25f, Color.parseColor("#6498F4"))
)
val arcBackground = Arc(0f,360f, Color.parseColor("#EBEDEB"))
.apply {
currentAngle = 360f
}
Animations
class ArcsView … {
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
arcBackground.setup(w, h)
arcs.forEach {
it.setup(w, h)
}
}
Animations
class ArcsView … {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
arcBackground.onDraw(paint, canvas)
arcs.forEach {
it.onDraw(paint, canvas)
}
}
Animations
class ArcsView … {
fun animateMe() {
arcs.forEach { arc ->
ValueAnimator.ofFloat(arc.startAngle, arc.finalAngle)
.apply {
duration = 700
addUpdateListener {
arc.currentAngle = it.animatedValue as Float
invalidate()
}
}.start()
}
}
}
Animations
The lasts steps
Lasts steps
Measurement Gesture
Gesture
Example : click on a rect
- event.action is ActionDown
- next event.action is ActionUp
- difference touch X/Y is small
- delay is > 200ms
- the rect contains the point X/Y
Override View.onTouch(motionEvent)
Override View.onTouch(motionEvent)
Example : click on a rect
- use GestureDetector.SimpleOnGestureListener.
- onSingleTapUp
- the rect contains the point X/Y
Gesture
“A last thing, do you know
PorterDuff ?“
PorterDuff
SRC SRC_OUT SRC_OVERSRC_IN
canvas.drawBitmap(destinationImage, 0, 0, paint)
paint.setXfermode(PorterDuffXfermode(mode))
canvas.drawBitmap(sourceImage, 0, 0, paint)
DST DST_INDST_OUT DST_OVER
“
https://github.com/florent37/ShapeOfView
<com.github.florent37.shapeofview.shapes.CutCornerView
android:id="@+id/clipCorner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="30dp"
android:elevation="4dp"
app:shape_cutCorner_topLeftSize="20dp">
<!-- YOUR CONTENT -->
</com.github.florent37.shapeofview.shapes.CutCornerView>
“
https://github.com/florent37/ShapeOfView
<com.github.florent37.shapeofview.shapes.DiagonalView
android:layout_width="match_parent"
android:layout_height="100dp"
android:elevation="4dp"
app:shape_diagonal_angle="10"
app:shape_diagonal_position="bottom">
<!-- YOUR CONTENT —>
</com.github.florent37.shapeofview.shapes.DiagonalView>
“
Thank you Florent
I’m ready to code
my custom views
(and earn $$$)
🍺🍺🍺
THANKS!
Florent37
florent_champ
Slack : Android Dev FR
http://bit.ly/AndroidDevFr

More Related Content

Similar to S'il te plait, dessine moi une vue

Html5 canvas
Html5 canvasHtml5 canvas
Html5 canvas
Nisa Soomro
 
canvas.pptx
canvas.pptxcanvas.pptx
canvas.pptx
VeenaNaik23
 
Intro to HTML5 Canvas
Intro to HTML5 CanvasIntro to HTML5 Canvas
Intro to HTML5 Canvas
Juho Vepsäläinen
 
Custom View
Custom ViewCustom View
Custom View
imaN Khoshabi
 
Canvas
CanvasCanvas
Introduction to Processing
Introduction to ProcessingIntroduction to Processing
Introduction to Processing
Green Moon Solutions
 
Drawing with the HTML5 Canvas
Drawing with the HTML5 CanvasDrawing with the HTML5 Canvas
Drawing with the HTML5 Canvas
Henry Osborne
 
Advanced html5 diving into the canvas tag
Advanced html5 diving into the canvas tagAdvanced html5 diving into the canvas tag
Advanced html5 diving into the canvas tag
David Voyles
 
HTML5 Canvas - The Future of Graphics on the Web
HTML5 Canvas - The Future of Graphics on the WebHTML5 Canvas - The Future of Graphics on the Web
HTML5 Canvas - The Future of Graphics on the Web
Robin Hawkes
 
Scmad Chapter06
Scmad Chapter06Scmad Chapter06
Scmad Chapter06
Marcel Caraciolo
 
Interactive Mouse (Report On Processing)
Interactive Mouse (Report On Processing)Interactive Mouse (Report On Processing)
Interactive Mouse (Report On Processing)
TongXu520
 
Computer Graphics in Java and Scala - Part 1b
Computer Graphics in Java and Scala - Part 1bComputer Graphics in Java and Scala - Part 1b
Computer Graphics in Java and Scala - Part 1b
Philip Schwarz
 
Html5 Canvas Drawing and Animation
Html5 Canvas Drawing and AnimationHtml5 Canvas Drawing and Animation
Html5 Canvas Drawing and Animation
Mindfire Solutions
 
Computer science project work on C++
Computer science project work on C++Computer science project work on C++
Computer science project work on C++
NARESH KUMAR
 
Android canvas-chapter20
Android canvas-chapter20Android canvas-chapter20
Android canvas-chapter20
Dr. Ramkumar Lakshminarayanan
 
Java Fx Overview Tech Tour
Java Fx Overview Tech TourJava Fx Overview Tech Tour
Java Fx Overview Tech Tour
Carol McDonald
 
Raphaël
RaphaëlRaphaël
Css5 canvas
Css5 canvasCss5 canvas
Css5 canvas
Vadim Spiridenko
 
HTML 5_Canvas
HTML 5_CanvasHTML 5_Canvas
HTML 5_Canvas
Vishakha Vaidya
 
Wallpaper Notifier
Wallpaper NotifierWallpaper Notifier
Wallpaper Notifier
Javier Eguiluz
 

Similar to S'il te plait, dessine moi une vue (20)

Html5 canvas
Html5 canvasHtml5 canvas
Html5 canvas
 
canvas.pptx
canvas.pptxcanvas.pptx
canvas.pptx
 
Intro to HTML5 Canvas
Intro to HTML5 CanvasIntro to HTML5 Canvas
Intro to HTML5 Canvas
 
Custom View
Custom ViewCustom View
Custom View
 
Canvas
CanvasCanvas
Canvas
 
Introduction to Processing
Introduction to ProcessingIntroduction to Processing
Introduction to Processing
 
Drawing with the HTML5 Canvas
Drawing with the HTML5 CanvasDrawing with the HTML5 Canvas
Drawing with the HTML5 Canvas
 
Advanced html5 diving into the canvas tag
Advanced html5 diving into the canvas tagAdvanced html5 diving into the canvas tag
Advanced html5 diving into the canvas tag
 
HTML5 Canvas - The Future of Graphics on the Web
HTML5 Canvas - The Future of Graphics on the WebHTML5 Canvas - The Future of Graphics on the Web
HTML5 Canvas - The Future of Graphics on the Web
 
Scmad Chapter06
Scmad Chapter06Scmad Chapter06
Scmad Chapter06
 
Interactive Mouse (Report On Processing)
Interactive Mouse (Report On Processing)Interactive Mouse (Report On Processing)
Interactive Mouse (Report On Processing)
 
Computer Graphics in Java and Scala - Part 1b
Computer Graphics in Java and Scala - Part 1bComputer Graphics in Java and Scala - Part 1b
Computer Graphics in Java and Scala - Part 1b
 
Html5 Canvas Drawing and Animation
Html5 Canvas Drawing and AnimationHtml5 Canvas Drawing and Animation
Html5 Canvas Drawing and Animation
 
Computer science project work on C++
Computer science project work on C++Computer science project work on C++
Computer science project work on C++
 
Android canvas-chapter20
Android canvas-chapter20Android canvas-chapter20
Android canvas-chapter20
 
Java Fx Overview Tech Tour
Java Fx Overview Tech TourJava Fx Overview Tech Tour
Java Fx Overview Tech Tour
 
Raphaël
RaphaëlRaphaël
Raphaël
 
Css5 canvas
Css5 canvasCss5 canvas
Css5 canvas
 
HTML 5_Canvas
HTML 5_CanvasHTML 5_Canvas
HTML 5_Canvas
 
Wallpaper Notifier
Wallpaper NotifierWallpaper Notifier
Wallpaper Notifier
 

Recently uploaded

Eric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball play
Eric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball playEric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball play
Eric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball play
enizeyimana36
 
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
Yasser Mahgoub
 
ISPM 15 Heat Treated Wood Stamps and why your shipping must have one
ISPM 15 Heat Treated Wood Stamps and why your shipping must have oneISPM 15 Heat Treated Wood Stamps and why your shipping must have one
ISPM 15 Heat Treated Wood Stamps and why your shipping must have one
Las Vegas Warehouse
 
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
Sinan KOZAK
 
132/33KV substation case study Presentation
132/33KV substation case study Presentation132/33KV substation case study Presentation
132/33KV substation case study Presentation
kandramariana6
 
Iron and Steel Technology Roadmap - Towards more sustainable steelmaking.pdf
Iron and Steel Technology Roadmap - Towards more sustainable steelmaking.pdfIron and Steel Technology Roadmap - Towards more sustainable steelmaking.pdf
Iron and Steel Technology Roadmap - Towards more sustainable steelmaking.pdf
RadiNasr
 
22CYT12-Unit-V-E Waste and its Management.ppt
22CYT12-Unit-V-E Waste and its Management.ppt22CYT12-Unit-V-E Waste and its Management.ppt
22CYT12-Unit-V-E Waste and its Management.ppt
KrishnaveniKrishnara1
 
Properties Railway Sleepers and Test.pptx
Properties Railway Sleepers and Test.pptxProperties Railway Sleepers and Test.pptx
Properties Railway Sleepers and Test.pptx
MDSABBIROJJAMANPAYEL
 
Understanding Inductive Bias in Machine Learning
Understanding Inductive Bias in Machine LearningUnderstanding Inductive Bias in Machine Learning
Understanding Inductive Bias in Machine Learning
SUTEJAS
 
5214-1693458878915-Unit 6 2023 to 2024 academic year assignment (AutoRecovere...
5214-1693458878915-Unit 6 2023 to 2024 academic year assignment (AutoRecovere...5214-1693458878915-Unit 6 2023 to 2024 academic year assignment (AutoRecovere...
5214-1693458878915-Unit 6 2023 to 2024 academic year assignment (AutoRecovere...
ihlasbinance2003
 
IEEE Aerospace and Electronic Systems Society as a Graduate Student Member
IEEE Aerospace and Electronic Systems Society as a Graduate Student MemberIEEE Aerospace and Electronic Systems Society as a Graduate Student Member
IEEE Aerospace and Electronic Systems Society as a Graduate Student Member
VICTOR MAESTRE RAMIREZ
 
International Conference on NLP, Artificial Intelligence, Machine Learning an...
International Conference on NLP, Artificial Intelligence, Machine Learning an...International Conference on NLP, Artificial Intelligence, Machine Learning an...
International Conference on NLP, Artificial Intelligence, Machine Learning an...
gerogepatton
 
官方认证美国密歇根州立大学毕业证学位证书原版一模一样
官方认证美国密歇根州立大学毕业证学位证书原版一模一样官方认证美国密歇根州立大学毕业证学位证书原版一模一样
官方认证美国密歇根州立大学毕业证学位证书原版一模一样
171ticu
 
New techniques for characterising damage in rock slopes.pdf
New techniques for characterising damage in rock slopes.pdfNew techniques for characterising damage in rock slopes.pdf
New techniques for characterising damage in rock slopes.pdf
wisnuprabawa3
 
Presentation of IEEE Slovenia CIS (Computational Intelligence Society) Chapte...
Presentation of IEEE Slovenia CIS (Computational Intelligence Society) Chapte...Presentation of IEEE Slovenia CIS (Computational Intelligence Society) Chapte...
Presentation of IEEE Slovenia CIS (Computational Intelligence Society) Chapte...
University of Maribor
 
basic-wireline-operations-course-mahmoud-f-radwan.pdf
basic-wireline-operations-course-mahmoud-f-radwan.pdfbasic-wireline-operations-course-mahmoud-f-radwan.pdf
basic-wireline-operations-course-mahmoud-f-radwan.pdf
NidhalKahouli2
 
CSM Cloud Service Management Presentarion
CSM Cloud Service Management PresentarionCSM Cloud Service Management Presentarion
CSM Cloud Service Management Presentarion
rpskprasana
 
哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样
哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样
哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样
insn4465
 
Engineering Drawings Lecture Detail Drawings 2014.pdf
Engineering Drawings Lecture Detail Drawings 2014.pdfEngineering Drawings Lecture Detail Drawings 2014.pdf
Engineering Drawings Lecture Detail Drawings 2014.pdf
abbyasa1014
 
Advanced control scheme of doubly fed induction generator for wind turbine us...
Advanced control scheme of doubly fed induction generator for wind turbine us...Advanced control scheme of doubly fed induction generator for wind turbine us...
Advanced control scheme of doubly fed induction generator for wind turbine us...
IJECEIAES
 

Recently uploaded (20)

Eric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball play
Eric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball playEric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball play
Eric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball play
 
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
 
ISPM 15 Heat Treated Wood Stamps and why your shipping must have one
ISPM 15 Heat Treated Wood Stamps and why your shipping must have oneISPM 15 Heat Treated Wood Stamps and why your shipping must have one
ISPM 15 Heat Treated Wood Stamps and why your shipping must have one
 
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
 
132/33KV substation case study Presentation
132/33KV substation case study Presentation132/33KV substation case study Presentation
132/33KV substation case study Presentation
 
Iron and Steel Technology Roadmap - Towards more sustainable steelmaking.pdf
Iron and Steel Technology Roadmap - Towards more sustainable steelmaking.pdfIron and Steel Technology Roadmap - Towards more sustainable steelmaking.pdf
Iron and Steel Technology Roadmap - Towards more sustainable steelmaking.pdf
 
22CYT12-Unit-V-E Waste and its Management.ppt
22CYT12-Unit-V-E Waste and its Management.ppt22CYT12-Unit-V-E Waste and its Management.ppt
22CYT12-Unit-V-E Waste and its Management.ppt
 
Properties Railway Sleepers and Test.pptx
Properties Railway Sleepers and Test.pptxProperties Railway Sleepers and Test.pptx
Properties Railway Sleepers and Test.pptx
 
Understanding Inductive Bias in Machine Learning
Understanding Inductive Bias in Machine LearningUnderstanding Inductive Bias in Machine Learning
Understanding Inductive Bias in Machine Learning
 
5214-1693458878915-Unit 6 2023 to 2024 academic year assignment (AutoRecovere...
5214-1693458878915-Unit 6 2023 to 2024 academic year assignment (AutoRecovere...5214-1693458878915-Unit 6 2023 to 2024 academic year assignment (AutoRecovere...
5214-1693458878915-Unit 6 2023 to 2024 academic year assignment (AutoRecovere...
 
IEEE Aerospace and Electronic Systems Society as a Graduate Student Member
IEEE Aerospace and Electronic Systems Society as a Graduate Student MemberIEEE Aerospace and Electronic Systems Society as a Graduate Student Member
IEEE Aerospace and Electronic Systems Society as a Graduate Student Member
 
International Conference on NLP, Artificial Intelligence, Machine Learning an...
International Conference on NLP, Artificial Intelligence, Machine Learning an...International Conference on NLP, Artificial Intelligence, Machine Learning an...
International Conference on NLP, Artificial Intelligence, Machine Learning an...
 
官方认证美国密歇根州立大学毕业证学位证书原版一模一样
官方认证美国密歇根州立大学毕业证学位证书原版一模一样官方认证美国密歇根州立大学毕业证学位证书原版一模一样
官方认证美国密歇根州立大学毕业证学位证书原版一模一样
 
New techniques for characterising damage in rock slopes.pdf
New techniques for characterising damage in rock slopes.pdfNew techniques for characterising damage in rock slopes.pdf
New techniques for characterising damage in rock slopes.pdf
 
Presentation of IEEE Slovenia CIS (Computational Intelligence Society) Chapte...
Presentation of IEEE Slovenia CIS (Computational Intelligence Society) Chapte...Presentation of IEEE Slovenia CIS (Computational Intelligence Society) Chapte...
Presentation of IEEE Slovenia CIS (Computational Intelligence Society) Chapte...
 
basic-wireline-operations-course-mahmoud-f-radwan.pdf
basic-wireline-operations-course-mahmoud-f-radwan.pdfbasic-wireline-operations-course-mahmoud-f-radwan.pdf
basic-wireline-operations-course-mahmoud-f-radwan.pdf
 
CSM Cloud Service Management Presentarion
CSM Cloud Service Management PresentarionCSM Cloud Service Management Presentarion
CSM Cloud Service Management Presentarion
 
哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样
哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样
哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样
 
Engineering Drawings Lecture Detail Drawings 2014.pdf
Engineering Drawings Lecture Detail Drawings 2014.pdfEngineering Drawings Lecture Detail Drawings 2014.pdf
Engineering Drawings Lecture Detail Drawings 2014.pdf
 
Advanced control scheme of doubly fed induction generator for wind turbine us...
Advanced control scheme of doubly fed induction generator for wind turbine us...Advanced control scheme of doubly fed induction generator for wind turbine us...
Advanced control scheme of doubly fed induction generator for wind turbine us...
 

S'il te plait, dessine moi une vue

  • 1. S’il te plait, dessine moi une vue
  • 3.
  • 5. One day, on my twitter… “
  • 6. One day, on my twitter… “ @florent_champ can you teach me how to draw a custom view ?
  • 7. @florent_champ please teach me how to draw a custom view ? @florent_champ I saw a video on youtube, a man became rich by building it own app, and displaying ads $$$$$
  • 8. @florent_champ I need to draw custom views, but I don’t know where to start @florent_champ Please !!!!!!! Please !!!!!!! Please !!!!!!!
  • 9. K3v1n : FORTNIGHT <3 <3 Love to code on Windows Learn Android, want to become billionaire with an Android app displaying ads $ $ $ HTML is its favorite programming language
  • 10. @k3v1n can you please stop spamming me ?
  • 11. @florent_champ I will offer you beers 🍺🍺🍺 @k3v1n when can we start ? ☺☺☺
  • 12. “WHERE DO I START“ ? ComicsSansMS
  • 13. “First thing, create a extension of View“ Not ComicsSansMS
  • 14. class MyCustomView(context: Context) : View(context) { override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) { //when the view has a size } override fun onDraw(canvas: Canvas) { //where we draw } } Custom View
  • 15. class MyCustomViewGroup(context: Context) : FrameLayout(context) { init { setWillNotDraw(false) } override fun onDraw(canvas: Canvas) { //where we draw } } Custom ViewGroup
  • 16. class MyCustomViewGroup(context: Context) : FrameLayout(context) { override fun dispatchDraw(canvas: Canvas) { //where we draw } } Custom ViewGroup
  • 17. “Next thing you need to know : Paint“
  • 18. “YEEES ! I ALREADY KNOW PAINT“
  • 19.
  • 20.
  • 21.
  • 22. Paint The Paint class holds the style and color information about how to draw geometries, text and bitmaps. https://developer.android.com/reference/android/graphics/Paint
  • 23. PAINT - Style FILL FILL AND STROKE STROKE paint.setStyle(FILL) paint.setStyle(STROKE) paint.setStyle(FILL_AND_STROKE)
  • 25. PAINT - Stroke Width paint.setStrokeWidth(3) 1px 2px 3px
  • 26. PAINT - Stroke Join BEVEL SQUARE ROUND paint.setStrokeJoin(BEVEL) paint.setStrokeJoin(ROUND) paint.setStrokeJoin(SQUARE)
  • 27. “And with this paint, you can draw shapes on a canvas“
  • 28. Canvas The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing). https://developer.android.com/reference/android/graphics/Canvas
  • 29. val paint = Paint().apply{ color = Color.WHITE } fun onDraw(canvas: Canvas) { canvas.drawRect(0, 0, 30, 10, paint) } Draw a Rect 30 10
  • 30. val paint = Paint().apply{ color = Color.WHITE } fun onDraw(canvas: Canvas) { canvas.drawRect(0, 0, 30, 10, paint) } Draw a Rect 30 10 Values in pixels !
  • 31. Draw a Rect 30 10 val myRect = Rect() fun onSizeChanged(width:Int, height: Int, oldWidth: Int, oldHeight: Int) { myRect.right = width myRect.bottom = height } fun onDraw(canvas: Canvas) { canvas.drawRect(myRect, paint) }
  • 32. Draw a Circle canvas.drawCircle(cx: float, cy: float, radius: float, paint: Paint) fun onDraw(canvas: Canvas) { canvas.drawCircle(100, 100, 15, paint) } 30 15
  • 33. Draw a Line canvas.drawLine(startX: float, startY: float, endX: float, endY: float, paint: Paint) fun onDraw(canvas: Canvas) { canvas.drawLine(0, 30, 100, 50, paint) } 0,30 100,50
  • 34. Draw a Line init { paint.setStrokeWidth(10) paint.setStrokeCap(ROUND) } fun onDraw(canvas: Canvas) { canvas.drawLine(0, 30, 100, 50, paint) } 0,30 100,50
  • 35. Draw a Custom Shape val path = Path() fun onSizeChanged(width:Int, height: Int, oldWidth: Int, oldHeight: Int) { path.reset() path.move(0,0) path.lineTo(width, height) path.lineTo(0, height) path.close() } fun onDraw(canvas: Canvas) { canvas.drawPath(path, paint) } Width height
  • 36. Everything is a shape ! Google fit
  • 37. Everything is a shape ! Line + StrokeCap round Arcs Lines Rectangle
  • 39. Canvas Transformations TRANSLATION canvas.translate(x, y) SCALE ROTATION canvas.scale(x, y) canvas.scale(x, y, pivotX, pivotY) canvas.rotate(degrees) canvas.rotate(degrees, pivotX, pivotY)
  • 40. Translations val myRect = Rect(5, 5, 100, 90) fun onDraw(canvas: Canvas) { canvas.drawRect(myRect, paint) } 0 100 100
  • 41. Translations val myRect = Rect(5, 5, 100, 90) fun onDraw(canvas: Canvas) { canvas.translate(100, 0) canvas.drawRect(myRect, paint) } 0 100 100 0
  • 42. Translations val myRect = Rect(5, 5, 100, 90) fun onDraw(canvas: Canvas) { canvas.translate(100, 0) canvas.drawRect(myRect, paint) canvas.drawCircle(50, 50, 15, paint) } 0 100 100
  • 43. Translations val myRect = Rect(5, 5, 100, 90) fun onDraw(canvas: Canvas) { canvas.save() canvas.translate(100, 0) canvas.drawRect(myRect, paint) canvas.restore() canvas.drawCircle(50, 50, 15, paint) } 0 100 100 0
  • 44. Translations val myRect = Rect(5, 5, 100, 90) fun onDraw(canvas: Canvas) { canvas.save() canvas.translate(100, 0) canvas.drawRect(myRect, paint) canvas.restore() canvas.drawCircle(50, 50, 15, paint) } 0 100 100 new 0
  • 45. Translations val myRect = Rect(5, 5, 100, 90) fun onDraw(canvas: Canvas) { canvas.save() canvas.translate(100, 0) canvas.drawRect(myRect, paint) canvas.restore() canvas.drawCircle(50, 50, 15, paint) } 0 100 100 0
  • 46. Translations val myRect = Rect(5, 5, 100, 90) fun onDraw(canvas: Canvas) { canvas.save() canvas.translate(100, 0) canvas.drawRect(myRect, paint) canvas.restore() canvas.drawCircle(50, 50, 15, paint) } 0 100 100 0
  • 47. Translations val myRect = Rect(5, 5, 100, 90) fun onDraw(canvas: Canvas) { canvas.save() canvas.translate(100, 0) canvas.drawRect(myRect, paint) canvas.restore() canvas.drawCircle(50, 50, 15, paint) } 0 100 100
  • 49. Translations val myRect = Rect(5, 5, 100, 90) fun onDraw(canvas: Canvas) { canvas.save() canvas.translate(10, 0) canvas.save() canvas.translate(90, 0) canvas.drawRect(myRect, paint) canvas.restore() canvas.drawCircle(50, 50, 15, paint) } 0 100 100
  • 50. Translations val myRect = Rect(5, 5, 100, 90) fun onDraw(canvas: Canvas) { val count = canvas.save() canvas.translate(10, 0) canvas.save() canvas.translate(90, 0) canvas.drawRect(myRect, paint) canvas.restoreToCount(count) canvas.drawCircle(50, 50, 15, paint) } 0 100 100
  • 51. Translations + rotations val myRect = Rect(0, 0, 10, 10) fun onDraw(canvas: Canvas) { val count = canvas.save() for(angle in 0..360 step 30) { canvas.rotate(angle) canvas.translate(100, 0) canvas.drawRect(myRect, paint) canvas.restoreToCount(count) } } Center
  • 52. Translations + rotations val myRect = Rect(0, 0, 10, 10) fun onDraw(canvas: Canvas) { val count = canvas.save() for(angle in 0..360 step 30) { canvas.rotate(angle) canvas.translate(100, 0) canvas.drawRect(myRect, paint) canvas.restoreToCount(count) } } Center
  • 53. Translations + rotations val myRect = Rect(0, 0, 10, 10) fun onDraw(canvas: Canvas) { val count = canvas.save() for(angle in 0..360 step 30) { canvas.rotate(angle) canvas.translate(100, 0) canvas.drawRect(myRect, paint) canvas.restoreToCount(count) } } Center
  • 54. Translations + rotations val myRect = Rect(0, 0, 10, 10) fun onDraw(canvas: Canvas) { val count = canvas.save() for(angle in 0..360 step 30) { canvas.rotate(angle) canvas.translate(100, 0) canvas.drawRect(myRect, paint) canvas.restoreToCount(count) } } Center
  • 55. Translations + rotations val myRect = Rect(0, 0, 10, 10) fun onDraw(canvas: Canvas) { val count = canvas.save() for(angle in 0..360 step 30) { canvas.rotate(angle) canvas.translate(100, 0) canvas.drawRect(myRect, paint) canvas.restoreToCount(count) } } Center
  • 56. Translations + rotations val myRect = Rect(0, 0, 10, 10) fun onDraw(canvas: Canvas) { val count = canvas.save() for(angle in 0..360 step 30) { canvas.rotate(angle) canvas.translate(100, 0) canvas.drawRect(myRect, paint) canvas.restoreToCount(count) } } Center
  • 57. Translations + rotations val myRect = Rect(0, 0, 10, 10) fun onDraw(canvas: Canvas) { val count = canvas.save() for(angle in 0..360 step 30) { canvas.rotate(angle) canvas.translate(100, 0) canvas.drawRect(myRect, paint) canvas.restoreToCount(count) } } Center
  • 58. Translations + rotations val myRect = Rect(0, 0, 10, 10) fun onDraw(canvas: Canvas) { val count = canvas.save() for(angle in 0..360 step 30) { canvas.rotate(angle) canvas.translate(100, 0) canvas.drawRect(myRect, paint) canvas.restoreToCount(count) } } Center
  • 59. “I NEED TO DRAW TEXT BY HAND ?“
  • 60. val paint = Paint().apply{ color = Color.parseColor("#3E3E3E") typeface = Typeface.DEFAULT_BOLD textSize = 24f //px } fun onDraw(canvas: Canvas) { canvas.drawText("android", 10, 10, paint) } Draw Text android
  • 61. val paint = Paint().apply{ color = Color.parseColor("#3E3E3E") typeface = Typeface.DEFAULT_BOLD textSize = 24f //px } fun onDraw(canvas: Canvas) { canvas.drawText("android KitKat Lollipop Marshmallow Nougat Oreo", 10, 10, paint) } Draw Text android KitKat Lollipop Marshmal
  • 62. “I JUST HAVE TO ADD n ON MY TEXT !“
  • 63.
  • 64.
  • 65. StaticLayout StaticLayout is a Layout for text that will not be edited after it is laid out. Use DynamicLayout for text that may change. This is used by widgets to control text layout. You should not need to use this class directly unless you are implementing your own widget or custom display object, or would be tempted to call Canvas.drawText() directly. https://developer.android.com/reference/android/text/StaticLayout
  • 66. StaticLayout StaticLayout(source: CharSequence, paint: TextPaint, width: Int, align: Layout.Alignment, spacingmult: Float, spacingadd: Float, includepad: Boolean) https://developer.android.com/reference/android/text/StaticLayout
  • 67. val paint = Paint().apply{ color = Color.parseColor("#3E3E3E") typeface = Typeface.DEFAULT_BOLD textSize = 24f //px } fun onDraw(canvas: Canvas) { canvas.drawText("android KitKat Lollipop Marshmallow Nougat Oreo", 10, 10, paint) } Draw Text android KitKat Lollipop Marshmal
  • 68. val paint = TextPaint().apply{ color = Color.parseColor("#3E3E3E") typeface = Typeface.DEFAULT_BOLD textSize = 24f //px } var text = "android KitKat Lollipop Marshmallow Nougat Oreo" var staticLayout: StaticLayout? = null Draw Text android KitKat Lollipop Marshmal
  • 69. override fun onDraw(canvas: Canvas) { super.onDraw(canvas) val textWidth = Math.min(width, textPaint.measureText(text).toInt()) //warning : allocation ondraw, don’t do this staticLayout = StaticLayout(text, textPaint, textWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0f, false) staticLayout!!.draw(canvas) } Draw Text android KitKat Lollipop Marshmallow Nougat Oreo Text Width
  • 70. “YESSS !!! I CAN DRAW ALL MY VIEWS !!!!!“
  • 71. “mmm can you draw this ?“
  • 72. CAN YOU DRAW THIS ? Material Design 2 - ShrineGoogle Fit
  • 73. CAN YOU DRAW THIS ? Google Fit
  • 74. “For 90% of cases, you will need a custom shape, so let’s see what we can do with Path?“ “
  • 75. Path The Path class encapsulates compound (multiple contour) geometric paths consisting of straight line segments, quadratic curves, and cubic curves. It can be drawn with canvas.drawPath(path, paint), either filled or stroked (based on the paint's Style), or it can be used for clipping or to draw text on a path. https://developer.android.com/reference/android/graphics/Path
  • 76. Draw a Custom Shape var path = Path() fun onSizeChanged(width:Int, height: Int, oldWidth: Int, oldHeight: Int) { path.reset() path.move(0,0) path.lineTo(width, height) path.lineTo(0, height) path.close() } fun onDraw(canvas: Canvas) { canvas.drawPath(path, paint) } Width height
  • 77. Draw a Custom Shape var path = Path() fun onSizeChanged(width:Int, height: Int, oldWidth: Int, oldHeight: Int) { path.reset() path.move(0,0) path.lineTo(width, height) path.lineTo(0, height) path.close() } fun onDraw(canvas: Canvas) { canvas.drawPath(path, paint) } Width height
  • 78. Draw a Custom Shape var path = Path() fun onSizeChanged(width:Int, height: Int, oldWidth: Int, oldHeight: Int) { path.reset() path.move(0,0) path.lineTo(width, height) path.lineTo(0, height) path.close() } fun onDraw(canvas: Canvas) { canvas.drawPath(path, paint) } Width height
  • 79. Draw a Custom Shape var path = Path() fun onSizeChanged(width:Int, height: Int, oldWidth: Int, oldHeight: Int) { path.reset() path.move(0,0) path.lineTo(width, height) path.lineTo(0, height) path.close() } fun onDraw(canvas: Canvas) { canvas.drawPath(path, paint) } Width height
  • 80. Draw a Custom Shape var path = Path() fun onSizeChanged(width:Int, height: Int, oldWidth: Int, oldHeight: Int) { path.reset() path.move(0,0) path.lineTo(width, height) path.lineTo(0, height) path.close() } fun onDraw(canvas: Canvas) { canvas.drawPath(path, paint) } Width height
  • 81. Draw a Custom Shape var path = Path() fun onSizeChanged(width:Int, height: Int, oldWidth: Int, oldHeight: Int) { path.reset() path.move(0,0) path.lineTo(width, height) path.lineTo(0, height) path.close() } fun onDraw(canvas: Canvas) { canvas.drawPath(path, paint) } Width height
  • 82. Draw a Custom Shape Material Design Study Shrine
  • 83. Draw a Custom Shape path.reset() path.moveTo(0, 0) path.lineTo(90, 0) path.lineTo(100, 10) path.lineTo(100, 100) path.lineTo(0, 100) path.close() 0 100 100
  • 84. Draw a Custom Shape path.reset() path.moveTo(0, 0) path.lineTo(90, 0) path.lineTo(100, 10) path.lineTo(100, 100) path.lineTo(0, 100) path.close() 0 100 100
  • 85. Draw a Custom Shape path.reset() path.moveTo(0, 0) path.lineTo(90, 0) path.lineTo(100, 10) path.lineTo(100, 100) path.lineTo(0, 100) path.close() 0 100 100
  • 86. Draw a Custom Shape path.reset() path.moveTo(0, 0) path.lineTo(90, 0) path.lineTo(100, 10) path.lineTo(100, 100) path.lineTo(0, 100) path.close() 0 100 100
  • 87. Draw a Custom Shape path.reset() path.moveTo(0, 0) path.lineTo(90, 0) path.lineTo(100, 10) path.lineTo(100, 100) path.lineTo(0, 100) path.close() 0 100 100
  • 88. Draw a Custom Shape path.reset() path.moveTo(0, 0) path.lineTo(90, 0) path.lineTo(100, 10) path.lineTo(100, 100) path.lineTo(0, 100) path.close() 0 100 100
  • 89. Draw a Custom Shape path.reset() path.moveTo(0, 0) path.lineTo(90, 0) path.lineTo(100, 10) path.lineTo(100, 100) path.lineTo(0, 100) path.close() 0 100 100
  • 90. Draw a Custom Shape path.reset() path.moveTo(0, 0) path.lineTo(90, 0) path.lineTo(100, 10) path.lineTo(100, 100) path.lineTo(0, 100) path.close() 0 100 100
  • 91. Draw a Custom Shape ??? ???
  • 92. Draw a Custom Shape 0° -90°
  • 93. Draw a Custom Shape 0° -90° 90° StartAngle = -90 SweepAngle = 90 80 80 100 30 10 10
  • 94. Draw a Custom Shape 0° -90° 80 80 100 30 10 10 StartAngle = -90 SweepAngle = 90 Center = (80, 30) Radius = 20 Rect = left: 80-20 top: 30-20 right: 80+20 bottom: 30+20
  • 95. Draw a Custom Shape 0° -90° 80 80 100 30 10 10 Center = (80, 30) Radius = 20 Rect = left: 80-20 top: 30-20 right: 80+20 bottom: 30+20 path.reset() path.moveTo(10, 10) path.lineTo(80, 10) //radius = 20 path.arcTo(Rect(80, 0, 100, 20), -90, 90) path.lineTo(100, 80) path.lineTo(10, 80) path.close() StartAngle = -90 SweepAngle = 90 StartAngle SweepAngle
  • 96. Draw a Custom Shape path.reset() path.moveTo(10, 10) path.lineTo(80, 10) path.arcTo(Rect(80, 0, 100, 20), -90, 90) path.lineTo(100, 80) path.lineTo(10, 80) path.close()
  • 97. “and now, you can add some elevation“
  • 98. Draw a Custom Shape if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { outlineProvider = object: ViewOutlineProvider() { override fun getOutline(view: View?, outline: Outline?) { outline?.setConvexPath(path) } } }
  • 99. Draw a Custom Shape Material Design Study Owl
  • 100. “another way to draw curves“
  • 101. Draw a Custom Shape path.quadTo(quadX, quadY, endX, endY) Quadratic curve endX, endY
  • 102. Draw a Custom Shape path.cubicTo(bezierX1, bezierY1, bezierX2, bezierY2, endX, endY) Bezier curve endX, endY
  • 103. “And now, we can animate this“
  • 104. Animate Shapes Google FitAndroid OK… that’s a sample
  • 105. var rect = RectF(120f, 120f, 400f, 400f) override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.drawRect(rect, paint) } Animations
  • 106. var rect = RectF(120f, 120f, 400f, 400f) override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.drawRect(rect, paint) } fun animateMe(){ ValueAnimator.ofFloat(rect.right, 700f).apply{ addUpdateListener{ rect.right = it.animatedValue as Float invalidate() } start() } } Animations
  • 107. var rect = RectF(120f, 120f, 400f, 400f) override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.drawRect(rect, paint) } fun animateMe(){ ValueAnimator.ofFloat(rect.right, 700f).apply{ addUpdateListener{ rect.right = it.animatedValue as Float invalidate() } start() } } Animations
  • 109. class Arc(val startAngle: Float, val finalAngle: Float, val color: Int) { val rect = RectF() val radius = 200f var currentAngle = 5f } Animations
  • 110. class Arc(val startAngle: Float, val finalAngle: Float, val color: Int) { val rect = RectF() val radius = 200f var currentAngle = 5f } Animations StartAngle FinalAngle Color Radius
  • 111. class Arc(…) { … fun setup(parentWidth: Int, parentHeight: Int){ val centerX = parentWidth/2f val centerY = parentHeight / 2f rect.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius) } Animations Radius +
  • 112. class Arc(…) { … fun onDraw(paint: Paint, canvas: Canvas){ paint.color = color //update current drawing color canvas.drawArc(rect, -90f, angleToDraw, false, paint) } } Animations -90
  • 113. class ArcsView(context: Context, attributes: AttributeSet) : View(context, attributes) { val paint = Paint().apply { strokeWidth = 30f style = Paint.Style.STROKE strokeCap = Paint.Cap.ROUND isAntiAlias = true } Animations
  • 114. class ArcsView(context: Context, attributes: AttributeSet) : View(context, attributes) { val paint = Paint().apply { strokeWidth = 30f style = Paint.Style.STROKE strokeCap = Paint.Cap.ROUND isAntiAlias = true } Animations
  • 116. “ I always disable it in fortnite
  • 118. class ArcsView … { val arcs = listOf<Arc>( Arc(8f, 300f, Color.parseColor("#FA8A26")), Arc(6f, 110f, Color.parseColor("#F5464E")), Arc(5f, 25f, Color.parseColor("#6498F4")) ) Animations
  • 119. class ArcsView … { val arcs = listOf<Arc>( Arc(8f, 300f, Color.parseColor("#FA8A26")), Arc(6f, 110f, Color.parseColor("#F5464E")), Arc(5f, 25f, Color.parseColor("#6498F4")) ) val arcBackground = Arc(0f,360f, Color.parseColor("#EBEDEB")) .apply { currentAngle = 360f } Animations
  • 120. class ArcsView … { override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) arcBackground.setup(w, h) arcs.forEach { it.setup(w, h) } } Animations
  • 121. class ArcsView … { override fun onDraw(canvas: Canvas) { super.onDraw(canvas) arcBackground.onDraw(paint, canvas) arcs.forEach { it.onDraw(paint, canvas) } } Animations
  • 122. class ArcsView … { fun animateMe() { arcs.forEach { arc -> ValueAnimator.ofFloat(arc.startAngle, arc.finalAngle) .apply { duration = 700 addUpdateListener { arc.currentAngle = it.animatedValue as Float invalidate() } }.start() } } } Animations
  • 125. Gesture Example : click on a rect - event.action is ActionDown - next event.action is ActionUp - difference touch X/Y is small - delay is > 200ms - the rect contains the point X/Y Override View.onTouch(motionEvent)
  • 126. Override View.onTouch(motionEvent) Example : click on a rect - use GestureDetector.SimpleOnGestureListener. - onSingleTapUp - the rect contains the point X/Y Gesture
  • 127. “A last thing, do you know PorterDuff ?“
  • 128. PorterDuff SRC SRC_OUT SRC_OVERSRC_IN canvas.drawBitmap(destinationImage, 0, 0, paint) paint.setXfermode(PorterDuffXfermode(mode)) canvas.drawBitmap(sourceImage, 0, 0, paint) DST DST_INDST_OUT DST_OVER
  • 131. “ Thank you Florent I’m ready to code my custom views (and earn $$$) 🍺🍺🍺
  • 132. THANKS! Florent37 florent_champ Slack : Android Dev FR http://bit.ly/AndroidDevFr