I was wondering if there is a straightforward way to display line numbers with StyledText text field - even if lines are wrapped. I'm using it in my application and if content gets to big, some line numbers would be nice.
Thank you.
The key is org.eclipse.swt.custom.Bullet. It's basically a symbol (or in our case a number) you can add to the beginning of a line.
//text is your StyledText
text.addLineStyleListener(new LineStyleListener()
{
public void lineGetStyle(LineStyleEvent e)
{
//Set the line number
e.bulletIndex = text.getLineAtOffset(e.lineOffset);
//Set the style, 12 pixles wide for each digit
StyleRange style = new StyleRange();
style.metrics = new GlyphMetrics(0, 0, Integer.toString(text.getLineCount()+1).length()*12);
//Create and set the bullet
e.bullet = new Bullet(ST.BULLET_NUMBER,style);
}
});
This is my working implementation.
styledText.addLineStyleListener(new LineStyleListener() {
#Override
public void lineGetStyle(LineStyleEvent event) {
// Using ST.BULLET_NUMBER sometimes results in weird alignment.
//event.bulletIndex = styledText.getLineAtOffset(event.lineOffset);
StyleRange styleRange = new StyleRange();
styleRange.foreground = Display.getCurrent().getSystemColor(SWT.COLOR_GRAY);
int maxLine = styledText.getLineCount();
int bulletLength = Integer.toString(maxLine).length();
// Width of number character is half the height in monospaced font, add 1 character width for right padding.
int bulletWidth = (bulletLength + 1) * styledText.getLineHeight() / 2;
styleRange.metrics = new GlyphMetrics(0, 0, bulletWidth);
event.bullet = new Bullet(ST.BULLET_TEXT, styleRange);
// getLineAtOffset() returns a zero-based line index.
int bulletLine = styledText.getLineAtOffset(event.lineOffset) + 1;
event.bullet.text = String.format("%" + bulletLength + "s", bulletLine);
}
});
styledText.addModifyListener(new ModifyListener() {
#Override
public void modifyText(ModifyEvent e) {
// For line number redrawing.
styledText.redraw();
}
});
Note that the possible overhead of syntax highlighting recalculation when calling redraw() is likely to be acceptable, because lineGetStyle() are only called with lines currently on screen.
I believe that using a LineStyleListener should work. Something along the lines of:
styledText.addLineStyleListener(
new LineStyleListener() {
#Override
public void lineGetStyle(LineStyleEvent event) {
String line = event.lineText;
int lineNumber = event.lineOffset;
// Do stuff to add line numbers
}
}
);
This is a way to use bullets that updates the numbers when the content changes:
text.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent event) {
int maxLine = text.getLineCount();
int lineCountWidth = Math.max(String.valueOf(maxLine).length(), 3);
StyleRange style = new StyleRange();
style.metrics = new GlyphMetrics(0, 0, lineCountWidth * 8 + 5);
Bullet bullet = new Bullet(ST.BULLET_NUMBER, style);
text.setLineBullet(0, text.getLineCount(), null);
text.setLineBullet(0, text.getLineCount(), bullet);
}
});
As a side-note for colouring the line numbers:
Device device = Display.getCurrent();
style.background = new Color(device, LINE_NUMBER_BG);
style.foreground = new Color(device, LINE_NUMBER_FG);
where LINE_NUMBER_BG and LINE_NUMBER_FG might be a RGB object such as:
final RGB LINE_NUMBER_BG = new RBG(160, 80, 0); // brown
final RGB LINE_NUMBER_FG = new RGB(255, 255, 255); // white
Related
So i have a barchart in MPandroid, and i need to put the names on the chart, but there is no space where i want them, the solution is to put them on top of the chart. Like this:
can someone help me achieve this ?
There isn't a built-in way of putting the text in a non-standard location, but you can do it by writing a custom renderer for your chart. If you extend the HorizontalBarChartRenderer class and override the drawValues method (making just a few modifications to the original version in the code linked) you can change where the label gets drawn.
Custom Renderer
Code copied from drawValues in HorizontalBarChartRenderer and simplified to only include required parts for this use-case (removed logic for stacked charts, icons, toggling values on and off, etc). The only change is to the x,y values passed to drawValue()
private static class MyRenderer extends HorizontalBarChartRenderer {
public MyRenderer(BarDataProvider chart, ChartAnimator animator,
ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
}
#Override
public void drawValues(Canvas c) {
List<IBarDataSet> dataSets = mChart.getBarData().getDataSets();
final float valueOffsetPlus = Utils.convertDpToPixel(5f);
for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) {
IBarDataSet dataSet = dataSets.get(i);
// apply the text-styling defined by the DataSet
applyValueTextStyle(dataSet);
ValueFormatter formatter = dataSet.getValueFormatter();
// get the buffer
BarBuffer buffer = mBarBuffers[i];
for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) {
if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 1]))
break;
if (!mViewPortHandler.isInBoundsX(buffer.buffer[j]))
continue;
if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1]))
continue;
BarEntry entry = dataSet.getEntryForIndex(j / 4);
String formattedValue = formatter.getBarLabel(entry);
// Modify the x, y position here to control where the
// text is. The "buffer" array gives the positions of
// the current bar (in pixels)
drawValue(c,
formattedValue,
buffer.buffer[j] + valueOffsetPlus,
buffer.buffer[j+1] - valueOffsetPlus,
dataSet.getValueTextColor(j / 2));
}
}
}
}
Example Use
To use the custom renderer, set it on the chart and modify the bar widths to leave room for the text.
HorizontalBarChart chart = findViewById(R.id.chart);
List<Float> values = Arrays.asList(3f, 2f, 2.2f);
List<Integer> colors = Arrays.asList(Color.CYAN, Color.RED, Color.GREEN);
List<String> labels = Arrays.asList("Pickled Horses", "Horses", "Pickles");
List<BarEntry> entries = new ArrayList<>();
for(int i = 0; i < values.size(); ++i) {
entries.add(new BarEntry(i+1, values.get(i), labels.get(i)));
}
BarDataSet ds = new BarDataSet(entries, "Values");
ds.setColors(colors);
ds.setValueTextSize(14f);
ds.setValueFormatter(new ValueFormatter() {
#Override
public String getBarLabel(BarEntry barEntry) {
return (String)barEntry.getData();
}
});
ds.setAxisDependency(YAxis.AxisDependency.RIGHT);
BarData data = new BarData(ds);
// Reduce bar width to leave room for text
data.setBarWidth(0.75f);
// Use the custom renderer
chart.setRenderer(new MyRenderer(chart, chart.getAnimator(), chart.getViewPortHandler()));
// Misc formatting & setup
chart.setData(data);
chart.getAxisLeft().setEnabled(false);
chart.getAxisRight().setAxisMinimum(0f);
chart.getAxisRight().setAxisMaximum(4f);
chart.getAxisRight().setGranularity(1f);
chart.getAxisRight().setTextSize(14f);
chart.getAxisRight().setDrawGridLines(false);
chart.getXAxis().setDrawLabels(false);
chart.getXAxis().setDrawGridLines(false);
chart.getXAxis().setAxisMaximum(3.6f);
chart.getDescription().setEnabled(false);
chart.getLegend().setEnabled(false);
chart.setBorderColor(Color.BLACK);
chart.setBorderWidth(1f);
chart.setDrawBorders(true);
I need extends TextRenderer for custom underline, but in one element Text i have one letter.
I have Large text and do this:
String s = "d < like to filter > RENDER_TEXT events as they are <written> to an output file. I have a <PDF that has some text in it> that I want filtered out. I've found that I can walk the document once and determine the characteristics of the render events that I want to filter. Now I'd like to copy the pages of the source document and skip over some RENDER_TEXT events so that the text does not appear in the destination document. I have an IEventFilter that will accept the correct events. I just need to know how to put this filter on the document writer.";
String[] words = s.split("");
Paragraph paragraph = new Paragraph();
Text text = null;
boolean isUnderStart = false;
boolean isUnderEnd = false;
int i = 1;
for (String word : words) {
if (word.equals("<")) {
isUnderStart = true;
}
if (word.equals(">")) {
isUnderEnd = true;
}
text = new Text(word);
text.setNextRenderer(new Word25TextRenderer(text, isUnderStart,isUnderEnd));
isUnderStart = false;
isUnderEnd = false;
paragraph.add(text);
i++;
}
doc.add(paragraph);
doc.close();
"<" и ">" i do for test (In a real program, I do to use Jsoup)
With help this symbols i find out coordinate axes for first simbol and end simbol. This is needed to draw the canvas element(line, curveTo). Also i add boolean to draw two times, the coordinates for the first letter and for the second.
Everything word for one line, if I want to draw underline for two line it does not break. In this part: <PDF that has some text in it>
static class Word25TextRenderer extends TextRenderer {
private boolean isUnderStart;
private boolean isUnderEnd;
public Word25TextRenderer(Text textElement, boolean isUnderStart, boolean isUnderEnd) {
super(textElement);
this.isUnderStart = isUnderStart;
this.isUnderEnd = isUnderEnd;
}
#Override
public IRenderer getNextRenderer() {
return new Word25TextRenderer((Text) modelElement, isUnderStart, isUnderEnd);
}
#Override
public void draw(DrawContext drawContext) {
super.draw(drawContext);
Rectangle textRect = getOccupiedAreaBBox();
int pageNumber = getOccupiedArea().getPageNumber();
PdfCanvas canvas = drawContext.getCanvas();
if (isUnderStart) {
canvas
.saveState()
.setStrokeColor(DeviceRgb.RED)
.moveTo(textRect.getRight(), textRect.getBottom());
}
if (isUnderEnd) {
canvas.curveTo(textRect.getRight(), textRect.getBottom() + textRect.getHeight() / 2,
textRect.getRight(), textRect.getBottom(),
textRect.getRight(), textRect.getBottom())
.stroke()
.restoreState();
}
}
}
Maybe you know other way?
Here's what happened in the end
p.s:I add one element to the text, because then I change the elements individually (font, fontSize etc) It's necessary!
Instead of doing the underlining at the Text element / renderer level, it's much handier to do it at the level of the Paragraph, where all the information about line split is known, since you have to underline parts of a same Text that are placed on different lines.
We will create a CustomText class to store information about whether we want to underline that piece of text still (although we could have used setProperty on a regular Text element):
static class CustomText extends Text {
private boolean isUnderStart;
private boolean isUnderEnd;
public CustomText(String text, boolean isUnderStart, boolean isUnderEnd) {
super(text);
this.isUnderStart = isUnderStart;
this.isUnderEnd = isUnderEnd;
}
public boolean isUnderStart() {
return isUnderStart;
}
public boolean isUnderEnd() {
return isUnderEnd;
}
}
The meaty part is our custom ParagraphRenderer - we go over the lines, remember the last "open bracket", or the text chunk start defines the start of the text to be underlined, and then once we face the "closing bracket", or the text chunk that defines the end of the text we do the actual drawing for the whole region. Additionally, we take care of the splits by underlining the remaining part of the line if by the time we finished line inspection we still have our "open bracket". Here id the code:
static class CustomParagraphRenderer extends ParagraphRenderer {
public CustomParagraphRenderer(Paragraph modelElement) {
super(modelElement);
}
#Override
public IRenderer getNextRenderer() {
return new CustomParagraphRenderer((Paragraph) modelElement);
}
#Override
public void draw(DrawContext drawContext) {
super.draw(drawContext);
Rectangle lastRectStart = null;
for (int i = 0; i < lines.size(); i++) {
LineRenderer lineRenderer = lines.get(i);
boolean anyEventsThisLine = false;
for (IRenderer renderer : lineRenderer.getChildRenderers()) {
if (renderer.getModelElement() instanceof CustomText &&
(((CustomText) renderer.getModelElement()).isUnderEnd()
|| ((CustomText) renderer.getModelElement()).isUnderStart())) {
anyEventsThisLine = true;
if ((((CustomText) renderer.getModelElement()).isUnderEnd())) {
Rectangle endRect = renderer.getOccupiedArea().getBBox();
PdfCanvas canvas = drawContext.getCanvas();
canvas.saveState().setStrokeColor(ColorConstants.RED)
.moveTo(lastRectStart.getRight(), lastRectStart.getBottom())
.curveTo(endRect.getRight(), endRect.getBottom() + endRect.getHeight() / 2,
endRect.getRight(), endRect.getBottom(),
endRect.getRight(), endRect.getBottom())
.stroke().restoreState();
lastRectStart = null;
} else {
lastRectStart = renderer.getOccupiedArea().getBBox();
}
}
}
if (lastRectStart != null && !anyEventsThisLine) {
// Underline the whole line
PdfCanvas canvas = drawContext.getCanvas();
Rectangle lineRect = lineRenderer.getOccupiedArea().getBBox();
canvas.saveState().setStrokeColor(ColorConstants.RED)
.moveTo(lineRect.getLeft(), lineRect.getBottom())
.curveTo(lineRect.getRight(), lineRect.getBottom() + lineRect.getHeight() / 2,
lineRect.getRight(), lineRect.getBottom(),
lineRect.getRight(), lineRect.getBottom())
.stroke().restoreState();
}
if (lastRectStart != null) {
// Draw till end of line
Rectangle endRect = lineRenderer.getChildRenderers().get(lineRenderer.getChildRenderers().size() - 1)
.getOccupiedArea().getBBox();
PdfCanvas canvas = drawContext.getCanvas();
canvas.saveState().setStrokeColor(ColorConstants.RED)
.moveTo(lastRectStart.getRight(), lastRectStart.getBottom())
.curveTo(endRect.getRight(), endRect.getBottom() + endRect.getHeight() / 2,
endRect.getRight(), endRect.getBottom(),
endRect.getRight(), endRect.getBottom())
.stroke().restoreState();
if (i + 1 < lines.size()) {
lastRectStart = lines.get(i + 1).getChildRenderers().get(0).getOccupiedArea().getBBox();
} else {
lastRectStart = null;
}
}
}
}
}
The main entry point code only has slight changes:
PdfDocument pdfDocument = new PdfDocument(new PdfWriter("C:/path/to.pdf"));
Document doc = new Document(pdfDocument);
String s = "d < like to filter > RENDER_TEXT events as they are <written> to an output file. I have a <PDF that has some text in it> that I want filtered out. I've found that I can walk the document once and determine the characteristics of the render events that I want to filter. Now I'd like to copy the pages of the source document and skip over some RENDER_TEXT events so that the text does not appear in the destination document. I have an IEventFilter that will accept the correct events. I just need to know how to put this filter on the document writer.";
String[] words = s.split("");
Paragraph paragraph = new Paragraph();
paragraph.setNextRenderer(new CustomParagraphRenderer(paragraph));
Text text = null;
boolean isUnderStart = false;
boolean isUnderEnd = false;
int i = 1;
for (String word : words) {
if (word.equals("<")) {
isUnderStart = true;
}
if (word.equals(">")) {
isUnderEnd = true;
}
text = new CustomText(word, isUnderStart, isUnderEnd);
isUnderStart = false;
isUnderEnd = false;
paragraph.add(text);
i++;
}
doc.add(paragraph);
doc.close();
Why printed me one line in SWT Eclipse (Text). I wanted to print multi line in my Text. That is my code. From my printer i got one line of my message instead of many rows in my print
Button btnPrint = new Button(shell, SWT.NONE);
btnPrint.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
PrintDialog printDialog = new PrintDialog(shell, SWT.MULTI| SWT.BORDER );
printDialog.setText("Print");
PrinterData printerData = printDialog.open();
if (!(printerData == null)) {
Printer p = new Printer(printerData);
p.startJob("PrintJob");
p.startPage();
Rectangle trim = p.computeTrim(0,0,0,0);
Point dpi = p.getDPI();
int leftMargin = dpi.x + trim.x;
int topMargin = dpi.y / 2 + trim.y;
GC gc = new GC(p);
Font font = gc.getFont();
String printText = text.getText();
Point extent = gc.stringExtent(printText);
gc.drawString(printText, leftMargin, topMargin+ font.getFontData()[0].getHeight());
p.endPage();
gc.dispose();
p.endJob();
p.dispose();
}
}
});
gc.drawString does not look for line delimiters.
Use gc.drawText:
public void drawText(String string, int x, int y)
which does look for line delimiters (and tabs). Or use
public void drawText(String string, int x, int y, int flags)
and specify SWT.DRAW_DELIMITER for the flags value
I'm trying to highlight the bullet background of the currently active line but if I just set the background color of the bullet it only highlights the number portion. I would like all the space occupied by the bullet to highlighted.
I would like all the space to the left of the 9 to be highlighted as well and probably a bit to the right too.
The code I'm using to get what I have so far is
#Override
public void lineGetStyle(LineStyleEvent event) {
// Set the line number
int activeLine = styledText.getLineAtOffset(styledText.getCaretOffset());
int currentLine = styledText.getLineAtOffset(event.lineOffset);
event.bulletIndex = currentLine;
int width = 36;
if (styledText.getLineCount() > 999)
width = (int) ((Math.floor(Math.log10(styledText.getLineCount()))+1) * 12);
// Set the style, 12 pixles wide for each digit
StyleRange style = new StyleRange();
style.metrics = new GlyphMetrics(0, 0, width);
if (activeLine == currentLine) {
style.background = highlightedLine;
}
style.foreground = mainBackground;
// Create and set the bullet
event.bullet = new Bullet(ST.BULLET_NUMBER, style);
event.styles = matchKeywords(event);
}
Is this possible?
You can do this with custom painting. First, change the bullet type to ST.BULLET_CUSTOM. Then add a PaintObjectListener:
styledText.addPaintObjectListener(new PaintObjectListener() {
public void paintObject(PaintObjectEvent event) {
drawBullet(event.bullet, event.gc, event.x, event.y, event.bulletIndex, event.ascent, event.descent);
}
});
There is a standard implementation of drawBullet in StyledTextRenderer. I've changed this to accept custom bullets as numbered bullets, and to draw an extra rectangle in the background:
void drawBullet(Bullet bullet, GC gc, int paintX, int paintY, int index, int lineAscent, int lineDescent) {
StyleRange style = bullet.style;
GlyphMetrics metrics = style.metrics;
Color color = style.foreground;
if (color != null) gc.setForeground(color);
Font font = style.font;
if (font != null) gc.setFont(font);
String string = "";
int type = bullet.type & (ST.BULLET_DOT|ST.BULLET_CUSTOM|ST.BULLET_NUMBER|ST.BULLET_LETTER_LOWER|ST.BULLET_LETTER_UPPER);
switch (type) {
case ST.BULLET_DOT: string = "\u2022"; break;
case ST.BULLET_CUSTOM: string = String.valueOf(index + 1); break;
case ST.BULLET_NUMBER: string = String.valueOf(index + 1); break;
case ST.BULLET_LETTER_LOWER: string = String.valueOf((char) (index % 26 + 97)); break;
case ST.BULLET_LETTER_UPPER: string = String.valueOf((char) (index % 26 + 65)); break;
}
if ((bullet.type & ST.BULLET_TEXT) != 0) string += bullet.text;
gc.setBackground(style.background);
gc.fillRectangle(paintX, paintY, metrics.width, styledText.getLineHeight());
Display display = styledText.getDisplay();
TextLayout layout = new TextLayout(display);
layout.setText(string);
layout.setAscent(lineAscent);
layout.setDescent(lineDescent);
style = (StyleRange)style.clone();
style.metrics = null;
if (style.font == null) style.font = styledText.getFont();
layout.setStyle(style, 0, string.length());
int x = paintX + Math.max(0, metrics.width - layout.getBounds().width - 8);
layout.draw(gc, x, paintY);
layout.dispose();
}
In my Java application, I was able to get the Color of a JButton in terms of red, green and blue; I have stored these values in three ints.
How do I convert those RGB values into a String containing the equivalent hexadecimal representation? Such as #0033fA
You can use
String hex = String.format("#%02x%02x%02x", r, g, b);
Use capital X's if you want your resulting hex-digits to be capitalized (#FFFFFF vs. #ffffff).
A one liner but without String.format for all RGB colors:
Color your_color = new Color(128,128,128);
String hex = "#"+Integer.toHexString(your_color.getRGB()).substring(2);
You can add a .toUpperCase()if you want to switch to capital letters. Note, that this is valid (as asked in the question) for all RGB colors.
When you have ARGB colors you can use:
Color your_color = new Color(128,128,128,128);
String buf = Integer.toHexString(your_color.getRGB());
String hex = "#"+buf.substring(buf.length()-6);
A one liner is theoretically also possible but would require to call toHexString twice. I benchmarked the ARGB solution and compared it with String.format() and the toHexString solution has a much higher performance:
Random ra = new Random();
int r, g, b;
r=ra.nextInt(255);
g=ra.nextInt(255);
b=ra.nextInt(255);
Color color = new Color(r,g,b);
String hex = Integer.toHexString(color.getRGB() & 0xffffff);
if (hex.length() < 6) {
hex = "0" + hex;
}
hex = "#" + hex;
Convert a java.awt.Color to a 24-bit hexadecimal RGB representation even if alpha channel value is zero (e.g. 0000ff):
String.format("%06x", 0xFFFFFF & Color.BLUE.getRGB())
For uppercase (e.g. 0000FF) :
String.format("%06X", 0xFFFFFF & Color.BLUE.getRGB())
This is an adapted version of the answer given by Vivien Barousse with the update from Vulcan applied. In this example I use sliders to dynamically retreive the RGB values from three sliders and display that color in a rectangle. Then in method toHex() I use the values to create a color and display the respective Hex color code.
This example does not include the proper constraints for the GridBagLayout. Though the code will work, the display will look strange.
public class HexColor
{
public static void main (String[] args)
{
JSlider sRed = new JSlider(0,255,1);
JSlider sGreen = new JSlider(0,255,1);
JSlider sBlue = new JSlider(0,255,1);
JLabel hexCode = new JLabel();
JPanel myPanel = new JPanel();
GridBagLayout layout = new GridBagLayout();
JFrame frame = new JFrame();
//set frame to organize components using GridBagLayout
frame.setLayout(layout);
//create gray filled rectangle
myPanel.paintComponent();
myPanel.setBackground(Color.GRAY);
//In practice this code is replicated and applied to sGreen and sBlue.
//For the sake of brevity I only show sRed in this post.
sRed.addChangeListener(
new ChangeListener()
{
#Override
public void stateChanged(ChangeEvent e){
myPanel.setBackground(changeColor());
myPanel.repaint();
hexCode.setText(toHex());
}
}
);
//add each component to JFrame
frame.add(myPanel);
frame.add(sRed);
frame.add(sGreen);
frame.add(sBlue);
frame.add(hexCode);
} //end of main
//creates JPanel filled rectangle
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawRect(360, 300, 10, 10);
g.fillRect(360, 300, 10, 10);
}
//changes the display color in JPanel
private Color changeColor()
{
int r = sRed.getValue();
int b = sBlue.getValue();
int g = sGreen.getValue();
Color c;
return c = new Color(r,g,b);
}
//Displays hex representation of displayed color
private String toHex()
{
Integer r = sRed.getValue();
Integer g = sGreen.getValue();
Integer b = sBlue.getValue();
Color hC;
hC = new Color(r,g,b);
String hex = Integer.toHexString(hC.getRGB() & 0xffffff);
while(hex.length() < 6){
hex = "0" + hex;
}
hex = "Hex Code: #" + hex;
return hex;
}
}
A huge thank you to both Vivien and Vulcan. This solution works perfectly and was super simple to implement.
slightly modified versions for RGBA from How to convert a color integer to a hex String in Android?
and How to code and decode RGB to Hex
public static String ColorToHex (Color color) {
int red = color.getRed();
int green = color.getGreen();
int blue = color.getBlue();
int alpha = color.getAlpha();
String redHex = To00Hex(red);
String greenHex = To00Hex(green);
String blueHex = To00Hex(blue);
String alphaHex = To00Hex(alpha);
// hexBinary value: RRGGBBAA
StringBuilder str = new StringBuilder("#");
str.append(redHex);
str.append(greenHex);
str.append(blueHex);
str.append(alphaHex);
return str.toString();
}
private static String To00Hex(int value) {
String hex = "00".concat(Integer.toHexString(value));
hex=hex.toUpperCase();
return hex.substring(hex.length()-2, hex.length());
}
another way, this one could be related to the benchmark above:
public static String rgbToHex (Color color) {
String hex = String.format("#%02x%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha() );
hex=hex.toUpperCase();
return hex;
}
a very simple benchmark shows that solution with String.format is 2+ times slower than StringBuilder for 10 million color conversions. For small amount of objects you cannot really see a difference.
I am not an expert so my opinion is subjective. I'm posting the benchmark code for any use, replace methods rgbToHex, rgbToHex2 with those you want to test:
public static void benchmark /*ColorToHex*/ () {
Color color = new Color(12,12,12,12);
ArrayList<Color> colorlist = new ArrayList<Color>();
// a list filled with a color i times
for (int i = 0; i < 10000000; i++) {
colorlist.add((color));
}
ArrayList<String> hexlist = new ArrayList<String>();
System.out.println("START TIME... " + ZonedDateTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME) + " TEST CASE 1...");
for (int i = 0; i < colorlist.size(); i++) {
hexlist.add(rgbToHex(colorlist.get(i)));
}
System.out.println("END TIME... " + ZonedDateTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME) + " TEST CASE 1...");
System.out.println("hexlist.get(0)... "+hexlist.get(0));
ArrayList<String> hexlist2 = new ArrayList<String>();
System.out.println("START TIME... " + ZonedDateTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME) + " TEST CASE 2...");
for (int i = 0; i < colorlist.size(); i++) {
hexlist2.add(rgbToHex1(colorlist.get(i)));
}
System.out.println("END TIME... " + ZonedDateTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME) + " TEST CASE 2...");
System.out.println("hexlist2.get(0)... "+hexlist2.get(0));
}
it seems that there are issues with Integer.toHexString(color.getRGB())
try it with Color color = new Color(0,0,0,0); and you will find out that we have subtraction of zeros. #0 instead of #00000000 and we need all digits in order to have valid hex color values, 6 or 8 if with Alpha. So as far as I can see we need an improved use of Integer.toHexString to handle those cases. There should be other cases that cannot handle leading zeros at hex values. For example try with #0c0c0c0c that corresponds to Color color = new Color(12,12,12,12); The result will be #C0C0C0C witch is wrong.
Here is a 2 line version that also encodes the alpha value. These other examples are too verbose.
int rgba = (color.getRGB() << 8) | color.getAlpha();
return String.format("#%08X", rgba);