diff --git a/docs/developer-guide/graphics.asciidoc b/docs/developer-guide/graphics.asciidoc index 6bcd86e4da..de91696b84 100644 --- a/docs/developer-guide/graphics.asciidoc +++ b/docs/developer-guide/graphics.asciidoc @@ -60,6 +60,37 @@ hi.show(); .Hi world demo code, notice that the blue bar on top is the iOS7+ status bar image::img/developer-guide/graphics-hiworld.png[Hi world demo code, notice that the blue bar on top is the iOS7+ status bar,scaledwidth=20%] +==== Painting with Gradients + +Solid colors are only the starting point. `Graphics` also understands "paint" objects +that describe gradients and other patterns. You can pass an instance of +https://www.codenameone.com/javadoc/com/codename1/ui/Paint.html[`Paint`] +to `setColor(Paint)` in place of an integer color value to activate a gradient for +subsequent fill and draw operations. The +https://www.codenameone.com/javadoc/com/codename1/ui/LinearGradientPaint.html[`LinearGradientPaint`] +class is the most common option and accepts a list of color stops along a line: + +[source,java] +---- +LinearGradientPaint gradient = new LinearGradientPaint( + 0, 0, getWidth(), 0, // horizontal gradient + new int[] {0xff4285f4, 0xff34a853, 0xfffbbc05}, + new float[] {0f, 0.5f, 1f} +); +g.setColor(gradient); +g.fillRect(getX(), getY(), getWidth(), getHeight()); +---- + +The `fillLinearGradient()` convenience methods (with optional `repeat` flag) +provide a shorthand when you just need a two-color gradient without constructing +your own `Paint` object. + +Radial gradients are equally straightforward using +`fillRadialGradient()` or `fillRectRadialGradient()`, which can render circular +and rectangular radial transitions respectively. These APIs accept the +inner/outer colors, focal point, and spread so you can combine them with linear +gradients to build sophisticated backgrounds and lighting effects. + === Glass Pane The `GlassPane `in Codename One is inspired by the Swing `GlassPane` & `LayeredPane` with quite a few twists. @@ -139,9 +170,13 @@ paths and curves and caching the shape drawn in the GPU. === Device Support -Shapes and transforms are available on most smartphone platforms with some caveats for the current Windows Phone port. +Shapes and transforms ship with all of Codename One's actively maintained ports +(Android, iOS, JavaScript, desktop/Simulator, and UWP). Older platforms that +have reached end of life may lack these APIs, so keep the guard code shown below +if you still target them with legacy builds. -Notice that perspective transform is missing from the desktop/simulator port. Unfortunately there is no real equivalent to perspective transform in JavaSE that we could use. +Notice that perspective transform is missing from the desktop/simulator port. +Unfortunately there is no real equivalent to perspective transform in JavaSE that we could use. === A 2D Drawing App @@ -316,8 +351,9 @@ NOTE: `scale()` and `rotate()` methods are only available on platforms that supp ==== Device Support -As of this writing, not all devices support transforms (i.e. `scale()` and `rotate()`). The following is a list of platforms -and their respective levels of support. +All current Codename One ports expose affine transforms (i.e. `scale()` and +`rotate()`). Use the following table as a quick reference when deciding whether +you need a fallback path. .Transforms Device Support [cols="2*"] @@ -325,7 +361,7 @@ and their respective levels of support. |Platform |Affine Supported -| Simulator +| Simulator/Desktop | Yes | iOS @@ -337,14 +373,8 @@ and their respective levels of support. | JavaScript | Yes -| J2ME -| No - -| BlackBerry (4.2 & 5) -| No - -| Windows Phone -| No (pending) +| UWP +| Yes |=== @@ -1002,6 +1032,24 @@ You could also use the `Graphics.setTransform()` class to apply rotations and ot (including 3D perspective transforms), but I'll leave that for its own topic as it is a little bit more complex. +==== Global Alpha & Anti-Aliasing + +So far we have relied on the per-pixel alpha stored in images and gradients. `Graphics` +also lets you apply a global alpha multiplier to every draw call by using +`setAlpha(int)` or `concatenateAlpha(int)` after checking `isAlphaSupported()`. +Both methods accept values from `0` (fully transparent) to `255` (fully opaque) +and remain active until you change them again. `concatenateAlpha()` is +especially handy when you need to temporarily fade a component because it +returns the previous alpha so you can restore it later. + +Anti-aliasing can likewise be toggled at runtime. Call `isAntiAliasingSupported()` +and `isAntiAliasedTextSupported()` to discover which hints the current port +exposes, then use `setAntiAliased(boolean)` and `setAntiAliasedText(boolean)` to +opt into smoother edges for shapes and glyphs respectively. These switches make +it easy to balance rendering quality versus speed depending on the type of +content you draw. + + ==== Event Coordinates The coordinate system and event handling are closely tied. You can listen for touch events on a component by diff --git a/docs/developer-guide/io.asciidoc b/docs/developer-guide/io.asciidoc index 8a51bf8676..5743515913 100644 --- a/docs/developer-guide/io.asciidoc +++ b/docs/developer-guide/io.asciidoc @@ -127,6 +127,19 @@ You can then read the token like this: String token = Preferences.get("token", null); ---- +The backing store filename defaults to `"codenameone.properties"`, but you can +override it by calling `Preferences.setPreferencesLocation(String)` before your +app touches the API. This is useful when you need to segregate preference +namespaces per user or plug in an encrypted storage layer. + +When you have a batch of updates, prefer the `Preferences.set(Map)` +overload so that all values are persisted with a single disk write. + +Preferences can also notify interested parties when a key changes. Register a +`PreferenceListener` via `addPreferenceListener()` to react to updates made in +other parts of your code (or even triggered remotely via `Codename One Push`). +Remember to remove listeners you no longer need to avoid leaks. + WARNING: This gets somewhat confusing with primitive numbers e.g. if you use `Preferences.set("primitiveLongValue", myLongNumber)` then invoke `Preferences.get("primitiveLongValue", 0)` you might get an exception! + This would happen because the value is physically a `Long` object but you are trying to get an `Integer`. The workaround is to remain consistent and use code like this `Preferences.get("primitiveLongValue", (long)0)`. @@ -250,9 +263,9 @@ In general SQL seems overly complex for most embedded device programming tasks. .Portability Of SQLite **** -SQLite is supported on iOS, Android, RIM, Desktop & JavaScript builds. However, the JavaScript version of SQL has been deprecated and isn't supported on all platforms. - -You will notice that at this time support is still missing from the Windows builds. +SQLite is supported on iOS, Android, JavaScript, Desktop/Simulator, and UWP +builds. The JavaScript port relies on the browser's WebSQL implementation, so +it may be unavailable in environments that have already removed that feature. The biggest issue with SQLite portability is in iOS. The SQLite version for most platforms is threadsafe and as a result very stable. However, the iOS version is not! @@ -416,6 +429,17 @@ byte[] resultOfRequest = request.getData(); Notice that in this case the `addToQueueAndWait` method returned after the connection completed. Also notice that this was totally legal to do on the EDT! +==== Timeouts & Retries + +Each request can override the global timeout using `setTimeout(int)`, which +expects a duration in milliseconds. This is especially useful when you are +talking to endpoints that occasionally need a longer window than the default +NetworkManager setting. You can also configure how many times Codename One +should retry a failing call silently by using `setSilentRetryCount(int)`. When +the silent retry limit is reached the standard error handling kicks in (e.g. +listeners fire, fail dialogs show, etc.). + + ==== Threading By default the `NetworkManager` launches with a single network thread. This is sufficient for very simple applications that don't do too much networking but if you need to fetch many images concurrently and perform web services in parallel this might be an issue. @@ -1296,6 +1320,18 @@ Map jsonData = Rest.post(myUrl).body(bodyValueAsString).getAsJso Notice the usage of post and the body builder method. There are MANY methods in the builder class that cover pretty much everything you would expect and then some when it comes to the needs of rest services. +Some highlights that are easy to miss: + +* `.priority(byte)` lets you change the underlying `ConnectionRequest` priority + when you need certain calls to jump the queue. +* `.cookiesEnabled(boolean)` controls whether cookies are persisted for the + request when you need stateless behavior. +* `.useBoolean(boolean)` and `.useLongs(boolean)` toggle how the JSON parser + materializes number and boolean types inside the resulting `Map`, which is + handy when your backend is strict about data types. +* `.cacheMode(...)` and `.postParameters(...)` expose the same knobs as + `ConnectionRequest`, keeping you in the fluent API even for advanced tweaks. + I changed the code in the kitchen sink webservice sample to use this API. I was able to make it shorter and more readable without sacrificing anything. ==== Rest in Practice - Twilio diff --git a/docs/developer-guide/performance.asciidoc b/docs/developer-guide/performance.asciidoc index 7d11b46793..ce2322d81e 100644 --- a/docs/developer-guide/performance.asciidoc +++ b/docs/developer-guide/performance.asciidoc @@ -52,11 +52,17 @@ The Performance Monitor tool can be accessible via the #Simulator# -> #Performan .Main tab of the performance monitor: Logs and timings image::img/developer-guide/performance-monitor-tab-1.png[Main tab of the performance monitor: Logs and timings] -The first tab of the performance monitor includes a table of the drawn components. Each entry includes the number of times it was drawn and the slowest/fastest and average drawing time. +The first tab of the performance monitor includes a table of the drawn components. Each entry includes the number of times it was drawn and the slowest/fastest and average drawing time. The toolbar across the top +includes Pause/Continue buttons so you can freeze the counters while you inspect +the current snapshot, a "Clear Data" action to reset the tables, and a "GC" +button that invokes the simulator's garbage collector so you can see how memory +usage changes. This is useful if a `Form` is slow. You might be able to pinpoint it to a specific component using this tool. -The Log on the bottom includes debug related information. E.g. it warns about the usage of mutable images which might be slow on some platforms. This also displays warnings when an unlocked image is drawn etc. +The Log on the bottom includes debug related information. E.g. it warns about the usage of mutable images which might be slow on some platforms. This also displays warnings when an unlocked image is drawn etc. A live +"Image Memory Overhead" meter summarizes how much native image memory the +current form consumes so you can correlate spikes with your drawing code. .Rendering tree image::img/developer-guide/performance-monitor-tab-2.png[Rendering tree] @@ -138,6 +144,17 @@ Log.bindCrashProtection(true); We normally place this in the `init(Object)` method so all future on-device errors are emailed to you. Internally this method uses the `Display.getInstance().addEdtErrorHandler()` API to bind error listeners to the EDT. When an exception is thrown there it is swallowed (using `ActionEvent.consume()`). The `Log` data is then sent using `Log.sendLog()`. +If your crash handler runs while networking is unavailable or you want to avoid +blocking the EDT, use `Log.sendLogAsync()` instead. It performs the upload in a +background thread and is what Codename One's lifecycle helper falls back to when +regular error reporting fails. + +You can also plug in your own crash reporting pipeline by calling +`Display.getInstance().setCrashReporter(CrashReport)`. The +https://www.codenameone.com/javadoc/com/codename1/system/CrashReport.html[`CrashReport`] +callback will receive the exception, device information, and log payload so you +can forward it to services like Firebase Crashlytics or your in-house tools. + To truly benefit from this feature we need to use the `Log` class for all logging and exception handling instead of API's such as `System.out`. To log standard printouts you can use the `Log.p(String)` method and to log exceptions with their stack trace you can use `Log.e(Throwable)`.