İçeriğe geç
academia.sh

Ders 04 / 10

Kendi Çizim Motoruyla Yaklaşım

Kendi çizim motoruyla çalışan yaklaşım platformun yerel bileşenlerini kullanmaz; arayüzü kendi motoruyla bir tuvale çizer. Ders düzen, boyama sırası ve dokunma çözümlemesinin paylaşımını ölçer ve metin genişliğinin platformdan gelmesinin düzeni nasıl dallandırdığını gösterir.

Yayın Güncelleme

Bu derste

  • Kendi çizim motoruyla çalışan yaklaşımın tuvali, bileşen ağacını ve düzeni nasıl ele aldığını açıklar
  • Kutuların konumunu hesaplayan düzen işlevinin neden iki platformda ortak çıktığını ölçer
  • Motorun dokunma ve kaydırma çözümlemesini kendi yaptığını paylaşım açısından gösterir
  • Metin genişliği platformun yazı tipi ölçüsünden gelince aynı düzen işlevinin dallı çıktığını ölçer
İçindekiler

Önceki ders ortak kodun bir düğüm ağacı kurduğunu ve köprünün bu ağacı platformun kendi bileşenlerine eşlediğini gösterdi; paylaşım dallı çıktı, çünkü bileşenin kimliği platforma aitti. Üçüncü düzenek bu eşlemeyi tümüyle kaldırır: platformun yerel bileşenleri kullanılmaz. Uygulama, platformun verdiği tek bir yüzeye, tuvale, kendi çizim motoruyla her şeyi çizer.

Kursun sorusu değişmez: bu özelliğin kodu platformlar arasında nasıl paylaşılıyor? Çizim motoru bu soruya cesur bir yanıt verir. Düğme, liste, metin alanı ve kaydırma çubuğu motorun kendi kodudur; platformun karşılığı yoktur. Kod bu yüzden en geniş biçimde ortak olabilir.

Düzenek şöyle çalışır. Uygulama bir bileşen ağacı kurar; ağaçtaki her öğe bir düzen kuralıyla boyutlandırılır ve konumlandırılır. Motor ağacı gezer, her öğeyi tuvale çizer ve platformdan temel olarak girdileri alır: dokunma noktaları, tuvalin boyutu ve kare başına bir sinyal. Erişilebilirlik, yaşam döngüsü ve platform iletileri de platform tarafından sağlanır. Yerel bileşen olmadığı için platformun kendi düğmesinin görünüşü ya da davranışı kullanılmaz; motor bunları kendisi taklit eder ya da kendi tasarımını çizer.

YA1. Model, bu paylaşımın kuralını kurar; model çizim motorunu, tuvali ya da bir kareyi çalıştırmaz. Modelde motorun düzen işlevi kutu genişliklerinden satırlar üretir. Kapsayıcı genişliği 100’dür ve bu sayı modelin girdisidir; bir aygıtın ekran ölçüsü değildir.

// sharing.mjs — bir ozelligin kodunun platformlar arasinda nasil paylasildigini siniflandirir
export const PLATFORMS = ["ios", "android"];

export class Sharing {
  constructor(label, impl, samples) {
    this.label = label;
    this.total = samples.length;
    this.missing = PLATFORMS.filter((p) => typeof impl[p] !== "function");
    const present = PLATFORMS.filter((p) => !this.missing.includes(p));
    this.code = new Set(present.map((p) => impl[p])).size;
    if (this.missing.length > 0) {
      this.differ = null;
      this.verdict = "tek";
      return;
    }
    this.differ = samples.filter((s) => {
      const outs = PLATFORMS.map((p) => JSON.stringify(impl[p](s, p)));
      return new Set(outs).size > 1;
    }).length;
    if (this.code > 1) this.verdict = "ikiz";
    else this.verdict = this.differ === 0 ? "ortak" : "dalli";
  }

