Saltar al contenido
CrossFly es 100% open sourceSíguenos en GitHub
CrossFlyCrossFly
Empezar
Todos los ejemplos
RecetasImágenes97 líneas · 1 archivos

Images

Decode once, keep the Image, reuse it. Loading inside onPaint or inside a list's bind step is what makes scrolling stutter where the pictures are.

Qué muestra esto

Images, and the two questions that come up before the picture does.

How does it fill its box? ImageSize is the whole answer, and picking wrong is the most visible mistake in an app: a portrait photo stretched into a landscape tile is noticed by everybody and reported by nobody, because it looks like a bad photo rather than a bug. Cropping to fill is almost always right for a photo; fitting inside is almost always right for a logo.

Where does the image come from? An app's own assets, or bytes it was handed — a camera capture, a database blob, a download. Both end up as an Image, and neither belongs in onPaint: decoding is expensive, and doing it per frame is the classic reason a list stutters exactly where the pictures are.

Drawn here rather than loaded from a file, so the recipe carries no asset of its own — but a generated image and a decoded one behave identically from this point on.

Ejecutarlo en tu máquina

Cada ejemplo es una Window normal, así que el lanzador solo necesita su nombre.

gradlew lwjgl3:run --args="classname dev.cf.recipe.R26Images"
Editarlo

Java se compila antes de ejecutarse, así que el navegador no puede reconstruir una edición por su cuenta: el código que ves aquí se convirtió en JavaScript con TeaVM antes de publicarse. Copia el archivo a un proyecto y funciona sin cambios en las cuatro plataformas; el comando de arriba es toda la preparación.

Ejecutándose aquíCrossFly 0.1.32
Iniciando la aplicación…

Compilado a JavaScript por TeaVM: es el mismo código, no un vídeo de él. · Todos los ejemplos

Qué usa

Tipos del framework que toca este ejemplo. Los enlazados tienen página de referencia.

CardColorUtilsComponentGapImageImageViewRelativeStylesWidgets

El código

Completo, sin recortes. Es el archivo, no un fragmento.

Crudo
dev/cf/recipe/R26Images.java
package dev.cf.recipe;

import dev.crossfly.ui.*;
import dev.crossfly.ui.color.ColorUtils;
import dev.crossfly.ui.graphics.*;
import dev.crossfly.ui.image.Image;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;

import static dev.crossfly.ui.Widgets.*;

/**
 * Images, and the two questions that come up before the picture does.
 *
 * <p><b>How does it fill its box?</b> {@link ImageView.ImageSize} is the whole answer, and picking
 * wrong is the most visible mistake in an app: a portrait photo stretched into a landscape tile is
 * noticed by everybody and reported by nobody, because it looks like a bad photo rather than a bug.
 * Cropping to fill is almost always right for a photo; fitting inside is almost always right for a
 * logo.</p>
 *
 * <p><b>Where does the image come from?</b> An app's own assets, or bytes it was handed — a camera
 * capture, a database blob, a download. Both end up as an {@link Image}, and neither belongs in
 * {@code onPaint}: decoding is expensive, and doing it per frame is the classic reason a list
 * stutters exactly where the pictures are.</p>
 *
 * <p>Drawn here rather than loaded from a file, so the recipe carries no asset of its own — but a
 * generated image and a decoded one behave identically from this point on.</p>
 */
public class R26Images extends Recipe
{
   public String title()
   {
      return "Images";
   }

   public String note()
   {
      return "Decode once, keep the Image, reuse it. Loading inside onPaint or inside a list's bind"
         + " step is what makes scrolling stutter where the pictures are.";
   }

   public void build(Component body) throws Exception
   {
      // Made once, held, reused by all three views below — the point of the recipe.
      Image swatch = gradient(240, 160);

      // The names repay one careful reading, because two of them are the opposite of what they
      // sound like. RATIO fits the whole picture INSIDE the box; FIT scales until it COVERS the
      // box and crops what hangs over. Guessing between those two is how a portrait photo ends up
      // letterboxed in a tile that had room to crop it properly.
      show(body, "RATIO — the whole picture, inside the box", swatch, ImageSize.RATIO);
      show(body, "FIT — covers the box, crops the overflow", swatch, ImageSize.FIT);
      show(body, "STRETCH — fills by distorting. Almost never right", swatch, ImageSize.STRETCH);
   }

   private void show(Component body, String caption, Image img, ImageSize size)
      throws Exception
   {
      Card c = new Card();
      c.add(LAY_AUTO, label(caption, 1f, true, Styles.textColor, Position.START));
      ImageView v = new ImageView(img, size);
      v.getStyle()
         .autoHeight(Relative.ofFont(6f));
      c.add(LAY_AUTO, outgap(v, 0, .4f, 0, 0));
      c.getStyle()
         .outgap(Gap.ofFont(0, 0, 0, .6f));
      body.add(LAY_AUTO, c);
   }

   /**
    * Builds an image in memory from the theme's own colours.
    *
    * <p>An {@code Image} is pixels; where they came from stops mattering the moment you have one.
    * The same object could have arrived from {@code PhotoPicker}, from a SQLite blob or from a
    * download, and every line after this one would be unchanged.</p>
    */
   private static Image gradient(int w, int h) throws Exception
   {
      Image img = Image.createSurface(w, h);
      int[] px = new int[w * h];
      int from = Styles.primaryColor.color(), to = tertiary().color();
      for (int y = 0; y < h; y++)
      {
         int rowColor = ColorUtils.lerpColor(from, to, y / (float) h);
         for (int x = 0; x < w; x++)
            // Diagonal bands, so cropping and stretching are both obvious at a glance.
            px[y * w + x] = ((x + y) / 18) % 2 == 0 ? rowColor
               : ColorUtils.lerpColor(rowColor, Styles.backColor.color(), .35f);
      }
      img.setPixels(px);
      // Pixels written, texture stale: applyChanges is what pushes them to the GPU. Forgetting it
      // gives you an image that is correct in memory and blank on screen.
      img.applyChanges();
      return img;
   }
}
Para una IA

Versiones de esta página legibles por máquina, para que un modelo lea el estante en vez de adivinar la API.

Este ejemplo, en JSONEste ejemplo, en Markdown
AnteriorA map with pinsSiguienteKeyboard and focus