JTable, kolorowe tło w komórkach

0

Temat podobny był tutaj:
http://4programmers.net/Forum/Java/113713-JTable_kolorowy_tekst_w_komorkach?p=777984#id777984
ale jak w nim napisałem to nie zrobiło go wyżej, więc nikt by nie zauważyć (rocznik wątku: 2007, to już całkiem niezłe wino by było).

Moje uprzejme zapytanie do bazy danych Waszych mózgów:
W linku jest automatycznie formatowana komórka, ale ja chciałbym coś takiego:

jTable1.getCellRender(row, col)/*to jest*/.setBakground(Color c);

Otóż getCellRender zwraca TableCellRenderer, który jest klasą abstrakcyjną, ale tam nie ma metody setBackgound. Jest ona w DefaultTableCellRenderer.

Prubuję coś na ten sposób (wszystkich sposobów, którymi próbowałem się do tego dostać nie jestem w stanie wylistować):

DefaultTableCellRenderer celRenderer = (DefaultTableCellRenderer) myJXTable1.getCellRenderer(selectedRowId, 1);

Ale wyrzuca exceptiona, że nie może rzutować.

Settera na renderer komórki nie ma:-( Na kolumny jest, ale nie na komórki:-(

Dziękuję,
A.

P.S. To jest niefajne, że nie ma settera.

0

Ustaw renderera dla kolumny. Wewnątrz renderera sposób rysowania uzależniaj od wiersza.

0

Tylko, że nie chcę dla wszystkich wierszy tego zmieniać, ani z żadnego automatu po wartościach. Chcę zmieniać to już po narysowaniu komponentu. Ta kolumna akurat ma mieć wszystkie wartości "". Kolor jej komórek ma być informację dla usera, która będzie się też zmieniać za przyczyną jego działań.

1

Dodaj do modelu kolumnę duch, której nie wyświetlasz, wartości w tej kolumnie zależą od działań użytkownika. Kolor wyświetlany w kolumnie Cię interesującej zależy od tego co jest w kolumnie duch.

0

Ciekawy pomysł:-) Natchnął mnie.
Zrobię sobie Listę, która będzie przechowywać te dane. Moja tabela to rozszerzenie JXTable, więc nie muszę wiele zmieniać. I tak muszę takie coś zrobić, chciałem to tylko zapamiętywać poziom w hierarchii wyżej.

0

Nie wiem jak to jest w C#, ale działania użytkownika chcesz chyba zapamiętać. Pamiętanie ich przez typ renderera związanego z komórką jest, imo, dziwaczne. Jak nie chcesz modyfikować modelu, to możesz stworzyć dodatkową kolekcję (np. ArrayList) i w niej przechowywać wybory użytkownika. Jeżeli dopuszczasz zmianę uporządkowania na ekranie, to przy synchronizacji modelu z dodatkową kolekcja pamiętaj o użyciu metody convertRowIndexToModel.

0

Przecież JTable getCellRender() zwraca obiekt implementujący interfejs TableCellRenderer, a ten w swojej jedynej metodzie TableCellRenderer.getTableCellRendererComponent() zwraca obiekt klasy Component. Component ma już metodę setBackground(Color c).
Problem w tym, że ponieważ renderer jest jeden na całą tablicę, to trzeba by wywoływać tę metodę co jedno odrysowanie komórki przez JTable biorąc pod uwagę jak JTable renderuje swoje komórki (DefaultTableCellRenderer, Implementation Note). Zawsze jednak można użyć metody prepareRenderer() i ustawić dla wybranych lub wszystkich komórek takiego renderatora, który będzie uwzględniał malowanie tła innym kolorem. Można użyć do tego wcześniej wyciągnięty obiekt DefaultTableCellRenderer i zmienić mu po prostu malowanie tła za pomoca setBackground(). Właśnie po to są różne defaultowe klasy obiektów. Nie jestem tylko pewien czy przy wywołaniach dla różnych komórek można użyć tego samego obiektu po modyfikacji dla każdej komórki czy jednak koniecznie całe zmodyfikowane kopie.

0
bo napisał(a)

Jak nie chcesz modyfikować modelu, to możesz stworzyć dodatkową kolekcję (np. ArrayList) i w niej przechowywać wybory użytkownika.

Właśnie tak zrobiłem. Ale tu nie chodzi o to, tylko o rysowanie na podstawie tych danych (ArrayList, u mnie Map<Integer, Stack<Integer>>).

Olamagato napisał(a)

Przecież JTable getCellRender() zwraca obiekt implementujący interfejs TableCellRenderer,(...)

Właśnie tak zrobiłem, tylko nie działa dla pojedynczych komórek. Oto kod:

public class BackgroundCellRenderer extends DefaultTableCellRenderer {
    private Color fDarkGreen;
    public BackgroundCellRenderer() {super();}
 
    @Override
    public Component getTableCellRendererComponent(
    JTable aTable, Object aValue, boolean aIsSelected, 
    boolean aHasFocus, int aRow, int aColumn) {  
        Component renderer = super.getTableCellRendererComponent(aTable, aValue, aIsSelected, 
                                                                        aHasFocus, aRow, aColumn);
    
    MyJXTable table = (MyJXTable)aTable;
    
    if (table.isSelectedMy(aColumn, aRow)) renderer.setBackground(Color.ORANGE);
  
    return this;
  }
}

isSelectedMy(aColumn, aRow) sprawdza czy komórka jest na liście, jeżeli tak, ma ją pokolorować.

Oto użycie:

// dodanie do listy komórki.
myJXTable1.addSelectedMy(0, 1);

//  tutaj nadaje nowego rendera
myJXTable1.getColumn(0).setCellRenderer(new BackgroundCellRenderer());

No i mi wyświetla wszystkie komórki na pomarańczowo, a nie pojedyncze
Nie wiem co źle robię:/

0

Jeśli chcesz zmienić kolor tła JLabela, to musisz wywołać metodę setOpaque(true);

    if (table.isSelectedMy(aColumn, aRow)) 
    {
        renderer.setOpaque(true);    
        renderer.setBackground(Color.ORANGE);
    }
0
bo napisał(a)

Jeśli chcesz zmienić kolor tła JLabela, to musisz wywołać metodę setOpaque(true);

Kolor się zmienia poprawnie, ale nie dla jednej tylko wszystkich komórek w kolumnie:/
Nie potrafię tego rozgryźć. Przeglądam pełno przykładów na google i nic, robi mi się dla wszystkich.

0

Tak jak napisałem, użyj
http://download.oracle.com/javase/7/docs/api/javax/swing/JTable.html#prepareRenderer%28javax.swing.table.TableCellRenderer,%20int,%20int%29

JTable to jeden z najbardziej skomplikowanych obiektów Swinga i można zrobić z nim rzeczy, o których Tobie (oraz mi również) się nie śniło.
Metody TableColumn.setCellRenderer() używa się do czego innego niż ty potrzebujesz. Czasem nawet bardzo uważne przeczytanie dokumentacji jednej klasy nie wystarcza aby zrozumieć wszystkie zależności w tej klasie.
Dla każdej kolumny tworzysz osobny obiekt renderatora, ale przecież nie masz pojęcia w którym momencie zostanie gdzieś w obiekcie JTable wywołana metoda getTableCellRendererComponent(), a tym samym czy użycie w takiej metodzie Twojej specyficznej metody isSelectedMy() ma w ogóle jakikolwiek sens.
Pomijam już to, że zakładanie a priori iż argument aTable jest na pewno Twoją MyJXTable też jest dość ryzykowne bo nie masz pojęcia co się dzieje wewnątrz nieznanego sobie obiektu, niektóre klasy zwracają różne wrappery i inne wynalazki implementujące ten sam interfejs, ale klasy zupełnie innego typu.
Generalnie wyłącz domniemania i uważnie doczytaj dokumentację. W razie potrzeby wrzuć sobie źródła JTable do swojego IDE i prześledź sobie co się dzieje z obiektami renderującymi.

Weź pod uwagę notę implementacyjną do DefaultTableCellRenderer:
"JTable employs a unique mechanism for rendering its cells and therefore requires some slightly modified behavior from its cell renderer. The table class defines a single cell renderer and uses it as a as a rubber-stamp for rendering all cells in the table; it renders the first cell, changes the contents of that cell renderer, shifts the origin to the new location, re-draws it, and so on."

oraz to:
"The standard JLabel component was not designed to be used this way and we want to avoid triggering a revalidate each time the cell is drawn. This would greatly decrease performance because the revalidate message would be passed up the hierarchy of the container to determine whether any other components would be affected. As the renderer is only parented for the lifetime of a painting operation we similarly want to avoid the overhead associated with walking the hierarchy for painting operations. So this class overrides the validate, invalidate, revalidate, repaint, and firePropertyChange methods to be no-ops and override the isOpaque method solely to improve performance. If you write your own renderer, please keep this performance consideration in mind. "

Jak dla mnie to jest to dość dobrze wyjaśnione.

0

No to trudno. Już nie mogę dłużej siedzieć przy takiej rzeczy. Muszę się poddać, trudno. Zrobię to przy zmianie wartości widocznej kolumny. spacja jest niewidoczna. Jej użyję, chciałem uniknąć tego typu rozwiązania.
To paranoja, żeby tak oczywistej rzeczy nie móc zrobić w jakiś przystępny sposób. No, ale nie ma rzeczy idealnych:-)

