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

A map with pins

OSM needs no API key, so a map works the moment you write it. Opens in a window of its own — a native view inside a scrolling page floats over everything around it.

Qué muestra esto

A real map with pins on it, and the two things that make a map different from every other widget.

It is a native view on top of the GL canvas, not a component inside it. It does not scroll with a parent, does not clip to one, and does not take part in a screen transition — a fade over a map shows a black rectangle for the whole animation. So a map gets a rectangle of its own and every control you want over it goes outside that rectangle, in ordinary components.

The engine is a constructor argument. Changing provider means building a new MapContainer — there is no setter, and there should not be one. OSM needs no key and is the right default for anything that is not shipping; GOOGLE needs MapContainer.googleMapsKey set before the container is built.

Opened on a button rather than built into this screen, on purpose: a native view inside a scrolling recipe list would float over the recipes above and below it.

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.R25Map"
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.

ButtonComponentDebugGapGridLayoutLatLonMapMapContainerMapPinMaterialIconStylesVerticalLayoutWidgetsWindow

El código

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

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

import dev.cf.Tones;

import dev.crossfly.map.*;
import dev.crossfly.ui.*;
import dev.crossfly.ui.color.*;
import dev.crossfly.ui.graphics.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;
import java.util.ArrayList;
import java.util.List;

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

/**
 * A real map with pins on it, and the two things that make a map different from every other widget.
 *
 * <p><b>It is a native view on top of the GL canvas</b>, not a component inside it. It does not
 * scroll with a parent, does not clip to one, and does not take part in a screen transition — a
 * fade over a map shows a black rectangle for the whole animation. So a map gets a rectangle of its
 * own and every control you want over it goes <i>outside</i> that rectangle, in ordinary
 * components.</p>
 *
 * <p><b>The engine is a constructor argument.</b> Changing provider means building a new
 * {@link MapContainer} — there is no setter, and there should not be one. {@code OSM} needs no key
 * and is the right default for anything that is not shipping; {@code GOOGLE} needs
 * {@code MapContainer.googleMapsKey} set before the container is built.</p>
 *
 * <p>Opened on a button rather than built into this screen, on purpose: a native view inside a
 * scrolling recipe list would float over the recipes above and below it.</p>
 */
public class R25Map extends Recipe
{
   /** A few coordinates to plot. Real ones come from your store — with the nulls filtered out. */
   private static final double[][] WHERE = { { 53.7996, -1.5491 }, { 51.2194, 4.4025 },
      { 41.1579, -8.6291 }, { 45.0703, 7.6869 }, { 55.6050, 13.0038 }, { 51.4545, -2.5879 } };
   private static final String[] WHO = { "Northgate", "Bellamy", "Kestrel", "Halden & Roe",
      "Ironvale", "Meridian" };

   public String title()
   {
      return "A map with pins";
   }

   public String note()
   {
      return "OSM needs no API key, so a map works the moment you write it. Opens in a window of"
         + " its own — a native view inside a scrolling page floats over everything around it.";
   }

   public void build(Component body) throws Exception
   {
      body.add(LAY_AUTO, outgap(label("Six suppliers, plotted from coordinates. The pins are data;"
         + " the map only draws them.", 1f, false, Styles.captionColor, Position.START), 0, 0, 0,
         1f));

      Button open = Tones.action("Open the map");
      open.testTag("map.open");
      open.onAction(c -> new MapScreen().show());
      body.add(LAY_AUTO, open);
   }

   /** The map, in a window of its own, with the app's chrome kept outside its rectangle. */
   private static class MapScreen extends Window
   {
      private MapContainer container;

      @Override
      public void onAdd(boolean firstTime) throws Exception
      {
         if (!firstTime)
            return;
         fullscreenColor = Styles.backColor.color();
         setLayout(new VerticalLayout(Position.START, Position.START));

         Component bar = new Component();
         bar.setLayout(new GridLayout(Position.START, Position.CENTER));
         bar.getStyle()
            .ingap(Gap.ofFont(.6f, .5f, .6f, .5f));
         Button back = new Button(MaterialIcon.of(0xE5C4, 1.4f));
         back.onAction(c -> hide());
         bar.add(0f, 1f, back, outgap(label("Suppliers", 1.5f, true, Styles.textColor,
            Position.START), .6f, 0, 0, 0));
         add(0f, bar);

         // OSM: no key, no account, works offline against its own tiles cache. The callback fires
         // when the native view is ready — plotting before that plots into nothing.
         container = new MapContainer(Map.MapEngine.OSM, 0, this::plot);
         add(LAY_FILL, container);
      }

      private void plot()
      {
         try
         {
            final List<MapPin> pins = new ArrayList<>();
            double lat = 0, lon = 0;
            for (int i = 0; i < WHERE.length; i++)
            {
               pins.add(new MapPin().position(WHERE[i][0], WHERE[i][1])
                  .title(WHO[i])
                  .snippet("Supplier"));
               lat += WHERE[i][0];
               lon += WHERE[i][1];
            }

            // batch(): every add is a call across to the native view, and six of them is six
            // round-trips and six redraws. Inside a batch it is one. At six pins nobody notices;
            // at six hundred it is the difference between a map and a freeze.
            container.map.batch(() -> container.map.add(pins.toArray(new MapPin[0])));

            // Open on the middle of what was plotted, not on a fixed point: a map that opens
            // somewhere the data is not makes the user pan before it tells them anything.
            container.map.centerOnLocation(new LatLon(lat / WHERE.length, lon / WHERE.length), 4);
         }
         catch (Exception e)
         {
            dev.crossfly.util.Debug.handle(e);
         }
      }
   }
}
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
AnteriorFloating action buttonSiguienteImages