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

Master your custom views

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

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() }