StaticLayout_Delegate.java revision f3284cbd38989279a116e0082b7ab5b987c13388
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.graphics.BidiRenderer; 8import android.graphics.Paint; 9import android.graphics.Paint_Delegate; 10import android.graphics.RectF; 11import android.text.StaticLayout.LineBreaks; 12import android.text.Primitive.PrimitiveType; 13 14import java.util.ArrayList; 15import java.util.Arrays; 16import java.util.List; 17 18import com.ibm.icu.text.BreakIterator; 19import com.ibm.icu.util.ULocale; 20import javax.swing.text.Segment; 21 22/** 23 * Delegate that provides implementation for native methods in {@link android.text.StaticLayout} 24 * <p/> 25 * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced 26 * by calls to methods of the same name in this delegate class. 27 * 28 */ 29public class StaticLayout_Delegate { 30 31 private static final char CHAR_SPACE = 0x20; 32 private static final char CHAR_TAB = 0x09; 33 private static final char CHAR_NEWLINE = 0x0A; 34 private static final char CHAR_ZWSP = 0x200B; // Zero width space. 35 36 // ---- Builder delegate manager ---- 37 private static final DelegateManager<Builder> sBuilderManager = 38 new DelegateManager<Builder>(Builder.class); 39 40 @LayoutlibDelegate 41 /*package*/ static int nComputeLineBreaks(long nativeBuilder, 42 int length, float firstWidth, int firstWidthLineCount, float restWidth, 43 int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle, 44 int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength) { 45 46 Builder builder = sBuilderManager.getDelegate(nativeBuilder); 47 // compute all possible breakpoints. 48 BreakIterator it = BreakIterator.getLineInstance(new ULocale(builder.mLocale)); 49 it.setText(new Segment(builder.mText, 0, length)); 50 // average word length in english is 5. So, initialize the possible breaks with a guess. 51 List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d)); 52 int loc; 53 it.first(); 54 while ((loc = it.next()) != BreakIterator.DONE) { 55 breaks.add(loc); 56 } 57 58 LineWidth lineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth); 59 TabStops tabStopCalculator = new TabStops(variableTabStops, defaultTabStop); 60 List<Primitive> primitives = computePrimitives(builder.mText, builder.mWidths, length, breaks); 61 LineBreaker lineBreaker; 62 if (optimize) { 63 lineBreaker = new OptimizingLineBreaker(primitives, lineWidth, tabStopCalculator); 64 } else { 65 lineBreaker = new GreedyLineBreaker(primitives, lineWidth, tabStopCalculator); 66 } 67 lineBreaker.computeBreaks(recycle); 68 return recycle.breaks.length; 69 } 70 71 /** 72 * Compute metadata each character - things which help in deciding if it's possible to break 73 * at a point or not. 74 */ 75 @NonNull 76 private static List<Primitive> computePrimitives(@NonNull char[] text, @NonNull float[] widths, 77 int length, @NonNull List<Integer> breaks) { 78 // Initialize the list with a guess of the number of primitives: 79 // 2 Primitives per non-whitespace char and approx 5 chars per word (i.e. 83% chars) 80 List<Primitive> primitives = new ArrayList<Primitive>(((int) Math.ceil(length * 1.833))); 81 int breaksSize = breaks.size(); 82 int breakIndex = 0; 83 for (int i = 0; i < length; i++) { 84 char c = text[i]; 85 if (c == CHAR_SPACE || c == CHAR_ZWSP) { 86 primitives.add(PrimitiveType.GLUE.getNewPrimitive(i, widths[i])); 87 } else if (c == CHAR_TAB) { 88 primitives.add(PrimitiveType.VARIABLE.getNewPrimitive(i)); 89 } else if (c != CHAR_NEWLINE) { 90 while (breakIndex < breaksSize && breaks.get(breakIndex) < i) { 91 breakIndex++; 92 } 93 Primitive p; 94 if (widths[i] != 0) { 95 if (breakIndex < breaksSize && breaks.get(breakIndex) == i) { 96 p = PrimitiveType.PENALTY.getNewPrimitive(i, 0, 0); 97 } else { 98 p = PrimitiveType.WORD_BREAK.getNewPrimitive(i, 0); 99 } 100 primitives.add(p); 101 } 102 103 primitives.add(PrimitiveType.BOX.getNewPrimitive(i, widths[i])); 104 } 105 } 106 // final break at end of everything 107 primitives.add( 108 PrimitiveType.PENALTY.getNewPrimitive(length, 0, -PrimitiveType.PENALTY_INFINITY)); 109 return primitives; 110 } 111 112 @LayoutlibDelegate 113 /*package*/ static long nNewBuilder() { 114 return sBuilderManager.addNewDelegate(new Builder()); 115 } 116 117 @LayoutlibDelegate 118 /*package*/ static void nFinishBuilder(long nativeBuilder) { 119 } 120 121 @LayoutlibDelegate 122 /*package*/ static void nFreeBuilder(long nativeBuilder) { 123 sBuilderManager.removeJavaReferenceFor(nativeBuilder); 124 } 125 126 @LayoutlibDelegate 127 /*package*/ static void nSetLocale(long nativeBuilder, String locale) { 128 Builder builder = sBuilderManager.getDelegate(nativeBuilder); 129 builder.mLocale = locale; 130 } 131 132 @LayoutlibDelegate 133 /*package*/ static void nSetText(long nativeBuilder, char[] text, int length) { 134 Builder builder = sBuilderManager.getDelegate(nativeBuilder); 135 builder.mText = text; 136 builder.mWidths = new float[length]; 137 } 138 139 140 @LayoutlibDelegate 141 /*package*/ static float nAddStyleRun(long nativeBuilder, long nativePaint, long nativeTypeface, 142 int start, int end, boolean isRtl) { 143 Builder builder = sBuilderManager.getDelegate(nativeBuilder); 144 145 int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR; 146 return measureText(nativePaint, builder.mText, start, end - start, builder.mWidths, bidiFlags); 147 } 148 149 150 @LayoutlibDelegate 151 /*package*/ static void nAddMeasuredRun(long nativeBuilder, int start, int end, float[] widths) { 152 Builder builder = sBuilderManager.getDelegate(nativeBuilder); 153 System.arraycopy(widths, start, builder.mWidths, start, end - start); 154 } 155 156 @LayoutlibDelegate 157 /*package*/ static void nAddReplacementRun(long nativeBuilder, int start, int end, float width) { 158 Builder builder = sBuilderManager.getDelegate(nativeBuilder); 159 builder.mWidths[start] = width; 160 Arrays.fill(builder.mWidths, start + 1, end, 0.0f); 161 } 162 163 @LayoutlibDelegate 164 /*package*/ static void nGetWidths(long nativeBuilder, float[] floatsArray) { 165 Builder builder = sBuilderManager.getDelegate(nativeBuilder); 166 System.arraycopy(builder.mWidths, 0, floatsArray, 0, builder.mWidths.length); 167 } 168 169 private static float measureText(long nativePaint, char []text, int index, int count, 170 float[] widths, int bidiFlags) { 171 Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint); 172 RectF bounds = new BidiRenderer(null, paint, text) 173 .renderText(index, index + count, bidiFlags, widths, 0, false); 174 return bounds.right - bounds.left; 175 } 176 177 /** 178 * Java representation of the native Builder class. 179 */ 180 static class Builder { 181 String mLocale; 182 char[] mText; 183 float[] mWidths; 184 } 185} 186