Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

Android Wear Essentials

754 Aufrufe

Veröffentlicht am

Improve your Android skills, building stunning watch faces

Veröffentlicht in: Technologie
  • Als Erste(r) kommentieren

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

×