2016-05-11 8 views
0

私はapache-poiを使って動的に新しいxlsxファイルを作成しています。どの列にも異なる値型(数値、文字列、ブール値など)を含めることができます。私はデータの種類に応じてCellStylesを設定ポイドキュメントにデータを挿入しながら:Apache POI:異なるスタイルを含む列に罫線を設定するためのエレガントな方法

public final XSSFCellStyle cellStyleString; 
public final XSSFCellStyle cellStyleNumber; 
public final XSSFCellStyle cellStyleDate; 
public final XSSFCellStyle cellStyleHeader; 

これは私のヘッダー行がどのように見えるかです:

| | | | Shared Header | 
| H1| H2| H3|SH1|SH2|SH3|SH4| 

あり、「シンプル」ヘッダがあると「共有します「サブヘッダ」を含む「ヘッダ」を含む。共有ヘッダーは結合されたセルにあります。

いいえいいえ、私は左の枠をSH1に、右の枠を列SH4にして、グループ化を強調したいと思います。私は別のボーダーサイズによって区別したい

public final XSSFCellStyle cellStyleString; 
public final XSSFCellStyle cellStyleStringBorderLeft; 
public final XSSFCellStyle cellStyleStringBorderRight; 
//and so on for the other styles... 

さらにそこにネストすることができ、共有ヘッダのようなCellStylesを作成する必要がありますように。しかし、すべてのcellstylesの混在する可能性が任意の列として、それはそうです。だから私は関係なく、既存のスタイルの列の境界線を設定するために、よりエレガントな方法はあります

public final XSSFCellStyle cellStyleString; 
public final XSSFCellStyle cellStyleStringBorderLeftThickLine; 
public final XSSFCellStyle cellStyleStringBorderRightThickLine; 
public final XSSFCellStyle cellStyleStringBorderLeftThinLine; 
public final XSSFCellStyle cellStyleStringBorderRightThinLine; 
//and so on for the other styles... 

ようなものが必要だろうか?

編集

私はクリーンでシンプルなアプローチを好むし、作成したスタイルの数を最小限にするためのものが、私は重複cellstylesを削除HSSFOptimiser時stumpled。私はそのクラスについて知らなかった。このユーティリティを避けることを好むとはいえ、問題に合っており、ここで言及する価値があります。

答えて

5

POIの拡張の終わりに近づいており、特定のスタイルで値を記入し、必要なすべてのスタイルを手動で作成しなくても境界線を描くことができますそれのために。その間に、CellUtil.setCellStyleProperties()を使ってそれを行う方法があります。これにより、セルに既に存在するCellStyleに一連のプロパティを追加できます。 HSSF/XSSFためのPOIクイックガイドから

Workbook workbook = new XSSFWorkbook(); // OR new HSSFWorkbook() 
Sheet sheet = workbook.createSheet("Sheet1"); 
Map<String, Object> properties = new HashMap<String, Object>(); 

// create your spreadsheet without borders 
... 

// create property set for vertical borders 
properties.put(CellUtil.BORDER_LEFT, CellStyle.BORDER_MEDIUM); 
properties.put(CellUtil.BORDER_RIGHT, CellStyle.BORDER_MEDIUM); 

// Apply the borders to a 3x3 region starting at D4 
for (int ix=3; ix <= 5; ix++) { 
    row = sheet.createRow(ix); 
    for (int iy = 3; iy <= 5; iy++) { 
    cell = row.createCell(iy); 
    CellUtil.setCellStyleProperties(cell, properties); 
    } 
} 

これはその後、一度に一つのセルの境界線を描く、あなたは基本的にスプレッドシートに記入することができます。すべての罫線が似ている場合(すべてTHIN)、これはあなたの全範囲で機能します。しかし、テーブルの外側にMEDIUM枠を描きたい場合、追加のプロパティセットをいくつか作成する必要があります。スプレッドシートに既に含まれている行とセルにcreateRow()createCell()を使用する必要はありません。これは、結合されたセルの周りで動作します。

注:CellUtil.setCellStyleProperties()はPOI 3.14に登場し、1回のショットで複数のセルプロパティを追加できるため、未使用の複数のスタイルが作成されることはありません。古いCellUtil.setCellStyleProperty()は一度に1つのプロパティを設定し、意図しない結果としてスプレッドシートに中間のCellStyleオブジェクトを作成し、これは決して使用されません。これは大きな用紙では問題になる可能性があります。

編集: PropertyTemplateはPOI 3.15で追加された新しいオブジェクトで、セルの枠線のグループを定義し、適用するシートにスタンプすることができます。このオブジェクトは、データをオーバーレイするためのあらかじめ印刷されたフォームを作成するようなものです。 PropertyTemplateの使用方法の詳細については、POIスプレッドシートクイックガイドを参照してください。

