Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Master your custom views
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Rui Gonçalo
October 18, 2017
Programming
160
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Master your custom views
Real examples of how to create pixel perfect views that will make your design team happy :)
Rui Gonçalo
October 18, 2017
More Decks by Rui Gonçalo
See All by Rui Gonçalo
NYC Android meetup May 2019
ruigoncalo
0
42
Start speking it @ Droidcon Krakow 2017
ruigoncalo
0
130
Espressing yourself
ruigoncalo
0
76
Other Decks in Programming
See All in Programming
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
750
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
1k
Kotlinの15年とServer-Side Kotlin
n_takehata
1
150
すこし踏み込む CancellationToken
htkym
2
1.7k
AIに “答え“を書かせるのを やめてみた
___akari11
0
120
Unity6 URPによる海底をテーマにしたVJシステムの解説 | Unity Shader 完全に理解した 勉強会
gam0022
0
370
パズルゲームの作り方 / how to make puzzle games
kaityo256
PRO
2
290
Long Living Tokens, Die, Die, Die! (or: The One Ring is Banned in Modern)
sylph01
0
300
contenteditable と日本語入力に向き合う
colorful12
0
200
golang-bob.pdf
iwatsukayura
0
130
最近”作らせてる” ローカルAIアプリの話
akit37
0
470
AIで開発が速くなった先で、開発チームは何を目指すか
shihochan
0
880
Featured
See All Featured
Optimising Largest Contentful Paint
csswizardry
37
4k
Amusing Abliteration
ianozsvald
1
350
Bash Introduction
62gerente
615
220k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.9k
Fireside Chat
paigeccino
43
4.1k
Context Engineering - Making Every Token Count
addyosmani
9
1.2k
The Invisible Side of Design
smashingmag
301
52k
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
2
480
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
570
Rebuilding a faster, lazier Slack
samanthasiow
85
9.7k
Six Lessons from altMBA
skipperchong
29
4.5k
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
450
Transcript
Master your custom views @rmgoncalo Rui Gonçalo
Screens
Screen size: physical size (diagonal) e.g: 5.5’’
Screen size: physical size (diagonal) e.g: 5.5’’ Resolution: # of
physical pixels on a screen e.g: 1080 pixels x 1920 pixels
Screen size: physical size (diagonal) e.g: 5.5’’ Screen density: #
of pixels within a physical area e.g: 160 dots per inch (dpi) Resolution: # of physical pixels on a screen e.g: 1080 pixels x 1920 pixels
Density-independent pixel (dp): virtual pixel that abstracts different density screens
1 dp = 1 pixel on 160 dpi
None
resolution 1080x1920 density xxhdpi 300dp x 100dp 250dp x 100dp
200dp x 100dp 150dp x 100dp
300dp x 100dp 250dp x 100dp 200dp x 100dp 150dp
x 100dp
300dp x 100dp 250dp x 100dp 200dp x 100dp 150dp
x 100dp resolution 1080x1920 density xxhdpi resolution 1080x1920 density xxhdpi
resources.displayMetrics.density resolution 1080x1920 density xxhdpi
2.6 resolution 1080x1920 density xxhdpi resources.displayMetrics.density
2.6 160 * 2.6 = 416 dpi width 1080 /
2.6 ~ 411 dp height 1920 / 2.6 ~ 731 dp resolution 1080x1920 density xxhdpi resources.displayMetrics.density
2.6 300 dp * 2.6 = 780 px 250 dp
* 2.6 = 650 px 780px 650px 520px 390px 200 dp * 2.6 = 520 px 150 dp * 2.6 = 390 px 160 * 2.6 = 416 dpi width 1080 / 2.6 ~ 411 dp height 1920 / 2.6 ~ 731 dp resolution 1080x1920 density xxhdpi resources.displayMetrics.density
resources.displayMetrics.density resolution 1080x1920 density xxhdpi
3.0 160 * 3.0 = 480 dpi width 1080 /
3.0 ~ 360 dp height 1920 / 3.0 ~ 640 dp resolution 1080x1920 density xxhdpi resources.displayMetrics.density
3.0 300 dp * 3.0 = 900 px 250 dp
* 3.0 = 750 px 200 dp * 3.0 = 600 px 150 dp * 3.0 = 450 px 900px 750px 600px 450px 160 * 3.0 = 480 dpi width 1080 / 3.0 ~ 360 dp height 1920 / 3.0 ~ 640 dp resolution 1080x1920 density xxhdpi resources.displayMetrics.density
resolution 1080x1920 size (dp) 411x731 resolution 1080x1920 size (dp) 360x640
Google Pixel Nexus 5 resolution 1080x1920 size (dp) 411x731 resolution
1080x1920 size (dp) 360x640
Wide range of Android devices with different screen resolutions and
densities
<View android:layout_width=“300dp” android:layout_height=“100dp” />
/res/values-sw411dp/dimens.xml /res/layout-sw411dp <View android:layout_width=“@dimen/block_width” android:layout_height=“100dp” /> sw?
topView centerTopView centerBottomView bottomView val screenWidth = resources.displayMetrics.widthPixels val topW
= screenWidth * ( 300 / 360f ) val cTopW = screenWidth * ( 250 / 360f ) val cBottomW = screenWidth * ( 200 / 360f ) val bottomW = screenWidth * ( 150 / 360f ) val height = (100 * resources.displayMetrics.density) default view width default screen width
topView centerTopView centerBottomView bottomView topView.layoutParams = LinearLayout.LayoutParams(topW, height) centerTopView.layoutParams =
LinearLayout.LayoutParams(cTopW, height) centerBottomView.layoutParams = LinearLayout.LayoutParams(cBottomW, height) bottomView.layoutParams = LinearLayout.LayoutParams(bottomW, height)
Design for 1080x1920 480dpi (xxhdpi) take into account different screens
width (do the math) https://material.io/devices/ Don’t trust the Android emulator Google Pixel has 480 DPI instead of 416 DPI
Custom Views
class CustomView : View { } ViewGroup FrameLayout …
class CustomView : View { constructor(context: Context) : super(context) constructor(context:
Context, attrs: AttributeSet) : super(context, attrs) }
class CustomView : View { override fun onAttachedToWindow() override fun
onDetachedFromWindow() }
onMeasure() onLayout() onDraw() onAttachedToWindow() onDetachedFromWindow()
onDraw()
onDraw()
Canvas()
Canvas() Y X resources.displayMetrics .widthPixels resources.displayMetrics .heightPixels
Canvas() Y X resources.displayMetrics .widthPixels resources.displayMetrics .heightPixels Paint() - geometries
- text - bitmaps Path()
resources.displayMetrics.widthPixels ? ? ? ? ?
? ? 70dp ? ? resources.displayMetrics.widthPixels
? ? ? 70dp 50dp resources.displayMetrics.widthPixels
? 110dp 20dp 70dp 50dp resources.displayMetrics.widthPixels
110dp 20dp 70dp 50dp resources.displayMetrics.widthPixels resources.displayMetrics.widthPixels-130dp
110dp 20dp 70dp 50dp resources.displayMetrics.widthPixels resources.displayMetrics.widthPixels-130dp
path.lineTo(0f, containerHeight) (0, 0) (0, containerHeight)
path.lineTo(screenWidthPx - tagWidthBigPx, containerHeight)
path.cubicTo( screenWidthPx - tagWidthSmallPx, containerHeight, screenWidthPx - tagWidthBigPx, containerHeight +
tagHeightPx, screenWidthPx - tagWidthSmallPx, containerHeight + tagHeightPx )
path.cubicTo( screenWidthPx - tagWidthSmallPx, containerHeight, screenWidthPx - tagWidthBigPx, containerHeight +
tagHeightPx, screenWidthPx - tagWidthSmallPx, containerHeight + tagHeightPx ) http://cubic-bezier.com/
x path.cubicTo( screenWidthPx - tagWidthSmallPx, containerHeight, screenWidthPx - tagWidthBigPx, containerHeight
+ tagHeightPx, screenWidthPx - tagWidthSmallPx, containerHeight + tagHeightPx )
path.cubicTo( screenWidthPx - tagWidthSmallPx, containerHeight, screenWidthPx - tagWidthBigPx, containerHeight +
tagHeightPx, screenWidthPx - tagWidthSmallPx, containerHeight + tagHeightPx ) x
path.cubicTo( screenWidthPx - tagWidthSmallPx, containerHeight, screenWidthPx - tagWidthBigPx, containerHeight +
tagHeightPx, screenWidthPx - tagWidthSmallPx, containerHeight + tagHeightPx ) x
path.lineTo(screenWidthPx, containerHeight + tagHeightPx)
path.lineTo(screenWidthPx, 0f)
path.lineTo(0f, 0f)
private val path by lazy { Path() } override fun
onDraw(canvas: Canvas) { super.onDraw(canvas) with(path) { lineTo(…) cubicTo(…) … } canvas.drawPath(path, paint) }
private val paint by lazy { Paint().apply { style =
Paint.Style.FILL color = Color.WHITE isAntiAlias = true } }
onMeasure() onLayout() onDraw() onAttachedToWindow() onDetachedFromWindow()
None
<FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/blue"> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <com.example.CustomView android:layout_width="match_parent" android:layout_height="wrap_content"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end|bottom" android:layout_margin="12dp" android:text="123" android:textSize="30sp"/> </FrameLayout> </FrameLayout>
<FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/blue"> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <com.example.CustomView android:layout_width="match_parent" android:layout_height="wrap_content"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end|bottom" android:layout_margin="12dp" android:text="123" android:textSize="30sp"/> </FrameLayout> </FrameLayout>
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val customHeightMeasuredSpec
= MeasureSpec.makeMeasureSpec( containerHeight + tagHeightPx, MeasureSpec.EXACTLY) super.onMeasure(widthMeasureSpec, customHeightMeasuredSpec) } setMeasuredDimension(measuredWidth: Int, measuredHeight: Int)
onMeasure() onLayout() onDraw() onAttachedToWindow() onDetachedFromWindow() invalidate()
None
BL BR TR TL
BL BR TR TL
BL BR TR TL
BL BR TR TL
BL BR TR TL AL AR
BL BR TR TL AL AR
BL BR TR TL AL AR
TL width center in x axis = width / 2
TLx = width / 2 - peekTopWidth / 2 TLy
= 0f TL width center in x axis = width / 2 peekTopWidth y x
BLx = (width / 2 - peekBottomWidth / 2) *
(1 - offset * 3) BL x offset = 0,1 BLx = 100 * (1 - 0,1 * 3) = 100 * 0,7 = 70 offset = 0,2 BLx = 100 * (1 - 0,2 * 3) = 100 * 0,4 = 40
BL y peekHeight BLy = if(BLx >= 0) { peekHeight
} else { ?????????? }
BL y BLy = if(BLx >= 0) { peekHeight }
else { ?????????? } peekHeight
peekHeight BLy = peekHeight - (peekHeight * offset) offset =
0,1 BLy = 100 - (100 * 0,1) = 100 - 10 = 90 offset = 0,6 BLy = 100 - (100 * 0,6) = 100 - 60 = 40
peekHeight BLy = peekHeight - (peekHeight * offset) offset =
0,1 BLy = 100 - (100 * 0,1) = 100 - 10 = 90 offset = 0,35 BLy = 100 - (100 * 0,35) = 100 - 35 = 65 offset is 0,35!!!
x x (anchorOffset, 0) (1, 1) offset = 0,35 peekHeight
peekHeight - (peekHeight * offset)
x x (anchorOffset, 0) (1, 1) To calculate the slope
of a line you need only two points from that line m = ( y2 - y1 ) / ( x2 - x1 )
x x (anchorOffset, 0) (1, 1) m = ( 1
- 0 ) / ( 1 - anchorOffset )
x x (anchorOffset, 0) (1, 1) Equation of a line
given a point and the slope y - y1 = m ( x - x1 )
x x (anchorOffset, 0) (1, 1) y - 0 =
( 1 / (1 - anchorOffset ) ) * ( x - anchorOffset )
x x (anchorOffset, 0) (1, 1) y = ( x
- anchorOffset ) / ( 1 - anchorOffset )
BLy = if(BLx >= 0) { peekHeight } else
{ if(anchorOffset = 0) { anchorOffset = offset } verticalOffset = (offset - anchorOffset) / (1 - anchorOffset) peekHeight - (peekHeight * verticalOffset) } BL y y = ( x - anchorOffset ) / ( 1 - anchorOffset )
BL BR TR TL TRx = width - TLx TRy
= TLy BRx = width - BLx BRy = BLy
ALx = BLx ALy = peekHeight ARx = BRx ARy
= peekHeight BL BR TR TL AL AR
override fun onPanelSlide(slideOffset: Float) { offset = slideOffset invalidate() }
private fun init() { setWillNotDraw(false) LayoutInflater.from(context).inflate(xml, this, true) invalidate() }
Be careful with onDraw() don’t call methods that call onDraw()
or requestLayout()
Master your custom views #19231A DARK JUNGLE GREEN @rmgoncalo Rui
Gonçalo