मुख्य सामग्री पर जाएँ
CrossFly 100% ओपन सोर्सGitHub पर फ़ॉलो करें
CrossFlyCrossFly
शुरू करें
सभी उदाहरण
रेसिपीडेटा154 पंक्तियाँ · 1 फ़ाइलें

Storing something

Add a few notes and reopen the recipe — they are still there. The same code runs on desktop, Android, iOS and the web.

यह क्या दिखाता है

Storing something, on every platform, with no ORM and no generated code.

SqliteDb is the whole data layer — native on desktop, Android and iOS, and WASM on the web, so the same four calls work everywhere. exec for DDL, execute for a parameterised write, query for a cursor, and transaction to make several writes one thing.

Two habits worth copying from the first line you write. Bind parameters, never concatenate SQL — the ? form is not only safe, it lets SQLite reuse the prepared statement. And name your columns in the SELECT: the cursor reads by position, so a SELECT * silently shifts every getter the day somebody adds a column.

अपनी मशीन पर चलाएँ

हर उदाहरण एक साधारण Window है, इसलिए लॉन्चर को सिर्फ़ उसका नाम चाहिए।

gradlew lwjgl3:run --args="classname dev.cf.recipe.R18Storage"
इसे बदलना

Java पहले से संकलित होता है, इसलिए ब्राउज़र आपके बदलाव को खुद दोबारा नहीं बना सकता — यहाँ दिखा कोड प्रकाशित होने से पहले TeaVM द्वारा JavaScript में बदला गया था। फ़ाइल को किसी प्रोजेक्ट में कॉपी कीजिए और वह चारों प्लेटफ़ॉर्म पर बिना बदलाव चलती है; ऊपर दी गई कमांड ही पूरी तैयारी है।

यहीं चल रहा हैCrossFly 0.1.32
ऐप शुरू हो रहा है…

TeaVM ने इसे JavaScript में संकलित किया — यह वही कोड है, उसका वीडियो नहीं। · सभी उदाहरण

यह क्या इस्तेमाल करता है

इस उदाहरण में छुए गए फ़्रेमवर्क टाइप। जिनमें लिंक है उनका संदर्भ पृष्ठ मौजूद है।

ButtonCardComponentDebugDeviceGapGridLayoutMaterialIconScreenSqliteCursorSqliteDbStylesTextFieldVerticalLayoutWidgets

सोर्स कोड

पूरा, बिना कटौती के। यह वही फ़ाइल है, उसका अंश नहीं।

कच्चा
dev/cf/recipe/R18Storage.java
package dev.cf.recipe;

import dev.cf.Tones;

import dev.crossfly.device.Device;
import dev.crossfly.sqlite.SqliteCursor;
import dev.crossfly.sqlite.SqliteDb;
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 static dev.crossfly.ui.Widgets.*;

/**
 * Storing something, on every platform, with no ORM and no generated code.
 *
 * <p>{@link SqliteDb} is the whole data layer — native on desktop, Android and iOS, and WASM on the
 * web, so the same four calls work everywhere. {@code exec} for DDL, {@code execute} for a
 * parameterised write, {@code query} for a cursor, and {@code transaction} to make several writes
 * one thing.</p>
 *
 * <p>Two habits worth copying from the first line you write. <b>Bind parameters, never concatenate
 * SQL</b> — the {@code ?} form is not only safe, it lets SQLite reuse the prepared statement.
 * And <b>name your columns in the SELECT</b>: the cursor reads by position, so a {@code SELECT *}
 * silently shifts every getter the day somebody adds a column.</p>
 */
public class R18Storage extends Recipe
{
   private SqliteDb db;
   private TextField input;
   private Component list;

   public String title()
   {
      return "Storing something";
   }

   public String note()
   {
      return "Add a few notes and reopen the recipe — they are still there. The same code runs on"
         + " desktop, Android, iOS and the web.";
   }

   public void build(Component body) throws Exception
   {
      open();

      Component row = new Component();
      row.setLayout(new GridLayout(Position.START, Position.CENTER));
      input = new TextField();
      input.caption("A note");
      input.testTag("store.input");
      Button add = new Button(MaterialIcon.of(0xE145, 1.4f));   // add
      add.getStyle()
         .backColor(Tones.accent())
         .iconColor(Styles.colorScheme().onPrimary)
         .borderColor(0);
      add.testTag("store.add");
      add.onAction(c -> save());
      row.add(1f, 0f, input, outgap(add, .5f, 0, 0, 0));
      body.add(LAY_AUTO, outgap(row, 0, 0, 0, .8f));

      list = new Component();
      list.setLayout(new VerticalLayout(Position.START, Position.START));
      list.testTag("store.list");
      body.add(LAY_AUTO, list);
      reload();
   }

   private void open()
   {
      String base = Device.homeFolder;
      db = SqliteDb.open(base == null || base.isEmpty() ? "cf-notes.db" : base + "/cf-notes.db");
      db.exec("CREATE TABLE IF NOT EXISTS note(id INTEGER PRIMARY KEY AUTOINCREMENT, text TEXT)");
   }

   private void save()
   {
      String t = input.getText()
         .trim();
      if (t.isEmpty())
         return;
      db.execute("INSERT INTO note(text) VALUES(?)", t);   // bound, never concatenated
      input.setText("");
      reload();
      Screen.repaint();
   }

   private void reload()
   {
      try
      {
         list.removeAll();
         // removeAll() takes the layout with it — put it back before adding anything.
         list.setLayout(new VerticalLayout(Position.START, Position.START));
         // Columns named, so position 0 stays position 0 when the table grows a column.
         try (SqliteCursor c = db.query("SELECT id, text FROM note ORDER BY id DESC LIMIT 12"))
         {
            boolean any = false;
            while (c.next())
            {
               any = true;
               list.add(LAY_AUTO, note(c.getLong(0), c.getString(1)));
            }
            if (!any)
               list.add(LAY_AUTO, label("Nothing stored yet.", 1f, false, Styles.captionColor,
                  Position.START));
         }
         // invalidateMeasure, not setValidLayout(false): the list grew, and the scrolling body
         // around it has to measure it again - or the new note came out cut (iPhone, 17/10/2026).
         list.invalidateMeasure();
      }
      catch (Exception e)
      {
         dev.crossfly.util.Debug.handle(e);
      }
   }

   private Component note(final long id, String text) throws Exception
   {
      Card c = new Card();
      c.setLayout(new GridLayout(Position.START, Position.CENTER));
      c.getStyle()
         .outgap(Gap.ofFont(0, 0, 0, .4f));
      Button del = new Button(MaterialIcon.of(0xE872, 1.2f));   // delete
      del.getStyle()
         .iconColor(Tones.danger())
         .borderColor(0)
         .backColor(0);
      del.onAction(e ->
      {
         db.execute("DELETE FROM note WHERE id=?", id);
         reload();
         Screen.repaint();
      });
      c.add(1f, 0f, label(text, 1f, false, Styles.textColor, Position.START), del);
      return c;
   }

   /** Closing when the screen goes away — a connection is a resource, not a value. */
   @Override
   public void onRemove()
   {
      super.onRemove();
      if (db != null)
      {
         db.close();
         db = null;
      }
   }
}
एआई सहायक के लिए

इस पृष्ठ के मशीन-पठनीय रूप, ताकि मॉडल API का अनुमान लगाने के बजाय उदाहरण पढ़ सके।

यह उदाहरण, JSON मेंयह उदाहरण, Markdown में
पिछलाA list too big to buildअगलाCapturing a signature