+0

このソリューションは素晴らしいサウンドです。悲しいことに、私は別の作業に進む必要があったので、今すぐテストすることはできません... CellStylesはこの方法で再利用できますか?言い換えれば:遭遇したセルごとに、または遭遇したcellStyleごとに、新しいCellStyleを作成するか? – samjaf

+0

私は依存関係の階層をちょうど読みました:私たちはまだpoi 3.9を使っています。しかし、私は古いメソッド 'setCellStyleProperty'を使って未使用の中間形式を作成することは受け入れられると思います。合計スタイルはほとんどなく、変更できるのはセルあたり最大2つの境界だけです。 30%の未使用スタイルを持つことは、30,000を超える疑似ユニークスタイルよりも優れています。私の以前の質問は、 'setCellStyleProperty'のソースを読んで、すでに知られているスタイルを再利用している部分を見つけて、今は時代遅れです。どうもありがとう! – samjaf

+0

'setCellStyleProperty'を使用すると、セルから' CellStyle'を取得し、新しいプロパティがマージされた 'CellStyle'模様を作成し、ワークブックに相当する' CellStyle'を見つけようとします。存在しない場合、新しい「CellStyle」が作成され、セルに適用されます。 'setCellStyleProperties'は、一度に1つのメソッドに対してプロパティグループ全体に対してこれを行うことを可能にします。垂直の枠線を作成している場合は、枠線を追加した順番に応じて、左端の枠線のみ、または右端の枠線だけを使用して、未使用のスタイルを束ねることになります。 – jmarkmurphy

1

EDIT:

それでは、どのようにキャッシュするためにPOIオブジェクトのハッシュを利用して装飾されたオブジェクトを追跡する程度。使用されていない他の作成されたCellStylesは、ガベージコレクションによって破棄されます。

final class MyCellStyle implements Cloneable { 
    private XSSFCellStyle xssfCellStyle; 

    public MyCellStyle(XSSFCellStyle xssfCellStyle) { 
     this.xssfCellStyle = xssfCellStyle; 
    } 

    @Override 
    public MyCellStyle clone() { 
     MyCellStyle clone = new MyCellStyle(xssfCellStyle); 
     return clone; 
    } 

    public final MyCellStyle borderLeftMedium() { 
     MyCellStyle result = clone(); 
     result.xssfCellStyle.setBorderLeft(XSSFCellStyle.BORDER_MEDIUM); 
     return result; 
    } 

    ... further decorations 

    public XSSFCellStyle getXSSFCellStyle() { 
     return xssfCellStyle; 
    } 

} 

新しいオブジェクトを作成しないようにするには、我々は

private MyCellStyle getCellStyle(MyCellStyle targetStyle) { 
    int targetHash = targetStyle.hashCode(); 
    if (styleCache.keySet().contains(targetHash)) { 
     return styleCache.get(targetHash); 
    } else { 
     return styleCache.put(targetHash, targetStyle); 
    } 
} 

その後、私たちが作成できる小さな関数を書く

private Map<Integer, MyCellStyle> styleCache = new HashMap<>(); 

そして私たち自身のCellStyleクラス:ここ

は、当社のキャッシュです細胞そのものは次のようになります:

public void createCells() { 
    Workbook wb = new XSSFWorkbook(); 
    Sheet sheet = wb.createSheet(); 

    Row row = sheet.createRow(1); 
    Cell cell = row.createCell(1); 

    MyCellStyle baseStyle = new MyCellStyle(
      (XSSFCellStyle) wb.createCellStyle()); 

    MyCellStyle decoratedStyle = getCellStyle(baseStyle.borderLeftMedium()); 

    cell.setCellStyle(decoratedStyle.getXSSFCellStyle()); 

} 

のhashCodeがMyCellStyleオブジェクトの同じプロパティに対して一意でない場合、我々はhashCodeを機能上書きする必要がある場合があります:

@Override 
public int hashCode() { 
    return hashValue; 
} 

を、私たちの装飾機能の各々の中のスタイル値を追加します。

public final MyCellStyle borderLeftMedium() { 
     MyCellStyle result = clone(); 
     result.xssfCellStyle.setBorderLeft(XSSFCellStyle.BORDER_MEDIUM); 
     hashValue += XSSFCellStyle.BORDER_MEDIUM; // simplified hash 
     return result; 
    } 

=======================

ORIGINAL:

私は、セルスタイルにセルの特定のアスペクトを追加する装飾方法を作成するのが好きです。だから、最初にあなたは、あなたの基本スタイル

public final XSSFCellStyle cellStyleStringBase = wb.createCellStyle(); 

を作成し、

public XSSFCellStyle addBorderLeft(XSSFCellStyle style) { 
    XSSFCellStyle result = style.clone(); 
    result.setBorderLeft(XSSFCellStyle.BORDER_MEDIUM); 
    return result; 
} 

