2011-09-10 2 views
5

저는 스윙 개발에 익숙하지 않습니다. 제 질문이 바보가 아니길 바랍니다.포커스 소유자 일시적으로 null로 변경

다음과 같은 문제가 있습니다. 을 사용하여 포커스를 추적 중입니다. 속성 permanentFocusOwner의 수신 대기 중입니다. 그러나 하나의 컨트롤에서 다른 컨트롤로 포커스가 변경되면 permanentFocusOwner 속성의 중간 변경이 null이됩니다.

현재 포커스가 패널 중 하나 또는 하위 패널에있는 경우 현재 UI 로직에서 컨트롤을 일부 변경하고 있습니다. 그러나 중간을 얻는 것은 null이 논리를 나눕니다.

Google에서이 문제에 대한 정보를 검색 한 결과 관련성이 발견되지 않았습니다.

문제는 의도적으로 설계된 동작인지와 임시 해결 방법 중간에 null이 있는지 여부입니다.

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

public class FocusNullTest extends JFrame { 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      public void run() { 
       FocusNullTest self = new FocusNullTest(); 
       self.setVisible(true); 
      } 
     }); 
    } 

    public FocusNullTest() { 
     setSize(150, 100); 
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     Container contentPane = getContentPane(); 
     contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.X_AXIS)); 

     contentPane.add(new JButton("1")); 
     contentPane.add(new JButton("2")); 

     KeyboardFocusManager focusManager = 
      KeyboardFocusManager.getCurrentKeyboardFocusManager(); 
     focusManager.addPropertyChangeListener(
       "permanentFocusOwner", 
       new PropertyChangeListener() { 
        @Override 
        public void propertyChange(PropertyChangeEvent e) { 
         System.out.println("permanentFocusOwner changed from: " 
              + e.getOldValue()); 
         System.out.println("permanentFocusOwner changed to : " 
              + e.getNewValue()); 
        } 
       }); 
    } 
} 

로그 출력은 : 여기

는 상기 동작 재생 최소화 애플리케이션이다

(프로그램 개시는 자동 버튼 1 세트 포커스)
permanentFocusOwner은 변경된 : null
permanentFocusOwner가 다음으로 변경되었습니다 : javax.swing.JButton [, 0,18,41x26, (건너 뛴)]
,691,363,210 는 permanentFocusOwner가 변경
(2 버튼 클릭) : NULL
permanentFocusOwner가 변경 : javax.swing.JButton
permanentFocusOwner로 변경 [0,18,41x26 (스킵)] 널 의사 코드에서, [(스킵) 41,18,41x26] javax.swing.JButton


(선택 부 :
permanentFocusOwner로 변경)
내 목표는 항목이 확장되어 포커스를 얻었을 때 더 많은 정보를 표시하는 목록보기처럼 보이게하는 것입니다. 확장 된보기에는 몇 가지 추가 버튼이 있습니다.

JList은 (1) 버튼을 클릭 할 수 없으며 (2) 해당 항목의 높이가 일정하기 때문에 항목을 동적으로 확대하려는 경우 적절한 컨트롤이 아닌 것처럼 보입니다. 편집 모드가있는 JTable은 최소한 일정한 항목 크기 때문에 적절한 해결책이 아닌 것으로 보입니다.

그래서 세로 상자 레이아웃을 컨테이너로 사용하여 일반 JPanel을 사용하고 모델 변경 사항을 구독하고 수동으로 비주얼을 업데이트합니다. 문제는 단추를 클릭 할 때 포함하는 목록 항목이 포커스를 잃는 것입니다. 초점이 null으로 일시적으로 변경되지 않으면 목록 항목 내에 초점이 남아 있음을 감지 할 수 있습니다.(- 원인을 못 찾았 단 초점 asynchrous 자연이 어떻게 든 이유가있을 수 있다는 추측이 아니라, 그것은해야 콩 사양 기준)

firePropertyChange(someProperty, oldValue, null) 
    firePropertyChange(someProperty, null, newValue) 

+0

