Pular para o conteúdo
CrossFly é 100% open sourceSiga no GitHub
CrossFlyCrossFly
Começar
Todos os exemplos
ReceitasDesenho119 linhas · 1 arquivos

Painting your own

A component is a rectangle that can draw. width/height are yours, getAutoHeight says how much room you want, and onPaint puts something in it.

O que isto mostra

Painting something the framework does not have — and when not to.

Reach for onPaint when the thing you need is a drawing: a gauge, a sparkline, a seat map, a signature. Do not reach for it to make a card look different — that is a ComponentStyle, and a card painted by hand loses the skin, the theme, the press state and the shadow, all of which you then rebuild worse.

Three rules hold for everything drawn here. Sizes come from Styles.fontHeight, so the drawing scales with density instead of shrinking on a good screen. Colours come from tokens, so it follows the theme. And onPaint runs every frame — it may compute, but it must not allocate, which is why the shapes below are fields and not locals.

Rodar na sua máquina

Todo exemplo é uma Window comum, então o launcher só precisa do nome dela.

gradlew lwjgl3:run --args="classname dev.cf.recipe.R23Painting"
Editar o exemplo

Java é compilado antes de rodar, então o navegador não consegue reconstruir uma edição sozinho — o código que você vê aqui virou JavaScript pelo TeaVM antes de ser publicado. Copie o arquivo para um projeto e ele roda sem mudanças nas quatro plataformas; o comando acima é toda a preparação.

Rodando aquiCrossFly 0.1.32
Iniciando o aplicativo…

Compilado para JavaScript pelo TeaVM — é o mesmo código, não um vídeo dele. · Todos os exemplos

O que ele usa

Tipos do framework que este exemplo toca. Os que têm link têm página de referência.

CardColorUtilsComponentGRectPaintStylesWidgets

O código

Completo, sem cortes. É o arquivo, não um trecho dele.

Bruto
dev/cf/recipe/R23Painting.java
package dev.cf.recipe;

import dev.cf.Tones;

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

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

/**
 * Painting something the framework does not have — and when not to.
 *
 * <p>Reach for {@code onPaint} when the thing you need is <b>a drawing</b>: a gauge, a sparkline, a
 * seat map, a signature. Do not reach for it to make a card look different — that is a
 * {@code ComponentStyle}, and a card painted by hand loses the skin, the theme, the press state and
 * the shadow, all of which you then rebuild worse.</p>
 *
 * <p>Three rules hold for everything drawn here. Sizes come from {@code Styles.fontHeight}, so the
 * drawing scales with density instead of shrinking on a good screen. Colours come from tokens, so
 * it follows the theme. And {@code onPaint} runs every frame — it may compute, but it must not
 * allocate, which is why the shapes below are fields and not locals.</p>
 */
public class R23Painting extends Recipe
{
   public String title()
   {
      return "Painting your own";
   }

   public String note()
   {
      return "A component is a rectangle that can draw. width/height are yours, getAutoHeight says"
         + " how much room you want, and onPaint puts something in it.";
   }

   public void build(Component body) throws Exception
   {
      body.add(LAY_AUTO, outgap(label("A bar with a target line — the kind of thing no widget set"
         + " has, because the target is your idea.", 1f, false, Styles.captionColor,
         Position.START), 0, 0, 0, .8f));

      for (int i = 0; i < 4; i++)
      {
         Card c = new Card();
         // The bar is NOT toned: the class above reasons that over-target must read as more of the
         // same hue, and four hues would take that away. The name carries the colour instead.
         c.add(LAY_AUTO, label(new String[] { "North", "South", "East", "West" }[i], 1.2f, true,
            Tones.accent(), Position.START));
         Meter m = new Meter(new float[] { .82f, .41f, 1.12f, .67f }[i]);
         c.add(LAY_AUTO, outgap(m, 0, .3f, 0, 0));
         body.add(LAY_AUTO, outgap(c, 0, 0, 0, .5f));
      }
   }

   /**
    * A bar against a target: filled to {@code value}, with a line at 100% and the excess in a
    * different tone.
    */
   private static class Meter extends Component
   {
      /** Shapes as FIELDS: onPaint runs every frame, and allocating there is how you make garbage. */
      private final GRect track = new GRect(), fill = new GRect(), mark = new GRect();

      private final float value;

      Meter(float value)
      {
         this.value = value;
      }

      /**
       * How tall this wants to be. Expressed in font heights, so it grows with the type rather than
       * staying a fixed number of pixels that is right on exactly one screen.
       */
      @Override
      public int getAutoHeight()
      {
         return Math.round(Styles.fontHeight * 1.1f);
      }

      @Override
      public void onPaint(Paint paint) throws Exception
      {
         int r = height / 2;
         int reached = Math.round(Math.min(value, 1f) * width);

         track.clear()
            .bounds(0, 0, width, height)
            .radius(r)
            .fill(ColorUtils.lerpColor(Styles.backColor.color(), Styles.textColor.color(), .1f));
         paint.rect(track);

         // Over target gets its own tone. A bar that just stops at 100% hides the best news on the
         // screen, and one that keeps growing past the edge hides the worst.
         fill.clear()
            .bounds(0, 0, reached, height)
            .radius(r)
            // Over target gets a DEEPER shade of the same hue, not a different colour: the reader
            // should see "more of the same thing", and tertiary() on a blue seed resolves to a
            // muted tone that reads as less rather than more.
            .fill(value > 1f
               ? ColorUtils.lerpColor(Styles.primaryColor.color(), Styles.textColor.color(), .4f)
               : Styles.primaryColor.color());
         paint.rect(fill);

         // The target line. One pixel is invisible on a dense screen — measure it in font units.
         int t = Math.max(2, Math.round(Styles.fontHeight * .08f));
         mark.clear()
            .bounds(width - t, -t, t, height + t * 2)
            .fill(Styles.textColor.color());
         paint.rect(mark);
      }
   }
}
Para uma IA

Versões desta página legíveis por máquina, para um modelo ler a prateleira em vez de adivinhar a API.

Este exemplo, em JSONEste exemplo, em Markdown
AnteriorAnimationPróximoFloating action button