设置 JComboBox PopupMenu 的大小
我正在编写一个扩展 JComboBox 的自定义组件。我的问题是,如果我添加或删除项目,PopupMenu 将不会实现其大小。例如,列表中有 2 个项目,但如果之前有 4 个项目,则 PopupMenu 中也有 2 个“空”项目。
我发现的唯一解决方法是(在 JIntelligentComboBox.java 第 213 行)
this.setPopupVisible(false);
this.setPopupVisible(true);
但结果将是一个闪烁的 PopupMenu :-(
那么我还能做什么来刷新/重新绘制 PopupMenu 而不闪烁?
用于测试: 组件 和一点测试程序
要生成我的问题,您可以例如:
- 输入“e”
- 按“return”
- 输入“m”
提前致谢
编辑: 我的目标是一个 ComboBox,其作用类似于 Firefox 或 Chrome 中的地址栏,我想显示包含键入字符的 PopupMenu 的所有项目。
cboxtester.java:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
public class cboxtester extends JFrame {
private DefaultComboBoxModel dcm = new DefaultComboBoxModel(new Object[][] {new Object[] {"Mittagessen", "", 0},
new Object[] {"Essen", "", 0},
new Object[] {"Frühstück", "", 0},
new Object[] {"Abendessen", "", 0}});
private JIntelligentComboBox icb = new JIntelligentComboBox(dcm);
private cboxtester(){
this.add(icb, BorderLayout.CENTER);
this.add(new JButton("bla"), BorderLayout.EAST);
this.pack();
this.setVisible(true);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
cboxtester cbt = new cboxtester();
}
}
JIntelligentComboBox.java:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Vector;
import javax.swing.ComboBoxEditor;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultRowSorter;
import javax.swing.JComboBox;
import javax.swing.JList;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.MutableComboBoxModel;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.basic.BasicComboBoxEditor;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
import javax.swing.plaf.metal.MetalComboBoxEditor;
public class JIntelligentComboBox extends JComboBox {
private ArrayList<Object> itemBackup = new ArrayList<Object>();
/** Initisiert die JIntelligentComboBox */
private void init(){
class searchComboBoxEditor extends BasicComboBoxEditor {
public searchComboBoxEditor(){
super();
}
@Override
public void setItem(Object anObject){
if (anObject == null) {
super.setItem(anObject);
} else {
Object[] o = (Object[]) anObject;
super.setItem(o[0]);
}
}
@Override
public Object getItem(){
return new Object[]{super.getItem(), super.getItem(), 0};
}
}
this.setEditor(new searchComboBoxEditor());
this.setEditable(true);
class searchRenderer extends BasicComboBoxRenderer {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus){
if (index == 0) {
setText("");
this.setPreferredSize(new Dimension(1, 1));
return this;
}
this.setPreferredSize(new Dimension(160, 17));
if (index == list.getModel().getSize() - 1) {
this.setBorder(new EmptyBorder(0, 3, 1, 3));
} else {
this.setBorder(new EmptyBorder(0, 3, 0, 3));
}
Object[] v = (Object[]) value;
//System.out.println(v[0]);
this.setFont(new Font("Arial", Font.PLAIN, 12));
this.setBackground(Color.white);
String s = (String) v[0];
String lowerS = s.toLowerCase();
String sf = (String) v[1];
String lowerSf = sf.toLowerCase();
ArrayList<String> notMatching = new ArrayList<String>();
if (!sf.equals("")){
int fs = -1;
int lastFs = 0;
while ((fs = lowerS.indexOf((String) lowerSf, (lastFs == 0) ? -1 : lastFs)) > -1) {
notMatching.add(s.substring(lastFs, fs));
lastFs = fs + sf.length();
//System.out.println(fs+sf.length());
}
notMatching.add(s.substring(lastFs));
//System.out.println(notMatching);
}
String html = "";
if (notMatching.size() > 1) {
html = notMatching.get(0);
int start = html.length();
int sfl = sf.length();
for (int i = 1; i < notMatching.size(); i++) {
String t = notMatching.get(i);
html += "<b style=\"color: black;\">" + s.substring(start, start + sfl) + "</b>" + t;
start += sfl + t.length();
}
}
System.out.println(index + html);
this.setText("<html><head></head><body style=\"color: gray;\">" + html + "</body></head>");
return this;
}
}
this.setRenderer(new searchRenderer());
// leeres Element oben einfügen
int size = this.getModel().getSize();
Object[] tmp = new Object[this.getModel().getSize()];
for (int i = 0; i < size; i++) {
tmp[i] = this.getModel().getElementAt(i);
itemBackup.add(tmp[i]);
}
this.removeAllItems();
this.getModel().addElement(new Object[]{"", "", 0});
for (int i = 0; i < tmp.length; i++) {
this.getModel().addElement(tmp[i]);
}
// keylistener hinzufügen
this.getEditor().getEditorComponent().addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
searchAndListEntries(((JTextField)JIntelligentComboBox.this.getEditor().getEditorComponent()).getText());
//System.out.println(((JTextField)JIntelligentComboBox.this.getEditor().getEditorComponent()).getText());
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
});
}
public JIntelligentComboBox(){
super();
}
public JIntelligentComboBox(MutableComboBoxModel aModel){
super(aModel);
init();
}
public JIntelligentComboBox(Object[] items){
super(items);
init();
}
public JIntelligentComboBox(Vector<?> items){
super(items);
init();
}
@Override
public MutableComboBoxModel getModel(){
return (MutableComboBoxModel) super.getModel();
}
private void searchAndListEntries(Object searchFor){
ArrayList<Object> found = new ArrayList<Object>();
//System.out.println("sf: "+searchFor);
for (int i = 0; i < this.itemBackup.size(); i++) {
Object tmp = this.itemBackup.get(i);
if (tmp == null || searchFor == null) continue;
Object[] o = (Object[]) tmp;
String s = (String) o[0];
if (s.matches("(?i).*" + searchFor + ".*")){
found.add(new Object[]{((Object[])tmp)[0], searchFor, ((Object[])tmp)[2]});
}
}
this.removeAllItems();
this.getModel().addElement(new Object[] {searchFor, searchFor, 0});
for (int i = 0; i < found.size(); i++) {
this.getModel().addElement(found.get(i));
}
this.setPopupVisible(true);
}
}
i am programming a custom component which extends a JComboBox. My problem is, the PopupMenu won't actualise its size if i am adding or removing an item. So there are e.g. 2 items in the list, but if there were 4 before i had 2 "empty" items in the PopupMenu as well.
The only workaround i found was to do (in JIntelligentComboBox.java line 213)
this.setPopupVisible(false);
this.setPopupVisible(true);
but the result will be a flickering PopupMenu :-(
So what else could i do to refresh/repaint the PopupMenu without flickering?
For testing: the component and a little test programm
To generate my problem you could e.g.:
- type "e"
- press "return"
- type "m"
Thanks in advance
Edit:
My goal is a ComboBox that acts like e.g. the adressbar in Firefox or Chrome, i want to show all items of the PopupMenu that contain the typed chars.
cboxtester.java:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
public class cboxtester extends JFrame {
private DefaultComboBoxModel dcm = new DefaultComboBoxModel(new Object[][] {new Object[] {"Mittagessen", "", 0},
new Object[] {"Essen", "", 0},
new Object[] {"Frühstück", "", 0},
new Object[] {"Abendessen", "", 0}});
private JIntelligentComboBox icb = new JIntelligentComboBox(dcm);
private cboxtester(){
this.add(icb, BorderLayout.CENTER);
this.add(new JButton("bla"), BorderLayout.EAST);
this.pack();
this.setVisible(true);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
cboxtester cbt = new cboxtester();
}
}
JIntelligentComboBox.java:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Vector;
import javax.swing.ComboBoxEditor;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultRowSorter;
import javax.swing.JComboBox;
import javax.swing.JList;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.MutableComboBoxModel;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.basic.BasicComboBoxEditor;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
import javax.swing.plaf.metal.MetalComboBoxEditor;
public class JIntelligentComboBox extends JComboBox {
private ArrayList<Object> itemBackup = new ArrayList<Object>();
/** Initisiert die JIntelligentComboBox */
private void init(){
class searchComboBoxEditor extends BasicComboBoxEditor {
public searchComboBoxEditor(){
super();
}
@Override
public void setItem(Object anObject){
if (anObject == null) {
super.setItem(anObject);
} else {
Object[] o = (Object[]) anObject;
super.setItem(o[0]);
}
}
@Override
public Object getItem(){
return new Object[]{super.getItem(), super.getItem(), 0};
}
}
this.setEditor(new searchComboBoxEditor());
this.setEditable(true);
class searchRenderer extends BasicComboBoxRenderer {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus){
if (index == 0) {
setText("");
this.setPreferredSize(new Dimension(1, 1));
return this;
}
this.setPreferredSize(new Dimension(160, 17));
if (index == list.getModel().getSize() - 1) {
this.setBorder(new EmptyBorder(0, 3, 1, 3));
} else {
this.setBorder(new EmptyBorder(0, 3, 0, 3));
}
Object[] v = (Object[]) value;
//System.out.println(v[0]);
this.setFont(new Font("Arial", Font.PLAIN, 12));
this.setBackground(Color.white);
String s = (String) v[0];
String lowerS = s.toLowerCase();
String sf = (String) v[1];
String lowerSf = sf.toLowerCase();
ArrayList<String> notMatching = new ArrayList<String>();
if (!sf.equals("")){
int fs = -1;
int lastFs = 0;
while ((fs = lowerS.indexOf((String) lowerSf, (lastFs == 0) ? -1 : lastFs)) > -1) {
notMatching.add(s.substring(lastFs, fs));
lastFs = fs + sf.length();
//System.out.println(fs+sf.length());
}
notMatching.add(s.substring(lastFs));
//System.out.println(notMatching);
}
String html = "";
if (notMatching.size() > 1) {
html = notMatching.get(0);
int start = html.length();
int sfl = sf.length();
for (int i = 1; i < notMatching.size(); i++) {
String t = notMatching.get(i);
html += "<b style=\"color: black;\">" + s.substring(start, start + sfl) + "</b>" + t;
start += sfl + t.length();
}
}
System.out.println(index + html);
this.setText("<html><head></head><body style=\"color: gray;\">" + html + "</body></head>");
return this;
}
}
this.setRenderer(new searchRenderer());
// leeres Element oben einfügen
int size = this.getModel().getSize();
Object[] tmp = new Object[this.getModel().getSize()];
for (int i = 0; i < size; i++) {
tmp[i] = this.getModel().getElementAt(i);
itemBackup.add(tmp[i]);
}
this.removeAllItems();
this.getModel().addElement(new Object[]{"", "", 0});
for (int i = 0; i < tmp.length; i++) {
this.getModel().addElement(tmp[i]);
}
// keylistener hinzufügen
this.getEditor().getEditorComponent().addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
searchAndListEntries(((JTextField)JIntelligentComboBox.this.getEditor().getEditorComponent()).getText());
//System.out.println(((JTextField)JIntelligentComboBox.this.getEditor().getEditorComponent()).getText());
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
});
}
public JIntelligentComboBox(){
super();
}
public JIntelligentComboBox(MutableComboBoxModel aModel){
super(aModel);
init();
}
public JIntelligentComboBox(Object[] items){
super(items);
init();
}
public JIntelligentComboBox(Vector<?> items){
super(items);
init();
}
@Override
public MutableComboBoxModel getModel(){
return (MutableComboBoxModel) super.getModel();
}
private void searchAndListEntries(Object searchFor){
ArrayList<Object> found = new ArrayList<Object>();
//System.out.println("sf: "+searchFor);
for (int i = 0; i < this.itemBackup.size(); i++) {
Object tmp = this.itemBackup.get(i);
if (tmp == null || searchFor == null) continue;
Object[] o = (Object[]) tmp;
String s = (String) o[0];
if (s.matches("(?i).*" + searchFor + ".*")){
found.add(new Object[]{((Object[])tmp)[0], searchFor, ((Object[])tmp)[2]});
}
}
this.removeAllItems();
this.getModel().addElement(new Object[] {searchFor, searchFor, 0});
for (int i = 0; i < found.size(); i++) {
this.getModel().addElement(found.get(i));
}
this.setPopupVisible(true);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我修改了您的 sscce 下面,我注意到了一些事情:
您观察到的异常在以下情况下并不明显使用
apple.laf.AquaComboBoxUI
。特别是,输入和删除文本会按预期增大和缩小列表。您可以在您的平台上尝试修改后的代码。为了方便起见,我从
KeyListener
切换到KeyAdapter
,但这不是解决方案。您可能应该使用DocumentListener
。它在使用时无法发生变化,就像您现在所做的那样,所以我没有进一步追求这一点。始终在事件调度线程上构建 GUI .
硬编码的尺寸和新颖的字体很少在其他外观和设计上看起来正确。感受实施。我只是删除了你的以显示外观。
您的构造函数在构造父级后修改模型,因此结果取决于实例化的顺序。单独的模型可能更容易管理。
更新:添加了代码来验证 @camickr 的解决方案。
I have revised your sscce below, and I noticed a few things:
The anomaly you observe is not apparent when using
apple.laf.AquaComboBoxUI
. In particular, entering and deleting text grows and shrinks the list as expected. You might try the revised code on your platform.I switched from
KeyListener
toKeyAdapter
for expedience, but that's not a solution. You should probably use aDocumentListener
. It can't be mutated while in use, as you are doing now, so I didn't pursue this further.Always build the GUI on the event dispatch thread.
Hard-coded dimensions and novel fonts rarely look right on other look & feel implementations. I simply removed yours to get the appearance shown.
Your constructor modifies the model after constructing the parent, so the result depends on the order of instantiation. A separate model might be easier to manage.
Update: Added code to verify @camickr's solution.
以下解决方案的基础是每次调用
searchAndListRoutine
时调整弹出窗口的大小。您需要考虑到,当弹出窗口显示在父框架的边界之外时,弹出窗口可以显示在其自己的窗口中,或者可以显示在父框架的分层窗格上:唯一的问题是,当组合框字段为空,模型包含 4 个条目。我猜你的匹配逻辑有问题。
The basis of the solution below is to resize the popup every time you invoke the
searchAndListRoutine
. You need to take into account that the popup can be displayed in its own Window when the popup displays outside the bounds of the parent frame or it can be displayed on the layered pane of the parent frame:The one issue is that when the combo box field is empty the model contains 4 entries. I would guess that is a problem with your matching logic.
未测试您的代码,
请在此处提供渲染器内容的建议对于自动完成 JComboBox 此处
not tested your code,
please advice for content of Renderer here and for AutoComplete JComboBox here
在扩展 AbstractListModel 和 ComboboxModel 中使用向量数组时遇到了同样的问题。
实现 MutableComboBoxModel。使用 setMaximumRowCount 解决:
迭代数据库值并将它们保存到
public ArrayList listInCombobox = new ArrayList();
myComboBox.setMaximumRowCount(listInCombobox.size());
在 myComboBox MouseListener (mousePressed) 中执行上述操作。
Faced the same problem using Vector array in ComboboxModel that extends AbstractListModel &
implements MutableComboBoxModel. Solved using setMaximumRowCount:
iterate over database values and save them into
public ArrayList listInCombobox = new ArrayList();
myComboBox.setMaximumRowCount(listInCombobox.size());
do the above inside myComboBox MouseListener (mousePressed).
你试过这个吗?
http://download.oracle .com/javase/6/docs/api/javax/swing/JComboBox.html#updateUI()
Did you tried this?
http://download.oracle.com/javase/6/docs/api/javax/swing/JComboBox.html#updateUI()