草庐IT

java - jTabbedPane 将焦点转移到下一张纸

coder 2024-04-04 原文

问题描述

我目前正在编写一个用于手动将数据从一张纸复制到数据库的应用程序。这个应用程序有很多小部件,用户可以在其中输入数据。为了使 UI 保持整洁,我决定使用选项卡式 Pane ,将输入字段分隔成逻辑单元。

该应用程序最重要的特性是它应该可以单独通过键盘使用。所以你应该能够通过击键来切换标签。默认情况下,这可以使用 CTRL+PgUp/PgDown。但是,作为额外的便利,我想在用户将焦点从当前选项卡上的最后一个小部件移出时立即激活下一个选项卡。

因此,如果用户将焦点放在最后一个文本字段上,然后按下选项卡,我想激活下一个选项卡,并将焦点放在其中的第一个小部件上。

为了解决这个问题,我将 jTabbedPane 标记为 focusCycleRootProvider 并添加了自定义 FocusTraversalPolicy。我当前的问题是:一旦我以编程方式激活下一个选项卡(使用 setSelectedIndex),这发生在 getComponentAfter 方法中,方法 getComponentAfter 第二次执行。这违背了我目前的逻辑。我似乎无法找到一种方法来防止这种情况发生。有什么想法吗?

在下面的示例中,您将看到一个 ArrayIndexOutOfBoundsException。发生这种情况是因为 getComponentAfter 被调用了两次。一次在第一个选项卡上,一次在第二个选项卡上。但是两次使用相同的组件作为参数。这意味着,第二次,for 循环将找不到匹配的组件,因此计数器 i 将与第二个选项卡中的组件 一样大+ 1. 这会导致异常。

测试可执行文件

/*
 * TestFrame.java
 *
 * Created on Apr 18, 2011, 4:37:52 PM
 */

package testrun;

import java.awt.Component;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;

/**
 *
 * @author malbert
 */
public class TestFrame extends javax.swing.JFrame {

    /** Creates new form TestFrame */
    public TestFrame() {
        initComponents();
        jTabbedPane1.setFocusTraversalPolicyProvider(true);
        jTabbedPane1.setFocusTraversalPolicy(new EasyTabberFocusTraversalPolicy(jTabbedPane1));

        jTabbedPane1.addFocusListener(new FocusAdapter() {

            @Override
            public void focusGained(FocusEvent e) {
                super.focusGained(e);
                jTabbedPane1.setSelectedIndex(0);
                Component ca = jTabbedPane1.getFocusTraversalPolicy().getFirstComponent(jTabbedPane1);
                if (ca != null) {
                    ca.requestFocusInWindow();
                }
            }
        });
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jTabbedPane1 = new javax.swing.JTabbedPane();
        jPanel1 = new javax.swing.JPanel();
        jTextField2 = new javax.swing.JTextField();
        jTextField3 = new javax.swing.JTextField();
        jPanel2 = new javax.swing.JPanel();
        jButton1 = new javax.swing.JButton();
        jButton2 = new javax.swing.JButton();
        jTextField4 = new javax.swing.JTextField();
        jTextField1 = new javax.swing.JTextField();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jTextField2.setText("jTextField2");

        jTextField3.setText("jTextField3");

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jTextField3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(289, Short.MAX_VALUE))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jTextField3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(168, Short.MAX_VALUE))
        );

        jTabbedPane1.addTab("tab1", jPanel1);

        jButton1.setText("jButton1");

        jButton2.setText("jButton2");

        jTextField4.setText("jTextField4");

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addComponent(jButton1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jButton2))
                    .addComponent(jTextField4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(165, Short.MAX_VALUE))
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jButton1)
                    .addComponent(jButton2))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jTextField4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(162, Short.MAX_VALUE))
        );

        jTabbedPane1.addTab("tab2", jPanel2);

        jTextField1.setText("jTextField1");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 376, Short.MAX_VALUE)
                    .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 251, Short.MAX_VALUE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new TestFrame().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton jButton1;
    private javax.swing.JButton jButton2;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JTabbedPane jTabbedPane1;
    private javax.swing.JTextField jTextField1;
    private javax.swing.JTextField jTextField2;
    private javax.swing.JTextField jTextField3;
    private javax.swing.JTextField jTextField4;
    // End of variables declaration//GEN-END:variables

}

遍历策略

package testrun;

import java.awt.Component;
import java.awt.Container;
import javax.swing.JTabbedPane;
import javax.swing.LayoutFocusTraversalPolicy;

/**
 *
 * @author malbert
 */
public class EasyTabberFocusTraversalPolicy extends LayoutFocusTraversalPolicy {

    private final JTabbedPane container;
    private int currentTab = 0;

    public EasyTabberFocusTraversalPolicy(JTabbedPane container) {
        this.container = container;
    }

    @Override
    public Component getComponentAfter(Container aContainer, Component aComponent) {
        System.out.println("after " + aComponent);
        Component comp = container.getComponentAt(currentTab);
        if (Container.class.isInstance(comp)) {
            Component[] components = ((Container) comp).getComponents();
            int i = 0;
            for (i = 0; i < components.length; i++) {
                if (!components[i].isEnabled() || !components[i].isFocusable()) {
                    continue;
                }
                if (components[i].equals(aComponent)) {
                    break;
                }
            }
            if (i == components.length - 1) {
                // we reached the end. Go to the next tab!
                currentTab = currentTab + 1;
                Component fc = firstComponentInCurrentTab();
                activateTab(currentTab);
                return fc;
            } else {
                return components[i + 1];
            }
        } else {
            return comp;
        }
    }

    @Override
    public Component getComponentBefore(Container aContainer, Component aComponent) {
        System.out.println("before");
        return super.getComponentBefore(aContainer, aComponent);
    }

