2012-03-12 12 views
7

Possiedo un'applicazione Java Swing che genera finestre di dialogo figlio con controlli di testo. E il problema è che quando si cambia il layout della tastiera nella finestra di dialogo figlio, esso torna indietro subito dopo la chiusura della finestra di dialogo.Preservare il layout della tastiera nell'app swing?

Quello che mi serve è che il layout della scheda rimanga dopo essere stato commutato sia che sia stato inserito nel frame principale o in un frame secondario.

Ecco uno SSCCE che illustra il problema:

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 

public class InheritInputContext { 

    public static void main(String[] arg) { 
     final MainFrame mainFrame = new MainFrame(); 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       mainFrame.setPreferredSize(new Dimension(300, 400)); 
       mainFrame.pack(); 
       mainFrame.setLocationRelativeTo(null); 
       mainFrame.setVisible(true); 
      } 
     }); 

    } 
} 


class MainFrame extends JFrame { 

    MainFrame() { 
     setLayout(new BorderLayout()); 
     JTextArea textArea = new JTextArea(); 
     add(textArea, BorderLayout.CENTER); 

     JButton dialogBtn = new JButton("Dialog"); 
     add(dialogBtn, BorderLayout.SOUTH); 
     dialogBtn.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) { 
       ChildDialog cd = new ChildDialog(MainFrame.this); 
       cd.setPreferredSize(new Dimension(200, 200)); 
       cd.setLocationRelativeTo(MainFrame.this); 
       cd.pack(); 
       cd.setVisible(true); 
      } 
     }); 
    } 
} 


class ChildDialog extends JDialog { 

    ChildDialog(Window w) { 
     super(w); 
     JTextArea textArea = new JTextArea(); 
     getContentPane().add(textArea); 
    } 
} 
+1

Stai parlando del layout della tastiera del sistema operativo? Un po 'confuso qui. –

risposta

2

Ok, ho appena sistemati con questa soluzione:

Aggiunto un ascoltatore a java toolkit nel metodo main() in questo modo:

AWTEventListener awtWindowListener = new AWTEventListener() { 
    @Override 
    public void eventDispatched(AWTEvent event) { 
     if (event instanceof WindowEvent) { 
      if (WindowEvent.WINDOW_CLOSED == event.getID() 
        || WindowEvent.WINDOW_CLOSING == event.getID()) { 
       Window child = ((WindowEvent) event).getWindow(); 
       Window parent = SwingUtilities.getWindowAncestor(child); 
       if (parent == null) return; 
       InputContext childIC = child.getInputContext(); 
       parent.getInputContext().selectInputMethod(childIC.getLocale()); 
      } 
     } 

    } 
}; 

Toolkit.getDefaultToolkit().addAWTEventListener(awtWindowListener, AWTEvent.WINDOW_EVENT_MASK); 

Funziona su tutti i dialoghi secondari generati con finestra padre come parametro costruttore Su evento close Locale da InputContext della finestra di dialogo figlio viene inserito in InputContext della finestra padre.

Potrebbe esserci un modo migliore però.

1

Stai solo cercando un modo per avere alcun cambiamento layout di influenzare l'applicazione a livello globale?

In tal caso, un approccio consiste nel creare un listener personalizzato, fare in modo che i vari componenti interessati alla modifica del layout registrino il loro interesse in tali eventi e quindi attivare un evento di layout di modifica che attiva la modifica di tutti i componenti quando è cambiamenti in ognuno di essi.

Un altro modo per farlo sarebbe archiviare le proprietà di layout in un oggetto accessibile a qualsiasi componente e aggiornarle periodicamente tramite un timer. Ciò sarebbe meno desiderabile, tuttavia, poiché ci sarebbero probabilmente molti aggiornamenti inutili rispetto alla modalità di funzionamento "solo aggiornamento su evento". Immagino che gli utenti della tua applicazione non cambieranno il layout della tastiera più di una volta o due per sessione (rispetto a ogni 5 secondi)?