0

Kolor tła ma od czegoś zależeć. Możesz to coś przechowywać w modelu, ale nie wyświetlać. Możesz przechowywać w innym obiekcie (ArrayList, List, HashMap, ...), wtedy musisz synchronizować inny obiekt z modelem. Masz przykład kolorowania tła, gdy informacja jest przechowywana w modelu.

//pole w modelu
private ArrayList<Planet> planets=new ArrayList<Planet>();
//fragment konstruktora modelu
        planets.add(new Planet("Merkury",2440.0,0,false,Color.YELLOW));
        planets.add(new Planet("Wenus",6052.0,0,false,Color.YELLOW));
        planets.add(new Planet("Ziemia",6378.0,1,false,Color.BLUE));
        planets.add(new Planet("Mars",3397.0,2,false,Color.RED));
        planets.add(new Planet("Jowisz",71492.0,16,true,Color.ORANGE));
        planets.add(new Planet("Saturn",60268.0,18,true,Color.ORANGE));
        planets.add(new Planet("Uran",25559.0,17,true,Color.BLUE));
        planets.add(new Planet("Neptun",24766.0,8,true,Color.BLUE));
//konstrukcja JTable
        JTable table=new JTable(model);
        TableColumn td=table.getColumn("Kolor");
        td.setCellRenderer(new CellRenderer());