    @Override
    public Component getFirstComponent(Container aContainer) {
        System.out.println("first");
        return firstComponentInCurrentTab();
    }

    @Override
    public Component getLastComponent(Container aContainer) {
        System.out.println("last");
        return lastComponentInCurrentTab();
    }

    private Component firstComponentInCurrentTab() {
        Component comp = container.getComponentAt(currentTab);
        if (comp instanceof Container) {
            Component[] components = ((Container) comp).getComponents();
            if (components.length == 0) {
                return null;
            }
            return components[0];
        } else {
            return comp;
        }
    }

    private Component lastComponentInCurrentTab() {
        Component comp = container.getComponentAt(currentTab);
        if (comp instanceof Container) {
            Component[] components = ((Container) comp).getComponents();
            if (components.length == 0) {
                return null;
            }
            return components[components.length - 1];
        } else {
            return comp;
        }
    }

    private void activateTab(int index) {
        // wrap around
        if (index < 0) {
            index = container.getTabCount() - 1;
        } else if (index > container.getTabCount() - 1) {
            index = 0;
        }
        currentTab = index;
        container.setSelectedIndex(index);
    }
}

最佳答案

FTP 是......真正令人痛苦的痛苦;-)

不完全确定到底是什么导致了 NPE,只是简化了您的代码(如果您有访问最后一个的方法,最好使用它):

@Override
public Component getComponentAfter(Container aContainer, Component aComponent) {
    System.out.println("after " + aComponent);
    Component last = lastComponentInCurrentTab();
    if (aComponent == last) {
      // we reached the end. Go to the next tab!
      currentTab = currentTab + 1;
      Component fc = firstComponentInCurrentTab();
      activateTab(currentTab);
      return fc;

    }
    return super.getComponentAfter(aContainer, aComponent);
}

AIOOB 消失了,显示了第二个选项卡……但没有聚焦。发生这种情况的原因是(只是猜测,因为我在其他设置中看到过类似的东西,没有记住所有肮脏的细节;)在返回时第二个选项卡的第一个组件还没有资格接收焦点,所以它进一步转移到选项卡外的文本。

编辑:必须修改我的猜测 - 经过一些挖掘,问题似乎是对 getFirst/LastComponent 的错误覆盖。他们必须返回所有选项卡的第一个/最后一个,即第一个始终是第一个选项卡的第一个,最后一个始终是最后一个选项卡的最后一个。这是第一个片段:

@Override
public Component getFirstComponent(Container aContainer) {
    System.out.println("first");
    return firstComponentInTab(0) ;//firstComponentInCurrentTab();
}

private Component firstComponentInCurrentTab() {
    int tabIndex = currentTab;
    return firstComponentInTab(tabIndex);
}

private Component firstComponentInTab(int tabIndex) {
    Component comp = container.getComponentAt(tabIndex);
    LOG.info("comp: " + comp.getName());
    if (comp instanceof Container) {
        Component[] components = ((Container) comp).getComponents();
        if (components.length == 0) {
            return null;
        }
        return components[0];
    } else {
        return comp;
    }
}

然后向前的标签看起来很好。最后需要类似的,清理留给 OP :-)

顺便说一句:好主意!

Edit2:当心 - 清理不会是微不足道的。 FTP 必须处理其子项的完整层次结构,因此仅检查直接子项很快就会中断(例如对于 JComboBox 等复合组件)

关于java - jTabbedPane 将焦点转移到下一张纸,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5704512/

有关java - jTabbedPane 将焦点转移到下一张纸的更多相关文章

  1. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  2. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  3. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  4. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  5. ETH 徘徊在 1,700 美元附近;下一步是什么? - 2

    以太坊价格分析表明横盘整理,偏向中性。价格从前一交易日的高点1,791美元回落后正在盘整。但是,有趣的是,多头在1,680美元附近持有重要支撑。多头在1,700美元的心理水平附近聚集动能,并准备在接下来的几个交易日推向1,800美元。以太坊价格显示出盘整迹象,因为它形成了多个连续的顶部形态。这种回撤可能是第二大加密货币下一轮上涨的基石。以太坊连续第二个交易日走低。过去10天,价格在1,590-1,760美元的短期区间内盘整。每日烛台高于1,800美元将维持ETH的进一步上涨。ETH价格走低日线图上,以太坊价格在上升趋势线附近获得一轮支撑。来自879.80美元低点的看涨趋势线为ETH买家提供了支

  6. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  7. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

    HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

  8. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  9. java - 为什么 ruby​​ modulo 与 java/other lang 不同? - 2

    我基本上来自Java背景并且努力理解Ruby中的模运算。(5%3)(-5%3)(5%-3)(-5%-3)Java中的上述操作产生,2个-22个-2但在Ruby中,相同的表达式会产生21个-1-2.Ruby在逻辑上有多擅长这个?模块操作在Ruby中是如何实现的?如果将同一个操作定义为一个web服务,两个服务如何匹配逻辑。 最佳答案 在Java中,模运算的结果与被除数的符号相同。在Ruby中,它与除数的符号相同。remainder()在Ruby中与被除数的符号相同。您可能还想引用modulooperation.

  10. java - Ruby 相当于 Java 的 Collections.unmodifiableList 和 Collections.unmodifiableMap - 2

    Java的Collections.unmodifiableList和Collections.unmodifiableMap在Ruby标准API中是否有等价物? 最佳答案 使用freeze应用程序接口(interface):Preventsfurthermodificationstoobj.ARuntimeErrorwillberaisedifmodificationisattempted.Thereisnowaytounfreezeafrozenobject.SeealsoObject#frozen?.Thismethodretur

随机推荐