Android Drawable工具类

收录整理的一些Drawable用法,包含:
- Drawable 着色
- Drawable 圆角矩形
- Drawable 选择器


package com.skx.tomike.util;

import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.graphics.drawable.DrawableCompat;

/**
 * Drawable 工具类
 *
 * @author shiguotao
 */
public class SkxDrawableUtil {

    //----------------------------------------------- Drawable 着色 start -----------------------------------------------------------------

    /**
     * 对目标Drawable 进行着色
     *
     * @param drawable 目标Drawable
     * @param color    着色的颜色值
     * @return 着色处理后的Drawable
     */
    public static Drawable tintDrawable(@NonNull Drawable drawable, int color) {
        // 获取此drawable的共享状态实例
        Drawable wrappedDrawable = getCanTintDrawable(drawable);
        // 对 drawable 进行着色
        DrawableCompat.setTint(wrappedDrawable, color);
        return wrappedDrawable;
    }

    /**
     * 对目标Drawable 进行着色
     *
     * @param drawable 目标Drawable
     * @param colors   着色值
     * @return 着色处理后的Drawable
     */
    public static Drawable tintListDrawable(@NonNull Drawable drawable, ColorStateList colors) {
        Drawable wrappedDrawable = getCanTintDrawable(drawable);
        // 对 drawable 进行着色
        DrawableCompat.setTintList(wrappedDrawable, colors);
        return wrappedDrawable;
    }

    /**
     * 获取可以进行tint 的Drawable
     * <p>
     * 对原drawable进行重新实例化  newDrawable()
     * 包装  warp()
     * 可变操作 mutate()
     *
     * @param drawable 原始drawable
     * @return 可着色的drawable
     */
    @NonNull
    private static Drawable getCanTintDrawable(@NonNull Drawable drawable) {
        // 获取此drawable的共享状态实例
        Drawable.ConstantState state = drawable.getConstantState();
        // 对drawable 进行重新实例化、包装、可变操作
        return DrawableCompat.wrap(state == null ? drawable : state.newDrawable()).mutate();
    }

    //------------------------------------------------ Drawable 着色 end -----------------------------------------------------------------

    //----------------------------------------------------- Drawable 选择器 start-----------------------------------------------------------

    /**
     * 获得一个选择器Drawable.
     * Android 中 在xml中写的"selector"标签映射对象就是StateListDrawable 对象
     *
     * @param defaultDrawable 默认时显示的Drawable
     * @param pressedDrawable 按下时显示的Drawable
     * @return 选择器Drawable
     */
    public static StateListDrawable getSelectorDrawable(Drawable defaultDrawable, Drawable pressedDrawable) {
        if (defaultDrawable == null) return null;
        if (pressedDrawable == null) pressedDrawable = defaultDrawable;
        int[][] state = {{android.R.attr.state_enabled, -android.R.attr.state_pressed},
                {android.R.attr.state_enabled, android.R.attr.state_pressed}};
        StateListDrawable stateListDrawable = new StateListDrawable();
        stateListDrawable.addState(state[0], defaultDrawable);
        stateListDrawable.addState(state[1], pressedDrawable);
        return stateListDrawable;
    }

    /**
     * 获得一个选择器Drawable.
     * Android 中 在xml中写的"selector"标签映射对象就是StateListDrawable 对象
     *
     * @param defaultColor 默认时显示的颜色
     * @param pressedColor 按下时显示的颜色
     * @param radius       圆角半径
     * @return 选择器Drawable
     */
    public static StateListDrawable getSelectorDrawable(int defaultColor, int pressedColor, float radius) {

        Drawable defaultDrawable = new Builder(Builder.RECTANGLE).setColor(defaultColor).setCornerRadius(radius).create();
        Drawable pressedDrawable = new Builder(Builder.RECTANGLE).setColor(pressedColor).setCornerRadius(radius).create();

        return getSelectorDrawable(defaultDrawable, pressedDrawable);
    }

    //----------------------------------------------------- Drawable 选择器 end---------------------------------------------------------------


    // --------------------------------------------- 构造器模式 -------------------------------------------------


    /**
     * 获取建造者
     *
     * @param shape 图形
     * @return {@link Builder}
     */
    public static Builder getBuilder(int shape) {
        return new Builder(shape);
    }

