Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Master your custom views

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Master your custom views

Real examples of how to create pixel perfect views that will make your design team happy :)

Avatar for Rui Gonçalo

Rui Gonçalo

October 18, 2017
Tweet

More Decks by Rui Gonçalo

Other Decks in Programming

Transcript

  1. Screen size: physical size (diagonal) e.g: 5.5’’ Resolution: # of

    physical pixels on a screen e.g: 1080 pixels x 1920 pixels
  2. 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
  3. 300dp x 100dp 250dp x 100dp 200dp x 100dp 150dp

    x 100dp resolution 1080x1920
 density xxhdpi resolution 1080x1920
 density xxhdpi
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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)
  10. 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
  11. path.cubicTo( screenWidthPx - tagWidthSmallPx, containerHeight,
 screenWidthPx - tagWidthBigPx, containerHeight +

    tagHeightPx,
 screenWidthPx - tagWidthSmallPx, containerHeight + tagHeightPx )
  12. path.cubicTo( screenWidthPx - tagWidthSmallPx, containerHeight,
 screenWidthPx - tagWidthBigPx, containerHeight +

    tagHeightPx,
 screenWidthPx - tagWidthSmallPx, containerHeight + tagHeightPx ) http://cubic-bezier.com/
  13. x path.cubicTo( screenWidthPx - tagWidthSmallPx, containerHeight,
 screenWidthPx - tagWidthBigPx, containerHeight

    + tagHeightPx,
 screenWidthPx - tagWidthSmallPx, containerHeight + tagHeightPx )
  14. path.cubicTo( screenWidthPx - tagWidthSmallPx, containerHeight,
 screenWidthPx - tagWidthBigPx, containerHeight +

    tagHeightPx,
 screenWidthPx - tagWidthSmallPx, containerHeight + tagHeightPx ) x
  15. path.cubicTo( screenWidthPx - tagWidthSmallPx, containerHeight,
 screenWidthPx - tagWidthBigPx, containerHeight +

    tagHeightPx,
 screenWidthPx - tagWidthSmallPx, containerHeight + tagHeightPx ) x
  16. private val path by lazy { Path() } override fun

    onDraw(canvas: Canvas) { super.onDraw(canvas) with(path) { lineTo(…)
 cubicTo(…) … } canvas.drawPath(path, paint) }
  17. private val paint by lazy { Paint().apply { style =

    Paint.Style.FILL color = Color.WHITE isAntiAlias = true } }
  18. <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>
  19. <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>
  20. override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { 
 val customHeightMeasuredSpec

    =
 MeasureSpec.makeMeasureSpec( containerHeight + tagHeightPx, MeasureSpec.EXACTLY)
 
 super.onMeasure(widthMeasureSpec, customHeightMeasuredSpec) } setMeasuredDimension(measuredWidth: Int, measuredHeight: Int)
  21. TLx = width / 2 - peekTopWidth / 2 TLy

    = 0f TL width center in x axis = width / 2 peekTopWidth y x
  22. 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
  23. BL y BLy = if(BLx >= 0) { peekHeight }

    else { 
 ??????????
 } peekHeight
  24. 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
  25. 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!!!
  26. x x (anchorOffset, 0) (1, 1) offset = 0,35 peekHeight

    peekHeight - (peekHeight * offset)
  27. 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 )
  28. x x (anchorOffset, 0) (1, 1) m = ( 1

    - 0 ) / ( 1 - anchorOffset )
  29. x x (anchorOffset, 0) (1, 1) Equation of a line

    given a point and the slope y - y1 = m ( x - x1 )
  30. x x (anchorOffset, 0) (1, 1) y - 0 =

    ( 1 / (1 - anchorOffset ) ) * ( x - anchorOffset )
  31. x x (anchorOffset, 0) (1, 1) y = ( x

    - anchorOffset ) / ( 1 - anchorOffset )
  32. 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 )
  33. BL BR TR TL TRx = width - TLx TRy

    = TLy BRx = width - BLx BRy = BLy
  34. ALx = BLx ALy = peekHeight ARx = BRx ARy

    = peekHeight BL BR TR TL AL AR
  35. override fun onPanelSlide(slideOffset: Float) { offset = slideOffset invalidate() }

    private fun init() { setWillNotDraw(false) LayoutInflater.from(context).inflate(xml, this, true)
 invalidate() }