Successfully reported this slideshow.
Android Wear Essen-als
Improve your Android skills, building watch faces
What you should know about
Android Wear
Android Wear
• Android SDK
• Specific wear APIs in external libraries (support:wearable,
play-services-wearable)
• All devi...
Crea%ng a wear app
h"ps://developer.android.com/training/building-wearables.html
Crea%ng a wear app
• Ac$vity
• AndroidManifest.xml
• LayoutInflater
• You know the stuff...
Crea%ng a watch face
h"ps://developer.android.com/training/wearables/watch-faces/index.html
Crea%ng a watch face
1. Create a class that extends
CanvasWatchFaceService.Engine
2. Create a Service that extends CanvasW...
Create a watch face project
Ba#ery usage
AMOLED
Interac(ve mode (default)
Ambient mode / Low-bit Ambient
Low-bit Ambient / Ambient
Burn-in effect
Burn-in effect
h"ps://en.wikipedia.org/wiki/Screen_burn-in
Burn-in effect
Burn-in effect
Avoid burn-in
• Do not draw large areas of pixels in ambient mode
• Do not place content within 10 pixels of the edge of t...
I'm bored, can I see some code?
class Engine extends CanvasWatchFaceService.Engine {
Paint paint;
@Override void onCreate(SurfaceHolder holder) {
paint = ...
Style.STROKE
if (inAmbientMode) {
paint.setColor(WHITE);
paint.setStyle(STROKE);
paint.setStrokeWidth(1_DIP);
if (lowBitEn...
Lifecycle
Enough theory
10mn to create a watch face
#1: Draw the background
Paint backgroundPaint = new Paint();
@Override void onCreate(SurfaceHolder holder) {
super.onCreate(holder);
backgroundPai...
Background
Gradient: Linear / Sweep / Radial
LinearGradient(0, centerY, width, centerY, WHITE, BLACK, CLAMP));
SweepGradient(radius, ...
Tile mode: Clamp / Mirror / Repeat
tileMode = Shader.TileMode.CLAMP;
tileMode = Shader.TileMode.MIRROR;
tileMode = Shader....
Gradient posi-ons
int[] colors = new int[] { RED, GREEN, BLUE, MAGENTA, CYAN};
float[] positions = new float[] { 0f, 0.1f,...
Gradient posi-ons
Shader shader = new LinearGradient(
0, centerY, width, centerY,
new int[] {blue, blue, white, white, red...
Back to our background
int[] colors = new int[] {DKGRAY, DKGRAY, BLACK, BLACK};
float[] positions = new float[] {0, 0.25f,...
#2 Place the minutes indicators
Path path;
@Override void onApplyWindowInsets(WindowInsets insets) {
[...]
path = createMinutesIndicators(centerX, centerY...
Minutes indicators
Add shadow
mnPaint.setShadowLayer(4f, 2f, 2f, Color.GRAY);
#3 Create the watch hands
Paint handHourPaint;
Path handHourPath;
@Override void onCreate(SurfaceHolder holder) { [...]
handHourPaint = new Paint();...
Hour hand
path.moveTo(
centerX - 16_DIP, centerY
);
path.lineTo(
centerX - 10_DIP, centerY
);
path.arcTo(
new RectF(
centerX - 10_DIP,
centerY - 10_DIP,
centerX + 10_DIP,
centerY + 10_DIP
),
180f, -180f
);
path.lineTo(
centerX + 16_DIP, centerY
);
path.quadTo(
centerX,
centerY - 20_DIP,
centerX + 1_DIP,
centerY - needleHeight
);
path.quadTo(
centerX,
centerY - 20_DIP,
centerX - 16_DIP,
centerY
);
int[] colors = new int[] {
0xff878191, 0xffaba6b3,
0xffb9b1c5, 0xffa9a2b3
};
float[] positions = new float[] {
0, 0.49f, 0...
canvas.save();
canvas.rotate(
10 * 360 / 12,
centerX, centerY
);
canvas.drawPath(path, paint);
canvas.restore();
Paint shadowPaint = new Paint();
shadowPaint.setAntiAlias(true);
shadowPaint.setColor(GRAY);
shadowPaint.setShadowLayer(4f...
Add the minute
hand
Add the second
hand
#4 Create the ambient mode
#5 No step 5
Source code
github.com/Nilhcem/the-10mn-watchface
Tips & Tricks
#1: Official documenta2on is !
developer.android.com/wear/index.html
#2: Share code with a common module
#3: Custom WatchFrame
Layout
h"ps://github.com/Nilhcem/hexawatch/
blob/master/companion/src/main/java/
com/nilhcem/hexawat...
#4: Consider using Protobuf over
Json
#5: Want to cut bitmaps? Use Xfermode
#6: Check out ustwo clockwise SDK
h"ps://github.com/ustwo/clockwise
#7: Stripes shader
aka "A burn-in friendly way to fill a large surface"
paint.setStyle(Paint.Style.FILL);
paint.setShader(n...
#8: Bitmap shader
github.com/Nilhcem/shammane-
androidwear
Bitmap dotPattern = BitmapFactory.decodeResource(
context.getRe...
#9: Experiment in an
Android (not wear) custom
View
<com.nilhcem.experiments.ui.widget.WearFrameLayout
android:layout_widt...
#10: Use ValueAnimator
for onDraw anima7ons
private ValueAnimator animator;
private final Handler handler = new Handler();...
#11: Path Interpolator
Anima3on
Path path = new Path();
path.moveTo(0, 0);
path.lineTo(0.250f, 0.250f);
path.lineTo(0.500f...
#11: Path Interpolator
Anima3on
#12: Move a view along a
Path
h"p://stackoverflow.com/ques5ons/6154370/
android-move-object-along-a-path
Path path = new Pa...
#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...
#13: Vector Drawables
#14: Port your app to
Tizen
• HTML5 Canvas api
• Low-Bit Ambient mode
• Burn-in support
• onTimeTick() becomes
window.addE...
Hexawatch
github.com/Nilhcem/hexawatch
• Square / Circular shapes
• Se1ngs app
• Protobuf
• Gear s2 port
• Custom views
• ...
From a hoodie to a watch face
nilhcem.com/android-wear/watchfaces-
design
• Android Wear 1 + 2 support
• Square / Circular...
Conclusion
• Good to be curious
• Improve your skills
• Fun
• Rewarding
Android Wear Essen-als
• Twi%er: @Nilhcem
• Slides: slideshare.net/Nilhcem/android-wear-essen:als
• 10mn-watchface: github...
Android Wear Essentials
Android Wear Essentials
Android Wear Essentials
Android Wear Essentials
Android Wear Essentials
Android Wear Essentials
Prochain SlideShare
Chargement dans…5
×

Android Wear Essentials

502 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

×