Last active
May 23, 2017 04:45
-
-
Save VincentJian/345faa6a599ea46460a567a888cd33a8 to your computer and use it in GitHub Desktop.
Enhanced TextView for Android
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?xml version="1.0" encoding="utf-8"?> | |
| <resources> | |
| <declare-styleable name="DrawableTextView"> | |
| <attr name="drawableLeftCompat" format="reference" /> | |
| <attr name="drawableTopCompat" format="reference" /> | |
| <attr name="drawableRightCompat" format="reference" /> | |
| <attr name="drawableBottomCompat" format="reference" /> | |
| <attr name="drawableWidth" format="dimension" /> | |
| <attr name="drawableHeight" format="dimension" /> | |
| <attr name="drawableTint" format="color" /> | |
| <attr name="drawableTintMode" format="enum"> | |
| <!-- The tint is drawn on top of the drawable. | |
| [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] --> | |
| <enum name="src_over" value="3" /> | |
| <!-- The tint is masked by the alpha channel of the drawable. The drawable’s | |
| color channels are thrown out. [Sa * Da, Sc * Da] --> | |
| <enum name="src_in" value="5" /> | |
| <!-- The tint is drawn above the drawable, but with the drawable’s alpha | |
| channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] --> | |
| <enum name="src_atop" value="9" /> | |
| <!-- Multiplies the color and alpha channels of the drawable with those of | |
| the tint. [Sa * Da, Sc * Dc] --> | |
| <enum name="multiply" value="14" /> | |
| <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] --> | |
| <enum name="screen" value="15" /> | |
| <!-- Combines the tint and drawable color and alpha channels, clamping the | |
| result to valid color values. Saturate(S + D) --> | |
| <enum name="add" value="16" /> | |
| </attr> | |
| </declare-styleable> | |
| </resources> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import android.content.Context; | |
| import android.content.res.ColorStateList; | |
| import android.content.res.TypedArray; | |
| import android.graphics.PorterDuff; | |
| import android.graphics.Rect; | |
| import android.graphics.drawable.Drawable; | |
| import android.os.Build; | |
| import android.support.annotation.ColorInt; | |
| import android.support.annotation.DrawableRes; | |
| import android.support.v4.content.ContextCompat; | |
| import android.support.v4.graphics.drawable.DrawableCompat; | |
| import android.support.v7.widget.AppCompatTextView; | |
| import android.util.AttributeSet; | |
| /** | |
| * Enhanced TextView Widget that can set drawable size, tint and using vector drawable | |
| * | |
| * @author VincentJian | |
| * @version 1.0 | |
| * @see <a href="http://stackoverflow.com/a/31916731">http://stackoverflow.com/a/31916731</a> | |
| * @see <a href="http://stackoverflow.com/a/40250753">http://stackoverflow.com/a/40250753</a> | |
| * @since 2017/5/16 | |
| */ | |
| public class DrawableTextView extends AppCompatTextView { | |
| private static final int DEFAULT_DRAWABLE_SIZE = -1; | |
| private static final int DEFAULT_DRAWABLE_RESOURCE = -1; | |
| private static final int DEFAULT_DRAWABLE_TINT_MODE = -1; | |
| private int mDrawableWidth; | |
| private int mDrawableHeight; | |
| private ColorStateList mDrawableTint; | |
| private PorterDuff.Mode mDrawableTintMode; | |
| public DrawableTextView(Context context) { | |
| this(context, null); | |
| } | |
| public DrawableTextView(Context context, AttributeSet attrs) { | |
| this(context, attrs, 0); | |
| } | |
| public DrawableTextView(Context context, AttributeSet attrs, int defStyleAttr) { | |
| super(context, attrs, defStyleAttr); | |
| init(context, attrs); | |
| } | |
| private void init(Context context, AttributeSet attrs) { | |
| if (attrs == null) { | |
| return; | |
| } | |
| final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DrawableTextView); | |
| mDrawableWidth = a.getDimensionPixelSize(R.styleable.DrawableTextView_drawableWidth, DEFAULT_DRAWABLE_SIZE); | |
| mDrawableHeight = a.getDimensionPixelSize(R.styleable.DrawableTextView_drawableHeight, DEFAULT_DRAWABLE_SIZE); | |
| mDrawableTint = a.getColorStateList(R.styleable.DrawableTextView_drawableTint); | |
| mDrawableTintMode = parseTintMode(a.getInt(R.styleable.DrawableTextView_drawableTintMode, DEFAULT_DRAWABLE_TINT_MODE)); | |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | |
| setCompoundDrawables(a.getDrawable(R.styleable.DrawableTextView_drawableLeftCompat), | |
| a.getDrawable(R.styleable.DrawableTextView_drawableTopCompat), | |
| a.getDrawable(R.styleable.DrawableTextView_drawableRightCompat), | |
| a.getDrawable(R.styleable.DrawableTextView_drawableBottomCompat)); | |
| } else { | |
| setCompoundDrawables(a.getResourceId(R.styleable.DrawableTextView_drawableLeftCompat, DEFAULT_DRAWABLE_RESOURCE), | |
| a.getResourceId(R.styleable.DrawableTextView_drawableTopCompat, DEFAULT_DRAWABLE_RESOURCE), | |
| a.getResourceId(R.styleable.DrawableTextView_drawableRightCompat, DEFAULT_DRAWABLE_RESOURCE), | |
| a.getResourceId(R.styleable.DrawableTextView_drawableBottomCompat, DEFAULT_DRAWABLE_RESOURCE)); | |
| } | |
| a.recycle(); | |
| } | |
| /** | |
| * Sets the Drawables (if any) to appear to the left of, above, to the right of, and below the | |
| * text. | |
| * | |
| * @param left left drawable resource, -1 means no left drawable | |
| * @param top top drawable resource, -1 means no top drawable | |
| * @param right right drawable resource, -1 means no right drawable | |
| * @param bottom bottom drawable resource, -1 means no bottom drawable | |
| */ | |
| public void setCompoundDrawables(@DrawableRes int left, @DrawableRes int top, | |
| @DrawableRes int right, @DrawableRes int bottom) { | |
| setCompoundDrawables(left == -1 ? null : ContextCompat.getDrawable(getContext(), left), | |
| top == -1 ? null : ContextCompat.getDrawable(getContext(), top), | |
| right == -1 ? null : ContextCompat.getDrawable(getContext(), right), | |
| bottom == -1 ? null : ContextCompat.getDrawable(getContext(), bottom)); | |
| } | |
| @Override | |
| public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) { | |
| if (left != null) { | |
| left = DrawableCompat.wrap(left); | |
| } | |
| if (top != null) { | |
| top = DrawableCompat.wrap(top); | |
| } | |
| if (right != null) { | |
| right = DrawableCompat.wrap(right); | |
| } | |
| if (bottom != null) { | |
| bottom = DrawableCompat.wrap(bottom); | |
| } | |
| tintDrawable(left); | |
| tintDrawable(top); | |
| tintDrawable(right); | |
| tintDrawable(bottom); | |
| scaleDrawable(left); | |
| scaleDrawable(top); | |
| scaleDrawable(right); | |
| scaleDrawable(bottom); | |
| super.setCompoundDrawables(left, top, right, bottom); | |
| } | |
| public int getDrawableWidth() { | |
| return mDrawableWidth; | |
| } | |
| public void setDrawableWidth(int drawableWidth) { | |
| mDrawableWidth = drawableWidth; | |
| } | |
| public int getDrawableHeight() { | |
| return mDrawableHeight; | |
| } | |
| public void setDrawableHeight(int drawableHeight) { | |
| mDrawableHeight = drawableHeight; | |
| } | |
| public ColorStateList getDrawableTint() { | |
| return mDrawableTint; | |
| } | |
| public void setDrawableTint(@ColorInt int drawableTint) { | |
| if (drawableTint == -1) { | |
| mDrawableTint = null; | |
| } else { | |
| mDrawableTint = ColorStateList.valueOf(drawableTint); | |
| } | |
| } | |
| public void setDrawableTint(ColorStateList drawableTint) { | |
| mDrawableTint = drawableTint; | |
| } | |
| public PorterDuff.Mode getDrawableTintMode() { | |
| return mDrawableTintMode; | |
| } | |
| public void setDrawableTintMode(PorterDuff.Mode drawableTintMode) { | |
| mDrawableTintMode = drawableTintMode; | |
| } | |
| private void tintDrawable(Drawable drawable) { | |
| if (drawable == null) { | |
| return; | |
| } | |
| if (mDrawableTint != null) { | |
| DrawableCompat.setTint(drawable, mDrawableTint.getDefaultColor()); | |
| } | |
| if (mDrawableTintMode != null) { | |
| DrawableCompat.setTintMode(drawable, mDrawableTintMode); | |
| } | |
| } | |
| private void scaleDrawable(Drawable drawable) { | |
| if (drawable == null) { | |
| return; | |
| } | |
| Rect bounds = new Rect(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); | |
| if (mDrawableWidth == DEFAULT_DRAWABLE_SIZE && mDrawableHeight == DEFAULT_DRAWABLE_SIZE) { | |
| drawable.setBounds(bounds); | |
| return; | |
| } | |
| float drawableWidth = bounds.width(); | |
| float drawableHeight = bounds.height(); | |
| float scaleFactor = drawableHeight / drawableWidth; | |
| if (mDrawableWidth > 0 && drawableWidth > mDrawableWidth) { | |
| drawableWidth = mDrawableWidth; | |
| drawableHeight = drawableWidth * scaleFactor; | |
| } | |
| if (mDrawableHeight > 0 && drawableHeight > mDrawableHeight) { | |
| drawableHeight = mDrawableHeight; | |
| drawableWidth = drawableHeight / scaleFactor; | |
| } | |
| bounds.right = bounds.left + Math.round(drawableWidth); | |
| bounds.bottom = bounds.top + Math.round(drawableHeight); | |
| drawable.setBounds(bounds); | |
| } | |
| private static PorterDuff.Mode parseTintMode(int mode) { | |
| switch (mode) { | |
| case 3: | |
| return PorterDuff.Mode.SRC_OVER; | |
| case 5: | |
| return PorterDuff.Mode.SRC_IN; | |
| case 9: | |
| return PorterDuff.Mode.SRC_ATOP; | |
| case 14: | |
| return PorterDuff.Mode.MULTIPLY; | |
| case 15: | |
| return PorterDuff.Mode.SCREEN; | |
| case 16: | |
| return PorterDuff.Mode.ADD; | |
| default: | |
| return null; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment