سؤال

كنت بحاجة إلى jbutton مع قائمة نمط المنسدلة المرفقة. لذلك أخذت jpopupmenu وتعلق عليه إلى JButton بالطريقة التي يمكنك رؤيتها في التعليمات البرمجية أدناه. ما يجب القيام به هو:

  • إظهار المنبثقة عند النقر
  • إخفاءه إذا تم النقر فوق المرة الثانية
  • إخفاءها إذا تم تحديد عنصر في المنبثقة
  • إخفاءها إذا نقر المستخدم في مكان آخر في الشاشة

تعمل هذه الأشياء الأربعة هذه، ولكن بسبب العلم المنطقي الذي أستخدمه، إذا نقر المستخدم في مكان آخر أو حدد عنصرا، يجب أن أضغط مرتين على الزر قبل ظهوره مرة أخرى. لهذا السبب حاولت إضافة قائمة focallistener (التي لا تستجيب تماما) لإصلاح ذلك وتعيين العلم كاذبة في هذه الحالات.

تحرير: آخر محاولة في وظيفة الإجابة ...

فيما يلي المستمعون: (إنه في فئة تمتد JButton، لذلك المستمع الثاني على JButton.)

// Show popup on left click.
menu.addFocusListener(new FocusListener() {
 @Override
 public void focusLost(FocusEvent e) {
  System.out.println("LOST FOCUS");
  isShowingPopup = false;
 }

 @Override
 public void focusGained(FocusEvent e) {
  System.out.println("GAINED FOCUS");
 }
});

addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {
  System.out.println("isShowingPopup: " + isShowingPopup);
  if (isShowingPopup) {
   isShowingPopup = false;
  } else {
   Component c = (Component) e.getSource();
   menu.show(c, -1, c.getHeight());
   isShowingPopup = true;
  }
 }
});

لقد قاتل مع هذا لفترة طويلة جدا الآن. إذا كان شخص ما يمكن أن يعطيني فكرة عن الخطأ في هذا، فسيكون ذلك رائعا!

شكرًا!

شفرة:

public class Button extends JButton {

    // Icon.
    private static final ImageIcon ARROW_SOUTH = new ImageIcon("ArrowSouth.png");

    // Unit popup menu.
    private final JPopupMenu menu;

    // Is the popup showing or not?
    private boolean isShowingPopup = false;

    public Button(int height) {
        super(ARROW_SOUTH);
        menu = new JPopupMenu(); // menu is populated somewhere else

        // FocusListener on the JPopupMenu
        menu.addFocusListener(new FocusListener() {
            @Override
            public void focusLost(FocusEvent e) {
                System.out.println("LOST FOCUS");
                isShowingPopup = false;
            }

            @Override
            public void focusGained(FocusEvent e) {
                System.out.println("GAINED FOCUS");
            }
        });

        // ComponentListener on the JPopupMenu
        menu.addComponentListener(new ComponentListener() {
            @Override
            public void componentShown(ComponentEvent e) {
                System.out.println("SHOWN");
            }

            @Override
            public void componentResized(ComponentEvent e) {
                System.out.println("RESIZED");
            }

            @Override
            public void componentMoved(ComponentEvent e) {
                System.out.println("MOVED");
            }

            @Override
            public void componentHidden(ComponentEvent e) {
                System.out.println("HIDDEN");
            }
        });

        // ActionListener on the JButton
        addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("isShowingPopup: " + isShowingPopup);
                if (isShowingPopup) {
                    menu.requestFocus();
                    isShowingPopup = false;
                } else {
                    Component c = (Component) e.getSource();
                    menu.show(c, -1, c.getHeight());
                    isShowingPopup = true;
                }
            }
        });

        // Skip when navigating with TAB.
        setFocusable(true); // Was false first and should be false in the end.

        menu.setFocusable(true);
    }

}
هل كانت مفيدة؟

المحلول

فيما يلي نهج آخر لا سيء للغاية من الاختراق، إن لم يكن أنيقا، والذي، بقدر ما أستطيع أن أقول، يعمل. أولا، في القمة جدا، أضفت منطقية ثانية تسمى showPopup.

ال FocusListener يجب أن يكون كما يلي:

    menu.addFocusListener(new FocusListener() {
        @Override
        public void focusLost(FocusEvent e) {
            System.out.println("LOST FOCUS");
            isShowingPopup = false;
        }

        @Override
        public void focusGained(FocusEvent e) {
            System.out.println("GAINED FOCUS");
            isShowingPopup = true;
        }
    });

ال isShowingPopup لا يتغير Boolean في أي مكان آخر - إذا كان يحصل على تركيزه، فهذا يفترض أنه يظهر، وإذا كان يفقد التركيز، فهذا يفترض أنه ليس كذلك.

بعد ذلك، ActionListener على الزر مختلف:

   addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("isShowingPopup: " + isShowingPopup);
            if (showPopup) {
                Component c = (Component) e.getSource();
                menu.show(c, -1, c.getHeight());
                menu.requestFocus();
            } else {
                showPopup = true;
            }
        }
    });

الآن يأتي قليلا حقا. انه MouseListener على الزر:

    addMouseListener(new MouseAdapter() {
        @Override
        public void mousePressed(MouseEvent e) {
            System.out.println("ispopup?: " + isShowingPopup);
            if (isShowingPopup) {
                showPopup = false;
            }
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            showPopup = true;
        }
    });

في الأساس، mousePressed يحصل دعا قبل أن تخسر القائمة التركيز، لذلك isShowingPopup يعكس ما إذا كان عرض المنبثقة قبل الضغط على الزر. بعد ذلك، إذا كانت القائمة موجودة، فقد وضعنا للتو showPopup ل false, ، بحيث actionPerformed لا تظهر الطريقة القائمة بمجرد استدعاءها (بعد السماح للماوس).

