هل هناك طريقة سهلة لتغيير سلوك عنصر تحكم Java/Swing عندما يتم التركيز عليه؟

StackOverflow https://stackoverflow.com/questions/66455

  •  09-06-2019
  •  | 
  •  

سؤال

بالنسبة لمعظم واجهات المستخدم الرسومية التي استخدمتها، عندما يتم التركيز على عنصر تحكم يحتوي على نص، يتم تحديد محتويات عنصر التحكم بالكامل.وهذا يعني أنه إذا بدأت الكتابة للتو، فإنك تقوم باستبدال المحتويات السابقة بالكامل.

مثال:لديك تحكم في الدوران تمت تهيئته بالقيمة صفر.يمكنك النقر عليه واكتب "1" القيمة الموجودة في عنصر التحكم الآن هي 1.

مع سوينغ، هذا لا يحدث.لم يتم تحديد النص الموجود في عنصر التحكم، ويظهر القيراط في نهاية أو أخرى من النص الموجود.استمرار المثال أعلاه:

باستخدام Swing JSpinner، عندما تنتقل إلى عنصر التحكم في الدوران، يكون القيراط على اليسار.تكتب "1" وتصبح القيمة في عنصر التحكم الآن 10.

هذا يقودني (والمستخدمين) إلى طريق مسدود، وأرغب في تغييره.والأهم من ذلك، أود تغييره عالميًا بحيث ينطبق السلوك الجديد على JTextField وJPasswordField وJFormattedTextField وJTextArea وJComboBox وJSpinner وما إلى ذلك.الطريقة الوحيدة التي وجدتها للقيام بذلك هي إضافة FocusAdapter إلى كل عنصر تحكم وتجاوز طريقة focusGained() للقيام بالشيء الصحيح[tm].

يجب أن تكون هناك طريقة أسهل وأقل هشاشة.لو سمحت؟

يحرر:قطعة إضافية من المعلومات لهذه الحالة بالذات.تم إنشاء النموذج الذي أعمل معه باستخدام مصمم نماذج Idea.هذا يعني أنني عادةً لا أكتب الكود لإنشاء المكونات.من الممكن أن تخبر Idea أنك تريد إنشائها بنفسك، لكن هذه مشكلة أود تجنبها.

شعار:جميع المبرمجين الجيدين كسالى في الأساس.

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

المحلول 4

بعد قراءة الردود حتى الآن (شكرًا!) مررت JPanel الأبعد إلى الطريقة التالية:

void addTextFocusSelect(JComponent component){
    if(component instanceof JTextComponent){
        component.addFocusListener(new FocusAdapter() {
                @Override
                public void focusGained(FocusEvent event) {
                    super.focusGained(event);
                    JTextComponent component = (JTextComponent)event.getComponent();
                    // a trick I found on JavaRanch.com
                    // Without this, some components don't honor selectAll
                    component.setText(component.getText());
                    component.selectAll();
                }
            });

    }
    else
    {
        for(Component child: component.getComponents()){
            if(child instanceof JComponent){
                addTextFocusSelect((JComponent) child);
            }
        }
    }
}

إنها تعمل!

نصائح أخرى

عندما كنت بحاجة إلى هذا في الماضي، قمت بإنشاء فئات فرعية من المكونات التي أردت إضافة وظيفة "المسح التلقائي" إليها أيضًا.على سبيل المثال:

public class AutoClearingTextField extends JTextField {
   final FocusListener AUTO_CLEARING_LISTENER = new FocusListener(){
      @Override
      public void focusLost(FocusEvent e) {
         //onFocusLost(e);
      }

      @Override
      public void focusGained(FocusEvent e) {
         selectAll();
      }
   };

   public AutoClearingTextField(String string) {
      super(string);
      addListener();
   }

   private void addListener() {
      addFocusListener(AUTO_CLEARING_LISTENER);      
   }
}

المشكلة الأكبر هي أنني لم أجد طريقة "جيدة" للحصول على جميع المنشئات القياسية دون كتابة التجاوزات.إن إضافتها وفرض استدعاء على addListener هو النهج الأكثر عمومية الذي وجدته.

هناك خيار آخر وهو مراقبة ContainerEvents على حاوية ذات مستوى أعلى باستخدام ContainerListeer لاكتشاف وجود عناصر واجهة مستخدم جديدة، وإضافة مستمع تركيز مطابق استنادًا إلى عناصر واجهة المستخدم التي تمت إضافتها.(على سبيل المثال:إذا كان حدث الحاوية ناتجًا عن إضافة TextField، فقم بإضافة مستمع تركيز يعرف كيفية تحديد كل النص في TextField، وما إلى ذلك.) إذا تمت إضافة حاوية، فأنت بحاجة إلى إضافة ContainerListener بشكل متكرر إلى تلك الحاوية الجديدة الحاوية الفرعية أيضًا.