+ 1에 대해 [sscce] (http://sscce.org/)이지만 목표에 대한 자세한 내용은 더 나은 해결책을 제시 할 수 있습니다. – trashgod

+0

@trashgod : 코드 의도에 대한 세부 정보를 추가했습니다. – Vlad

답변

2

의 KeyboardFocusManager는 대부분의 속성에 대한 두 가지 이벤트를 발사한다 newVaue에 따라 작업을 수행 할 때 두 번째를 기다리십시오.

+1

이것이 윈도우가 포커스를 얻는 방법에 대한 플랫폼 간 차이의 결과라고 추정 할 수 없습니다. – trashgod

2

해결 방법으로 마지막 "실제"이전 포커스 소유자를 이벤트 처리기에 멤버로 저장하십시오.

if ((e.getOldValue() != null) && (e.getNewValue() == null)) 
prev_owner = e.getOldValue(); 

그러면 대상에 실제로 초점을 맞출 때 해당 개체를 처리 할 수 ​​있습니다. 실제 구성 요소가 실제로 포커스를받을 때만 (즉, getNewValue()이 null이 아닌 경우) 강조 표시를 처리합니다.

(이 동작은 The AWT Focus Subsystem에 설명 된 것과 동일하게 보입니다. 이전 구성 요소가 먼저 포커스를 잃었을 때 대상 구성 요소가 가져옵니다. 원자가 아니므로 아무 것도 실제로 존재하지 않는 기간이 있습니다. 그러나 나는 전문가가 아니기 때문에 다를 수 있습니다.)

2

목표는 항목이 확장되어 포커스를 얻었을 때 더 많은 정보를 표시하는 목록보기처럼 보이게 만드는 것입니다. JTable, Outline 또는 패널의 수직 Box에서 확장 버튼의 왼쪽에, 내가 넣어 (포커스) :

는 다른, 나는 때때로 JSplitPane를 사용 오른쪽에는 확장 된보기가 있습니다.

+0

제안 해 주셔서 감사합니다. 이렇게하면 UI가 달라 지지만 확실히 좋은 디자인입니다. (그리고 전에'Outline'에 대해서 들었습니다.) – Vlad

+0

그런데 JPanel/Box 자식을 모델에 바인딩하는 쉬운 방법이 있습니까? JTable의 항목을 TableModel에 바인딩하는 것과 같은 방법입니까? – Vlad

+0

보통 나는 JPanel 생성자에서 ['Action'] (http://download.oracle.com/javase/tutorial/uiswing/misc/action.html) 버튼을 제공합니다. 'actionPerformed()'메소드는 애플리케이션의 데이터 모델을 쿼리하고 확장 된 뷰를 업데이트합니다. Action은 버튼, 메뉴, 툴바 또는 포커스 리스너에서 호출 할 수 있습니다. – trashgod

2
My goal is to make something looking like a list view, where the entries 
expand and display more information when they get focus (and collapse back 
when they lose it). The expanded view contains some additional buttons. 

enter image description here enter image description here

ButtonModel에가하는 JButton를 사용하여, 아주 좋은 출력 여전히 JToggleButton에를 사용하는 것 또는 할 수

가 JPanel의 +의 MouseListener를 (지지와 함께 원래의 아이디어)가

import java.awt.*; 
import java.awt.event.*; 
import java.awt.font.*; 
import java.awt.image.BufferedImage; 
import javax.swing.*; 
import javax.swing.event.ChangeEvent; 
import javax.swing.event.ChangeListener; 

public class CollapsablePanelTest { 

    public static void main(String[] args) { 
     CollapsablePanel cp = new CollapsablePanel("test", buildPanel()); 
     JFrame f = new JFrame(); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.getContentPane().add(new JScrollPane(cp)); 
     f.setSize(360, 300); 
     f.setLocation(200, 100); 
     f.setVisible(true); 
    } 

    public static JPanel buildPanel() { 
     GridBagConstraints gbc = new GridBagConstraints(); 
     gbc.insets = new Insets(2, 1, 2, 1); 
     gbc.weightx = 1.0; 
     gbc.weighty = 1.0; 
     JPanel p1 = new JPanel(new GridBagLayout()); 
     p1.setBackground(Color.blue); 
     gbc.gridwidth = GridBagConstraints.RELATIVE; 
     p1.add(new JButton("button 1"), gbc); 
     gbc.gridwidth = GridBagConstraints.REMAINDER; 
     p1.add(new JButton("button 2"), gbc); 
     gbc.gridwidth = GridBagConstraints.RELATIVE; 
     p1.add(new JButton("button 3"), gbc); 
     gbc.gridwidth = GridBagConstraints.REMAINDER; 
     p1.add(new JButton("button 4"), gbc); 
     return p1; 
    } 

    private CollapsablePanelTest() { 
    } 
} 

class CollapsablePanel extends JPanel { 

    private static final long serialVersionUID = 1L; 
    private boolean selected; 
    private JPanel contentPanel_; 
    private HeaderPanel headerPanel_; 

    private class HeaderPanel extends JButton /*JToggleButton //implements MouseListener*/ { 

     private static final long serialVersionUID = 1L; 
     private String __text; 
     private Font __font; 
     private BufferedImage open, closed; 
     private final int OFFSET = 30, PAD = 5; 

     public HeaderPanel(String text) { 
      //addMouseListener(this); 
      __text = text; 
      setText(__text); 
      __font = new Font("sans-serif", Font.PLAIN, 12); 
      // setRequestFocusEnabled(true); 
      setPreferredSize(new Dimension(200, 30)); 
      int w = getWidth(); 
      int h = getHeight(); 
      /*try { 
      open = ImageIO.read(new File("images/arrow_down_mini.png")); 
      closed = ImageIO.read(new File("images/arrow_right_mini.png")); 
      } catch (IOException e) { 
      e.printStackTrace(); 
      }*/ 
      getModel().addChangeListener(new ChangeListener() { 

       @Override 
       public void stateChanged(ChangeEvent e) { 
        ButtonModel model = (ButtonModel) e.getSource(); 
        if (model.isRollover()) { 
         toggleSelection(); 
        } else if (model.isPressed()) { 
         toggleSelection();//for JToggleButton 
        } 
       } 
      }); 
     } 

     /*@Override 
     protected void paintComponent(Graphics g) { 
     super.paintComponent(g); 
     Graphics2D g2 = (Graphics2D) g; 
     g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
     int h = getHeight(); 
     ///if (selected) 
     //g2.drawImage(open, PAD, 0, h, h, this); 
     //else 
     //g2.drawImage(closed, PAD, 0, h, h, this); 
     // Uncomment once you have your own images 
     g2.setFont(font); 
     FontRenderContext frc = g2.getFontRenderContext(); 
     LineMetrics lm = font.getLineMetrics(__text, frc); 
     float height = lm.getAscent() + lm.getDescent(); 
     float x = OFFSET; 
     float y = (h + height)/2 - lm.getDescent(); 
     g2.drawString(__text, x, y); 
     } 
     @Override 
     public void mouseClicked(MouseEvent e) { 
     toggleSelection(); 
     } 

     @Override 
     public void mouseEntered(MouseEvent e) { 
     } 

     @Override 
     public void mouseExited(MouseEvent e) { 
     } 

     @Override 
     public void mousePressed(MouseEvent e) { 
     } 

     @Override 
     public void mouseReleased(MouseEvent e) { 
     }*/ 
    } 

    public CollapsablePanel(String text, JPanel panel) { 
     super(new GridBagLayout()); 
     GridBagConstraints gbc = new GridBagConstraints(); 
     gbc.insets = new Insets(1, 3, 0, 3); 
     gbc.weightx = 1.0; 
     gbc.fill = GridBagConstraints.HORIZONTAL; 
     gbc.gridwidth = GridBagConstraints.REMAINDER; 
     selected = false; 
     headerPanel_ = new HeaderPanel(text); 
     setBackground(Color.orange); 
     contentPanel_ = panel; 
     add(headerPanel_, gbc); 
     add(contentPanel_, gbc); 
     contentPanel_.setVisible(false); 
     JLabel padding = new JLabel(); 
     gbc.weighty = 1.0; 
     add(padding, gbc); 
    } 

    public void toggleSelection() { 
     selected = !selected; 
     if (contentPanel_.isShowing()) { 
      contentPanel_.setVisible(false); 
     } else { 
      contentPanel_.setVisible(true); 
     } 
     validate(); 
     headerPanel_.repaint(); 
    } 
} 
+0

코드에 대해 고마워! 그러나 구현하고자하는 것과 관련하여 몇 가지 문제가 있습니다. 먼저 확장은 버튼 활성화에 의해 토글됩니다. 이것은 사용자가 마우스로 만들면 초점을 얻는 것과 같지만 초점이 키보드로 포커스를 설정하면 (예 : 탭 이동) 버튼이 활성화되지 않습니다. 둘째, 패널은 초점이 사라지 자마자 붕괴해야합니다. – Vlad

+0

MouseListener가 존재하지 않는 경우, ButtonModel가 javax.swing.Timer (지연된 표시, 또는 복수의 액션, 또는 가장 가까운 JComponents로부터의 가능한 concurency) 및 KeyBinding (TAB는 아니고, F1, F2 또는 F5) 그렇게 졸졸 수 있습니다,하지만 난 여전히 컨테이너 JPanel – mKorbel

+0

점프 대신 JWindow 구현하는 것이 더 좋을 것이라고 생각 정확히 어떤 MouseListener 당신이 언급 한 이해가 잘 모르겠습니다. ButtonModel이 작동하려면 버튼이 포커스가있는 동안 또는 확장 된 패널의 항목에 포커스가있는 한 버튼이 눌린 상태인지 확인해야합니다. 쉬운 방법이 있습니까? 당분간, 예제 에서처럼 포커스를 추적하고 null에 대한 변경 사항을 추가로 무시합니다. UI는 나를 정의하지 않으므로 점프 패널은 피할 수없는 것처럼 보입니다. – Vlad

관련 문제