2011-01-12 3 views
1

Я немного смущен тем, как управлять компоновкой моих компонентов на Java (я хочу сделать это вручную, а не обрабатывать его менеджером макета). Есть эти методы в Component:Java/AWT/Swing: об утверждении и размерах

  • layout, doLayout
  • validate, invalidate, revalidate
  • validateTree, invalidateTree
  • setSize, setBounds, setPreferredSize
  • getSize, getBounds, getPreferredSize
  • paint, repaint, update
  • updateUI

Ранее я пытался перегружать различные комбинации выше, но я не был уверен, какой из них перегружать и что именно я должен сделать внутри и что функции, я должен вызвать дочерние компоненты.


Что я делаю сейчас:

  • только перегружать doLayout выше.
  • В doLayout для всех дочерних компонентов:
    • Вызов child.doLayout.
    • Звоните child.setBounds (иногда до child.setBounds, иногда после, иногда оба).
  • В doLayout, потому что я делаю макет, я автоматически рассчитал также его предпочтительный размер.
  • В doLayout, звоните this.setPreferredSize.
  • Для всех конструкторов звоните: this.setLayout(null).
  • В некоторых конструкторах звоните: this.doLayout. (И если я этого не делаю, он отображается неправильно).
  • Когда я выполняю некоторую операцию, в которой я должен переделать макет (например, я динамически добавил некоторое текстовое поле в какой-либо контейнер, и поэтому я хочу изменить размер контейнера и также все родители соответственно), я звоню container.revalidate().

Остающиеся проблемы:

  • Я думаю, что я до сих пор не совсем понял, что функция вызывает то, что, что я должен перегружать и как обращаться.
  • В doLayout, я звоню this.setPreferredSize. doLayout сам часто зависит от this.getSize(). Для родителя часто child.setBounds зависит от child.getPreferredSize().Поэтому у меня есть дилемма, что в некоторых случаях мне сначала нужно позвонить child.doLayout, а затем child.setBounds, а в некоторых других случаях наоборот. А в некоторых случаях еще сложнее. Так что все кажется, что я должен позвонить this.setPreferredSize где-то в другом месте. Но где? Потому что он всегда должен обновляться при изменении размера (именно поэтому я перегрузил setBounds раньше, но это было еще более уродливо).
  • У меня есть все внутри JScrollPane, которое устанавливает полосы прокрутки в соответствии с viewportView.getPreferredSize(). revalidate Я звоню в случаях, когда я хочу пересчитать макет, вызывает doLayout звонки, которые правильно звонят setPreferredSize для всех компонентов/контейнеров в иерархии. Хотя, кажется, что JScrollPane устанавливает свою полосу прокрутки до, вызванный doLayout, и, следовательно, это всегда неправильно. Как я могу это исправить?

Некоторые дополнительные мысли (просьба прокомментировать) о том, как я, возможно, мог бы решить эту проблему JScrollPane (на самом деле не пытался, потому что это потребует некоторых крупных переделок, так что я хотел спросить первого):

  • Удалить все setPreferredSize calls in doLayout.
  • Перегрузка getPreferredSize и звоните doLayout оттуда (чтобы получить предпочтительный размер).

- ИЛИ -

  • Вместо вызова revalidate когда я делаю то, что требует, чтобы переделать макет, вызовите validateTree.

- ИЛИ -

  • Вместо вызова revalidate когда я делаю то, что требует, чтобы переделать макет, назвать все doLayout вручную, а затем с revalidate на JScrollPanel.

И, наконец, как я идти о круговой зависимости от размера и предпочтительного размера? Я довольно часто имею этот случай:

  • comp.width зафиксирован в корне. Я могу установить ширину в корне и рекурсивно перейти к всем дочерним элементам и установить ее ширину.
  • comp.height закреплен на самом внутреннем ребенке и зависит от его ширины. Поэтому после того, как я установил все ширины, я могу рассчитать и установить высоты снизу вверх.

Я не могу позвонить setPreferredSize, прежде чем я не позвонил setSize. И я не могу позвонить setSize, прежде чем я не позвонил setPreferredSize.

+1

-1, Почему вы изобретаете колесо? «Обработка вручную» на самом деле создает собственный менеджер макетов. Почему бы просто не использовать интерфейс на месте, чтобы другие люди поняли ваш код и смогут его поддерживать? – camickr

+0

@camickr: Потому что я всегда чувствовал, что он не делает то, что я хочу. Кроме того, мое оформление абсолютно тривиально; Я действительно не понимаю, зачем мне вообще нужен. Я абсолютно уверен, что фактический код для компоновки моих компонентов намного проще и короче, чем настроить и взломать любой существующий менеджер макетов, который работает именно так, как я хочу. – Albert

+1

@ Альберт, тогда вы не понимаете, как работают менеджеры макетов. Гораздо проще создать собственный менеджер компоновки с тривиальной логикой компоновки, тогда он будет переопределять все те методы, о которых вы упомянули выше.Пока вы не попробуете написать простой менеджер макетов, я не думаю, что вы можете сказать, что проще. Ведь ваш текущий подход не работает, и именно поэтому вы задаете вопрос. Придерживайтесь известных подходов, и больше людей на форуме смогут помочь. У меня нет совета, чтобы предложить вашу проблему, так как я понятия не имею, что вы пытаетесь сделать. – camickr

ответ

1

Боюсь, что вы ошибаетесь.

Используйте ответ Стюарта и установите нулевой диспетчер макетов. Это позволяет вам установить положение всех компонентов, вызвав setBounds().

