MatteBorder не работает с ориентацией компонента справа налево - PullRequest
2 голосов
/ 06 декабря 2011

У меня проблемы с настройкой MatteBorder для работы с компонентами справа налево. Код ниже иллюстрирует проблему.

Я помещаю одну JPanel в другую JPanel. Оба JPanels имеют MatteBorder. Внутренняя граница имеет толщину 10,10,10,10, тогда как внешняя граница имеет толщину 20,20,20,10 (обратите внимание, что внешняя граница асимметрична).

Если JPanels имеют ориентацию компонента LEFT_TO_RIGHT, все выглядит хорошо; но если ориентация RIGHT_TO_LEFT, границы перекрываются.

Overlapping borders

Рассмотрим следующий код:

import java.awt.*;
import javax.swing.*;

public class Xyzzy extends JFrame{
    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                Xyzzy frame = new Xyzzy();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new FlowLayout(FlowLayout.RIGHT, 0, 5));
                frame.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); 

                {
                    JPanel outer = new JPanel();
                    outer.setLayout(new FlowLayout(FlowLayout.RIGHT, 0, 5));
                    outer.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); 
                    outer.setBorder(BorderFactory.createMatteBorder(20, 20, 20, 10, Color.black));

                    JPanel inner = new JPanel();
                    inner.setLayout(new FlowLayout(FlowLayout.RIGHT, 0, 5));
                    inner.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); 
                    inner.setBorder(BorderFactory.createMatteBorder(10, 10, 10, 10, Color.red));

                    inner.add(Box.createRigidArea(new Dimension(10,10)));
                    outer.add(inner);
                    frame.add(outer);
                }

                {
                    JPanel outer = new JPanel();
                    outer.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 5));
                    outer.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); 
                    outer.setBorder(BorderFactory.createMatteBorder(20, 20, 20, 10, Color.blue));

                    JPanel inner = new JPanel();
                    inner.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 5));
                    inner.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); 
                    inner.setBorder(BorderFactory.createMatteBorder(10, 10, 10, 10, Color.yellow));

                    inner.add(Box.createRigidArea(new Dimension(10,10)));
                    outer.add(inner);
                    frame.add(outer);
                }

                frame.setSize(600, 600);
                frame.setVisible(true);
            }
        });
    }
}

Первая половина кода генерирует RIGHT_TO_LEFT JPanels с черными и красными границами, вторая половина генерирует LEFT_TO_RIGHT JPanels с желтой и синей границами Если вы запустите программу, вы увидите желтые границы внутри синих, но красные пересекаются с черными.

Почему?

Ответы [ 2 ]

1 голос
/ 07 декабря 2011

Примечание: это не ответ - просто расширенный комментарий с кодом: -)

Вычеркнул немного из примера OP, чтобы сконцентрироваться на FlowLayout-issue-with-asym-border-in-RToL - для улучшения добавлена ​​метка вместо жесткой области (для меня :-) посмотрите, где она находится расположен. Макет просто сходит с ума ...

public void run() {
    MatteBorderCO frame = new MatteBorderCO();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLayout(new FlowLayout()); 

    frame.add(createRToLPanel(50, 100, true));
    frame.add(createRToLPanel(100, 100, true));
    frame.add(createRToLPanel(100, 50, true));

    frame.setSize(600, 600);
    frame.setVisible(true);
}

private JPanel createRToLPanel(int leftInset, int rightInset, boolean matte) {
    JPanel outer = new JPanel();
    Border asymBorder = matte ? 
            BorderFactory.createMatteBorder(20, leftInset, 20, rightInset, Color.black) :
            BorderFactory.createEmptyBorder(20, leftInset, 20, rightInset)    ;
    outer.setBorder(BorderFactory.createCompoundBorder(
            asymBorder, BorderFactory.createLineBorder(Color.RED)
    ));

    JPanel inner = new JPanel();
    inner.setBackground(Color.YELLOW);
    inner.setBorder(BorderFactory.createLineBorder(Color.BLUE));
    inner.add(new JLabel("RToL"));
    outer.add(inner);
    outer.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
    return outer;
}

Редактировать

Culprit - неправильный расчет местоположения в flowLayout.moveComponents:

   if (ltr) {
        m.setLocation(x, cy);
   } else { // RtoL orientation
        // here's the error: location is adjusted relative to the target width
        // without taking the insets into account ... 
        m.setLocation(target.width - x - m.width, cy);
   }

Редактировать 2

удалено предполагаемое «простое» исправление - недостаточно хорошо; -)

Редактировать 3

не смог устоять, вот FixedFlowLayout (формально не проверенный, только что использованный в примере выше, который отлично с ним выглядит!)

public static class FixedFlowLayout extends FlowLayout {

