En la entrada anterior sobre apache POI, vimos como crear archivos excel con Java y Apache POI. En esta ocasión, véremos como personalizar el estilo de los archivos excel generados.
Cómo crear un estilo de celda
La clase que representa un estilo de una celda es HSSFCellStyle. Nos permite definir características propias de la celda: tipo de borde, color del borde, alineación del contenido, color de fondo, etc. Para crear un estilo de celda, solo necesitamos una referencia al libro de trabajo de excel:
HSSFWorkbook workbook = new HSSFWorkbook(); HSSFCellStyle style = workbook.createCellStyle(); |
Una vez creado el estilo, podemos invocar sus métodos set...(), para configurar cada una de sus propiedades. La clase HSSFCellStyle posee una serie de propiedes definidas que nos ayudarán a configurar nuestro estilo. Así mismo, la clase que nos permite definir los colores utilizados en nuestros estilos es HSSFColor:
// Sets the cell background style.setFillForegroundColor(HSSFColor.BLUE_GREY.index); style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); |
Probablemente, también querremos modificar las propiedades del texto que se muestra en las celdas. Para hacer esto, debemos crear un estilo de fuente (a continuación).
Cómo crear un estilo de fuente
Los estilos de fuente está representados por la clase HSSFFont. Esta clase nos da control sobre la fuente tipográfica usada, color del texto, negrita, cursiva, etc. Al igual que los estilos de celdas, podemos crear estilos de fuentes a partir del libro de trabajo:
HSSFFont font = workbook.createFont(); |
Una vez tenemos la fuente, la asignamos al estilo donde queremos aplicarla:
style.setFont(font); |
Ejemplo Completo
Como ejemplo, vamos a generar un archivo excel tal que así:
Utilizaremos una clase auxiliar llamada FakeDataProvider, que simplemente nos devuelve de datos de prueba para rellenar la tabla:
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | public final class FakeDataProvider { /** Return the columns name for the table */ public static List<String> getTableHeaders() { List<String> tableHeader = new ArrayList<String>(); tableHeader.add("Name"); tableHeader.add("Address"); tableHeader.add("Phone"); return tableHeader; } /** * Return values for the table * * @param numberOfRows Number of rows we want to receive * * @return Values */ public static List<List<String>> getTableContent(int numberOfRows) { if (numberOfRows <= 0) { throw new IllegalArgumentException("The number of rows must be a positive integer"); } List<List<String>> tableContent = new ArrayList<List<String>>(); List<String> row = null; for (int i = 0; i < numberOfRows; i++) { tableContent.add(row = new ArrayList<String>()); row.add("my name is " + i); row.add("my address is " + i); row.add("my phone is " + i); } return tableContent; } } |
La clase encargada de la generación del excel es ExcelGenerator. Tiene un sólo método público, generateExcel(), que devuelve el excel generado:
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | public class ExcelGenerator { // Excel work book private HSSFWorkbook workbook; // Fonts private HSSFFont headerFont; private HSSFFont contentFont; // Styles private HSSFCellStyle headerStyle; private HSSFCellStyle oddRowStyle; private HSSFCellStyle evenRowStyle; // Integer to store the index of the next row private int rowIndex; /** * Make a new excel workbook with sheet that contains a stylized table * * @return */ public HSSFWorkbook generateExcel() { // Initialize rowIndex rowIndex = 0; // New Workbook workbook = new HSSFWorkbook(); // Generate fonts headerFont = createFont(HSSFColor.WHITE.index, (short)12, true); contentFont = createFont(HSSFColor.BLACK.index, (short)10, false); // Generate styles headerStyle = createStyle(headerFont, HSSFCellStyle.ALIGN_CENTER, HSSFColor.BLUE_GREY.index, true, HSSFColor.WHITE.index); oddRowStyle = createStyle(contentFont, HSSFCellStyle.ALIGN_LEFT, HSSFColor.GREY_25_PERCENT.index, true, HSSFColor.GREY_80_PERCENT.index); evenRowStyle = createStyle(contentFont, HSSFCellStyle.ALIGN_LEFT, HSSFColor.GREY_40_PERCENT.index, true, HSSFColor.GREY_80_PERCENT.index); // New sheet HSSFSheet sheet = workbook.createSheet("Very Cool Sheet"); // Table header HSSFRow headerRow = sheet.createRow( rowIndex++ ); List<String> headerValues = FakeDataProvider.getTableHeaders(); HSSFCell headerCell = null; for (int i = 0; i < headerValues.size(); i++) { headerCell = headerRow.createCell(i); headerCell.setCellStyle(headerStyle); headerCell.setCellValue( headerValues.get(i) ); } // Table content HSSFRow contentRow = null; HSSFCell contentCell = null; // Obtain table content values List<List<String>> contentRowValues = FakeDataProvider.getTableContent(20); for (List<String> rowValues : contentRowValues) { // At each row creation, rowIndex must grow one unit contentRow = sheet.createRow( rowIndex++ ); for (int i = 0; i < rowValues.size(); i++) { contentCell = contentRow.createCell(i); contentCell.setCellValue( rowValues.get(i) ); // Style depends on if row is odd or even contentCell.setCellStyle( rowIndex % 2 == 0 ? oddRowStyle : evenRowStyle ); } } // Autosize columns for (int i = 0; i < headerValues.size(); sheet.autoSizeColumn(i++)); return workbook; } /** * Create a new font on base workbook * * @param fontColor Font color (see {@link HSSFColor}) * @param fontHeight Font height in points * @param fontBold Font is boldweight (<code>true</code>) or not (<code>false</code>) * * @return New cell style */ private HSSFFont createFont(short fontColor, short fontHeight, boolean fontBold) { HSSFFont font = workbook.createFont(); font.setBold(fontBold); font.setColor(fontColor); font.setFontName("Arial"); font.setFontHeightInPoints(fontHeight); return font; } /** * Create a style on base workbook * * @param font Font used by the style * @param cellAlign Cell alignment for contained text (see {@link HSSFCellStyle}) * @param cellColor Cell background color (see {@link HSSFColor}) * @param cellBorder Cell has border (<code>true</code>) or not (<code>false</code>) * @param cellBorderColor Cell border color (see {@link HSSFColor}) * * @return New cell style */ private HSSFCellStyle createStyle(HSSFFont font, short cellAlign, short cellColor, boolean cellBorder, short cellBorderColor) { HSSFCellStyle style = workbook.createCellStyle(); style.setFont(font); style.setAlignment(cellAlign); style.setFillForegroundColor(cellColor); style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); if (cellBorder) { style.setBorderTop(HSSFCellStyle.BORDER_THIN); style.setBorderLeft(HSSFCellStyle.BORDER_THIN); style.setBorderRight(HSSFCellStyle.BORDER_THIN); style.setBorderBottom(HSSFCellStyle.BORDER_THIN); style.setTopBorderColor(cellBorderColor); style.setLeftBorderColor(cellBorderColor); style.setRightBorderColor(cellBorderColor); style.setBottomBorderColor(cellBorderColor); } return style; } } |
Ya sólo nos quedaría utilizarlo desde la clase principal y escribir el excel generado en disco:
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public class App { public static void main(String[] args) { HSSFWorkbook workbook = new ExcelGenerator().generateExcel(); // Writing the excel to output file try { OutputStream out = new FileOutputStream("src/main/resources/ExcelWithStyles.xls"); workbook.write(out); workbook.close(); out.flush(); out.close(); } catch (IOException e) { System.err.println("Error at file writing"); e.printStackTrace(); } } } |
Estilos con nombre
Si definimos un nombre de usuario para el estilo, éste aparecerá entre los estilos predefinidos al abrir el documento desde Microsoft Excel. Por ejemplo, si hubiéramos hecho lo siguiente:
headerStyle.setUserStyleName("Header"); |
Nuestro estilo estaría disponible para el usuario:
Estilos sobre filas
Al igual que las celdas, las filas (HSSFRow) tienen un método setRowStyle(). Es importante tener en cuenta que si aplicamos un estilo sobre una fila, éste sólo se aplica a las celdas vacías. Es decir, que en cuanto creemos una celda sobre esa fila invocando a sú método createCell(), ésta no usará el estilo de la fila.
Optimización
Tanto los estilos (HSSFCellStyle) como las fuentes (HSSFFont) son creados a nivel de libro excel.
Es necesario tenerlo en cuenta porque, si necesitamos aplicar un mismo estilo a varias celdas, lo correcto es crearlo una sola vez y aplicarlo a todas las celdas. Si en lugar de eso, creamos el estilo de nuevo cada vez que tenemos que aplicarlo a una celda, estaremos multiplicando el estilo tantas veces como celdas haya. Esto no es apreciable al mostrar el archivo excel generado, ya que todos los estilos son iguales, pero sí que tendremos un archivo generado de mayor tamaño.
De igual forma, si queremos aplicar el mismo estilo de texto a varios estilos de celda, lo correcto es utilizar una misma fuente para todos los estilos, en lugar de crear una nueva fuente para cada estilo que la utilice.
Código fuente
Puedes descargar el proyecto de ejemplo mi repositorio de GitHub. El ejemplo mostrado corresponde a Poi-ExcelWithStyles.
Saludos,

Hola, buenas tardes.
Estoy generando archivos xls con la libreria npoi en visual basic .net, pero cuando genero un archivo con varias hojas y les doy estilo, en algunas ya no quiere ponerlo, sabes a que se debe esta situación?
Gracias
Hola Sandra,
Es complicado saber exactamente la causa del error sin ver el código, pero te aconsejo que revises:
Siento no poder darte más ayuda en este momento
Saludos,
Hola, buenos días, gracias por responder.
Ya revisé lo que me sugeriste y al parecer todo esta bien, hice una prueba generando archivos por separado en vez de uno solo con hoja y cuando los crea por separado todo el formato lo pone muy bien, pero al momento de quererlo generar por hojas como que pierde los estilos y las fuentes.
Ahora la idea es generarlos por separado y concatenar esos archivos en uno solo pero no lo he logrado este es el código:
Dim CnH As Integer
Dim ArchivoFinal As New HSSFWorkbook
Dim ArchivoFinal2 As New HSSFWorkbook
Dim ArchivoH As HSSFWorkbook
Dim sheetCH As HSSFSheet
Dim file2 As FileStream
Try
For CnH = 1 To totalH
ArchivoH = New HSSFWorkbook(New FileStream(RutaPrincipal & «Temp\\» & filename & «_» & CnH & «.xls», FileMode.Open))
sheetCH = ArchivoH.GetSheetAt(0)
sheetCH.CopyTo(ArchivoFinal, sheetCH.SheetName, True, True)
Next CnH
file2 = New FileStream(RutaPrincipal & «Temp\\» & filename & «.xls», FileMode.Create, FileAccess.ReadWrite)
ArchivoFinal.Write(file2)
file2.Close()
Catch ex As Exception
GrabaError(ex.ToString())
End Try
me da el error «Referencia a objeto no establecida como instancia de un objeto»
Esto lo marca en la línea ArchivoFinal.Write(file2)
Sabes a que se debe esta situación?
Buenos días:
Me gustaría saber si se puede poner dos estilos distintos de letra dentro de la misma celda ( una palabra en cursiva y otra en negrita).
En caso de que sí. ¿Como se hace?
Gracias
Hola Rubén,
No puedo ponerte un ejemplo completo en este momento, pero sí que es posible hacer lo que dices. Tendrás que poner como contenido de esa celda un
RichTextString. En ese objeto es posible definir distintos formatos para partes de un mismo texto.Gracias por comentar y un saludo,
Buenas, estoy generando un Excel desde java, quiero ajustar las columnas a en relación a su contenido. Uso la función:
hoja.autoSizeColumn((short)0 )
el tema es que con el mismo código funciona en el pc de mi compañero, generándose el tamaño correcto, pero con mi pc en mi Excel no hace caso, y se quedan sin ajustar las columnas.
Que puede pasar? Supongo que debe ser la configuración del Excel, pero no doy con la tecla correcta.
Un saludo.
Abel
Buenas tardes, no sé si sigue activo este tema, pero tengo una duda sobre como combinar 2 columnas que se encuentren en filas distintas, una debajo de la otra, en realidad lo hago con : hoja.addMergedRegion(new CellRangeAddress(0,1,0,2));
los 2 primeros parametros son las filas, y los otros 2 son las columnas que se quiere combinar, eso lo entiendo, pero en este ejemplo especifico solo combina las celdas de la primera columna, si yo necesito combinar 2 celdas una debajo de la otra de la columna 5.
En este momento sería de mucha ayuda.
La columna 5 es inidice 4 harías
hoja.addMergedRegion(new CellRangeAddress(0,1,5,5));