Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

Android Wear Essentials

669 vues

Publié le

Improve your Android skills, building stunning watch faces

Publié dans : Technologie
  • Soyez le premier à commenter

Android Wear Essentials

  1. 1. Android Wear Essen-als Improve your Android skills, building watch faces
  2. 2. What you should know about Android Wear
  3. 3. Android Wear • Android SDK • Specific wear APIs in external libraries (support:wearable, play-services-wearable) • All devices are compa>ble with API 23 (minSdkVersion 23) • Only a few devices are compa>ble with API > 23 ("Android Wear 2.0")
  4. 4. Crea%ng a wear app h"ps://developer.android.com/training/building-wearables.html
  5. 5. Crea%ng a wear app • Ac$vity • AndroidManifest.xml • LayoutInflater • You know the stuff...
  6. 6. Crea%ng a watch face h"ps://developer.android.com/training/wearables/watch-faces/index.html
  7. 7. Crea%ng a watch face 1. Create a class that extends CanvasWatchFaceService.Engine 2. Create a Service that extends CanvasWatchFaceService and override onCreateEngine Prefer OpenGL ES instead of the Canvas API ? Use Gles2WatchFaceService instead
  8. 8. Create a watch face project
  9. 9. Ba#ery usage
  10. 10. AMOLED
  11. 11. Interac(ve mode (default)
  12. 12. Ambient mode / Low-bit Ambient
  13. 13. Low-bit Ambient / Ambient
  14. 14. Burn-in effect
  15. 15. Burn-in effect h"ps://en.wikipedia.org/wiki/Screen_burn-in
  16. 16. Burn-in effect
  17. 17. Burn-in effect
  18. 18. Avoid burn-in • Do not draw large areas of pixels in ambient mode • Do not place content within 10 pixels of the edge of the screen
  19. 19. I'm bored, can I see some code?
  20. 20. class Engine extends CanvasWatchFaceService.Engine { Paint paint; @Override void onCreate(SurfaceHolder holder) { paint = new Paint(); paint.setTextSize(80_DIP); paint.setStyle(Style.FILL); paint.setColor(Color.MAGENTA); paint.setTextAlign(Align.CENTER); paint.setAntiAlias(true); } @Override void onDraw(Canvas canvas, Rect bounds) { canvas.drawText("18:42", bounds.centerX(), bounds.centerY(), paint); } }
  21. 21. Style.STROKE if (inAmbientMode) { paint.setColor(WHITE); paint.setStyle(STROKE); paint.setStrokeWidth(1_DIP); if (lowBitEnabled) { paint.setAntiAlias(false); } }
  22. 22. Lifecycle
  23. 23. Enough theory
  24. 24. 10mn to create a watch face
  25. 25. #1: Draw the background
  26. 26. Paint backgroundPaint = new Paint(); @Override void onCreate(SurfaceHolder holder) { super.onCreate(holder); backgroundPaint.setStyle(FILL); backgroundPaint.setColor(DKGRAY); backgroundPaint.setAntiAlias(true); } @Override void onDraw(Canvas canvas, Rect bounds) { canvas.drawRect(0, 0, bounds.width(), bounds.height(), backgroundPaint); }
  27. 27. Background
  28. 28. Gradient: Linear / Sweep / Radial LinearGradient(0, centerY, width, centerY, WHITE, BLACK, CLAMP)); SweepGradient(radius, radius, WHITE, BLACK)); RadialGradient(centerX, centerY, radius, WHITE, BLACK, CLAMP)); paint.setShader(shader);
  29. 29. Tile mode: Clamp / Mirror / Repeat tileMode = Shader.TileMode.CLAMP; tileMode = Shader.TileMode.MIRROR; tileMode = Shader.TileMode.REPEAT; new RadialGradient(radius, radius, radius / 10, WHITE, BLACK, tileMode));
  30. 30. Gradient posi-ons int[] colors = new int[] { RED, GREEN, BLUE, MAGENTA, CYAN}; float[] positions = new float[] { 0f, 0.1f, 0.4f, 0.8f, 1.0f}; gradient = new LinearGradient(0, radius, width, radius, colors, positions, CLAMP);
  31. 31. Gradient posi-ons Shader shader = new LinearGradient( 0, centerY, width, centerY, new int[] {blue, blue, white, white, red, red}, new float[] {0f, 0.33f, 0.33f, 0.66f, 0.66f, 1f}, CLAMP); bgPaint.setShader(shader);
  32. 32. Back to our background int[] colors = new int[] {DKGRAY, DKGRAY, BLACK, BLACK}; float[] positions = new float[] {0, 0.25f, 0.25f, 1f}; bgPaint.setShader(new RadialGradient(centerX, centerY, 6_DIP, colors, positions, REPEAT));
  33. 33. #2 Place the minutes indicators
  34. 34. Path path; @Override void onApplyWindowInsets(WindowInsets insets) { [...] path = createMinutesIndicators(centerX, centerY, radius - 10_DP); } @Override void onDraw(Canvas canvas, Rect bounds) { [...] canvas.drawPath(path, paint); }
  35. 35. Minutes indicators
  36. 36. Add shadow mnPaint.setShadowLayer(4f, 2f, 2f, Color.GRAY);
  37. 37. #3 Create the watch hands
  38. 38. Paint handHourPaint; Path handHourPath; @Override void onCreate(SurfaceHolder holder) { [...] handHourPaint = new Paint(); handHourPaint.setStyle(Paint.Style.FILL); handHourPaint.setColor(Color.WHITE); handHourPaint.setAntiAlias(true); handHourPaint.setPathEffect(new CornerPathEffect(2_DP)); } @Override void onApplyWindowInsets(WindowInsets insets) { [...] handHourPath = createHandHour(centerX, centerY, radius - 20_DP); } @Override void onDraw(Canvas canvas, Rect bounds) { [...] canvas.drawPath(handHourPath, handHourPaint); }
  39. 39. Hour hand path.moveTo( centerX - 16_DIP, centerY ); path.lineTo( centerX - 10_DIP, centerY );
  40. 40. path.arcTo( new RectF( centerX - 10_DIP, centerY - 10_DIP, centerX + 10_DIP, centerY + 10_DIP ), 180f, -180f );
  41. 41. path.lineTo( centerX + 16_DIP, centerY );
  42. 42. path.quadTo( centerX, centerY - 20_DIP, centerX + 1_DIP, centerY - needleHeight );
  43. 43. path.quadTo( centerX, centerY - 20_DIP, centerX - 16_DIP, centerY );
  44. 44. int[] colors = new int[] { 0xff878191, 0xffaba6b3, 0xffb9b1c5, 0xffa9a2b3 }; float[] positions = new float[] { 0, 0.49f, 0.51f, 1f }, Shader gradient = new LinearGradient( radius - 10_DIP, 0, radius + 10_DIP, 0, colors, positions, Shader.TileMode.CLAMP ); handHourPaint.setShader(gradient);
  45. 45. canvas.save(); canvas.rotate( 10 * 360 / 12, centerX, centerY ); canvas.drawPath(path, paint); canvas.restore();
  46. 46. Paint shadowPaint = new Paint(); shadowPaint.setAntiAlias(true); shadowPaint.setColor(GRAY); shadowPaint.setShadowLayer(4f, 4f, 2f, GRAY); [...] canvas.drawPath(handHourPath, shadowPaint); canvas.drawPath(handHourPath, watchHandPaint);
  47. 47. Add the minute hand
  48. 48. Add the second hand
  49. 49. #4 Create the ambient mode
  50. 50. #5 No step 5
  51. 51. Source code github.com/Nilhcem/the-10mn-watchface
  52. 52. Tips & Tricks
  53. 53. #1: Official documenta2on is ! developer.android.com/wear/index.html
  54. 54. #2: Share code with a common module
  55. 55. #3: Custom WatchFrame Layout h"ps://github.com/Nilhcem/hexawatch/ blob/master/companion/src/main/java/ com/nilhcem/hexawatch/ui/widget/ WearFrameLayout.java <com.nilhcem.hexawatch.ui.widget.WearFrameLayout android:layout_width="wrap_content" android:layout_height="match_parent"> <com.nilhcem.hexawatch.ui.widget.HexawatchView android:layout_width="match_parent" android:layout_height="match_parent"/> </com.nilhcem.hexawatch.ui.widget.WearFrameLayout>
  56. 56. #4: Consider using Protobuf over Json
  57. 57. #5: Want to cut bitmaps? Use Xfermode
  58. 58. #6: Check out ustwo clockwise SDK h"ps://github.com/ustwo/clockwise
  59. 59. #7: Stripes shader aka "A burn-in friendly way to fill a large surface" paint.setStyle(Paint.Style.FILL); paint.setShader(new LinearGradient( 0f, 0f, TWO_DIP, TWO_DIP, new int[] { WHITE, WHITE, TRANSPARENT, TRANSPARENT }, new float[] { 0, 0.25f, 0.25f, 1f }, Shader.TileMode.REPEAT ) );
  60. 60. #8: Bitmap shader github.com/Nilhcem/shammane- androidwear Bitmap dotPattern = BitmapFactory.decodeResource( context.getResources(), R.drawable.dot_pattern ); paint.setShader( new BitmapShader( dotPattern, TileMode.REPEAT, TileMode.REPEAT ) );
  61. 61. #9: Experiment in an Android (not wear) custom View <com.nilhcem.experiments.ui.widget.WearFrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.nilhcem.experiments.ui.ExperimentalView android:layout_width="match_parent" android:layout_height="match_parent"/> </com.nilhcem.experiments.ui.widget.WearFrameLayout>
  62. 62. #10: Use ValueAnimator for onDraw anima7ons private ValueAnimator animator; private final Handler handler = new Handler(); public void onTapCommand(int tapType, int x, int y, long e) { if (tapType == TAP_TYPE_TAP) { animator = ValueAnimator.ofInt(0, Math.round(MAX_RADIUS)); animator.setDuration(600L); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.start(); invalidate(); } } public void onDraw(Canvas canvas, Rect bounds) { if (animator != null && animator.isRunning()) { int value = (Integer) animator.getAnimatedValue(); canvas.drawCircle(centerX, centerY, value, paint); // Invalidate at a 30fps ratio handler.postDelayed(() -> invalidate()), 1000L / 30); } }
  63. 63. #11: Path Interpolator Anima3on Path path = new Path(); path.moveTo(0, 0); path.lineTo(0.250f, 0.250f); path.lineTo(0.500f, -0.500f); path.lineTo(0.750f, 0.625f); path.lineTo(0.875f, 0.500f); path.lineTo(1f, 1f); ObjectAnimator animator = ObjectAnimator.ofFloat(bugdroid, View.TRANSLATION_X, 0, 100); animator.setRepeatCount(ObjectAnimator.INFINITE); animator.setRepeatMode(ObjectAnimator.REVERSE); animator.setInterpolator(PathInterpolatorCompat.create(path)); animator.setDuration(2000); animator.start();
  64. 64. #11: Path Interpolator Anima3on
  65. 65. #12: Move a view along a Path h"p://stackoverflow.com/ques5ons/6154370/ android-move-object-along-a-path Path path = new Path(); path.arcTo(new RectF(0, 0, 300, 300), 0, 180); // 1 -> 2 path.quadTo(200, 80, 400, 400); // 2 -> 3 path.lineTo(500f, 300f); // 3 -> 4 path.close(); // 4 -> 1 ObjectAnimator animator = ObjectAnimator.ofFloat(bugdroid, "x", "y", path); animator.setRepeatCount(ObjectAnimator.INFINITE); animator.setInterpolator(new DecelerateInterpolator()); animator.setDuration(7000); animator.start();
  66. 66. #13: Vector Drawables • M (x,y): Absolute move to (x,y) • m (x,y): Rela5ve move to (x,y) • L or l (x,y): Line to (x,y) • C or c (x1,y1,x2,y2): Curve from (x1,y1) to (x2,y2) • Q or q (x1,y1,x,y): Quadra5c curve to (x,y) using (x1,y1) as the control point • Z or z: Close path h#ps://www.w3.org/TR/SVG/paths.html
  67. 67. #13: Vector Drawables
  68. 68. #14: Port your app to Tizen • HTML5 Canvas api • Low-Bit Ambient mode • Burn-in support • onTimeTick() becomes window.addEventListener("time tick", drawWatchContent);
  69. 69. Hexawatch github.com/Nilhcem/hexawatch • Square / Circular shapes • Se1ngs app • Protobuf • Gear s2 port • Custom views • Custom wear frame layout • Common module
  70. 70. From a hoodie to a watch face nilhcem.com/android-wear/watchfaces- design • Android Wear 1 + 2 support • Square / Circular shapes • Chin support • Textures + Xfer modes
  71. 71. Conclusion • Good to be curious • Improve your skills • Fun • Rewarding
  72. 72. Android Wear Essen-als • Twi%er: @Nilhcem • Slides: slideshare.net/Nilhcem/android-wear-essen:als • 10mn-watchface: github.com/Nilhcem/the-10mn-watchface • Hexawatch: github.com/Nilhcem/hexawatch • Hoodie watch face making-of: nilhcem.com/android-wear/ watchfaces-design

×