Как выделить часть JLabel?

Прежде чем кто-либо предложит HTML, я объясню позже, почему это не вариант здесь. У меня есть таблица, содержащая столбец с текстовыми ячейками. Мне нужно иметь возможность выделить часть текста в каждой ячейке. Так, например, если в ячейке содержится «кошка, фу, собака»... Я мог бы выделить foo.

Мой текущий метод заключается в использовании пользовательского TableCellRenderer, который помещает html в компонент JLabel, который обрабатывается, и какое-то время это было хорошо. Затем я заметил, что когда текст в ячейке становится слишком длинным, чтобы соответствовать ширине столбца, он просто усекает текст без обычного «...», что обычно происходит в этом случае. Таким образом, пользователи не знали, что есть еще текст, который они не видят. Другая проблема заключалась в том, что если исходный текст сам по себе содержал HTML, что иногда случается в моем случае, ячейка отображалась неправильно. Я знаю, что мог бы просто избежать html, но у меня все еще была бы предыдущая проблема.

Если я использую компонент, отличный от jlabel, то это делает ячейки моей таблицы странными. У кого-нибудь есть предложения? Спасибо


person startoftext    schedule 20.08.2010    source источник


Ответы (3)


Что ж, вот решение.

Короче говоря, вы можете создать подкласс JLabel, чтобы рисовать блик вручную. Переопределите метод paintComponent для фактического рисования и используйте FontMetrics, чтобы рассчитать, где должна быть нарисована выделенная область.

Вот этот ответ в мучительных подробностях:

По сути, вы можете создать подкласс JLabel, который будет подсвечивать вещи. Я бы сделал это так; вы можете сделать это несколько иначе:

Добавьте метод, сообщающий метке, какую часть выделить. Это может быть что-то вроде этого, если вам нужна только одна выделенная область:

public void highlightRegion(int start, int end) {
     // Set some field to tell you where the highlight starts and ends...
}

Если вам нужно несколько регионов, просто используйте ArrayList вместо простого поля. Возможно, также будет полезен метод удаления выделения.

Теперь вам нужно переопределить метод paintComponent для JLabel. Здесь вам нужно сделать несколько отдельных шагов, которые вы можете организовать разными методами или чем-то еще. Для простоты я поставлю все это в методе рисования.

@Override
protected void paintComponent(Graphics g) {
  ...

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

  FontMetrics metrics = new FontMetrics(getFont());

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

  String start = getText().substring(0, startOfHighlight);
  String text = getText().substring(startOfHighlight, endOfHighlight);
  //You may also need to account for some offsets here:
  int startX = metrics.stringWidth(start);
  int startY = 0; //You probably have some vertical offset to add here.
  int length = metrics.stringWidth(text);
  int height = metrics.getHeight();

Теперь вы можете нарисовать выделенную область, прежде чем рисовать остальную часть этикетки:

  g.fillRect(startX, startY, length, height);
  super.paintComponent(g);
}

Конечно, если вы хотите, чтобы выделение охватило несколько строк, это потребует дополнительной работы.

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

person Tikhon Jelvis    schedule 21.08.2010
comment
Вау, спасибо за ваш ответ. Это работает почти идеально для JLabels. Однако я столкнулся с одной проблемой. Если метка установлена ​​как setOpaque(true), как это требуется в моей ситуации, подсветка не отображается. Я думаю, это вызвано вызовом super.paintComponent(g) после выполнения пользовательского рисования. Сначала я попытался сделать пользовательский рисунок выделения, но затем выделенный прямоугольник рисует текст, который нужно выделить. Это длинная история, но чтобы в моем случае отображалось чередование таблиц и некоторые другие вещи, такие как выделение строк, я должен установить метку в непрозрачное состояние. Спасибо за ответ. - person startoftext; 25.08.2010
comment
Что ж, попробуйте сначала вызвать super.paintComponent(g) и использовать g.setColor, чтобы установить цвет на что-то частично прозрачное (например, new Color(0x33, 0x66, 0xFF, 0x66) — приятный синий) перед вызовом fillRect. - person Tikhon Jelvis; 26.08.2010
comment
В конце концов я в основном переделал свой собственный метод компонента рисования, чтобы он работал для моих целей. И отказался от вызова метода родительского компонента краски. Ваш ответ был очень хорошим, и решение довольно хорошо отвечает на заголовок этого поста, хотя что касается jlabel, поэтому я отмечу его как принятое. Спасибо большое Тихон. - person startoftext; 01.09.2010

Не могу удержаться от того, чтобы бросить механизм декорирования рендерера SwingX в кольцо: его способ решить требование состоит в том, чтобы реализовать Highlighter, делающий это. Что на самом деле уже сделано (правда, пока не в официально поддерживаемом), но скрыто в проекте SwingLabs-Demos с именем X/MatchingTextHighlighter (вам понадобятся оба) и недавно исправлено, чтобы справляться со значками, RToL -ComponentOrientation, выравнивание, эллипсы..

person kleopatra    schedule 27.10.2011

Это отличный ответ и, вероятно, лучшее решение. Но альтернативой, которую некоторые могут найти более простой, является использование JTextfield вместо JLabel для рендеринга, тогда вы можете использовать возможности подсветки JTextfields, т.е.

void highlightWhitespaceText(JTextField text)
    {
        text.setHighlighter(AbstractTableCellRenderer.defaultHighlighter);
        try
        {
            Matcher m = AbstractTableCellRenderer.whitespaceStartPattern.matcher(text.getText());
            if (m.matches())
            {
                text.getHighlighter().addHighlight(m.start(1), m.end(1), AbstractTableCellRenderer.highlightPainter);
            }
            m = AbstractTableCellRenderer.whitespaceEndPattern.matcher(text.getText());
            if (m.matches())
            {
                text.getHighlighter().addHighlight(m.start(1), m.end(1), AbstractTableCellRenderer.highlightPainter);
            }
        }
        catch (BadLocationException ble)
        {
            //
        }
    }

Вы можете изменить свойства JTextfield, чтобы в других отношениях оно выглядело как jLabel.

person Paul Taylor    schedule 27.10.2010