  line() {
    const detail = this.differ === null
      ? `eksik=${this.missing.join(",")}`
      : `fark=${this.differ}/${this.total}`;
    console.log(`${this.label}: kod=${this.code} | ${detail} -> ${this.verdict}`);
  }
}

export function countSharing(items) {
  for (const name of ["ortak", "dalli", "ikiz", "tek"]) {
    console.log(`${name}: ${items.filter((s) => s.verdict === name).length}`);
  }
}

Düzen: Kutuların Konumu

Düzenin en basit hâli satır kırmadır. Kutular sırayla soldan sağa dizilir; bir sonraki kutu kapsayıcıya sığmıyorsa yeni bir satır başlar. İşlev kutu genişliklerinden satırlar üretir ve platformu hiç okumaz. Aynı bloğa boyama sırasını da koyuyoruz: katmanlar z değerine göre sıralanır, küçük z önce boyanır.

// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";

function layoutBoxes(widths) {
  const rows = [];
  let row = [];
  let used = 0;
  for (const w of widths) {
    if (row.length > 0 && used + w > 100) {
      rows.push(row);
      row = [];
      used = 0;
    }
    row.push(w);
    used += w;
  }
  rows.push(row);
  return rows;
}

function paintOrder(layers) {
  return [...layers].sort((a, b) => a.z - b.z).map((l) => l.name).join(">");
}

const boxSets = [[30, 40, 20], [60, 50], [100], [45, 45, 45]];
const layerSets = [
  [{ name: "metin", z: 2 }, { name: "zemin", z: 0 }, { name: "golge", z: 1 }],
  [{ name: "dugme", z: 1 }, { name: "zemin", z: 0 }, { name: "ipucu", z: 3 }],
  [{ name: "liste", z: 1 }, { name: "zemin", z: 0 }, { name: "baslik", z: 2 }],
];

const features = [
  new Sharing("kutu-yerlesimi", { ios: layoutBoxes, android: layoutBoxes }, boxSets),
  new Sharing("boyama-sirasi", { ios: paintOrder, android: paintOrder }, layerSets),
];
features.forEach((f) => f.line());
countSharing(features);
kutu-yerlesimi: kod=1 | fark=0/4 -> ortak
boyama-sirasi: kod=1 | fark=0/3 -> ortak
ortak: 2
dalli: 0
ikiz: 0
tek: 0

İki özellik de ortaktır. kutu-yerlesimi için kod=1 ve fark=0/4 çıktı: dört kutu kümesinin hiçbirinde iki platform farklı satır üretmedi. Düzen motorun içinde hesaplanır ve girdisi yalnız kutu genişlikleridir. Aynı bileşen ağacı iki platformda aynı yerleşimi verir; bu yaklaşımın en güçlü vaadidir. Düzenin platformdan bağımsız olması, bileşenin kendi düzeninden doğan platforma özgü hataların ortadan kalkması demektir; yazı tipi gibi platformdan gelen girdiler bunun dışındadır. Bir düzen hatası düzeltildiğinde iki platform birlikte düzelir.

Motoru kodda kullanan taraf, tuvale çizim işini şöyle yazar. Parça ölçülmez; model motoru çalıştırmaz.

// Platform kodu: LabelPainter
import 'package:flutter/widgets.dart';

class LabelPainter extends CustomPainter {
  LabelPainter(this.text);
  final String text;

  @override
  void paint(Canvas canvas, Size size) {
    final painter = TextPainter(
      text: TextSpan(text: text, style: const TextStyle(color: Color(0xFF202020))),
      textDirection: TextDirection.ltr,
    )..layout(maxWidth: size.width);
    painter.paint(canvas, Offset.zero);
  }

  @override
  bool shouldRepaint(LabelPainter old) => old.text != text;
}

