Dialogdesign med Java Swing – del 8

Våra valbara-paneler i vänstra kolumnen i skissen ovan delar egenskaper utöver basklassen som vi redan baserat vår valbara-panel på. Det gör att vi kan skapa ytterligare en abstrakt klass som baseras på vår basklass, som vi sedan kan basera panelerna för standardintervall och egna intervall på.

Basklassen för våra valbara-komponenter

package com.golcher.tidsintervall.komponenter.valbara;

import com.golcher.tidsintervall.dialoger.ITidsintervallModellFasad;
import com.golcher.tidsintervall.komponenter.AbstraktTidsintervallDialogKomponent;
import com.golcher.tidsintervall.komponenter.data.KomponentFilter;
import com.golcher.tidsintervall.komponenter.data.TidsintervallNyckel;
import com.golcher.tidsintervall.komponenter.data.TidsintervallWrapper;

import javax.swing.*;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.ArrayList;

public abstract class AbstraktTidsintervallValbarKomponent extends AbstraktTidsintervallDialogKomponent
{
    private final JComboBox _valbara;
    private TidsintervallValbarModell _komponentModell;

    public AbstraktTidsintervallValbarKomponent
    (
        String ramRubrik,
        String knappRubrik,
        ITidsintervallModellFasad modellFasad,
        KomponentFilter filter
    )
    {
        super(modellFasad, filter);
        this.setBorder
        (
            BorderFactory.createTitledBorder
            (
                BorderFactory.createEtchedBorder(),
                ramRubrik,
                TitledBorder.LEFT,
                TitledBorder.TOP
            )
        );
        this.setLayout(new FlowLayout(FlowLayout.LEFT));

        _komponentModell = new TidsintervallValbarModell();
        _valbara = new JComboBox<>(_komponentModell);
        _valbara.setPreferredSize(new Dimension(hamtaLampligBredd(), 20));
        this.add(_valbara);

        JButton valjKnapp = new JButton(knappRubrik);
        this.add(valjKnapp);

        valjKnapp.addActionListener(new AbstractAction()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(AbstraktTidsintervallValbarKomponent.this._valbara.getItemCount() > 0)
                {
                    TidsintervallWrapper wrapper = (TidsintervallWrapper) AbstraktTidsintervallValbarKomponent.this._valbara.getSelectedItem();
                    if(null!=wrapper)
                    {
                        TidsintervallNyckel nyckel = wrapper.nyckel();
                        valj(nyckel);
                    }
                }
            }
        });

        uppdateraLokalModell();
    }

    private void uppdateraLokalModell()
    {
        ArrayList<TidsintervallWrapper> sorteradLista = hamtaKomponentDataFranDialogModell();
        _komponentModell.uppdateraModellen(sorteradLista);
        _valbara.setPreferredSize(new Dimension(hamtaLampligBredd(),20));
        _valbara.invalidate();
    }

    @Override
    public void modellenHarUppdaterats()
    {
        uppdateraLokalModell();
    }


}

Här är den delade modellen för våra valbara – vilka båda behöver en ComboBoxModel
Modellen ska i sin tur lyssna på dialogmodellen men ska inte behöva veta mer än att den får data via uppdateraModellen, vilket precis som för valda-komponenten kommer ifrån panelen som agerar lyssnare på dialogmodellen.

ComboBox-modellen som våra valbara komponenter båda använder sig av

package com.golcher.tidsintervall.komponenter.valbara;

import com.golcher.tidsintervall.komponenter.data.TidsintervallWrapper;

import javax.swing.*;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import java.util.ArrayList;

public class TidsintervallValbarModell implements ComboBoxModel
{
    private ArrayList<TidsintervallWrapper> _lista;
    private ArrayList<ListDataListener> _lyssnare;
    private TidsintervallWrapper _selectedItem;

    public TidsintervallValbarModell()
    {
        _lista = new ArrayList<>();
        _lyssnare = new ArrayList<>();
        _selectedItem = null;
    }

    public void uppdateraModellen(ArrayList<TidsintervallWrapper> nyLista)
    {
        _lista = nyLista;

        TidsintervallWrapper currentItem = null;
        if(_lista.size() > 0)
        {
            currentItem = _lista.get(0);
        }
        _selectedItem = currentItem;

        for(ListDataListener l : _lyssnare)
        {
            l.contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, _lista.size()-1));
        }
    }

    @Override
    public void setSelectedItem(Object anItem)
    {
        if(null!=anItem && anItem instanceof TidsintervallWrapper)
        {
            _selectedItem = (TidsintervallWrapper) anItem;
        }
    }

    @Override
    public Object getSelectedItem()
    {
        return _selectedItem;
    }

    @Override
    public int getSize()
    {
        return _lista.size();
    }

    @Override
    public Object getElementAt(int index)
    {
        if(index < 0 || index > _lista.size()-1) throw new ArrayIndexOutOfBoundsException(index);
        return _lista.get(index);
    }

    @Override
    public void addListDataListener(ListDataListener l)
    {
        _lyssnare.add(l);
    }

    @Override
    public void removeListDataListener(ListDataListener l)
    {
        _lyssnare.remove(l);
    }
}

Slutligen kan vi skapa våra två paneler – den ena i princip direkt från den delade basklassen och den andra med lite mer kod för redigering av intervall.

Panelen för standardintervall

package com.golcher.tidsintervall.komponenter.valbara;

import com.golcher.tidsintervall.dialoger.ITidsintervallModellFasad;
import com.golcher.tidsintervall.komponenter.data.KomponentFilter;

public class StandardIntervallPanel extends AbstraktTidsintervallValbarKomponent
{
    public StandardIntervallPanel
    (
        ITidsintervallModellFasad modellFasad
    )
    {
        super("Standardintervall", "Välj", modellFasad, KomponentFilter.Standard);
    }
}

Panelen för egna intervall

package com.golcher.tidsintervall.komponenter.valbara;

import com.golcher.tidsintervall.dialoger.ITidsintervallModellFasad;
import com.golcher.tidsintervall.komponenter.data.KomponentFilter;
import com.golcher.tidsintervall.komponenter.data.TidsintervallVo;
import com.golcher.tidsintervall.redigerare.IEgnaIntervallRedigerare;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.util.ArrayList;

public class EgnaIntervallPanel extends AbstraktTidsintervallValbarKomponent
{
    private final IEgnaIntervallRedigerare _redigerare;
    public EgnaIntervallPanel
    (
        ITidsintervallModellFasad modellFasad,
        IEgnaIntervallRedigerare redigerare
    )
    {
        super("Egna intervall", "Välj", modellFasad, KomponentFilter.Egen);
        _redigerare = redigerare;
        if(null!=_redigerare) this.add(redigeringsKnapp());
    }

    private JButton redigeringsKnapp()
    {
        JButton knapp = new JButton("Redigera");
        knapp.addActionListener(new AbstractAction()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                ArrayList<TidsintervallVo> egnaIntervall = _redigerare.visaRedigeringsDialog();
                uppdateraEgnaIntervallFranRedigerare(egnaIntervall);
            }
        });
        return knapp;
    }

}

I våra kommande två avsnitt syr vi ihop säcken för det här dialogexemplet.