    /**
     * Drawable 建造者,链式结构,方便使用
     */
    public static class Builder {
        /**
         * Shape is a rectangle, possibly with rounded corners
         */
        public static final int RECTANGLE = GradientDrawable.RECTANGLE;

        /**
         * Shape is an ellipse
         */
        public static final int OVAL = GradientDrawable.OVAL;

        /**
         * Shape is a line
         */
        public static final int LINE = GradientDrawable.LINE;

        private ColorStateList mSolidColors;
        /**
         * The color state list of the stroke
         */
        private ColorStateList mStrokeColors;
        private @ColorInt
        int[] mGradientColors;
        /**
         * The width in pixels of the stroke
         */
        private int mStrokeWidth = -1; // if >= 0 use stroking.
        /**
         * The length in pixels of the dashes, set to 0 to disable dashes
         */
        private float mStrokeDashWidth = 0.0f;
        /**
         * The gap in pixels between dashes
         */
        private float mStrokeDashGap = 0.0f;
        private float mRadius = 0.0f; // use this if mRadiusArray is null
        private float[] mRadiusArray = null;

        GradientDrawable mGradientDrawable;

        public Builder(int shape) {
            mGradientDrawable = new GradientDrawable();
            mGradientDrawable.setShape(shape == RECTANGLE || shape == OVAL || shape == LINE ? shape : RECTANGLE);
        }

        /**
         * Sets the colors used to draw the gradient.
         * <p>
         * Each color is specified as an ARGB integer and the array must contain at
         * least 2 colors.
         * <p>
         * <strong>Note</strong>: changing colors will affect all instances of a
         * drawable loaded from a resource. It is recommended to invoke
         * {@link # GradientDrawable.mutate()} before changing the colors.
         *
         * @param colors an array containing 2 or more ARGB colors
         * @see # mutate()
         * @see #setColor(int)
         */
        public Builder setColors(@Nullable int[] colors) {
            this.mGradientColors = colors;
            return this;
        }

        /**
         * Changes this drawable to use a single color instead of a gradient.
         * <p>
         * <strong>Note</strong>: changing color will affect all instances of a
         * drawable loaded from a resource. It is recommended to invoke
         * {@link # GradientDrawable.mutate()} before changing the color.
         *
         * @param argb The color used to fill the shape
         */
        public Builder setColor(@ColorInt int argb) {
            this.mSolidColors = ColorStateList.valueOf(argb);
            return this;
        }

        /**
         * Changes this drawable to use a single color state list instead of a
         * gradient. Calling this method with a null argument will clear the color
         * and is equivalent to calling {@link #setColor(int)} with the argument
         * {@link Color#TRANSPARENT}.
         * <p>
         * <strong>Note</strong>: changing color will affect all instances of a
         * drawable loaded from a resource. It is recommended to invoke
         * {@link # GradientDrawable.mutate()()} before changing the color.</p>
         *
         * @param colorStateList The color state list used to fill the shape
         */
        public Builder setColor(@Nullable ColorStateList colorStateList) {
            this.mSolidColors = colorStateList;
            return this;
        }


        /**
         * <p>Set the stroke width and color for the drawable. If width is zero,
         * then no stroke is drawn.</p>
         * <p><strong>Note</strong>: changing this property will affect all instances
         * of a drawable loaded from a resource. It is recommended to invoke
         * {@link # mutate()} before changing this property.</p>
         *
         * @param width The width in pixels of the stroke
         * @param color The color of the stroke
         * @see # mutate()
         * @see #setStroke(int, int, float, float)
         */
        public Builder setStroke(int width, @ColorInt int color) {
            this.mStrokeWidth = width;
            this.mStrokeColors = ColorStateList.valueOf(color);
            return this;
        }

        /**
         * <p>Set the stroke width and color state list for the drawable. If width
         * is zero, then no stroke is drawn.</p>
         * <p><strong>Note</strong>: changing this property will affect all instances
         * of a drawable loaded from a resource. It is recommended to invoke
         * {@link # mutate()} before changing this property.</p>
         *
         * @param width          The width in pixels of the stroke
         * @param colorStateList The color state list of the stroke
         * @see #setStroke(int, ColorStateList, float, float)
         */
        public Builder setStroke(int width, ColorStateList colorStateList) {
            this.mStrokeWidth = width;
            this.mStrokeColors = colorStateList;
            return this;
        }

