Einzelne Zelle in JTable Rahmen unten setzen

HoT

HoT

Mitglied
Hallo Forum,
ich habe jetzt ca. 10 Stunden mit Youtube und Forum lesen verbracht, aber leider keine Ahnung, wie ich in einer Tabelle bei einer einzelnen Zelle nur den Rahmen unten oder rechts setzen kann.
Ich habe einen Turnierplaner programmiert, welcher ein KO-System erzeugt und wollte dies als Baum anzeigenlassen.
Den Spielplan erstellt das Programm richtig. Dabei wird der Turnierbaum (siehe Anhang) Spaltenweise in einer Tabelle geschrieben. (z.B.. Erste Spalte "Runde der letzten 16", 2 Spalte Runde der letzten 8, 3 Spalte Halbfinale, 4 Spalte Finale).
Immer wenn eine Runde gespielt wurde, wird diese ausgewertet und die neuen Begegnungen in die nächste Spalte geschreiben.

Um das Ganze optisch zu verschönern, würde ich jetzt gerne durch setzen der Zellrahmen einen Schönen Turnierbaum erzeugen (Wie dies aussehen soll habe ich mal in Excel dargestellt, siehe Anhang")
Ich würde also gerne nachdem die Spalte der Begegnungen geschrieben wurde eine Methode baumdesign() aufrufen, welche die "Striche"(Rahmen der Zellen) so setzt, dass es eben wie ein Turnierbaum ausschaut. Evtl kann man die Baumstruktur auch schon komplett zeichnen, denn man weis ja die Teilnehmerzahl
Ich kann aber ja die Zellen nicht ansprechen und sagen Celle(x,y).border(bottom).
Und wie das mit dem TableCellRenderer funktioniert, habe ich leider nicht verstanden.
Muss ich dafür eine komplett neue Klasse schreiben und wenn ja, wie übergebe ich diese an die Tabelle? (table.setCellRenderer(renderer))?
was kann ich mit prepareRenderer() anstellen?
Vielen Dank für die Info im Voraus.

hier noch die Methode welche die einzelnen Spalten der KOtable schreibt, nachdem eine andere Methode die Eingabe ausgewertet hat.
CODE:
public void kobaumSpaltenEintragen(int spaltennummer){
int zeilenbeginn = (int)Math.pow(2, (spaltennummer-1)); //In dieser Zeile steht die erste Mannschaft
int leerzeilen= zeilenbeginn*2-1; //Leerzellen zwischen zwei Teams
int spaltenende= kobaumarray.size()+leerzeilen*(kobaumarray.size()-1);
int teamzaehler=0;
int a = zeilenbeginn-1;

while(teamzaehler<kobaumarray.size()){
su.kobaumtable.setValueAt(""+kobaumarray.get(teamzaehler),a,spaltennummer-1);
a=a+leerzeilen+1;
teamzaehler++;
}
baumspaltencounter++;
//Zellenrahmen setzen

}
 

Anhänge

  • Kobaum.png
    Kobaum.png
    11,1 KB · Aufrufe: 11
  • Excel.png
    Excel.png
    5,9 KB · Aufrufe: 10
mihe7

mihe7

Top Contributor
Ich kann aber ja die Zellen nicht ansprechen und sagen Celle(x,y).border(bottom).
Und wie das mit dem TableCellRenderer funktioniert, habe ich leider nicht verstanden.
Muss ich dafür eine komplett neue Klasse schreiben und wenn ja, wie übergebe ich diese an die Tabelle? (table.setCellRenderer(renderer))?
Von oben nach unten: doch, so etwas kann man sich basteln. TableCellRenderer funktioniert wie ein Stempel, d. h. es wird nicht für jede Zelle eine neue Komponente erstellt, sondern die selbe Komponente wird zum Zeichnen mehrfach verwendet. Ja, dafür schreibt man eine Klasse und ja, man registriert den Renderer bei der JTable bzw. den TableColumns.

Man kann auch Methoden von JTable überschreiben, wenn man das braucht. In der Regel ist das aber unnötig.

In Deinem Fall könntest Du z. B. ein BorderModel erstellen, das Du deinem Renderer übergibst.
Java:
interface BorderModel {
    Border getBorder(int row, int col);
}

Dann noch einen passenden Renderer dazu, z. B.:
Java:
public class BorderRenderer implements TableCellRenderer {
    private final BorderModel borders;

    public BorderRenderer(TableCellRenderer delegate, BorderModel borders) {
        this.delegate = delegate;
        this.borders = borders;
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, 
            boolean isSelected, boolean hasFocus, int row, int column) {
        JComponent comp = (JComponent) delegate.getTableCellRendererComponent(
                table, value, isSelected, hasFocus, row, column);
        comp.setBorder(borders.getBorder(row, column));
        return comp;
    }
}
Achtung: den Code habe ich nur hier in den Editor getippt, er kann also Fehler enthalten. Auch wäre er ausbaufähig, z. B. könnte man den Border nur setzen, wenn die Zelle nicht ausgewählt ist bzw. den Fokus hat usw. Das kannst Du ja machen, wie Du es brauchst.

Und das wars auch schon im großen und ganzen, was die Tabelle betrifft. Jetzt musst Du nur noch BorderModel implementieren, z. B. könntest Du standardmäßig einen bestimmten Rahmen zurückgeben. Die Rahmen für ausgewählte Zellen ließen sich über eine Methode setBorder(row, column, aBorder) in einer Map verwalten.
 
HoT

HoT

Mitglied
Hallo mihe7
Danke für deine schnelle Antwort. Leider komme ich immer noch nicht ganz klar. Ich bin erst ein Anfänger in Sachen programmieren.
Also ich erstelle ein Interface BorderModel.
Dann eine Klasse BorderRenderer in der ich das interface implementiere.
Doch wo sage ich jetzt wie der Rahmen aussehen soll? (Könntest du mir dazu bitte noch ein Beispiel schicken.) oder ist dieses hier richtig.
MatteBorder unten= new MatteBorder(0,0,2,0,black);
Und welche Zeile er den Rahmen stempeln soll mach ich mit einer for-schleife mit der ich durch alle Zeilen gehen und entsprechenden if-abfragen, welche in dem Code oben zwischen Zeile 14 und 15 kommt, oder?.
Wenn ich drei verschiedene Stempel habe, wo muss ich die anlegen (Meintest du eine Hashmap? im Interface oder in der BorderRender Klasse?)
Sorry, aber da steige ich noch nicht ganz durch.
Vielen Dank im Voraus
 
mihe7

mihe7

Top Contributor
Nein, nein. Nochmal langsam: ein TableCellRenderer wird von JTable wie ein Stempel benutzt. Wenn JTable eine Zelle malt, holt sie sich einen registrierten Renderer, ruft dessen getTableCellRendererComponent-Methode auf und malt die zurückgegebene Komponente auf den Schirm.

Der BorderRenderer ist ein solcher TaleCellRenderer, der einen beliebigen anderen TableCellRenderer mit der Fähigkeit ausstattet, um dessen Komponente einen Rahmen zu setzen, der aus einem BorderModel abgerufen wird. Der BorderRenderer implementiert nicht das BorderModel, sondern nutzt dieses nur.

Das BorderModel musst Du schon noch irgendwo implementieren. Wie Du das machst, ist dem BorderRenderer egal, Hauptsache ist, dass getBorder() für eine gegebene Zelle einen Rahmen liefert.

Als separate Klasse könnte das quick & dirty in etwa so aussehen:
Java:
public class SparseBorderModel implements BorderModel {
    private final Border defaultBorder;
    private Map<Point, Border> borders = new HashMap<>();

    public SparseBorderModel() {
        defaultBorder = BorderFactory.createEmptyBorder();
    }

    public SparseBorderModel(Border defaultBorder) {
        this.defaultBorder = defaultBorder;
    }

    public void setBorder(int row, int column, Border border) {
        borders.put(new Point(row, column), border);
    }

    public Border getBorder(int row, int column) {
        borders.getOrDefault(new Point(row, column), defaultBorder);
    }
}

Wenn Du jetzt den Spaß verwenden willst, geht das ungefähr so:
Java:
SparseBorderModel borders = new SparseBorderModel();
borders.setBorder(2, 1, new MatteBorder(0,0,2,0,black)); // für 3. Zeile, 2. Spalte einen MatteBorder verwenden
...
BorderRenderer renderer = new BorderRenderer(new DefaultTableCellRenderer(), borders);
JTable table = new JTable(...);
table.setDefaultRenderer(Object.class, renderer); // Daten mit nicht näher spezifiziertem Typ mit renderer zeichnen
// oder
table.getColumnModel().getColumn(0).setCellRenderer(renderer); // erste Spalte mit renderer zeichnen.
 
HoT

HoT

Mitglied
Danke für deine Antwort, aber ich verstehe nur die Hälfte.
Also SparseBorderModel ist eine neue Klasse in der ich unten eine Getter und eine Setter Methode habe, das habe ich soweit verstanden.
Bei getBorder(int row, int column) habe ich noch ein return eingefügt.
Der Unterschied zwischen Zeile 5 und 9 ist, dass ich den defaultBorder einmal leer setzte und einmal einen Border aus der Klasse Border übergebe.

Das BorderModel muss dann wieder eine eigene Klasse sein?
Was gehört da rein? Wie der Border aussehen soll, oder?
Aber im zweiten Code steht ja der MatterBorder, der doch angibt, wo der Rahmen gesetzt werden soll.????

Was macht der 2 Code?
Sorry ich verstehe es nicht.
 
mihe7

mihe7

Top Contributor
Bei getBorder(int row, int column) habe ich noch ein return eingefügt.
Richtig, habe ich vergessen.

Der Unterschied zwischen Zeile 5 und 9 ist, dass ich den defaultBorder einmal leer setzte und einmal einen Border aus der Klasse Border übergebe.
Richtig, das sind einfach zwei Konstruktoren. Der parameterlose dient einfach der Bequemlichkeit.

Das BorderModel muss dann wieder eine eigene Klasse sein?
Das ist einfach das Interface aus Kommentar 2.

Hier mal als Bild:
1608212632124.png


Was macht der 2 Code?
Was ist 2 Code?!?
 
HoT

HoT

Mitglied
Hi mihe7,
ok ich habe jetzt schon mal diesen Strich an der Stelle (3. Zeile, 2. Spalte) und werde jetzt etwas weiter experimentieren, wie ich damit in Richtung meines Zieles komme.
Vielen vielen Dank, auch wenn ich das ganze noch nicht so richtig verstanden habe.
Jetzt ist es sinnvoll die drei verschiedenen Rahmentypen(unten, unten und rechts, nur rechts) in die Map zu speichern und dann irgendwie abzurufen, oder?
Was ist hier das sinnvollste vorgehen?
P.s. Kann man bei dir eigentlich auch eine Videokenferenz-Nachhilfestunde buchen?
 

Anhänge

  • KoBaum_unterstrich.png
    KoBaum_unterstrich.png
    19,6 KB · Aufrufe: 7
HoT

HoT

Mitglied
Ok ich habe es auch hingebracht, alle Rahmen an den entsprechenden Zellen unten zu setzen.
Wollte jetzt die Rahmen unten und rechts setzen, da stürzt er aber ab.
Wo habe ich da einen Denkfehler?
Hier die Methode für die erste Spalte:
  public void rahmenLinienSetzenSpalteEins(int spaltennummer){
    int zeilenbeginn = (int)Math.pow(2, (spaltennummer-1)); //In dieser Zeile steht die erste Mannschaft
    int leerzeilen= zeilenbeginn*2-1;  //Leerzellen zwischen zwei Teams
    int spaltenende= needteams+leerzeilen*(needteams-1);//letzte Zeile der Tabelle
    System.out.println("Zeilenbegin = "+zeilenbeginn+" leerzeilen = "+leerzeilen+" Spaltenende = "+spaltenende);
    
    SparseBorderModel borders = new SparseBorderModel();
    BorderRenderer renderer = new BorderRenderer(new DefaultTableCellRenderer(), borders);
    su.jTkobaum.setDefaultRenderer(Object.class, renderer); // Daten mit nicht näher spezifiziertem Typ mit renderer zeichnen
    //Rahmen unten setzen
    int a=0;
    while(a<spaltenende){
    borders.setBorder(a, 0, new MatteBorder(0,0,2,0,black));
    a=a+4;
    }
    //Rahmen unten&rechts
    int b=0;
    while(2<spaltenende){
    borders.setBorder(b, 0, new MatteBorder(0,0,2,2,black));
    b=b+4;
    }

Dachte ich hab es verstanden, ist aber wohl doch nicht so. :-(
 

Anhänge

  • KoBaum_unterstrich.png
    KoBaum_unterstrich.png
    19,6 KB · Aufrufe: 8
mihe7

mihe7

Top Contributor
Jetzt ist es sinnvoll die drei verschiedenen Rahmentypen(unten, unten und rechts, nur rechts) in die Map zu speichern und dann irgendwie abzurufen, oder?
Genau, mit dem SparseBorderModel kannst Du eben für einzelne Zellen angeben, welcher Border gezeichnet werden soll.

Der Vorteil des Interfaces BorderModel ist, dass es völlig egal ist, wie die Implementierung aussieht. Du könntest auch ein BorderModel implementieren, das einfach berechnet, in welcher Zelle welcher Rahmen verwendet werden soll. Hinzu kommt, dass die Implementierung keine separate Klasse sein muss. Man kann das also auch kombinieren mit dem TableModel.
 
HoT

HoT

Mitglied
Hallo mihe7,
vielen Dank nochmal für deine Hilfe.
ich habe es hingebracht.
Der Vorteil des Interfaces BorderModel ist, dass es völlig egal ist, wie die Implementierung aussieht. Du könntest auch ein BorderModel implementieren, das einfach berechnet, in welcher Zelle welcher Rahmen verwendet werden soll. Hinzu kommt, dass die Implementierung keine separate Klasse sein muss.
Allerdings bräuchte ich da glaube ich tatsächlich nochmal eine Nachhilfe um es wirklich zu verstehen mit den einzelnen Klassen und dem Berechnen durch das BorderModel. Würde mich schon interessieren.
Ich habe es jetzt über die einfache Berechnung der Leerzellen und durchlaufen der Zeilen geschafft.
Vielen Dank
Java:
    public void rahmenLinienSetzen(int spaltennr){
    int spaltennummer =spaltennr-1;   
    int zeilenbeginn = (int)Math.pow(2, (spaltennummer)); //In dieser Zeile steht die erste Mannschaft
    int leerzeilen= zeilenbeginn*2-1;  //Leerzellen zwischen zwei Teams
    int spaltenende= needteams/spaltennummer+leerzeilen*((needteams/spaltennummer)-1);
    int lastrow = needteams*2;
    System.out.println("Zeilenbegin = "+zeilenbeginn+" leerzeilen = "+leerzeilen+" Spaltenende = "+spaltenende);
    
    //Rahmen unten setzen
    int a=zeilenbeginn-1;
    while(a<lastrow){
    borders.setBorder(a, spaltennummer, new MatteBorder(0,0,2,0,black));
    a=a+2*(leerzeilen+1);
    }
    //Rahmen unten&rechts
    int b=zeilenbeginn+leerzeilen;
    while(b<lastrow){
    borders.setBorder(b, spaltennummer, new MatteBorder(0,0,2,2,black));
    b=b+2*(leerzeilen+1);
    }
    //Rahmen rechts
    int c = zeilenbeginn;
    int counter=1;
    while(c<spaltenende){
        borders.setBorder(c, spaltennummer, new MatteBorder(0,0,0,2,black));
        counter++;
        if (counter>leerzeilen){
            c=c+leerzeilen+3;
            counter=1;
        }
        else{c=c+1;}
            
        }
    
    }
 

Anhänge

  • KoBaum_fertig.png
    KoBaum_fertig.png
    11,2 KB · Aufrufe: 7
mihe7

mihe7

Top Contributor
Allerdings bräuchte ich da glaube ich tatsächlich nochmal eine Nachhilfe um es wirklich zu verstehen mit den einzelnen Klassen und dem Berechnen durch das BorderModel. Würde mich schon interessieren.
Das ist ganz einfach, Du brauchst lediglich ein Objekt, das BorderModel implementiert. Das kannst Du erstellen, indem Du eine benannten Klasse schreibst
Java:
public class MyBorderModel implements BorderModel {
    @Override
    public Border getBorder(int row, int col) {
        // hier kanst Du entscheiden und zurückgeben, welchen Border die Zelle (row,col) erhalten soll.
    }
}
und ein Objekt davon erzeugst, oder eine anonyme Klasse verwendest
Java:
BorderModel borders = new BorderModel() {
    @Override
    public Border getBorder(int row, int col) {
        // hier kanst Du entscheiden und zurückgeben, welchen Border die Zelle (row,col) erhalten soll.
    }
};
Und da BorderModel nur eine Methode enthält, ginge auch ein Lamda-Ausdruck.
 
HoT

HoT

Mitglied
Also ich erstelle eine Klasse, die BorderModel implementiert und erzeuge dann ein Objekt davon um Zugriff auf die Methoden von BorderModel zu haben. ?
Aber warum ist dann in deinem letzten Post borders ein Objekt von BorderModels und nicht von MyBorderModel?
 
mihe7

mihe7

Top Contributor
Aber warum ist dann in deinem letzten Post borders ein Objekt von BorderModels und nicht von MyBorderModel?
Das ist eine sog. anonyme innere Klasse (als Alternative zur benannten Klasse in einer extra Datei). Kennst Du vielleicht von ActionListener oder ähnlichem.

EDIT: Wenn Du die separate Klasse verwenden wolltest, würdest Du natürlich ein Objekt von MyBorderModel erzeugen (new MyBorderModel()).
 
HoT

HoT

Mitglied
Ah OK jetzt wird es langsam klarer. Es waren zwei mögliche Codes. Der zweite war die anonyme innere Klasse. Vielen Dank für deine Erklärungen.
 
mihe7

mihe7

Top Contributor
Genau. Hier mal ein Beispiel:
Java:
import java.awt.Color;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.table.*;

public class Test {
    private static final Border BORDER_NONE = BorderFactory.createEmptyBorder();
    private static final Border BORDER_FIRST = BorderFactory.createMatteBorder(
            0, 0, 2, 0, Color.BLACK);
    private static final Border BORDER_NEXT = BorderFactory.createMatteBorder(
            0, 0, 0, 2, Color.BLACK);
    private static final Border BORDER_LAST = BorderFactory.createMatteBorder(
            0, 0, 2, 2, Color.BLACK);

    public void run() {
        final JTable table = new JTable(31, 5);

        BorderModel borders = new BorderModel() {
            public Border getBorder(int row, int col) {
                int distance = (int) Math.pow(2, col+1);
                int offset = distance/2 - 1;
                int current = row - offset;
                int sectionStart = current - current % (2*distance);
                int sectionEnd = sectionStart + distance;
                int last = table.getModel().getRowCount() - offset;

                if (current == sectionStart) {
                    return BORDER_FIRST;
                } else if (current == sectionEnd) {
                    return BORDER_LAST;
                } else if (sectionEnd < last && current > sectionStart &&
                        current < sectionEnd) {
                    return BORDER_NEXT;
                }
                return BORDER_NONE;
            }
        };

        BorderRenderer renderer = new BorderRenderer(
                new DefaultTableCellRenderer(), borders);

        TableColumnModel columns = table.getColumnModel();
        for (int i = 0, n = columns.getColumnCount(); i < n; i++) {
            columns.getColumn(i).setCellRenderer(renderer);
        }

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(new JScrollPane(table));
        frame.setSize(800, 600);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Test().run());
    }
}
Nicht schön, aber selten :) Wie du siehst, kann man in der inneren anonymen Klasse durchaus auf die Variablen außerhalb zugreifen, hier auf table.
 
HoT

HoT

Mitglied
Ah ok.
Vielen Dank. Man lernt ja glücklicherweise, durch solche Hilfen immer dazu.
Das ist natürlich jetzt schon sehr elegant. Es war aber auch mein erstes Projekt.
Hier ist alles in einer Klasse außer dem Border interface, oder brauche ich das nicht weil der Border static final definiert wurde?
Zwei Frage noch. Warum sind die Border in Zeile 7-12 static final?
Weil sie fest definiert sind und keine Variablen darstellen, oder?
Und diese Klasse kann ich dann überall aufrufen als Objekt in der passenden Klasse mit:
Test rahmen=new Test;
rahmen.run();
Die innerer anonyme Klasse wäre hier die Zeilen 19-36, oder?
Zeile 55 ist dann die Lambdafunktion, die von der main hier selbst aufgerufen wird. Weil ja hier auch eine leere Tabelle mit 32 Zeilen und 6 Spalten erzeugt wird.
Vielen Dank
 
mihe7

mihe7

Top Contributor
Hier ist alles in einer Klasse außer dem Border interface, oder brauche ich das nicht weil der Border static final definiert wurde?
BorderRenderer und BorderModel sind separat. Ich habe hier nur ein Beispiel zusammengebaut, damit Du mal die innere anonyme Klasse "in Action" siehst. Das static final wird zur Definition von Konstanten verwendet.

Zwei Frage noch. Warum sind die Border in Zeile 7-12 static final?
Weil sie fest definiert sind und keine Variablen darstellen, oder?
Exakt.

Und diese Klasse kann ich dann überall aufrufen als Objekt in der passenden Klasse mit:
Test rahmen=new Test;
rahmen.run();
Könntest Du, ja, das ist aber hier nicht "Sinn der Übung". Die Klasse heißt deswegen Test, weil es eine Wegwerfklasse ist, die ich nur zum Testen verwende :)

