aboutsummaryrefslogtreecommitdiff
path: root/WordPress/src/main/java/org/wordpress/android/widgets/TextDrawable.java
diff options
context:
space:
mode:
Diffstat (limited to 'WordPress/src/main/java/org/wordpress/android/widgets/TextDrawable.java')
-rw-r--r--WordPress/src/main/java/org/wordpress/android/widgets/TextDrawable.java442
1 files changed, 442 insertions, 0 deletions
diff --git a/WordPress/src/main/java/org/wordpress/android/widgets/TextDrawable.java b/WordPress/src/main/java/org/wordpress/android/widgets/TextDrawable.java
new file mode 100644
index 000000000..b4aa27643
--- /dev/null
+++ b/WordPress/src/main/java/org/wordpress/android/widgets/TextDrawable.java
@@ -0,0 +1,442 @@
+package org.wordpress.android.widgets;
+
+/**
+ * A Drawable object used to display text content.
+ *
+ * Based on https://github.com/devunwired/textdrawable
+ */
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.TypedValue;
+
+/**
+ * A Drawable object that draws text.
+ * A TextDrawable accepts most of the same parameters that can be applied to
+ * {@link android.widget.TextView} for displaying and formatting text.
+ *
+ * Optionally, a {@link Path} may be supplied on which to draw the text.
+ *
+ * A TextDrawable has an intrinsic size equal to that required to draw all
+ * the text it has been supplied, when possible. In cases where a {@link Path}
+ * has been supplied, the caller must explicitly call
+ * {@link #setBounds(android.graphics.Rect) setBounds()} to provide the Drawable
+ * size based on the Path constraints.
+ */
+public class TextDrawable extends Drawable {
+
+ /* Platform XML constants for typeface */
+ private static final int SANS = 1;
+ private static final int SERIF = 2;
+ private static final int MONOSPACE = 3;
+
+ /* Resources for scaling values to the given device */
+ private Resources mResources;
+ /* Paint to hold most drawing primitives for the text */
+ private TextPaint mTextPaint;
+ /* Layout is used to measure and draw the text */
+ private StaticLayout mTextLayout;
+ /* Alignment of the text inside its bounds */
+ private Layout.Alignment mTextAlignment = Layout.Alignment.ALIGN_NORMAL;
+ /* Optional path on which to draw the text */
+ private Path mTextPath;
+ /* Stateful text color list */
+ private ColorStateList mTextColors;
+ /* Container for the bounds to be reported to widgets */
+ private Rect mTextBounds;
+ /* Text string to draw */
+ private CharSequence mText = "";
+
+ /* Attribute lists to pull default values from the current theme */
+ private static final int[] themeAttributes = {
+ android.R.attr.textAppearance
+ };
+ private static final int[] appearanceAttributes = {
+ android.R.attr.textSize,
+ android.R.attr.typeface,
+ android.R.attr.textStyle,
+ android.R.attr.textColor
+ };
+
+
+ public TextDrawable(Context context) {
+ super();
+ //Used to load and scale resource items
+ mResources = context.getResources();
+ //Definition of this drawables size
+ mTextBounds = new Rect();
+ //Paint to use for the text
+ mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
+ mTextPaint.density = mResources.getDisplayMetrics().density;
+ mTextPaint.setDither(true);
+
+ int textSize = 15;
+ ColorStateList textColor = null;
+ int styleIndex = -1;
+ int typefaceIndex = -1;
+
+ //Set default parameters from the current theme
+ TypedArray a = context.getTheme().obtainStyledAttributes(themeAttributes);
+ int appearanceId = a.getResourceId(0, -1);
+ a.recycle();
+
+ TypedArray ap = null;
+ if (appearanceId != -1) {
+ ap = context.obtainStyledAttributes(appearanceId, appearanceAttributes);
+ }
+ if (ap != null) {
+ for (int i=0; i < ap.getIndexCount(); i++) {
+ int attr = ap.getIndex(i);
+ switch (attr) {
+ case 0: //Text Size
+ textSize = a.getDimensionPixelSize(attr, textSize);
+ break;
+ case 1: //Typeface
+ typefaceIndex = a.getInt(attr, typefaceIndex);
+ break;
+ case 2: //Text Style
+ styleIndex = a.getInt(attr, styleIndex);
+ break;
+ case 3: //Text Color
+ textColor = a.getColorStateList(attr);
+ break;
+ default:
+ break;
+ }
+ }
+
+ ap.recycle();
+ }
+
+ setTextColor(textColor != null ? textColor : ColorStateList.valueOf(0xFF000000));
+ setRawTextSize(textSize);
+
+ Typeface tf = null;
+ switch (typefaceIndex) {
+ case SANS:
+ tf = Typeface.SANS_SERIF;
+ break;
+
+ case SERIF:
+ tf = Typeface.SERIF;
+ break;
+
+ case MONOSPACE:
+ tf = Typeface.MONOSPACE;
+ break;
+ }
+
+ setTypeface(tf, styleIndex);
+ }
+
+
+ public void setText(int text) {
+ this.setText(String.valueOf(text));
+ }
+
+ /**
+ * Set the text that will be displayed
+ * @param text Text to display
+ */
+ public void setText(CharSequence text) {
+ if (text == null) text = "";
+
+ mText = text;
+
+ measureContent();
+ }
+
+ /**
+ * Return the text currently being displayed
+ */
+ public CharSequence getText() {
+ return mText;
+ }
+
+ /**
+ * Return the current text size, in pixels
+ */
+ public float getTextSize() {
+ return mTextPaint.getTextSize();
+ }
+
+ /**
+ * Set the text size. The value will be interpreted in "sp" units
+ * @param size Text size value, in sp
+ */
+ public void setTextSize(float size) {
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
+ }
+
+ /**
+ * Set the text size, using the supplied complex units
+ * @param unit Units for the text size, such as dp or sp
+ * @param size Text size value
+ */
+ public void setTextSize(int unit, float size) {
+ float dimension = TypedValue.applyDimension(unit, size,
+ mResources.getDisplayMetrics());
+ setRawTextSize(dimension);
+ }
+
+ /*
+ * Set the text size, in raw pixels
+ */
+ private void setRawTextSize(float size) {
+ if (size != mTextPaint.getTextSize()) {
+ mTextPaint.setTextSize(size);
+
+ measureContent();
+ }
+ }
+
+ /**
+ * Return the horizontal stretch factor of the text
+ */
+ public float getTextScaleX() {
+ return mTextPaint.getTextScaleX();
+ }
+
+ /**
+ * Set the horizontal stretch factor of the text
+ * @param size Text scale factor
+ */
+ public void setTextScaleX(float size) {
+ if (size != mTextPaint.getTextScaleX()) {
+ mTextPaint.setTextScaleX(size);
+ measureContent();
+ }
+ }
+
+ /**
+ * Return the current text alignment setting
+ */
+ public Layout.Alignment getTextAlign() {
+ return mTextAlignment;
+ }
+
+ /**
+ * Set the text alignment. The alignment itself is based on the text layout direction.
+ * For LTR text NORMAL is left aligned and OPPOSITE is right aligned.
+ * For RTL text, those alignments are reversed.
+ * @param align Text alignment value. Should be set to one of:
+ *
+ * {@link Layout.Alignment#ALIGN_NORMAL},
+ * {@link Layout.Alignment#ALIGN_NORMAL},
+ * {@link Layout.Alignment#ALIGN_OPPOSITE}.
+ */
+ public void setTextAlign(Layout.Alignment align) {
+ if (mTextAlignment != align) {
+ mTextAlignment = align;
+ measureContent();
+ }
+ }
+
+ /**
+ * Sets the typeface and style in which the text should be displayed.
+ * Note that not all Typeface families actually have bold and italic
+ * variants, so you may need to use
+ * {@link #setTypeface(Typeface, int)} to get the appearance
+ * that you actually want.
+ */
+ public void setTypeface(Typeface tf) {
+ if (mTextPaint.getTypeface() != tf) {
+ mTextPaint.setTypeface(tf);
+
+ measureContent();
+ }
+ }
+
+ /**
+ * Sets the typeface and style in which the text should be displayed,
+ * and turns on the fake bold and italic bits in the Paint if the
+ * Typeface that you provided does not have all the bits in the
+ * style that you specified.
+ *
+ */
+ public void setTypeface(Typeface tf, int style) {
+ if (style > 0) {
+ if (tf == null) {
+ tf = Typeface.defaultFromStyle(style);
+ } else {
+ tf = Typeface.create(tf, style);
+ }
+
+ setTypeface(tf);
+ // now compute what (if any) algorithmic styling is needed
+ int typefaceStyle = tf != null ? tf.getStyle() : 0;
+ int need = style & ~typefaceStyle;
+ mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
+ mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
+ } else {
+ mTextPaint.setFakeBoldText(false);
+ mTextPaint.setTextSkewX(0);
+ setTypeface(tf);
+ }
+ }
+
+ /**
+ * Return the current typeface and style that the Paint
+ * using for display.
+ */
+ public Typeface getTypeface() {
+ return mTextPaint.getTypeface();
+ }
+
+ /**
+ * Set a single text color for all states
+ * @param color Color value such as {@link Color#WHITE} or {@link Color#argb(int, int, int, int)}
+ */
+ public void setTextColor(int color) {
+ setTextColor(ColorStateList.valueOf(color));
+ }
+
+ /**
+ * Set the text color as a state list
+ * @param colorStateList ColorStateList of text colors, such as inflated from an R.color resource
+ */
+ public void setTextColor(ColorStateList colorStateList) {
+ mTextColors = colorStateList;
+ updateTextColors(getState());
+ }
+
+ /**
+ * Optional Path object on which to draw the text. If this is set,
+ * TextDrawable cannot properly measure the bounds this drawable will need.
+ * You must call {@link #setBounds(int, int, int, int) setBounds()} before
+ * applying this TextDrawable to any View.
+ *
+ * Calling this method with <code>null</code> will remove any Path currently attached.
+ */
+ public void setTextPath(Path path) {
+ if (mTextPath != path) {
+ mTextPath = path;
+ measureContent();
+ }
+ }
+
+ /**
+ * Internal method to take measurements of the current contents and apply
+ * the correct bounds when possible.
+ */
+ private void measureContent() {
+ //If drawing to a path, we cannot measure intrinsic bounds
+ //We must resly on setBounds being called externally
+ if (mTextPath != null) {
+ //Clear any previous measurement
+ mTextLayout = null;
+ mTextBounds.setEmpty();
+ } else {
+ //Measure text bounds
+ double desired = Math.ceil( Layout.getDesiredWidth(mText, mTextPaint) );
+ mTextLayout = new StaticLayout(mText, mTextPaint, (int)desired,
+ mTextAlignment, 1.0f, 0.0f, false);
+ mTextBounds.set(0, 0, mTextLayout.getWidth(), mTextLayout.getHeight());
+ }
+
+ //We may need to be redrawn
+ invalidateSelf();
+ }
+
+ /**
+ * Internal method to apply the correct text color based on the drawable's state
+ */
+ private boolean updateTextColors(int[] stateSet) {
+ int newColor = mTextColors.getColorForState(stateSet, Color.WHITE);
+ if (mTextPaint.getColor() != newColor) {
+ mTextPaint.setColor(newColor);
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ //Update the internal bounds in response to any external requests
+ mTextBounds.set(bounds);
+ }
+
+ @Override
+ public boolean isStateful() {
+ /*
+ * The drawable's ability to represent state is based on
+ * the text color list set
+ */
+ return mTextColors.isStateful();
+ }
+
+ @Override
+ protected boolean onStateChange(int[] state) {
+ //Upon state changes, grab the correct text color
+ return updateTextColors(state);
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ //Return the vertical bounds measured, or -1 if none
+ if (mTextBounds.isEmpty()) {
+ return -1;
+ } else {
+ return (mTextBounds.bottom - mTextBounds.top);
+ }
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ //Return the horizontal bounds measured, or -1 if none
+ if (mTextBounds.isEmpty()) {
+ return -1;
+ } else {
+ return (mTextBounds.right - mTextBounds.left);
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ final Rect bounds = getBounds();
+ final int count = canvas.save();
+ canvas.translate(bounds.left, bounds.top);
+ if (mTextPath == null) {
+ //Allow the layout to draw the text
+ mTextLayout.draw(canvas);
+ } else {
+ //Draw directly on the canvas using the supplied path
+ canvas.drawTextOnPath(mText.toString(), mTextPath, 0, 0, mTextPaint);
+ }
+ canvas.restoreToCount(count);
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ if (mTextPaint.getAlpha() != alpha) {
+ mTextPaint.setAlpha(alpha);
+ }
+ }
+
+ @Override
+ public int getOpacity() {
+ return mTextPaint.getAlpha();
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ if (mTextPaint.getColorFilter() != cf) {
+ mTextPaint.setColorFilter(cf);
+ }
+ }
+
+} \ No newline at end of file