هذا يتصرف كما هو متوقع في كل حالة ولكن واحد: إذا كانت القائمة تظهر وضغط المستخدم على الماوس على الزر ولكن أصدره خارجها، actionPerformed لم يتم استدعاؤه أبدا. هذا يعني ذلك showPopup ظلت خاطئة ولم تظهر القائمة في المرة التالية التي تم فيها الضغط على الزر. لإصلاح هذا، mouseReleased طريقة إعادة تعيين showPopup. وبعد ال mouseReleased يتم استدعاء الأسلوب بعد actionPerformed, ، بقدر ما أستطيع أن أقول.

لقد لعبت مع الزر الناتج عن القليل، قم بكل الأشياء التي يمكنني التفكير فيها في الزر، وعملت كما هو متوقع. ومع ذلك، أنا لست متأكدا 100٪ من أن الأحداث ستحدث دائما بنفس الترتيب.

في نهاية المطاف، أعتقد أن هذا هو، على الأقل، يستحق المحاولة.

نصائح أخرى

إليك متغير من اقتراح "الاختراق الكبير" العنبر شاه الذي صنعته للتو. بدون علم المنشأ ...

إنها ليست مقاومة للرصاص، لكنها تعمل بشكل جيد تماما حتى يأتي شخص ما بنقرة بطيئة بشكل لا يصدق لإغلاق المنبثقة (أو نقرة ثانية سريعة للغاية لإعادة فتحها ...).

public class Button extends JButton {

 // Icon.
 private static final ImageIcon ARROW_SOUTH = new ImageIcon("ArrowSouth.png");

 // Popup menu.
 private final JPopupMenu menu;

 // Last time the popup closed.
 private long timeLastShown = 0;

 public Button(int height) {
  super(ARROW_SOUTH);
  menu = new JPopupMenu(); // Populated somewhere else.

  // Show and hide popup on left click.
  menu.addPopupMenuListener(new PopupMenuListener() {
   @Override
   public void popupMenuWillBecomeInvisible(PopupMenuEvent arg0) {
    timeLastShown = System.currentTimeMillis();
   }
   @Override public void popupMenuWillBecomeVisible(PopupMenuEvent arg0) {}
   @Override public void popupMenuCanceled(PopupMenuEvent arg0) {}
  });
  addActionListener(new ActionListener() {
   @Override
   public void actionPerformed(ActionEvent e) {
    if ((System.currentTimeMillis() - timeLastShown) > 300) {
     Component c = (Component) e.getSource();
     menu.show(c, -1, c.getHeight());
    }
   }
  });

  // Skip when navigating with TAB.
  setFocusable(false);
 }

}

كما قلت في التعليقات، هذا ليس الحل الأكثر أناقة، لكنه بسيط للغاية ويعمل في 98٪ من الحالات.

مفتوحة للاقتراحات!

يمكنك استخدام jpopupmenu.isvisible () بدلا من المتغير المنطقي الخاص بك للتحقق من الحالة الحالية من القائمة المنبثقة.

هل حاولت إضافة ComponentListener إلى JPopupMenu, ، حتى تعرف متى يتم عرضها ومخفية (وتحديث isShowingPopup العلامات وفقا لذلك)؟ لست متأكدا من الاستماع للتغيرات التركيز هو بالضرورة النهج الصحيح.

ما تحتاجه هو popupmenulistener:

        menu.addPopupMenuListener(new PopupMenuListener() {

            @Override
            public void popupMenuWillBecomeVisible(PopupMenuEvent arg0) {

            }

            @Override
            public void popupMenuWillBecomeInvisible(PopupMenuEvent arg0) {
                System.out.println("MENU INVIS"); 
                isShowingPopup = false;     
            }

            @Override
            public void popupMenuCanceled(PopupMenuEvent arg0) {
                System.out.println("MENU CANCELLED"); 
                isShowingPopup = false;                     
            }
        });

أدرجت هذا في التعليمات البرمجية الخاصة بك والتحقق من أنه يعمل.

حسنا، لا أستطيع التأكد من عدم رؤية كل التعليمات البرمجية الخاصة بك، لكن هل من الممكن أن يحصل المنبثقة أبدا على التركيز على الإطلاق؟ لقد واجهت مشاكل مع الأشياء "عدم التركيز بشكل صحيح في التأرجح من قبل، لذلك يمكن أن يكون الجاني. حاول الاتصال setFocusable(true) في القائمة ثم الاتصال requestFocus() عند إصدار القائمة تظهر.

جربت إجابة Tikhon Jelvis (تقديم مزيج ذكي من Focuslistener و Mouselistener). لا يعمل من أجلي على Linux (Java7 / GTK). :-(

قراءة http://docs.oracle.com/javase/7/docs/api/javax/swing/jcomponent.html#requestfocus٪28٪29. هناك مكتوب "لاحظ أن استخدام هذه الطريقة محبط لأن سلوكه يعتمد على النظام الأساسي."

قد يكون الأمر أن ترتيب مكالمات المستمع المتغيرة مع Java7 أو يتغير مع GTK مقابل Windows. لن أوصي بهذا الحل إذا كنت تريد أن تكون منصة مستقلة.

راجع للشغل: قمت بإنشاء حساب جديد على Stackoverflow لإعطاء هذا التلميح. يبدو أنني لا يسمح لي للتعليق على إجابته (بسبب السمعة). ولكن يبدو أن لدي زر لتعديله. هذا stackoverflow هو شيء مضحك جدا. :-)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top