Die innerer anonyme Klasse wäre hier die Zeilen 19-36, oder?
Ja, 18-37, um genau zu sein. BorderModel ist ein Interface, von dem man keine Instanzen erzeugen kann. Ein [ICODE]new BorderModel();[/icode] wäre ein Fehler. Instanzen kann man nur von Implementierungen erzeugen, die in Form einer Klasse vorliegen. Statt explizit eine benannte Klasse zu schreiben (class X implements BorderModel { ... }) wird hier eine anonyme innere Klasse erzeugt (new BorderInterface() { /* Implementierung */ };).

Zeile 55 ist dann die Lambdafunktion, die von der main hier selbst aufgerufen wird.
Das () -> ... ist die Lambdafunktion, was im übrigen nichts anderes als eine anonyme Funktion ist (wie Du siehst, ist z. B. bei x -> 2*x kein Funktionsname angegeben).
 
HoT

HoT

Mitglied
Ja ok super, ich habe mir die Test Klasse nochmal extra mit dem Interface BorderModel und dem BorderRenderer angelegt und es hat funktioniert.
Vielen Dank für deine Erklärungen, die haben mir echt viel geholfen, vor allem fürs Verständnis.
Instanzen kann man nur von Implementierungen erzeugen, die in Form einer Klasse vorliegen. Statt explizit eine benannte Klasse zu schreiben (class X implements BorderModel { ... }) wird hier eine anonyme innere Klasse erzeugt (new BorderInterface() { /* Implementierung */ };).
Das war mir so auch noch nicht klar. Ich sehe schon, da habe ich noch sehr sehr viel zu lernen.
Von Lambdafunktionen habe ich schon gelesen, aber mich noch nicht herangetraut.
Vielen Dank für deine Zeit.
 