Un altro, terzo modo, per fare ciò è avere le impostazioni del layout della tastiera memorizzate a livello di applicazione e caricate all'avvio. Quindi, quando si verifica una modifica al layout della tastiera, chiedere all'utente di riavviare l'applicazione affinché le modifiche abbiano effetto a livello globale.

+0

Sì, sto cercando un modo per conservare qualsiasi modifica del layout apportata nell'applicazione, per evitare la necessità di modificare il layout più e più volte in ogni nuova finestra di dialogo. In questo momento sto esplorando l'opzione di utilizzare AWTEventListener a livello di app, per evitare di scrivere codice aggiuntivo per ogni frame figlio – yggdraa

+0

L'aggiornamento del timer è fuori questione, sarebbe sovraccarico. Chiedere l'app-ricarica è fuori questione anche, l'utente deve cambiare tra 2 lingue tutte le volte. – yggdraa

+0

Ho cercato anche in NetBeans e IntellijIdea, c'è la stessa cosa. Apri le Impostazioni, ad esempio, cambia la lingua di input, chiudi Impostazioni e il layout della tastiera torna a quello che era prima. In altre applicazioni (browser, mssql, blocco note) il cambiamento del layout della tastiera è consistente su tutta l'app. – yggdraa

1

Sì e no: il codice di yggdraa di Mar 13 ha funzionato correttamente su Windows ma non è riuscito su Linux.

Potrebbe non esserci nessuna soluzione universale per Linux: niente come Windows GetKeyboardLayout() e ActivateKeyboardLayout() lì. Tuttavia, alcuni hacker dipendenti dalla configurazione potrebbero essere possibili, ad esempio analizzare l'output di xset (details here) e forzare il layout, ad esempio, sul tasto su/giù.

Nell'esempio precedente, il codice di selezione dell'ingresso in eventDispatched() viene chiamato troppo tardi - quando la tastiera del sistema operativo è già tornata al sistema predefinito US.

Alcuni tentativi di forza bruta non hanno funzionato: myParticularJField.setLocale (myForcedLocale) dal gestore di attivazione del campo viene immediatamente annullato alla prima pressione del tasto. Lo stesso per forzare le impostazioni locali di primo livello (JFrame/JDialog).

Aggiornamento:

Abbiamo solo Windows nella produzione, in modo da rendere questo lavoro sotto Linux è impraticabile: troppo sforzo.

Nel caso, un sottoprodotto. Questo determina correttamente quale layout è attualmente attivo: predefinito o alternativo ("locale"). Esso non può distinguere tra diversi layout alternativi:

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 

public class LinuxKeyboardLayoutStatus { 

    public enum LayoutType { DEFAULT, LOCAL } 

    public LinuxKeyboardLayoutStatus.LayoutType getCurrentKeyboardLayoutType() throws IOException, InterruptedException { 
     String[] command = createCommand(); 
     Process p = Runtime.getRuntime().exec(command); 
     BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream())); 
     String l = r.readLine(); 
     r.close(); 
     p.waitFor(); 
     return decodeLayoutType(l); 
    } 

    protected String[] createCommand() { 
     return new String[] { "/bin/sh", "-c", "xset -q | grep LED | awk '{ print $10 }' | cut -c5" }; 
    } 

    protected LinuxKeyboardLayoutStatus.LayoutType decodeLayoutType(String commandOutput) { 
     return 
      commandOutput != null && !commandOutput.equals("0") ? LayoutType.LOCAL : LayoutType.DEFAULT; 
    } 

} 

Aggiornamento:

In Ubuntu, il cambiamento indietro al layout predefinito accade a livello X Window (eventi DBus). Una soluzione: per disattivare i layout separati per ogni finestra: Impostazioni => Tastiera => Layout, deselezionare "Layout separato per ogni finestra".

Problemi correlati