特定のスタイルを作成するために、デコレータのメソッドを作成するには、新しいオブジェクトを作成しないようにしたい場合は今、あなたはまだ維持する必要があります独自の変数でcellStyles、あなたはそれを避けることはできませんが、あなたが飾る場合は、単にこの

cell1.setCellStyle(addBorderLeft(cellStyleStringBase); 
cell2.setCellStyle(addBorderRight(addBorderRight(cellStyleStringBase)); 
... 

のようなあなたの細胞を飾る場合は、私の経験から、パフォーマンスが十分ですそれは理にかなっているスタイルの多くは、独自のCellStyleクラス

public final MyCellStyle implements Cloneable { 

    private XSSFCellStyle xssfCellStyle; 

    public MyCellStyle(XSSFCellStyle xssfCellStyle) { 
     this.xssfCellStyle = xssfCellStyle; 
    } 

    @Override 
    public MyCellStyle clone() { 
     MyCellStyle clone = new MyCellStyle(this.xssfCellStyle); 
     return clone; 
    } 

    public final MyCellStyle borderLeftMedium() { 
     return this.clone().setBorderLeft(XSSFCellStyle.BORDER_MEDIUM); 
    } 

    public final MyCellStyle borderRightThick() { 
     ... 

} 

あなたがより良い読めるようにし、あなたのスタイルを構築することができます作成​​します。

MyCellStyle base = new MyCellStyle(cellStyleStringBase);  
cell1.setCellStyle(base 
    .addBorderLeftMedium() 
    .addBorderRightThick() 
    .addBorderBottomThin()); 

テストされていないが、私はそれが役に立てば幸い。

+0

各ボーダーセルに新しいスタイルを作成すると、何万もの冗長なセルスタイルが完成します。いくつかの理由から、これは受け入れられません。 – samjaf

+0

あなたの編集をありがとう。しかし、それはまだ各装飾のための新しいスタイルを作成することに依存しているので、この解決法も受け入れられません。 – samjaf

+0

これは、同一のCellStyleの多くの、多くの、多くのインスタンスを作成し、Excelファイルのサイズを増加させます。 –

1

すでに言及したように、何千もの類似したセルスタイルのオブジェクトを作成することは好ましくありません。私のプロジェクトでは、私はすべての既存のスタイル・インスタンス

private Workbook workbook; 
private HashMap<String, CellStyle> styleMap = new HashMap<>(); 

public CellStyle getStyle(Font font, ExcelCellAlign hAlign, ExcelCellAlign vAlign, boolean wrapText, ExcelCellBorder border, Color color, ExcelCellFormat cellFormat) { 

    //build unique which represents the style 
    String styleKey = ((font != null) ? font.toString() : "") + "_" + hAlign + "_" + vAlign + (wrapText ? "_wrapText" : "") + ((border != null) ? "_" + border.toString() : "") + "_" 
      + styleKeyColor + (cellFormat != null ? "_" + cellFormat.toString() : ""); 

    if (styleMap.containsKey(styleKey)) { 
     //return existing instance from map 
     return styleMap.get(styleKey); 
    } else { 
     //create new style from workbook 
     CellStyle cellStyle = workbook.createCellStyle(); 


     // set all formattings to new cellStyle object 
     if (font != null) { 
      cellStyle.setFont(font); 
     } 

     // alignment 
     if (vAlign != null) { 
      cellStyle.setVerticalAlignment(vAlign.getAlign()); 
     } 

     //... snip ... 

     //border 
     if (border != null) { 
      if (border.getTop() > BorderFormatting.BORDER_NONE) { 
       cellStyle.setBorderTop(border.getTop()); 
       cellStyle.setTopBorderColor(HSSFColor.BLACK.index); 
      } 

      //... snip ... 
     } 

      if (color != null) { 
       XSSFColor xssfColor = new XSSFColor(color); 
       ((XSSFCellStyle)cellStyle).setFillForegroundColor(xssfColor); 
      } 
     } 
     cellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND); 

     styleMap.put(styleKey, cellStyle); 

     return cellStyle; 
    } 
} 

パラメータExcelCellAlignがCellStyleの値をカプセル化する簡単な列挙型であるのを認識していることでマップを持っているシンプルな「スタイルヘルパー」クラスを作成しました。 ALIGN_LEFT、CellStyle.ALIGN_RIGHT、... ExcelCellBorderは、Alignと似ています。値を隠すだけです:-) ExcelCellFormatは、値を確定するためのデフォルトパターンを保持する列挙型です。

私はこれがあなた自身の実装のための良いスタートであることを願っています。何かが明確でないかどうかお気軽にお問い合わせください

関連する問題