HoT

HoT

Mitglied
Sorry doch noch eine Frage oder Erklärung.
Das
interface BorderModel {
Border getBorder(int row, int col);
}
wird nur angelegt, damit ich später die einzelnen Zellen über Zeile und Spalte ansprechen kann, oder?
Denn dies wird ja in die Klasse SparseBorderModel implementiert, wo ich die Rahmen der Zellen vordefiniere.
 
mihe7

mihe7

Top Contributor
Das Interface dient zum Entkoppeln, damit der BorderRenderer nicht von einer konkreten Implementierung abhängig ist. Übersetzt sagt der BorderRenderer: es ist mir völlig egal, mit welcher Klasse ich zusammenarbeiten muss, das einzige, was mich interessiert ist, dass das Objekt über eine BorderModel-Schnittstelle verfügt.

Das kannst Du vergleichen mit der Fernbedienung: Du musst nicht wissen, wie Dein Fernseher intern funktioniert, ob da Pin 123 mit Pin 432 kurzgeschlossen wird, damit das Teil anspringt usw. Du brauchst nur zu wissen, dass der rote Knopf auf der Fernbedienung das Teil ein und ausschaltet.
 
HoT

HoT

Mitglied
Sorry, ich will nicht nerven, aber ich möchte sicher gehen, dass ich es verstanden habe.
Das heißt also die Testklasse ruft den Renderer über das Interface BorderModel auf, welches wiederum die ganzen Bordermethoden enthält wie BorderFactory.createEmptyBorder() oder createMatteBorder(), da diese über javax.swing.border.Border importiert werden. (Ich könnte den Renderer aber über jede andere Klasse aufrufen, da der Renderer durch das Interface entkoppelt ist.
Das BorderModel ermöglicht die Zelle (row, column) auszulesen und an den Renderer weiterzugeben. Dieser kann dann an dieser Stelle über JComponent setBorder den DefaultBorder überschreiben.
(Aber könnte ich das Interface auch weglassen und den BorderRenderer direkt in die Testklasse implementieren?)
In der anonymen Border Klasse berechnest du von Zeile 20 bis 25 jeweils in welcher Zelle die erstgenannte Mannschaft und die zweite steht bzw. dass dazwischen der rechte Rahmen angebracht werden soll und machst dann in der if-Abfrage den Zellen klar, welchen in Zeile 7-13 definierten Rahmen sie bekommen.
Danke
 

Anhänge

  • Interface.png
    Interface.png
    14,7 KB · Aufrufe: 8
mihe7

mihe7

Top Contributor
Das heißt also die Testklasse ruft den Renderer über das Interface BorderModel auf
Nein :)

