[]
GcExcel Java为添加自定义函数提供了广泛的支持,从而使用户能够将自定义算术逻辑实现到电子表格中。自定义函数运行速度极快,可用于进行web服务调用,工作方式与本机Excel函数类似,并可在所有Excel平台上使用,包括主要操作系统(Windows、Mac、Mobile OS和Office:在线和离线)。
例如,在处理复杂的表单计算时,可以使用公司的专有函数、将嵌套公式应用于自定义函数,或者使用标准内置函数的组合。
为了在GcExcel Java中实现自定义函数,需要从 CustomFunction 类派生一个类,并在新创建的类中声明自定义函数以及函数名、返回类型和参数。
您还可以在自定义函数中使用自定义对象,如本主题的 示例 5 所示。如果将重载 Parameter 方法的一个参数设置为 FunctionValueType.Object 并且 acceptCustomObjects 设置为True时,可以使用自定义对象。类似地,如果返回类型是 FunctionValueType.Object,该公式可以返回自定义对象。
同一列中的自定义函数将结果值存储为缓存。因此,当随后使用前一个参数调用列中的自定义函数时,自定义函数将使用缓存的值,而不是再次计算它。此功能有助于优化性能,尤其是在单个列中重复使用自定义函数的情况下。
然而,为了控制自定义函数的缓存行为,GcExcel Java在从 CustomFunction 类的继承类中提供了 setIsVolatile 方法。该方法允许您选择是每次为具有相同参数的列重新计算自定义函数,还是使用缓存结果。此方法的默认值为 false,这意味着应用于单个列的自定义函数维护自己的缓存,并在重复调用时重用它。有关实现,请参见示例6:创建易失性缓存。
在GcExcel Java中创建自定义函数涉及以下三个步骤。
步骤 1: 定义自定义函数
步骤 2: 使用 AddCustomFunction 方法在工作表中注册自定义函数
步骤 3: 实现自定义功能
下面是一些可以创建并用于执行复杂计算任务的自定义函数的示例:
**注意:**GcExcel Java不允许用户导出自定义函数,即不支持将自定义函数保存到excel文件。如果用户尝试这样做,将抛出#NAME异常。
要在表单中创建和使用自定义条件和函数,请参阅以下示例代码。此函数可以根据所需的显示格式或样式(如内部颜色为红色的单元格)对单元格值求和。
// Step 1- Defining custom function: MyConditionalSum
// Creating a new class MyConditionalSumFunctionX by inheriting the CustomFunction class
class MyConditionalSumFunctionX extends CustomFunction
{
public MyConditionalSumFunctionX()
{
super("MyConditionalSum", FunctionValueType.Number, CreateParameters());
}
private static Parameter[] CreateParameters()
{
Parameter[] parameters = new Parameter[254];
for (int i = 0; i < 254; i++)
{
parameters[i] = new Parameter(FunctionValueType.Object, true);
}
return parameters;
}
@Override
public Object evaluate(Object[] arguments, ICalcContext context)
{
double sum = 0d;
for (Object argument : arguments)
{
Iterable iterator = toIterable(argument);
for (Object item : iterator)
{
if (item instanceof CalcError)
{
return item;
}
else if (item instanceof Double)
{
sum += (double) item;
}
}
}
return sum;
}
private static Iterable toIterable(Object obj) {
if (obj instanceof Iterable)
{
return (Iterable) obj;
}
else if (obj instanceof Object[][])
{
List list = new ArrayList();
Object[][] array = (Object[][]) obj;
for (int i = 0; i < array.length; i++)
{
for (int j = 0; j < array[i].length; j++)
{
list.add(array[i][j]);
}
}
return list;
}
else if (obj instanceof CalcReference)
{
List list = new ArrayList();
CalcReference reference = (CalcReference) obj;
for (IRange range : reference.getRanges())
{
int rowCount = range.getRows().getCount();
int colCount = range.getColumns().getCount();
for (int i = 0; i < rowCount; i++)
{
for (int j = 0; j < colCount; j++)
{
if (range.getCells().get(i, j).getDisplayFormat().getInterior().getColor().equals(Color.getRed()))
{
list.add(range.getCells().get(i, j).getValue());
}
}
}
}
return list;
}
else
{
List list = new ArrayList();
list.add(obj);
return list;
}
}
}
// Step 2: Register the custom function using the AddCustomFunction method.
Workbook workbook = new Workbook();
Workbook.AddCustomFunction(new MyConditionalSumFunctionX());
IWorksheet worksheet = workbook.getActiveSheet();
// Step 3: Implement the custom function
worksheet.getRange("A1:A10").setValue(new Object[][]
{
{ 1 }, { 2 }, { 3 }, { 4 }, { 5 },
{ 6 }, { 7 }, { 8 }, { 9 }, { 10 }
});
IFormatCondition cellValueRule = (IFormatCondition) worksheet.getRange("A1:A10").getFormatConditions()
.add(FormatConditionType.CellValue, FormatConditionOperator.Greater, 5, null);
cellValueRule.getInterior().setColor(Color.getRed());
// Sum cells value which display format interior color are red.
worksheet.getRange("C1").setFormula("=MyConditionalSum(A1:A10)");
// Range["C1"]'s value is 40.
Object result = worksheet.getRange("C1").getValue();
// Display result in cell D1
worksheet.getRange("D1").setValue(result);
要在表单中创建和使用自定义连接函数,请参阅以下示例代码。
// Step 1- Defining custom function: MyConcatenate
// Creating a new class MyConcatenateFunctionX by inheriting the CustomFunction class
class MyConcatenateFunctionX extends CustomFunction
{
public MyConcatenateFunctionX() {
super("MyConcatenate", FunctionValueType.Text, CreateParameters());
}
static Parameter[] CreateParameters()
{
Parameter[] parameters = new Parameter[254];
for (int i = 0; i < 254; i++)
{
parameters[i] = new Parameter(FunctionValueType.Variant);
}
return parameters;
}
@Override
public Object evaluate(Object[] arguments, ICalcContext context)
{
StringBuilder sb = new StringBuilder();
for (Object argument : arguments)
{
if (argument instanceof CalcError)
{
return argument;
}
if (argument instanceof String || argument instanceof Double) {
sb.append(argument);
}
}
return sb.toString();
}
}
// Step 2: Register the custom function using the AddCustomFunction method.
Workbook workbook = new Workbook();
Workbook.AddCustomFunction(new MyConcatenateFunctionX());
IWorksheet worksheet = workbook.getActiveSheet();
// Step 3: Implement the custom function
worksheet.getRange("A1").setFormula("=MyConcatenate(\"I\", \" \", \"work\", \" \",
\"with\", \" \", \"GcExcel\", \".\")");
worksheet.getRange("A2").setFormula("=MyConcatenate(A1, \"Documents.\")");
// Value of cell A1 is "I work with GcExcel."
Object resultA1 = worksheet.getRange("A1").getValue();
// Value of cell A2 is "I work with GcExcel Documents."
Object resultA2 = worksheet.getRange("A2").getValue();
// Display result in cell D1
worksheet.getRange("D1").setValue(resultA2);
要在表单中创建和使用自定义合并区域函数,请参阅以下示例代码。
// Step 1- Defining custom function: MyIsMergedRange
// Creating a new class MyIsMergedRangeFunctionX by inheriting the CustomFunction class
class MyIsMergedRangeFunctionX extends CustomFunction
{
public MyIsMergedRangeFunctionX()
{
super("MyIsMergedRange", FunctionValueType.Boolean,
new Parameter[] { new Parameter(FunctionValueType.Object, true) });
}
@Override
public Object evaluate(Object[] arguments, ICalcContext context)
{
if (arguments[0] instanceof CalcReference) {
if (arguments[0] instanceof CalcReference) {
List ranges = ((CalcReference) arguments[0]).getRanges();
for (IRange range : ranges) {
return range.getMergeCells();
}
}
}
return false;
}
}
// Step 2: Register the custom function using the AddCustomFunction method.
Workbook workbook = new Workbook();
Workbook.AddCustomFunction(new MyIsMergedRangeFunctionX());
IWorksheet worksheet = workbook.getActiveSheet();
// Step 3: Implement the custom function
worksheet.getRange("A1:B2").merge();
worksheet.getRange("C1").setFormula("=MyIsMergedRange(A1)");
worksheet.getRange("C2").setFormula("=MyIsMergedRange(H2)");
// A1 is a merged cell, getRange("C1")'s value is true.
Object resultC1 = worksheet.getRange("C1").getValue();
// H2 is not a merged cell, getRange("C2")'s value is false.
Object resultC2 = worksheet.getRange("C2").getValue();
// Display result in cell D1
worksheet.getRange("D1").setValue(resultC2);
要在表单中创建和使用自定义错误检测函数,请参阅以下示例代码。
// Step 1- Defining custom function: MyIsError
// Creating a new class MyIsErrorFunctionX by inheriting the CustomFunction class
class MyIsErrorFunctionX extends CustomFunction
{
public MyIsErrorFunctionX()
{
super("MyIsError", FunctionValueType.Boolean, new Parameter[]{new Parameter(FunctionValueType.Variant)});
}
@Override
public Object evaluate(Object[] arguments, ICalcContext context)
{
if (arguments[0] instanceof CalcError)
{
if ((CalcError) arguments[0] != CalcError.None && (CalcError) arguments[0] != CalcError.GettingData)
{
return true;
} else
{
return false;
}
}
return false;
}
}
// Step 2: Register the custom function using the AddCustomFunction method.
Workbook workbook = new Workbook();
Workbook.AddCustomFunction(new MyIsErrorFunctionX());
IWorksheet worksheet = workbook.getActiveSheet();
// Step 3: Implement the custom function
worksheet.getRange("A1").setValue(CalcError.Num);
worksheet.getRange("A2").setValue(100);
worksheet.getRange("B1").setFormula("=MyIsError(A1)");
worksheet.getRange("B2").setFormula("=MyIsError(A2)");
// getRange("B1")'s value is true.
Object resultB1 = worksheet.getRange("B1").getValue();
// getRange("B2")'s value is false.
Object resultB2 = worksheet.getRange("B2").getValue();
// Display result in cell D2
worksheet.getRange("D2").setValue(resultB2);
请参考以下示例代码创建和使用 BigInteger 函数计算最大公除法。
// Formula implementation
public static class BigIntegerMultiplyFunction extends CustomFunction
{
public BigIntegerMultiplyFunction()
{
super("BIG.INTEGER.MULT", FunctionValueType.Object, new Parameter[]
{
new Parameter(FunctionValueType.Text),
new Parameter(FunctionValueType.Text)
});
}
@Override
public Object evaluate(Object[] arguments, ICalcContext context)
{
if (!(arguments[0] instanceof String) || !(arguments[1] instanceof String))
{
return CalcError.Value;
}
String leftNumber = (String)arguments[0];
String rightNumber = (String)arguments[1];
try
{
return new BigInteger(leftNumber).multiply(new BigInteger(rightNumber));
}
catch (NumberFormatException e)
{
return CalcError.Value;
}
catch (ArithmeticException e2)
{
return CalcError.Value;
}
}
}
public static class BigIntegerPowFunction extends CustomFunction
{
public BigIntegerPowFunction()
{
super("BIG.INTEGER.POW", FunctionValueType.Object, new Parameter[]
{
new Parameter(FunctionValueType.Text),
new Parameter(FunctionValueType.Number)
});
}
@Override
public Object evaluate(Object[] arguments, ICalcContext context)
{
if (!(arguments[0] instanceof String) || !(arguments[1] instanceof Double))
{
return CalcError.Value;
}
String number = (String)arguments[0];
double exp = (Double)arguments[1];
if (exp > Integer.MAX_VALUE || exp < Integer.MIN_VALUE)
{
return CalcError.Value;
}
int iExp = CInt(exp);
try
{
return new BigInteger(number).pow(iExp);
}
catch (NumberFormatException e)
{
return CalcError.Value;
}
catch (ArithmeticException e2)
{
return CalcError.Value;
}
}
public static int CInt(double source)
{
int floor = (int)Math.floor(source);
if (Math.abs(source - floor) == 0.5)
{
if (floor % 2 == 0)
return floor;
else
return (int)Math.ceil(source);
}
else if (Math.abs(source - floor) < 0.5)
return floor;
else
return (int)Math.ceil(source);
}
}
public static class GreatestCommonDivisionFunction extends CustomFunction
{
public GreatestCommonDivisionFunction()
{
super("BIG.INTEGER.GCD", FunctionValueType.Object, new Parameter[]
{
new Parameter(FunctionValueType.Object, false, true),
new Parameter(FunctionValueType.Object, false, true)
});
}
@Override
public Object evaluate(Object[] arguments, ICalcContext context)
{
if (!(arguments[0] instanceof BigInteger) || !(arguments[1] instanceof BigInteger))
{
return CalcError.Value;
}
BigInteger leftNumber = (BigInteger)arguments[0];
BigInteger rightNumber = (BigInteger)arguments[1];
try
{
return leftNumber.gcd(rightNumber);
}
catch (ArithmeticException e)
{
return CalcError.Value;
}
}
}
//create a new workbook
Workbook workbook = new Workbook();
try
{
Workbook.AddCustomFunction(new BigIntegerPowFunction());
}
catch (RuntimeException ex)
{
// Function was added
} // End Try
try
{
Workbook.AddCustomFunction(new BigIntegerMultiplyFunction());
}
catch (RuntimeException ex)
{
// Function was added
} // End Try
try
{
Workbook.AddCustomFunction(new GreatestCommonDivisionFunction());
}
catch (RuntimeException ex)
{
// Function was added
} // End Try
// Use BigInteger to calculate results
IWorksheet worksheet = workbook.getActiveSheet();
worksheet.getRange("A1").setValue("154382190 ^ 3 = ");
worksheet.getRange("A2").setValue("1643590 * 166935 = ");
worksheet.getRange("A3").setValue("Greatest common division = ");
worksheet.getRange("B1").setFormula("=BIG.INTEGER.POW(\"154382190\", 3)");
worksheet.getRange("B2").setFormula("=BIG.INTEGER.MULT(\"1643590\", \"166935\")");
worksheet.getRange("B3").setFormula("=BIG.INTEGER.GCD(B1,B2)");
// Arrange
worksheet.getColumns().get(0).autoFit();
worksheet.getColumns().get(1).setColumnWidth(worksheet.getRange("B1").getText().length() + 1);
//save to an pdf file
workbook.save("CustomObjectInCustomFunction.pdf");
下面的示例演示如何创建用于生成 GUID 的自定义函数。要每次生成唯一的 GUID,该自定义函数不应使用缓存。因此,示例代码将 setIsVolatile 方法设置为 true,以便在每次调用时生成新的GUID。
public class GeneralID extends CustomFunction {
public GeneralID() {
super("GeneralID", FunctionValueType.Object);
this.setIsVolatile(true);
}
@Override
public Object evaluate(Object[] objects, ICalcContext iCalcContext) {
return UUID.randomUUID().toString().replaceAll("-","");
}
}
// Create a new workbook
Workbook workbook = new Workbook();
Workbook.AddCustomFunction(new GeneralID());
IWorksheet worksheet = workbook.getActiveSheet();
worksheet.getRange("A1").setFormula("=GeneralID()");
Object valueA1Before = worksheet.getRange("A1").getValue();
worksheet.getRange("A2").setFormula("=GeneralID()");
// A1's value has changed.
Object valueA1After = worksheet.getRange("A1").getValue();
System.out.println(valueA1After);
异步函数是以异步或并发方式提供其结果的任何函数。异步函数具有非阻塞体系结构,因此一个任务的执行不依赖于另一个任务。任务可以同时运行。运行异步函数可以通过允许同时运行多个计算来提高性能。GcExcel 通过从 AsyncCustomFunction 类派生函数,使函数能够执行异步计算。 evaluateAsync 方法异步计算函数。GcExcel 还在 CalcError 枚举中提供了一个枚举值“Busy”,该值指示单元格正在计算异步公式。
参考以下示例代码添加和使用自定义异步函数:
public class AsyncFunction {
public static void main(String[] args) {
// Register Async custom function.
Workbook.AddCustomFunction(new MyAddFunction());
// Implement the Async custom Function.
Workbook workbook = new Workbook();
IWorksheet worksheet = workbook.getWorksheets().get(0);
worksheet.getRange("A1").setValue(1);
worksheet.getRange("B1").setValue(2);
// Add the cell values.
worksheet.getRange("C1").setFormula("=MyAdd(A1,B1)");
Object value1 = worksheet.getRange("C1").getValue();
// Display result. The result will be "Busy".
System.out.println(value1);
Thread.sleep(2000);
Object value2 = worksheet.getRange("C1").getValue();
// Display result. The result will be "3".
System.out.println(value2);
}
}
// Define Async custom function: MyAddFunction.
class MyAddFunction extends AsyncCustomFunction {
public MyAddFunction() {
super("MyAdd", FunctionValueType.Number, new Parameter[] { new Parameter(FunctionValueType.Number), new Parameter(FunctionValueType.Number) });
}
@Override
public CompletableFuture<Object> evaluateAsync(Object[] arguments, ICalcContext context) {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
return (double)arguments[0] + (double)arguments[1];
});
}
}
限制
AsyncCustomFunction 的参数不接受任何单元格引用,因为异步函数可能在另一个线程中运行,如果使用单元格引用,将导致多线程冲突。同样,不允许在异步函数中使用 IWorksheet 和 IWorkbook 等对象。