Focus setzen und Reihenfolge festlegen

Status
Nicht offen für weitere Antworten.

thE_29

Top Contributor
Da diese Focus-Geschichten manchmal recht verwirrend sein können hier mal eine kurze Anleitung wie man es machen könnte.


Focus setzen

In erster Linie gibt es mal 2 Befehle die einem den Focus verschaffen können:


JComponent.requestFocus
Verschafft einem den Focus und dem ParentFenster auch (falls dieses den Focus noch nicht hat).

JComponent.requestFocusInWindow()
Verschafft einem den Focus wenn das ParentFenster bereits den Focus besitzt (ansonsten nicht).


Ein großes Problem stellt sich zeitweise unter Linux da.

Gleich nachdem man das Fenster sieht, hat nichts einen Focus. Aber wenn man ganz schnell hinsieht, hat man gesehen dass die Komponente der man eigentlich gesagt hat, du sollst den Focus haben ganz kurz den Focus hat und dann wieder nicht.

Soweit ich das rausgefunden habe, ist das so, weil er mit dem Zeichnen nicht so nachkommt und die Komponente zwar zuerst den Focus hat, Linux (bzw KDE 3.0) das ganze aber erst neu zeichnen lässt und somit das ganze Fenster den Focus besitzt (also mehr keine Komponente).

Dieses Problem kann man entweder kompliziert mit synchronized und notfiyAll oder mit einem Timer (die etwas unsaubere Lösung).

Gegebenenfalls sollte man einen Timer nach der letzten add Methode aufrufen, der dann einem den Focus setzt.

Für die Methoden mit synchronized könnte man so vorgehen.


Synchronized Focus setzen

Zuerst überschreibt man die paint - Methode

Code:
  /**************************************************************************
   * Ueberschriebene paint Methode
   * @param g Graphics
   *************************************************************************/
  public void paint(Graphics g) 
  {
    super.paint(g);
    if (! bPaintCalled) <--boolsche Klassenvariable die am Anfang false gesetzt wird
    {
      bPaintCalled = true;
      synchronized (this) { notifyAll();
      }
    }
  }

Am Ende (also nachdem man show gesagt hat) fügt man noch diese Zeilen hinzu (macht man es vorher, wird nie etwas aufgebaut).

Code:
    if (! EventQueue.isDispatchThread()) {
      synchronized (this) {
        while (! this.bPaintCalled) { //<- solange bPaintCalled nicht fertig aufgerufen wurde (solange ist es false)
          try {
            this.wait(); <-- solange warte
          } catch (InterruptedException e) {}
        }
        JComponet.requestFocus(); //<-- und hier dann der Komponente den Focus verschaffen
      }
    }



Focusreihenfolge setzen

Um eine gewisse Focus-Reihenfolge festlegen zu können gab es früher mal die Methoden

JComponent.setNextFocusableComponent
Diese wurde aber durch die FocusTraversalPolicy ersetzt.

Aber wie kann man diese benutzen?

In meinem Bsp. setzt die FocusTraversalPolicy einfach die sequentiellie Reihenfolge fest. Man könnte es aber je nach Bedarf abändern und sich seine Reihenfolge selbst zusammenbauen oder nach bestimmten Abfragekriterien weiterverarbeiten (zB isEnabled(), oder dergleichen).

Man schreibt sich einfach eine Funktion die einem die FocusTraversalPolicy zurückgibt.

Bsp.:

Code:
  /****************************************************************************
   * Gibt die Reihenfolge der in order[] gespeicherten Componenten als FocusTraversalPolicy Objekt zurück
   * @param order  sind die Componenten in richtiger Reihenfolge und notfalls muss eine Typkonvertierung
   * mittels (JComponent) durchgeführt werden
   * @return  das Objekt mit dem man mit setFocusTraversalPolicy(FocsTraersalPolicy) und aktivieren mit
   * setFocusCycleRoot(true)
   ***************************************************************************/
  public static FocusTraversalPolicy getFocusTraversal(final JComponent order[])
  {
    FocusTraversalPolicy policy = new FocusTraversalPolicy()
    {
      java.util.List list = java.util.Arrays.asList(order);
      public java.awt.Component getFirstComponent(java.awt.Container
                                                  focusCycleRoot)
      {
        return order[0];
      }

      public java.awt.Component getLastComponent(java.awt.Container
                                                 focusCycleRoot)
      {
        return order[order.length - 1];
      }

      public java.awt.Component getComponentAfter(java.awt.Container
                                                  focusCycleRoot,
                                                  java.awt.Component aComponent)
      {
        int index = list.indexOf(aComponent);
        return order[ (index + 1) % order.length];
      }

      public java.awt.Component getComponentBefore(java.awt.Container
          focusCycleRoot,
          java.awt.Component aComponent)
      {
        int index = list.indexOf(aComponent);
        return order[ (index - 1 + order.length) % order.length];
      }

      public java.awt.Component getDefaultComponent(java.awt.Container
          focusCycleRoot)
      {
        return order[0];
      }

      public java.awt.Component getInitialComponent(java.awt.Window window)
      {
        return order[0];
      }
    };
    return policy;
  }

Diese Funktion gibt jetzt einfach die FocusTravesalPolicy zurück die für die übergebenen Komponenten erstellt worden sind.

Aufruf:

Code:
    /*Setzt die richtige Tabulatorenreihenfolge */
    FocusTraversalPolicy policy = getFocusTraversal(
        new JComponent[]
        {button1, textfield1, button3,textarea1}); //hier alles eintragen was man focussieren will

    setFocusTraversalPolicy(policy); //setzen
    setFocusCycleRoot(true);//enablen
 

thE_29

Top Contributor
So, habe die Funktion getFocusTravsalPolicy(...) ein bißchen geändert, sodaß er weiterspringen soll, falls eine Komponente in der Liste disabled oder invisible (oder gar nicht angezeigt wird) ist.

Folgender Code (ich habe ihn nicht zu 100% getestet, also kanns sein das er vielleicht net zu 100% klappt - habe nur 1h getestet!)

Code:
  /****************************************************************************
   * Gibt die Reihenfolge der in order[] gespeicherten Componenten als FocusTraversalPolicy Objekt zurück
   * @param order  sind die Componenten in richtiger Reihenfolge und notfalls muss eine Typkonvertierung
   * mittels (JComponent) durchgeführt werden
   * @return  das Objekt mit dem man mit setFocusTraversalPolicy(FocsTraersalPolicy) und aktivieren mit
   * setFocusCycleRoot(true)
   ***************************************************************************/
  public static FocusTraversalPolicy getFocusTraversal( final JComponent order[])
  {
    FocusTraversalPolicy policy = new FocusTraversalPolicy()
    {
      java.util.List list = java.util.Arrays.asList(order);
      public java.awt.Component getFirstComponent(java.awt.Container
                                                  focusCycleRoot)
      {
        return order[0];
      }

      public java.awt.Component getLastComponent(java.awt.Container
                                                 focusCycleRoot)
      {
        return order[order.length - 1];
      }

      public java.awt.Component getComponentAfter(java.awt.Container
                                                  focusCycleRoot,
                                                  java.awt.Component aComponent)
      {
        int index = 0,x = -1;
        index = list.indexOf(aComponent);
        index++; //automatisch erhöht, sodaß er unten nichts wegzeiehn muß
        //er geht rein entweder wenn es disabled ist oder wenn es nicht angezeigt wird
        if(!order[index % order.length].isEnabled() || !order[index % order.length].isVisible())
        {
          x = index;
          index = -1;
          //zuerst die Schleife nach hinten
          for (; x != order.length; x++)
          {
            if(order[x].isEnabled() && order[x].isVisible())
            {
              index = x;
              break;
            }
          }
          if(index == -1)
          {
            x = list.indexOf(aComponent);
            for(int y = 0; y <= x; y++)
            {
              if(order[y].isEnabled() && order[x].isVisible())
              {
                index = y;
                break;
              }
            }
          }
        }
        return order[ index % order.length];
      }

      public java.awt.Component getComponentBefore(java.awt.Container
          focusCycleRoot,
          java.awt.Component aComponent)
      {
        int index = list.indexOf(aComponent), x = -1;
        index--;
        if(!order[(index + order.length) % order.length].isEnabled() || !order[(index + order.length) % order.length].isVisible())
        {
          x = index;
          index = -1;
          for(; x >= 0; x--)
          {
            if(order[x].isEnabled() && order[x].isVisible())
            {
              index = x;
              break;
            }
          }
          //wenn sich nix getan hat
          if(index == -1)
          {
            x = list.indexOf(aComponent);
            for(int y = order.length -1; y >= x; y--)
            {
              if(order[y].isEnabled() && order[x].isVisible())
              {
                index = y;
                break;
              }
            }
          }

        }
        return order[ (index + order.length) % order.length];
      }

      public java.awt.Component getDefaultComponent(java.awt.Container
          focusCycleRoot)
      {
        return order[0];
      }

      public java.awt.Component getInitialComponent(java.awt.Window window)
      {
        return order[0];
      }
    };
    return policy;
  }


Anzuwenden ist er gleich wie der obige Code nur das er halt die disableden oder nicht angezeigten Komponenten überspringt!
 
Status
Nicht offen für weitere Antworten.

Oben