Die Testklasse erzeugt nur die Objekte für das User Interface, fügt sie richtig zusammen und sorgt dafür, dass das Fenster auch angezeigt wird.

Also nochmal, was hat es mit dem Interface BorderModel auf sich? Oder allgemein: wofür ein Interface? Das sind tatsächlich Grundlagen, die man hier im Forum schlecht behandeln kann. Ich probier es mal trotzdem.

Zurück auf 0. Man hätte Dein Problem in einer einzigen Klasse lösen können. Diese Klasse wäre dann für drei Dinge verantwortlich gewesen:
  1. Komponente erzeugen und konfigurieren
  2. Berechnen, welche Zelle welchen Border bekommt
  3. Komponente mit Border ausstatten
So etwas will man nicht, denn dadurch hat man starke Abhängigkeiten, die den Code starr werden lassen und aufblähen. Der BorderRenderer sollte sich nur um den letzten Punkt kümmern. Das ist seine Aufgabe, nicht mehr, nicht weniger.

Den ersten Punkt kann im konkreten Fall einfach ein anderer TableCellRenderer übernehmen, das ist im Code das delegate. Der BorderRenderer delegiert diese Aufgabe also einfach an ein anderes Objekt.

Ähnlich verfahren wir auch mit Punkt 2: wir packen den Code für die Berechnung in eine eigene Klasse, nennen wir sie mal TournamentLinesModel. Im BorderRenderer hätten wir dann eine Variable