Затем добавьте слушателя в окно, которое вызывается всякий раз, когда изменяется размер окна, и пересчитывает позиции и вызывает setBounds().

Это все может быть автоматизировано, если вы реализуете свой код позиционирования в качестве диспетчера макета. (В конце концов, позиционирование компонентов при изменении размеров == управления макет, так верить или нет, вы являются разработки менеджер компоновки, то это просто!)

public class MyLayout implements LayoutManager,LayoutManager2 { 

    @Override 
    public void addLayoutComponent(Component comp, Object constraints) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public Dimension maximumLayoutSize(Container target) { 
     // TODO Auto-generated method stub 
     return null; 
    } 

    @Override 
    public float getLayoutAlignmentX(Container target) { 
     // TODO Auto-generated method stub 
     return 0; 
    } 

    @Override 
    public float getLayoutAlignmentY(Container target) { 
     // TODO Auto-generated method stub 
     return 0; 
    } 

    @Override 
    public void invalidateLayout(Container target) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void addLayoutComponent(String name, Component comp) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void removeLayoutComponent(Component comp) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public Dimension preferredLayoutSize(Container parent) { 
     // TODO Auto-generated method stub 
     return null; 
    } 

    @Override 
    public Dimension minimumLayoutSize(Container parent) { 
     // TODO Auto-generated method stub 
     return null; 
    } 

    @Override 
    public void layoutContainer(Container parent) { 
     // Now call setBounds of your components here 
    } 

} 

В методе layoutContainer, вы можете позвонить setBounds всех компонентов. Этот метод вызывается при первоначальном изложении окна, а также при каждом изменении размера.

Затем, когда вы, например. поместите вещи в окно или в JPanel, просто setLayoutManager (новый MyLayoutManager()), и вы золотой.

Однако очень грубый вопрос все еще остается. Ваш менеджер макетов - это отдельный класс, но он все равно должен иметь доступ к компонентам, которые вы создали в другом месте вашего кода окна. Решение перебора, чтобы просто получить ссылку на все компоненты в конструкторе, например .:

class MyWindow extends JFrame { 
    public MyWindow() { 
     JLabel label=new JLabel("Hello"); 
     JButton button=new JButton("Ok"); 

     setLayoutManager(new MyLayoutManager(label,button)); // PASS THEM 
     add(label); 
     add(button); 
     pack(); 
     setVisible(true); 
    } 
} 

Конечно, это наивный подход, но может работать. Правильный способ справиться с этим - реализовать addLayoutComponent в вашем менеджере компоновки, который вызывается, когда вы добавляете что-либо в JFrame (например, при вызове add (label)). Таким образом, менеджер компоновки знает о компонентах в макете.

3

Все ваши вопросы и вопросы могут быть решены, если вы использовали LayoutManagers. Кажется, вы очень стараетесь их избежать, но на самом деле это ваш лучший союзник, когда дело доходит до компоновки элементов графического интерфейса.

Многие люди стараются избегать LayoutManager сначала из-за кривой обучения. То, что я нашел очень полезным, когда я изучал их, было визуальным руководством для менеджеров макетов: http://download.oracle.com/javase/tutorial/uiswing/layout/visual.html.

Мое предложение было бы найти способ заставить ваш макет работать с LayoutManagers. Даже вы решаете некоторые из этих проблем, делая изменения, или кто-то другой, кто должен прочитать ваш код, будет очень трудоемким. Плюс ко всему, что вам нужно сделать, чтобы заставить это работать, есть много места для ошибок из-за того, что вы оставите что-то на одном компоненте.

+0

Хм, но все кажется очень сложным. Абсолютный код позиционирования был бы тривиальным. Мне не нужно было бы больше, чем, может быть, 10 строк кода, чтобы получить то, что я хочу, если бы я только знал, как и где поставить этот код. (Вот что я задавал с моим вопросом). – Albert

+0

В зависимости от ваших потребностей макета может быть менее 10 строк кода. Плюс абсолютное позиционирование становится отрицательным, когда пользователь меняет размер фрейма. – jzd

+0

Но я действительно хочу иметь фиксированный X/Y (всегда относительно его контейнера). Только размер должен измениться. Но так, как я описал. Поскольку диспетчер компоновки, похоже, работает на одном контейнере, а не на иерархии контейнеров, я не вижу, как они должны давать мне какое-либо преимущество в моей описанной проблеме (круговая зависимость от размеров). – Albert

0

После прочтения ваших комментариев кажется, что вы просто хотите сделать абсолютное позиционирование, используя нулевой макет. Там в tutorial на нем, но суть в том, что вы установите менеджер компоновки обнулить для контейнера, а затем вызвать SetBounds() на каждом компоненте в этом контейнере, чтобы установить его размер и положение, как это:

JPanel p = new JPanel(); 
p.setLayout(null); 

JButton b = new JButton ("Hit It"); 
p.add(b); 
b.setBounds(new Rectangle(10, 20, 100, 50)); 
+0

Да, это то, что я делаю сейчас (за исключением того, что ширина/высота зависит от ширины/высоты контейнера родителя), и это работает хорошо. Дело в том, что когда я изменяю размер, я не знаю, какую функцию переопределить те, которые я перечислил. – Albert

+0

Кроме того, не могли бы вы объяснить, если я хочу позиционировать 'b' в' 10, 20, 100, 50', как любой менеджер компоновки мог сделать эту строку более простой: 'b.setBounds (новый Rectangle (10, 20, 100) , 50)) '? – Albert

+0

@Albert: для изменения размера я добавил компонент ComponentListener в родительский контейнер, который затем может реагировать на изменения размера. –

Смежные вопросы