وفي كلتا الحالتين، لن تحتاج إلى التلاعب بمستمعي التركيز في كود واجهة المستخدم الفعلي الخاص بك - سيتم الاهتمام بكل ذلك على مستوى أعلى.

لم أجرب هذا بنفسي (لقد انخرطت فيه منذ فترة فقط)، ولكن ربما يمكنك الحصول على المكون الحالي الذي يتم التركيز عليه باستخدام:لوحة المفاتيح FocusManager (هناك طريقة ثابتة getCurrentKeyboardFocusmanager ()) إضافة propertychangelistener إلى ذلك.من هناك، يمكنك معرفة ما إذا كان المكون هو JTextComponent وتحديد النص بأكمله.

يمكن كتابة فئة منفصلة تربط FocusListener بحقل النص المطلوب.كل ما سيفعله مستمع التركيز هو استدعاء SelectAll() على عنصر واجهة المستخدم النصي عندما يحصل على التركيز.

public class SelectAllListener implements FocusListener {
  private static INSTANCE = new SelectAllListener();

  public void focusLost(FocusEvent e) { }

  public void focusGained(FocusEvent e) {
    if (e.getSource() instanceof JTextComponent) {  
      ((JTextComponent)e.getSource()).selectAll();
    }
  };

  public static void addSelectAllListener(JTextComponent tc) {
    tc.addFocusListener(INSTANCE);
  }

  public static void removeSelectAllListener(JTextComponent tc) {
    tc.removeFocusListener(INSTANCE);
  }
}

من خلال قبول JTextComponent كوسيطة، يمكن إضافة هذا السلوك إلى JTextArea وJPasswordField وجميع مكونات تحرير النص الأخرى مباشرةً.يسمح هذا أيضًا للفصل بإضافة تحديد الكل إلى مربعات التحرير والسرد القابلة للتحرير وJSpinners، حيث قد يكون التحكم في مكون محرر النصوص أكثر محدودية.يمكن إضافة طرق الراحة:

public static void addSelectAllListener(JSpinner spin) {
  if (spin.getEditor() instanceof JTextComponent) {
    addSelectAllListener((JTextComponent)spin.getEditor());
  }
}

public static void addSelectAllListener(JComboBox combo) {
  JComponent editor = combo.getEditor().getEditorComponent();
  if (editor instanceof JTextComponent) {
    addSelectAllListener((JTextComponent)editor);
  }
}

أيضًا، من المحتمل أن تكون أساليب إزالة المستمع غير ضرورية، نظرًا لأن المستمع لا يحتوي على مراجع خارجية لأي حالات أخرى، ولكن يمكن إضافتها لجعل مراجعات التعليمات البرمجية أكثر سلاسة.

الطريقة الوحيدة التي أعرفها هي إنشاء FocusListener وإرفاقه بالمكون الخاص بك.إذا كنت تريد أن يكون FocusListener عالميًا لجميع المكونات في التطبيق الخاص بك، فقد تفكر في استخدام البرمجة الموجهة نحو الجانب (AOP).باستخدام AOP، من الممكن ترميزه مرة واحدة وتطبيق مستمع التركيز الخاص بك على جميع المكونات التي تم إنشاؤها في تطبيقك دون الحاجة إلى نسخ ولصق مكون.addFocusListener(المستمع) كود في جميع أنحاء التطبيق الخاص بك ..

يجب أن يعترض الجانب الخاص بك إنشاء JComponent (أو الفئات الفرعية التي تريد إضافة هذا السلوك إليها) وإضافة مستمع التركيز إلى المثيل الذي تم إنشاؤه حديثًا.يعد نهج AOP أفضل من نسخ ولصق FocusListener في التعليمات البرمجية بأكملها لأنك تحتفظ بها كلها في جزء واحد من التعليمات البرمجية، ولا تخلق كابوسًا للصيانة بمجرد أن تقرر تغيير سلوكك العام مثل إزالة المستمع لـ JSpinners.

هناك العديد من أطر عمل AOP المتاحة للاختيار من بينها.انا يعجبني JBossAOP نظرًا لأنها لغة Java خالصة بنسبة 100%، ولكن قد ترغب في إلقاء نظرة عليها الجانب J.

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