Java:
    private TournamentLinesModel borders;

Und jetzt stellt sich die Frage: warum muss es denn ausgerechnet ein TournamentLinesModel sein? Man könnte doch z. B. auch ein SparseBorderModel haben wollen. Oder vielleicht will man jede 2. Zeile mit einer grünen Linie versehen. Oder, oder, oder....

Tatsächlich spielt die konkrete Implementierung für den BorderRenderer keine Rolle, so lange alle Typen eines gemeinsam haben: sie müssen eine öffentliche Methode anbieten, über die der BorderRenderer den Border für eine gegebene Zelle ermitteln kann. Genau das ist die Schnittstelle zwischen dem BorderRenderer und allen Typen, die den Border für eine Zelle zur Verfügung stellen können. Und genau das drücken wir im Code aus: wir haben eine Schnittstelle BorderModel. BorderRenderer benötigt lediglich ein Objekt, das diese Schnittstelle anbietet und z. B. SparseBorderModel implementiert diese Schnittstelle.

Das ist wie die 3,5 mm-Klinke am Kopfhöreranschluss: interessiert es Dein Laptop, ob Du dort ein Headset oder einen Lautsprecher anschließt und ob dieser einen Verstärker eingebaut hat? Nein. So lange das Gerät der Spezifikation der Schnittstelle genügt, kannst Du eine x-beliebige "Implementierung" dranhängen. Das Laptop kennt diese Implementierung nicht. Die Implementierung (z. B. Dein Kopfhörer) kennt das Laptop nicht. Beide kennen nur die Schnittstelle, die die Geräte entkoppelt. Und genauso entkoppelt im Code ein Interface eben Implementierungen.

