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...
- Idag Iphones marknadsandel faller i USA 26
- Igår Airtec Pro Type1 – batteridrivet alternativ till tryckluft på burk 59
- Igår Nu stiger hårddiskpriserna med uppemot 10 procent 15
- Igår Analytiker: Apple har överskattat intresset för Vision Pro 51
- 24 / 4 AMD, Nvidia och Intel – vad är det för skillnad mellan grafikkortstillverkarna? 31
- SFW! Känn doften av nyklippt gräs med Dreame Roboticmower A133
- Microsoft rullar ut Startmenyreklam till alla68
- Fedora 401
- Konsumentverket granskar tio nätbutiker som lurat kunder16
- Dator toppar på 100°C vid spel eller annan krävande aktivitet1
- Tråden om Xbox Series X|S7517
- Köpa mesh8
- Simulator - hjälp/tips på bildförskjutning sökes19
- Elbilar - Tråden för intresserade23182
- [LEK] Gissa spelet15511
- Säljes Kingston PCie 4.0 NVMe M.2 4TB 7300MB/s
- Säljes i3-8100
- Köpes Luftkylning och Fläktar
- Köpes Nvidia Quadro P400,600 eller 620
- Säljes Dell Latitude 7530 15,6”
- Köpes Önskar köpa 6700xt eller 3070
- Säljes Asus Geforce RTX 2070 8GB Strix Gaming OC
- Köpes Köper bärbara datorer, trasiga, utan skärm etc.
- Säljes Lenovo Thinkcentre M73
- Säljes Lenovo Legion Slim 5 14” OLED
- Därför blockerar Windows 11 24H2 Start‑menyhack10
- Iphones marknadsandel faller i USA26
- Konsumentverket granskar tio nätbutiker som lurat kunder16
- Airtec Pro Type1 – batteridrivet alternativ till tryckluft på burk59
- Nintendo-innehåll tas bort från Garrys Mod24
- Nu stiger hårddiskpriserna med uppemot 10 procent15
- Quiz: Vad kan du om Inet?70
- Analytiker: Apple har överskattat intresset för Vision Pro51
- Microsoft rullar ut Startmenyreklam till alla68
- EU röstar igenom ”rätten att reparera”53