//kod renderera
    public Component getTableCellRendererComponent(JTable t,Object value,boolean selected,boolean hasFocus,int row,int col)
    {
        JLabel header=new JLabel("");
        header.setOpaque(true);
        header.setBackground((Color)value);
        return header;
    }
0

to już uzyskałem pośrednio przez rozszerzenie JXTable o Map. Jeżeli komórki to mają być Stringi to nie wiem tylko jak zrobić, żeby za string robiła się jak w Twoim przykładzie Nazwa planety. Ale z tym nie mam już problemu. Ominąłem to inaczej, w TableDataModel nie chciałem ingerować bo zbyt często się zmienia i przypisywanie mu teraz na stałe kolumn wymusiłoby zmianę poprzedniego kodu.
Jestem przekonany, że można rysować tło indywidualnie. Jak to wynika z dokumentacji, trzeba ustawić "ręcznie" rendera na komórkę i wywołać rysowanie. Jednak musiałbym sięgać do źródeł JTable, a na to już czasu nie mam.
Szkoda, że to nie działa normalniej. Tzn. nie napisali paru metod więcej. Może się tym zajmę, bo spokoju mi to nie daje. To jest nie do zniesienia, że coś tak oczywistego wymaga takich kombinacji.
Podejrzewam, że do tego typu rzeczy nie stosuje się komponentów SWING. Może poszukam sobie akurat do tabeli, czegoś innego.

Dzięki za pomoc. Jednak jakby ktoś na coś wpadł to będę wdzięczny za skorbnięcie paru linijek:-)

0

Jak nie chcesz ingerować w model, to sam renderer może zawierać pole typu HashMap<Integer,Color>. Elementy tej Hashmapy, to pary (numer_wiersza,kolor) dla wszystkich wierszy lub tylko dla tych, które mają nietypowy kolor.

0

to nie z tym mam problem. Próbowałem czegoś podobnego. Problem jest w tym, że renderer zachowuje się tak jakby był jeden dla wszystkich komórek, ale wg. dokumentacji tak nie jest, co mnie strasznie dziwiło.
Dzisiaj robiłem coś podobnego z JCoboBox. Nie miałem żadnego problemu. A metoda bardzo podobna. Zajęło mi to chwilkę.

Albo byłem przedwczoraj zmęczony przez niewyspanie i popełniałem jakiś głupi błąd, albo to rzeczywiście działa inaczej. Nie mam czasu na razie tego sprawdzić, limit na to wyczerpał mi się. Ale pewnie do tego kiedyś wrócę.

1 użytkowników online, w tym zalogowanych: 0, gości: 1