Damit hast Du größtmögliche Flexibilität und Wiederverwendbarkeit: Du kannst bestehenden Code wiederverwenden, um mit kleinstmöglichem Aufwand neue Anforderungen zu erfüllen. Wie beim Kopfhöreranschluss.
 
HoT

HoT

Mitglied
Ok, jetzt wird mir auch der Sinn vom Interface klar.
Ich werde da wohl nochmal genauer nachlesen. Das war mir noch nie so richtig klar auch der Unterschied und die Anwendung bzw. der Nutzen zu abstrakten Klassen.
Werde mich da nochmal besser reinarbeiten müssen.
Vielen Dank für deine Hilfe, deine Mühe und Gedult.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
B Swing Einzelne Zelle in JTable soll NICHT auswählbar sein AWT, Swing, JavaFX & SWT 9
T einzelne Zelle einer JTable auswählen und Hintergrundfarbe ändern AWT, Swing, JavaFX & SWT 2
J JTable: Einzelne Zelle selektieren AWT, Swing, JavaFX & SWT 6
S JavaFX TableView einzelne Zelle Layout zuweisen AWT, Swing, JavaFX & SWT 3
J JavaFX TabPane / Wie füge ich einzelne items ? AWT, Swing, JavaFX & SWT 1
VfL_Freak Swing Einzelne Zeile in jTable selektieren klappt nicht AWT, Swing, JavaFX & SWT 7
T AWT Grafik"Array" einzelne Elemente verschieben AWT, Swing, JavaFX & SWT 1
S Swing JTable - Einzelne Rows einfärben AWT, Swing, JavaFX & SWT 11
L Swing CellRenderer für einzelne Zellen? AWT, Swing, JavaFX & SWT 5
N jFreeChart einzelne Punkte anwählen und Formatieren AWT, Swing, JavaFX & SWT 0
M JavaFX TextArea einzelne Buchstaben färben AWT, Swing, JavaFX & SWT 3
T Swing Einzelne Teile eines Textes färben? AWT, Swing, JavaFX & SWT 10
M Einzelne Ordner im JFileChooser ausgrauen? AWT, Swing, JavaFX & SWT 4
D Swing Mit TableCellRenderer einzelne Zellen markieren AWT, Swing, JavaFX & SWT 3
R einzelne Zeichen in jTable einlesen AWT, Swing, JavaFX & SWT 10
M Swing Einzelne Fenster schließen AWT, Swing, JavaFX & SWT 2
V JTable: Zugriff auf einzelne Zellen AWT, Swing, JavaFX & SWT 4
K Swing CellEditor für einzelne Zellen AWT, Swing, JavaFX & SWT 2
K Swing JTable Editierfunktion für einzelne Zellen ausschalten AWT, Swing, JavaFX & SWT 3
S Einzelne JComboBox in JTable austauschen AWT, Swing, JavaFX & SWT 4
F Einzelne TreeNode an einem JTree editierbar machen AWT, Swing, JavaFX & SWT 3
C JTree Einzelne Objekte färben AWT, Swing, JavaFX & SWT 4
C Spielfeld Gitter - Einzelne Zellen verändern AWT, Swing, JavaFX & SWT 18
J CellRenderer einzelne zellen Hintergrund AWT, Swing, JavaFX & SWT 9
M JTable einzelne Zeilen färben AWT, Swing, JavaFX & SWT 2
A Swing JTree Editable einzelne Nodes editierbar machen! AWT, Swing, JavaFX & SWT 2
T JTable - Einzelne Spalte nicht sortieren? AWT, Swing, JavaFX & SWT 2
R JTable einzelne Zellen selektieren AWT, Swing, JavaFX & SWT 7
H Einzelne Pixel auf Transparenz überprüfen? AWT, Swing, JavaFX & SWT 3
X Einzelne Border setzen AWT, Swing, JavaFX & SWT 2
F png-Datei erstellen und einzelne Pixel schreiben AWT, Swing, JavaFX & SWT 2
J MVC- einzelne Sichten auf einer Hauptsicht anzeigen AWT, Swing, JavaFX & SWT 3
S Einzelne Frame Fenster schließen AWT, Swing, JavaFX & SWT 2
S Einzelne Frame Fenster schließen AWT, Swing, JavaFX & SWT 2
P JTextArea - einzelne Zeile löschen bzw einfügn AWT, Swing, JavaFX & SWT 2
G Einzelne Objekte neu zeichnen AWT, Swing, JavaFX & SWT 8
B einzelne Zellen scrollbar machen innerhalb einer Tabelle AWT, Swing, JavaFX & SWT 3
E JTable einzelne reihe editierbar machen AWT, Swing, JavaFX & SWT 2
I einzelne Zellen bei JTable markieren AWT, Swing, JavaFX & SWT 3
C JTable: Einzelne Spalten nicht editierbar machen AWT, Swing, JavaFX & SWT 2
P JavaFX aktuelle Tabellenzeile bei Choice-Box-Auswahl in Zelle ermitteln AWT, Swing, JavaFX & SWT 28
T Zelle- und die Zeile-Farbenwechsel bei der Selektion in der Tabelle AWT, Swing, JavaFX & SWT 4
P JavaFX TableView Zelle markieren AWT, Swing, JavaFX & SWT 3
W Swing JTable Zeilenumbruch innerhalb einer Zelle AWT, Swing, JavaFX & SWT 3
M Swing Automatischer Editorstart in JTable-Zelle AWT, Swing, JavaFX & SWT 5
M Wert einer Zelle aus JTable ziehen AWT, Swing, JavaFX & SWT 4
P Fokus auf Zelle in JTable AWT, Swing, JavaFX & SWT 1
LexeB4F Zelle in JTable gezielt einfärben AWT, Swing, JavaFX & SWT 4
LexeB4F JTable mehrere Zelle selektieren und inhalte Löschen.. Ideen gesucht AWT, Swing, JavaFX & SWT 1
K Swing JTable mit ImageIcon und Text in einer Zelle AWT, Swing, JavaFX & SWT 1
E JavaFX TableView mit Rechtsklick Zelle auswählen AWT, Swing, JavaFX & SWT 8
E JTable Hintergrund von geklickter Zelle ändern AWT, Swing, JavaFX & SWT 2
J CellFactory in Abhängigkeit von anderer Zelle AWT, Swing, JavaFX & SWT 4
M JTable: Prüfen ob Zelle leer ist AWT, Swing, JavaFX & SWT 5
O Swing JTable - Zelle fokussieren AWT, Swing, JavaFX & SWT 10
L JTable selektierte Zelle in Bearbeitungs Modus AWT, Swing, JavaFX & SWT 2
H Combobox in einer einzelnen Zelle der Jtable AWT, Swing, JavaFX & SWT 2
R Swing Hintergrundfarbe der Zelle in einer JTable bei Markierung AWT, Swing, JavaFX & SWT 2
M Jtable Objecte aus Zelle AWT, Swing, JavaFX & SWT 13
M Beim Start von TableEditor Inhalt der Zelle markieren. AWT, Swing, JavaFX & SWT 2
S Swing Zelle einer JTable hervorheben (ohne CellRenderer) AWT, Swing, JavaFX & SWT 3
A JTable Zelle markieren von bis und Löschen AWT, Swing, JavaFX & SWT 6
F JTable soll mit Buttons befüllt werden, aber nicht in jede Zelle einer Spalte AWT, Swing, JavaFX & SWT 3
T DefaultTableCellRenderer Farbe bestimmter Zelle ändern AWT, Swing, JavaFX & SWT 2
GianaSisters Swing ComboBox im jTable (in bestimmter Zelle nicht über die ganze Spalte) AWT, Swing, JavaFX & SWT 4
O JButton in Zelle von JTable AWT, Swing, JavaFX & SWT 4
K Tabellen Inhalt ( Zelle ) Modifizieren AWT, Swing, JavaFX & SWT 2
J Zelle eine JTable ändern AWT, Swing, JavaFX & SWT 3
R Drag an Drop JTable Zelle AWT, Swing, JavaFX & SWT 6
T JTable, Popup Fester per Rechtsklick auf Zelle AWT, Swing, JavaFX & SWT 19
H variable Höhe einer JTextArea (als Renderer/Editor) als Zelle einer JTable AWT, Swing, JavaFX & SWT 9
C Swing JTable zeigt in jeder Zelle einer Reihe denselben Inhalt AWT, Swing, JavaFX & SWT 2
lenniii Zelle in JTable ändern AWT, Swing, JavaFX & SWT 2
J Swing JTable, jede Zelle ein anderer Title im TitledBorder AWT, Swing, JavaFX & SWT 5
D JTables, Test highlighten innerhalb einer Zelle AWT, Swing, JavaFX & SWT 5
S Swing JTable nach einer Änderung einer Zelle komplett aktualisieren. AWT, Swing, JavaFX & SWT 10
N Methode zum ermitteln der editierte Zeile/Zelle in jTable AWT, Swing, JavaFX & SWT 8
C Swing JTable Zelle mit Einfachklick editieren AWT, Swing, JavaFX & SWT 3
C JTable: Mehrere Farben in einer Zelle darstellen AWT, Swing, JavaFX & SWT 10
D Swing Farbige Zelle ohne DefaultTableCellRenderer AWT, Swing, JavaFX & SWT 17
D Swing Farbige Zelle ohne DefaultTableCellRenderer AWT, Swing, JavaFX & SWT 4
T Swing JTable - true zurückbekommen, wenn der Cursor in einer Zelle blinkt AWT, Swing, JavaFX & SWT 7
C JTable markierte Zelle wird bei removeRow nicht mit gelöscht AWT, Swing, JavaFX & SWT 6
Dit_ Verschiedene Icons in einer JTable-Zelle AWT, Swing, JavaFX & SWT 2
A JTable Zelle Padding AWT, Swing, JavaFX & SWT 9
R JTable Hintergrund der Zeile abhängig von Inhalt einer Zelle verändern AWT, Swing, JavaFX & SWT 3
S Swing DnD: Wie finde ich heraus, von welcher Zelle aus im JTable der Drag gestartet wurde? AWT, Swing, JavaFX & SWT 3
U LayoutManager [GBL] Component aus einer Zelle löschen AWT, Swing, JavaFX & SWT 2
E Zelle in JTable nach klick markieren AWT, Swing, JavaFX & SWT 2
O JTable Zelle mit "ENTER" in Editmode setzen AWT, Swing, JavaFX & SWT 4
eskimo328 JTable nächste Zelle nach Eingabe einer Ziffer AWT, Swing, JavaFX & SWT 5
A Swing JTable Icon nur in einer Zelle AWT, Swing, JavaFX & SWT 2
B Swing JTable merkt sich die zuletzt selektierte Zelle AWT, Swing, JavaFX & SWT 3
M JTable - verlassen einer Zelle AWT, Swing, JavaFX & SWT 15
R JTable Darstellen der selektierten Zelle braucht lange AWT, Swing, JavaFX & SWT 7
S JTable Probleme wenn ich Zelle verlasse AWT, Swing, JavaFX & SWT 4
P Für den Inhalt einer Zelle (aus Tabelle) STRG-C anwenden AWT, Swing, JavaFX & SWT 2
S Welches Event beim ändern einer Zelle in einer JTable AWT, Swing, JavaFX & SWT 4
S Welches Event beim auswählen einer Zelle? [JTable] AWT, Swing, JavaFX & SWT 3
A Multiple Zeilen in einer JTable Zelle AWT, Swing, JavaFX & SWT 6

Ähnliche Java Themen

Anzeige


Oben