Parçanın kritik satırı layout(maxWidth: size.width) satırıdır. Metnin ne kadar yer kaplayacağı burada hesaplanır ve bu hesap, motorun tuvale çizeceği şeyi belirler. Bu hesabın girdisi neyden gelir sorusu, dersin sınırlayıcı ölçümünde yeniden karşımıza çıkacak.

Girdi ve Kaydırma Motorda

Motor yalnız çizmez; dokunmayı da kendi çözümler. Platform dokunma noktasını verir, hangi öğeye dokunulduğunu motor bulur. Kaydırma konumunu da motor sınırlar: içerik bir görünüm kadar uzunsa konum sıfırla içerik farkı arasında kalır. İki işlev de platformu okumaz.

YA2. Sabit kutular modelde dört bölgedir. İçerik yüksekliği 300, görünüm yüksekliği 100’dür; bu sayılar modelin girdisidir ve kaydırma konumu 0 ile 200 arasında kalır.

// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";

const regions = [
  { x: 0, y: 0, w: 50, h: 40 },
  { x: 50, y: 0, w: 50, h: 40 },
  { x: 0, y: 40, w: 100, h: 60 },
];

function hitTest(point) {
  const [px, py] = point;
  return regions.findIndex(
    (r) => px >= r.x && px < r.x + r.w && py >= r.y && py < r.y + r.h,
  );
}

function clampScroll(offset) {
  return Math.min(Math.max(offset, 0), 200);
}

const points = [[5, 5], [60, 10], [30, 70], [200, 5]];
const offsets = [-10, 0, 120, 500];

const features = [
  new Sharing("dokunma-hedefi", { ios: hitTest, android: hitTest }, points),
  new Sharing("kaydirma-siniri", { ios: clampScroll, android: clampScroll }, offsets),
];
features.forEach((f) => f.line());
countSharing(features);
dokunma-hedefi: kod=1 | fark=0/4 -> ortak
kaydirma-siniri: kod=1 | fark=0/4 -> ortak
ortak: 2
dalli: 0
ikiz: 0
tek: 0

Böylece dört özellik ortak çıktı: yerleşim, boyama sırası, dokunma hedefi ve kaydırma sınırı. Dördü de motorun içindedir ve dördü de platformu okumaz. Yaklaşımın ortaklık vaadi, motorun yerel bileşenleri gerçekten bıraktığı ölçüde tutar.

Bu vaadin bedeli, platformun kendi bileşeninden gelen şeylerin kaybıdır. Bir yerel metin alanının erişilebilirlik davranışı, klavye düzeni ve seçim menüsü platformun işidir. Motor bunları kendi çizdiği bir alanda taklit etmek zorundadır; taklit eksik kalırsa kullanıcı bunu hisseder. Kural bu eksikliği görmez, çünkü eksiklik bir paylaşım farkı olarak değil bir davranış farkı olarak yaşanır.

Sınırlayıcı Ölçüm

Düzen platformu okumaz dedik, ama düzenin girdisi yalnız kutu genişlikleri midir? Metin bir kutudur ve kutunun genişliği, kullanılan yazı tipinin karakter ölçüsünden gelir. Yazı tipi motorla birlikte gelen bir yazı tipiyse ölçü iki platformda aynıdır. Platformun kendi yazı tipi kullanılıyorsa ölçü platforma göre değişir.

YA3. Karakter genişliği modelde bir girdidir: motorun kendi yazı tipinde 6, iOS’un sistem yazı tipinde 6, Android’in sistem yazı tipinde 7. Gerçek yazı tipi ölçüleri karakterden karaktere değişir ve bu üç sayı tek bir temsil değeridir. Kapsayıcı genişliği 60’tır; bu durumda satır başına karakter sayısı iOS’ta 10, Android’de 8 olur.

// text.mjs — metni karakter genisligine gore satirlara boler
export function glyphWidth(platform) {
  return platform === "ios" ? 6 : 7;
}

export function bundledWidth() {
  return 6;
}

