2016-12-09 26 views
8

스윙 JComboBox를 사용하고 테두리를 클릭하면 팝업이 나타나 즉시 사라집니다. 내가 클릭을 말할 때, 나는 마우스의 왼쪽 버튼을 눌러 즉시 풀어 놓는 것을 의미합니다. 어떤 사용자가 일어날 것으로 예상하지 않기 때문에테두리를 클릭하면 JComboBox 팝업이 나타나고 즉시 숨김

enter image description here

는 그것은 나쁜 사용자 경험이 고려 될 수있다. 모든 사용자는 콤보 상자의 테두리를 클릭 다음 동작 중 하나를 기대 :

  1. 팝업 열을하고,
  2. 을 열어 남아 아니면 전혀 열 수 없습니다.

사용자가 즉시 팝업을 열고 닫을 것으로 기대하는 사용자는 없습니다.

사용자가 의도적으로 테두리를 클릭하지 않습니다. 그러나 콤보 박스가 작아서 빠르게 클릭하려고하면 빈번히 발생할 수 있습니다. 다음으로, "해결되지 않습니다": 2000 년 누군가에

은 오픈 JDK 사이트의 버그로이 동작을 등록 : 그들은 버그로 인식 한 https://bugs.openjdk.java.net/browse/JDK-4346918

하지만, 해상도를 폐쇄 관찰 :

나는 문제를 재현 할 수 있었지만 그렇게 중요하지는 않다. 나는 그것을 고치지 않을 것이다. 문제는 테두리를 클릭 한 후 마우스를 놓을 때 콤보 상자의 드롭 다운 부분이 숨겨집니다. 이 버그는 큰 영향을주지 않습니다.

나는 매우 중대한 영향을 미치지 않는다는 것에 동의한다. 그러나 여전히 나쁜 사용자 경험으로 이어질 것이라고 생각하며 사용자가 테두리를 클릭 할 때 팝업을 열거 나 열지 않는 간단한 해결 방법이 있는지 알고 싶습니다.

설명 된 동작은 JComboBox의 테두리를 마우스 왼쪽 버튼으로 클릭하여 재현 할 수 있습니다.

import java.awt.FlowLayout; 
import javax.swing.*; 

public class JComboBoxUX{ 
    public static void main(String[] args){ 
     SwingUtilities.invokeLater(new Runnable(){ 
      @Override 
      public void run(){ 
       JComboBox<String> combobox = new JComboBox<String>(
         new String[]{"aaaaaaaaaa","bbbbbbbb","ccccccccc"}); 

       JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 10)); 
       panel.add(combobox); 

       JFrame frame = new JFrame("JComboBox UX"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setContentPane(panel); 
       frame.setSize(300, 150); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 
} 
+2

테두리를 클릭하지 마십시오. 어쨌든 픽셀 만 넓습니다. ComboBoxUI의 세부 사항을보고 마우스 클릭을 처리하기 위해 사용자 정의 LAF를 작성해야 할 때 정말 귀찮은 경우. 나 한텐 가치 없어. – camickr

+0

나는 국경을 의도적으로 클릭하지 않는다. 그러나 나는 그것에 대해 생각하지 않고 단지 콤보 박스를 사용하고자 할 때 제 의지에 반하여 자주 발생합니다. 그것은 정말로 나를 귀찮게하지만, 나는 그것에 많은 시간을 할 가치가 없다는 것에 동의한다. 나는 어쩌면이 결함에 대한 간단한 해결책이 있었기 때문에 물었습니다. 인터넷에서 아무것도 찾지 못했습니다. – viniciussss

+0

참고 : 사용자 오류! = (JAVA BUG). 더 나은 도움을 받으려면 [MCVE] 또는 [단락, 자체 포함, 올바른 예] (http://www.sscce.org/)를 게시하십시오. –

답변

5

문제가 될 것 같다 :이 재현 할 수있는 간단한 코드 아래 참조 size.widthsize.height에서 하나를 빼면

class BasicComboPopup extends ... { 

    private Handler getHandler() { 
     if (handler == null) { 
      handler = new Handler(); 
     } 
     return handler; 
    } 

    private class Handler implements ... MouseListener ... { 

     public void mouseReleased(MouseEvent e) { 
      //... 
      Component source = (Component)e.getSource(); 
      Dimension size = source.getSize(); 
      Rectangle bounds = new Rectangle(0, 0, size.width - 1, size.height - 1); 
      if (!bounds.contains(e.getPoint())) { 
       //... 
       comboBox.setPopupVisible(false); 
      } 
     } 
    } 
} 

, 마우스 화살표의 경계 범위 밖에 버튼을 클릭하면 팝업 메뉴가 숨겨집니다.

문제를 해결하는 데 문제가 있습니다. Handler 클래스는 private이므로 확장 할 수 없으므로 getHandler()private이므로 BasicComboPopup에서도이 클래스를 재정의 할 수 없습니다.

MetalComboBoxUI을 연장하고 같은 사람이 BasicComboPopup을 확장하지만 위의 Handler 유사한 클래스를 반환하는 createMouseListener()을 확장 사용자 정의 ComboPopup을 반환하는 createPopup()을 무시하지만, 빼기 것없이 수 있습니다.

아, 지원하려는 각 LAF에 대해 동일한 작업을 수행하십시오. 수다. 다른 방향에서 문제를 공격

하나는 (e.getSource() 의해 반환) MetalComboBoxButton과 메뉴가 표시 될 때, 양방향으로 1 차원 화소 큰 반환 할 getSize() 메소드를 재정의 연장 할 수있다. 물론이 맞춤식 버튼을 만들고 설치하려면 MetalComboBoxUI을 확장하고 재정의해야합니다.

다시 한 번 지원하려는 LAF마다 동일한 작업을 수행해야합니다. 다시, yuk.

불행히도 필요한 기능을 쉽게 오버라이드 할 수있는 스윙이 필요하지 않으며 다양한 클래스를 개인 내부 구현 세부 정보로 표시하여 재사용을 방지합니다 (나중에 변경을 원할 경우 파손을 방지하기 위해). 내부).

+0

그래, 나쁘지. 우리는 JavaFX에 항복해야 할 것입니다 ... – Mordechai

+2

아마도이 정보를 버그 보고서 https://bugs.openjdk.java.net/browse/JDK-4346918에 첨부했거나이 정보를 가진 새로운 버그를 제출했다면 그들은 그것을 기꺼이 고칠 것입니다. 버그 보고서를 읽는 것은 그것이 버그라는 것을 인정하지만 조사 할만큼 중요하지는 않다는 것입니다. 그러나 조사를하고 변경해야하는 코드를 표시 할 수 있기 때문에 코드를 변경할 수 있습니다. – Enwired

+0

@Enwired : JDK 버그 시스템 (JBS)에 대한 계정이 없습니다. 버그를 만들고 편집하려면 OpenJDK Author 상태 (또는 이상)가 필요합니다. 원래 기자와 양수인 모두 활동하지 않아서 전자 메일로 분석서를 보낼 수는 없습니다. 분석을 추가 할 액세스 수준을 가진 다른 사용자에게 맡깁니다. – AJNeufeld

1

AJNeufeld의 제안은 완벽하게 작동했습니다. 고맙습니다!

아래 코드는 누군가 필요한 경우입니다.

JComboBoxGoodBorder.java :

import java.awt.Component; 
import java.awt.Dimension; 
import java.awt.Point; 
import java.awt.Rectangle; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import java.awt.event.MouseListener; 
import java.util.Vector; 
import javax.swing.ComboBoxModel; 
import javax.swing.JComboBox; 
import javax.swing.JComponent; 
import javax.swing.SwingUtilities; 
import javax.swing.plaf.ComponentUI; 
import javax.swing.plaf.basic.BasicComboPopup; 
import javax.swing.plaf.basic.ComboPopup; 
import javax.swing.plaf.metal.MetalComboBoxUI; 

public class JComboBoxGoodBorder<T> extends JComboBox<T> { 

    public JComboBoxGoodBorder(){ 
     super(); 
    } 

    public JComboBoxGoodBorder(ComboBoxModel<T> aModel){ 
     super(aModel); 
    } 

    public JComboBoxGoodBorder(T[] items){ 
     super(items); 
    } 

    public JComboBoxGoodBorder(Vector<T> items){ 
     super(items); 
    } 

    @Override 
    public void updateUI(){ 
     setUI(MetalComboBoxUIGoodBorder.createUI(this)); 
    } 

    private static class MetalComboBoxUIGoodBorder extends MetalComboBoxUI { 
     public static ComponentUI createUI(JComponent c) { 
      return new MetalComboBoxUIGoodBorder();   
     } 

     @Override 
     protected ComboPopup createPopup() { 
      return new BasicComboPopup(comboBox) { 
       @Override 
       protected MouseListener createMouseListener(){ 
        return new MouseAdapter(){ 
         @Override 
         public void mousePressed(MouseEvent e) { 
          if (e.getSource() == list) { 
           return; 
          } 
          if (!SwingUtilities.isLeftMouseButton(e) || !comboBox.isEnabled()) 
           return; 

          if (comboBox.isEditable()) { 
           Component comp = comboBox.getEditor().getEditorComponent(); 
           if ((!(comp instanceof JComponent)) || ((JComponent)comp).isRequestFocusEnabled()) { 
            comp.requestFocus(); 
           } 
          } 
          else if (comboBox.isRequestFocusEnabled()) { 
           comboBox.requestFocus(); 
          } 
          togglePopup(); 
         } 

         @Override 
         public void mouseReleased(MouseEvent e) { 
          if (e.getSource() == list) { 
           if (list.getModel().getSize() > 0) { 
            // JList mouse listener 
            if (comboBox.getSelectedIndex() != list.getSelectedIndex()) { 
             comboBox.setSelectedIndex(list.getSelectedIndex()); 
            } else { 
             comboBox.getEditor().setItem(list.getSelectedValue()); 
            } 
           } 
           comboBox.setPopupVisible(false); 
           // workaround for cancelling an edited item (bug 4530953) 
           if (comboBox.isEditable() && comboBox.getEditor() != null) { 
            comboBox.configureEditor(comboBox.getEditor(), 
              comboBox.getSelectedItem()); 
           } 
           return; 
          } 
          // JComboBox mouse listener 
          Component source = (Component)e.getSource(); 
          Dimension size = source.getSize(); 
          Rectangle bounds = new Rectangle(0, 0, size.width, size.height); 
          if (!bounds.contains(e.getPoint())) { 
           MouseEvent newEvent = convertMouseEvent(e); 
           Point location = newEvent.getPoint(); 
           Rectangle r = new Rectangle(); 
           list.computeVisibleRect(r); 
           if (r.contains(location)) { 
            if (comboBox.getSelectedIndex() != list.getSelectedIndex()) { 
             comboBox.setSelectedIndex(list.getSelectedIndex()); 
            } else { 
             comboBox.getEditor().setItem(list.getSelectedValue()); 
            } 
           } 
           comboBox.setPopupVisible(false); 
          } 
          hasEntered = false; 
          stopAutoScrolling(); 
         } 
        }; 
       } 
      };   
     } 
    } 
} 

Test.java :

import java.awt.FlowLayout; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.SwingUtilities; 

public class Test{ 
    public static void main(String[] args){ 
     SwingUtilities.invokeLater(new Runnable(){ 
      @Override 
      public void run(){ 
       JComboBoxGoodBorder<String> combobox = new JComboBoxGoodBorder<String>(
         new String[]{"aaaaaaaaaa","bbbbbbbb","ccccccccc"}); 

       JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 10)); 
       panel.add(combobox); 

       JFrame frame = new JFrame("JComboBox Good Border"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setContentPane(panel); 
       frame.setSize(300, 300); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 
} 
+0

당신을 위해 일해 준 것을 기쁘게 생각합니다. "... 사용자가 국경을 클릭 할 때 팝업이 열리거나 열리지 않게하는 간단한 해결 방법이 있는지 알고 싶습니다." 슬프게도, 당신이 요청한 간단한 해결 방법이 아닙니다. – AJNeufeld

+0

간단하지 않습니다. 그러나 작동하고, 코드를 통해 (여분의 작업없이) 재사용 할 수 있으며, 도움을 받아 쉽게 할 수 있습니다. 귀하의 지침을 따르고 Handler 내부 클래스 코드를 복사/지나쳐야했습니다. – viniciussss