    /**
     * C&p mostly - RToL border fix implemented.
     */
    protected int moveComponents(Container target, int x, int y, int width,
            int height, int rowStart, int rowEnd, boolean ltr,
            boolean useBaseline, int[] ascent, int[] descent) {
        switch (getAlignment()) {
        case LEFT:
            x += ltr ? 0 : width;
            break;
        case CENTER:
            x += width / 2;
            break;
        case RIGHT:
            x += ltr ? width : 0;
            break;
        case LEADING:
            break;
        case TRAILING:
            x += width;
            break;
        }
        int maxAscent = 0;
        int nonbaselineHeight = 0;
        int baselineOffset = 0;
        if (useBaseline) {
            int maxDescent = 0;
            for (int i = rowStart; i < rowEnd; i++) {
                Component m = target.getComponent(i);
                if (m.isVisible()) {
                    if (ascent[i] >= 0) {
                        maxAscent = Math.max(maxAscent, ascent[i]);
                        maxDescent = Math.max(maxDescent, descent[i]);
                    } else {
                        nonbaselineHeight = Math.max(m.getHeight(),
                                nonbaselineHeight);
                    }
                }
            }
            height = Math.max(maxAscent + maxDescent, nonbaselineHeight);
            baselineOffset = (height - maxAscent - maxDescent) / 2;
        }

        int right = target.getWidth() - target.getInsets().right - getHgap();

        for (int i = rowStart; i < rowEnd; i++) {
            Component m = target.getComponent(i);
            if (m.isVisible()) {
                int cy;
                if (useBaseline && ascent[i] >= 0) {
                    cy = y + baselineOffset + maxAscent - ascent[i];
                } else {
                    cy = y + (height - m.getHeight()) / 2;
                }
                if (ltr) {
                    m.setLocation(x, cy);
                    x += m.getWidth() + getHgap();
                } else {
                    m.setLocation(right - m.getWidth(), cy);
                    right -= m.getWidth() + getHgap();
                }
            }
        }
        return height;
    }

    /**
     * C&p, to be able to call the fixed moveComponent.
     */
    @Override
    public void layoutContainer(Container target) {
        synchronized (target.getTreeLock()) {
            Insets insets = target.getInsets();
            int maxwidth = target.getWidth()
                    - (insets.left + insets.right + getHgap() * 2);
            int nmembers = target.getComponentCount();
            int x = 0, y = insets.top + getVgap();
            int rowh = 0, start = 0;

            boolean ltr = target.getComponentOrientation().isLeftToRight();

            boolean useBaseline = getAlignOnBaseline();
            int[] ascent = null;
            int[] descent = null;

            if (useBaseline) {
                ascent = new int[nmembers];
                descent = new int[nmembers];
            }

            for (int i = 0; i < nmembers; i++) {
                Component m = target.getComponent(i);
                if (m.isVisible()) {
                    Dimension d = m.getPreferredSize();
                    m.setSize(d.width, d.height);

                    if (useBaseline) {
                        int baseline = m.getBaseline(d.width, d.height);
                        if (baseline >= 0) {
                            ascent[i] = baseline;
                            descent[i] = d.height - baseline;
                        } else {
                            ascent[i] = -1;
                        }
                    }
                    if ((x == 0) || ((x + d.width) <= maxwidth)) {
                        if (x > 0) {
                            x += getHgap();
                        }
                        x += d.width;
                        rowh = Math.max(rowh, d.height);
                    } else {
                        rowh = moveComponents(target, insets.left + getHgap(),
                                y, maxwidth - x, rowh, start, i, ltr,
                                useBaseline, ascent, descent);
                        x = d.width;
                        y += getVgap() + rowh;
                        rowh = d.height;
                        start = i;
                    }
                }
            }
            moveComponents(target, insets.left + getHgap(), y, maxwidth - x,
                    rowh, start, nmembers, ltr, useBaseline, ascent,
                    descent);
        }
    }

    public FixedFlowLayout() {
    }

    public FixedFlowLayout(int align, int hgap, int vgap) {
        super(align, hgap, vgap);
    }

    public FixedFlowLayout(int align) {
        super(align);
    }

}
0 голосов
/ 07 декабря 2011

Я думаю, что нашел способ решить проблему.(Я основываю следующее на модифицированном коде Клеопатры.)

Хитрость заключается в том, чтобы вставить промежуточную JPanel между внешней и внутренней JPanel.Сохраните внешнюю JPanel L-to-R и сделайте промежуточную и внутреннюю JPanel R-to-L.Вам также необходимо указать зазоры нулевой ширины в макете внешней панели.

import java.awt.*;
import javax.swing.*;
import javax.swing.border.Border;

public class Xyzzy extends JFrame{
    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    Xyzzy frame = new Xyzzy();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new FlowLayout()); 

                    frame.add(createRToLPanel(50, 100, true));
                    frame.add(createRToLPanel(100, 100, true));
                    frame.add(createRToLPanel(100, 50, true));

                    frame.setSize(600, 600);
                    frame.setVisible(true);
                }


                private JPanel createRToLPanel(int leftInset, int rightInset, boolean matte) {
                    JPanel outer = new JPanel();
                    Border asymBorder = matte ? 
                        BorderFactory.createMatteBorder(20, leftInset, 20, rightInset, Color.black) :
                        BorderFactory.createEmptyBorder(20, leftInset, 20, rightInset)    ;
                    outer.setBorder(BorderFactory.createCompoundBorder(
                                        asymBorder, BorderFactory.createLineBorder(Color.RED)
                                        ));

                    JPanel intermediate = new JPanel();
                    outer.setLayout(new FlowLayout(FlowLayout.LEADING,0,0)); 
                    outer.add(intermediate);

                    JPanel inner = new JPanel();
                    inner.setBackground(Color.YELLOW);
                    inner.setBorder(BorderFactory.createLineBorder(Color.BLUE));
                    inner.add(new JLabel("RToL1"));
                    inner.add(new JLabel("RToL2"));

                    intermediate.add(inner);
                    intermediate.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);

                    return outer;
                }


            });
    }
}

В этом коде я добавляю два JLabel во внутреннюю JPanel, чтобы доказать, что ориентация действительно R-to-L.

Глупо, глупо.Но если это действительно ошибка, я могу обойти это таким образом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...