JComboBox gruppera val
Här är en hyfast enkel och lite småful lösning som kanske duger:
import java.awt.Component;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.UIManager;
class GroupMarker
{
private final String text;
public GroupMarker(String text)
{
this.text = text;
}
@Override
public String toString()
{
return text;
}
}
class GroupComboRenderer
extends DefaultListCellRenderer
{
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)
{
JLabel lbl = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value instanceof GroupMarker) {
lbl.setText("<html><b><i>" + value.toString() + "</i></b></html>");
} else {
if (index == -1) {
// Index är -1 om det är det valda elementet "högst upp" i combon. Då vill vi inte ha indraget.
lbl.setText(value.toString());
} else {
lbl.setText(" " + value.toString());
}
}
return lbl;
}
}
public class GroupComboTest
extends JFrame
{
public GroupComboTest()
{
setDefaultCloseOperation(EXIT_ON_CLOSE);
Object[] comboData = {
new GroupMarker("Frukter"), "Äpple", "Päron", "Banan",
new GroupMarker("Grönsaker"), "Tomat", "Gurka", "Morot",
new GroupMarker("Skillade snubbar"), "Donald Knuth", "Brian Goetz", "Mark Russinovich"
};
JComboBox cb = new JComboBox(comboData);
cb.setRenderer(new GroupComboRenderer());
add(cb);
pack();
}
public static void main(String[] args) throws Exception
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new GroupComboTest().setVisible(true);
}
}
Principen är att vi skiljer på rena strängar i combo-listan och våra grupprubriker genom att göra grupprubrikerna till en annan klass. Vi sätter sedan en egen cellrenderare för combon, som i stort sett är densamma som defaulten en JComboBox får (en DefaultListCellRenderer), förutom att vi använder lite HTML för att styra upp utseendet på elementen och hjälpa oss skilja på grupprubrikerna och elementen.
För att veta om en användare har valt en grupprubrik eller inte kan du kolla om det valda elementet i combon har typen GroupMarker (genom instanceof GroupMarker). Skulle så vara fallet är valet felaktigt och du får styra upp det på det sätt du tycker är lämpligt (automatiskt välja ett giltigt alternativ uppåt eller neråt, eller strejka och spotta felmeddelanden i ansiktet på användaren).
EDIT: Den är inte lika snygg och bra som HTML-varianten du länkade, i och med att man här fortfarande kan välja grupprubrik-elementen. Det finns sätt runt det, men allt jag kommer på just nu är fulhack. Jag kanske återkommer om jag kommer på något snyggt.
Jag kom på ett sätt som är bara lite fulhackigt :), men som nog kan funka acceptabelt? Vi subklassar JComboBox som lyssnar efter händelsen att användaren väljer något - om vi inte gillar valet så återväljer vi det tidigare elementet som var valt.
Lösningen har sina begränsningar, men den kanske räcker för det du vill ha den till? Du får nog bryta ut GroupComboBox och GroupMarker till egna filer och göra dem publika för att kunna använda dem på ett smidigt sätt.
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.UIManager;
class GroupMarker
{
private final String text;
public GroupMarker(String text)
{
this.text = text;
}
public String getText()
{
return text;
}
@Override
public String toString()
{
return text;
}
}
class GroupComboBox
extends JComboBox
{
// Har renderarklassen som en privat medlemsklass, eftersom den är helt
// ointressant för andra än GroupComboBox.
private static class GroupComboRenderer
extends DefaultListCellRenderer
{
@Override
public Component getListCellRendererComponent(
JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus)
{
JLabel lbl = (JLabel) super.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus);
if (value instanceof GroupMarker) {
lbl = new JLabel(); // Fuling för att bli av med bakgrundsfärg etc
lbl.setText("<html><b><i>" + value.toString() + "</i></b></html>");
} else {
if (index == -1) {
// Index är -1 om det är det valda elementet "högst upp" i combon. Då vill vi inte ha indraget.
lbl.setText(value.toString());
} else {
lbl.setText(" " + value.toString());
}
}
return lbl;
}
}
private Object lastObject = null;
public GroupComboBox()
{
this(new Object[]{" "});
}
public GroupComboBox(Vector<?> data)
{
this(data.toArray());
}
public GroupComboBox(Object[] data)
{
super(data);
setRenderer(new GroupComboRenderer());
if (data != null && data.length > 0) {
lastObject = data[0];
}
// Se till att ingen GroupMarker är vald från början.
for (Object o : data) {
if (!(o instanceof GroupMarker)) {
lastObject = o;
break;
}
}
addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
Object o = getSelectedItem();
if (o instanceof GroupMarker) {
setSelectedItem(lastObject);
fireActionEvent();
} else {
lastObject = o;
}
}
});
}
}
public class GroupComboTest
extends JFrame
{
public GroupComboTest()
{
setDefaultCloseOperation(EXIT_ON_CLOSE);
Object[] comboData = {
new GroupMarker("Frukter"), "Äpple", "Päron", "Banan",
new GroupMarker("Grönsaker"), "Tomat", "Gurka", "Morot",
new GroupMarker("Skillade snubbar"), "Donald Knuth", "Brian Goetz", "Mark Russinovich"
};
GroupComboBox cb = new GroupComboBox(comboData);
add(cb);
pack();
}
public static void main(String[] args) throws Exception
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new GroupComboTest().setVisible(true);
}
}
AHhhhhhhh!!!!
Helt lysande. Tusen tack. Har googlat hela jefvla dagen men inte hittat nån lösning som jag kunde förstå.
Hur påverkar html-koden prestandan tro? Och är det vänligt över olika plattformar?
Ursprungligen inskrivet av stantemo
AHhhhhhhh!!!!
Helt lysande. Tusen tack. Har googlat hela jefvla dagen men inte hittat nån lösning som jag kunde förstå.
Hur påverkar html-koden prestandan tro? Och är det vänligt över olika plattformar?
Prestandan påverkas knappt alls. Du hade säkert märkt av det om du försökt rendera tusentals JLabels i sekunden, men det lär du aldrig vara i närheten av.
Det är inget här som är plattformsberoende heller. Dock bör man ha i åtanke att vissa look-and-feels har som standard att JLabels (som standardelementen i en JComboBox är) är fetstilta från början. T.ex. är standard-look-and-feel:en för Java (Java LAF även kallad Metal) gjord så. Detta skulle medföra att GroupMarker-elementen och de "riktiga" elementen, strängarna, alla skulle vara fetstilta. Och vad jag vet finns ingen html-tag som gör nånting "ofett"?
Man kan ju på olika sätt försöka tvinga comboboxar att alltid rendera "smalstilt", så att även Javas standard-LAF följer det. Trodde jag iaf... Jag bet i gräset när jag försökte på min sida här och vet inte riktigt vad jag gjort för fel. Jag kanske återkommer...
BTW, ändra for-loopen i konstruktorn till
// Se till att ingen GroupMarker är vald från början.
for (Object o : data) {
if (!(o instanceof GroupMarker)) {
lastObject = o;
setSelectedItem(o);
fireActionEvent();
break;
}
}
...så funkar den som den ska också.
Nu har jag en fråga till. I början av boxen har jag skapat en GroupMarker("") så att default är tom. problemet är att om jag från början klickar direkt på en annan groupmarker så väljs den. går detta att fixa på något sätt?
Jag hänger inte med.
1) Varför ha en GroupMarker("") alls? "så att default är tom" - menar du att hela comboboxen är tom som default? Isf behövs inga GroupMarkers alls.
2) "problemet är att om jag från början klickar direkt på en annan groupmarker så väljs den" - vilken väljs, den tomma eller den andra?
Ursprungligen inskrivet av badboll
Jag hänger inte med.
1) Varför ha en GroupMarker("") alls? "så att default är tom" - menar du att hela comboboxen är tom som default? Isf behövs inga GroupMarkers alls.
2) "problemet är att om jag från början klickar direkt på en annan groupmarker så väljs den" - vilken väljs, den tomma eller den andra?
hehe jag var ganska otydlig såg jag nu.
jo, jag har en box som från början är tom. den ska när programmet laddas fyllas med val (kategorier) som då ska hamna under rätt huvudrubrik.
typ så här:
Inkomster
-Lön
-Övrigt
Utgifter
-Hyra
-Garage
-Försäkring
Om exempelvis Inkomster inte har några kategorier i sig vill jag inte att denna grupp ska visas likaså om Utgifter inte har några kategorier.
När programmet laddas och det då finns kategorier att fylla boxen med vill jag att defaul ska vara att boxen inte är satt till något val utan att den visas som blank. Detta trodde jag att jag kunde lösa med att ha en GroupMarker(""). Problemet är egentligen att jag inte riktigt forstod hur ajg skulle använda din kod och blandade då in min gammla kod som fyller boxen. Detta kom jag på i morse innan jag stack till jobbet. ska kolla mer på det när jag kommer hem.
Jag får verkligen inte rätsida på problemet. Så här ser min kod ut.
//Comboboxen instansieras av NetBeans men jag har ändrat den så att den ser ut så här:
//jComboBoxCashBookCategory = new GroupComboBox();
private void setComboBoxCategories() {
List incomeCategories = categoryManager.getCategories(false);
List expenceCategories = categoryManager.getCategories(true);
DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
if (incomeCategories.size() == 0 && expenceCategories.size() == 0) {
comboBoxModel.addElement(new String("-"));
}
if (incomeCategories.size() > 0) {
comboBoxModel.addElement(new GroupMarker("Inkomster"));
for (int i=0; i<incomeCategories.size(); i++) {
Category category = (Category)incomeCategories.get(i);
comboBoxModel.addElement(new ComboBoxItem(category.getId(), category.getName()));
}
}
if (expenceCategories.size() > 0) {
comboBoxModel.addElement(new GroupMarker("Utgifter"));
for (int i=0; i<expenceCategories.size(); i++) {
Category category = (Category)expenceCategories.get(i);
comboBoxModel.addElement(new ComboBoxItem(category.getId(), category.getName()));
}
}
jComboBoxCashBookCategory.setModel(comboBoxModel);
}
jag har även testat detta, men då funkar det inte alls.
private void setComboBoxCategories() {
List incomeCategories = categoryManager.getCategories(false);
List expenceCategories = categoryManager.getCategories(true);
Vector v = new Vector();
if (incomeCategories.size() == 0 && expenceCategories.size() == 0) {
v.add(new String("-"));
}
if (incomeCategories.size() > 0) {
v.add(new GroupMarker("Inkomster"));
for (int i=0; i<incomeCategories.size(); i++) {
Category category = (Category)incomeCategories.get(i);
v.add(new ComboBoxItem(category.getId(), category.getName()));
}
}
if (expenceCategories.size() > 0) {
v.add(new GroupMarker("Utgifter"));
for (int i=0; i<expenceCategories.size(); i++) {
Category category = (Category)expenceCategories.get(i);
v.add(new ComboBoxItem(category.getId(), category.getName()));
}
}
jComboBoxCashBookCategory = new GroupComboBox(v);
}
Ursprungligen inskrivet av stantemo
hehe jag var ganska otydlig såg jag nu.
jo, jag har en box som från början är tom. den ska när programmet laddas fyllas med val (kategorier) som då ska hamna under rätt huvudrubrik.
typ så här:
Inkomster
-Lön
-Övrigt
Utgifter
-Hyra
-Garage
-Försäkring
Om exempelvis Inkomster inte har några kategorier i sig vill jag inte att denna grupp ska visas likaså om Utgifter inte har några kategorier.
När programmet laddas och det då finns kategorier att fylla boxen med vill jag att defaul ska vara att boxen inte är satt till något val utan att den visas som blank.
Jag har vissa svårigheter att förstå ditt problem. Menar du att även om du har en klase kategorier att välja bland, så ska ingen vara vald från början? Det får du i så fall lösa med att ha ett blankt dummyelement i combon, och ha det förvalt. Du måste då också vara medveten om att användaren kan välja det elementet, och du måste ta ställning till om användaren får välja det blanka elementet eller inte, och agera därefter där kontroll av det valda elementet görs.
Jag skulle nog också skapa en egen liten klass för det blanka elementet, ungefär som så här:
final class DummyCategoryElement {
@Override
public void toString() {
return " ";
}
}
...sedan kan man kolla om dummyelementet är valt på följande sätt...:
if (comboBox.getSelectedObject() instanceof DummyCategoryElement) {
//... protestera?
}
Om du vill ha ett blankt element förvalt som magiskt "försvinner" så fort användaren börjar bläddra i combon, så går det också att ordna, men det är ingen användarvänlig lösning och kräver lite lätt Java-voodoo som bara krånglar till saker i onödan.
List incomeCategories = categoryManager.getCategories(false);
List expenceCategories = categoryManager.getCategories(true);
BTW, varför använder du inte generics, och varför använder du en skum boolean för att bestämma kategorityp? Varför kan det inte vara:
List<Category> incomeCategories = categoryManager.getIncomeCategories();
List<Category> expenseCategories = categoryManager.getExpenseCategories();
...?
Med den striktare typningen det här medför, kan du också skriva om
if (incomeCategories.size() > 0) {
comboBoxModel.addElement(new GroupMarker("Inkomster"));
for (int i=0; i<incomeCategories.size(); i++) {
Category category = (Category)incomeCategories.get(i);
comboBoxModel.addElement(new ComboBoxItem(category.getId(), category.getName()));
}
}
till:
if (!incomeCategories.isEmpty()) {
comboBoxModel.addElement(new GroupMarker("Inkomster"));
for (Category category : incomeCategories) {
comboBoxModel.add(new ComboBoxItem(category.getId(), category.getName()));
}
}
Typade listor och for-each-loopar istället för indexerade loopar med typecasts från Object är en bättre lösning om du ändå "vet" att listan bara ska innehålla en typ...
- Vilken Linux-distro använder du just nu? - Dela gärna dina erfarenheter!222
- Fråga om höga temperaturer på SSDs och hårddiskar13
- Dagens fynd (bara tips, ingen diskussion) — Läs första inlägget först!20k
- Programmerare besegrar Chat GPT i kodmaraton60
- Hur vanligt är det med coil whine ?97
- Ny M.2 1678,02GB Inte allokerat3
- Solceller, solpaneler, egen elproduktion, styra elförbrukning1,1k
- Vad lyssnar du på just nu?15k
- Hur investerar ni?12k
- Tråden för hörlursgalningar! Lurar, förstärkare, dacs, pads osv osv It´s all here!20k
- Köpes Köpes: RTX 4070 Ti Super (Billigt?)
- Säljes Intel Arc b580 limited edition
- Säljes Wooting 80HE (ISO-Nordic, TTC KOM RGB, Glorious Aura Keycaps, ABS Ghost)
- Säljes Äldre PC till salu - GPU eller HDD ingår ej
- Skänkes NYTT Skärmskydd till iPhone 11/XR
- Säljes Asus 5060 ti
- Köpes i7 6700(k)/7700(k) eller Ryzen 5/7 gen 1-3
- Köpes Söker mobo till 8e/9e gen. Intel, eller mobo + cpu paket
- Säljes Speldator - Ryzen 5 3600x / RX 5700 XT / 32GiB 3200MHz
- Säljes MacBook Air 13 2024 8/256GB (Midnight)
- Microsoft slutar sälja film och serier17
- Programmerare besegrar Chat GPT i kodmaraton60
- AMD:s Threadripper Pro med 96 kärnor kostar 113 000 kronor22
- Civilization VI med alla expansioner gratis hos Epic 40
- Navigera rätt i grafikkortsdjungeln102
- Telia kan köpa Bredband290
- Adware i populära Crystaldiskinfo8
- Säkerhetsbrist hos Gigabyte-moderkort berör över 240 modeller19
- Kivra testar att ta betalt för tjänsten237
- Quiz: Känner du igen programmen på ikonerna?109
Externa nyheter
Spelnyheter från FZ