Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Master your custom views
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Rui Gonçalo
October 18, 2017
Programming
1
140
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
Tweet
Share
More Decks by Rui Gonçalo
See All by Rui Gonçalo
NYC Android meetup May 2019
ruigoncalo
0
34
Start speking it @ Droidcon Krakow 2017
ruigoncalo
0
120
Espressing yourself
ruigoncalo
0
69
Other Decks in Programming
See All in Programming
Best-Practices-for-Cortex-Analyst-and-AI-Agent
ryotaroikeda
1
110
AIエージェントのキホンから学ぶ「エージェンティックコーディング」実践入門
masahiro_nishimi
5
460
高速開発のためのコード整理術
sutetotanuki
1
400
CSC307 Lecture 05
javiergs
PRO
0
500
Fluid Templating in TYPO3 14
s2b
0
130
Honoを使ったリモートMCPサーバでAIツールとの連携を加速させる!
tosuri13
1
180
Amazon Bedrockを活用したRAGの品質管理パイプライン構築
tosuri13
4
690
フルサイクルエンジニアリングをAI Agentで全自動化したい 〜構想と現在地〜
kamina_zzz
0
400
Unicodeどうしてる? PHPから見たUnicode対応と他言語での対応についてのお伺い
youkidearitai
PRO
1
2.5k
dchart: charts from deck markup
ajstarks
3
990
Lambda のコードストレージ容量に気をつけましょう
tattwan718
0
130
React 19でつくる「気持ちいいUI」- 楽観的UIのすすめ
himorishige
11
7.4k
Featured
See All Featured
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.3k
The Curious Case for Waylosing
cassininazir
0
240
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
1
1.4k
Why You Should Never Use an ORM
jnunemaker
PRO
61
9.7k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
830
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
1
440
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
55k
Unsuck your backbone
ammeep
671
58k
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
76
Java REST API Framework Comparison - PWX 2021
mraible
34
9.1k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
1.9k
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
0
2.3k
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