StaticLayout_Delegate.java revision fe12aa77743c4a4a98409418a6cbb0be11b8fee5
1package android.text; 2 3import com.android.annotations.NonNull; 4import com.android.layoutlib.bridge.impl.DelegateManager; 5import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 6 7import android.text.StaticLayout.LineBreaks; 8import android.text.Primitive.PrimitiveType; 9 10import java.util.ArrayList; 11import java.util.List; 12 13import com.ibm.icu.text.BreakIterator; 14import com.ibm.icu.util.ULocale; 15import javax.swing.text.Segment; 16 17/** 18 * Delegate that provides implementation for native methods in {@link android.text.StaticLayout} 19 * <p/> 20 * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced 21 * by calls to methods of the same name in this delegate class. 22 * 23 */ 24public class StaticLayout_Delegate { 25 26 private static final char CHAR_SPACE = 0x20; 27 private static final char CHAR_TAB = 0x09; 28 private static final char CHAR_NEWLINE = 0x0A; 29 private static final char CHAR_ZWSP = 0x200B; // Zero width space. 30 31 // ---- Builder delegate manager ---- 32 private static final DelegateManager<Builder> sBuilderManager = 33 new DelegateManager<Builder>(Builder.class); 34 35 @LayoutlibDelegate 36 /*package*/ static int nComputeLineBreaks(long nativeBuilder, char[] inputText, float[] widths, 37 int length, float firstWidth, int firstWidthLineCount, float restWidth, 38 int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle, 39 int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength) { 40 41 Builder builder = sBuilderManager.getDelegate(nativeBuilder); 42 // compute all possible breakpoints. 43 BreakIterator it = BreakIterator.getLineInstance(new ULocale(builder.mLocale)); 44 it.setText(new Segment(inputText, 0, length)); 45 // average word length in english is 5. So, initialize the possible breaks with a guess. 46 List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d)); 47 int loc; 48 it.first(); 49 while ((loc = it.next()) != BreakIterator.DONE) { 50 breaks.add(loc); 51 } 52 53 LineWidth lineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth); 54 TabStops tabStopCalculator = new TabStops(variableTabStops, defaultTabStop); 55 List<Primitive> primitives = computePrimitives(inputText, widths, length, breaks); 56 LineBreaker lineBreaker; 57 if (optimize) { 58 lineBreaker = new OptimizingLineBreaker(primitives, lineWidth, tabStopCalculator); 59 } else { 60 lineBreaker = new GreedyLineBreaker(primitives, lineWidth, tabStopCalculator); 61 } 62 lineBreaker.computeBreaks(recycle); 63 return recycle.breaks.length; 64 } 65 66 /** 67 * Compute metadata each character - things which help in deciding if it's possible to break 68 * at a point or not. 69 */ 70 @NonNull 71 private static List<Primitive> computePrimitives(@NonNull char[] text, @NonNull float[] widths, 72 int length, @NonNull List<Integer> breaks) { 73 // Initialize the list with a guess of the number of primitives: 74 // 2 Primitives per non-whitespace char and approx 5 chars per word (i.e. 83% chars) 75 List<Primitive> primitives = new ArrayList<Primitive>(((int) Math.ceil(length * 1.833))); 76 int breaksSize = breaks.size(); 77 int breakIndex = 0; 78 for (int i = 0; i < length; i++) { 79 char c = text[i]; 80 if (c == CHAR_SPACE || c == CHAR_ZWSP) { 81 primitives.add(PrimitiveType.GLUE.getNewPrimitive(i, widths[i])); 82 } else if (c == CHAR_TAB) { 83 primitives.add(PrimitiveType.VARIABLE.getNewPrimitive(i)); 84 } else if (c != CHAR_NEWLINE) { 85 while (breakIndex < breaksSize && breaks.get(breakIndex) < i) { 86 breakIndex++; 87 } 88 Primitive p; 89 if (widths[i] != 0) { 90 if (breakIndex < breaksSize && breaks.get(breakIndex) == i) { 91 p = PrimitiveType.PENALTY.getNewPrimitive(i, 0, 0); 92 } else { 93 p = PrimitiveType.WORD_BREAK.getNewPrimitive(i, 0); 94 } 95 primitives.add(p); 96 } 97 98 primitives.add(PrimitiveType.BOX.getNewPrimitive(i, widths[i])); 99 } 100 } 101 // final break at end of everything 102 primitives.add( 103 PrimitiveType.PENALTY.getNewPrimitive(length, 0, -PrimitiveType.PENALTY_INFINITY)); 104 return primitives; 105 } 106 107 @LayoutlibDelegate 108 /*package*/ static long nNewBuilder() { 109 return sBuilderManager.addNewDelegate(new Builder()); 110 } 111 112 @LayoutlibDelegate 113 /*package*/ static void nFinishBuilder(long nativeBuilder) { 114 } 115 116 @LayoutlibDelegate 117 /*package*/ static void nFreeBuilder(long nativeBuilder) { 118 sBuilderManager.removeJavaReferenceFor(nativeBuilder); 119 } 120 121 @LayoutlibDelegate 122 /*package*/ static void nBuilderSetLocale(long nativeBuilder, String locale) { 123 Builder builder = sBuilderManager.getDelegate(nativeBuilder); 124 builder.mLocale = locale; 125 } 126 127 /** 128 * Java representation of the native Builder class. It currently only stores the locale 129 * set by nBuilderSetLocale. 130 */ 131 static class Builder { 132 String mLocale; 133 } 134} 135