export function makeWrap(widthOf) {
  return function wrapText(text, platform) {
    const perLine = Math.floor(60 / widthOf(platform));
    const lines = [];
    let line = "";
    for (const word of text.split(" ")) {
      const next = line === "" ? word : `${line} ${word}`;
      if (line !== "" && next.length > perLine) {
        lines.push(line);
        line = word;
      } else {
        line = next;
      }
    }
    lines.push(line);
    return lines.join("/");
  };
}

Aynı dört metni iki kez ölçelim: bir kez motorla gelen yazı tipinin genişliğiyle, bir kez platformun yazı tipinin genişliğiyle. Kırma işlevi aynıdır; değişen yalnız genişliğin nereden geldiğidir.

// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";
import { glyphWidth, bundledWidth, makeWrap } from "./text.mjs";

const bundledWrap = makeWrap(bundledWidth);
const platformWrap = makeWrap(glyphWidth);

const texts = ["Kahve", "Cay kahve", "Haftalik plan", "Su ve cay"];

const features = [
  new Sharing("yazi-motor-fontu", { ios: bundledWrap, android: bundledWrap }, texts),
  new Sharing("yazi-platform-fontu", { ios: platformWrap, android: platformWrap }, texts),
];
features.forEach((f) => f.line());
countSharing(features);
yazi-motor-fontu: kod=1 | fark=0/4 -> ortak
yazi-platform-fontu: kod=1 | fark=2/4 -> dalli
ortak: 1
dalli: 1
ikiz: 0
tek: 0

İki ölçümde de kod=1 ve motor aynıdır. Motorla gelen yazı tipinde dört metin iki platformda aynı satırlara bölündü ve paylaşım ortak çıktı. Platformun yazı tipinde ise iki metin ayrıştı: “Cay kahve” ve “Su ve cay”. Dokuz karakterlik bu iki metin, iOS’ta on karakterlik satıra sığar, Android’de sekiz karakterlik satıra sığmaz ve ikiye bölünür. “Haftalik plan” ise iki platformda da ikiye bölünür, çünkü on üç karakter iki sınırı da aşar; sonuç aynı kalır.

Motor her pikseli kendisi çizse de girdisinin bir kısmı platformdan gelir. Yazı tipi seçimi gibi bir girdi platforma bağlandığında düzen, yerleşimin kendisini değiştirmeden dallanır. Bu yaklaşımda paylaşımı korumanın yolu, platformdan girdi almayı en aza indirmektir; motorla gelen yazı tipi bu yüzden ortak düzenin ön koşuludur. Kural, girdinin nereden geldiğini görmez; yalnız sonucun ayrıştığını görür ve ayrışmanın sebebini düzyazı söyler.

Özet

  • Bu dersin paylaşımı: ortak. Kutuların konumunu hesaplayan düzen işlevi platformu okumaz ve iki platformda dört kutu kümesinde aynı yerleşimi verdi.
  • Kendi çizim motoruyla çalışan yaklaşımda düzen, boyama sırası, dokunma hedefi ve kaydırma sınırı motorun içindedir; dördü de ortak çıktı.
  • Metin genişliği platformun yazı tipi ölçüsünden gelince aynı düzen işlevi dört metnin ikisinde farklı satır kırdı ve paylaşım dallıya döndü.
  • Motorla gelen yazı tipinde aynı metinler ortak çıktı: platformdan girdi almak, paylaşımı kodu değiştirmeden dallandırır.
  • Kural paylaşımın yapısını görür, bedelini görmez; yerel bileşenin erişilebilirlik ve seçim davranışlarının kaybı düzyazıda söylenir.

Sonraki Adım

Bu derste platformun yerel bileşenleri bütünüyle bırakıldı ve paylaşım en geniş hâline vardı. Dördüncü düzenek ters yönde gider: yalnız iş kurallarını ortaklaştırır, arayüzü her platformda yerel yazar. Sıradaki ders bu düzenekte arayüzün paylaşımını ölçer.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat