Selam coderlar! Bu yazıda, Java’da sık sık kullanılan fakat pek adı geçmeyen LayoutManager interface’ine değineceğim. LayoutManager’lar, swing’de bir Container’ın içerisindeki Component’ların diziliminin belirli bir kurala göre ayarlanmasını sağlar. LayoutManager interface’inden türetilmiş hali hazırda birçok class olduğu için, insanlar bu küçük interface’in adını pek anmazlar.

Hali hazırdaki LayoutManager’lar için: A Visual Guide to Layout Managers

Peki neden biz de küçük bir LayoutManager yazmayalım? Bir nedenimiz yok. Öyleyse başlayalım.

Öncelikle LayoutManager interface’inde ne var ne yok bir bakalım:

/**
 * Parent Container'a başka bir Component eklenince
 * LayoutManager'a da haber verilir
 */
void addLayoutComponent(String, Component);

/**
 * Parent Container'dan bir Component silindiğinde
 * bize haber eder. remove veya removeAll methodları
 * bu methodu tetikler.
 */
void removeLayoutComponent(Component);

/**
 * Parent Container'ın getPreferredSize() methodu
 * tarafından çağırılır. Container'ın ideal
 * boyutlarını return etmesi beklenir.
 */
Dimension preferredLayoutSize(Container);

/**
 * Parent Container'ın getMinimumSize() methodu
 * tarafından çağırılır. Container'ın minimum
 * boyutlarını return etmesi beklenir.
 */
Dimension minimumLayoutSize(Container);

/**
 * Parent Container'ın tüm Component'larının
 * boyutlarını ve pozisyonlarını ayarlaması
 * beklenen method.
 */
void layoutContainer(Container);

Şimdi örnek olarak yazacağım LayoutManager’da, ben removeLayoutComponent ve addLayoutComponent methodlarını kullanmayacağım. Benim LayoutManager’larım genellikle elemanların listesini tutmaz ve sonradan eklenen elemanlara göre ekstra işlem yapmaz. Genellikle işlemleri layoutContainer ve minimumLayoutSize methodlarında yaparım. Şimdi bir LayoutManager yazmaya başlayalım. Bu LayoutManager’ın ismi AltAlta ve özelliği de herşeyi alt alta dizmesi. Alt alta dizerken de tüm elemanların genişliğini sabit ve eşit tutuyor. Yükseklik değerlerini ise hiç değiştirmeden olduğu gibi kullanıyor.

Bu AltAlta layout’u kenarlardan beşer pixellik boşluklar bırakıyor ve elemanların arasında da belirli miktarda bir boşluk bırakıyor. Önce constructor’lara ve private alanlara bakalım.

public class AltAlta implements LayoutManager {
    // Elemanlar arası boşluk
    private int gap = 5;

    // Kenar boşlukları
    private int myInsets = 5;

    public AltAlta() {}

    public AltAlta(int gap) {
        this.gap = gap;
    }

Şimdi AltAlta layout’unun zihnimizde canlanması için bir fotoğraf yükleyeyim:

Alt Alta Layout Manager

Gördüğünüz gibi, tüm elemanlar kendi yüksekliklerine sahipler fakat hepsinin genişliği eşitlenmiş durumda. Şimdi bunu sağlayan layoutContainer methodunu inceleyelim.

layoutContainer methodunda yapmamız gereken öncelikle parent Container’ın içindeki tüm Component’ların genişlik değerlerine bakıp maksimumu bulmak. Bunu basit bir döngüyle hallediyoruz:

public void layoutContainer(Container parent) {
    int nComp = parent.getComponentCount();
    Dimension d = null;
    int maxWidth = 0;
    for(int i = 0; i < nComp; i++) {
        d = parent.getComponent(i).getPreferredSize();
        if(d.width > maxWidth) {
            maxWidth = d.width;
        }
    }

Böylece maxWidth isimli bir değişkende maksimum genişliği yakalamış olduk. Şimdi tüm Component’ları bu genişlikte birbirinin altına yerleştirelim. LayoutManager yazarken dikkat etmemiz gereken bir diğer nokta da Insets. Insets çok önemlidir. Eğer Container bir Border içeriyorsa, bu bize bir Insets objesi olarak bildirilir. Elemanları yerleştirirken, onları kenara dayamadan önce, kenarda bir border var mı yok mu bakmamız gerekir. layoutContainer methodumuzun geri kalan kısmı da şu şekilde olacak:

     Insets borders = parent.getInsets();
     int top = borders.top + myInsets;
     for(int i = 0; i < nComp; i++) {
          Component c = parent.getComponent(i);
          d = c.getPreferredSize();
          c.setBounds(borders.left + myInsets, top, maxWidth, d.height);
          top += d.height + gap;
     }
}

Burada yaptığımız işlem, her elemanı sayfaya eklediğimizde, bir sonraki elemanın yukarıya ne kadar uzaklıkta olacağını hesaplamak(int top değişkeni) ve elemanı maxWidth genişlikte tutmak. setBounds(int x, int y, int width, int height) methodu ile elemanımızı parent Container içerisinde konumlandırıyoruz.

Şimdi minimumLayoutSize methoduna bakalım. Burada yapmamız gereken, parent Container’ımızın ne kadar yer kaplayacağını hesaplayıp bunu return etmek. Yine en önemli noktalardan birisi Border’ları, yani Insets’i hesaba katmayı unutmamak. Öncelikle yine bir maxWidth değişkeni tutarak elemanların genişliklerinin maksimumunu hesaplayacağız. Aynı zamanda tüm elemanların yüksekliklerinin de toplamını hesaplayacağız. Daha sonra private int gap olarak tuttuğumuz elemanlar arası boşlukları ve border yüksekliklerini bu yüksekliğe ekleyeceğiz. Hesapladığımız maksimum eleman genişliğine de border genişliklerini ekleyeceğiz. Ben bu yükseklik ve genişlik değerlerine bir de private olarak tuttuğum myInsets değişkenini ekliyorum, benim Layout Manager’ım kenarlardan 5 pixel fazla boşluk bıraksın istediğim için. Şimdi kodu görelim:

public Dimension minimumLayoutSize(Container parent) {
   Insets borders = parent.getInsets();
   int nComp = parent.getComponentCount();
   Dimension d = null;
   int maxWidth = 0;
   int sumHeight = 0;
   for(int i = 0; i < nComp; i++) {
        d = parent.getComponent(i).getPreferredSize();
        if( d.width > maxWidth ) {
          maxWidth = d.width;
        }
        sumHeight += d.height;
   }
   int height = sumHeight + borders.top + myInsets * 2 + borders.bottom + gap * (nComp - 1);
   int width = maxWidth + borders.left + borders.right + myInsets * 2;
   return new Dimension(width, height);
}

Şimdi bahsetmediğimiz bir method kaldı. preferredLayoutSize methodunda da Container’ımız için tercih ettiğimiz genişlik ve yükseklik değerini vermemiz gerekiyor. Ben bunun minimumLayoutSize‘dan pek farklı olacağını düşünmediğim için doğrudan diğer fonksiyona yönlendiriyorum. Yani şöyle:

public Dimension preferredLayoutSize(Container parent) {
   return minimumLayoutSize(parent);
}

Bu konu hakkında söylemek istediklerim de bu kadar. Yazmadığım ve havada kalan birşeyler varsa bana yorum bırakın, lütfen. Son olarak AltAlta sınıfının tam halini ve onu kullanan, yukarıdaki resimde gördüğünüz mini uygulamayı da paylaşayım.

AltAlta.java -> Github Gist

Pencere.java -> Github Gist

İlerleyen günlerde Java’da Swing ile ilgili daha derin konulara iniyorken bulabiliriz kendimizi. Bu yazılık bu kadar, kalın sağlıcakla.