/** * Computes the maximum font size which makes the TextView's (possibly multi-line) text fit in the * bounding box defined by the viewWidth and viewHeight parameters, always within the range * ({@link #MIN_TEXT_SIZE}, {@link #MAX_TEXT_SIZE}) but taking into account any predetermined font * size constraints that come from the font size coordinator (if present). * * @param viewWidth the width of the View into which the text will be drawn (including padding) * @param viewHeight the height of the View into which the text will be drawn (including padding) * @param force whether or not to force a recalculation of font size (this could otherwise be * avoided */ private void fitText(int viewWidth, int viewHeight, boolean force) { // Avoid unnecessary (re)fitting: 0 or negative view dimensions if (viewWidth <= 0 || viewHeight <= 0) return; // Avoid unnecessary (re)fitting: text is empty if (text == null || text.isEmpty()) return; // Compute the target text width/height as the provided container width minus relevant padding int targetWidth = viewWidth - this.getPaddingLeft() - this.getPaddingRight(); int targetHeight = viewHeight - this.getPaddingBottom() - this.getPaddingTop(); // Avoid unnecessary (re)fitting: no forcing and target dimensions are the same as last time if (!force && targetWidth == lastTargetWidth && targetHeight == lastTargetHeight) return; // At this point we know a new size computation will take place... // Log.d("TFV", "fitText: IS REFITTING, text: " + this.getText() + ", hash: " + hashCode() + " // w=" + viewWidth + ", h=" + viewHeight + ", forced: " + force); // Initialise lo & hi bounds: float lo = MIN_TEXT_SIZE; float hi = (coordinator == null) ? MAX_TEXT_SIZE : coordinator.getCoordinatedTextSize(); // See if we can use the "hi" instead of iterating (if "hi" fits then iterating will only get us // back to a value marginally smaller than "hi"): float textSize = hi; // Only iterate if "hi" was too big: if (!textFits(textSize, targetWidth, targetHeight)) { // Fitting loop (binary search): while ((hi - lo) > THRESHOLD) { textSize = (hi + lo) / 2; // See if our text fits if (textFits(textSize, targetWidth, targetHeight)) lo = textSize; // was too small, so increase lo else hi = textSize; // was too big, so reduce hi } // We set lo as the new textSize, so that we undershoot rather than overshoot: textSize = lo; } // Log.d("TFV", "final textSize for slot " + coordinatorSlot + ": " + textSize); // Remember target dimensions: lastTargetWidth = targetWidth; lastTargetHeight = targetHeight; // Set new textSize: this.setTextSizePx( textSize, coordinator == null); // if there is a coordinate the new size is not applied immediately but at a // later time determined by the coordinator // Inform coordinator if there is one: if (coordinator != null) coordinator.updated(this); }
/** * @param context * @param coordinator * @param coordinatorSlot */ public TextFitView(Context context, TextSizeCoordinator coordinator, int coordinatorSlot) { super(context); this.coordinator = coordinator; // Set or claim coordinator slot: this.coordinatorSlot = coordinator != null ? (coordinatorSlot >= 0 ? coordinatorSlot : coordinator.claimSlot(this)) : UNASSIGNED_SLOT; // Initialise paint: paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); paint.setAntiAlias(true); // text looks pixellated otherwise paint.setColor(DEFAULT_TEXT_COLOR); paint.setTextSize(textSizePx); }