        /**
         * <p>Set the stroke width and color for the drawable. If width is zero,
         * then no stroke is drawn. This method can also be used to dash the stroke.</p>
         * <p><strong>Note</strong>: changing this property will affect all instances
         * of a drawable loaded from a resource. It is recommended to invoke
         * {@link # mutate()} before changing this property.</p>
         *
         * @param width     The width in pixels of the stroke
         * @param color     The color of the stroke
         * @param dashWidth The length in pixels of the dashes, set to 0 to disable dashes
         * @param dashGap   The gap in pixels between dashes
         * @see #setStroke(int, int)
         */
        public Builder setStroke(int width, @ColorInt int color, float dashWidth, float dashGap) {
            this.mStrokeWidth = width;
            this.mStrokeColors = ColorStateList.valueOf(color);
            this.mStrokeDashWidth = dashWidth;
            this.mStrokeDashGap = dashGap;
            return this;
        }

        /**
         * <p>Set the stroke width and color state list for the drawable. If width
         * is zero, then no stroke is drawn. This method can also be used to dash
         * the stroke.</p>
         * <p><strong>Note</strong>: changing this property will affect all instances
         * of a drawable loaded from a resource. It is recommended to invoke
         * {@link # mutate()} before changing this property.</p>
         *
         * @param width          The width in pixels of the stroke
         * @param colorStateList The color state list of the stroke
         * @param dashWidth      The length in pixels of the dashes, set to 0 to disable dashes
         * @param dashGap        The gap in pixels between dashes
         * @see #setStroke(int, ColorStateList)
         */
        public Builder setStroke(int width, ColorStateList colorStateList, float dashWidth, float dashGap) {
            this.mStrokeWidth = width;
            this.mStrokeColors = colorStateList;
            this.mStrokeDashWidth = dashWidth;
            this.mStrokeDashGap = dashGap;
            return this;
        }

        public Builder setCornerRadius(float radius) {
            if (radius < 0) {
                radius = 0;
            }
            this.mRadius = radius;
            this.mRadiusArray = null;
            return this;
        }

        /**
         * Specifies radii for each of the 4 corners. For each corner, the array
         * contains 2 values, <code>[X_radius, Y_radius]</code>. The corners are
         * ordered top-left, top-right, bottom-right, bottom-left. This property
         * is honored only when the shape is of type {@link #RECTANGLE}.
         * <p>
         * <strong>Note</strong>: changing this property will affect all instances
         * of a drawable loaded from a resource. It is recommended to invoke
         * {@link # mutate()} before changing this property.
         *
         * @param radii an array of length >= 8 containing 4 pairs of X and Y
         *              radius for each corner, specified in pixels
         * @see #setCornerRadius(float)
         */
        public Builder setCornerRadii(@Nullable float[] radii) {
            this.mRadiusArray = radii;
            if (radii == null) {
                this.mRadius = 0;
            }
            return this;
        }

        public Drawable create() {
            if (mRadiusArray != null) {
                mGradientDrawable.setCornerRadii(mRadiusArray);

            } else if (mRadius > 0.0f) {
                mGradientDrawable.setCornerRadius(mRadius);
            }

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                mGradientDrawable.setColor(mSolidColors);
                //显示一条虚线,破折线的宽度为dashWith,破折线之间的空隙的宽度为dashGap,当dashGap=0dp时,为实线
                mGradientDrawable.setStroke(mStrokeWidth, mStrokeColors, mStrokeDashWidth, mStrokeDashGap);
            } else {
                mGradientDrawable.setColor(mSolidColors.getDefaultColor());
                //显示一条虚线,破折线的宽度为dashWith,破折线之间的空隙的宽度为dashGap,当dashGap=0dp时,为实线
                mGradientDrawable.setStroke(mStrokeWidth, mStrokeColors.getDefaultColor(), mStrokeDashWidth, mStrokeDashGap);
            }
            return mGradientDrawable;
        }
    }
}
发布了22 篇原创文章 · 获赞 44 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/JM_beizi/article/details/77433558